Add DecodedValueThunk to allow getting bytes back from dynamic queries (#680)

This commit is contained in:
James Wilson
2022-10-06 09:58:01 +01:00
committed by GitHub
parent 81175b2dd9
commit 98af356644
5 changed files with 91 additions and 11 deletions
+4 -3
View File
@@ -51,7 +51,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 2. Dynamic constant access (the dynamic equivalent to the fetch_constants example).
let constant_address = subxt::dynamic::constant("Balances", "ExistentialDeposit");
let existential_deposit = api.constants().at(&constant_address)?;
let existential_deposit = api.constants().at(&constant_address)?.to_value()?;
println!("Existential Deposit: {}", existential_deposit);
// 3. Dynamic storage access
@@ -67,7 +67,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let account = api
.storage()
.fetch_or_default(&storage_address, None)
.await?;
.await?
.to_value()?;
println!("Bob's account details: {account}");
// 4. Dynamic storage iteration (the dynamic equivalent to the fetch_all_accounts example).
@@ -75,7 +76,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let storage_address = subxt::dynamic::storage_root("System", "Account");
let mut iter = api.storage().iter(storage_address, 10, None).await?;
while let Some((key, account)) = iter.next().await? {
println!("{}: {}", hex::encode(key), account);
println!("{}: {}", hex::encode(key), account.to_value()?);
}
Ok(())
+2 -2
View File
@@ -3,7 +3,7 @@
// see LICENSE for license details.
use crate::{
dynamic::DecodedValue,
dynamic::DecodedValueThunk,
metadata::DecodeWithMetadata,
};
use std::borrow::Cow;
@@ -97,7 +97,7 @@ pub fn dynamic<'a>(
}
impl<'a> ConstantAddress for DynamicConstantAddress<'a> {
type Target = DecodedValue;
type Target = DecodedValueThunk;
fn pallet_name(&self) -> &str {
&self.pallet_name
+55
View File
@@ -5,6 +5,14 @@
//! This module provides the entry points to create dynamic
//! transactions, storage and constant lookups.
use crate::{
error::Error,
metadata::{
DecodeWithMetadata,
Metadata,
},
};
pub use scale_value::Value;
/// A [`scale_value::Value`] type endowed with contextual information
@@ -24,3 +32,50 @@ pub use crate::storage::{
dynamic as storage,
dynamic_root as storage_root,
};
/// This is the result of making a dynamic request to a node. From this,
/// we can return the raw SCALE bytes that we were handed back, or we can
/// complete the decoding of the bytes into a [`DecodedValue`] type.
pub struct DecodedValueThunk {
type_id: u32,
metadata: Metadata,
scale_bytes: Vec<u8>,
}
impl DecodeWithMetadata for DecodedValueThunk {
type Target = Self;
fn decode_with_metadata(
bytes: &mut &[u8],
type_id: u32,
metadata: &Metadata,
) -> Result<Self::Target, Error> {
let mut v = Vec::with_capacity(bytes.len());
v.extend_from_slice(*bytes);
*bytes = &[];
Ok(DecodedValueThunk {
type_id,
metadata: metadata.clone(),
scale_bytes: v,
})
}
}
impl DecodedValueThunk {
/// Return the SCALE encoded bytes handed back from the node.
pub fn to_encoded(self) -> Vec<u8> {
self.scale_bytes
}
/// Return the SCALE encoded bytes handed back from the node without taking ownership of them.
pub fn encoded(&self) -> &[u8] {
&self.scale_bytes
}
/// Decode the SCALE encoded storage entry into a dynamic [`DecodedValue`] type.
pub fn to_value(&self) -> Result<DecodedValue, Error> {
DecodedValue::decode_with_metadata(
&mut &*self.scale_bytes,
self.type_id,
&self.metadata,
)
}
}
+2 -2
View File
@@ -5,7 +5,7 @@
use super::storage_map_key::StorageMapKey;
use crate::{
dynamic::{
DecodedValue,
DecodedValueThunk,
Value,
},
error::{
@@ -194,7 +194,7 @@ impl<'a, Encodable> StorageAddress for DynamicStorageAddress<'a, Encodable>
where
Encodable: EncodeWithMetadata,
{
type Target = DecodedValue;
type Target = DecodedValueThunk;
// For dynamic types, we have no static guarantees about any of
// this stuff, so we just allow it and let it fail at runtime:
@@ -164,11 +164,35 @@ async fn tx_dynamic_transfer() -> Result<(), subxt::Error> {
.fetch_or_default(&bob_account_addr, None)
.await?;
let alice_pre_free = alice_pre.at("data").at("free").unwrap().as_u128().unwrap();
let alice_post_free = alice_post.at("data").at("free").unwrap().as_u128().unwrap();
let alice_pre_free = alice_pre
.to_value()?
.at("data")
.at("free")
.unwrap()
.as_u128()
.unwrap();
let alice_post_free = alice_post
.to_value()?
.at("data")
.at("free")
.unwrap()
.as_u128()
.unwrap();
let bob_pre_free = bob_pre.at("data").at("free").unwrap().as_u128().unwrap();
let bob_post_free = bob_post.at("data").at("free").unwrap().as_u128().unwrap();
let bob_pre_free = bob_pre
.to_value()?
.at("data")
.at("free")
.unwrap()
.as_u128()
.unwrap();
let bob_post_free = bob_post
.to_value()?
.at("data")
.at("free")
.unwrap()
.as_u128()
.unwrap();
assert!(alice_pre_free - 10_000 >= alice_post_free);
assert_eq!(bob_pre_free + 10_000, bob_post_free);