mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-10 19:17:22 +00:00
Rework Subxt API to support offline and dynamic transactions (#593)
* WIP API changes * debug impls * Get main crate compiling with first round of changes * Some tidy up * Add WithExtrinsicParams, and have SubstrateConfig + PolkadotConfig, not DefaultConfig * move transaction into extrinsic folder * Add runtime updates back to OnlineClient * rework to be 'client first' to fit better with storage + events * add support for events to Client * tidy dupe trait bound * Wire storage into client, but need to remove static reliance * various tidy up and start stripping codegen to remove bits we dont need now * First pass updating calls and constants codegen * WIP storage client updates * First pass migrated runtime storage over to new format * pass over codegen to generate StorageAddresses and throw other stuff out * don't need a Call trait any more * shuffle things around a bit * Various proc_macro fixes to get 'cargo check' working * organise what's exposed from subxt * Get first example working; balance_transfer_with_params * get balance_transfer example compiling * get concurrent_storage_requests.rs example compiling * get fetch_all_accounts example compiling * get a bunch more of the examples compiling * almost get final example working; type mismatch to look into * wee tweaks * move StorageAddress to separate file * pass Defaultable/Iterable info to StorageAddress in codegen * fix storage validation ne, and partial run through example code * Remove static iteration and strip a generic param from everything * fix doc tests in subxt crate * update test utils and start fixing frame tests * fix frame staking tests * fix the rest of the test compile issues, Borrow on storage values * cargo fmt * remove extra logging during tests * Appease clippy and no more need for into_iter on events * cargo fmt * fix dryRun tests by waiting for blocks * wait for blocks instead of sleeping or other test hacks * cargo fmt * Fix doc links * Traitify StorageAddress * remove out-of-date doc comments * optimise decoding storage a little * cleanup tx stuff, trait for TxPayload, remove Err type param and decode at runtime * clippy fixes * fix doc links * fix doc example * constant address trait for consistency * fix a typo and remove EncodeWithMetadata stuff * Put EventDetails behind a proper interface and allow decoding into top level event, too * fix docs * tweak StorageAddress docs * re-export StorageAddress at root for consistency * fix clippy things * Add support for dynamic values * fix double encoding of storage map key after refactor * clippy fix * Fixes and add a dynamic usage example (needs new scale_value release) * bump scale_value version * cargo fmt * Tweak event bits * cargo fmt * Add a test and bump scale-value to 0.4.0 to support this * remove unnecessary vec from dynamic example * Various typo/grammar fixes Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> * Address PR nits * Undo accidental rename in changelog * Small PR nits/tidyups * fix tests; codegen change against latest substrate * tweak storage address util names * move error decoding to DecodeError and expose * impl some basic traits on the extrinsic param builder Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
This commit is contained in:
+61
-95
@@ -7,67 +7,29 @@
|
||||
//! This is used behind the scenes by various `subxt` APIs, but can
|
||||
//! also be used directly.
|
||||
//!
|
||||
//! # Examples
|
||||
//! # Example
|
||||
//!
|
||||
//! ## Fetch Storage
|
||||
//! Fetching storage keys
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use subxt::{ClientBuilder, DefaultConfig, PolkadotExtrinsicParams};
|
||||
//! # use subxt::storage::StorageKeyPrefix;
|
||||
//! # use subxt::rpc::Rpc;
|
||||
//! use subxt::{ PolkadotConfig, OnlineClient, storage::StorageKey };
|
||||
//!
|
||||
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
|
||||
//! pub mod polkadot {}
|
||||
//!
|
||||
//! # #[tokio::main]
|
||||
//! # async fn main() {
|
||||
//! # let api = ClientBuilder::new()
|
||||
//! # .build()
|
||||
//! # .await
|
||||
//! # .unwrap()
|
||||
//! # .to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
|
||||
//! // Storage prefix is `twox_128("System") ++ twox_128("ExtrinsicCount")`.
|
||||
//! let key = StorageKeyPrefix::new::<polkadot::system::storage::ExtrinsicCount>()
|
||||
//! .to_storage_key();
|
||||
//! let api = OnlineClient::<PolkadotConfig>::new().await.unwrap();
|
||||
//!
|
||||
//! // Obtain the RPC from a generated API
|
||||
//! let rpc: &Rpc<_> = api
|
||||
//! .client
|
||||
//! .rpc();
|
||||
//!
|
||||
//! let result = rpc.storage(&key, None).await.unwrap();
|
||||
//! println!("Storage result: {:?}", result);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Fetch Keys
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use subxt::{ClientBuilder, DefaultConfig, PolkadotExtrinsicParams};
|
||||
//! # use subxt::storage::StorageKeyPrefix;
|
||||
//! # use subxt::rpc::Rpc;
|
||||
//!
|
||||
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
|
||||
//! pub mod polkadot {}
|
||||
//!
|
||||
//! # #[tokio::main]
|
||||
//! # async fn main() {
|
||||
//! # let api = ClientBuilder::new()
|
||||
//! # .build()
|
||||
//! # .await
|
||||
//! # .unwrap()
|
||||
//! # .to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
|
||||
//! let key = StorageKeyPrefix::new::<polkadot::xcm_pallet::storage::VersionNotifiers>()
|
||||
//! .to_storage_key();
|
||||
//!
|
||||
//! // Obtain the RPC from a generated API
|
||||
//! let rpc: &Rpc<_> = api
|
||||
//! .client
|
||||
//! .rpc();
|
||||
//! let key = polkadot::storage()
|
||||
//! .xcm_pallet()
|
||||
//! .version_notifiers_root()
|
||||
//! .to_bytes();
|
||||
//!
|
||||
//! // Fetch up to 10 keys.
|
||||
//! let keys = rpc
|
||||
//! .storage_keys_paged(Some(key), 10, None, None)
|
||||
//! let keys = api
|
||||
//! .rpc()
|
||||
//! .storage_keys_paged(&key, 10, None, None)
|
||||
//! .await
|
||||
//! .unwrap();
|
||||
//!
|
||||
@@ -88,10 +50,10 @@ use std::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::BasicError,
|
||||
error::Error,
|
||||
utils::PhantomDataSendSync,
|
||||
Config,
|
||||
Metadata,
|
||||
PhantomDataSendSync,
|
||||
};
|
||||
use codec::{
|
||||
Decode,
|
||||
@@ -198,7 +160,7 @@ pub type SystemProperties = serde_json::Map<String, serde_json::Value>;
|
||||
/// must be kept compatible with that type from the target substrate version.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum SubstrateTransactionStatus<Hash, BlockHash> {
|
||||
pub enum SubstrateTxStatus<Hash, BlockHash> {
|
||||
/// Transaction is part of the future queue.
|
||||
Future,
|
||||
/// Transaction is part of the ready queue.
|
||||
@@ -324,13 +286,13 @@ impl<T: Config> Rpc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch a storage key
|
||||
/// Fetch the raw bytes for a given storage key
|
||||
pub async fn storage(
|
||||
&self,
|
||||
key: &StorageKey,
|
||||
key: &[u8],
|
||||
hash: Option<T::Hash>,
|
||||
) -> Result<Option<StorageData>, BasicError> {
|
||||
let params = rpc_params![key, hash];
|
||||
) -> Result<Option<StorageData>, Error> {
|
||||
let params = rpc_params![to_hex(key), hash];
|
||||
let data = self.client.request("state_getStorage", params).await?;
|
||||
Ok(data)
|
||||
}
|
||||
@@ -340,12 +302,13 @@ impl<T: Config> Rpc<T> {
|
||||
/// If `start_key` is passed, return next keys in storage in lexicographic order.
|
||||
pub async fn storage_keys_paged(
|
||||
&self,
|
||||
key: Option<StorageKey>,
|
||||
key: &[u8],
|
||||
count: u32,
|
||||
start_key: Option<StorageKey>,
|
||||
start_key: Option<&[u8]>,
|
||||
hash: Option<T::Hash>,
|
||||
) -> Result<Vec<StorageKey>, BasicError> {
|
||||
let params = rpc_params![key, count, start_key, hash];
|
||||
) -> Result<Vec<StorageKey>, Error> {
|
||||
let start_key = start_key.map(to_hex);
|
||||
let params = rpc_params![to_hex(key), count, start_key, hash];
|
||||
let data = self.client.request("state_getKeysPaged", params).await?;
|
||||
Ok(data)
|
||||
}
|
||||
@@ -353,10 +316,11 @@ impl<T: Config> Rpc<T> {
|
||||
/// Query historical storage entries
|
||||
pub async fn query_storage(
|
||||
&self,
|
||||
keys: Vec<StorageKey>,
|
||||
keys: impl IntoIterator<Item = &[u8]>,
|
||||
from: T::Hash,
|
||||
to: Option<T::Hash>,
|
||||
) -> Result<Vec<StorageChangeSet<T::Hash>>, BasicError> {
|
||||
) -> Result<Vec<StorageChangeSet<T::Hash>>, Error> {
|
||||
let keys: Vec<String> = keys.into_iter().map(to_hex).collect();
|
||||
let params = rpc_params![keys, from, to];
|
||||
self.client
|
||||
.request("state_queryStorage", params)
|
||||
@@ -367,9 +331,10 @@ impl<T: Config> Rpc<T> {
|
||||
/// Query historical storage entries
|
||||
pub async fn query_storage_at(
|
||||
&self,
|
||||
keys: &[StorageKey],
|
||||
keys: impl IntoIterator<Item = &[u8]>,
|
||||
at: Option<T::Hash>,
|
||||
) -> Result<Vec<StorageChangeSet<T::Hash>>, BasicError> {
|
||||
) -> Result<Vec<StorageChangeSet<T::Hash>>, Error> {
|
||||
let keys: Vec<String> = keys.into_iter().map(to_hex).collect();
|
||||
let params = rpc_params![keys, at];
|
||||
self.client
|
||||
.request("state_queryStorageAt", params)
|
||||
@@ -378,7 +343,7 @@ impl<T: Config> Rpc<T> {
|
||||
}
|
||||
|
||||
/// Fetch the genesis hash
|
||||
pub async fn genesis_hash(&self) -> Result<T::Hash, BasicError> {
|
||||
pub async fn genesis_hash(&self) -> Result<T::Hash, Error> {
|
||||
let block_zero = 0u32;
|
||||
let params = rpc_params![block_zero];
|
||||
let genesis_hash: Option<T::Hash> =
|
||||
@@ -387,7 +352,7 @@ impl<T: Config> Rpc<T> {
|
||||
}
|
||||
|
||||
/// Fetch the metadata
|
||||
pub async fn metadata(&self) -> Result<Metadata, BasicError> {
|
||||
pub async fn metadata(&self) -> Result<Metadata, Error> {
|
||||
let bytes: Bytes = self
|
||||
.client
|
||||
.request("state_getMetadata", rpc_params![])
|
||||
@@ -398,7 +363,7 @@ impl<T: Config> Rpc<T> {
|
||||
}
|
||||
|
||||
/// Fetch system properties
|
||||
pub async fn system_properties(&self) -> Result<SystemProperties, BasicError> {
|
||||
pub async fn system_properties(&self) -> Result<SystemProperties, Error> {
|
||||
Ok(self
|
||||
.client
|
||||
.request("system_properties", rpc_params![])
|
||||
@@ -406,22 +371,22 @@ impl<T: Config> Rpc<T> {
|
||||
}
|
||||
|
||||
/// Fetch system health
|
||||
pub async fn system_health(&self) -> Result<Health, BasicError> {
|
||||
pub async fn system_health(&self) -> Result<Health, Error> {
|
||||
Ok(self.client.request("system_health", rpc_params![]).await?)
|
||||
}
|
||||
|
||||
/// Fetch system chain
|
||||
pub async fn system_chain(&self) -> Result<String, BasicError> {
|
||||
pub async fn system_chain(&self) -> Result<String, Error> {
|
||||
Ok(self.client.request("system_chain", rpc_params![]).await?)
|
||||
}
|
||||
|
||||
/// Fetch system name
|
||||
pub async fn system_name(&self) -> Result<String, BasicError> {
|
||||
pub async fn system_name(&self) -> Result<String, Error> {
|
||||
Ok(self.client.request("system_name", rpc_params![]).await?)
|
||||
}
|
||||
|
||||
/// Fetch system version
|
||||
pub async fn system_version(&self) -> Result<String, BasicError> {
|
||||
pub async fn system_version(&self) -> Result<String, Error> {
|
||||
Ok(self.client.request("system_version", rpc_params![]).await?)
|
||||
}
|
||||
|
||||
@@ -429,7 +394,7 @@ impl<T: Config> Rpc<T> {
|
||||
pub async fn system_account_next_index(
|
||||
&self,
|
||||
account: &T::AccountId,
|
||||
) -> Result<T::Index, BasicError> {
|
||||
) -> Result<T::Index, Error> {
|
||||
Ok(self
|
||||
.client
|
||||
.request("system_accountNextIndex", rpc_params![account])
|
||||
@@ -440,7 +405,7 @@ impl<T: Config> Rpc<T> {
|
||||
pub async fn header(
|
||||
&self,
|
||||
hash: Option<T::Hash>,
|
||||
) -> Result<Option<T::Header>, BasicError> {
|
||||
) -> Result<Option<T::Header>, Error> {
|
||||
let params = rpc_params![hash];
|
||||
let header = self.client.request("chain_getHeader", params).await?;
|
||||
Ok(header)
|
||||
@@ -450,14 +415,14 @@ impl<T: Config> Rpc<T> {
|
||||
pub async fn block_hash(
|
||||
&self,
|
||||
block_number: Option<BlockNumber>,
|
||||
) -> Result<Option<T::Hash>, BasicError> {
|
||||
) -> Result<Option<T::Hash>, Error> {
|
||||
let params = rpc_params![block_number];
|
||||
let block_hash = self.client.request("chain_getBlockHash", params).await?;
|
||||
Ok(block_hash)
|
||||
}
|
||||
|
||||
/// Get a block hash of the latest finalized block
|
||||
pub async fn finalized_head(&self) -> Result<T::Hash, BasicError> {
|
||||
pub async fn finalized_head(&self) -> Result<T::Hash, Error> {
|
||||
let hash = self
|
||||
.client
|
||||
.request("chain_getFinalizedHead", rpc_params![])
|
||||
@@ -469,7 +434,7 @@ impl<T: Config> Rpc<T> {
|
||||
pub async fn block(
|
||||
&self,
|
||||
hash: Option<T::Hash>,
|
||||
) -> Result<Option<ChainBlock<T>>, BasicError> {
|
||||
) -> Result<Option<ChainBlock<T>>, Error> {
|
||||
let params = rpc_params![hash];
|
||||
let block = self.client.request("chain_getBlock", params).await?;
|
||||
Ok(block)
|
||||
@@ -483,7 +448,7 @@ impl<T: Config> Rpc<T> {
|
||||
pub async fn block_stats(
|
||||
&self,
|
||||
block_hash: T::Hash,
|
||||
) -> Result<Option<BlockStats>, BasicError> {
|
||||
) -> Result<Option<BlockStats>, Error> {
|
||||
let params = rpc_params![block_hash];
|
||||
let stats = self.client.request("dev_getBlockStats", params).await?;
|
||||
Ok(stats)
|
||||
@@ -492,9 +457,10 @@ impl<T: Config> Rpc<T> {
|
||||
/// Get proof of storage entries at a specific block's state.
|
||||
pub async fn read_proof(
|
||||
&self,
|
||||
keys: Vec<StorageKey>,
|
||||
keys: impl IntoIterator<Item = &[u8]>,
|
||||
hash: Option<T::Hash>,
|
||||
) -> Result<ReadProof<T::Hash>, BasicError> {
|
||||
) -> Result<ReadProof<T::Hash>, Error> {
|
||||
let keys: Vec<String> = keys.into_iter().map(to_hex).collect();
|
||||
let params = rpc_params![keys, hash];
|
||||
let proof = self.client.request("state_getReadProof", params).await?;
|
||||
Ok(proof)
|
||||
@@ -504,7 +470,7 @@ impl<T: Config> Rpc<T> {
|
||||
pub async fn runtime_version(
|
||||
&self,
|
||||
at: Option<T::Hash>,
|
||||
) -> Result<RuntimeVersion, BasicError> {
|
||||
) -> Result<RuntimeVersion, Error> {
|
||||
let params = rpc_params![at];
|
||||
let version = self
|
||||
.client
|
||||
@@ -514,7 +480,7 @@ impl<T: Config> Rpc<T> {
|
||||
}
|
||||
|
||||
/// Subscribe to blocks.
|
||||
pub async fn subscribe_blocks(&self) -> Result<Subscription<T::Header>, BasicError> {
|
||||
pub async fn subscribe_blocks(&self) -> Result<Subscription<T::Header>, Error> {
|
||||
let subscription = self
|
||||
.client
|
||||
.subscribe(
|
||||
@@ -530,7 +496,7 @@ impl<T: Config> Rpc<T> {
|
||||
/// Subscribe to finalized blocks.
|
||||
pub async fn subscribe_finalized_blocks(
|
||||
&self,
|
||||
) -> Result<Subscription<T::Header>, BasicError> {
|
||||
) -> Result<Subscription<T::Header>, Error> {
|
||||
let subscription = self
|
||||
.client
|
||||
.subscribe(
|
||||
@@ -545,7 +511,7 @@ impl<T: Config> Rpc<T> {
|
||||
/// Subscribe to runtime version updates that produce changes in the metadata.
|
||||
pub async fn subscribe_runtime_version(
|
||||
&self,
|
||||
) -> Result<Subscription<RuntimeVersion>, BasicError> {
|
||||
) -> Result<Subscription<RuntimeVersion>, Error> {
|
||||
let subscription = self
|
||||
.client
|
||||
.subscribe(
|
||||
@@ -561,7 +527,7 @@ impl<T: Config> Rpc<T> {
|
||||
pub async fn submit_extrinsic<X: Encode>(
|
||||
&self,
|
||||
extrinsic: X,
|
||||
) -> Result<T::Hash, BasicError> {
|
||||
) -> Result<T::Hash, Error> {
|
||||
let bytes: Bytes = extrinsic.encode().into();
|
||||
let params = rpc_params![bytes];
|
||||
let xt_hash = self
|
||||
@@ -575,8 +541,7 @@ impl<T: Config> Rpc<T> {
|
||||
pub async fn watch_extrinsic<X: Encode>(
|
||||
&self,
|
||||
extrinsic: X,
|
||||
) -> Result<Subscription<SubstrateTransactionStatus<T::Hash, T::Hash>>, BasicError>
|
||||
{
|
||||
) -> Result<Subscription<SubstrateTxStatus<T::Hash, T::Hash>>, Error> {
|
||||
let bytes: Bytes = extrinsic.encode().into();
|
||||
let params = rpc_params![bytes];
|
||||
let subscription = self
|
||||
@@ -596,14 +561,14 @@ impl<T: Config> Rpc<T> {
|
||||
key_type: String,
|
||||
suri: String,
|
||||
public: Bytes,
|
||||
) -> Result<(), BasicError> {
|
||||
) -> Result<(), Error> {
|
||||
let params = rpc_params![key_type, suri, public];
|
||||
self.client.request("author_insertKey", params).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate new session keys and returns the corresponding public keys.
|
||||
pub async fn rotate_keys(&self) -> Result<Bytes, BasicError> {
|
||||
pub async fn rotate_keys(&self) -> Result<Bytes, Error> {
|
||||
Ok(self
|
||||
.client
|
||||
.request("author_rotateKeys", rpc_params![])
|
||||
@@ -615,10 +580,7 @@ impl<T: Config> Rpc<T> {
|
||||
/// `session_keys` is the SCALE encoded session keys object from the runtime.
|
||||
///
|
||||
/// Returns `true` iff all private keys could be found.
|
||||
pub async fn has_session_keys(
|
||||
&self,
|
||||
session_keys: Bytes,
|
||||
) -> Result<bool, BasicError> {
|
||||
pub async fn has_session_keys(&self, session_keys: Bytes) -> Result<bool, Error> {
|
||||
let params = rpc_params![session_keys];
|
||||
Ok(self.client.request("author_hasSessionKeys", params).await?)
|
||||
}
|
||||
@@ -630,7 +592,7 @@ impl<T: Config> Rpc<T> {
|
||||
&self,
|
||||
public_key: Bytes,
|
||||
key_type: String,
|
||||
) -> Result<bool, BasicError> {
|
||||
) -> Result<bool, Error> {
|
||||
let params = rpc_params![public_key, key_type];
|
||||
Ok(self.client.request("author_hasKey", params).await?)
|
||||
}
|
||||
@@ -642,8 +604,8 @@ impl<T: Config> Rpc<T> {
|
||||
&self,
|
||||
encoded_signed: &[u8],
|
||||
at: Option<T::Hash>,
|
||||
) -> Result<ApplyExtrinsicResult, BasicError> {
|
||||
let params = rpc_params![format!("0x{}", hex::encode(encoded_signed)), at];
|
||||
) -> Result<ApplyExtrinsicResult, Error> {
|
||||
let params = rpc_params![to_hex(encoded_signed), at];
|
||||
let result_bytes: Bytes = self.client.request("system_dryRun", params).await?;
|
||||
let data: ApplyExtrinsicResult =
|
||||
codec::Decode::decode(&mut result_bytes.0.as_slice())?;
|
||||
@@ -669,6 +631,10 @@ async fn ws_transport(url: &str) -> Result<(WsSender, WsReceiver), RpcError> {
|
||||
.map_err(|e| RpcError::Transport(e.into()))
|
||||
}
|
||||
|
||||
fn to_hex(bytes: impl AsRef<[u8]>) -> String {
|
||||
format!("0x{}", hex::encode(bytes.as_ref()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user