Skip to content

Commit

Permalink
Make subxt-core ready for publishing (#1508)
Browse files Browse the repository at this point in the history
* Move Extrinsic decoding things to subxt_core and various tidy-ups

* A couple more fixes and fmt

* first pass moving tx logic to subxt_core

* cargo fmt

* fix wasm example

* clippy

* more clippy

* WIP Adding examples and such

* Move storage functionality more fully to subxt_core and nice examples for storage and txs

* Add example for events

* consistify how addresses/payloads are exposed in subxt-core and add runtime API fns

* Add runtime API core example

* fmt

* remove scale-info patch

* Add a little to the top level docs

* swap args around

* clippy

* cargo fmt and fix wasm-example

* doc fixes

* no-std-ise new subxt-core additions

* alloc, not core

* more no-std fixes

* A couple more fixes

* Add back extrinsic decode test
  • Loading branch information
jsdw authored Apr 15, 2024
1 parent b527c85 commit 1e111ea
Show file tree
Hide file tree
Showing 89 changed files with 4,415 additions and 3,456 deletions.
495 changes: 258 additions & 237 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions artifacts/demo_chain_specs/polkadot.json

Large diffs are not rendered by default.

Binary file modified artifacts/polkadot_metadata_small.scale
Binary file not shown.
Binary file modified artifacts/polkadot_metadata_tiny.scale
Binary file not shown.
7 changes: 5 additions & 2 deletions cli/src/commands/explore/pallets/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub fn explore_calls(
Usage:
subxt explore pallet {pallet_name} calls <CALL>
explore a specific call of this pallet
{calls}
"}
};
Expand Down Expand Up @@ -154,7 +154,10 @@ fn mocked_offline_client(metadata: Metadata) -> OfflineClient<SubstrateConfig> {
H256::from_str("91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3")
.expect("Valid hash; qed");

let runtime_version = subxt::client::RuntimeVersion::new(9370, 20);
let runtime_version = subxt::client::RuntimeVersion {
spec_version: 9370,
transaction_version: 20,
};

OfflineClient::<SubstrateConfig>::new(genesis_hash, runtime_version, metadata)
}
Expand Down
4 changes: 2 additions & 2 deletions codegen/src/api/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ pub fn generate_calls(
pub fn #fn_name(
&self,
#( #call_fn_args, )*
) -> #crate_path::tx::Payload<types::#struct_name> {
#crate_path::tx::Payload::new_static(
) -> #crate_path::tx::payload::Payload<types::#struct_name> {
#crate_path::tx::payload::Payload::new_static(
#pallet_name,
#call_name,
types::#struct_name { #( #call_args, )* },
Expand Down
4 changes: 2 additions & 2 deletions codegen/src/api/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ pub fn generate_constants(

Ok(quote! {
#docs
pub fn #fn_name(&self) -> #crate_path::constants::Address<#return_ty> {
#crate_path::constants::Address::new_static(
pub fn #fn_name(&self) -> #crate_path::constants::address::Address<#return_ty> {
#crate_path::constants::address::Address::new_static(
#pallet_name,
#constant_name,
[#(#constant_hash,)*]
Expand Down
4 changes: 2 additions & 2 deletions codegen/src/api/custom_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ fn generate_custom_value_fn(
};

Some(quote!(
pub fn #fn_name_ident(&self) -> #crate_path::custom_values::StaticAddress<#return_ty, #decodable> {
#crate_path::custom_values::StaticAddress::new_static(#name, [#(#custom_value_hash,)*])
pub fn #fn_name_ident(&self) -> #crate_path::custom_values::address::StaticAddress<#return_ty, #decodable> {
#crate_path::custom_values::address::StaticAddress::new_static(#name, [#(#custom_value_hash,)*])
}
))
}
4 changes: 2 additions & 2 deletions codegen/src/api/runtime_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ fn generate_runtime_api(

let method = quote!(
#docs
pub fn #method_name(&self, #( #fn_params, )* ) -> #crate_path::runtime_api::Payload<types::#struct_name, types::#method_name::output::Output> {
#crate_path::runtime_api::Payload::new_static(
pub fn #method_name(&self, #( #fn_params, )* ) -> #crate_path::runtime_api::payload::Payload<types::#struct_name, types::#method_name::output::Output> {
#crate_path::runtime_api::payload::Payload::new_static(
#trait_name_str,
#method_name_str,
types::#struct_name { #( #param_names, )* },
Expand Down
9 changes: 9 additions & 0 deletions codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,16 @@ impl CodegenBuilder {
}

/// Set the path to the `subxt` crate. By default, we expect it to be at `::subxt::ext::subxt_core`.
///
/// # Panics
///
/// Panics if the path provided is not an absolute path.
pub fn set_subxt_crate_path(&mut self, crate_path: syn::Path) {
if absolute_path(crate_path.clone()).is_err() {
// Throw an error here, because otherwise we end up with a harder to comprehend error when
// substitute types don't begin with an absolute path.
panic!("The provided crate path must be an absolute path, ie prefixed with '::' or 'crate'");
}
self.crate_path = crate_path;
}

Expand Down
4 changes: 4 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,15 @@ sp-runtime = { workspace = true, optional = true }
tracing = { workspace = true, default-features = false }

[dev-dependencies]
assert_matches = { workspace = true }
bitvec = { workspace = true }
codec = { workspace = true, features = ["derive", "bit-vec"] }
subxt-macro = { workspace = true }
subxt-signer = { workspace = true, features = ["sr25519", "subxt"] }
sp-core = { workspace = true }
sp-keyring = { workspace = true }
sp-runtime = { workspace = true }
hex = { workspace = true }


[package.metadata.docs.rs]
Expand Down
169 changes: 169 additions & 0 deletions core/src/blocks/extrinsic_signed_extensions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Copyright 2019-2024 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::signed_extensions::{
ChargeAssetTxPayment, ChargeTransactionPayment, CheckNonce,
};
use crate::config::SignedExtension;
use crate::dynamic::Value;
use crate::{config::Config, error::Error, Metadata};
use scale_decode::DecodeAsType;

/// The signed extensions of an extrinsic.
#[derive(Debug, Clone)]
pub struct ExtrinsicSignedExtensions<'a, T: Config> {
bytes: &'a [u8],
metadata: &'a Metadata,
_marker: core::marker::PhantomData<T>,
}

impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> {
pub(crate) fn new(bytes: &'a [u8], metadata: &'a Metadata) -> Self {
Self {
bytes,
metadata,
_marker: core::marker::PhantomData,
}
}

/// Returns an iterator over each of the signed extension details of the extrinsic.
/// If the decoding of any signed extension fails, an error item is yielded and the iterator stops.
pub fn iter(&self) -> impl Iterator<Item = Result<ExtrinsicSignedExtension<T>, Error>> {
let signed_extension_types = self.metadata.extrinsic().signed_extensions();
let num_signed_extensions = signed_extension_types.len();
let bytes = self.bytes;
let mut index = 0;
let mut byte_start_idx = 0;
let metadata = &self.metadata;

core::iter::from_fn(move || {
if index == num_signed_extensions {
return None;
}

let extension = &signed_extension_types[index];
let ty_id = extension.extra_ty();
let cursor = &mut &bytes[byte_start_idx..];
if let Err(err) = scale_decode::visitor::decode_with_visitor(
cursor,
&ty_id,
metadata.types(),
scale_decode::visitor::IgnoreVisitor::new(),
)
.map_err(|e| Error::Decode(e.into()))
{
index = num_signed_extensions; // (such that None is returned in next iteration)
return Some(Err(err));
}
let byte_end_idx = bytes.len() - cursor.len();
let bytes = &bytes[byte_start_idx..byte_end_idx];
byte_start_idx = byte_end_idx;
index += 1;
Some(Ok(ExtrinsicSignedExtension {
bytes,
ty_id,
identifier: extension.identifier(),
metadata,
_marker: core::marker::PhantomData,
}))
})
}

/// Searches through all signed extensions to find a specific one.
/// If the Signed Extension is not found `Ok(None)` is returned.
/// If the Signed Extension is found but decoding failed `Err(_)` is returned.
pub fn find<S: SignedExtension<T>>(&self) -> Result<Option<S::Decoded>, Error> {
for ext in self.iter() {
// If we encounter an error while iterating, we won't get any more results
// back, so just return that error as we won't find the signed ext anyway.
let ext = ext?;
match ext.as_signed_extension::<S>() {
// We found a match; return it:
Ok(Some(e)) => return Ok(Some(e)),
// No error, but no match either; next!
Ok(None) => continue,
// Error? return it
Err(e) => return Err(e),
}
}
Ok(None)
}

/// The tip of an extrinsic, extracted from the ChargeTransactionPayment or ChargeAssetTxPayment
/// signed extension, depending on which is present.
///
/// Returns `None` if `tip` was not found or decoding failed.
pub fn tip(&self) -> Option<u128> {
// Note: the overhead of iterating multiple time should be negligible.
self.find::<ChargeTransactionPayment>()
.ok()
.flatten()
.map(|e| e.tip())
.or_else(|| {
self.find::<ChargeAssetTxPayment<T>>()
.ok()
.flatten()
.map(|e| e.tip())
})
}

/// The nonce of the account that submitted the extrinsic, extracted from the CheckNonce signed extension.
///
/// Returns `None` if `nonce` was not found or decoding failed.
pub fn nonce(&self) -> Option<u64> {
self.find::<CheckNonce>().ok()?
}
}

/// A single signed extension
#[derive(Debug, Clone)]
pub struct ExtrinsicSignedExtension<'a, T: Config> {
bytes: &'a [u8],
ty_id: u32,
identifier: &'a str,
metadata: &'a Metadata,
_marker: core::marker::PhantomData<T>,
}

impl<'a, T: Config> ExtrinsicSignedExtension<'a, T> {
/// The bytes representing this signed extension.
pub fn bytes(&self) -> &'a [u8] {
self.bytes
}

/// The name of the signed extension.
pub fn name(&self) -> &'a str {
self.identifier
}

/// The type id of the signed extension.
pub fn type_id(&self) -> u32 {
self.ty_id
}

/// Signed Extension as a [`scale_value::Value`]
pub fn value(&self) -> Result<Value<u32>, Error> {
let value = scale_value::scale::decode_as_type(
&mut &self.bytes[..],
&self.ty_id,
self.metadata.types(),
)?;
Ok(value)
}

/// Decodes the bytes of this Signed Extension into its associated `Decoded` type.
/// Returns `Ok(None)` if the data we have doesn't match the Signed Extension we're asking to
/// decode with.
pub fn as_signed_extension<S: SignedExtension<T>>(&self) -> Result<Option<S::Decoded>, Error> {
if !S::matches(self.identifier, self.ty_id, self.metadata.types()) {
return Ok(None);
}
self.as_type::<S::Decoded>().map(Some)
}

fn as_type<E: DecodeAsType>(&self) -> Result<E, Error> {
let value = E::decode_as_type(&mut &self.bytes[..], &self.ty_id, self.metadata.types())?;
Ok(value)
}
}
Loading

0 comments on commit 1e111ea

Please sign in to comment.