// 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;
pub use sp_core;
pub use sp_runtime;
use codec::{
Codec,
Decode,
};
use futures::future;
use jsonrpsee_http_client::HttpClientBuilder;
use jsonrpsee_ws_client::{
Subscription,
WsClientBuilder,
};
use sp_core::{
storage::{
StorageChangeSet,
StorageData,
StorageKey,
},
Bytes,
};
pub use sp_runtime::traits::SignedExtension;
pub use sp_version::RuntimeVersion;
use std::{
marker::PhantomData,
sync::Arc,
};
mod error;
mod events;
pub mod extrinsic;
mod frame;
mod metadata;
mod rpc;
mod runtimes;
mod subscription;
#[cfg(test)]
mod tests;
pub use crate::{
error::{
Error,
ModuleError,
RuntimeError,
},
events::{
EventTypeRegistry,
EventsDecoder,
RawEvent,
},
extrinsic::{
PairSigner,
SignedExtra,
Signer,
UncheckedExtrinsic,
},
frame::*,
metadata::{
Metadata,
MetadataError,
},
rpc::{
BlockNumber,
ExtrinsicSuccess,
ReadProof,
RpcClient,
SystemProperties,
},
runtimes::*,
subscription::{
EventStorageSubscription,
EventSubscription,
FinalizedEventStorageSubscription,
},
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,
accept_weak_inclusion: 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,
accept_weak_inclusion: false,
}
}
/// Sets the jsonrpsee client.
pub fn set_client>(mut self, client: C) -> 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
}
/// Only check that transactions are InBlock on submit.
pub fn accept_weak_inclusion(mut self) -> Self {
self.accept_weak_inclusion = 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://") {
let client = WsClientBuilder::default()
.max_notifs_per_subscription(4096)
.build(url)
.await?;
RpcClient::WebSocket(Arc::new(client))
} else {
let client = HttpClientBuilder::default().build(&url)?;
RpcClient::Http(Arc::new(client))
}
};
let mut rpc = Rpc::new(client);
if self.accept_weak_inclusion {
rpc.accept_weak_inclusion();
}
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