WIP New storage APIs roughly completed, lots of errors still

This commit is contained in:
James Wilson
2025-09-30 12:38:53 +01:00
parent b720b59d95
commit fc793a8b79
22 changed files with 717 additions and 169 deletions
+4 -4
View File
@@ -214,22 +214,22 @@ pub enum ExtrinsicCallError {
#[allow(missing_docs)]
#[derive(Debug, thiserror::Error)]
#[error("Storage entry is not a map: pallet {pallet_name}, storage {storage_name}")]
#[error("Storage entry is not a map: pallet {pallet_name}, storage {entry_name}")]
pub struct StorageEntryIsNotAMap {
/// The pallet containing the storage entry that was not found.
pub pallet_name: String,
/// The storage entry that was not found.
pub storage_name: String,
pub entry_name: String,
}
#[allow(missing_docs)]
#[derive(Debug, thiserror::Error)]
#[error("Storage entry is not a plain value: pallet {pallet_name}, storage {storage_name}")]
#[error("Storage entry is not a plain value: pallet {pallet_name}, storage {entry_name}")]
pub struct StorageEntryIsNotAPlainValue {
/// The pallet containing the storage entry that was not found.
pub pallet_name: String,
/// The storage entry that was not found.
pub storage_name: String,
pub entry_name: String,
}
#[allow(missing_docs)]
+2 -2
View File
@@ -98,7 +98,7 @@ impl<'extrinsics, 'atblock> ExtrinsicCallFields<'extrinsics, 'atblock> {
}
/// Attempt to decode the fields into the given type.
pub fn decode<T: scale_decode::DecodeAsFields>(&self) -> Result<T, ExtrinsicCallError> {
pub fn decode_as<T: scale_decode::DecodeAsFields>(&self) -> Result<T, ExtrinsicCallError> {
with_info!(&self.info => {
let cursor = &mut self.bytes();
let mut fields = &mut info.info.call_data().map(|named_arg| {
@@ -156,7 +156,7 @@ impl<'extrinsics, 'atblock> ExtrinsicCallField<'extrinsics, 'atblock> {
}
/// Attempt to decode the value of this field into the given type.
pub fn decode<T: scale_decode::DecodeAsType>(&self) -> Result<T, ExtrinsicCallError> {
pub fn decode_as<T: scale_decode::DecodeAsType>(&self) -> Result<T, ExtrinsicCallError> {
with_call_field_info!(&self.info => {
let cursor = &mut &*self.field_bytes;
let decoded = T::decode_as_type(cursor, info.info.ty().clone(), info.resolver)
@@ -105,7 +105,7 @@ impl<'extrinsics, 'atblock> ExtrinsicTransactionExtensions<'extrinsics, 'atblock
/// Attempt to decode the transaction extensions into a type where each field name is the name of the transaction
/// extension and the field value is the decoded extension.
pub fn decode<T: scale_decode::DecodeAsFields>(
pub fn decode_as<T: scale_decode::DecodeAsFields>(
&self,
) -> Result<T, ExtrinsicTransactionExtensionError> {
with_extensions_info!(&self.info => {
@@ -189,7 +189,7 @@ impl<'extrinsics, 'atblock> ExtrinsicTransactionExtension<'extrinsics, 'atblock>
}
/// Decode the bytes for this transaction extension into a type that implements `scale_decode::DecodeAsType`.
pub fn decode<T: scale_decode::DecodeAsType>(
pub fn decode_as<T: scale_decode::DecodeAsType>(
&self,
) -> Result<T, ExtrinsicTransactionExtensionError> {
with_extension_info!(&self.info => {
+46 -52
View File
@@ -42,14 +42,14 @@ where
pub fn entry(
&self,
pallet_name: impl Into<String>,
storage_name: impl Into<String>,
entry_name: impl Into<String>,
) -> Result<StorageEntryClient<'atblock, Client, T>, StorageError> {
let pallet_name = pallet_name.into();
let storage_name = storage_name.into();
let entry_name = entry_name.into();
let storage_info = AnyStorageInfo::new(
&pallet_name,
&storage_name,
&entry_name,
self.client.metadata(),
self.client.legacy_types(),
)?;
@@ -58,7 +58,7 @@ where
Ok(StorageEntryClient::Map(StorageEntryMapClient {
client: self.client,
pallet_name,
storage_name,
entry_name,
info: storage_info,
marker: std::marker::PhantomData,
}))
@@ -66,7 +66,7 @@ where
Ok(StorageEntryClient::Plain(StorageEntryPlainClient {
client: self.client,
pallet_name,
storage_name,
entry_name,
info: storage_info,
marker: std::marker::PhantomData,
}))
@@ -105,7 +105,7 @@ where
}
/// The storage entry name.
pub fn storage_name(&self) -> &str {
pub fn entry_name(&self) -> &str {
&self.entry.storage_entry
}
@@ -143,10 +143,10 @@ where
}
/// Get the storage entry name.
pub fn storage_name(&self) -> &str {
pub fn entry_name(&self) -> &str {
match self {
StorageEntryClient::Plain(client) => &client.storage_name,
StorageEntryClient::Map(client) => &client.storage_name,
StorageEntryClient::Plain(client) => &client.entry_name,
StorageEntryClient::Map(client) => &client.entry_name,
}
}
@@ -168,7 +168,7 @@ where
StorageEntryClient::Plain(client) => Ok(client),
StorageEntryClient::Map(_) => Err(StorageEntryIsNotAPlainValue {
pallet_name: self.pallet_name().into(),
storage_name: self.storage_name().into(),
entry_name: self.entry_name().into(),
}),
}
}
@@ -180,18 +180,27 @@ where
match self {
StorageEntryClient::Plain(_) => Err(StorageEntryIsNotAMap {
pallet_name: self.pallet_name().into(),
storage_name: self.storage_name().into(),
entry_name: self.entry_name().into(),
}),
StorageEntryClient::Map(client) => Ok(client),
}
}
/// Return the default value for this storage entry, if there is one. Returns `None` if there
/// is no default value.
pub fn default_value(&self) -> Option<StorageValue<'_, 'atblock>> {
match self {
StorageEntryClient::Plain(client) => client.default_value(),
StorageEntryClient::Map(client) => client.default_value(),
}
}
}
/// A client for working with a plain storage entry.
pub struct StorageEntryPlainClient<'atblock, Client, T> {
client: &'atblock Client,
pallet_name: String,
storage_name: String,
entry_name: String,
info: AnyStorageInfo<'atblock>,
marker: std::marker::PhantomData<T>,
}
@@ -207,13 +216,13 @@ where
}
/// Get the storage entry name.
pub fn storage_name(&self) -> &str {
&self.storage_name
pub fn entry_name(&self) -> &str {
&self.entry_name
}
/// Return the default value for this storage entry, if there is one. Returns `None` if there
/// is no default value.
pub fn default(&self) -> Option<StorageValue<'_, 'atblock>> {
pub fn default_value(&self) -> Option<StorageValue<'_, 'atblock>> {
with_info!(info = &self.info => {
info.info.default_value.as_ref().map(|default_value| {
StorageValue::new(&self.info, default_value.clone())
@@ -227,30 +236,24 @@ where
T: Config + 'atblock,
Client: OnlineClientAtBlockT<'atblock, T>,
{
/// Fetch the value for this storage entry.
/// Fetch the value for this storage entry. If no value exists and no default value is
/// set for this storage entry, then `None` will be returned.
pub async fn fetch(&self) -> Result<Option<StorageValue<'_, 'atblock>>, StorageError> {
let key_bytes = self.key();
fetch(self.client, &key_bytes)
.await
.map(|v| v.map(|bytes| StorageValue::new(&self.info, Cow::Owned(bytes))))
}
let value = fetch(self.client, &key_bytes)
.await?
.map(|bytes| StorageValue::new(&self.info, Cow::Owned(bytes)))
.or_else(|| self.default_value());
/// Fetch the value for this storage entry as per [`StorageEntryPlainClient::fetch`], but return the default
/// value for the storage entry if one exists and the entry does not exist.
pub async fn fetch_or_default(
&self,
) -> Result<Option<StorageValue<'_, 'atblock>>, StorageError> {
self.fetch()
.await
.map(|option_val| option_val.or_else(|| self.default()))
Ok(value)
}
/// The key for this storage entry.
pub fn key(&self) -> [u8; 32] {
let pallet_name = &*self.pallet_name;
let storage_name = &*self.storage_name;
let entry_name = &*self.entry_name;
frame_decode::storage::encode_storage_key_prefix(pallet_name, storage_name)
frame_decode::storage::encode_storage_key_prefix(pallet_name, entry_name)
}
}
@@ -258,7 +261,7 @@ where
pub struct StorageEntryMapClient<'atblock, Client, T> {
client: &'atblock Client,
pallet_name: String,
storage_name: String,
entry_name: String,
info: AnyStorageInfo<'atblock>,
marker: std::marker::PhantomData<T>,
}
@@ -274,13 +277,13 @@ where
}
/// Get the storage entry name.
pub fn storage_name(&self) -> &str {
&self.storage_name
pub fn entry_name(&self) -> &str {
&self.entry_name
}
/// Return the default value for this storage entry, if there is one. Returns `None` if there
/// is no default value.
pub fn default(&self) -> Option<StorageValue<'_, 'atblock>> {
pub fn default_value(&self) -> Option<StorageValue<'_, 'atblock>> {
with_info!(info = &self.info => {
info.info.default_value.as_ref().map(|default_value| {
StorageValue::new(&self.info, default_value.clone())
@@ -296,7 +299,8 @@ where
{
/// Fetch a specific key in this map. If the number of keys provided is not equal
/// to the number of keys required to fetch a single value from the map, then an error
/// will be emitted.
/// will be emitted. If no value exists but there is a default value for this storage
/// entry, then the default value will be returned. Else, `None` will be returned.
pub async fn fetch<Keys: IntoEncodableValues>(
&self,
keys: Keys,
@@ -313,20 +317,12 @@ where
}
let key_bytes = self.key(keys)?;
fetch(self.client, &key_bytes)
.await
.map(|v| v.map(|bytes| StorageValue::new(&self.info, Cow::Owned(bytes))))
}
let value = fetch(self.client, &key_bytes)
.await?
.map(|bytes| StorageValue::new(&self.info, Cow::Owned(bytes)))
.or_else(|| self.default_value());
/// Fetch a specific key in this map as per [`StorageEntryMapClient::fetch`], but return the default
/// value for the storage entry if one exists and the entry was not found.
pub async fn fetch_or_default<Keys: IntoEncodableValues>(
&self,
keys: Keys,
) -> Result<Option<StorageValue<'_, 'atblock>>, StorageError> {
self.fetch(keys)
.await
.map(|option_val| option_val.or_else(|| self.default()))
Ok(value)
}
/// Iterate over the values underneath the provided keys.
@@ -387,14 +383,12 @@ where
// the key bytes and some metadata about them. Or maybe just fetch_raw and iter_raw.
fn key<Keys: IntoEncodableValues>(&self, keys: Keys) -> Result<Vec<u8>, StorageError> {
with_info!(info = &self.info => {
let mut key_bytes = Vec::new();
frame_decode::storage::encode_storage_key_with_info_to(
let key_bytes = frame_decode::storage::encode_storage_key_with_info(
&self.pallet_name,
&self.storage_name,
&self.entry_name,
keys,
&info.info,
info.resolver,
&mut key_bytes,
).map_err(|e| StorageError::KeyEncodeError { reason: e })?;
Ok(key_bytes)
})
+5 -11
View File
@@ -1,8 +1,7 @@
use super::storage_info::AnyStorageInfo;
use super::storage_key::StorageKey;
use super::storage_value::StorageValue;
use crate::error::{StorageKeyError, StorageValueError};
use scale_decode::DecodeAsType;
use crate::error::StorageKeyError;
use std::borrow::Cow;
/// This represents a storage entry, which is a key-value pair in the storage.
@@ -30,11 +29,6 @@ impl<'entry, 'atblock> StorageEntry<'entry, 'atblock> {
&self.key
}
/// Get the raw bytes for this storage entry's value.
pub fn value_bytes(&self) -> &[u8] {
self.value.bytes()
}
/// Consume this storage entry and return the raw bytes for the key and value.
pub fn into_key_and_value_bytes(self) -> (Vec<u8>, Vec<u8>) {
(self.key, self.value.into_bytes())
@@ -42,12 +36,12 @@ impl<'entry, 'atblock> StorageEntry<'entry, 'atblock> {
/// Decode the key for this storage entry. This gives back a type from which we can
/// decode specific parts of the key hash (where applicable).
pub fn decode_key(&'_ self) -> Result<StorageKey<'_, 'atblock>, StorageKeyError> {
pub fn key(&'_ self) -> Result<StorageKey<'_, 'atblock>, StorageKeyError> {
StorageKey::new(self.value.info, &self.key)
}
/// Decode this storage value.
pub fn decode_value<T: DecodeAsType>(&self) -> Result<T, StorageValueError> {
self.value.decode::<T>()
/// Return the storage value.
pub fn value(&self) -> &StorageValue<'entry, 'atblock> {
&self.value
}
}
+2 -2
View File
@@ -81,7 +81,7 @@ impl<'entry, 'atblock> StorageKey<'entry, 'atblock> {
/// Attempt to decode the values contained within this storage key to the `Target` type
/// provided. This type is typically a tuple of types which each implement [`scale_decode::DecodeAsType`]
/// and correspond to each of the key types present, in order.
pub fn decode<Target: IntoDecodableValues>(&self) -> Result<Target,StorageKeyError> {
pub fn decode_as<Target: IntoDecodableValues>(&self) -> Result<Target,StorageKeyError> {
with_key_info!(info = &self.info => {
let values = frame_decode::storage::decode_storage_key_values(
self.bytes,
@@ -154,7 +154,7 @@ impl<'key, 'entry, 'atblock> StorageKeyPart<'key, 'entry, 'atblock> {
/// is available as a part of the key hash, allowing us to decode it into anything
/// implementing [`scale_decode::DecodeAsType`]. If the key was produced using a
/// different hasher, this will return `None`.
pub fn decode<T: scale_decode::DecodeAsType>(&self) -> Result<Option<T>, StorageKeyError> {
pub fn decode_as<T: scale_decode::DecodeAsType>(&self) -> Result<Option<T>, StorageKeyError> {
with_key_info!(info = &self.info => {
let part_info = &info.info[self.index];
let Some(value_info) = part_info.value() else {
+1 -1
View File
@@ -27,7 +27,7 @@ impl<'entry, 'atblock> StorageValue<'entry, 'atblock> {
}
/// Decode this storage value.
pub fn decode<T: DecodeAsType>(&self) -> Result<T, StorageValueError> {
pub fn decode_as<T: DecodeAsType>(&self) -> Result<T, StorageValueError> {
with_info!(info = &self.info => {
let cursor = &mut &*self.bytes;