mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 04:01:02 +00:00
Merge branch 'master' into tadeohepperle/support-decoding-signed-extensions
This commit is contained in:
+1
-1
@@ -103,7 +103,7 @@ sp-core = { workspace = true }
|
||||
sp-keyring = { workspace = true }
|
||||
sp-runtime = { workspace = true }
|
||||
assert_matches = { workspace = true }
|
||||
subxt-signer = { path = "../signer", features = ["subxt"] }
|
||||
subxt-signer = { path = "../signer" }
|
||||
# Tracing subscriber is useful for light-client examples to ensure that
|
||||
# the `bootNodes` and chain spec are configured correctly. If all is fine, then
|
||||
# the light-client wlll emit INFO logs with
|
||||
|
||||
@@ -71,15 +71,29 @@ impl<T: Config + Send + Sync + 'static> Backend<T> for LegacyBackend<T> {
|
||||
key: Vec<u8>,
|
||||
at: T::Hash,
|
||||
) -> Result<StreamOfResults<Vec<u8>>, Error> {
|
||||
Ok(StreamOf(Box::pin(StorageFetchDescendantKeysStream {
|
||||
let keys = StorageFetchDescendantKeysStream {
|
||||
at,
|
||||
key,
|
||||
methods: self.methods.clone(),
|
||||
done: Default::default(),
|
||||
keys: Default::default(),
|
||||
keys_fut: Default::default(),
|
||||
pagination_start_key: None,
|
||||
})))
|
||||
};
|
||||
|
||||
let keys = keys.flat_map(|keys| {
|
||||
match keys {
|
||||
Err(e) => {
|
||||
// If there's an error, return that next:
|
||||
Either::Left(stream::iter(std::iter::once(Err(e))))
|
||||
}
|
||||
Ok(keys) => {
|
||||
// Or, stream each "ok" value:
|
||||
Either::Right(stream::iter(keys.into_iter().map(Ok)))
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(StreamOf(Box::pin(keys)))
|
||||
}
|
||||
|
||||
async fn storage_fetch_descendant_values(
|
||||
@@ -92,15 +106,14 @@ impl<T: Config + Send + Sync + 'static> Backend<T> for LegacyBackend<T> {
|
||||
key,
|
||||
methods: self.methods.clone(),
|
||||
done: Default::default(),
|
||||
keys: Default::default(),
|
||||
keys_fut: Default::default(),
|
||||
pagination_start_key: Default::default(),
|
||||
pagination_start_key: None,
|
||||
};
|
||||
|
||||
Ok(StreamOf(Box::pin(StorageFetchDescendantValuesStream {
|
||||
keys: keys_stream,
|
||||
next_key: None,
|
||||
value_fut: Default::default(),
|
||||
results_fut: None,
|
||||
results: Default::default(),
|
||||
})))
|
||||
}
|
||||
|
||||
@@ -319,6 +332,9 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// How many keys/values to fetch at once.
|
||||
const STORAGE_PAGE_SIZE: u32 = 32;
|
||||
|
||||
/// This provides a stream of values given some prefix `key`. It
|
||||
/// internally manages pagination and such.
|
||||
pub struct StorageFetchDescendantKeysStream<T: Config> {
|
||||
@@ -329,33 +345,23 @@ pub struct StorageFetchDescendantKeysStream<T: Config> {
|
||||
pagination_start_key: Option<Vec<u8>>,
|
||||
// Keys, future and cached:
|
||||
keys_fut: Option<Pin<Box<dyn Future<Output = Result<Vec<Vec<u8>>, Error>> + Send + 'static>>>,
|
||||
keys: VecDeque<Vec<u8>>,
|
||||
// Set to true when we're done:
|
||||
done: bool,
|
||||
}
|
||||
|
||||
impl<T: Config> std::marker::Unpin for StorageFetchDescendantKeysStream<T> {}
|
||||
|
||||
// How many storage keys to ask for each time.
|
||||
const STORAGE_FETCH_PAGE_SIZE: u32 = 32;
|
||||
|
||||
impl<T: Config> Stream for StorageFetchDescendantKeysStream<T> {
|
||||
type Item = Result<Vec<u8>, Error>;
|
||||
type Item = Result<Vec<Vec<u8>>, Error>;
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let mut this = self.as_mut();
|
||||
loop {
|
||||
let mut this = self.as_mut();
|
||||
|
||||
// We're already done.
|
||||
if this.done {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
// We have some keys to hand back already, so do that.
|
||||
if let Some(key) = this.keys.pop_front() {
|
||||
return Poll::Ready(Some(Ok(key)));
|
||||
}
|
||||
|
||||
// Else, we don't have any keys, but we have a fut to get more so poll it.
|
||||
// Poll future to fetch next keys.
|
||||
if let Some(mut keys_fut) = this.keys_fut.take() {
|
||||
let Poll::Ready(keys) = keys_fut.poll_unpin(cx) else {
|
||||
this.keys_fut = Some(keys_fut);
|
||||
@@ -371,9 +377,8 @@ impl<T: Config> Stream for StorageFetchDescendantKeysStream<T> {
|
||||
}
|
||||
// The last key is where we want to paginate from next time.
|
||||
this.pagination_start_key = keys.last().cloned();
|
||||
// Got new keys; loop around to start returning them.
|
||||
this.keys = keys.into_iter().collect();
|
||||
continue;
|
||||
// return all of the keys from this run.
|
||||
return Poll::Ready(Some(Ok(keys)));
|
||||
}
|
||||
Err(e) => {
|
||||
// Error getting keys? Return it.
|
||||
@@ -391,7 +396,7 @@ impl<T: Config> Stream for StorageFetchDescendantKeysStream<T> {
|
||||
methods
|
||||
.state_get_keys_paged(
|
||||
&key,
|
||||
STORAGE_FETCH_PAGE_SIZE,
|
||||
STORAGE_PAGE_SIZE,
|
||||
pagination_start_key.as_deref(),
|
||||
Some(at),
|
||||
)
|
||||
@@ -406,10 +411,18 @@ impl<T: Config> Stream for StorageFetchDescendantKeysStream<T> {
|
||||
pub struct StorageFetchDescendantValuesStream<T: Config> {
|
||||
// Stream of keys.
|
||||
keys: StorageFetchDescendantKeysStream<T>,
|
||||
next_key: Option<Vec<u8>>,
|
||||
// Then we track the next value:
|
||||
value_fut:
|
||||
Option<Pin<Box<dyn Future<Output = Result<Option<Vec<u8>>, Error>> + Send + 'static>>>,
|
||||
// Then we track the future to get the values back for each key:
|
||||
results_fut: Option<
|
||||
Pin<
|
||||
Box<
|
||||
dyn Future<Output = Result<Option<VecDeque<(Vec<u8>, Vec<u8>)>>, Error>>
|
||||
+ Send
|
||||
+ 'static,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
// And finally we return each result back one at a time:
|
||||
results: VecDeque<(Vec<u8>, Vec<u8>)>,
|
||||
}
|
||||
|
||||
impl<T: Config> Stream for StorageFetchDescendantValuesStream<T> {
|
||||
@@ -417,47 +430,56 @@ impl<T: Config> Stream for StorageFetchDescendantValuesStream<T> {
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let mut this = self.as_mut();
|
||||
loop {
|
||||
// If we're waiting on the next value then poll that future:
|
||||
if let Some(mut value_fut) = this.value_fut.take() {
|
||||
match value_fut.poll_unpin(cx) {
|
||||
Poll::Ready(Ok(Some(value))) => {
|
||||
let key = this.next_key.take().expect("key should exist");
|
||||
return Poll::Ready(Some(Ok(StorageResponse { key, value })));
|
||||
// If we have results back, return them one by one
|
||||
if let Some((key, value)) = this.results.pop_front() {
|
||||
let res = StorageResponse { key, value };
|
||||
return Poll::Ready(Some(Ok(res)));
|
||||
}
|
||||
|
||||
// If we're waiting on the next results then poll that future:
|
||||
if let Some(mut results_fut) = this.results_fut.take() {
|
||||
match results_fut.poll_unpin(cx) {
|
||||
Poll::Ready(Ok(Some(results))) => {
|
||||
this.results = results;
|
||||
continue;
|
||||
}
|
||||
Poll::Ready(Ok(None)) => {
|
||||
// No value back for some key? Skip.
|
||||
// No values back for some keys? Skip.
|
||||
continue;
|
||||
}
|
||||
Poll::Ready(Err(e)) => return Poll::Ready(Some(Err(e))),
|
||||
Poll::Pending => {
|
||||
this.value_fut = Some(value_fut);
|
||||
this.results_fut = Some(results_fut);
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Else, if we have the next key then let's start waiting on the next value.
|
||||
if let Some(key) = &this.next_key {
|
||||
let key = key.clone();
|
||||
let methods = this.keys.methods.clone();
|
||||
let at = this.keys.at;
|
||||
let fut = async move { methods.state_get_storage(&key, Some(at)).await };
|
||||
|
||||
this.value_fut = Some(Box::pin(fut));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Else, poll the keys stream to get the next key.
|
||||
match this.keys.poll_next_unpin(cx) {
|
||||
Poll::Ready(Some(Ok(key))) => {
|
||||
this.next_key = Some(key);
|
||||
Poll::Ready(Some(Ok(keys))) => {
|
||||
let methods = this.keys.methods.clone();
|
||||
let at = this.keys.at;
|
||||
let results_fut = async move {
|
||||
let keys = keys.iter().map(|k| &**k);
|
||||
let values = methods.state_query_storage_at(keys, Some(at)).await?;
|
||||
let values: VecDeque<_> = values
|
||||
.into_iter()
|
||||
.flat_map(|v| {
|
||||
v.changes.into_iter().filter_map(|(k, v)| {
|
||||
let v = v?;
|
||||
Some((k.0, v.0))
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
Ok(Some(values))
|
||||
};
|
||||
|
||||
this.results_fut = Some(Box::pin(results_fut));
|
||||
continue;
|
||||
}
|
||||
Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(e))),
|
||||
Poll::Ready(None) => return Poll::Ready(None),
|
||||
Poll::Pending => {
|
||||
return Poll::Pending;
|
||||
}
|
||||
Poll::Pending => return Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,35 +8,20 @@ use crate::backend::rpc::{rpc_params, RpcClient, RpcSubscription};
|
||||
use crate::metadata::Metadata;
|
||||
use crate::{Config, Error};
|
||||
use codec::Decode;
|
||||
use derivative::Derivative;
|
||||
use primitive_types::U256;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// An interface to call the legacy RPC methods. This interface is instantiated with
|
||||
/// some `T: Config` trait which determines some of the types that the RPC methods will
|
||||
/// take or hand back.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone(bound = ""), Debug(bound = ""))]
|
||||
pub struct LegacyRpcMethods<T> {
|
||||
client: RpcClient,
|
||||
_marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Clone for LegacyRpcMethods<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
client: self.client.clone(),
|
||||
_marker: self._marker,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::fmt::Debug for LegacyRpcMethods<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("LegacyRpcMethods")
|
||||
.field("client", &self.client)
|
||||
.field("_marker", &self._marker)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> LegacyRpcMethods<T> {
|
||||
/// Instantiate the legacy RPC method interface.
|
||||
pub fn new(client: RpcClient) -> Self {
|
||||
@@ -73,7 +58,10 @@ impl<T: Config> LegacyRpcMethods<T> {
|
||||
Ok(data.into_iter().map(|b| b.0).collect())
|
||||
}
|
||||
|
||||
/// Query historical storage entries
|
||||
/// Query historical storage entries in the range from the start block to the end block,
|
||||
/// defaulting the end block to the current best block if it's not given. The first
|
||||
/// [`StorageChangeSet`] returned has all of the values for each key, and subsequent ones
|
||||
/// only contain values for any keys which have changed since the last.
|
||||
pub async fn state_query_storage(
|
||||
&self,
|
||||
keys: impl IntoIterator<Item = &[u8]>,
|
||||
@@ -88,7 +76,9 @@ impl<T: Config> LegacyRpcMethods<T> {
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Query historical storage entries
|
||||
/// Query storage entries at some block, using the best block if none is given.
|
||||
/// This essentially provides a way to ask for a batch of values given a batch of keys,
|
||||
/// despite the name of the [`StorageChangeSet`] type.
|
||||
pub async fn state_query_storage_at(
|
||||
&self,
|
||||
keys: impl IntoIterator<Item = &[u8]>,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
use crate::backend::rpc::{rpc_params, RpcClient, RpcSubscription};
|
||||
use crate::config::BlockHash;
|
||||
use crate::{Config, Error};
|
||||
use derivative::Derivative;
|
||||
use futures::{Stream, StreamExt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
@@ -17,29 +18,13 @@ use std::task::Poll;
|
||||
/// An interface to call the unstable RPC methods. This interface is instantiated with
|
||||
/// some `T: Config` trait which determines some of the types that the RPC methods will
|
||||
/// take or hand back.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone(bound = ""), Debug(bound = ""))]
|
||||
pub struct UnstableRpcMethods<T> {
|
||||
client: RpcClient,
|
||||
_marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Clone for UnstableRpcMethods<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
client: self.client.clone(),
|
||||
_marker: self._marker,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::fmt::Debug for UnstableRpcMethods<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("UnstableRpcMethods")
|
||||
.field("client", &self.client)
|
||||
.field("_marker", &self._marker)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> UnstableRpcMethods<T> {
|
||||
/// Instantiate the legacy RPC method interface.
|
||||
pub fn new(client: RpcClient) -> Self {
|
||||
|
||||
@@ -4,45 +4,43 @@
|
||||
|
||||
//! # Light Client
|
||||
//!
|
||||
//! The Light Client aims to contribute to the decentralization of blockchains by providing connectivity
|
||||
//! to the P2P network and behaving similarly to a full node.
|
||||
//! The light client based interface uses _Smoldot_ to connect to a _chain_, rather than an individual
|
||||
//! node. This means that you don't have to trust a specific node when interacting with some chain.
|
||||
//!
|
||||
//! To enable this functionality, the unstable-light-client feature flag needs to be enabled.
|
||||
//! To enable light client for WASM environments, also enable the web feature flag.
|
||||
//! This feature is currently unstable. Use the `unstable-light-client` feature flag to enable it.
|
||||
//! To use this in WASM environments, also enable the `web` feature flag.
|
||||
//!
|
||||
//! To connect to a blockchain network, the Light Client requires a trusted sync state of the network, named "chain spec".
|
||||
//! This can be obtained by making a `sync_state_genSyncSpec` RPC call to a trusted node.
|
||||
//! To connect to a blockchain network, the Light Client requires a trusted sync state of the network,
|
||||
//! known as a _chain spec_. One way to obtain this is by making a `sync_state_genSyncSpec` RPC call to a
|
||||
//! trusted node belonging to the chain that you wish to interact with.
|
||||
//!
|
||||
//! The following is an example of fetching the chain spec from a local running onde on port 9933.
|
||||
//! The following is an example of fetching the chain spec from a local running node on port 9933:
|
||||
//!
|
||||
//! ```bash
|
||||
//! curl -H "Content-Type: application/json" -d '{"id":1, "jsonrpc":"2.0", "method": "sync_state_genSyncSpec", "params":[true]}' http://localhost:9933/ | jq .result > chain_spec.json
|
||||
//! ```
|
||||
//!
|
||||
//! Alternately, you can have the `LightClient` download the chain spec from a trusted node when it
|
||||
//! initializes, which is not recommended in production but is useful for examples and testing, as below.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! You can construct a Light Client from a trusted chain spec stored on disk.
|
||||
//! Similary, the Light Client can fetch the chain spec from a running node and
|
||||
//! overwrite the bootNodes section. The `jsonrpsee` feature flag exposes the
|
||||
//! `build_from_url` method.
|
||||
//! This example connects to a local chain and submits a transaction. To run this, you first need
|
||||
//! to have a local polkadot node running using the following command:
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! let light_client = LightClientBuilder::new()
|
||||
//! .bootnodes(
|
||||
//! ["/ip4/127.0.0.1/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp"]
|
||||
//! )
|
||||
//! .build_from_url("ws://127.0.0.1:9944")
|
||||
//! .await?;
|
||||
//! ```text
|
||||
//! polkadot --dev --node-key 0000000000000000000000000000000000000000000000000000000000000001
|
||||
//! ```
|
||||
//!
|
||||
//! Here's an example which connects to a local chain and submits a transaction.
|
||||
//!
|
||||
//! You can run the example using the following command:
|
||||
//! Leave that running for a minute, and then you can run the example using the following command
|
||||
//! in the `subxt` crate:
|
||||
//!
|
||||
//! ```bash
|
||||
//! cargo run --example unstable_light_client_tx_basic --features="unstable-light-client jsonrpsee"
|
||||
//! cargo run --example unstable_light_client_tx_basic --features=unstable-light-client
|
||||
//! ```
|
||||
//!
|
||||
//! This is the code that will be executed:
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../examples/unstable_light_client_tx_basic.rs")]
|
||||
//! ```
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::{rpc::LightClientRpc, LightClient, LightClientError};
|
||||
use crate::backend::{rpc::RpcClient, Backend};
|
||||
use crate::backend::rpc::RpcClient;
|
||||
use crate::{config::Config, error::Error, OnlineClient};
|
||||
use std::num::NonZeroU32;
|
||||
use subxt_lightclient::{AddChainConfig, AddChainConfigJsonRpc, ChainId};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{dynamic::DecodedValueThunk, metadata::DecodeWithMetadata};
|
||||
use derivative::Derivative;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// This represents a constant address. Anything implementing this trait
|
||||
@@ -26,6 +27,8 @@ pub trait ConstantAddress {
|
||||
}
|
||||
|
||||
/// This represents the address of a constant.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone(bound = ""), Debug(bound = ""))]
|
||||
pub struct Address<ReturnTy> {
|
||||
pallet_name: Cow<'static, str>,
|
||||
constant_name: Cow<'static, str>,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use derivative::Derivative;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::dynamic::DecodedValueThunk;
|
||||
@@ -36,6 +37,8 @@ impl CustomValueAddress for str {
|
||||
pub struct Yes;
|
||||
|
||||
/// A static address to a custom value.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone(bound = ""), Debug(bound = ""))]
|
||||
pub struct StaticAddress<ReturnTy, IsDecodable> {
|
||||
name: &'static str,
|
||||
hash: Option<[u8; 32]>,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use derivative::Derivative;
|
||||
use scale_encode::EncodeAsFields;
|
||||
use scale_value::Composite;
|
||||
use std::borrow::Cow;
|
||||
@@ -65,7 +66,11 @@ pub trait RuntimeApiPayload {
|
||||
///
|
||||
/// This can be created from static values (ie those generated
|
||||
/// via the `subxt` macro) or dynamic values via [`dynamic`].
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Derivative)]
|
||||
#[derivative(
|
||||
Clone(bound = "ArgsData: Clone"),
|
||||
Debug(bound = "ArgsData: std::fmt::Debug")
|
||||
)]
|
||||
pub struct Payload<ArgsData, ReturnTy> {
|
||||
trait_name: Cow<'static, str>,
|
||||
method_name: Cow<'static, str>,
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::{
|
||||
metadata::{DecodeWithMetadata, EncodeWithMetadata, Metadata},
|
||||
utils::{Encoded, Static},
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use scale_info::TypeDef;
|
||||
use std::borrow::Cow;
|
||||
use subxt_metadata::{StorageEntryType, StorageHasher};
|
||||
@@ -51,6 +52,11 @@ pub struct Yes;
|
||||
|
||||
/// A concrete storage address. This can be created from static values (ie those generated
|
||||
/// via the `subxt` macro) or dynamic values via [`dynamic`].
|
||||
#[derive(Derivative)]
|
||||
#[derivative(
|
||||
Clone(bound = "StorageKey: Clone"),
|
||||
Debug(bound = "StorageKey: std::fmt::Debug")
|
||||
)]
|
||||
pub struct Address<StorageKey, ReturnTy, Fetchable, Defaultable, Iterable> {
|
||||
pallet_name: Cow<'static, str>,
|
||||
entry_name: Cow<'static, str>,
|
||||
|
||||
Reference in New Issue
Block a user