mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 07:31:02 +00:00
Add subxt-historic crate for accesing historic (non head-of-chain) blocks (#2040)
* WIP subxt-historic * WIP subxt-historic * WIP subxt-historic; flesh out basic foundations * WIP filling in extrinsic decoding functionality * iter and decode transaction extensions * Fill in the Online/OfflineClient APIs and move more things to be part of the chain Config * WIP storage * clippy, fmt, finish extrinsics example * prep for 0.0.1 release to claim crate name * fix README link * fmt * WIP thinking about storage APIs * WIP working out storage APIs * Storage plain value fetching first pass * WIP storage: first pass iterating over values done * First apss finishing storage APIs * fmt and clippy * Create a storage example showing fetch and iteration * Bump to frame-decode 0.9.0 * Bump subxt-historic to 0.0.3 for preview release * Remove unused deps * fix import * clippy * doc fixes * tweak CI and fix some cargo hack findings * Update README: subxt-historic is prerelease
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
use super::extrinsic_info::{AnyExtrinsicInfo, with_info};
|
||||
use crate::error::ExtrinsicCallError;
|
||||
use crate::utils::Either;
|
||||
use scale_info_legacy::{LookupName, TypeRegistrySet};
|
||||
|
||||
/// This represents the call data in the extrinsic.
|
||||
pub struct ExtrinsicCall<'extrinsics, 'atblock> {
|
||||
all_bytes: &'extrinsics [u8],
|
||||
info: &'extrinsics AnyExtrinsicInfo<'atblock>,
|
||||
}
|
||||
|
||||
impl<'extrinsics, 'atblock> ExtrinsicCall<'extrinsics, 'atblock> {
|
||||
pub(crate) fn new(
|
||||
all_bytes: &'extrinsics [u8],
|
||||
info: &'extrinsics AnyExtrinsicInfo<'atblock>,
|
||||
) -> Self {
|
||||
Self { all_bytes, info }
|
||||
}
|
||||
|
||||
/// The index of the pallet that this call is for
|
||||
pub fn pallet_index(&self) -> u8 {
|
||||
with_info!(&self.info => info.info.pallet_index())
|
||||
}
|
||||
|
||||
/// The name of the pallet that this call is for.
|
||||
pub fn pallet_name(&self) -> &str {
|
||||
with_info!(&self.info => info.info.pallet_name())
|
||||
}
|
||||
|
||||
/// The index of this call.
|
||||
pub fn index(&self) -> u8 {
|
||||
with_info!(&self.info => info.info.call_index())
|
||||
}
|
||||
|
||||
/// The name of this call.
|
||||
pub fn name(&self) -> &str {
|
||||
with_info!(&self.info => info.info.call_name())
|
||||
}
|
||||
|
||||
/// Get the raw bytes for the entire call, which includes the pallet and call index
|
||||
/// bytes as well as the encoded arguments for each of the fields.
|
||||
pub fn bytes(&self) -> &'extrinsics [u8] {
|
||||
with_info!(&self.info => &self.all_bytes[info.info.call_data_range()])
|
||||
}
|
||||
|
||||
/// Work with the fields in this call.
|
||||
pub fn fields(&self) -> ExtrinsicCallFields<'extrinsics, 'atblock> {
|
||||
ExtrinsicCallFields::new(self.all_bytes, self.info)
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents the fields of the call.
|
||||
pub struct ExtrinsicCallFields<'extrinsics, 'atblock> {
|
||||
all_bytes: &'extrinsics [u8],
|
||||
info: &'extrinsics AnyExtrinsicInfo<'atblock>,
|
||||
}
|
||||
|
||||
impl<'extrinsics, 'atblock> ExtrinsicCallFields<'extrinsics, 'atblock> {
|
||||
pub(crate) fn new(
|
||||
all_bytes: &'extrinsics [u8],
|
||||
info: &'extrinsics AnyExtrinsicInfo<'atblock>,
|
||||
) -> Self {
|
||||
Self { all_bytes, info }
|
||||
}
|
||||
|
||||
/// Return the bytes representing the fields stored in this extrinsic.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This is a subset of [`ExtrinsicCall::bytes`] that does not include the
|
||||
/// first two bytes that denote the pallet index and the variant index.
|
||||
pub fn bytes(&self) -> &'extrinsics [u8] {
|
||||
with_info!(&self.info => &self.all_bytes[info.info.call_data_args_range()])
|
||||
}
|
||||
|
||||
/// Iterate over each of the fields of the extrinsic call data.
|
||||
pub fn iter(&self) -> impl Iterator<Item = ExtrinsicCallField<'extrinsics, 'atblock>> {
|
||||
match &self.info {
|
||||
AnyExtrinsicInfo::Legacy(info) => {
|
||||
Either::A(info.info.call_data().map(|named_arg| ExtrinsicCallField {
|
||||
field_bytes: &self.all_bytes[named_arg.range()],
|
||||
info: AnyExtrinsicCallFieldInfo::Legacy(ExtrinsicCallFieldInfo {
|
||||
info: named_arg,
|
||||
resolver: info.resolver,
|
||||
}),
|
||||
}))
|
||||
}
|
||||
AnyExtrinsicInfo::Current(info) => {
|
||||
Either::B(info.info.call_data().map(|named_arg| ExtrinsicCallField {
|
||||
field_bytes: &self.all_bytes[named_arg.range()],
|
||||
info: AnyExtrinsicCallFieldInfo::Current(ExtrinsicCallFieldInfo {
|
||||
info: named_arg,
|
||||
resolver: info.resolver,
|
||||
}),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to decode the fields into the given type.
|
||||
pub fn decode<T: scale_decode::DecodeAsFields>(&self) -> Result<T, ExtrinsicCallError> {
|
||||
with_info!(&self.info => {
|
||||
let cursor = &mut self.bytes();
|
||||
let mut fields = &mut info.info.call_data().map(|named_arg| {
|
||||
scale_decode::Field::new(named_arg.ty().clone(), Some(named_arg.name()))
|
||||
});
|
||||
|
||||
let decoded = T::decode_as_fields(cursor, &mut fields, info.resolver)
|
||||
.map_err(|e| ExtrinsicCallError::FieldsDecodeError { reason: e })?;
|
||||
|
||||
if !cursor.is_empty() {
|
||||
return Err(ExtrinsicCallError::FieldsLeftoverBytes {
|
||||
leftover_bytes: cursor.to_vec(),
|
||||
})
|
||||
}
|
||||
|
||||
Ok(decoded)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExtrinsicCallField<'extrinsics, 'atblock> {
|
||||
field_bytes: &'extrinsics [u8],
|
||||
info: AnyExtrinsicCallFieldInfo<'extrinsics, 'atblock>,
|
||||
}
|
||||
|
||||
enum AnyExtrinsicCallFieldInfo<'extrinsics, 'atblock> {
|
||||
Legacy(ExtrinsicCallFieldInfo<'extrinsics, 'atblock, LookupName, TypeRegistrySet<'atblock>>),
|
||||
Current(ExtrinsicCallFieldInfo<'extrinsics, 'atblock, u32, scale_info::PortableRegistry>),
|
||||
}
|
||||
|
||||
struct ExtrinsicCallFieldInfo<'extrinsics, 'atblock, TypeId, Resolver> {
|
||||
info: &'extrinsics frame_decode::extrinsics::NamedArg<'atblock, TypeId>,
|
||||
resolver: &'atblock Resolver,
|
||||
}
|
||||
|
||||
macro_rules! with_call_field_info {
|
||||
(&$self:ident.$info:ident => $fn:expr) => {
|
||||
#[allow(clippy::clone_on_copy)]
|
||||
match &$self.$info {
|
||||
AnyExtrinsicCallFieldInfo::Legacy($info) => $fn,
|
||||
AnyExtrinsicCallFieldInfo::Current($info) => $fn,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<'extrinsics, 'atblock> ExtrinsicCallField<'extrinsics, 'atblock> {
|
||||
/// Get the raw bytes for this field.
|
||||
pub fn bytes(&self) -> &'extrinsics [u8] {
|
||||
self.field_bytes
|
||||
}
|
||||
|
||||
/// Get the name of this field.
|
||||
pub fn name(&self) -> &'extrinsics str {
|
||||
with_call_field_info!(&self.info => info.info.name())
|
||||
}
|
||||
|
||||
/// Attempt to decode the value of this field into the given type.
|
||||
pub fn decode<T: scale_decode::DecodeAsType>(&self) -> Result<T, ExtrinsicCallError> {
|
||||
with_call_field_info!(&self.info => {
|
||||
let cursor = &mut &*self.field_bytes;
|
||||
let decoded = T::decode_as_type(cursor, info.info.ty().clone(), info.resolver)
|
||||
.map_err(|e| ExtrinsicCallError::FieldDecodeError {
|
||||
name: info.info.name().to_string(),
|
||||
reason: e,
|
||||
})?;
|
||||
|
||||
if !cursor.is_empty() {
|
||||
return Err(ExtrinsicCallError::FieldLeftoverBytes {
|
||||
name: info.info.name().to_string(),
|
||||
leftover_bytes: cursor.to_vec(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(decoded)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user