diff --git a/Cargo.lock b/Cargo.lock index cdce675a26..1f1e3ac848 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1931,9 +1931,9 @@ dependencies = [ [[package]] name = "frame-decode" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c470df86cf28818dd3cd2fc4667b80dbefe2236c722c3dc1d09e7c6c82d6dfcd" +checksum = "641e3739fa708a278d35b008a05244008c221240abc3e1c27138466c13e999ed" dependencies = [ "frame-metadata 23.0.0", "parity-scale-codec", diff --git a/Cargo.toml b/Cargo.toml index d4c74591fc..70acb0a06e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,7 +81,7 @@ darling = "0.20.10" derive-where = "1.2.7" either = { version = "1.13.0", default-features = false } finito = { version = "0.1.0", default-features = false } -frame-decode = { version = "0.9.0", default-features = false } +frame-decode = { version = "0.10.0", default-features = false } frame-metadata = { version = "23.0.0", default-features = false } futures = { version = "0.3.31", default-features = false, features = ["std"] } getrandom = { version = "0.2", default-features = false } diff --git a/historic/examples/storage.rs b/historic/examples/storage.rs index fb4253a33d..2798b73c89 100644 --- a/historic/examples/storage.rs +++ b/historic/examples/storage.rs @@ -24,6 +24,12 @@ async fn main() -> Result<(), Error> { .entry("System", "Account")? .into_map()?; + // We can see the default value for this entry at this block, if one exists. + if let Some(default_value) = account_balances.default() { + let default_balance_info = default_value.decode::()?; + println!(" Default balance info: {default_balance_info}"); + } + // We can fetch a specific account balance by its key, like so (here I just picked a random key // I knew to exist from iterating over storage entries): let account_id_hex = "9a4d0faa2ba8c3cc5711852960940793acf55bf195b6eecf88fa78e961d0ce4a"; diff --git a/historic/src/storage.rs b/historic/src/storage.rs index 3cb81f17b7..14496445fa 100644 --- a/historic/src/storage.rs +++ b/historic/src/storage.rs @@ -7,6 +7,7 @@ use crate::client::{OfflineClientAtBlockT, OnlineClientAtBlockT}; use crate::config::Config; use crate::error::{StorageEntryIsNotAMap, StorageEntryIsNotAPlainValue, StorageError}; use crate::storage::storage_info::with_info; +use std::borrow::Cow; use storage_info::AnyStorageInfo; pub use storage_entry::StorageEntry; @@ -208,6 +209,16 @@ where pub fn storage_name(&self) -> &str { &self.storage_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> { + 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> @@ -220,7 +231,17 @@ where let key_bytes = self.key(); fetch(self.client, &key_bytes) .await - .map(|v| v.map(|bytes| StorageValue::new(&self.info, bytes))) + .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>, StorageError> { + self.fetch() + .await + .map(|option_val| option_val.or_else(|| self.default())) } /// The key for this storage entry. @@ -255,6 +276,16 @@ where pub fn storage_name(&self) -> &str { &self.storage_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> { + 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> StorageEntryMapClient<'atblock, Client, T> @@ -283,7 +314,18 @@ where let key_bytes = self.key(keys)?; fetch(self.client, &key_bytes) .await - .map(|v| v.map(|bytes| StorageValue::new(&self.info, bytes))) + .map(|v| v.map(|bytes| StorageValue::new(&self.info, Cow::Owned(bytes)))) + } + + /// 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( + &self, + keys: Keys, + ) -> Result>, StorageError> { + self.fetch(keys) + .await + .map(|option_val| option_val.or_else(|| self.default())) } /// Iterate over the values underneath the provided keys. @@ -324,8 +366,13 @@ where Err(e) => return Some(Err(StorageError::RpcError { reason: e })), }; - item.value - .map(|value| Ok(StorageEntry::new(&self.info, item.key.0, value.0))) + item.value.map(|value| { + Ok(StorageEntry::new( + &self.info, + item.key.0, + Cow::Owned(value.0), + )) + }) }); Ok(Box::pin(sub)) diff --git a/historic/src/storage/storage_entry.rs b/historic/src/storage/storage_entry.rs index c777013311..736b092fb0 100644 --- a/historic/src/storage/storage_entry.rs +++ b/historic/src/storage/storage_entry.rs @@ -3,6 +3,7 @@ use super::storage_key::StorageKey; use super::storage_value::StorageValue; use crate::error::{StorageKeyError, StorageValueError}; use scale_decode::DecodeAsType; +use std::borrow::Cow; /// This represents a storage entry, which is a key-value pair in the storage. pub struct StorageEntry<'entry, 'atblock> { @@ -13,7 +14,11 @@ pub struct StorageEntry<'entry, 'atblock> { impl<'entry, 'atblock> StorageEntry<'entry, 'atblock> { /// Create a new storage entry. - pub fn new(info: &'entry AnyStorageInfo<'atblock>, key: Vec, value: Vec) -> Self { + pub fn new( + info: &'entry AnyStorageInfo<'atblock>, + key: Vec, + value: Cow<'atblock, [u8]>, + ) -> Self { Self { key, value: StorageValue::new(info, value), diff --git a/historic/src/storage/storage_info.rs b/historic/src/storage/storage_info.rs index 7b2b2174df..4c660890b3 100644 --- a/historic/src/storage/storage_info.rs +++ b/historic/src/storage/storage_info.rs @@ -85,7 +85,7 @@ impl<'atblock> From> } pub struct StorageInfo<'atblock, TypeId, Resolver> { - pub info: frame_decode::storage::StorageInfo, + pub info: frame_decode::storage::StorageInfo<'atblock, TypeId>, pub resolver: &'atblock Resolver, } diff --git a/historic/src/storage/storage_value.rs b/historic/src/storage/storage_value.rs index 4afd00d5cc..f4f2704e50 100644 --- a/historic/src/storage/storage_value.rs +++ b/historic/src/storage/storage_value.rs @@ -2,16 +2,17 @@ use super::storage_info::AnyStorageInfo; use super::storage_info::with_info; use crate::error::StorageValueError; use scale_decode::DecodeAsType; +use std::borrow::Cow; /// This represents a storage value. pub struct StorageValue<'entry, 'atblock> { pub(crate) info: &'entry AnyStorageInfo<'atblock>, - bytes: Vec, + bytes: Cow<'atblock, [u8]>, } impl<'entry, 'atblock> StorageValue<'entry, 'atblock> { /// Create a new storage value. - pub fn new(info: &'entry AnyStorageInfo<'atblock>, bytes: Vec) -> Self { + pub fn new(info: &'entry AnyStorageInfo<'atblock>, bytes: Cow<'atblock, [u8]>) -> Self { Self { info, bytes } } @@ -22,7 +23,7 @@ impl<'entry, 'atblock> StorageValue<'entry, 'atblock> { /// Consume this storage value and return the raw bytes. pub fn into_bytes(self) -> Vec { - self.bytes + self.bytes.to_vec() } /// Decode this storage value. diff --git a/testing/integration-tests/src/full_client/blocks.rs b/testing/integration-tests/src/full_client/blocks.rs index 99125429c8..beb4b6042f 100644 --- a/testing/integration-tests/src/full_client/blocks.rs +++ b/testing/integration-tests/src/full_client/blocks.rs @@ -207,7 +207,7 @@ async fn fetch_block_and_decode_extrinsic_details() { let block = api.blocks().at(block_hash).await.unwrap(); // Ensure that we can clone the block. - block.clone(); + let _ = block.clone(); let extrinsics = block.extrinsics().await.unwrap(); @@ -295,8 +295,7 @@ async fn submit_extrinsic_and_get_it_back( let block_hash = in_block.block_hash(); let block = api.blocks().at(block_hash).await.unwrap(); let extrinsics = block.extrinsics().await.unwrap(); - let extrinsic_details = extrinsics.iter().find(|e| e.is_signed()).unwrap(); - extrinsic_details + extrinsics.iter().find(|e| e.is_signed()).unwrap() } #[cfg(fullclient)]