Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extrinsics: Decode extrinsics from blocks #929

Merged
merged 46 commits into from
May 10, 2023
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
0be5843
Update polkadot.scale
lexnv Apr 24, 2023
5d8c70c
extrinsics: Add extrinsics client
lexnv Apr 24, 2023
664b2a6
extrinsics: Decode extrinsics
lexnv Apr 24, 2023
7f85f4b
subxt: Add extrinsic error
lexnv Apr 24, 2023
dd16475
blocks: Expose extrinsics
lexnv Apr 24, 2023
f475ddf
examples: Fetch and decode block extrinsics
lexnv Apr 24, 2023
6074c62
Merge branch 'master' into lexnv/extrinsic_decode
lexnv Apr 24, 2023
00abf92
Fix clippy
lexnv Apr 24, 2023
42be28f
extrinsics: Fetch pallet and variant index
lexnv Apr 24, 2023
5d8f931
subxt: Move extrinsics on the subxt::blocks
lexnv Apr 26, 2023
fa70294
example: Adjust example
lexnv Apr 26, 2023
0a890a1
metadata: Collect ExtrinsicMetadata
lexnv Apr 26, 2023
cb91766
subxt: Implement StaticExtrinsic for the calls
lexnv Apr 26, 2023
a4ef5fc
Adjust examples
lexnv Apr 27, 2023
1b6a60c
codegen: Add root level Call enum
lexnv Apr 27, 2023
7088460
Adjust testing
lexnv Apr 27, 2023
98f912d
subxt: Add new decode interface
lexnv Apr 27, 2023
3750c76
subxt: Merge ExtrinsicError with BlockError
lexnv Apr 27, 2023
5890032
examples: Find first extrinsic
lexnv Apr 27, 2023
57d1b46
Merge branch 'master' into lexnv/extrinsic_decode
lexnv Apr 27, 2023
6f66f5c
Move code to extrinsic_types
lexnv Apr 27, 2023
1a5110c
Add Extrinsic struct
lexnv Apr 27, 2023
59ca2e7
Adjust examples
lexnv Apr 27, 2023
9e60cde
Merge remote-tracking branch 'origin/lexnv/extrinsic_decode' into lex…
lexnv Apr 27, 2023
77ebb84
test: Decode extinsics
lexnv Apr 28, 2023
8c2af5e
Merge branch 'master' into lexnv/extrinsic_decode
jsdw May 2, 2023
4f9b706
extrinsics/test: Add fake metadata for static decoding
lexnv May 2, 2023
0fcb395
extrinsics/test: Decode from insufficient bytes
lexnv May 2, 2023
1f79f39
extrinsics/test: Check unsupported versions
lexnv May 2, 2023
f6cccd7
extrinsics/test: Statically decode to root and pallet enums
lexnv May 2, 2023
44ab019
Merge remote-tracking branch 'origin/lexnv/extrinsic_decode' into lex…
lexnv May 2, 2023
6f3421f
extrinsics/tests: Remove clones
lexnv May 2, 2023
db5749c
blocks: Fetch block body inline
lexnv May 2, 2023
3edce15
blocks: Rename ExtrinsicIds to ExtrinsicPartTypeIds
lexnv May 2, 2023
e371a24
extrinsics/test: Check decode as_extrinsic
lexnv May 2, 2023
73fac13
blocks: Remove InsufficientData error
lexnv May 3, 2023
6fada8f
blocks: Return error from extrinsic_metadata
lexnv May 3, 2023
96b403e
extrinsics: Postpone decoding of call bytes
lexnv May 3, 2023
591c179
Merge remote-tracking branch 'origin/master' into lexnv/extrinsic_decode
lexnv May 3, 2023
b3dd274
Merge remote-tracking branch 'origin/master' into lexnv/extrinsic_decode
lexnv May 3, 2023
e7510ab
metadata_type: Rename variables
lexnv May 3, 2023
331116e
Adjust calls path for example and tests
lexnv May 3, 2023
c495368
Merge remote-tracking branch 'origin/master' into lexnv/extrinsic_decode
lexnv May 9, 2023
04f3174
examples: Remove traces
lexnv May 9, 2023
1a56328
book: Add extrinsics documentation
lexnv May 9, 2023
e2bab4c
book: Improve extrinsics docs
lexnv May 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified artifacts/polkadot_metadata.scale
Binary file not shown.
7 changes: 7 additions & 0 deletions codegen/src/api/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ pub fn generate_calls(
// The call structure's documentation was stripped above.
let call_struct = quote! {
#struct_def

impl #crate_path::blocks::StaticExtrinsic for #struct_name {
const PALLET: &'static str = #pallet_name;
const CALL: &'static str = #call_name;
}
};

let client_fn = quote! {
Expand All @@ -106,6 +111,7 @@ pub fn generate_calls(
.into_iter()
.unzip();

let call_type = type_gen.resolve_type_path(call.ty.id);
let call_ty = type_gen.resolve_type(call.ty.id);
let docs = &call_ty.docs;
let docs = should_gen_docs
Expand All @@ -114,6 +120,7 @@ pub fn generate_calls(

Ok(quote! {
#docs
pub type Call = #call_type;
pub mod calls {
use super::root_mod;
use super::#types_mod_ident;
Expand Down
49 changes: 49 additions & 0 deletions codegen/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,26 @@ impl RuntimeGenerator {
}
};

let outer_extrinsic_variants = self.metadata.pallets.iter().filter_map(|p| {
let variant_name = format_ident!("{}", p.name);
let mod_name = format_ident!("{}", p.name.to_string().to_snake_case());
let index = proc_macro2::Literal::u8_unsuffixed(p.index);

p.calls.as_ref().map(|_| {
quote! {
#[codec(index = #index)]
#variant_name(#mod_name::Call),
}
})
});

let outer_extrinsic = quote! {
#default_derives
pub enum Call {
#( #outer_extrinsic_variants )*
}
};

let root_event_if_arms = self.metadata.pallets.iter().filter_map(|p| {
let variant_name_str = &p.name;
let variant_name = format_ident!("{}", variant_name_str);
Expand All @@ -375,6 +395,24 @@ impl RuntimeGenerator {
})
});

let root_extrinsic_if_arms = self.metadata.pallets.iter().filter_map(|p| {
let variant_name_str = &p.name;
let variant_name = format_ident!("{}", variant_name_str);
let mod_name = format_ident!("{}", variant_name_str.to_string().to_snake_case());
p.calls.as_ref().map(|_| {
// An 'if' arm for the RootExtrinsic impl to match this variant name:
quote! {
if pallet_name == #variant_name_str {
return Ok(Call::#variant_name(#mod_name::Call::decode_with_metadata(
&mut &*pallet_bytes,
pallet_ty,
metadata
)?));
}
}
})
});

let outer_error_variants = self.metadata.pallets.iter().filter_map(|p| {
let variant_name = format_ident!("{}", p.name);
let mod_name = format_ident!("{}", p.name.to_string().to_snake_case());
Expand Down Expand Up @@ -465,7 +503,18 @@ impl RuntimeGenerator {
}
}

#outer_extrinsic

impl #crate_path::blocks::RootExtrinsic for Call {
fn root_extrinsic(pallet_bytes: &[u8], pallet_name: &str, pallet_ty: u32, metadata: &#crate_path::Metadata) -> Result<Self, #crate_path::Error> {
use #crate_path::metadata::DecodeWithMetadata;
#( #root_extrinsic_if_arms )*
Err(#crate_path::ext::scale_decode::Error::custom(format!("Pallet name '{}' not found in root Call enum", pallet_name)).into())
}
}

#outer_error

impl #crate_path::error::RootError for Error {
fn root_error(pallet_bytes: &[u8], pallet_name: &str, metadata: &#crate_path::Metadata) -> Result<Self, #crate_path::Error> {
use #crate_path::metadata::DecodeWithMetadata;
Expand Down
5 changes: 5 additions & 0 deletions codegen/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ pub enum CodegenError {
"{0} type should be an variant/enum type. Make sure you are providing a valid substrate-based metadata"
)]
InvalidType(String),
/// Extrinsic call type could not be found.
#[error(
"Extrinsic call type could not be found. Make sure you are providing a valid substrate-based metadata"
)]
MissingCallType,
}

impl CodegenError {
Expand Down
101 changes: 101 additions & 0 deletions examples/examples/block_extrinsics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// 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.

//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```

use futures::StreamExt;
use sp_keyring::AccountKeyring;
use std::time::Duration;
use subxt::{tx::PairSigner, OnlineClient, PolkadotConfig};

#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}

/// Subscribe to all events, and then manually look through them and
/// pluck out the events that we care about.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();

// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;

// Subscribe to (in this case, finalized) blocks.
let mut block_sub = api.blocks().subscribe_finalized().await?;

// While this subscription is active, balance transfers are made somewhere:
tokio::task::spawn({
let api = api.clone();
async move {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let mut transfer_amount = 1_000_000_000;

// Make small balance transfers from Alice to Bob in a loop:
loop {
let transfer_tx = polkadot::tx()
.balances()
.transfer(AccountKeyring::Bob.to_account_id().into(), transfer_amount);
api.tx()
.sign_and_submit_default(&transfer_tx, &signer)
.await
.unwrap();

tokio::time::sleep(Duration::from_secs(10)).await;
transfer_amount += 100_000_000;
}
}
});

// Get each finalized block as it arrives.
while let Some(block) = block_sub.next().await {
let block = block?;

let block_hash = block.hash();
println!(" Block {:?}", block_hash);

// Ask for the extrinsics for this block.
let extrinsics = block.body().await?.extrinsics();

let transfer_tx = extrinsics.find_first::<polkadot::balances::calls::Transfer>();
println!(" Transfer tx: {:?}", transfer_tx);

// Ask for the extrinsics for this block.
for extrinsic in extrinsics.iter() {
let extrinsic = extrinsic?;

println!(
" Extrinsic block index {:?}, pallet index {:?}, variant index {:?}",
extrinsic.index(),
extrinsic.pallet_index(),
extrinsic.variant_index()
);

let root_extrinsic = extrinsic.as_root_extrinsic::<polkadot::Call>();
println!(" As root extrinsic {:?}\n", root_extrinsic);

let pallet_extrinsic = extrinsic
.as_pallet_extrinsic::<polkadot::runtime_types::pallet_balances::pallet::Call>();
println!(
" Extrinsic as Balances Pallet call: {:?}\n",
pallet_extrinsic
);

let call = extrinsic.as_extrinsic::<polkadot::balances::calls::Transfer>()?;
println!(
" Extrinsic as polkadot::balances::calls::Transfer: {:?}\n\n",
call
);
}

println!("\n");
}

Ok(())
}
3 changes: 2 additions & 1 deletion examples/examples/subscribe_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!(" Extrinsics:");

let body = block.body().await?;
for ext in body.extrinsics() {
for ext in body.extrinsics().iter() {
let ext = ext?;
let idx = ext.index();
let events = ext.events().await?;
let bytes_hex = format!("0x{}", hex::encode(ext.bytes()));
Expand Down
1 change: 1 addition & 0 deletions subxt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,4 @@ sp-core = { workspace = true }
sp-runtime = { workspace = true }
sp-keyring = { workspace = true }
sp-version = { workspace = true }
assert_matches = { workspace = true }
Loading