fix: Convert vendor/pezkuwi-subxt from submodule to regular directory
This commit is contained in:
+12
@@ -0,0 +1,12 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
//! Types associated with accessing and working with storage items.
|
||||
|
||||
mod storage_client;
|
||||
mod storage_client_at;
|
||||
|
||||
pub use storage_client::StorageClient;
|
||||
pub use storage_client_at::{StorageClientAt, StorageEntryClient, StorageKeyValue, StorageValue};
|
||||
pub use pezkuwi_subxt_core::storage::address::{Address, DynamicAddress, StaticAddress, dynamic};
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::storage_client_at::StorageClientAt;
|
||||
use crate::{
|
||||
backend::BlockRef,
|
||||
client::{OfflineClientT, OnlineClientT},
|
||||
config::{Config, HashFor},
|
||||
error::StorageError,
|
||||
};
|
||||
use derive_where::derive_where;
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
use pezkuwi_subxt_core::storage::address::Address;
|
||||
|
||||
/// Query the runtime storage.
|
||||
#[derive_where(Clone; Client)]
|
||||
pub struct StorageClient<T, Client> {
|
||||
client: Client,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T, Client> StorageClient<T, Client> {
|
||||
/// Create a new [`StorageClient`]
|
||||
pub fn new(client: Client) -> Self {
|
||||
Self {
|
||||
client,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Client> StorageClient<T, Client>
|
||||
where
|
||||
T: Config,
|
||||
Client: OfflineClientT<T>,
|
||||
{
|
||||
/// Run the validation logic against some storage address you'd like to access. Returns `Ok(())`
|
||||
/// if the address is valid (or if it's not possible to check since the address has no validation hash).
|
||||
/// Return an error if the address was not valid or something went wrong trying to validate it (ie
|
||||
/// the pallet or storage entry in question do not exist at all).
|
||||
pub fn validate<Addr: Address>(&self, address: &Addr) -> Result<(), StorageError> {
|
||||
pezkuwi_subxt_core::storage::validate(address, &self.client.metadata()).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Client> StorageClient<T, Client>
|
||||
where
|
||||
T: Config,
|
||||
Client: OnlineClientT<T>,
|
||||
{
|
||||
/// Obtain storage at some block hash.
|
||||
pub fn at(&self, block_ref: impl Into<BlockRef<HashFor<T>>>) -> StorageClientAt<T, Client> {
|
||||
StorageClientAt::new(self.client.clone(), block_ref.into())
|
||||
}
|
||||
|
||||
/// Obtain storage at the latest finalized block.
|
||||
pub fn at_latest(
|
||||
&self,
|
||||
) -> impl Future<Output = Result<StorageClientAt<T, Client>, StorageError>> + Send + 'static
|
||||
{
|
||||
// Clone and pass the client in like this so that we can explicitly
|
||||
// return a Future that's Send + 'static, rather than tied to &self.
|
||||
let client = self.client.clone();
|
||||
async move {
|
||||
// get the ref for the latest finalized block and use that.
|
||||
let block_ref = client
|
||||
.backend()
|
||||
.latest_finalized_block_ref()
|
||||
.await
|
||||
.map_err(StorageError::CannotGetLatestFinalizedBlock)?;
|
||||
|
||||
Ok(StorageClientAt::new(client, block_ref))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{
|
||||
backend::{BackendExt, BlockRef},
|
||||
client::{OfflineClientT, OnlineClientT},
|
||||
config::{Config, HashFor},
|
||||
error::StorageError,
|
||||
};
|
||||
use derive_where::derive_where;
|
||||
use futures::StreamExt;
|
||||
use std::marker::PhantomData;
|
||||
use pezkuwi_subxt_core::Metadata;
|
||||
use pezkuwi_subxt_core::storage::{PrefixOf, address::Address};
|
||||
use pezkuwi_subxt_core::utils::{Maybe, Yes};
|
||||
|
||||
pub use pezkuwi_subxt_core::storage::{StorageKeyValue, StorageValue};
|
||||
|
||||
/// Query the runtime storage.
|
||||
#[derive_where(Clone; Client)]
|
||||
pub struct StorageClientAt<T: Config, Client> {
|
||||
client: Client,
|
||||
metadata: Metadata,
|
||||
block_ref: BlockRef<HashFor<T>>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T, Client> StorageClientAt<T, Client>
|
||||
where
|
||||
T: Config,
|
||||
Client: OfflineClientT<T>,
|
||||
{
|
||||
/// Create a new [`StorageClientAt`].
|
||||
pub(crate) fn new(client: Client, block_ref: BlockRef<HashFor<T>>) -> Self {
|
||||
// Retrieve and store metadata here so that we can borrow it in
|
||||
// subsequent structs, and thus also borrow storage info and
|
||||
// things that borrow from metadata.
|
||||
let metadata = client.metadata();
|
||||
|
||||
Self {
|
||||
client,
|
||||
metadata,
|
||||
block_ref,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Client> StorageClientAt<T, Client>
|
||||
where
|
||||
T: Config,
|
||||
Client: OfflineClientT<T>,
|
||||
{
|
||||
/// This returns a [`StorageEntryClient`], which allows working with the storage entry at the provided address.
|
||||
pub fn entry<Addr: Address>(
|
||||
&self,
|
||||
address: Addr,
|
||||
) -> Result<StorageEntryClient<'_, T, Client, Addr, Addr::IsPlain>, StorageError> {
|
||||
let inner = pezkuwi_subxt_core::storage::entry(address, &self.metadata)?;
|
||||
Ok(StorageEntryClient {
|
||||
inner,
|
||||
client: self.client.clone(),
|
||||
block_ref: self.block_ref.clone(),
|
||||
_marker: core::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Client> StorageClientAt<T, Client>
|
||||
where
|
||||
T: Config,
|
||||
Client: OnlineClientT<T>,
|
||||
{
|
||||
/// This is essentially a shorthand for `client.entry(addr)?.fetch(key_parts)`. See [`StorageEntryClient::fetch()`].
|
||||
pub async fn fetch<Addr: Address>(
|
||||
&self,
|
||||
addr: Addr,
|
||||
key_parts: Addr::KeyParts,
|
||||
) -> Result<StorageValue<'_, Addr::Value>, StorageError> {
|
||||
let entry = pezkuwi_subxt_core::storage::entry(addr, &self.metadata)?;
|
||||
fetch(&entry, &self.client, self.block_ref.hash(), key_parts).await
|
||||
}
|
||||
|
||||
/// This is essentially a shorthand for `client.entry(addr)?.try_fetch(key_parts)`. See [`StorageEntryClient::try_fetch()`].
|
||||
pub async fn try_fetch<Addr: Address>(
|
||||
&self,
|
||||
addr: Addr,
|
||||
key_parts: Addr::KeyParts,
|
||||
) -> Result<Option<StorageValue<'_, Addr::Value>>, StorageError> {
|
||||
let entry = pezkuwi_subxt_core::storage::entry(addr, &self.metadata)?;
|
||||
try_fetch(&entry, &self.client, self.block_ref.hash(), key_parts).await
|
||||
}
|
||||
|
||||
/// This is essentially a shorthand for `client.entry(addr)?.iter(key_parts)`. See [`StorageEntryClient::iter()`].
|
||||
pub async fn iter<Addr: Address, KeyParts: PrefixOf<Addr::KeyParts>>(
|
||||
&'_ self,
|
||||
addr: Addr,
|
||||
key_parts: KeyParts,
|
||||
) -> Result<
|
||||
impl futures::Stream<Item = Result<StorageKeyValue<'_, Addr>, StorageError>>
|
||||
+ use<'_, Addr, Client, T, KeyParts>,
|
||||
StorageError,
|
||||
> {
|
||||
let entry = pezkuwi_subxt_core::storage::entry(addr, &self.metadata)?;
|
||||
iter(entry, &self.client, self.block_ref.hash(), key_parts).await
|
||||
}
|
||||
|
||||
/// In rare cases, you may wish to fetch a storage value that does not live at a typical address. This method
|
||||
/// is a fallback for those cases, and allows you to provide the raw storage key bytes corresponding to the
|
||||
/// entry you wish to obtain. The response will either be the bytes for the value found at that location, or
|
||||
/// otherwise an error. [`StorageError::NoValueFound`] will be returned in the event that the request was valid
|
||||
/// but no value lives at the given location).
|
||||
pub async fn fetch_raw(&self, key_bytes: Vec<u8>) -> Result<Vec<u8>, StorageError> {
|
||||
let block_hash = self.block_ref.hash();
|
||||
let value = self
|
||||
.client
|
||||
.backend()
|
||||
.storage_fetch_value(key_bytes, block_hash)
|
||||
.await
|
||||
.map_err(StorageError::CannotFetchValue)?
|
||||
.ok_or(StorageError::NoValueFound)?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// The storage version of a pallet.
|
||||
/// The storage version refers to the `frame_support::traits::Metadata::StorageVersion` type.
|
||||
pub async fn storage_version(&self, pallet_name: impl AsRef<str>) -> Result<u16, StorageError> {
|
||||
// construct the storage key. This is done similarly in
|
||||
// `frame_support::traits::metadata::StorageVersion::storage_key()`:
|
||||
let mut key_bytes: Vec<u8> = vec![];
|
||||
key_bytes.extend(&pezsp_crypto_hashing::twox_128(
|
||||
pallet_name.as_ref().as_bytes(),
|
||||
));
|
||||
key_bytes.extend(&pezsp_crypto_hashing::twox_128(b":__STORAGE_VERSION__:"));
|
||||
|
||||
// fetch the raw bytes and decode them into the StorageVersion struct:
|
||||
let storage_version_bytes = self.fetch_raw(key_bytes).await?;
|
||||
|
||||
<u16 as codec::Decode>::decode(&mut &storage_version_bytes[..])
|
||||
.map_err(StorageError::CannotDecodeStorageVersion)
|
||||
}
|
||||
|
||||
/// Fetch the runtime WASM code.
|
||||
pub async fn runtime_wasm_code(&self) -> Result<Vec<u8>, StorageError> {
|
||||
// note: this should match the `CODE` constant in `sp_core::storage::well_known_keys`
|
||||
self.fetch_raw(b":code".to_vec()).await
|
||||
}
|
||||
}
|
||||
|
||||
/// 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, IsPlain> {
|
||||
inner: pezkuwi_subxt_core::storage::StorageEntry<'atblock, Addr>,
|
||||
client: Client,
|
||||
block_ref: BlockRef<HashFor<T>>,
|
||||
_marker: PhantomData<(T, IsPlain)>,
|
||||
}
|
||||
|
||||
impl<'atblock, T, Client, Addr, IsPlain> StorageEntryClient<'atblock, T, Client, Addr, IsPlain>
|
||||
where
|
||||
T: Config,
|
||||
Addr: Address,
|
||||
{
|
||||
/// Name of the pallet containing this storage entry.
|
||||
pub fn pallet_name(&self) -> &str {
|
||||
self.inner.pallet_name()
|
||||
}
|
||||
|
||||
/// Name of the storage entry.
|
||||
pub fn entry_name(&self) -> &str {
|
||||
self.inner.entry_name()
|
||||
}
|
||||
|
||||
/// Is the storage entry a plain value?
|
||||
pub fn is_plain(&self) -> bool {
|
||||
self.inner.is_plain()
|
||||
}
|
||||
|
||||
/// Is the storage entry a map?
|
||||
pub fn is_map(&self) -> bool {
|
||||
self.inner.is_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>> {
|
||||
self.inner.default_value()
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
Client: OnlineClientT<T>,
|
||||
{
|
||||
/// Fetch the storage value at this location. If no value is found, the default value will be returned
|
||||
/// for this entry if one exists. If no value is found and no default value exists, an error will be returned.
|
||||
pub async fn fetch(&self) -> Result<StorageValue<'atblock, Addr::Value>, StorageError> {
|
||||
let value = self.try_fetch().await?.map_or_else(
|
||||
|| self.inner.default_value().ok_or(StorageError::NoValueFound),
|
||||
Ok,
|
||||
)?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// Fetch the storage value at this location. If no value is found, `None` will be returned.
|
||||
pub async fn try_fetch(
|
||||
&self,
|
||||
) -> Result<Option<StorageValue<'atblock, Addr::Value>>, StorageError> {
|
||||
let value = self
|
||||
.client
|
||||
.backend()
|
||||
.storage_fetch_value(self.key_prefix().to_vec(), self.block_ref.hash())
|
||||
.await
|
||||
.map_err(StorageError::CannotFetchValue)?
|
||||
.map(|bytes| self.inner.value(bytes));
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// This is identical to [`StorageEntryClient::key_prefix()`] and is the full
|
||||
/// key for this storage entry.
|
||||
pub fn key(&self) -> [u8; 32] {
|
||||
self.inner.key_prefix()
|
||||
}
|
||||
|
||||
/// The keys for plain storage values are always 32 byte hashes.
|
||||
pub fn key_prefix(&self) -> [u8; 32] {
|
||||
self.inner.key_prefix()
|
||||
}
|
||||
}
|
||||
|
||||
// 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> StorageEntryClient<'atblock, T, Client, Addr, Maybe>
|
||||
where
|
||||
T: Config,
|
||||
Addr: Address,
|
||||
Client: OnlineClientT<T>,
|
||||
{
|
||||
/// Fetch a storage value within this storage entry.
|
||||
///
|
||||
/// This entry may be a map, and so you must provide the relevant values for each part of the storage
|
||||
/// key that is required in order to point to a single value.
|
||||
///
|
||||
/// If no value is found, the default value will be returned for this entry if one exists. If no value is
|
||||
/// found and no default value exists, an error will be returned.
|
||||
pub async fn fetch(
|
||||
&self,
|
||||
key_parts: Addr::KeyParts,
|
||||
) -> Result<StorageValue<'atblock, Addr::Value>, StorageError> {
|
||||
fetch(&self.inner, &self.client, self.block_ref.hash(), key_parts).await
|
||||
}
|
||||
|
||||
/// Fetch a storage value within this storage entry.
|
||||
///
|
||||
/// This entry may be a map, and so you must provide the relevant values for each part of the storage
|
||||
/// key that is required in order to point to a single value.
|
||||
///
|
||||
/// If no value is found, `None` will be returned.
|
||||
pub async fn try_fetch(
|
||||
&self,
|
||||
key_parts: Addr::KeyParts,
|
||||
) -> Result<Option<StorageValue<'atblock, Addr::Value>>, StorageError> {
|
||||
try_fetch(&self.inner, &self.client, self.block_ref.hash(), key_parts).await
|
||||
}
|
||||
|
||||
/// Iterate over storage values within this storage entry.
|
||||
///
|
||||
/// You may provide any prefix of the values needed to point to a single value. Normally you will
|
||||
/// provide `()` to iterate over _everything_, or `(first_key,)` to iterate over everything underneath
|
||||
/// `first_key` in the map, or `(first_key, second_key)` to iterate over everything underneath `first_key`
|
||||
/// and `second_key` in the map, and so on, up to the actual depth of the map - 1.
|
||||
pub async fn iter<KeyParts: PrefixOf<Addr::KeyParts>>(
|
||||
&self,
|
||||
key_parts: KeyParts,
|
||||
) -> Result<
|
||||
impl futures::Stream<Item = Result<StorageKeyValue<'atblock, Addr>, StorageError>>
|
||||
+ use<'atblock, Addr, Client, T, KeyParts>,
|
||||
StorageError,
|
||||
> {
|
||||
iter(
|
||||
self.inner.clone(),
|
||||
&self.client,
|
||||
self.block_ref.hash(),
|
||||
key_parts,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// This returns a full key to a single value in this storage entry.
|
||||
pub fn key(&self, key_parts: Addr::KeyParts) -> Result<Vec<u8>, StorageError> {
|
||||
let key = self.inner.fetch_key(key_parts)?;
|
||||
Ok(key)
|
||||
}
|
||||
|
||||
/// This returns valid keys to iterate over the storage entry at the available levels.
|
||||
pub fn iter_key<KeyParts: PrefixOf<Addr::KeyParts>>(
|
||||
&self,
|
||||
key_parts: KeyParts,
|
||||
) -> Result<Vec<u8>, StorageError> {
|
||||
let key = self.inner.iter_key(key_parts)?;
|
||||
Ok(key)
|
||||
}
|
||||
|
||||
/// 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] {
|
||||
self.inner.key_prefix()
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch<'atblock, T: Config, Client: OnlineClientT<T>, Addr: Address>(
|
||||
entry: &pezkuwi_subxt_core::storage::StorageEntry<'atblock, Addr>,
|
||||
client: &Client,
|
||||
block_hash: HashFor<T>,
|
||||
key_parts: Addr::KeyParts,
|
||||
) -> Result<StorageValue<'atblock, Addr::Value>, StorageError> {
|
||||
let value = try_fetch(entry, client, block_hash, key_parts)
|
||||
.await?
|
||||
.or_else(|| entry.default_value())
|
||||
.unwrap();
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
async fn try_fetch<'atblock, T: Config, Client: OnlineClientT<T>, Addr: Address>(
|
||||
entry: &pezkuwi_subxt_core::storage::StorageEntry<'atblock, Addr>,
|
||||
client: &Client,
|
||||
block_hash: HashFor<T>,
|
||||
key_parts: Addr::KeyParts,
|
||||
) -> Result<Option<StorageValue<'atblock, Addr::Value>>, StorageError> {
|
||||
let key = entry.fetch_key(key_parts)?;
|
||||
|
||||
let value = client
|
||||
.backend()
|
||||
.storage_fetch_value(key, block_hash)
|
||||
.await
|
||||
.map_err(StorageError::CannotFetchValue)?
|
||||
.map(|bytes| entry.value(bytes))
|
||||
.or_else(|| entry.default_value());
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
async fn iter<
|
||||
'atblock,
|
||||
T: Config,
|
||||
Client: OnlineClientT<T>,
|
||||
Addr: Address,
|
||||
KeyParts: PrefixOf<Addr::KeyParts>,
|
||||
>(
|
||||
entry: pezkuwi_subxt_core::storage::StorageEntry<'atblock, Addr>,
|
||||
client: &Client,
|
||||
block_hash: HashFor<T>,
|
||||
key_parts: KeyParts,
|
||||
) -> Result<
|
||||
impl futures::Stream<Item = Result<StorageKeyValue<'atblock, Addr>, StorageError>>
|
||||
+ use<'atblock, Addr, Client, T, KeyParts>,
|
||||
StorageError,
|
||||
> {
|
||||
let key_bytes = entry.iter_key(key_parts)?;
|
||||
|
||||
let stream = client
|
||||
.backend()
|
||||
.storage_fetch_descendant_values(key_bytes, block_hash)
|
||||
.await
|
||||
.map_err(StorageError::CannotIterateValues)?
|
||||
.map(move |kv| {
|
||||
let kv = match kv {
|
||||
Ok(kv) => kv,
|
||||
Err(e) => return Err(StorageError::StreamFailure(e)),
|
||||
};
|
||||
Ok(entry.key_value(kv.key, kv.value))
|
||||
});
|
||||
|
||||
Ok(Box::pin(stream))
|
||||
}
|
||||
Reference in New Issue
Block a user