mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-23 21:15:41 +00:00
@@ -58,31 +58,34 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let block = block?;
|
let block = block?;
|
||||||
|
|
||||||
let block_hash = block.hash();
|
let block_hash = block.hash();
|
||||||
|
println!(" Block {:?}", block_hash);
|
||||||
|
|
||||||
println!(" Block {:?}", block_hash);
|
|
||||||
println!(" Block {:?}", block_hash);
|
|
||||||
// Ask for the extrinsics for this block.
|
// Ask for the extrinsics for this block.
|
||||||
for extrinsic in block.body().await?.extrinsics() {
|
let extrinsics = block.extrinsics().await?;
|
||||||
|
|
||||||
|
// Ask for the extrinsics for this block.
|
||||||
|
for extrinsic in extrinsics.iter() {
|
||||||
|
let extrinsic = extrinsic?;
|
||||||
println!(" Extrinsic index {:?}", extrinsic.index());
|
println!(" Extrinsic index {:?}", extrinsic.index());
|
||||||
|
|
||||||
let decoded = extrinsic.decode_generic();
|
// let decoded = extrinsic.decode_generic();
|
||||||
match decoded {
|
// match decoded {
|
||||||
Ok(decoded) => {
|
// Ok(decoded) => {
|
||||||
if let Some((address, signature, extra)) = decoded.signature {
|
// if let Some((address, signature, extra)) = decoded.signature {
|
||||||
println!(" Decoded Signature");
|
// println!(" Decoded Signature");
|
||||||
println!(" Decoded Address: {:?}", address);
|
// println!(" Decoded Address: {:?}", address);
|
||||||
println!(" Decoded Sign: {:?}", signature);
|
// println!(" Decoded Sign: {:?}", signature);
|
||||||
println!(" Decoded Extra: {:?}", extra);
|
// println!(" Decoded Extra: {:?}", extra);
|
||||||
};
|
// };
|
||||||
|
|
||||||
let call = decoded.function;
|
// let call = decoded.function;
|
||||||
println!(" Decoded call:\n {:?}", call.to_value());
|
// println!(" Decoded call:\n {:?}", call.to_value());
|
||||||
}
|
// }
|
||||||
Err(err) => {
|
// Err(err) => {
|
||||||
println!(" Decoded extrinsic with error: {:?}", err);
|
// println!(" Decoded extrinsic with error: {:?}", err);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
println!("\n");
|
// println!("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("\n");
|
println!("\n");
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::{
|
|||||||
config::{Config, Hasher, Header},
|
config::{Config, Hasher, Header},
|
||||||
dynamic::DecodedValueThunk,
|
dynamic::DecodedValueThunk,
|
||||||
error::{BlockError, Error},
|
error::{BlockError, Error},
|
||||||
events,
|
events, extrinsics,
|
||||||
metadata::DecodeWithMetadata,
|
metadata::DecodeWithMetadata,
|
||||||
rpc::types::ChainBlockResponse,
|
rpc::types::ChainBlockResponse,
|
||||||
runtime_api::RuntimeApi,
|
runtime_api::RuntimeApi,
|
||||||
@@ -72,13 +72,15 @@ where
|
|||||||
get_events(&self.client, self.header.hash(), &self.cached_events).await
|
get_events(&self.client, self.header.hash(), &self.cached_events).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the extrinsics associated with the block.
|
||||||
|
pub async fn extrinsics(&self) -> Result<extrinsics::Extrinsics<T>, Error> {
|
||||||
|
let block_details = self.block_details().await?;
|
||||||
|
extrinsics::Extrinsics::new(self.client.metadata(), block_details.block)
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch and return the block body.
|
/// Fetch and return the block body.
|
||||||
pub async fn body(&self) -> Result<BlockBody<T, C>, Error> {
|
pub async fn body(&self) -> Result<BlockBody<T, C>, Error> {
|
||||||
let block_hash = self.header.hash();
|
let block_details = self.block_details().await?;
|
||||||
let block_details = match self.client.rpc().block(Some(block_hash)).await? {
|
|
||||||
Some(block) => block,
|
|
||||||
None => return Err(BlockError::not_found(block_hash).into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(BlockBody::new(
|
Ok(BlockBody::new(
|
||||||
self.client.clone(),
|
self.client.clone(),
|
||||||
@@ -97,6 +99,15 @@ where
|
|||||||
pub async fn runtime_api(&self) -> Result<RuntimeApi<T, C>, Error> {
|
pub async fn runtime_api(&self) -> Result<RuntimeApi<T, C>, Error> {
|
||||||
Ok(RuntimeApi::new(self.client.clone(), self.hash()))
|
Ok(RuntimeApi::new(self.client.clone(), self.hash()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch the block's body from the chain.
|
||||||
|
async fn block_details(&self) -> Result<ChainBlockResponse<T>, Error> {
|
||||||
|
let block_hash = self.header.hash();
|
||||||
|
match self.client.rpc().block(Some(block_hash)).await? {
|
||||||
|
Some(block) => Ok(block),
|
||||||
|
None => Err(BlockError::not_found(block_hash).into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic type IDs passed to the `UncheckedExtrinsic`.
|
/// Generic type IDs passed to the `UncheckedExtrinsic`.
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ pub enum Error {
|
|||||||
/// Block related error.
|
/// Block related error.
|
||||||
#[error("Block error: {0}")]
|
#[error("Block error: {0}")]
|
||||||
Block(#[from] BlockError),
|
Block(#[from] BlockError),
|
||||||
|
/// Extrinsic related error.
|
||||||
|
#[error("Extrinsic error: {0}")]
|
||||||
|
Extrinsic(#[from] ExtrinsicError),
|
||||||
/// An error encoding a storage address.
|
/// An error encoding a storage address.
|
||||||
#[error("Error encoding storage address: {0}")]
|
#[error("Error encoding storage address: {0}")]
|
||||||
StorageAddress(#[from] StorageAddressError),
|
StorageAddress(#[from] StorageAddressError),
|
||||||
@@ -112,6 +115,25 @@ impl BlockError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extrinsic error.
|
||||||
|
#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum ExtrinsicError {
|
||||||
|
/// Extrinsic type ID cannot be resolved with the provided metadata.
|
||||||
|
#[error("Extrinsic type ID cannot be resolved with the provided metadata. Make sure this is a valid metadata")]
|
||||||
|
MissingType,
|
||||||
|
/// Expected more extrinsic bytes.
|
||||||
|
#[error("Expected more extrinsic bytes")]
|
||||||
|
InsufficientData,
|
||||||
|
/// Unsupported signature.
|
||||||
|
#[error("Unsupported extrinsic version, only version 4 is supported currently")]
|
||||||
|
/// The extrinsic has an unsupported version.
|
||||||
|
UnsupportedVersion(u8),
|
||||||
|
/// Decoding error.
|
||||||
|
#[error("Cannot decode extrinsic: {0}")]
|
||||||
|
DecodingError(codec::Error),
|
||||||
|
}
|
||||||
|
|
||||||
/// Transaction error.
|
/// Transaction error.
|
||||||
#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
|
#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||||
|
// see LICENSE for license details.
|
||||||
|
|
||||||
|
use crate::config::Header;
|
||||||
|
use crate::{
|
||||||
|
client::OnlineClientT,
|
||||||
|
error::{BlockError, Error},
|
||||||
|
extrinsics::Extrinsics,
|
||||||
|
Config,
|
||||||
|
};
|
||||||
|
use derivative::Derivative;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
/// A client for working with extrinsics.
|
||||||
|
#[derive(Derivative)]
|
||||||
|
#[derivative(Clone(bound = "Client: Clone"))]
|
||||||
|
pub struct ExtrinsicsClient<T, Client> {
|
||||||
|
client: Client,
|
||||||
|
_marker: std::marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, Client> ExtrinsicsClient<T, Client> {
|
||||||
|
/// Create a new [`ExtrinsicsClient`].
|
||||||
|
pub fn new(client: Client) -> Self {
|
||||||
|
Self {
|
||||||
|
client,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, Client> ExtrinsicsClient<T, Client>
|
||||||
|
where
|
||||||
|
T: Config,
|
||||||
|
Client: OnlineClientT<T>,
|
||||||
|
{
|
||||||
|
/// Obtain extrinsics at some block hash.
|
||||||
|
///
|
||||||
|
/// # Warning
|
||||||
|
///
|
||||||
|
/// This call only supports blocks produced since the most recent
|
||||||
|
/// runtime upgrade. You can attempt to retrieve extrinsics from older blocks,
|
||||||
|
/// but may run into errors attempting to work with them.
|
||||||
|
pub fn at(
|
||||||
|
&self,
|
||||||
|
block_hash: T::Hash,
|
||||||
|
) -> impl Future<Output = Result<Extrinsics<T>, Error>> + Send + 'static {
|
||||||
|
self.at_or_latest(Some(block_hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain extrinsics at the latest block hash.
|
||||||
|
pub fn at_latest(&self) -> impl Future<Output = Result<Extrinsics<T>, Error>> + Send + 'static {
|
||||||
|
self.at_or_latest(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain extrinsics at some block hash.
|
||||||
|
fn at_or_latest(
|
||||||
|
&self,
|
||||||
|
block_hash: Option<T::Hash>,
|
||||||
|
) -> impl Future<Output = Result<Extrinsics<T>, Error>> + Send + 'static {
|
||||||
|
// Clone and pass the client in like this so that we can explicitly
|
||||||
|
// return a Future that's Send + 'static, rather than tied to &self.
|
||||||
|
let client = self.client.clone();
|
||||||
|
async move {
|
||||||
|
// If block hash is not provided, get the hash
|
||||||
|
// for the latest block and use that to create an explicit error.
|
||||||
|
let block_hash = match block_hash {
|
||||||
|
Some(hash) => hash,
|
||||||
|
None => client
|
||||||
|
.rpc()
|
||||||
|
.block_hash(None)
|
||||||
|
.await?
|
||||||
|
.expect("didn't pass a block number; qed"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = client.rpc().block(Some(block_hash)).await?;
|
||||||
|
let Some(block_details) = result else {
|
||||||
|
return Err(BlockError::not_found(block_hash).into());
|
||||||
|
};
|
||||||
|
|
||||||
|
Extrinsics::new(client.metadata(), block_details.block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,316 @@
|
|||||||
|
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||||
|
// see LICENSE for license details.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
client::{OfflineClientT, OnlineClientT},
|
||||||
|
config::{Config, Hasher, Header},
|
||||||
|
dynamic::DecodedValueThunk,
|
||||||
|
error::{BlockError, Error, ExtrinsicError},
|
||||||
|
events,
|
||||||
|
metadata::DecodeWithMetadata,
|
||||||
|
rpc::types::{ChainBlock, ChainBlockExtrinsic, ChainBlockResponse},
|
||||||
|
runtime_api::RuntimeApi,
|
||||||
|
storage::Storage,
|
||||||
|
Metadata,
|
||||||
|
};
|
||||||
|
use codec::{Codec, Decode, Encode};
|
||||||
|
use derivative::Derivative;
|
||||||
|
use frame_metadata::v15::RuntimeMetadataV15;
|
||||||
|
use futures::lock::Mutex as AsyncMutex;
|
||||||
|
use scale_value::{scale::decode_as_type, Value};
|
||||||
|
use std::{collections::HashMap, marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
|
/// A collection of extrinsics obtained from a block, bundled with the necessary
|
||||||
|
/// information needed to decode and iterate over them.
|
||||||
|
#[derive(Derivative)]
|
||||||
|
#[derivative(Debug(bound = ""), Clone(bound = ""))]
|
||||||
|
pub struct Extrinsics<T: Config> {
|
||||||
|
metadata: Metadata,
|
||||||
|
/// The block hash.
|
||||||
|
hash: T::Hash,
|
||||||
|
/// The accompanying extrinsics.
|
||||||
|
extrinsics: Vec<ChainBlockExtrinsic>,
|
||||||
|
/// Generic extrinsic parameter ids from the metadata.
|
||||||
|
ids: ExtrinsicIds,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Config> Extrinsics<T> {
|
||||||
|
pub(crate) fn new(metadata: Metadata, block: ChainBlock<T>) -> Result<Self, Error> {
|
||||||
|
let ids = ExtrinsicIds::new(metadata.runtime_metadata())?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
metadata,
|
||||||
|
hash: block.header.hash(),
|
||||||
|
extrinsics: block.extrinsics,
|
||||||
|
ids,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// /// Obtain the extrinsics from a block hash given custom metadata and a client.
|
||||||
|
// ///
|
||||||
|
// /// This method gives users the ability to inspect the extrinsics of older blocks,
|
||||||
|
// /// where the metadata changed. For those cases, the user is responsible for
|
||||||
|
// /// providing a valid metadata.
|
||||||
|
// pub async fn new_from_client<Client>(
|
||||||
|
// metadata: Metadata,
|
||||||
|
// block_hash: T::Hash,
|
||||||
|
// client: Client,
|
||||||
|
// ) -> Result<Self, Error>
|
||||||
|
// where
|
||||||
|
// Client: OnlineClientT<T>,
|
||||||
|
// {
|
||||||
|
// // let event_bytes = get_event_bytes(&client, Some(block_hash)).await?;
|
||||||
|
// // Ok(Events::new(metadata, block_hash, event_bytes))
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// The number of extrinsics.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.extrinsics.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Are there no extrinsics in this block?
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.extrinsics.len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the block hash that these extrinsics are from.
|
||||||
|
pub fn block_hash(&self) -> T::Hash {
|
||||||
|
self.hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over all of the events, using metadata to dynamically
|
||||||
|
/// decode them as we go, and returning the raw bytes and other associated
|
||||||
|
/// details. If an error occurs, all subsequent iterations return `None`.
|
||||||
|
// Dev note: The returned iterator is 'static + Send so that we can box it up and make
|
||||||
|
// use of it with our `FilterEvents` stuff.
|
||||||
|
pub fn iter(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = Result<ExtrinsicDetails, Error>> + Send + Sync + 'static {
|
||||||
|
let metadata = self.metadata.clone();
|
||||||
|
// TODO: Dummy clone should use Arc<[u8]>.
|
||||||
|
let extrinsics = self.extrinsics.clone();
|
||||||
|
let ids = self.ids.clone();
|
||||||
|
let num_extr = self.extrinsics.len();
|
||||||
|
let mut index = 0;
|
||||||
|
std::iter::from_fn(move || {
|
||||||
|
if index == num_extr {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
match ExtrinsicDetails::decode_from::<T>(
|
||||||
|
metadata.clone(),
|
||||||
|
extrinsics[index].clone(),
|
||||||
|
ids,
|
||||||
|
index,
|
||||||
|
) {
|
||||||
|
Ok(event_details) => {
|
||||||
|
// Increment the index:
|
||||||
|
index += 1;
|
||||||
|
// Return the event details:
|
||||||
|
Some(Ok(event_details))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// By setting the position to the "end" of the event bytes,
|
||||||
|
// the cursor len will become 0 and the iterator will return `None`
|
||||||
|
// from now on:
|
||||||
|
index = num_extr;
|
||||||
|
Some(Err(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// /// Iterate through the events using metadata to dynamically decode and skip
|
||||||
|
// /// them, and return only those which should decode to the provided `Ev` type.
|
||||||
|
// /// If an error occurs, all subsequent iterations return `None`.
|
||||||
|
// pub fn find<Ev: StaticEvent>(&self) -> impl Iterator<Item = Result<Ev, Error>> + '_ {
|
||||||
|
// self.iter().filter_map(|ev| {
|
||||||
|
// ev.and_then(|ev| ev.as_event::<Ev>().map_err(Into::into))
|
||||||
|
// .transpose()
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Iterate through the events using metadata to dynamically decode and skip
|
||||||
|
// /// them, and return the first event found which decodes to the provided `Ev` type.
|
||||||
|
// pub fn find_first<Ev: StaticEvent>(&self) -> Result<Option<Ev>, Error> {
|
||||||
|
// self.find::<Ev>().next().transpose()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Iterate through the events using metadata to dynamically decode and skip
|
||||||
|
// /// them, and return the last event found which decodes to the provided `Ev` type.
|
||||||
|
// pub fn find_last<Ev: StaticEvent>(&self) -> Result<Option<Ev>, Error> {
|
||||||
|
// self.find::<Ev>().last().transpose()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Find an event that decodes to the type provided. Returns true if it was found.
|
||||||
|
// pub fn has<Ev: StaticEvent>(&self) -> Result<bool, Error> {
|
||||||
|
// Ok(self.find::<Ev>().next().transpose()?.is_some())
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type IDs extracted from the metadata that represent the
|
||||||
|
/// generic type parameters passed to the `UncheckedExtrinsic` from
|
||||||
|
/// the substrate-based chain.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
struct ExtrinsicIds {
|
||||||
|
/// The address (source) of the extrinsic.
|
||||||
|
address: u32,
|
||||||
|
/// The extrinsic call type.
|
||||||
|
call: u32,
|
||||||
|
/// The signature of the extrinsic.
|
||||||
|
signature: u32,
|
||||||
|
/// The extra parameters of the extrinsic.
|
||||||
|
extra: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtrinsicIds {
|
||||||
|
/// Extract the generic type parameters IDs from the extrinsic type.
|
||||||
|
fn new(metadata: &RuntimeMetadataV15) -> Result<Self, ExtrinsicError> {
|
||||||
|
const ADDRESS: &str = "Address";
|
||||||
|
const CALL: &str = "Call";
|
||||||
|
const SIGNATURE: &str = "Signature";
|
||||||
|
const EXTRA: &str = "Extra";
|
||||||
|
|
||||||
|
let id = metadata.extrinsic.ty.id;
|
||||||
|
|
||||||
|
let Some(ty) = metadata.types.resolve(id) else {
|
||||||
|
return Err(ExtrinsicError::MissingType);
|
||||||
|
};
|
||||||
|
|
||||||
|
let params: HashMap<_, _> = ty
|
||||||
|
.type_params
|
||||||
|
.iter()
|
||||||
|
.map(|ty_param| {
|
||||||
|
let Some(ty) = ty_param.ty else {
|
||||||
|
return Err(ExtrinsicError::MissingType);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((ty_param.name.as_str(), ty.id))
|
||||||
|
})
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
let Some(address) = params.get(ADDRESS) else {
|
||||||
|
return Err(ExtrinsicError::MissingType);
|
||||||
|
};
|
||||||
|
let Some(call) = params.get(CALL) else {
|
||||||
|
return Err(ExtrinsicError::MissingType);
|
||||||
|
};
|
||||||
|
let Some(signature) = params.get(SIGNATURE) else {
|
||||||
|
return Err(ExtrinsicError::MissingType);
|
||||||
|
};
|
||||||
|
let Some(extra) = params.get(EXTRA) else {
|
||||||
|
return Err(ExtrinsicError::MissingType);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ExtrinsicIds {
|
||||||
|
address: *address,
|
||||||
|
call: *call,
|
||||||
|
signature: *signature,
|
||||||
|
extra: *extra,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The extrinsic details.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ExtrinsicDetails {
|
||||||
|
ids: ExtrinsicIds,
|
||||||
|
index: usize,
|
||||||
|
metadata: Metadata,
|
||||||
|
signature: Option<Value<u32>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtrinsicDetails {
|
||||||
|
// Attempt to dynamically decode a single event from our events input.
|
||||||
|
fn decode_from<T: Config>(
|
||||||
|
metadata: Metadata,
|
||||||
|
extrinsic: ChainBlockExtrinsic,
|
||||||
|
ids: ExtrinsicIds,
|
||||||
|
index: usize,
|
||||||
|
) -> Result<ExtrinsicDetails, Error> {
|
||||||
|
const SIGNATURE_MASK: u8 = 0b1000_0000;
|
||||||
|
const VERSION_MASK: u8 = 0b0111_1111;
|
||||||
|
const LATEST_EXTRINSIC_VERSION: u8 = 4;
|
||||||
|
|
||||||
|
let bytes = extrinsic.0;
|
||||||
|
|
||||||
|
// Extrinsic are encoded in memory in the following way:
|
||||||
|
// - first byte: abbbbbbb (a = 0 for unsigned, 1 for signed, b = version)
|
||||||
|
// - signature: [unknown TBD with metadata].
|
||||||
|
// - extrinsic data
|
||||||
|
if bytes.is_empty() {
|
||||||
|
return Err(ExtrinsicError::InsufficientData.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let version = bytes[0] & VERSION_MASK;
|
||||||
|
if version != LATEST_EXTRINSIC_VERSION {
|
||||||
|
return Err(ExtrinsicError::UnsupportedVersion(version).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_signed = bytes[0] & SIGNATURE_MASK != 0;
|
||||||
|
|
||||||
|
// Skip over the first byte which denotes the version and signing.
|
||||||
|
let cursor = &mut &bytes[1..];
|
||||||
|
|
||||||
|
let signature = if is_signed {
|
||||||
|
let address = decode_as_type(cursor, ids.address, &metadata.runtime_metadata().types)
|
||||||
|
.map_err(scale_decode::Error::from)?;
|
||||||
|
|
||||||
|
let _signature =
|
||||||
|
decode_as_type(cursor, ids.signature, &metadata.runtime_metadata().types)
|
||||||
|
.map_err(scale_decode::Error::from)?;
|
||||||
|
|
||||||
|
let _extra = decode_as_type(cursor, ids.extra, &metadata.runtime_metadata().types)
|
||||||
|
.map_err(scale_decode::Error::from)?;
|
||||||
|
|
||||||
|
Some(address)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Decode the extrinsic function call.
|
||||||
|
let extrinsic = <DecodedValueThunk as DecodeWithMetadata>::decode_with_metadata(
|
||||||
|
cursor, ids.call, &metadata,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(ExtrinsicDetails {
|
||||||
|
ids,
|
||||||
|
index,
|
||||||
|
metadata,
|
||||||
|
signature,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The index of the extrinsic in the given block.
|
||||||
|
/// What index is this event in the stored events for this block.
|
||||||
|
pub fn index(&self) -> usize {
|
||||||
|
self.index
|
||||||
|
}
|
||||||
|
|
||||||
|
// /// Return _all_ of the bytes representing this event, which include, in order:
|
||||||
|
// /// - The phase.
|
||||||
|
// /// - Pallet and event index.
|
||||||
|
// /// - Event fields.
|
||||||
|
// /// - Event Topics.
|
||||||
|
// pub fn bytes(&self) -> &[u8] {
|
||||||
|
// &self.all_bytes[self.start_idx..self.end_idx]
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Attempt to statically decode these [`EventDetails`] into a type representing the event
|
||||||
|
// /// fields. This leans directly on [`codec::Decode`]. You can also attempt to decode the entirety
|
||||||
|
// /// of the event using [`EventDetails::as_root_event()`], which is more lenient because it's able
|
||||||
|
// /// to lean on [`scale_decode::DecodeAsType`].
|
||||||
|
// pub fn as_event<E: StaticEvent>(&self) -> Result<Option<E>, Error> {
|
||||||
|
// let ev_metadata = self.event_metadata();
|
||||||
|
// if ev_metadata.pallet() == E::PALLET && ev_metadata.event() == E::EVENT {
|
||||||
|
// let decoded = E::decode_as_fields(
|
||||||
|
// &mut self.field_bytes(),
|
||||||
|
// ev_metadata.fields(),
|
||||||
|
// self.metadata.types(),
|
||||||
|
// )?;
|
||||||
|
// Ok(Some(decoded))
|
||||||
|
// } else {
|
||||||
|
// Ok(None)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||||
|
// see LICENSE for license details.
|
||||||
|
|
||||||
|
//! This module exposes the types and such necessary for working with extrinsics.
|
||||||
|
//! The two main entry points into events are [`crate::OnlineClient::events()`]
|
||||||
|
//! and calls like [crate::tx::TxProgress::wait_for_finalized_success()].
|
||||||
|
|
||||||
|
mod extrinsics_client;
|
||||||
|
mod extrinsics_type;
|
||||||
|
use codec::{Decode, Encode};
|
||||||
|
|
||||||
|
pub use extrinsics_client::ExtrinsicsClient;
|
||||||
|
pub use extrinsics_type::{ExtrinsicDetails, Extrinsics};
|
||||||
|
|
||||||
|
// pub use events_client::EventsClient;
|
||||||
|
// pub use events_type::{
|
||||||
|
// EventDetails,
|
||||||
|
// Events,
|
||||||
|
// // Used in codegen but hidden from docs:
|
||||||
|
// RootEvent,
|
||||||
|
// };
|
||||||
|
// use scale_decode::DecodeAsFields;
|
||||||
@@ -155,6 +155,7 @@ pub mod constants;
|
|||||||
pub mod dynamic;
|
pub mod dynamic;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
pub mod extrinsics;
|
||||||
pub mod metadata;
|
pub mod metadata;
|
||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
pub mod runtime_api;
|
pub mod runtime_api;
|
||||||
|
|||||||
Reference in New Issue
Block a user