diff --git a/libwallet/examples/persisted_in_keyring.rs b/libwallet/examples/persisted_in_keyring.rs index d3a1840..c8cbd6c 100644 --- a/libwallet/examples/persisted_in_keyring.rs +++ b/libwallet/examples/persisted_in_keyring.rs @@ -14,7 +14,6 @@ async fn main() -> Result<(), Box> { let vault = vault::OSKeyring::::new(TEST_USER, Language::default()); let mut wallet = Wallet::new(vault); - wallet.unlock(None, pin).await?; diff --git a/libwallet/src/vault/os.rs b/libwallet/src/vault/os.rs index c647103..7134326 100644 --- a/libwallet/src/vault/os.rs +++ b/libwallet/src/vault/os.rs @@ -3,8 +3,8 @@ use core::marker::PhantomData; use crate::{ mnemonic::{Language, Mnemonic}, util::{seed_from_entropy, Pin}, + vault::utils::{AccountSigner, RootAccount}, Vault, - vault::utils::{ RootAccount, AccountSigner } }; use arrayvec::ArrayVec; use keyring; @@ -16,7 +16,7 @@ pub struct OSKeyring { entry: keyring::Entry, root: Option, auto_generate: Option, - _phantom: PhantomData + _phantom: PhantomData, } impl OSKeyring { @@ -28,7 +28,7 @@ impl OSKeyring { entry: keyring::Entry::new(SERVICE, &uname), root: None, auto_generate: lang.into(), - _phantom: PhantomData::default() + _phantom: PhantomData::default(), } } @@ -119,7 +119,7 @@ impl> Vault for OSKeyring { .and_then(|l| self.generate(pin, l)) }) .map(|r| { - let acc = AccountSigner::new(account.as_ref().map(|x| x.as_ref())).unlock(&r); + let acc = AccountSigner::new(account.as_ref().map(|x| x.as_ref())).unlock(&r); self.root = Some(r); acc }) diff --git a/sube/examples/pallet_communities.rs b/sube/examples/pallet_communities.rs index b49c057..2630016 100644 --- a/sube/examples/pallet_communities.rs +++ b/sube/examples/pallet_communities.rs @@ -10,14 +10,20 @@ async fn main() -> Result<()> { if let Response::ValueSet(value) = response { let data = serde_json::to_value(&value).expect("to be serializable"); - println!("Collection {}", serde_json::to_string_pretty(&data).expect("it must return an str")); + println!( + "Collection {}", + serde_json::to_string_pretty(&data).expect("it must return an str") + ); } let result = sube!("https://kreivo.io/system/account/0x12840f0626ac847d41089c4e05cf0719c5698af1e3bb87b66542de70b2de4b2b").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")); + println!( + "Account info: {}", + serde_json::to_string_pretty(&data).expect("it must return an str") + ); } Ok(()) diff --git a/sube/examples/query_at_block.rs b/sube/examples/query_at_block.rs new file mode 100644 index 0000000..90f0085 --- /dev/null +++ b/sube/examples/query_at_block.rs @@ -0,0 +1,20 @@ +use env_logger; +use serde_json; +use sube::{sube, ExtrinsicBody, Response, Result, SubeBuilder}; + +#[async_std::main] +async fn main() -> Result<()> { + env_logger::init(); + + let result = sube!("ws://127.0.0.1:12281/system/account/0x12840f0626ac847d41089c4e05cf0719c5698af1e3bb87b66542de70b2de4b2b?at=0xd26390715322be085596da3d21a9f357e7477ea48e68438430ed9fbea6235845").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") + ); + } + + Ok(()) +} diff --git a/sube/examples/query_membership.rs b/sube/examples/query_membership.rs index ab17b69..cd8fa17 100644 --- a/sube/examples/query_membership.rs +++ b/sube/examples/query_membership.rs @@ -9,10 +9,9 @@ async fn main() -> sube::Result<()> { let query = format!( "ws://127.0.0.1:12281/communityMemberships/account/{}/{}", - "0x12840f0626ac847d41089c4e05cf0719c5698af1e3bb87b66542de70b2de4b2b", - 1 + "0x12840f0626ac847d41089c4e05cf0719c5698af1e3bb87b66542de70b2de4b2b", 1 ); - + let r = sube!(&query).await?; if let Response::ValueSet(ref v) = r { @@ -23,4 +22,4 @@ async fn main() -> sube::Result<()> { } Ok(()) -} \ No newline at end of file +} diff --git a/sube/examples/query_preimage.rs b/sube/examples/query_preimage.rs index 7d41929..4c72fbe 100644 --- a/sube/examples/query_preimage.rs +++ b/sube/examples/query_preimage.rs @@ -9,10 +9,9 @@ async fn main() -> sube::Result<()> { let query = format!( "ws://127.0.0.1:12281/preimage/preimageFor/{}/{}", - "0x6b172c3695dca229e71c0bca790f5991b68f8eee96334e842312a0a7d4a46c6c", - 30 + "0x6b172c3695dca229e71c0bca790f5991b68f8eee96334e842312a0a7d4a46c6c", 30 ); - + let r = sube!(&query).await?; if let Response::Value(ref v) = r { @@ -23,4 +22,4 @@ async fn main() -> sube::Result<()> { } Ok(()) -} \ No newline at end of file +} diff --git a/sube/examples/query_referendum_info.rs b/sube/examples/query_referendum_info.rs index 350a915..ef037e7 100644 --- a/sube/examples/query_referendum_info.rs +++ b/sube/examples/query_referendum_info.rs @@ -16,7 +16,10 @@ async fn main() -> Result<()> { if let Response::Value(ref v) = r { let json_value = serde_json::to_value(v).expect("it must to be an valid Value"); println!("Raw JSON value: {:?}", json_value); - println!("Info: {}", serde_json::to_string_pretty(&json_value).expect("it must return an str")); + println!( + "Info: {}", + serde_json::to_string_pretty(&json_value).expect("it must return an str") + ); } Ok(()) } diff --git a/sube/src/builder.rs b/sube/src/builder.rs index c6c7c37..2d465ee 100644 --- a/sube/src/builder.rs +++ b/sube/src/builder.rs @@ -62,6 +62,11 @@ impl<'a> SubeBuilder<'a, (), ()> { let Self { url, metadata, .. } = self; let url = chain_string_to_url(url.ok_or(Error::BadInput)?)?; + + let block = url + .query_pairs() + .find(|(k, _)| k == "at") + .map(|(_, v)| v.to_string()); let path = url.path(); log::trace!("building the backend for {}", url); @@ -71,7 +76,7 @@ impl<'a> SubeBuilder<'a, (), ()> { Ok(match path { "_meta" => Response::Meta(meta), "_meta/registry" => Response::Registry(&meta.types), - _ => crate::query(&backend, meta, path).await?, + _ => crate::query(&backend, meta, path, block).await?, }) } } @@ -329,13 +334,13 @@ impl Backend for &AnyBackend { } } - async fn query_storage(&self, key: &StorageKey) -> SubeResult> { + 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).await, + AnyBackend::Http(b) => b.query_storage(key, block).await, #[cfg(feature = "ws")] - AnyBackend::Ws(b) => b.query_storage(key).await, - AnyBackend::_Offline(b) => b.query_storage(key).await, + AnyBackend::Ws(b) => b.query_storage(key, block).await, + AnyBackend::_Offline(b) => b.query_storage(key, block).await, } } } diff --git a/sube/src/lib.rs b/sube/src/lib.rs index e36dbb3..198b2d8 100644 --- a/sube/src/lib.rs +++ b/sube/src/lib.rs @@ -70,13 +70,19 @@ pub fn sube(url: &str) -> builder::SubeBuilder<(), ()> { } pub type Result = core::result::Result; -async fn query<'m>(chain: &impl Backend, meta: &'m Metadata, path: &str) -> Result> { +async fn query<'m>( + chain: &impl Backend, + meta: &'m Metadata, + path: &str, + block: Option, +) -> Result> { + log::info!("path {}", path); let (pallet, item_or_call, mut keys) = parse_uri(path).ok_or(Error::BadInput)?; log::info!("pallet {}", pallet); let pallet = meta .pallet_by_name(&pallet) .ok_or_else(|| Error::PalletNotFound(pallet))?; - + if item_or_call == "_constants" { let const_name = keys.pop().ok_or_else(|| Error::MissingConstantName)?; let const_meta = pallet @@ -96,12 +102,12 @@ async fn query<'m>(chain: &impl Backend, meta: &'m Metadata, path: &str) -> Resu { if !key_res.is_partial() { log::info!("is not partial"); - let res = chain.query_storage(&key_res).await?; + let res = chain.query_storage(&key_res, 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, None).await?; + let result = chain.query_storage_at(res, block).await?; if let [storage_change, ..] = &result[..] { let value = storage_change @@ -110,19 +116,19 @@ async fn query<'m>(chain: &impl Backend, meta: &'m Metadata, path: &str) -> Resu .map(|[key, data]| { let key = key.replace(&hex::encode(&key_res.pallet), ""); let key = key.replace(&hex::encode(&key_res.call), ""); - let mut pointer = 2 + 32; // + 0x + 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[pointer..]; + let hashed = &key[offset..]; let value = Value::new( hex::decode(hashed).expect("hello world"), *type_id, &meta.types, ); - pointer += (value.size() * 2) + 32; + offset += (value.size() * 2) + 32; value } }) @@ -215,6 +221,7 @@ where &chain, meta, &format!("system/account/0x{}", hex::encode(from_account.as_ref())), + None, ) .await?; @@ -419,7 +426,7 @@ pub trait Backend { ) -> crate::Result>; /// Get raw storage items form the blockchain - async fn query_storage(&self, key: &StorageKey) -> Result>; + 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<()>; @@ -450,7 +457,7 @@ impl Backend for Offline { Err(Error::ChainUnavailable) } - async fn query_storage(&self, _key: &StorageKey) -> Result> { + async fn query_storage(&self, _key: &StorageKey, _block: Option) -> Result> { Err(Error::ChainUnavailable) } diff --git a/sube/src/meta_ext.rs b/sube/src/meta_ext.rs index d7b0178..4245192 100644 --- a/sube/src/meta_ext.rs +++ b/sube/src/meta_ext.rs @@ -128,7 +128,14 @@ impl StorageKey { .as_ref() .and_then(|s| s.entries.iter().find(|e| e.name == item)) .ok_or(crate::Error::CantFindMethodInPallet)?; - log::trace!("map_keys={}", map_keys.iter().map(|x| x.as_ref()).collect::>().join(", ")); + log::trace!( + "map_keys={}", + map_keys + .iter() + .map(|x| x.as_ref()) + .collect::>() + .join(", ") + ); entry.ty.key(registry, &meta.name, &entry.name, map_keys) } } @@ -181,7 +188,7 @@ pub trait EntryTy { } else { vec![] }; - + if type_call_ids.len() == hashers.len() { log::trace!("type_call_ids={:?}", type_call_ids); let storage_key = StorageKey::new( @@ -195,48 +202,49 @@ pub trait EntryTy { log::info!("type_call_ids.i={} type_call_ids.type_id={}", i, type_id); let k = map_keys.get(i); let hasher = hashers.get(i).expect("hasher not found"); - + if k.is_none() { return KeyValue::Empty(type_id); } - + let k = k.expect("it must exist").as_ref(); - + let hasher = hasher.borrow(); let mut out = vec![]; - + if let Some(k) = k.strip_prefix("0x") { let value = hex::decode(k).expect("str must be encoded"); - let _ = to_bytes_with_info(&mut out, &value, Some((portable_reg, type_id))); + let _ = + to_bytes_with_info(&mut out, &value, Some((portable_reg, type_id))); } else { let _ = to_bytes_with_info(&mut out, &k, Some((portable_reg, type_id))); } - + let hash = hash(hasher, &out); KeyValue::Value((type_id, hash, out, hasher.clone())) }) .collect(), ); - + Ok(storage_key) } else if hashers.len() == 1 { log::trace!("treating touple as argument for hasher"); let touple_hex: Vec = type_call_ids - .into_iter() - .enumerate() - .flat_map(|(i, type_id)| { - let k = map_keys.get(i).expect("to exist in map_keys").as_ref(); - let mut out = vec![]; - if let Some(k) = k.strip_prefix("0x") { - let value = hex::decode(k).expect("str must be hex encoded"); - let _ = to_bytes_with_info(&mut out, &value, Some((portable_reg, type_id))); - } else { - let _ = to_bytes_with_info(&mut out, &k, Some((portable_reg, type_id))); - } - out - }) - .collect(); + .into_iter() + .enumerate() + .flat_map(|(i, type_id)| { + let k = map_keys.get(i).expect("to exist in map_keys").as_ref(); + let mut out = vec![]; + if let Some(k) = k.strip_prefix("0x") { + let value = hex::decode(k).expect("str must be hex encoded"); + let _ = to_bytes_with_info(&mut out, &value, Some((portable_reg, type_id))); + } else { + let _ = to_bytes_with_info(&mut out, &k, Some((portable_reg, type_id))); + } + out + }) + .collect(); let hasher = hashers.first().expect("hasher not found"); let hasher = hasher.borrow(); @@ -246,12 +254,19 @@ pub trait EntryTy { value_ty_id, hash(&Hasher::Twox128, pallet_item.0), hash(&Hasher::Twox128, pallet_item.1), - vec![KeyValue::Value((key_ty_id.expect("must key id must work"), hashed_value, touple_hex, hasher.clone()))] + vec![KeyValue::Value(( + key_ty_id.expect("must key id must work"), + hashed_value, + touple_hex, + hasher.clone(), + ))], ); - + Ok(storage_key) } else { - Err(crate::Error::Encode("Wrong number of hashers vs map_keys".into())) + Err(crate::Error::Encode( + "Wrong number of hashers vs map_keys".into(), + )) } } } @@ -282,7 +297,7 @@ impl EntryTy for EntryType { map_keys, hashers, ) - }, + } } } } diff --git a/sube/src/rpc.rs b/sube/src/rpc.rs index bdda6ad..84c3c83 100644 --- a/sube/src/rpc.rs +++ b/sube/src/rpc.rs @@ -35,6 +35,8 @@ impl Backend for RpcClient { block: Option, ) -> crate::Result> { 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] } else { @@ -82,21 +84,41 @@ impl Backend for RpcClient { Ok(r) } - async fn query_storage(&self, key: &StorageKey) -> crate::Result> { + 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", &[&format!("\"{}\"", &key)]) + .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 })?; - - let response = hex::decode(&res[2..]).map_err(|_err| crate::Error::CantDecodeRawQueryResponse)?; + log::info!("result: {:?}", res); + let response = + hex::decode(&res[2..]).map_err(|_err| crate::Error::CantDecodeRawQueryResponse)?; Ok(response) } @@ -119,7 +141,8 @@ impl Backend for RpcClient { .rpc("state_getMetadata", &[]) .await .map_err(|e| crate::Error::Node(e.to_string()))?; - let response = hex::decode(&res[2..]).map_err(|_err| crate::Error::CantDecodeReponseForMeta)?; + let response = + hex::decode(&res[2..]).map_err(|_err| crate::Error::CantDecodeReponseForMeta)?; let meta = from_bytes(&mut response.as_slice()).map_err(|_| crate::Error::BadMetadata)?; log::trace!("Metadata {:#?}", meta); Ok(meta) diff --git a/sube/src/ws.rs b/sube/src/ws.rs index 2a0e807..ed249a9 100644 --- a/sube/src/ws.rs +++ b/sube/src/ws.rs @@ -1,9 +1,9 @@ use alloc::{collections::BTreeMap, sync::Arc}; -use no_std_async::Mutex; use ewebsock::{WsEvent, WsMessage as Message, WsReceiver as Rx, WsSender as Tx}; use futures_channel::{mpsc, oneshot}; use futures_util::StreamExt as _; +use no_std_async::Mutex; // use futures_util::StreamExt; use jsonrpc::{ error::{result_to_response, standard_error, StandardError}, @@ -92,7 +92,7 @@ impl Backend { let (tx, rx) = ewebsock::connect(url, ewebsock::Options::default()).map_err(Error::Platform)?; - + let (sender, recv) = mpsc::channel::(MAX_BUFFER); let backend = Backend { @@ -132,6 +132,7 @@ impl Backend { log::trace!("Got WS message {:?}", msg); if let Message::Text(msg) = msg { + log::info!("Got WS message {:?}", msg); let res: rpc::Response = serde_json::from_str(&msg).unwrap_or_else(|_| { result_to_response(