Add storage_version() and runtime_wasm_code() to storage (#1111)

* add storage_version function

* get runtime code

* add tests

* clippy fix

* just support CODE, remove other well known keys

* remove u16 wrapper
This commit is contained in:
Tadeo Hepperle
2023-08-10 15:40:38 +02:00
committed by GitHub
parent b97acc5df6
commit 9723a50969
4 changed files with 72 additions and 4 deletions
+39 -1
View File
@@ -3,6 +3,7 @@
// see LICENSE for license details.
use super::storage_address::{StorageAddress, Yes};
use crate::{
client::OnlineClientT,
error::{Error, MetadataError},
@@ -10,6 +11,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 +239,42 @@ where
})
}
}
/// 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, Error> {
// 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<u8> = 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_else(|| {
format!(
"Unexpected: entry for storage version in pallet \"{}\" not found",
pallet_name.as_ref()
)
})?;
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<Vec<u8>, Error> {
// note: this should match the `CODE` constant in `sp_core::storage::well_known_keys`
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()
})
}
}
/// Iterates over key value pairs in a map.
@@ -338,7 +376,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());
+1 -1
View File
@@ -389,7 +389,7 @@ impl<T: Config, C: OnlineClientT<T>> TxInBlock<T, C> {
.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);
+2 -2
View File
@@ -83,7 +83,7 @@ impl<T> EncodeAsType for WrapperKeepOpaque<T> {
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<T> EncodeAsType for WrapperKeepOpaque<T> {
return Err(Error::new(ErrorKind::WrongShape {
actual: Kind::Struct,
expected: type_id,
}))
}));
};
// Check that the name also lines up.
@@ -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(())
}