mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 14:41:11 +00:00
Introduce Backend trait to allow different RPC (or other) backends to be implemented (#1126)
* WIP backend trait * WIP converting higher level stuff to using Backend impl * more implementing new backend trait, mainly storage focused * Get core code compiling with new backend bits * subxt crate checks passing * fix tests * cargo fmt * clippy/fixes * merging and other fixes * fix test * fix lightclient code * Fix some broken doc links * another book link fix * fix broken test when moving default_rpc_client * fix dry_run test * fix more tests; lightclient and wasm * fix wasm tests * fix some doc examples * use next() instead of next_item() * missing next_item() -> next()s * move legacy RPc methods to LegacyRpcMethods type to host generic param instead of RpcClient * standardise on all RpcClient types prefixed with Rpc, and 'raw' trait types prefixed with RawRpc so it's less ocnfusing which is which * rename fixes * doc fixes * Add back system_dryRun RPC method and rename tx.dry_run() to tx.validate(), to signal that the calls are different * Add a test that we return the correct extrinsic hash from submit() * add TransactionValid details back, and protect against out of range bytes * add test for decoding transaction validation from empty bytes * fix clippy warning
This commit is contained in:
@@ -3,12 +3,12 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{
|
||||
backend::BlockRef,
|
||||
blocks::{extrinsic_types::ExtrinsicPartTypeIds, Extrinsics},
|
||||
client::{OfflineClientT, OnlineClientT},
|
||||
config::{Config, Header},
|
||||
error::{BlockError, Error},
|
||||
events,
|
||||
rpc::types::ChainBlockResponse,
|
||||
runtime_api::RuntimeApi,
|
||||
storage::Storage,
|
||||
};
|
||||
@@ -19,6 +19,7 @@ use std::sync::Arc;
|
||||
/// A representation of a block.
|
||||
pub struct Block<T: Config, C> {
|
||||
header: T::Header,
|
||||
block_ref: BlockRef<T::Hash>,
|
||||
client: C,
|
||||
// Since we obtain the same events for every extrinsic, let's
|
||||
// cache them so that we only ever do that once:
|
||||
@@ -34,17 +35,24 @@ where
|
||||
T: Config,
|
||||
C: OfflineClientT<T>,
|
||||
{
|
||||
pub(crate) fn new(header: T::Header, client: C) -> Self {
|
||||
pub(crate) fn new(header: T::Header, block_ref: BlockRef<T::Hash>, client: C) -> Self {
|
||||
Block {
|
||||
header,
|
||||
block_ref,
|
||||
client,
|
||||
cached_events: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a reference to the given block. While this reference is kept alive,
|
||||
/// the backend will (if possible) endeavour to keep hold of the block.
|
||||
pub fn reference(&self) -> BlockRef<T::Hash> {
|
||||
self.block_ref.clone()
|
||||
}
|
||||
|
||||
/// Return the block hash.
|
||||
pub fn hash(&self) -> T::Hash {
|
||||
self.header.hash()
|
||||
self.block_ref.hash()
|
||||
}
|
||||
|
||||
/// Return the block number.
|
||||
@@ -68,72 +76,31 @@ where
|
||||
get_events(&self.client, self.header.hash(), &self.cached_events).await
|
||||
}
|
||||
|
||||
/// Fetch and return the block body.
|
||||
pub async fn body(&self) -> Result<BlockBody<T, C>, Error> {
|
||||
/// Fetch and return the extrinsics in the block body.
|
||||
pub async fn extrinsics(&self) -> Result<Extrinsics<T, C>, Error> {
|
||||
let ids = ExtrinsicPartTypeIds::new(&self.client.metadata())?;
|
||||
let block_hash = self.header.hash();
|
||||
let Some(block_details) = self.client.rpc().block(Some(block_hash)).await? else {
|
||||
let Some(extrinsics) = self.client.backend().block_body(block_hash).await? else {
|
||||
return Err(BlockError::not_found(block_hash).into());
|
||||
};
|
||||
|
||||
Ok(BlockBody::new(
|
||||
Ok(Extrinsics::new(
|
||||
self.client.clone(),
|
||||
block_details,
|
||||
extrinsics,
|
||||
self.cached_events.clone(),
|
||||
ids,
|
||||
block_hash,
|
||||
))
|
||||
}
|
||||
|
||||
/// Work with storage.
|
||||
pub fn storage(&self) -> Storage<T, C> {
|
||||
let block_hash = self.hash();
|
||||
Storage::new(self.client.clone(), block_hash)
|
||||
Storage::new(self.client.clone(), self.block_ref.clone())
|
||||
}
|
||||
|
||||
/// Execute a runtime API call at this block.
|
||||
pub async fn runtime_api(&self) -> Result<RuntimeApi<T, C>, Error> {
|
||||
Ok(RuntimeApi::new(self.client.clone(), self.hash()))
|
||||
}
|
||||
}
|
||||
|
||||
/// The body of a block.
|
||||
pub struct BlockBody<T: Config, C> {
|
||||
details: ChainBlockResponse<T>,
|
||||
client: C,
|
||||
cached_events: CachedEvents<T>,
|
||||
ids: ExtrinsicPartTypeIds,
|
||||
}
|
||||
|
||||
impl<T, C> BlockBody<T, C>
|
||||
where
|
||||
T: Config,
|
||||
C: OfflineClientT<T>,
|
||||
{
|
||||
pub(crate) fn new(
|
||||
client: C,
|
||||
details: ChainBlockResponse<T>,
|
||||
cached_events: CachedEvents<T>,
|
||||
ids: ExtrinsicPartTypeIds,
|
||||
) -> Self {
|
||||
Self {
|
||||
details,
|
||||
client,
|
||||
cached_events,
|
||||
ids,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over the extrinsics in the block body.
|
||||
// Dev note: The returned iterator is 'static + Send so that we can box it up and make
|
||||
// use of it with our `FilterExtrinsic` stuff.
|
||||
pub fn extrinsics(&self) -> Extrinsics<T, C> {
|
||||
Extrinsics::new(
|
||||
self.client.clone(),
|
||||
self.details.block.extrinsics.clone(),
|
||||
self.cached_events.clone(),
|
||||
self.ids,
|
||||
self.details.block.header.hash(),
|
||||
)
|
||||
Ok(RuntimeApi::new(self.client.clone(), self.block_ref.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
|
||||
use super::Block;
|
||||
use crate::{
|
||||
backend::{BlockRef, StreamOfResults},
|
||||
client::OnlineClientT,
|
||||
config::{Config, Header},
|
||||
config::Config,
|
||||
error::{BlockError, Error},
|
||||
utils::PhantomDataSendSync,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use futures::{future::Either, stream, Stream, StreamExt};
|
||||
use std::{future::Future, pin::Pin};
|
||||
use futures::StreamExt;
|
||||
use std::future::Future;
|
||||
|
||||
type BlockStream<T> = Pin<Box<dyn Stream<Item = Result<T, Error>> + Send>>;
|
||||
type BlockStream<T> = StreamOfResults<T>;
|
||||
type BlockStreamRes<T> = Result<BlockStream<T>, Error>;
|
||||
|
||||
/// A client for working with blocks.
|
||||
@@ -48,9 +49,9 @@ where
|
||||
/// but may run into errors attempting to work with them.
|
||||
pub fn at(
|
||||
&self,
|
||||
block_hash: T::Hash,
|
||||
block_ref: impl Into<BlockRef<T::Hash>>,
|
||||
) -> impl Future<Output = Result<Block<T, Client>, Error>> + Send + 'static {
|
||||
self.at_or_latest(Some(block_hash))
|
||||
self.at_or_latest(Some(block_ref.into()))
|
||||
}
|
||||
|
||||
/// Obtain block details of the latest block hash.
|
||||
@@ -64,27 +65,22 @@ where
|
||||
/// provided.
|
||||
fn at_or_latest(
|
||||
&self,
|
||||
block_hash: Option<T::Hash>,
|
||||
block_ref: Option<BlockRef<T::Hash>>,
|
||||
) -> impl Future<Output = Result<Block<T, Client>, Error>> + Send + 'static {
|
||||
let client = self.client.clone();
|
||||
async move {
|
||||
// If block hash is not provided, get the hash
|
||||
// for the latest block and use that.
|
||||
let block_hash = match block_hash {
|
||||
Some(hash) => hash,
|
||||
None => client
|
||||
.rpc()
|
||||
.block_hash(None)
|
||||
.await?
|
||||
.expect("didn't pass a block number; qed"),
|
||||
// If a block ref isn't provided, we'll get the latest best block to use.
|
||||
let block_ref = match block_ref {
|
||||
Some(r) => r,
|
||||
None => client.backend().latest_best_block_ref().await?,
|
||||
};
|
||||
|
||||
let block_header = match client.rpc().header(Some(block_hash)).await? {
|
||||
let block_header = match client.backend().block_header(block_ref.hash()).await? {
|
||||
Some(header) => header,
|
||||
None => return Err(BlockError::not_found(block_hash).into()),
|
||||
None => return Err(BlockError::not_found(block_ref.hash()).into()),
|
||||
};
|
||||
|
||||
Ok(Block::new(block_header, client))
|
||||
Ok(Block::new(block_header, block_ref, client))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,8 +96,8 @@ where
|
||||
{
|
||||
let client = self.client.clone();
|
||||
header_sub_fut_to_block_sub(self.clone(), async move {
|
||||
let sub = client.rpc().subscribe_all_block_headers().await?;
|
||||
BlockStreamRes::Ok(Box::pin(sub))
|
||||
let sub = client.backend().stream_all_block_headers().await?;
|
||||
BlockStreamRes::Ok(sub)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -117,8 +113,8 @@ where
|
||||
{
|
||||
let client = self.client.clone();
|
||||
header_sub_fut_to_block_sub(self.clone(), async move {
|
||||
let sub = client.rpc().subscribe_best_block_headers().await?;
|
||||
BlockStreamRes::Ok(Box::pin(sub))
|
||||
let sub = client.backend().stream_best_block_headers().await?;
|
||||
BlockStreamRes::Ok(sub)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -131,22 +127,8 @@ where
|
||||
{
|
||||
let client = self.client.clone();
|
||||
header_sub_fut_to_block_sub(self.clone(), async move {
|
||||
// Fetch the last finalised block details immediately, so that we'll get
|
||||
// all blocks after this one.
|
||||
let last_finalized_block_hash = client.rpc().finalized_head().await?;
|
||||
let last_finalized_block_num = client
|
||||
.rpc()
|
||||
.header(Some(last_finalized_block_hash))
|
||||
.await?
|
||||
.map(|h| h.number().into());
|
||||
|
||||
let sub = client.rpc().subscribe_finalized_block_headers().await?;
|
||||
|
||||
// Adjust the subscription stream to fill in any missing blocks.
|
||||
BlockStreamRes::Ok(
|
||||
subscribe_to_block_headers_filling_in_gaps(client, last_finalized_block_num, sub)
|
||||
.boxed(),
|
||||
)
|
||||
let sub = client.backend().stream_finalized_block_headers().await?;
|
||||
BlockStreamRes::Ok(sub)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -159,69 +141,19 @@ async fn header_sub_fut_to_block_sub<T, Client, S>(
|
||||
) -> Result<BlockStream<Block<T, Client>>, Error>
|
||||
where
|
||||
T: Config,
|
||||
S: Future<Output = Result<BlockStream<T::Header>, Error>> + Send + 'static,
|
||||
S: Future<Output = Result<BlockStream<(T::Header, BlockRef<T::Hash>)>, Error>> + Send + 'static,
|
||||
Client: OnlineClientT<T> + Send + Sync + 'static,
|
||||
{
|
||||
let sub = sub.await?.then(move |header| {
|
||||
let sub = sub.await?.then(move |header_and_ref| {
|
||||
let client = blocks_client.client.clone();
|
||||
async move {
|
||||
let header = match header {
|
||||
Ok(header) => header,
|
||||
let (header, block_ref) = match header_and_ref {
|
||||
Ok(header_and_ref) => header_and_ref,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
Ok(Block::new(header, client))
|
||||
Ok(Block::new(header, block_ref, client))
|
||||
}
|
||||
});
|
||||
BlockStreamRes::Ok(Box::pin(sub))
|
||||
}
|
||||
|
||||
/// Note: This is exposed for testing but is not considered stable and may change
|
||||
/// without notice in a patch release.
|
||||
#[doc(hidden)]
|
||||
pub fn subscribe_to_block_headers_filling_in_gaps<T, Client, S, E>(
|
||||
client: Client,
|
||||
mut last_block_num: Option<u64>,
|
||||
sub: S,
|
||||
) -> impl Stream<Item = Result<T::Header, Error>> + Send
|
||||
where
|
||||
T: Config,
|
||||
Client: OnlineClientT<T>,
|
||||
S: Stream<Item = Result<T::Header, E>> + Send,
|
||||
E: Into<Error> + Send + 'static,
|
||||
{
|
||||
sub.flat_map(move |s| {
|
||||
let client = client.clone();
|
||||
|
||||
// Get the header, or return a stream containing just the error.
|
||||
let header = match s {
|
||||
Ok(header) => header,
|
||||
Err(e) => return Either::Left(stream::once(async { Err(e.into()) })),
|
||||
};
|
||||
|
||||
// We want all previous details up to, but not including this current block num.
|
||||
let end_block_num = header.number().into();
|
||||
|
||||
// This is one after the last block we returned details for last time.
|
||||
let start_block_num = last_block_num.map(|n| n + 1).unwrap_or(end_block_num);
|
||||
|
||||
// Iterate over all of the previous blocks we need headers for, ignoring the current block
|
||||
// (which we already have the header info for):
|
||||
let previous_headers = stream::iter(start_block_num..end_block_num)
|
||||
.then(move |n| {
|
||||
let rpc = client.rpc().clone();
|
||||
async move {
|
||||
let hash = rpc.block_hash(Some(n.into())).await?;
|
||||
let header = rpc.header(hash).await?;
|
||||
Ok::<_, Error>(header)
|
||||
}
|
||||
})
|
||||
.filter_map(|h| async { h.transpose() });
|
||||
|
||||
// On the next iteration, we'll get details starting just after this end block.
|
||||
last_block_num = Some(end_block_num);
|
||||
|
||||
// Return a combination of any previous headers plus the new header.
|
||||
Either::Right(previous_headers.chain(stream::once(async { Ok(header) })))
|
||||
})
|
||||
BlockStreamRes::Ok(StreamOfResults::new(Box::pin(sub)))
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ use crate::{
|
||||
error::{BlockError, Error, MetadataError},
|
||||
events,
|
||||
metadata::types::PalletMetadata,
|
||||
rpc::types::ChainBlockExtrinsic,
|
||||
Metadata,
|
||||
};
|
||||
|
||||
@@ -40,7 +39,7 @@ pub trait StaticExtrinsic: DecodeAsFields {
|
||||
/// The body of a block.
|
||||
pub struct Extrinsics<T: Config, C> {
|
||||
client: C,
|
||||
extrinsics: Vec<ChainBlockExtrinsic>,
|
||||
extrinsics: Vec<Vec<u8>>,
|
||||
cached_events: CachedEvents<T>,
|
||||
ids: ExtrinsicPartTypeIds,
|
||||
hash: T::Hash,
|
||||
@@ -53,7 +52,7 @@ where
|
||||
{
|
||||
pub(crate) fn new(
|
||||
client: C,
|
||||
extrinsics: Vec<ChainBlockExtrinsic>,
|
||||
extrinsics: Vec<Vec<u8>>,
|
||||
cached_events: CachedEvents<T>,
|
||||
ids: ExtrinsicPartTypeIds,
|
||||
hash: T::Hash,
|
||||
@@ -103,7 +102,7 @@ where
|
||||
} else {
|
||||
match ExtrinsicDetails::decode_from(
|
||||
index as u32,
|
||||
&extrinsics[index].0,
|
||||
&extrinsics[index],
|
||||
client.clone(),
|
||||
hash,
|
||||
cached_events.clone(),
|
||||
@@ -562,7 +561,7 @@ impl<T: Config> ExtrinsicEvents<T> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{rpc::types::RuntimeVersion, OfflineClient, PolkadotConfig};
|
||||
use crate::{backend::RuntimeVersion, OfflineClient, PolkadotConfig};
|
||||
use assert_matches::assert_matches;
|
||||
use codec::{Decode, Encode};
|
||||
use frame_metadata::v15::{CustomMetadata, OuterEnums};
|
||||
@@ -697,7 +696,6 @@ mod tests {
|
||||
let rt_version = RuntimeVersion {
|
||||
spec_version: 1,
|
||||
transaction_version: 4,
|
||||
other: Default::default(),
|
||||
};
|
||||
let block_hash = H256::random();
|
||||
OfflineClient::new(block_hash, rt_version, metadata)
|
||||
|
||||
@@ -8,6 +8,9 @@ mod block_types;
|
||||
mod blocks_client;
|
||||
mod extrinsic_types;
|
||||
|
||||
pub use block_types::{Block, BlockBody};
|
||||
pub use blocks_client::{subscribe_to_block_headers_filling_in_gaps, BlocksClient};
|
||||
/// A reference to a block.
|
||||
pub use crate::backend::BlockRef;
|
||||
|
||||
pub use block_types::Block;
|
||||
pub use blocks_client::BlocksClient;
|
||||
pub use extrinsic_types::{ExtrinsicDetails, ExtrinsicEvents, Extrinsics, StaticExtrinsic};
|
||||
|
||||
Reference in New Issue
Block a user