Skip to content

Commit

Permalink
refactor: Update method names to improve clarity
Browse files Browse the repository at this point in the history
  • Loading branch information
S0c5 committed Aug 18, 2024
1 parent 92398ce commit 4531532
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 117 deletions.
12 changes: 11 additions & 1 deletion sube/examples/query_at_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
33 changes: 18 additions & 15 deletions sube/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -275,17 +276,29 @@ enum AnyBackend {
}

impl Backend for &AnyBackend {
async fn query_storage_at(
async fn get_storage_items(
&self,
keys: Vec<String>,
block: Option<String>,
) -> crate::Result<Vec<StorageChangeSet>> {
) -> crate::Result<impl Iterator<Item = (Vec<u8>, Vec<u8>)>> {
let result: Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)>> = 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<String>) -> crate::Result<Vec<u8>> {
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,
}
}

Expand Down Expand Up @@ -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<String>) -> SubeResult<Vec<u8>> {
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]
Expand Down
125 changes: 71 additions & 54 deletions sube/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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::<Vec<Value<'m>>>();

let value = Value::new(
hex::decode(&data[2..]).expect("it to be a byte str"),
key_res.ty,
&meta.types,
);
(keys, value)
})
.collect::<Vec<_>>();

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::<Vec<Value<'m>>>();

let value = Value::new(data.to_vec(), key_res.ty, &meta.types);
(keys, value)
})
.collect::<Vec<_>>();

Ok(Response::ValueSet(value))
} else {
Err(Error::ChainUnavailable)
}
Expand Down Expand Up @@ -412,11 +427,20 @@ pub struct StorageChangeSet {
/// }
/// ```
pub trait Backend {
async fn query_storage_at(
async fn get_storage_items(
&self,
keys: Vec<String>,
block: Option<String>,
) -> crate::Result<Vec<StorageChangeSet>>;
) -> crate::Result<impl Iterator<Item = (Vec<u8>, Vec<u8>)>>;

async fn get_storage_item(&self, key: String, block: Option<String>) -> crate::Result<Vec<u8>> {
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,
Expand All @@ -425,9 +449,6 @@ pub trait Backend {
to: Option<&StorageKey>,
) -> crate::Result<Vec<String>>;

/// Get raw storage items form the blockchain
async fn query_storage(&self, key: &StorageKey, block: Option<String>) -> Result<Vec<u8>>;

/// Send a signed extrinsic to the blockchain
async fn submit(&self, ext: impl AsRef<[u8]>) -> Result<()>;

Expand All @@ -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<String>,
_block: Option<String>,
) -> crate::Result<Vec<StorageChangeSet>> {
Err(Error::ChainUnavailable)
) -> crate::Result<impl Iterator<Item = (Vec<u8>, Vec<u8>)>> {
Err::<Empty<(Vec<u8>, Vec<u8>)>, _>(Error::ChainUnavailable)
}

async fn get_keys_paged(
Expand All @@ -457,10 +478,6 @@ impl Backend for Offline {
Err(Error::ChainUnavailable)
}

async fn query_storage(&self, _key: &StorageKey, _block: Option<String>) -> Result<Vec<u8>> {
Err(Error::ChainUnavailable)
}

/// Send a signed extrinsic to the blockchain
async fn submit(&self, _ext: impl AsRef<[u8]>) -> Result<()> {
Err(Error::ChainUnavailable)
Expand Down
76 changes: 29 additions & 47 deletions sube/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,25 @@ pub trait Rpc {
pub struct RpcClient<R>(pub R);

impl<R: Rpc> Backend for RpcClient<R> {
async fn query_storage_at(
async fn get_storage_items(
&self,
keys: Vec<String>,
block: Option<String>,
) -> crate::Result<Vec<StorageChangeSet>> {
) -> crate::Result<impl Iterator<Item = (Vec<u8>, Vec<u8>)>> {
let keys = serde_json::to_string(&keys).expect("it to be a valid json");

log::info!("query_storage_at encoded: {}", keys);
let params: Vec<String> = 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::<Vec<StorageChangeSet>>(
"state_queryStorageAt",
params
.iter()
Expand All @@ -56,7 +59,22 @@ impl<R: Rpc> Backend for RpcClient<R> {
.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(
Expand Down Expand Up @@ -84,45 +102,6 @@ impl<R: Rpc> Backend for RpcClient<R> {
Ok(r)
}

async fn query_storage(
&self,
key: &StorageKey,
block: Option<String>,
) -> crate::Result<Vec<u8>> {
let key = key.to_string();
log::debug!("StorageKey encoded: {}", key);

let params: Vec<String> = 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::<Vec<_>>()
.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);
Expand Down Expand Up @@ -151,9 +130,12 @@ impl<R: Rpc> Backend for RpcClient<R> {
async fn block_info(&self, at: Option<u32>) -> crate::Result<meta::BlockInfo> {
#[inline]
async fn block_info(s: &impl Rpc, params: &[&str]) -> crate::Result<Vec<u8>> {
s.rpc("chain_getBlockHash", params)
let f = s
.rpc::<String>("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 {
Expand Down

0 comments on commit 4531532

Please sign in to comment.