mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-08-12 20:51:31 +00:00
v0.50.0: Integrate frame-decode, redo storage APIs and break up Error. (#2100)
* WIP integrating new frame-decode and working out new storage APIS * WIP: first pass adding new storage things to subxt-core * Second pass over Address type and start impl in Subxt * WIP new storage APIs * WIP New storage APIs roughly completed, lots of errors still * Remove PlainorMap enum; plain and map values now use same struct to simplify usage * Begin 'fixing' errors * WIP splitting errors and tidying payload/address traits * Get subxt-core compiling * Small fixes in subxt-core and remove metadata mod * subxt-core: cargo check --all-targets passes * Fix test * WIP starting to update subxt from subxt-core changes * WIP splitting up subxt errors into smaller variants * WIP errors: add DispatchError errors * Port new Storage APIs to subxt-core * cargo check -p subxt passes * Quick-fix errors in subxt-cli (explore subcommand) * fmt * Finish fixing codegen up and start fixing examples * get Subxt examples compiling and bytes_at for constants * Add some arcs to limit lifetimes in subxt/subxt-core storage APIs * A little Arcing to allow more method chaining in Storage APIs, aligning with Subxt * Update codegen test * cargo check --all-targets passing * cargo check --features 'unstable-light-client' passing * clippy * Remove unused dep in subxt * use published frame-decode * fix wasm-example * Add new tx extension to fix daily tests * Remove unused subxt_core::dynamic::DecodedValue type * Update book to match changes * Update docs to fix more broken bits * Add missing docs * fmt * allow larger result errs for now * Add missing alloc imports in subxt-core * Fix doc tests and fix bug getting constant info * Fix V14 -> Metadata transform for storage & constants * Fix parachain example * Fix FFI example * BlockLength decodes t ostruct, not u128 * use fetch/iter shorthands rather than entry in most storage tests * Fix some integration tests * Fix Runtime codegen tests * Expose the dynamic custom_value selecter and use in a UI test * Update codegen metadata * Tidy CLI storage query and support (str,str) as a storage address * Add (str,str) as valid constant address too * Show string tuple in constants example * Via the magic of traits, avoid needing any clones of queries/addresses and accept references to them * clippy
This commit is contained in:
+19
-10
@@ -21,10 +21,6 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
StorageError(#[from] StorageError),
|
||||
#[error(transparent)]
|
||||
StorageEntryIsNotAMap(#[from] StorageEntryIsNotAMap),
|
||||
#[error(transparent)]
|
||||
StorageEntryIsNotAPlainValue(#[from] StorageEntryIsNotAPlainValue),
|
||||
#[error(transparent)]
|
||||
StorageKeyError(#[from] StorageKeyError),
|
||||
#[error(transparent)]
|
||||
StorageValueError(#[from] StorageValueError),
|
||||
@@ -214,22 +210,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)]
|
||||
@@ -252,14 +248,23 @@ pub enum StorageError {
|
||||
reason: frame_decode::storage::StorageKeyEncodeError,
|
||||
},
|
||||
#[error(
|
||||
"Too many keys provided: expected {num_keys_expected} keys, but got {num_keys_provided}"
|
||||
"Wrong number of keys provided to fetch a value: expected {num_keys_expected} keys, but got {num_keys_provided}"
|
||||
)]
|
||||
WrongNumberOfKeysProvided {
|
||||
WrongNumberOfKeysProvidedForFetch {
|
||||
/// The number of keys that were provided.
|
||||
num_keys_provided: usize,
|
||||
/// The number of keys expected.
|
||||
num_keys_expected: usize,
|
||||
},
|
||||
#[error(
|
||||
"too many keys were provided to iterate over a storage entry: expected at most {max_keys_expected} keys, but got {num_keys_provided}"
|
||||
)]
|
||||
TooManyKeysProvidedForIter {
|
||||
/// The number of keys that were provided.
|
||||
num_keys_provided: usize,
|
||||
/// The maximum number of keys that we expect.
|
||||
max_keys_expected: usize,
|
||||
},
|
||||
#[error(
|
||||
"Could not extract storage information from metadata: Unsupported metadata version ({version})"
|
||||
)]
|
||||
@@ -295,6 +300,10 @@ pub enum StorageKeyError {
|
||||
index: usize,
|
||||
reason: scale_decode::Error,
|
||||
},
|
||||
#[error("Could not decode values out of the storage key: {reason}")]
|
||||
DecodeKeyValueError {
|
||||
reason: frame_decode::storage::StorageKeyValueDecodeError,
|
||||
},
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
|
||||
@@ -7,7 +7,7 @@ mod extrinsic_info;
|
||||
mod extrinsic_transaction_extensions;
|
||||
mod extrinsics_type;
|
||||
|
||||
pub use extrinsic_transaction_extensions::ExtrinsicTransactionExtensions;
|
||||
pub use extrinsic_transaction_extensions::ExtrinsicTransactionParams;
|
||||
pub use extrinsics_type::{Extrinsic, Extrinsics};
|
||||
|
||||
/// Work with extrinsics.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -16,7 +16,7 @@ struct ExtrinsicExtensionsInfo<'extrinsics, 'atblock, TypeId, Resolver> {
|
||||
}
|
||||
|
||||
/// This represents the transaction extensions of an extrinsic.
|
||||
pub struct ExtrinsicTransactionExtensions<'extrinsics, 'atblock> {
|
||||
pub struct ExtrinsicTransactionParams<'extrinsics, 'atblock> {
|
||||
all_bytes: &'extrinsics [u8],
|
||||
info: AnyExtrinsicExtensionsInfo<'extrinsics, 'atblock>,
|
||||
}
|
||||
@@ -31,7 +31,7 @@ macro_rules! with_extensions_info {
|
||||
};
|
||||
}
|
||||
|
||||
impl<'extrinsics, 'atblock> ExtrinsicTransactionExtensions<'extrinsics, 'atblock> {
|
||||
impl<'extrinsics, 'atblock> ExtrinsicTransactionParams<'extrinsics, 'atblock> {
|
||||
pub(crate) fn new(
|
||||
all_bytes: &'extrinsics [u8],
|
||||
info: &'extrinsics AnyExtrinsicInfo<'atblock>,
|
||||
@@ -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 => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::extrinsic_call::ExtrinsicCall;
|
||||
use super::extrinsic_info::{AnyExtrinsicInfo, with_info};
|
||||
use super::extrinsic_transaction_extensions::ExtrinsicTransactionExtensions;
|
||||
use super::extrinsic_transaction_extensions::ExtrinsicTransactionParams;
|
||||
use crate::client::OfflineClientAtBlockT;
|
||||
use crate::config::Config;
|
||||
use crate::error::ExtrinsicsError;
|
||||
@@ -106,8 +106,8 @@ impl<'extrinsics, 'atblock> Extrinsic<'extrinsics, 'atblock> {
|
||||
/// Get information about the transaction extensions of this extrinsic.
|
||||
pub fn transaction_extensions(
|
||||
&self,
|
||||
) -> Option<ExtrinsicTransactionExtensions<'extrinsics, 'atblock>> {
|
||||
ExtrinsicTransactionExtensions::new(self.bytes, self.info)
|
||||
) -> Option<ExtrinsicTransactionParams<'extrinsics, 'atblock>> {
|
||||
ExtrinsicTransactionParams::new(self.bytes, self.info)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+94
-219
@@ -5,16 +5,17 @@ mod storage_value;
|
||||
|
||||
use crate::client::{OfflineClientAtBlockT, OnlineClientAtBlockT};
|
||||
use crate::config::Config;
|
||||
use crate::error::{StorageEntryIsNotAMap, StorageEntryIsNotAPlainValue, StorageError};
|
||||
use crate::error::StorageError;
|
||||
use crate::storage::storage_info::with_info;
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
use storage_info::AnyStorageInfo;
|
||||
|
||||
pub use storage_entry::StorageEntry;
|
||||
pub use storage_key::{StorageHasher, StorageKey, StorageKeyPart};
|
||||
pub use storage_value::StorageValue;
|
||||
// We take how storage keys can be passed in from `frame-decode`, so re-export here.
|
||||
pub use frame_decode::storage::{IntoStorageKeys, StorageKeys};
|
||||
pub use frame_decode::storage::{EncodableValues, IntoEncodableValues};
|
||||
|
||||
/// Work with storage.
|
||||
pub struct StorageClient<'atblock, Client, T> {
|
||||
@@ -33,44 +34,34 @@ impl<'atblock, Client, T> StorageClient<'atblock, Client, T> {
|
||||
}
|
||||
|
||||
// Things that we can do offline with storage.
|
||||
impl<'atblock, 'client: 'atblock, Client, T> StorageClient<'atblock, Client, T>
|
||||
impl<'atblock, Client, T> StorageClient<'atblock, Client, T>
|
||||
where
|
||||
T: Config + 'client,
|
||||
Client: OfflineClientAtBlockT<'client, T>,
|
||||
T: Config + 'atblock,
|
||||
Client: OfflineClientAtBlockT<'atblock, T>,
|
||||
{
|
||||
/// Select the storage entry you'd like to work with.
|
||||
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(),
|
||||
)?;
|
||||
|
||||
if storage_info.is_map() {
|
||||
Ok(StorageEntryClient::Map(StorageEntryMapClient {
|
||||
client: self.client,
|
||||
pallet_name,
|
||||
storage_name,
|
||||
info: storage_info,
|
||||
marker: std::marker::PhantomData,
|
||||
}))
|
||||
} else {
|
||||
Ok(StorageEntryClient::Plain(StorageEntryPlainClient {
|
||||
client: self.client,
|
||||
pallet_name,
|
||||
storage_name,
|
||||
info: storage_info,
|
||||
marker: std::marker::PhantomData,
|
||||
}))
|
||||
}
|
||||
Ok(StorageEntryClient {
|
||||
client: self.client,
|
||||
pallet_name,
|
||||
entry_name,
|
||||
info: Arc::new(storage_info),
|
||||
marker: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Iterate over all of the storage entries listed in the metadata for the current block. This does **not** include well known
|
||||
@@ -78,6 +69,7 @@ where
|
||||
pub fn entries(&self) -> impl Iterator<Item = StorageEntriesItem<'atblock, Client, T>> {
|
||||
let client = self.client;
|
||||
let metadata = client.metadata();
|
||||
|
||||
frame_decode::helpers::list_storage_entries_any(metadata).map(|entry| StorageEntriesItem {
|
||||
entry,
|
||||
client: self.client,
|
||||
@@ -88,24 +80,24 @@ where
|
||||
|
||||
/// Working with a specific storage entry.
|
||||
pub struct StorageEntriesItem<'atblock, Client, T> {
|
||||
entry: frame_decode::helpers::StorageEntry<'atblock>,
|
||||
entry: frame_decode::storage::StorageEntry<'atblock>,
|
||||
client: &'atblock Client,
|
||||
marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'atblock, 'client: 'atblock, Client, T> StorageEntriesItem<'atblock, Client, T>
|
||||
impl<'atblock, Client, T> StorageEntriesItem<'atblock, Client, T>
|
||||
where
|
||||
T: Config + 'client,
|
||||
Client: OfflineClientAtBlockT<'client, T>,
|
||||
T: Config + 'atblock,
|
||||
Client: OfflineClientAtBlockT<'atblock, T>,
|
||||
{
|
||||
/// The pallet name.
|
||||
pub fn pallet_name(&self) -> &str {
|
||||
self.entry.pallet()
|
||||
&self.entry.pallet_name
|
||||
}
|
||||
|
||||
/// The storage entry name.
|
||||
pub fn storage_name(&self) -> &str {
|
||||
self.entry.entry()
|
||||
pub fn entry_name(&self) -> &str {
|
||||
&self.entry.storage_entry
|
||||
}
|
||||
|
||||
/// Extract the relevant storage information so that we can work with this entry.
|
||||
@@ -115,87 +107,22 @@ where
|
||||
marker: std::marker::PhantomData,
|
||||
}
|
||||
.entry(
|
||||
self.entry.pallet().to_owned(),
|
||||
self.entry.entry().to_owned(),
|
||||
self.entry.pallet_name.clone(),
|
||||
self.entry.storage_entry.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A client for working with a specific storage entry. This is an enum because the storage entry
|
||||
/// might be either a map or a plain value, and each has a different interface.
|
||||
pub enum StorageEntryClient<'atblock, Client, T> {
|
||||
Plain(StorageEntryPlainClient<'atblock, Client, T>),
|
||||
Map(StorageEntryMapClient<'atblock, Client, T>),
|
||||
/// A client for working with a specific storage entry.
|
||||
pub struct StorageEntryClient<'atblock, Client, T> {
|
||||
client: &'atblock Client,
|
||||
pallet_name: String,
|
||||
entry_name: String,
|
||||
info: Arc<AnyStorageInfo<'atblock>>,
|
||||
marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'atblock, Client, T> StorageEntryClient<'atblock, Client, T>
|
||||
where
|
||||
T: Config + 'atblock,
|
||||
Client: OfflineClientAtBlockT<'atblock, T>,
|
||||
{
|
||||
/// Get the pallet name.
|
||||
pub fn pallet_name(&self) -> &str {
|
||||
match self {
|
||||
StorageEntryClient::Plain(client) => &client.pallet_name,
|
||||
StorageEntryClient::Map(client) => &client.pallet_name,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the storage entry name.
|
||||
pub fn storage_name(&self) -> &str {
|
||||
match self {
|
||||
StorageEntryClient::Plain(client) => &client.storage_name,
|
||||
StorageEntryClient::Map(client) => &client.storage_name,
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the storage entry a plain value?
|
||||
pub fn is_plain(&self) -> bool {
|
||||
matches!(self, StorageEntryClient::Plain(_))
|
||||
}
|
||||
|
||||
/// Is the storage entry a map?
|
||||
pub fn is_map(&self) -> bool {
|
||||
matches!(self, StorageEntryClient::Map(_))
|
||||
}
|
||||
|
||||
/// If this storage entry is a plain value, return the client for working with it. Else return an error.
|
||||
pub fn into_plain(
|
||||
self,
|
||||
) -> Result<StorageEntryPlainClient<'atblock, Client, T>, StorageEntryIsNotAPlainValue> {
|
||||
match self {
|
||||
StorageEntryClient::Plain(client) => Ok(client),
|
||||
StorageEntryClient::Map(_) => Err(StorageEntryIsNotAPlainValue {
|
||||
pallet_name: self.pallet_name().into(),
|
||||
storage_name: self.storage_name().into(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// If this storage entry is a map, return the client for working with it. Else return an error.
|
||||
pub fn into_map(
|
||||
self,
|
||||
) -> Result<StorageEntryMapClient<'atblock, Client, T>, StorageEntryIsNotAMap> {
|
||||
match self {
|
||||
StorageEntryClient::Plain(_) => Err(StorageEntryIsNotAMap {
|
||||
pallet_name: self.pallet_name().into(),
|
||||
storage_name: self.storage_name().into(),
|
||||
}),
|
||||
StorageEntryClient::Map(client) => Ok(client),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A client for working with a plain storage entry.
|
||||
pub struct StorageEntryPlainClient<'atblock, Client, T> {
|
||||
client: &'atblock Client,
|
||||
pallet_name: String,
|
||||
storage_name: String,
|
||||
info: AnyStorageInfo<'atblock>,
|
||||
marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'atblock, Client, T> StorageEntryPlainClient<'atblock, Client, T>
|
||||
where
|
||||
T: Config + 'atblock,
|
||||
Client: OfflineClientAtBlockT<'atblock, T>,
|
||||
@@ -206,134 +133,72 @@ 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>> {
|
||||
with_info!(info = &self.info => {
|
||||
info.info.default_value.as_ref().map(|default_value| {
|
||||
StorageValue::new(&self.info, default_value.clone())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'atblock, Client, T> StorageEntryPlainClient<'atblock, Client, T>
|
||||
where
|
||||
T: Config + 'atblock,
|
||||
Client: OnlineClientAtBlockT<'atblock, T>,
|
||||
{
|
||||
/// Fetch the value for this storage entry.
|
||||
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))))
|
||||
}
|
||||
|
||||
/// 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()))
|
||||
}
|
||||
|
||||
/// The key for this storage entry.
|
||||
pub fn key(&self) -> [u8; 32] {
|
||||
/// The key which points to this storage entry (but not necessarily any values within it).
|
||||
pub fn key_prefix(&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_prefix(pallet_name, storage_name)
|
||||
}
|
||||
}
|
||||
|
||||
/// A client for working with a storage entry that is a map.
|
||||
pub struct StorageEntryMapClient<'atblock, Client, T> {
|
||||
client: &'atblock Client,
|
||||
pallet_name: String,
|
||||
storage_name: String,
|
||||
info: AnyStorageInfo<'atblock>,
|
||||
marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'atblock, Client, T> StorageEntryMapClient<'atblock, Client, T>
|
||||
where
|
||||
T: Config + 'atblock,
|
||||
Client: OfflineClientAtBlockT<'atblock, T>,
|
||||
{
|
||||
/// Get the pallet name.
|
||||
pub fn pallet_name(&self) -> &str {
|
||||
&self.pallet_name
|
||||
}
|
||||
|
||||
/// Get the storage entry name.
|
||||
pub fn storage_name(&self) -> &str {
|
||||
&self.storage_name
|
||||
frame_decode::storage::encode_storage_key_prefix(pallet_name, 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>> {
|
||||
with_info!(info = &self.info => {
|
||||
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())
|
||||
StorageValue::new(self.info.clone(), default_value.clone())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'atblock, Client, T> StorageEntryMapClient<'atblock, Client, T>
|
||||
impl<'atblock, Client, T> StorageEntryClient<'atblock, Client, T>
|
||||
where
|
||||
T: Config + 'atblock,
|
||||
Client: OnlineClientAtBlockT<'atblock, T>,
|
||||
{
|
||||
/// 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.
|
||||
pub async fn fetch<Keys: IntoStorageKeys>(
|
||||
/// 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,
|
||||
) -> Result<Option<StorageValue<'_, 'atblock>>, StorageError> {
|
||||
let expected_num_keys = with_info!(info = &self.info => {
|
||||
) -> Result<Option<StorageValue<'atblock>>, StorageError> {
|
||||
let expected_num_keys = with_info!(info = &*self.info => {
|
||||
info.info.keys.len()
|
||||
});
|
||||
|
||||
if expected_num_keys != keys.num_keys() {
|
||||
return Err(StorageError::WrongNumberOfKeysProvided {
|
||||
num_keys_provided: keys.num_keys(),
|
||||
// For fetching, we need exactly as many keys as exist for a storage entry.
|
||||
if expected_num_keys != keys.num_encodable_values() {
|
||||
return Err(StorageError::WrongNumberOfKeysProvidedForFetch {
|
||||
num_keys_provided: keys.num_encodable_values(),
|
||||
num_keys_expected: expected_num_keys,
|
||||
});
|
||||
}
|
||||
|
||||
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 info = self.info.clone();
|
||||
let value = fetch(self.client, &key_bytes)
|
||||
.await?
|
||||
.map(|bytes| StorageValue::new(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: IntoStorageKeys>(
|
||||
&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.
|
||||
pub async fn iter<Keys: IntoStorageKeys>(
|
||||
pub async fn iter<Keys: IntoEncodableValues>(
|
||||
&self,
|
||||
keys: Keys,
|
||||
) -> Result<
|
||||
impl futures::Stream<Item = Result<StorageEntry<'_, 'atblock>, StorageError>> + Unpin,
|
||||
impl futures::Stream<Item = Result<StorageEntry<'atblock>, StorageError>>
|
||||
+ Unpin
|
||||
+ use<'atblock, Client, T, Keys>,
|
||||
StorageError,
|
||||
> {
|
||||
use futures::stream::StreamExt;
|
||||
@@ -341,6 +206,19 @@ where
|
||||
ArchiveStorageEvent, StorageQuery, StorageQueryType,
|
||||
};
|
||||
|
||||
let expected_num_keys = with_info!(info = &*self.info => {
|
||||
info.info.keys.len()
|
||||
});
|
||||
|
||||
// For iterating, we need at most one less key than the number that exists for a storage entry.
|
||||
// TODO: The error message will be confusing if == keys are provided!
|
||||
if keys.num_encodable_values() >= expected_num_keys {
|
||||
return Err(StorageError::TooManyKeysProvidedForIter {
|
||||
num_keys_provided: keys.num_encodable_values(),
|
||||
max_keys_expected: expected_num_keys - 1,
|
||||
});
|
||||
}
|
||||
|
||||
let block_hash = self.client.block_hash();
|
||||
let key_bytes = self.key(keys)?;
|
||||
|
||||
@@ -356,23 +234,22 @@ where
|
||||
.await
|
||||
.map_err(|e| StorageError::RpcError { reason: e })?;
|
||||
|
||||
let sub = sub.filter_map(async |item| {
|
||||
let item = match item {
|
||||
Ok(ArchiveStorageEvent::Item(item)) => item,
|
||||
Ok(ArchiveStorageEvent::Error(err)) => {
|
||||
return Some(Err(StorageError::StorageEventError { reason: err.error }));
|
||||
}
|
||||
Ok(ArchiveStorageEvent::Done) => return None,
|
||||
Err(e) => return Some(Err(StorageError::RpcError { reason: e })),
|
||||
};
|
||||
let info = self.info.clone();
|
||||
let sub = sub.filter_map(move |item| {
|
||||
let info = info.clone();
|
||||
async move {
|
||||
let item = match item {
|
||||
Ok(ArchiveStorageEvent::Item(item)) => item,
|
||||
Ok(ArchiveStorageEvent::Error(err)) => {
|
||||
return Some(Err(StorageError::StorageEventError { reason: err.error }));
|
||||
}
|
||||
Ok(ArchiveStorageEvent::Done) => return None,
|
||||
Err(e) => return Some(Err(StorageError::RpcError { reason: e })),
|
||||
};
|
||||
|
||||
item.value.map(|value| {
|
||||
Ok(StorageEntry::new(
|
||||
&self.info,
|
||||
item.key.0,
|
||||
Cow::Owned(value.0),
|
||||
))
|
||||
})
|
||||
item.value
|
||||
.map(|value| Ok(StorageEntry::new(info, item.key.0, Cow::Owned(value.0))))
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Box::pin(sub))
|
||||
@@ -384,16 +261,14 @@ where
|
||||
// Dev note: We don't have any functions that can take an already-encoded key and fetch an entry from
|
||||
// it yet, so we don't expose this. If we did expose it, we might want to return some struct that wraps
|
||||
// the key bytes and some metadata about them. Or maybe just fetch_raw and iter_raw.
|
||||
fn key<Keys: IntoStorageKeys>(&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(
|
||||
fn key<Keys: IntoEncodableValues>(&self, keys: Keys) -> Result<Vec<u8>, StorageError> {
|
||||
with_info!(info = &*self.info => {
|
||||
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)
|
||||
})
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
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;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// This represents a storage entry, which is a key-value pair in the storage.
|
||||
pub struct StorageEntry<'entry, 'atblock> {
|
||||
pub struct StorageEntry<'atblock> {
|
||||
key: Vec<u8>,
|
||||
// This contains the storage information already:
|
||||
value: StorageValue<'entry, 'atblock>,
|
||||
value: StorageValue<'atblock>,
|
||||
}
|
||||
|
||||
impl<'entry, 'atblock> StorageEntry<'entry, 'atblock> {
|
||||
impl<'atblock> StorageEntry<'atblock> {
|
||||
/// Create a new storage entry.
|
||||
pub fn new(
|
||||
info: &'entry AnyStorageInfo<'atblock>,
|
||||
info: Arc<AnyStorageInfo<'atblock>>,
|
||||
key: Vec<u8>,
|
||||
value: Cow<'atblock, [u8]>,
|
||||
) -> Self {
|
||||
@@ -30,11 +30,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 +37,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> {
|
||||
StorageKey::new(self.value.info, &self.key)
|
||||
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<'atblock> {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ impl<'atblock> AnyStorageInfo<'atblock> {
|
||||
Resolver: scale_type_resolver::TypeResolver<TypeId = Info::TypeId>,
|
||||
AnyStorageInfo<'atblock>: From<StorageInfo<'atblock, Info::TypeId, Resolver>>,
|
||||
{
|
||||
m.get_storage_info(pallet_name, entry_name)
|
||||
m.storage_info(pallet_name, entry_name)
|
||||
.map(|frame_storage_info| {
|
||||
let info = StorageInfo {
|
||||
info: frame_storage_info,
|
||||
@@ -84,7 +84,7 @@ impl<'atblock> From<StorageInfo<'atblock, u32, scale_info::PortableRegistry>>
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StorageInfo<'atblock, TypeId, Resolver> {
|
||||
pub struct StorageInfo<'atblock, TypeId: Clone, Resolver> {
|
||||
pub info: frame_decode::storage::StorageInfo<'atblock, TypeId>,
|
||||
pub resolver: &'atblock Resolver,
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{error::StorageKeyError, storage::storage_info::with_info};
|
||||
use scale_info_legacy::{LookupName, TypeRegistrySet};
|
||||
|
||||
// This is part of our public interface.
|
||||
pub use frame_decode::storage::StorageHasher;
|
||||
pub use frame_decode::storage::{IntoDecodableValues, StorageHasher};
|
||||
|
||||
enum AnyStorageKeyInfo<'atblock> {
|
||||
Legacy(StorageKeyInfo<'atblock, LookupName, TypeRegistrySet<'atblock>>),
|
||||
@@ -78,6 +78,23 @@ 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_as<Target: IntoDecodableValues>(&self) -> Result<Target, StorageKeyError> {
|
||||
with_key_info!(info = &self.info => {
|
||||
let values = frame_decode::storage::decode_storage_key_values(
|
||||
self.bytes,
|
||||
&info.info,
|
||||
info.resolver
|
||||
).map_err(|e| {
|
||||
StorageKeyError::DecodeKeyValueError { reason: e }
|
||||
})?;
|
||||
|
||||
Ok(values)
|
||||
})
|
||||
}
|
||||
|
||||
/// Iterate over the parts of this storage key. Each part of a storage key corresponds to a
|
||||
/// single value that has been hashed.
|
||||
pub fn parts(&'_ self) -> impl ExactSizeIterator<Item = StorageKeyPart<'_, 'entry, 'atblock>> {
|
||||
@@ -137,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 {
|
||||
|
||||
@@ -3,16 +3,17 @@ use super::storage_info::with_info;
|
||||
use crate::error::StorageValueError;
|
||||
use scale_decode::DecodeAsType;
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// This represents a storage value.
|
||||
pub struct StorageValue<'entry, 'atblock> {
|
||||
pub(crate) info: &'entry AnyStorageInfo<'atblock>,
|
||||
pub struct StorageValue<'atblock> {
|
||||
pub(crate) info: Arc<AnyStorageInfo<'atblock>>,
|
||||
bytes: Cow<'atblock, [u8]>,
|
||||
}
|
||||
|
||||
impl<'entry, 'atblock> StorageValue<'entry, 'atblock> {
|
||||
impl<'atblock> StorageValue<'atblock> {
|
||||
/// Create a new storage value.
|
||||
pub fn new(info: &'entry AnyStorageInfo<'atblock>, bytes: Cow<'atblock, [u8]>) -> Self {
|
||||
pub fn new(info: Arc<AnyStorageInfo<'atblock>>, bytes: Cow<'atblock, [u8]>) -> Self {
|
||||
Self { info, bytes }
|
||||
}
|
||||
|
||||
@@ -27,8 +28,8 @@ impl<'entry, 'atblock> StorageValue<'entry, 'atblock> {
|
||||
}
|
||||
|
||||
/// Decode this storage value.
|
||||
pub fn decode<T: DecodeAsType>(&self) -> Result<T, StorageValueError> {
|
||||
with_info!(info = &self.info => {
|
||||
pub fn decode_as<T: DecodeAsType>(&self) -> Result<T, StorageValueError> {
|
||||
with_info!(info = &*self.info => {
|
||||
let cursor = &mut &*self.bytes;
|
||||
|
||||
let value = T::decode_as_type(
|
||||
|
||||
Reference in New Issue
Block a user