mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 14:41:11 +00:00
Remove PlainorMap enum; plain and map values now use same struct to simplify usage
This commit is contained in:
+11
-16
@@ -7,7 +7,7 @@
|
||||
use alloc::borrow::Cow;
|
||||
use frame_decode::storage::IntoEncodableValues;
|
||||
use scale_decode::DecodeAsType;
|
||||
use crate::utils::{Maybe, YesNoMaybe};
|
||||
use crate::utils::{Maybe, YesMaybe};
|
||||
|
||||
/// A storage address. This allows access to a given storage entry, which can then
|
||||
/// be iterated over or fetched from by providing the relevant set of keys, or
|
||||
@@ -19,14 +19,10 @@ pub trait Address {
|
||||
type KeyParts: IntoEncodableValues;
|
||||
/// Type of the storage value at this location.
|
||||
type Value: DecodeAsType;
|
||||
/// Does the address have a default value defined for it.
|
||||
/// Set to [`crate::utils::Yes`] to enable APIs which require one,
|
||||
/// or [`crate::utils::Maybe`] to enable APIs which allow one
|
||||
type HasDefaultValue: YesNoMaybe;
|
||||
/// Does the address point to a map (as opposed to a plain value)?
|
||||
/// Does the address point to a plain value (as opposed to a map)?
|
||||
/// Set to [`crate::utils::Yes`] to enable APIs which require a map,
|
||||
/// or [`crate::utils::Maybe`] to enable APIs which allow a map.
|
||||
type IsMap;
|
||||
type IsPlain: YesMaybe;
|
||||
|
||||
/// The pallet containing this storage entry.
|
||||
fn pallet_name(&self) -> &str;
|
||||
@@ -39,14 +35,14 @@ pub trait Address {
|
||||
}
|
||||
|
||||
/// An address which is generated by the static APIs.
|
||||
pub struct StaticAddress<KeyParts, Value, HasDefaultValue, IsMap> {
|
||||
pub struct StaticAddress<KeyParts, Value, IsPlain> {
|
||||
pallet_name: Cow<'static, str>,
|
||||
entry_name: Cow<'static, str>,
|
||||
validation_hash: Option<[u8; 32]>,
|
||||
marker: core::marker::PhantomData<(KeyParts, Value, HasDefaultValue, IsMap)>,
|
||||
marker: core::marker::PhantomData<(KeyParts, Value, IsPlain)>,
|
||||
}
|
||||
|
||||
impl<KeyParts, Value, HasDefaultValue, IsMap> StaticAddress<KeyParts, Value, HasDefaultValue, IsMap> {
|
||||
impl<KeyParts, Value, IsPlain> StaticAddress<KeyParts, Value, IsPlain> {
|
||||
/// Create a new [`StaticAddress`] using static strings for the pallet and call name.
|
||||
/// This is only expected to be used from codegen.
|
||||
#[doc(hidden)]
|
||||
@@ -83,17 +79,16 @@ impl<KeyParts, Value, HasDefaultValue, IsMap> StaticAddress<KeyParts, Value, Has
|
||||
}
|
||||
}
|
||||
|
||||
impl<KeyParts, Value, HasDefaultValue, IsMap> Address
|
||||
for StaticAddress<KeyParts, Value, HasDefaultValue, IsMap>
|
||||
impl<KeyParts, Value, IsPlain> Address
|
||||
for StaticAddress<KeyParts, Value, IsPlain>
|
||||
where
|
||||
KeyParts: IntoEncodableValues,
|
||||
Value: DecodeAsType,
|
||||
HasDefaultValue: YesNoMaybe,
|
||||
IsPlain: YesMaybe,
|
||||
{
|
||||
type KeyParts = KeyParts;
|
||||
type Value = Value;
|
||||
type HasDefaultValue = HasDefaultValue;
|
||||
type IsMap = IsMap;
|
||||
type IsPlain = IsPlain;
|
||||
|
||||
fn pallet_name(&self) -> &str {
|
||||
&self.pallet_name
|
||||
@@ -110,7 +105,7 @@ where
|
||||
|
||||
/// A dynamic address is simply a [`StaticAddress`] which asserts that the
|
||||
/// entry *might* be a map and *might* have a default value.
|
||||
pub type DynamicAddress<KeyParts = Vec<scale_value::Value>, Value = scale_value::Value> = StaticAddress<KeyParts, Value, Maybe, Maybe>;
|
||||
pub type DynamicAddress<KeyParts = Vec<scale_value::Value>, Value = scale_value::Value> = StaticAddress<KeyParts, Value, Maybe>;
|
||||
|
||||
/// Construct a new dynamic storage address. You can define the type of the
|
||||
/// storage keys and value yourself here, but have no guarantee that they will
|
||||
|
||||
@@ -22,7 +22,7 @@ use alloc::vec::Vec;
|
||||
use codec::{Compact, Decode, Encode};
|
||||
use derive_where::derive_where;
|
||||
|
||||
pub use yesnomaybe::{Yes, No, Maybe, YesNoMaybe};
|
||||
pub use yesnomaybe::{Yes, Maybe, YesMaybe};
|
||||
pub use account_id::AccountId32;
|
||||
pub use account_id20::AccountId20;
|
||||
pub use era::Era;
|
||||
|
||||
@@ -6,26 +6,19 @@
|
||||
pub enum Yes {}
|
||||
/// A unit marker enum.
|
||||
pub enum Maybe {}
|
||||
/// A unit marker enum.
|
||||
pub enum No {}
|
||||
|
||||
/// This is implemented for [`Yes`], [`No`] and [`Maybe`] and
|
||||
/// This is implemented for [`Yes`] and [`Maybe`] and
|
||||
/// allows us to check at runtime which of these types is present.
|
||||
pub trait YesNoMaybe {
|
||||
pub trait YesMaybe {
|
||||
/// [`Yes`]
|
||||
fn is_yes() -> bool { false }
|
||||
/// [`No`]
|
||||
fn is_no() -> bool { false }
|
||||
/// [`Maybe`]
|
||||
fn is_maybe() -> bool { false }
|
||||
}
|
||||
|
||||
impl YesNoMaybe for Yes {
|
||||
impl YesMaybe for Yes {
|
||||
fn is_yes() -> bool { true }
|
||||
}
|
||||
impl YesNoMaybe for No {
|
||||
fn is_no() -> bool { true }
|
||||
}
|
||||
impl YesNoMaybe for Maybe {
|
||||
impl YesMaybe for Maybe {
|
||||
fn is_maybe() -> bool { true }
|
||||
}
|
||||
@@ -21,8 +21,7 @@ async fn main() -> Result<(), Error> {
|
||||
// We'll work the account balances at the given block, for this example.
|
||||
let account_balances = client_at_block
|
||||
.storage()
|
||||
.entry("System", "Account")?
|
||||
.into_map()?;
|
||||
.entry("System", "Account")?;
|
||||
|
||||
// We can see the default value for this entry at this block, if one exists.
|
||||
if let Some(default_value) = account_balances.default_value() {
|
||||
|
||||
+11
-6
@@ -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),
|
||||
@@ -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})"
|
||||
)]
|
||||
|
||||
+33
-156
@@ -5,7 +5,7 @@ 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 storage_info::AnyStorageInfo;
|
||||
@@ -54,23 +54,13 @@ where
|
||||
self.client.legacy_types(),
|
||||
)?;
|
||||
|
||||
if storage_info.is_map() {
|
||||
Ok(StorageEntryClient::Map(StorageEntryMapClient {
|
||||
client: self.client,
|
||||
pallet_name,
|
||||
entry_name,
|
||||
info: storage_info,
|
||||
marker: std::marker::PhantomData,
|
||||
}))
|
||||
} else {
|
||||
Ok(StorageEntryClient::Plain(StorageEntryPlainClient {
|
||||
client: self.client,
|
||||
pallet_name,
|
||||
entry_name,
|
||||
info: storage_info,
|
||||
marker: std::marker::PhantomData,
|
||||
}))
|
||||
}
|
||||
Ok(StorageEntryClient {
|
||||
client: self.client,
|
||||
pallet_name,
|
||||
entry_name,
|
||||
info: 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
|
||||
@@ -122,90 +112,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// 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: 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 entry_name(&self) -> &str {
|
||||
match self {
|
||||
StorageEntryClient::Plain(client) => &client.entry_name,
|
||||
StorageEntryClient::Map(client) => &client.entry_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(),
|
||||
entry_name: self.entry_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(),
|
||||
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,
|
||||
entry_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>,
|
||||
@@ -220,66 +136,13 @@ where
|
||||
&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_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())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'atblock, Client, T> StorageEntryPlainClient<'atblock, Client, T>
|
||||
where
|
||||
T: Config + 'atblock,
|
||||
Client: OnlineClientAtBlockT<'atblock, T>,
|
||||
{
|
||||
/// 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();
|
||||
let value = fetch(self.client, &key_bytes)
|
||||
.await?
|
||||
.map(|bytes| StorageValue::new(&self.info, Cow::Owned(bytes)))
|
||||
.or_else(|| self.default_value());
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// 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 entry_name = &*self.entry_name;
|
||||
|
||||
frame_decode::storage::encode_storage_key_prefix(pallet_name, entry_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,
|
||||
entry_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 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.
|
||||
@@ -292,7 +155,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'atblock, Client, T> StorageEntryMapClient<'atblock, Client, T>
|
||||
impl<'atblock, Client, T> StorageEntryClient<'atblock, Client, T>
|
||||
where
|
||||
T: Config + 'atblock,
|
||||
Client: OnlineClientAtBlockT<'atblock, T>,
|
||||
@@ -309,8 +172,9 @@ where
|
||||
info.info.keys.len()
|
||||
});
|
||||
|
||||
// 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::WrongNumberOfKeysProvided {
|
||||
return Err(StorageError::WrongNumberOfKeysProvidedForFetch {
|
||||
num_keys_provided: keys.num_encodable_values(),
|
||||
num_keys_expected: expected_num_keys,
|
||||
});
|
||||
@@ -338,6 +202,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)?;
|
||||
|
||||
|
||||
@@ -557,11 +557,7 @@ async fn wait_runtime_upgrade_in_finalized_block<T: Config>(
|
||||
)));
|
||||
};
|
||||
|
||||
let client = client
|
||||
.into_plain()
|
||||
.expect("System.LastRuntimeUpgrade should always be a plain storage entry");
|
||||
|
||||
let value = match client.try_fetch().await {
|
||||
let value = match client.try_fetch(()).await {
|
||||
Ok(Some(value)) => value,
|
||||
Ok(None) => return Some(Err(Error::Other(
|
||||
"The storage item `system::lastRuntimeUpgrade` should always exist (2)".to_string(),
|
||||
|
||||
@@ -15,8 +15,8 @@ use derive_where::derive_where;
|
||||
use futures::StreamExt;
|
||||
use scale_info::PortableRegistry;
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
use subxt_core::storage::{address::Address, PrefixOf};
|
||||
use subxt_core::utils::{Maybe, Yes, No, YesNoMaybe};
|
||||
use subxt_core::storage::{address::Address, PrefixOf, EqualOrPrefixOf};
|
||||
use subxt_core::utils::{Maybe, Yes, YesMaybe};
|
||||
use subxt_core::Metadata;
|
||||
use frame_decode::storage::{IntoEncodableValues, StorageInfo};
|
||||
use std::borrow::Cow;
|
||||
@@ -61,7 +61,7 @@ where
|
||||
T: Config,
|
||||
Client: OfflineClientT<T>,
|
||||
{
|
||||
pub fn entry<Addr: Address>(&'_ self, address: Addr) -> Result<StorageEntryClient<'_, T, Client, Addr, Addr::IsMap>, Error> {
|
||||
pub fn entry<Addr: Address>(&'_ self, address: Addr) -> Result<StorageEntryClient<'_, T, Client, Addr, Addr::IsPlain>, Error> {
|
||||
subxt_core::storage::validate(&address, &self.client.metadata())?;
|
||||
|
||||
use frame_decode::storage::StorageTypeInfo;
|
||||
@@ -71,174 +71,51 @@ where
|
||||
.metadata()
|
||||
.storage_info(address.pallet_name(), address.entry_name())?;
|
||||
|
||||
let value = if info.keys.is_empty() {
|
||||
StorageEntryClientValue::Plain(StorageEntryPlainClient {
|
||||
client: self.client.clone(),
|
||||
block_ref: self.block_ref.clone(),
|
||||
address,
|
||||
info,
|
||||
types,
|
||||
_marker: core::marker::PhantomData
|
||||
})
|
||||
} else {
|
||||
StorageEntryClientValue::Map(StorageEntryMapClient {
|
||||
client: self.client.clone(),
|
||||
block_ref: self.block_ref.clone(),
|
||||
address,
|
||||
info,
|
||||
types,
|
||||
_marker: core::marker::PhantomData
|
||||
})
|
||||
};
|
||||
|
||||
Ok(StorageEntryClient {
|
||||
value,
|
||||
marker: core::marker::PhantomData
|
||||
client: self.client.clone(),
|
||||
block_ref: self.block_ref.clone(),
|
||||
address,
|
||||
info,
|
||||
types,
|
||||
_marker: core::marker::PhantomData
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents a single storage entry (be it a plain value or map)
|
||||
/// and the operations that can be performed on it.
|
||||
pub struct StorageEntryClient<'atblock, T: Config, Client, Addr: Address, IsMap> {
|
||||
value: StorageEntryClientValue<'atblock, T, Client, Addr>,
|
||||
marker: core::marker::PhantomData<IsMap>,
|
||||
}
|
||||
|
||||
enum StorageEntryClientValue<'atblock, T: Config, Client, Addr: Address> {
|
||||
Plain(StorageEntryPlainClient<'atblock, T, Client, Addr, Addr::HasDefaultValue>),
|
||||
Map(StorageEntryMapClient<'atblock, T, Client, Addr, Addr::HasDefaultValue>),
|
||||
}
|
||||
|
||||
impl <'atblock, T: Config, Client, Addr: Address, IsMap> StorageEntryClient<'atblock, T, Client, Addr, IsMap> {
|
||||
pub fn pallet_name(&self) -> &str {
|
||||
match &self.value {
|
||||
StorageEntryClientValue::Plain(client) => client.pallet_name(),
|
||||
StorageEntryClientValue::Map(client) => client.pallet_name(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entry_name(&self) -> &str {
|
||||
match &self.value {
|
||||
StorageEntryClientValue::Plain(client) => client.entry_name(),
|
||||
StorageEntryClientValue::Map(client) => client.entry_name(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the storage entry a plain value?
|
||||
pub fn is_plain(&self) -> bool {
|
||||
matches!(self.value, StorageEntryClientValue::Plain(_))
|
||||
}
|
||||
|
||||
/// Is the storage entry a map?
|
||||
pub fn is_map(&self) -> bool {
|
||||
matches!(self.value, StorageEntryClientValue::Map(_))
|
||||
}
|
||||
|
||||
/// 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, Addr::Value>> {
|
||||
match &self.value {
|
||||
StorageEntryClientValue::Plain(client) => client.default_value(),
|
||||
StorageEntryClientValue::Map(client) => client.default_value(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When IsMap = Yes, we have statically asserted that the entry is a map. This can only be false
|
||||
// if we skip validation of a static call, and the storage entry, while still present, has changed from
|
||||
// plain to map.
|
||||
impl <'atblock, T: Config, Client, Addr: Address> StorageEntryClient<'atblock, T, Client, Addr, Yes> {
|
||||
pub fn into_map(self) -> StorageEntryMapClient<'atblock, T, Client, Addr, Addr::HasDefaultValue> {
|
||||
match self.value {
|
||||
StorageEntryClientValue::Map(this) => this,
|
||||
StorageEntryClientValue::Plain(this) => {
|
||||
tracing::warn!("StorageEntryClient::into_map called on a plain storage value");
|
||||
StorageEntryMapClient {
|
||||
client: this.client,
|
||||
block_ref: this.block_ref,
|
||||
address: this.address,
|
||||
info: this.info,
|
||||
types: this.types,
|
||||
_marker: this._marker
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When IsMap = No, we have statically asserted that the entry is a plain value. This can only be false
|
||||
// if we skip validation of a static call, and the storage entry, while still present, has changed from
|
||||
// map to plain value.
|
||||
impl <'atblock, T: Config, Client, Addr: Address> StorageEntryClient<'atblock, T, Client, Addr, No> {
|
||||
pub fn into_plain(self) -> StorageEntryPlainClient<'atblock, T, Client, Addr, Addr::HasDefaultValue> {
|
||||
match self.value {
|
||||
StorageEntryClientValue::Plain(this) => this,
|
||||
StorageEntryClientValue::Map(this) => {
|
||||
tracing::warn!("StorageEntryClient::into_plain called on a map storage value");
|
||||
StorageEntryPlainClient {
|
||||
client: this.client,
|
||||
block_ref: this.block_ref,
|
||||
address: this.address,
|
||||
info: this.info,
|
||||
types: this.types,
|
||||
_marker: this._marker
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regardless, we can do the "safe" thing and try to convert the entry into a map or plain entry.
|
||||
impl <'atblock, T: Config, Client, Addr: Address> StorageEntryClient<'atblock, T, Client, Addr, Maybe> {
|
||||
// TODO: In subxt-historic we return Result, not Option, with "StorageEntryIsNotAMapValue" like err.
|
||||
pub fn into_map(self) -> Option<StorageEntryMapClient<'atblock, T, Client, Addr, Addr::HasDefaultValue>> {
|
||||
match self.value {
|
||||
StorageEntryClientValue::Map(client) => Some(client),
|
||||
StorageEntryClientValue::Plain(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_plain(self) -> Option<StorageEntryPlainClient<'atblock, T, Client, Addr, Addr::HasDefaultValue>> {
|
||||
// TODO: In subxt-historic we return Result, not Option, with "StorageEntryIsNotAPlainValue" like err.
|
||||
match self.value {
|
||||
StorageEntryClientValue::Plain(client) => Some(client),
|
||||
StorageEntryClientValue::Map(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents a plain storage value at some location.
|
||||
pub struct StorageEntryPlainClient<'atblock, T: Config, Client, Addr, HasDefaultValue> {
|
||||
pub struct StorageEntryClient<'atblock, T: Config, Client, Addr, IsPlain> {
|
||||
client: Client,
|
||||
block_ref: BlockRef<HashFor<T>>,
|
||||
address: Addr,
|
||||
info: StorageInfo<'atblock, u32>,
|
||||
types: &'atblock PortableRegistry,
|
||||
_marker: PhantomData<(T, HasDefaultValue)>,
|
||||
_marker: PhantomData<(T, IsPlain)>,
|
||||
}
|
||||
|
||||
impl <'atblock, T, Client, Addr, HasDefaultValue> StorageEntryPlainClient<'atblock, T, Client, Addr, HasDefaultValue>
|
||||
impl <'atblock, T, Client, Addr, IsPlain> StorageEntryClient<'atblock, T, Client, Addr, IsPlain>
|
||||
where
|
||||
T: Config,
|
||||
Addr: Address,
|
||||
{
|
||||
/// Get the pallet name.
|
||||
/// Name of the pallet containing this storage entry.
|
||||
pub fn pallet_name(&self) -> &str {
|
||||
self.address.pallet_name()
|
||||
}
|
||||
|
||||
/// Get the storage entry name.
|
||||
/// Name of the storage entry.
|
||||
pub fn entry_name(&self) -> &str {
|
||||
self.address.entry_name()
|
||||
}
|
||||
|
||||
/// Return the key used to retrieve this storage value.
|
||||
pub fn key(&self) -> [u8; 32] {
|
||||
frame_decode::storage::encode_storage_key_prefix(
|
||||
self.address.pallet_name(),
|
||||
self.address.entry_name()
|
||||
)
|
||||
/// Is the storage entry a plain value?
|
||||
pub fn is_plain(&self) -> bool {
|
||||
self.info.keys.is_empty()
|
||||
}
|
||||
|
||||
/// Is the storage entry a map?
|
||||
pub fn is_map(&self) -> bool {
|
||||
!self.is_plain()
|
||||
}
|
||||
|
||||
/// Return the default value for this storage entry, if there is one. Returns `None` if there
|
||||
@@ -252,9 +129,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// When HasDefaultValue = Yes, we expect there to exist a valid default value and will use that
|
||||
// if we fetch an entry and get nothing back.
|
||||
impl <'atblock, T, Client, Addr> StorageEntryPlainClient<'atblock, T, Client, Addr, Yes>
|
||||
// Plain values get a fetch method with no extra arguments.
|
||||
impl <'atblock, T, Client, Addr> StorageEntryClient<'atblock, T, Client, Addr, Yes>
|
||||
where
|
||||
T: Config,
|
||||
Addr: Address,
|
||||
@@ -275,64 +151,29 @@ where
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl <'atblock, T, Client, Addr, HasDefaultValue> StorageEntryPlainClient<'atblock, T, Client, Addr, HasDefaultValue>
|
||||
where
|
||||
T: Config,
|
||||
Addr: Address,
|
||||
Client: OnlineClientT<T>
|
||||
{
|
||||
pub async fn try_fetch(&self) -> Result<Option<StorageValue<'_, 'atblock, Addr::Value>>, Error> {
|
||||
let value = self.client
|
||||
.backend()
|
||||
.storage_fetch_value(self.key().to_vec(), self.block_ref.hash())
|
||||
.storage_fetch_value(self.key_prefix().to_vec(), self.block_ref.hash())
|
||||
.await?
|
||||
.map(|bytes| StorageValue::new(&self.info, self.types, bytes));
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents a map of storage values at some location.
|
||||
pub struct StorageEntryMapClient<'atblock, T: Config, Client, Addr, HasDefaultValue> {
|
||||
client: Client,
|
||||
block_ref: BlockRef<HashFor<T>>,
|
||||
address: Addr,
|
||||
info: StorageInfo<'atblock, u32>,
|
||||
types: &'atblock PortableRegistry,
|
||||
_marker: PhantomData<(T, HasDefaultValue)>,
|
||||
}
|
||||
|
||||
impl <'atblock, T, Client, Addr, HasDefaultValue> StorageEntryMapClient<'atblock, T, Client, Addr, HasDefaultValue>
|
||||
where
|
||||
T: Config,
|
||||
Addr: Address,
|
||||
{
|
||||
/// Get the pallet name.
|
||||
pub fn pallet_name(&self) -> &str {
|
||||
self.address.pallet_name()
|
||||
}
|
||||
|
||||
/// Get the storage entry name.
|
||||
pub fn entry_name(&self) -> &str {
|
||||
self.address.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_value(&self) -> Option<StorageValue<'_, 'atblock, Addr::Value>> {
|
||||
if let Some(default_bytes) = self.info.default_value.as_deref() {
|
||||
Some(StorageValue::new(&self.info, self.types, Cow::Borrowed(default_bytes)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
/// The keys for plain storage values are always 32 byte hashes.
|
||||
pub fn key_prefix(&self) -> [u8; 32] {
|
||||
frame_decode::storage::encode_storage_key_prefix(
|
||||
self.address.pallet_name(),
|
||||
self.address.entry_name()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// When HasDefaultValue = Yes, we expect there to exist a valid default value and will use that
|
||||
// if we fetch an entry and get nothing back.
|
||||
impl <'atblock, T, Client, Addr> StorageEntryMapClient<'atblock, T, Client, Addr, Yes>
|
||||
impl <'atblock, T, Client, Addr> StorageEntryClient<'atblock, T, Client, Addr, Maybe>
|
||||
where
|
||||
T: Config,
|
||||
Addr: Address,
|
||||
@@ -352,14 +193,7 @@ where
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl <'atblock, T, Client, Addr, HasDefaultValue> StorageEntryMapClient<'atblock, T, Client, Addr, HasDefaultValue>
|
||||
where
|
||||
T: Config,
|
||||
Addr: Address,
|
||||
Client: OnlineClientT<T>
|
||||
{
|
||||
|
||||
pub async fn try_fetch(&self, keys: Addr::KeyParts) -> Result<Option<StorageValue<'_, 'atblock, Addr::Value>>, Error> {
|
||||
if keys.num_encodable_values() != self.info.keys.len() {
|
||||
// This shouldn't be possible in static cases but if Vec<Value> is keys then we need to be checking.
|
||||
@@ -384,14 +218,17 @@ where
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub async fn iter<Keys: PrefixOf<Addr::KeyParts>>(&self, keys: Keys) -> Result<StreamOf<Result<StorageEntry<'_, 'atblock, Addr>, Error>>, Error> {
|
||||
if keys.num_encodable_values() != self.info.keys.len() {
|
||||
pub async fn iter<Keys: PrefixOf<Addr::KeyParts>>(
|
||||
&self,
|
||||
keys: Keys
|
||||
) -> Result<impl futures::Stream<Item = Result<StorageEntry<'_, 'atblock, Addr>, Error>>, Error> {
|
||||
if keys.num_encodable_values() >= self.info.keys.len() {
|
||||
// This shouldn't be possible in static cases but if Vec<Value> is keys then we need to be checking.
|
||||
todo!("Error: wrong number of keys provided.")
|
||||
}
|
||||
|
||||
let block_hash = self.block_ref.hash();
|
||||
let key_bytes = self.key(keys)?;
|
||||
let key_bytes = self.key_from(keys)?;
|
||||
let info = &self.info;
|
||||
let types = self.types;
|
||||
|
||||
@@ -408,10 +245,28 @@ where
|
||||
Ok(StorageEntry::new(info, types, kv.key, Cow::Owned(kv.value)))
|
||||
});
|
||||
|
||||
Ok(StreamOf::new(Box::pin(stream)))
|
||||
Ok(Box::pin(stream))
|
||||
}
|
||||
|
||||
fn key<Keys: PrefixOf<Addr::KeyParts>>(&self, keys: Keys) -> Result<Vec<u8>, Error> {
|
||||
/// Keys for map values require additional values; you can provide [`Address::KeyParts`]
|
||||
/// values to construct a complete key, or a prefix of these to construct a key that would
|
||||
/// iterate over values.
|
||||
pub fn key<Keys: EqualOrPrefixOf<Addr::KeyParts>>(&self, keys: Keys) -> Result<Vec<u8>, Error> {
|
||||
self.key_from(keys)
|
||||
}
|
||||
|
||||
/// The first 32 bytes of the storage entry key, which points to the entry but not necessarily
|
||||
/// a single storage value (unless the entry is a plain value).
|
||||
pub fn key_prefix(&self) -> [u8; 32] {
|
||||
frame_decode::storage::encode_storage_key_prefix(
|
||||
self.address.pallet_name(),
|
||||
self.address.entry_name()
|
||||
)
|
||||
}
|
||||
|
||||
// An internal function to generate keys, because owing to lack of specialisation, things that impl
|
||||
// `EqualOrPrefixOf` don't also provably impl `PrefixOf`.
|
||||
fn key_from<Keys: IntoEncodableValues>(&self, keys: Keys) -> Result<Vec<u8>, Error> {
|
||||
let key_bytes = frame_decode::storage::encode_storage_key_with_info(
|
||||
&self.address.pallet_name(),
|
||||
&self.address.entry_name(),
|
||||
|
||||
Reference in New Issue
Block a user