// Copyright 2019-2021 Parity Technologies (UK) Ltd. // This file is part of substrate-subxt. // // subxt is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // subxt is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with substrate-subxt. If not, see . //! A library to **sub**mit e**xt**rinsics to a //! [substrate](https://github.com/paritytech/substrate) node via RPC. #![deny( bad_style, const_err, improper_ctypes, missing_docs, non_shorthand_field_patterns, no_mangle_generic_items, overflowing_literals, path_statements, patterns_in_fns_without_body, private_in_public, unconditional_recursion, unused_allocation, unused_comparisons, unused_parens, while_true, trivial_casts, trivial_numeric_casts, unused_extern_crates, clippy::all )] #![allow(clippy::type_complexity)] #[macro_use] extern crate substrate_subxt_proc_macro; #[cfg(feature = "client")] pub use substrate_subxt_client as client; pub use sp_core; pub use sp_runtime; use codec::{ Codec, Decode, }; use futures::future; use jsonrpsee::client::Subscription; use sp_core::{ storage::{ StorageChangeSet, StorageData, StorageKey, }, Bytes, }; pub use sp_runtime::traits::SignedExtension; pub use sp_version::RuntimeVersion; use std::marker::PhantomData; mod error; mod events; pub mod extrinsic; mod frame; mod metadata; mod rpc; mod runtimes; mod subscription; pub use crate::{ error::Error, events::{ EventTypeRegistry, EventsDecoder, RawEvent, }, extrinsic::{ PairSigner, SignedExtra, Signer, UncheckedExtrinsic, }, frame::*, metadata::{ Metadata, MetadataError, }, rpc::{ BlockNumber, ExtrinsicSuccess, ReadProof, SystemProperties, }, runtimes::*, subscription::*, substrate_subxt_proc_macro::*, }; use crate::{ frame::system::{ AccountStoreExt, Phase, System, }, rpc::{ ChainBlock, Rpc, }, }; /// ClientBuilder for constructing a Client. #[derive(Default)] pub struct ClientBuilder { url: Option, client: Option, page_size: Option, event_type_registry: EventTypeRegistry, skip_type_sizes_check: bool, } impl ClientBuilder { /// Creates a new ClientBuilder. pub fn new() -> Self { Self { url: None, client: None, page_size: None, event_type_registry: EventTypeRegistry::new(), skip_type_sizes_check: false, } } /// Sets the jsonrpsee client. pub fn set_client>(mut self, client: P) -> Self { self.client = Some(client.into()); self } /// Set the substrate rpc address. pub fn set_url>(mut self, url: P) -> Self { self.url = Some(url.into()); self } /// Set the page size. pub fn set_page_size(mut self, size: u32) -> Self { self.page_size = Some(size); self } /// Register a custom type segmenter, for consuming types in events where the size cannot /// be inferred from the metadata. /// /// # Panics /// /// If there is already a type size registered with this name. pub fn register_type_size(mut self, name: &str) -> Self where U: Codec + Send + Sync + 'static, { self.event_type_registry.register_type_size::(name); self } /// Disable the check for missing type sizes on `build`. /// /// *WARNING* can lead to runtime errors if receiving events with unknown types. pub fn skip_type_sizes_check(mut self) -> Self { self.skip_type_sizes_check = true; self } /// Creates a new Client. pub async fn build<'a>(self) -> Result, Error> { let client = if let Some(client) = self.client { client } else { let url = self.url.as_deref().unwrap_or("ws://127.0.0.1:9944"); if url.starts_with("ws://") || url.starts_with("wss://") { jsonrpsee::ws_client(url).await? } else { jsonrpsee::http_client(url) } }; let rpc = Rpc::new(client); let (metadata, genesis_hash, runtime_version, properties) = future::join4( rpc.metadata(), rpc.genesis_hash(), rpc.runtime_version(None), rpc.system_properties(), ) .await; let metadata = metadata?; if let Err(missing) = self.event_type_registry.check_missing_type_sizes(&metadata) { if self.skip_type_sizes_check { log::warn!( "The following types do not have registered type segmenters: {:?} \ If any events containing these types are received, this can cause a \ `TypeSizeUnavailable` error and prevent decoding the actual event \ being listened for.\ \ Use `ClientBuilder::register_type_size` to register missing type sizes.", missing ); } else { return Err(Error::MissingTypeSizes(missing.into_iter().collect())) } } let events_decoder = EventsDecoder::new(metadata.clone(), self.event_type_registry); Ok(Client { rpc, genesis_hash: genesis_hash?, metadata, events_decoder, properties: properties.unwrap_or_else(|_| Default::default()), runtime_version: runtime_version?, _marker: PhantomData, page_size: self.page_size.unwrap_or(10), }) } } /// Client to interface with a substrate node. pub struct Client { rpc: Rpc, genesis_hash: T::Hash, metadata: Metadata, events_decoder: EventsDecoder, properties: SystemProperties, runtime_version: RuntimeVersion, _marker: PhantomData<(fn() -> T::Signature, T::Extra)>, page_size: u32, } impl Clone for Client { fn clone(&self) -> Self { Self { rpc: self.rpc.clone(), genesis_hash: self.genesis_hash, metadata: self.metadata.clone(), events_decoder: self.events_decoder.clone(), properties: self.properties.clone(), runtime_version: self.runtime_version.clone(), _marker: PhantomData, page_size: self.page_size, } } } /// Iterates over key value pairs in a map. pub struct KeyIter> { client: Client, _marker: PhantomData, count: u32, hash: T::Hash, start_key: Option, buffer: Vec<(StorageKey, StorageData)>, } impl> KeyIter { /// Returns the next key value pair from a map. pub async fn next(&mut self) -> Result, Error> { loop { if let Some((k, v)) = self.buffer.pop() { return Ok(Some((k, Decode::decode(&mut &v.0[..])?))) } else { let keys = self .client .fetch_keys::(self.count, self.start_key.take(), Some(self.hash)) .await?; if keys.is_empty() { return Ok(None) } self.start_key = keys.last().cloned(); let change_sets = self .client .rpc .query_storage_at(&keys, Some(self.hash)) .await?; for change_set in change_sets { for (k, v) in change_set.changes { if let Some(v) = v { self.buffer.push((k, v)); } } } debug_assert_eq!(self.buffer.len(), keys.len()); } } } } impl Client { /// Returns the genesis hash. pub fn genesis(&self) -> &T::Hash { &self.genesis_hash } /// Returns the chain metadata. pub fn metadata(&self) -> &Metadata { &self.metadata } /// Returns the system properties pub fn properties(&self) -> &SystemProperties { &self.properties } /// Fetch the value under an unhashed storage key pub async fn fetch_unhashed( &self, key: StorageKey, hash: Option, ) -> Result, Error> { if let Some(data) = self.rpc.storage(&key, hash).await? { Ok(Some(Decode::decode(&mut &data.0[..])?)) } else { Ok(None) } } /// Fetch a StorageKey with an optional block hash. pub async fn fetch>( &self, store: &F, hash: Option, ) -> Result, Error> { let key = store.key(&self.metadata)?; self.fetch_unhashed::(key, hash).await } /// Fetch a StorageKey that has a default value with an optional block hash. pub async fn fetch_or_default>( &self, store: &F, hash: Option, ) -> Result { if let Some(data) = self.fetch(store, hash).await? { Ok(data) } else { Ok(store.default(&self.metadata)?) } } /// Returns an iterator of key value pairs. pub async fn iter>( &self, hash: Option, ) -> Result, Error> { let hash = if let Some(hash) = hash { hash } else { self.block_hash(None) .await? .expect("didn't pass a block number; qed") }; Ok(KeyIter { client: self.clone(), hash, count: self.page_size, start_key: None, buffer: Default::default(), _marker: PhantomData, }) } /// Fetch up to `count` keys for a storage map in lexicographic order. /// /// Supports pagination by passing a value to `start_key`. pub async fn fetch_keys>( &self, count: u32, start_key: Option, hash: Option, ) -> Result, Error> { let prefix = >::prefix(&self.metadata)?; let keys = self .rpc .storage_keys_paged(Some(prefix), count, start_key, hash) .await?; Ok(keys) } /// Query historical storage entries pub async fn query_storage( &self, keys: Vec, from: T::Hash, to: Option, ) -> Result::Hash>>, Error> { self.rpc.query_storage(keys, from, to).await } /// Get a header pub async fn header(&self, hash: Option) -> Result, Error> where H: Into + 'static, { let header = self.rpc.header(hash.map(|h| h.into())).await?; Ok(header) } /// Get a block hash. By default returns the latest block hash pub async fn block_hash( &self, block_number: Option, ) -> Result, Error> { let hash = self.rpc.block_hash(block_number).await?; Ok(hash) } /// Get a block hash of the latest finalized block pub async fn finalized_head(&self) -> Result { let head = self.rpc.finalized_head().await?; Ok(head) } /// Get a block pub async fn block(&self, hash: Option) -> Result>, Error> where H: Into + 'static, { let block = self.rpc.block(hash.map(|h| h.into())).await?; Ok(block) } /// Get proof of storage entries at a specific block's state. pub async fn read_proof( &self, keys: Vec, hash: Option, ) -> Result, Error> where H: Into + 'static, { let proof = self.rpc.read_proof(keys, hash.map(|h| h.into())).await?; Ok(proof) } /// Subscribe to events. pub async fn subscribe_events( &self, ) -> Result>, Error> { let events = self.rpc.subscribe_events().await?; Ok(events) } /// Subscribe to new blocks. pub async fn subscribe_blocks(&self) -> Result, Error> { let headers = self.rpc.subscribe_blocks().await?; Ok(headers) } /// Subscribe to finalized blocks. pub async fn subscribe_finalized_blocks( &self, ) -> Result, Error> { let headers = self.rpc.subscribe_finalized_blocks().await?; Ok(headers) } /// Encodes a call. pub fn encode>(&self, call: C) -> Result { Ok(self .metadata() .module_with_calls(C::MODULE) .and_then(|module| module.call(C::FUNCTION, call))?) } /// Creates an unsigned extrinsic. pub fn create_unsigned + Send + Sync>( &self, call: C, ) -> Result, Error> { let call = self.encode(call)?; Ok(extrinsic::create_unsigned::(call)) } /// Creates a signed extrinsic. pub async fn create_signed + Send + Sync>( &self, call: C, signer: &(dyn Signer + Send + Sync), ) -> Result, Error> where <>::Extra as SignedExtension>::AdditionalSigned: Send + Sync, { let account_nonce = if let Some(nonce) = signer.nonce() { nonce } else { self.account(signer.account_id(), None).await?.nonce }; let call = self.encode(call)?; let signed = extrinsic::create_signed( &self.runtime_version, self.genesis_hash, account_nonce, call, signer, ) .await?; Ok(signed) } /// Returns the events decoder. pub fn events_decoder(&self) -> &EventsDecoder { &self.events_decoder } /// Create and submit an extrinsic and return corresponding Hash if successful pub async fn submit_extrinsic( &self, extrinsic: UncheckedExtrinsic, ) -> Result { self.rpc.submit_extrinsic(extrinsic).await } /// Create and submit an extrinsic and return corresponding Event if successful pub async fn submit_and_watch_extrinsic( &self, extrinsic: UncheckedExtrinsic, ) -> Result, Error> { self.rpc .submit_and_watch_extrinsic(extrinsic, &self.events_decoder) .await } /// Submits a transaction to the chain. pub async fn submit + Send + Sync>( &self, call: C, signer: &(dyn Signer + Send + Sync), ) -> Result where <>::Extra as SignedExtension>::AdditionalSigned: Send + Sync, { let extrinsic = self.create_signed(call, signer).await?; self.submit_extrinsic(extrinsic).await } /// Submits transaction to the chain and watch for events. pub async fn watch + Send + Sync>( &self, call: C, signer: &(dyn Signer + Send + Sync), ) -> Result, Error> where <>::Extra as SignedExtension>::AdditionalSigned: Send + Sync, { let extrinsic = self.create_signed(call, signer).await?; self.submit_and_watch_extrinsic(extrinsic).await } /// Insert a key into the keystore. pub async fn insert_key( &self, key_type: String, suri: String, public: Bytes, ) -> Result<(), Error> { self.rpc.insert_key(key_type, suri, public).await } /// Generate new session keys and returns the corresponding public keys. pub async fn rotate_keys(&self) -> Result { self.rpc.rotate_keys().await } /// Checks if the keystore has private keys for the given session public keys. /// /// `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 { self.rpc.has_session_keys(session_keys).await } /// Checks if the keystore has private keys for the given public key and key type. /// /// Returns `true` if a private key could be found. pub async fn has_key( &self, public_key: Bytes, key_type: String, ) -> Result { self.rpc.has_key(public_key, key_type).await } } /// Wraps an already encoded byte vector, prevents being encoded as a raw byte vector as part of /// the transaction payload #[derive(Clone, Debug, Eq, PartialEq)] pub struct Encoded(pub Vec); impl codec::Encode for Encoded { fn encode(&self) -> Vec { self.0.to_owned() } } #[cfg(test)] mod tests { use super::*; use sp_core::storage::{ well_known_keys, StorageKey, }; use sp_keyring::AccountKeyring; use substrate_subxt_client::{ DatabaseConfig, KeystoreConfig, Role, SubxtClient, SubxtClientConfig, }; use tempdir::TempDir; pub(crate) type TestRuntime = crate::NodeTemplateRuntime; pub(crate) async fn test_client_with( key: AccountKeyring, ) -> (Client, TempDir) { env_logger::try_init().ok(); let tmp = TempDir::new("subxt-").expect("failed to create tempdir"); let config = SubxtClientConfig { impl_name: "substrate-subxt-full-client", impl_version: "0.0.1", author: "substrate subxt", copyright_start_year: 2020, db: DatabaseConfig::RocksDb { path: tmp.path().join("db"), cache_size: 128, }, keystore: KeystoreConfig::Path { path: tmp.path().join("keystore"), password: None, }, chain_spec: test_node::chain_spec::development_config().unwrap(), role: Role::Authority(key), telemetry: None, wasm_method: Default::default(), }; let client = ClientBuilder::new() .set_client( SubxtClient::from_config(config, test_node::service::new_full) .expect("Error creating subxt client"), ) .set_page_size(3) .build() .await .expect("Error creating client"); (client, tmp) } pub(crate) async fn test_client() -> (Client, TempDir) { test_client_with(AccountKeyring::Alice).await } #[async_std::test] async fn test_insert_key() { // Bob is not an authority, so block production should be disabled. let (client, _tmp) = test_client_with(AccountKeyring::Bob).await; let mut blocks = client.subscribe_blocks().await.unwrap(); // get the genesis block. assert_eq!(blocks.next().await.number, 0); let public = AccountKeyring::Alice.public().as_array_ref().to_vec(); client .insert_key( "aura".to_string(), "//Alice".to_string(), public.clone().into(), ) .await .unwrap(); assert!(client .has_key(public.clone().into(), "aura".to_string()) .await .unwrap()); // Alice is an authority, so blocks should be produced. assert_eq!(blocks.next().await.number, 1); } #[async_std::test] async fn test_tx_transfer_balance() { let mut signer = PairSigner::new(AccountKeyring::Alice.pair()); let dest = AccountKeyring::Bob.to_account_id().into(); let (client, _) = test_client().await; let nonce = client .account(&AccountKeyring::Alice.to_account_id(), None) .await .unwrap() .nonce; signer.set_nonce(nonce); client .submit( balances::TransferCall { to: &dest, amount: 10_000, }, &signer, ) .await .unwrap(); // check that nonce is handled correctly signer.increment_nonce(); client .submit( balances::TransferCall { to: &dest, amount: 10_000, }, &signer, ) .await .unwrap(); } #[async_std::test] async fn test_getting_hash() { let (client, _) = test_client().await; client.block_hash(None).await.unwrap(); } #[async_std::test] async fn test_getting_block() { let (client, _) = test_client().await; let block_hash = client.block_hash(None).await.unwrap(); client.block(block_hash).await.unwrap(); } #[async_std::test] async fn test_getting_read_proof() { let (client, _) = test_client().await; let block_hash = client.block_hash(None).await.unwrap(); client .read_proof( vec![ StorageKey(well_known_keys::HEAP_PAGES.to_vec()), StorageKey(well_known_keys::EXTRINSIC_INDEX.to_vec()), ], block_hash, ) .await .unwrap(); } #[async_std::test] async fn test_chain_subscribe_blocks() { let (client, _) = test_client().await; let mut blocks = client.subscribe_blocks().await.unwrap(); blocks.next().await; } #[async_std::test] async fn test_chain_subscribe_finalized_blocks() { let (client, _) = test_client().await; let mut blocks = client.subscribe_finalized_blocks().await.unwrap(); blocks.next().await; } #[async_std::test] async fn test_fetch_keys() { let (client, _) = test_client().await; let keys = client .fetch_keys::>(4, None, None) .await .unwrap(); assert_eq!(keys.len(), 4) } #[async_std::test] async fn test_iter() { let (client, _) = test_client().await; let mut iter = client.iter::>(None).await.unwrap(); let mut i = 0; while let Some(_) = iter.next().await.unwrap() { i += 1; } assert_eq!(i, 4); } }