From 391e0efc1a01c9d1fb6567f6d4a6251fc7917cbe Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Mon, 7 Aug 2023 19:58:38 +0200 Subject: [PATCH 1/6] add storage_version function --- subxt/src/storage/storage_type.rs | 31 +++++++++++++++++++++++++++++++ subxt/src/utils/mod.rs | 16 ++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/subxt/src/storage/storage_type.rs b/subxt/src/storage/storage_type.rs index 7beb923873..f3258c9995 100644 --- a/subxt/src/storage/storage_type.rs +++ b/subxt/src/storage/storage_type.rs @@ -3,6 +3,8 @@ // see LICENSE for license details. use super::storage_address::{StorageAddress, Yes}; + +use crate::utils::StorageVersion; use crate::{ client::OnlineClientT, error::{Error, MetadataError}, @@ -10,6 +12,7 @@ use crate::{ rpc::types::{StorageData, StorageKey}, Config, }; +use codec::Decode; use derivative::Derivative; use std::{future::Future, marker::PhantomData}; use subxt_metadata::{PalletMetadata, StorageEntryMetadata, StorageEntryType}; @@ -237,6 +240,34 @@ where }) } } + + /// the storage version of a pallet. Accessible via the magic key `:__STORAGE_VERSION__:`. + pub async fn storage_version( + &self, + pallet_name: impl AsRef, + ) -> Result { + // check that the pallet exists in the metadata: + self.client + .metadata() + .pallet_by_name(pallet_name.as_ref()) + .ok_or_else(|| MetadataError::PalletNameNotFound(pallet_name.as_ref().into()))?; + + // construct the storage key. This is done similarly in `frame_support::traits::metadata::StorageVersion::storage_key()`. + pub const STORAGE_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__STORAGE_VERSION__:"; + let mut key_bytes: Vec = vec![]; + key_bytes.extend(&sp_core_hashing::twox_128(pallet_name.as_ref().as_bytes())); + key_bytes.extend(&sp_core_hashing::twox_128( + STORAGE_VERSION_STORAGE_KEY_POSTFIX, + )); + + // fetch the raw bytes and decode them into the StorageVersion struct: + let storage_version_bytes = self + .fetch_raw(&key_bytes) + .await? + .ok_or(Error::Other("storage version not found".into()))?; + let storage_version = StorageVersion::decode(&mut &storage_version_bytes[..])?; + Ok(storage_version) + } } /// Iterates over key value pairs in a map. diff --git a/subxt/src/utils/mod.rs b/subxt/src/utils/mod.rs index a9965355cc..fca79ad806 100644 --- a/subxt/src/utils/mod.rs +++ b/subxt/src/utils/mod.rs @@ -74,3 +74,19 @@ unsafe impl Sync for PhantomDataSendSync {} /// with collections like BTreeMap. This has the same type params /// as `BTreeMap` which allows us to easily swap the two during codegen. pub type KeyedVec = Vec<(K, V)>; + +/// This is a simplified version of Substrate's +/// `frame_support::traits::Metadata::StorageVersion`. +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + Debug, + scale_encode::EncodeAsType, + scale_decode::DecodeAsType, +)] +pub struct StorageVersion(u16); From 2791ea1cafff1ac7dbf9c04bf9553034a7713ceb Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Tue, 8 Aug 2023 10:42:35 +0200 Subject: [PATCH 2/6] get runtime code --- subxt/src/storage/mod.rs | 1 + subxt/src/storage/storage_type.rs | 22 +++++++++++---- subxt/src/storage/utils.rs | 47 +++++++++++++++++++++++++++++++ subxt/src/tx/tx_progress.rs | 2 +- subxt/src/utils/wrapper_opaque.rs | 4 +-- 5 files changed, 67 insertions(+), 9 deletions(-) diff --git a/subxt/src/storage/mod.rs b/subxt/src/storage/mod.rs index 8fac6bfca0..b4f5ace55f 100644 --- a/subxt/src/storage/mod.rs +++ b/subxt/src/storage/mod.rs @@ -11,6 +11,7 @@ mod storage_type; pub mod utils; pub use storage_client::StorageClient; +pub use utils::well_known_keys; pub use storage_type::{KeyIter, Storage}; diff --git a/subxt/src/storage/storage_type.rs b/subxt/src/storage/storage_type.rs index f3258c9995..ef85f23cdf 100644 --- a/subxt/src/storage/storage_type.rs +++ b/subxt/src/storage/storage_type.rs @@ -4,6 +4,7 @@ use super::storage_address::{StorageAddress, Yes}; +use crate::storage::well_known_keys; use crate::utils::StorageVersion; use crate::{ client::OnlineClientT, @@ -241,7 +242,7 @@ where } } - /// the storage version of a pallet. Accessible via the magic key `:__STORAGE_VERSION__:`. + /// the storage version of a pallet. pub async fn storage_version( &self, pallet_name: impl AsRef, @@ -261,13 +262,22 @@ where )); // fetch the raw bytes and decode them into the StorageVersion struct: - let storage_version_bytes = self - .fetch_raw(&key_bytes) - .await? - .ok_or(Error::Other("storage version not found".into()))?; + let storage_version_bytes = self.fetch_raw(&key_bytes).await?.ok_or(format!( + "Unexpected: entry for storage version in pallet \"{}\" not found", + pallet_name.as_ref() + ))?; let storage_version = StorageVersion::decode(&mut &storage_version_bytes[..])?; Ok(storage_version) } + + /// fetches the Wasm code of the runtime. + pub async fn runtime_wasm_code(&self) -> Result, Error> { + let key = well_known_keys::CODE; + let data = self.fetch_raw(key.key()).await?.ok_or(format!( + "Unexpected: entry for well known key \"{key}\" not found" + ))?; + Ok(data) + } } /// Iterates over key value pairs in a map. @@ -369,7 +379,7 @@ fn validate_storage( hash: [u8; 32], ) -> Result<(), Error> { let Some(expected_hash) = pallet.storage_hash(storage_name) else { - return Err(MetadataError::IncompatibleCodegen.into()) + return Err(MetadataError::IncompatibleCodegen.into()); }; if expected_hash != hash { return Err(MetadataError::IncompatibleCodegen.into()); diff --git a/subxt/src/storage/utils.rs b/subxt/src/storage/utils.rs index 728a581baf..88181b28b9 100644 --- a/subxt/src/storage/utils.rs +++ b/subxt/src/storage/utils.rs @@ -37,3 +37,50 @@ pub(crate) fn storage_address_root_bytes(addr: &Address write_storage_address_root_bytes(addr, &mut bytes); bytes } + +/// this should match the constants in the module `sp_core::storage::well_known_keys` +pub mod well_known_keys { + use std::fmt::Formatter; + use std::marker::PhantomData; + + /// Wasm code of the runtime. + pub const CODE: WellKnownKey> = key(b":code"); + + /// Number of wasm linear memory pages required for execution of the runtime. + // note: currently untested, does not seem to work on polkadot + const _HEAP_PAGES: WellKnownKey = key(b":heappages"); + + /// Current extrinsic index (u32) is stored under this key. + // note: currently untested, does not seem to work on polkadot + const _EXTRINSIC_INDEX: WellKnownKey = key(b":extrinsic_index"); + + /// Current intra-block entropy (a universally unique `[u8; 32]` value) is stored here. + // note: currently untested, does not seem to work on polkadot + const _INTRABLOCK_ENTROPY: WellKnownKey<[u8; 32]> = key(b":intrablock_entropy"); + + const fn key(key: &'static [u8]) -> WellKnownKey { + WellKnownKey { + key, + _phantom: PhantomData, + } + } + + /// a static key that every substrate node should contain a storage entry for. + /// The static key bytes can be given to the `fetch_raw()` storage call directly, no hashing needed. + /// The `R` type parameter is the return type of the storage entry. Should implement `Decode`. + pub struct WellKnownKey { + key: &'static [u8], + _phantom: PhantomData, + } + impl WellKnownKey { + /// the inner key (some static bytes) + pub fn key(&self) -> &'static [u8] { + self.key + } + } + impl std::fmt::Display for WellKnownKey { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(std::str::from_utf8(self.key).expect("only defined here in crate; qed")) + } + } +} diff --git a/subxt/src/tx/tx_progress.rs b/subxt/src/tx/tx_progress.rs index 440ed10cb1..392a197f35 100644 --- a/subxt/src/tx/tx_progress.rs +++ b/subxt/src/tx/tx_progress.rs @@ -389,7 +389,7 @@ impl> TxInBlock { .iter() .position(|ext| { use crate::config::Hasher; - let Ok((_,stripped)) = strip_compact_prefix(&ext.0) else { + let Ok((_, stripped)) = strip_compact_prefix(&ext.0) else { return false; }; let hash = T::Hasher::hash_of(&stripped); diff --git a/subxt/src/utils/wrapper_opaque.rs b/subxt/src/utils/wrapper_opaque.rs index 982a551094..9257405715 100644 --- a/subxt/src/utils/wrapper_opaque.rs +++ b/subxt/src/utils/wrapper_opaque.rs @@ -83,7 +83,7 @@ impl EncodeAsType for WrapperKeepOpaque { use scale_encode::error::{Error, ErrorKind, Kind}; let Some(ty) = types.resolve(type_id) else { - return Err(Error::new(ErrorKind::TypeNotFound(type_id))) + return Err(Error::new(ErrorKind::TypeNotFound(type_id))); }; // Do a basic check that the target shape lines up. @@ -91,7 +91,7 @@ impl EncodeAsType for WrapperKeepOpaque { return Err(Error::new(ErrorKind::WrongShape { actual: Kind::Struct, expected: type_id, - })) + })); }; // Check that the name also lines up. From 6e3f8f5b6544cb1f76d859d505b338b7e1d10fea Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Tue, 8 Aug 2023 10:55:40 +0200 Subject: [PATCH 3/6] add tests --- .../src/full_client/storage/mod.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/testing/integration-tests/src/full_client/storage/mod.rs b/testing/integration-tests/src/full_client/storage/mod.rs index 125d44a0a2..4ef438b0c7 100644 --- a/testing/integration-tests/src/full_client/storage/mod.rs +++ b/testing/integration-tests/src/full_client/storage/mod.rs @@ -125,3 +125,33 @@ async fn storage_n_map_storage_lookup() -> Result<(), subxt::Error> { assert_eq!(entry.map(|a| a.amount), Some(123)); Ok(()) } + +#[tokio::test] +async fn storage_runtime_wasm_code() -> Result<(), subxt::Error> { + let ctx = test_context().await; + let api = ctx.client(); + let wasm_blob = api.storage().at_latest().await?.runtime_wasm_code().await?; + assert!(wasm_blob.len() > 1000); // the wasm should be super big + Ok(()) +} + +#[tokio::test] +async fn storage_pallet_storage_version() -> Result<(), subxt::Error> { + let ctx = test_context().await; + let api = ctx.client(); + + // cannot assume anything about version number, but should work to fetch it + let version = api + .storage() + .at_latest() + .await? + .storage_version("System") + .await?; + let version = api + .storage() + .at_latest() + .await? + .storage_version("Balances") + .await?; + Ok(()) +} From 49c2a6e48e0542b50aa9edd8669a7ba274b9cb16 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Tue, 8 Aug 2023 11:10:48 +0200 Subject: [PATCH 4/6] clippy fix --- testing/integration-tests/src/full_client/storage/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/integration-tests/src/full_client/storage/mod.rs b/testing/integration-tests/src/full_client/storage/mod.rs index 4ef438b0c7..dc697d6267 100644 --- a/testing/integration-tests/src/full_client/storage/mod.rs +++ b/testing/integration-tests/src/full_client/storage/mod.rs @@ -141,13 +141,13 @@ async fn storage_pallet_storage_version() -> Result<(), subxt::Error> { let api = ctx.client(); // cannot assume anything about version number, but should work to fetch it - let version = api + let _version = api .storage() .at_latest() .await? .storage_version("System") .await?; - let version = api + let _version = api .storage() .at_latest() .await? From 3760b133dcd91c45f6cd0bd3c6020ee27a754b8b Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Tue, 8 Aug 2023 15:21:37 +0200 Subject: [PATCH 5/6] just support CODE, remove other well known keys --- subxt/src/storage/mod.rs | 1 - subxt/src/storage/storage_type.rs | 23 ++++++++------- subxt/src/storage/utils.rs | 47 ------------------------------- 3 files changed, 13 insertions(+), 58 deletions(-) diff --git a/subxt/src/storage/mod.rs b/subxt/src/storage/mod.rs index b4f5ace55f..8fac6bfca0 100644 --- a/subxt/src/storage/mod.rs +++ b/subxt/src/storage/mod.rs @@ -11,7 +11,6 @@ mod storage_type; pub mod utils; pub use storage_client::StorageClient; -pub use utils::well_known_keys; pub use storage_type::{KeyIter, Storage}; diff --git a/subxt/src/storage/storage_type.rs b/subxt/src/storage/storage_type.rs index ef85f23cdf..1e9f0ac3b4 100644 --- a/subxt/src/storage/storage_type.rs +++ b/subxt/src/storage/storage_type.rs @@ -4,7 +4,6 @@ use super::storage_address::{StorageAddress, Yes}; -use crate::storage::well_known_keys; use crate::utils::StorageVersion; use crate::{ client::OnlineClientT, @@ -242,7 +241,7 @@ where } } - /// the storage version of a pallet. + /// The storage version of a pallet. pub async fn storage_version( &self, pallet_name: impl AsRef, @@ -262,19 +261,23 @@ where )); // fetch the raw bytes and decode them into the StorageVersion struct: - let storage_version_bytes = self.fetch_raw(&key_bytes).await?.ok_or(format!( - "Unexpected: entry for storage version in pallet \"{}\" not found", - pallet_name.as_ref() - ))?; + let storage_version_bytes = self.fetch_raw(&key_bytes).await?.ok_or_else(|| { + format!( + "Unexpected: entry for storage version in pallet \"{}\" not found", + pallet_name.as_ref() + ) + })?; let storage_version = StorageVersion::decode(&mut &storage_version_bytes[..])?; Ok(storage_version) } - /// fetches the Wasm code of the runtime. + /// Fetches the Wasm code of the runtime. pub async fn runtime_wasm_code(&self) -> Result, Error> { - let key = well_known_keys::CODE; - let data = self.fetch_raw(key.key()).await?.ok_or(format!( - "Unexpected: entry for well known key \"{key}\" not found" + // note: this should match the `CODE` constant in `sp_core::storage::well_known_keys` + const CODE: &[u8] = b":code"; + let data = self.fetch_raw(CODE).await?.ok_or(format!( + "Unexpected: entry for well known key \"{}\" not found", + std::str::from_utf8(CODE).expect("qed;") ))?; Ok(data) } diff --git a/subxt/src/storage/utils.rs b/subxt/src/storage/utils.rs index 88181b28b9..728a581baf 100644 --- a/subxt/src/storage/utils.rs +++ b/subxt/src/storage/utils.rs @@ -37,50 +37,3 @@ pub(crate) fn storage_address_root_bytes(addr: &Address write_storage_address_root_bytes(addr, &mut bytes); bytes } - -/// this should match the constants in the module `sp_core::storage::well_known_keys` -pub mod well_known_keys { - use std::fmt::Formatter; - use std::marker::PhantomData; - - /// Wasm code of the runtime. - pub const CODE: WellKnownKey> = key(b":code"); - - /// Number of wasm linear memory pages required for execution of the runtime. - // note: currently untested, does not seem to work on polkadot - const _HEAP_PAGES: WellKnownKey = key(b":heappages"); - - /// Current extrinsic index (u32) is stored under this key. - // note: currently untested, does not seem to work on polkadot - const _EXTRINSIC_INDEX: WellKnownKey = key(b":extrinsic_index"); - - /// Current intra-block entropy (a universally unique `[u8; 32]` value) is stored here. - // note: currently untested, does not seem to work on polkadot - const _INTRABLOCK_ENTROPY: WellKnownKey<[u8; 32]> = key(b":intrablock_entropy"); - - const fn key(key: &'static [u8]) -> WellKnownKey { - WellKnownKey { - key, - _phantom: PhantomData, - } - } - - /// a static key that every substrate node should contain a storage entry for. - /// The static key bytes can be given to the `fetch_raw()` storage call directly, no hashing needed. - /// The `R` type parameter is the return type of the storage entry. Should implement `Decode`. - pub struct WellKnownKey { - key: &'static [u8], - _phantom: PhantomData, - } - impl WellKnownKey { - /// the inner key (some static bytes) - pub fn key(&self) -> &'static [u8] { - self.key - } - } - impl std::fmt::Display for WellKnownKey { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(std::str::from_utf8(self.key).expect("only defined here in crate; qed")) - } - } -} From 13cf18448ff98f246ec3c73cec6b87853eb82729 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Wed, 9 Aug 2023 19:02:05 +0200 Subject: [PATCH 6/6] remove u16 wrapper --- subxt/src/storage/storage_type.rs | 20 +++++++------------- subxt/src/utils/mod.rs | 16 ---------------- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/subxt/src/storage/storage_type.rs b/subxt/src/storage/storage_type.rs index 1e9f0ac3b4..1c510428bc 100644 --- a/subxt/src/storage/storage_type.rs +++ b/subxt/src/storage/storage_type.rs @@ -4,7 +4,6 @@ use super::storage_address::{StorageAddress, Yes}; -use crate::utils::StorageVersion; use crate::{ client::OnlineClientT, error::{Error, MetadataError}, @@ -242,10 +241,8 @@ where } /// The storage version of a pallet. - pub async fn storage_version( - &self, - pallet_name: impl AsRef, - ) -> Result { + /// The storage version refers to the `frame_support::traits::Metadata::StorageVersion` type. + pub async fn storage_version(&self, pallet_name: impl AsRef) -> Result { // check that the pallet exists in the metadata: self.client .metadata() @@ -267,19 +264,16 @@ where pallet_name.as_ref() ) })?; - let storage_version = StorageVersion::decode(&mut &storage_version_bytes[..])?; - Ok(storage_version) + u16::decode(&mut &storage_version_bytes[..]).map_err(Into::into) } /// Fetches the Wasm code of the runtime. pub async fn runtime_wasm_code(&self) -> Result, Error> { // note: this should match the `CODE` constant in `sp_core::storage::well_known_keys` - const CODE: &[u8] = b":code"; - let data = self.fetch_raw(CODE).await?.ok_or(format!( - "Unexpected: entry for well known key \"{}\" not found", - std::str::from_utf8(CODE).expect("qed;") - ))?; - Ok(data) + const CODE: &str = ":code"; + self.fetch_raw(CODE.as_bytes()).await?.ok_or_else(|| { + format!("Unexpected: entry for well known key \"{CODE}\" not found").into() + }) } } diff --git a/subxt/src/utils/mod.rs b/subxt/src/utils/mod.rs index 721c1ce16a..d216c2d761 100644 --- a/subxt/src/utils/mod.rs +++ b/subxt/src/utils/mod.rs @@ -76,19 +76,3 @@ unsafe impl Sync for PhantomDataSendSync {} /// with collections like BTreeMap. This has the same type params /// as `BTreeMap` which allows us to easily swap the two during codegen. pub type KeyedVec = Vec<(K, V)>; - -/// This is a simplified version of Substrate's -/// `frame_support::traits::Metadata::StorageVersion`. -#[derive( - Clone, - Eq, - PartialEq, - Ord, - PartialOrd, - Encode, - Decode, - Debug, - scale_encode::EncodeAsType, - scale_decode::DecodeAsType, -)] -pub struct StorageVersion(u16);