From 4531532c247dab59407f59aa2c8aafdfe918596f Mon Sep 17 00:00:00 2001 From: s0c5 Date: Sun, 18 Aug 2024 19:36:40 +0200 Subject: [PATCH] refactor: Update method names to improve clarity --- sube/examples/query_at_block.rs | 12 ++- sube/src/builder.rs | 33 +++++---- sube/src/lib.rs | 125 ++++++++++++++++++-------------- sube/src/rpc.rs | 76 ++++++++----------- 4 files changed, 129 insertions(+), 117 deletions(-) diff --git a/sube/examples/query_at_block.rs b/sube/examples/query_at_block.rs index 90f0085..4a1e758 100644 --- a/sube/examples/query_at_block.rs +++ b/sube/examples/query_at_block.rs @@ -6,7 +6,17 @@ use sube::{sube, ExtrinsicBody, Response, Result, SubeBuilder}; async fn main() -> Result<()> { env_logger::init(); - let result = sube!("ws://127.0.0.1:12281/system/account/0x12840f0626ac847d41089c4e05cf0719c5698af1e3bb87b66542de70b2de4b2b?at=0xd26390715322be085596da3d21a9f357e7477ea48e68438430ed9fbea6235845").await?; + let result = sube!("ws://127.0.0.1:12281/system/account/0x12840f0626ac847d41089c4e05cf0719c5698af1e3bb87b66542de70b2de4b2b?at=0x8c0eb4368ffcc1fca8226b1653a4b3ba50d22fe494dab1dac3df206d438c7e70").await?; + + if let Response::Value(value) = result { + let data = serde_json::to_value(&value).expect("to be serializable"); + println!( + "Account info: {}", + serde_json::to_string_pretty(&data).expect("it must return an str") + ); + } + + let result = sube!("ws://127.0.0.1:12281/system/account/0x12840f0626ac847d41089c4e05cf0719c5698af1e3bb87b66542de70b2de4b2b?at=2062650").await?; if let Response::Value(value) = result { let data = serde_json::to_value(&value).expect("to be serializable"); diff --git a/sube/src/builder.rs b/sube/src/builder.rs index 2d465ee..ccd5e1f 100644 --- a/sube/src/builder.rs +++ b/sube/src/builder.rs @@ -67,6 +67,7 @@ impl<'a> SubeBuilder<'a, (), ()> { .query_pairs() .find(|(k, _)| k == "at") .map(|(_, v)| v.to_string()); + let path = url.path(); log::trace!("building the backend for {}", url); @@ -275,17 +276,29 @@ enum AnyBackend { } impl Backend for &AnyBackend { - async fn query_storage_at( + async fn get_storage_items( &self, keys: Vec, block: Option, - ) -> crate::Result> { + ) -> crate::Result, Vec)>> { + let result: Box, Vec)>> = match self { + #[cfg(any(feature = "http", feature = "http-web"))] + AnyBackend::Http(b) => Box::new(b.get_storage_items(keys, block).await?), + #[cfg(feature = "ws")] + AnyBackend::Ws(b) => Box::new(b.get_storage_items(keys, block).await?), + AnyBackend::_Offline(b) => Box::new(b.get_storage_items(keys, block).await?), + }; + + Ok(result) + } + + async fn get_storage_item(&self, key: String, block: Option) -> crate::Result> { match self { #[cfg(any(feature = "http", feature = "http-web"))] - AnyBackend::Http(b) => b.query_storage_at(keys, block).await, + AnyBackend::Http(b) => b.get_storage_item(key, block).await, #[cfg(feature = "ws")] - AnyBackend::Ws(b) => b.query_storage_at(keys, block).await, - AnyBackend::_Offline(b) => b.query_storage_at(keys, block).await, + AnyBackend::Ws(b) => b.get_storage_item(key, block).await, + AnyBackend::_Offline(b) => b.get_storage_item(key, block).await, } } @@ -333,16 +346,6 @@ impl Backend for &AnyBackend { AnyBackend::_Offline(b) => b.block_info(at).await, } } - - async fn query_storage(&self, key: &StorageKey, block: Option) -> SubeResult> { - match self { - #[cfg(any(feature = "http", feature = "http-web"))] - AnyBackend::Http(b) => b.query_storage(key, block).await, - #[cfg(feature = "ws")] - AnyBackend::Ws(b) => b.query_storage(key, block).await, - AnyBackend::_Offline(b) => b.query_storage(key, block).await, - } - } } #[macro_export] diff --git a/sube/src/lib.rs b/sube/src/lib.rs index 198b2d8..f81a313 100644 --- a/sube/src/lib.rs +++ b/sube/src/lib.rs @@ -18,6 +18,8 @@ extern crate alloc; pub use codec; use codec::Encode; pub use core::fmt::Display; +use core::iter::Empty; + pub use frame_metadata::RuntimeMetadataPrefixed; pub use signer::{Signer, SignerFn}; @@ -98,55 +100,68 @@ async fn query<'m>( ))); } + let block = match block { + Some(block) if block.starts_with("0x") => Some(block), + Some(block) => { + let block_number = + u32::from_str_radix(&block, 10).expect("blockhash to be a number or either a hash"); + let info = chain.block_info(Some(block_number)).await?; + Some(format!("0x{}", hex::encode(info.hash))) + } + _ => None, + }; + if let Ok(key_res) = StorageKey::build_with_registry(&meta.types, pallet, &item_or_call, &keys) { if !key_res.is_partial() { - log::info!("is not partial"); - let res = chain.query_storage(&key_res, block).await?; + let res = chain.get_storage_item(key_res.to_string(), block).await?; return Ok(Response::Value(Value::new(res, key_res.ty, &meta.types))); } let res = chain.get_keys_paged(&key_res, 1000, None).await?; - let result = chain.query_storage_at(res, block).await?; - - if let [storage_change, ..] = &result[..] { - let value = storage_change - .changes - .iter() - .map(|[key, data]| { - let key = key.replace(&hex::encode(&key_res.pallet), ""); - let key = key.replace(&hex::encode(&key_res.call), ""); - let mut offset = 2 + 32; // + 0x - let keys = key_res - .args - .iter() - .map(|arg| match arg { - KeyValue::Empty(type_id) | KeyValue::Value((type_id, _, _, _)) => { - let hashed = &key[offset..]; - let value = Value::new( - hex::decode(hashed).expect("hello world"), - *type_id, - &meta.types, - ); - offset += (value.size() * 2) + 32; - value - } - }) - .collect::>>(); - - let value = Value::new( - hex::decode(&data[2..]).expect("it to be a byte str"), - key_res.ty, - &meta.types, - ); - (keys, value) - }) - .collect::>(); - - Ok(Response::ValueSet(value)) - } else { - Ok(Response::Void) - } + let result = chain.get_storage_items(res, block).await?; + + let value = result + .into_iter() + .map(|(key, data)| { + log::info!("- key: {}", hex::encode(&key)); + log::info!("- data: {}", hex::encode(&data)); + + log::info!("[+] pallet: {}", hex::encode(&key_res.pallet)); + log::info!("[+] call: {}", hex::encode(&key_res.call)); + + log::info!("[+] pallet_size: {}", key_res.pallet.len()); + log::info!("[+] call_size: {}", key_res.call.len()); + + let key = &key[(key_res.pallet.len() + key_res.call.len())..]; + + log::info!("- result: {}", hex::encode(&key)); + + let mut offset = 16; // TODO it depends on the hasher used to encode the key, then the size could change + let keys = key_res + .args + .iter() + .map(|arg| match arg { + KeyValue::Empty(type_id) | KeyValue::Value((type_id, _, _, _)) => { + let hashed = &key[offset..]; + log::info!("hashed: {}", hex::encode(&hashed)); + log::info!(" {:?}", &arg); + + let value = Value::new(hashed.to_vec(), *type_id, &meta.types); + + log::info!("value done!"); + offset += value.size() + 16; + value + } + }) + .collect::>>(); + + let value = Value::new(data.to_vec(), key_res.ty, &meta.types); + (keys, value) + }) + .collect::>(); + + Ok(Response::ValueSet(value)) } else { Err(Error::ChainUnavailable) } @@ -412,11 +427,20 @@ pub struct StorageChangeSet { /// } /// ``` pub trait Backend { - async fn query_storage_at( + async fn get_storage_items( &self, keys: Vec, block: Option, - ) -> crate::Result>; + ) -> crate::Result, Vec)>>; + + async fn get_storage_item(&self, key: String, block: Option) -> crate::Result> { + let res = self.get_storage_items(vec![key], block).await?; + Ok(res + .into_iter() + .next() + .map(|(_, v)| v) + .ok_or(Error::StorageKeyNotFound)?) + } async fn get_keys_paged( &self, @@ -425,9 +449,6 @@ pub trait Backend { to: Option<&StorageKey>, ) -> crate::Result>; - /// Get raw storage items form the blockchain - async fn query_storage(&self, key: &StorageKey, block: Option) -> Result>; - /// Send a signed extrinsic to the blockchain async fn submit(&self, ext: impl AsRef<[u8]>) -> Result<()>; @@ -440,12 +461,12 @@ pub trait Backend { pub struct Offline(pub Metadata); impl Backend for Offline { - async fn query_storage_at( + async fn get_storage_items( &self, _keys: Vec, _block: Option, - ) -> crate::Result> { - Err(Error::ChainUnavailable) + ) -> crate::Result, Vec)>> { + Err::, Vec)>, _>(Error::ChainUnavailable) } async fn get_keys_paged( @@ -457,10 +478,6 @@ impl Backend for Offline { Err(Error::ChainUnavailable) } - async fn query_storage(&self, _key: &StorageKey, _block: Option) -> Result> { - Err(Error::ChainUnavailable) - } - /// Send a signed extrinsic to the blockchain async fn submit(&self, _ext: impl AsRef<[u8]>) -> Result<()> { Err(Error::ChainUnavailable) diff --git a/sube/src/rpc.rs b/sube/src/rpc.rs index 84c3c83..8270df7 100644 --- a/sube/src/rpc.rs +++ b/sube/src/rpc.rs @@ -29,22 +29,25 @@ pub trait Rpc { pub struct RpcClient(pub R); impl Backend for RpcClient { - async fn query_storage_at( + async fn get_storage_items( &self, keys: Vec, block: Option, - ) -> crate::Result> { + ) -> crate::Result, Vec)>> { let keys = serde_json::to_string(&keys).expect("it to be a valid json"); log::info!("query_storage_at encoded: {}", keys); let params: Vec = if let Some(block_hash) = block { - vec![keys, block_hash] + vec![keys, format!("\"{}\"", block_hash)] } else { vec![keys] }; - self.0 - .rpc( + log::info!("params encoded: {:?}", params); + + let result = self + .0 + .rpc::>( "state_queryStorageAt", params .iter() @@ -56,7 +59,22 @@ impl Backend for RpcClient { .map_err(|err| { log::error!("error state_queryStorageAt {:?}", err); crate::Error::StorageKeyNotFound - }) + })?; + + if let Some(change_set) = result.into_iter().next() { + let keys_response = change_set.changes.into_iter().map(|[k, v]| { + log::info!("key: {} value: {}", k, v); + + ( + hex::decode(&k[2..]).expect("to be an hex"), + hex::decode(&v[2..]).expect("to be an hex"), + ) + }); + + return Ok(keys_response); + } else { + return Err(crate::Error::StorageKeyNotFound); + } } async fn get_keys_paged( @@ -84,45 +102,6 @@ impl Backend for RpcClient { Ok(r) } - async fn query_storage( - &self, - key: &StorageKey, - block: Option, - ) -> crate::Result> { - let key = key.to_string(); - log::debug!("StorageKey encoded: {}", key); - - let params: Vec = if let Some(block_hash) = block { - vec![format!("\"{}\"", key), format!("\"{}\"", block_hash)] - } else { - vec![format!("\"{}\"", key)] - }; - - log::info!("params with block: {:?}", params); - - let res: String = self - .0 - .rpc( - "state_getStorage", - params - .iter() - .map(|s| s.as_ref()) - .collect::>() - .as_slice(), - ) - .await - .map_err(|e| { - log::error!("RPC failure: {}", e); - // NOTE it could fail for more reasons - crate::Error::StorageKeyNotFound - })?; - log::info!("result: {:?}", res); - let response = - hex::decode(&res[2..]).map_err(|_err| crate::Error::CantDecodeRawQueryResponse)?; - - Ok(response) - } - async fn submit(&self, ext: impl AsRef<[u8]>) -> crate::Result<()> { let extrinsic = format!("0x{}", hex::encode(ext.as_ref())); log::debug!("Extrinsic: {}", extrinsic); @@ -151,9 +130,12 @@ impl Backend for RpcClient { async fn block_info(&self, at: Option) -> crate::Result { #[inline] async fn block_info(s: &impl Rpc, params: &[&str]) -> crate::Result> { - s.rpc("chain_getBlockHash", params) + let f = s + .rpc::("chain_getBlockHash", params) .await - .map_err(|e| crate::Error::Node(e.to_string())) + .map_err(|e| crate::Error::Node(e.to_string())); + + Ok(hex::decode(&f?.as_str()[2..]).expect("to be an valid hex")) } let block_hash = if let Some(block_number) = at {