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

Support decoding signed extensions #1209

Merged
merged 16 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
2 changes: 1 addition & 1 deletion subxt/src/blocks/extrinsic_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ impl<'a> ExtrinsicSignedExtensions<'a> {

/// The tip of an extrinsic, extracted from the ChargeTransactionPayment signed extension.
pub fn tip(&self) -> Option<u128> {
let tip = self.find("ChargeTransactionPayment")?;
let tip = self.find("ChargeAssetTxPayment")?;
let tip = Compact::<u128>::decode(&mut tip.bytes()).ok()?.0;
Some(tip)
}
Expand Down
91 changes: 51 additions & 40 deletions testing/integration-tests/src/full_client/blocks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

use crate::utils::TestContext;
use crate::{test_context, utils::node_runtime};
use codec::{Compact, Encode};
use futures::StreamExt;
use std::future::Future;
use std::time::Duration;
use subxt::config::DefaultExtrinsicParamsBuilder;
use subxt_metadata::Metadata;
use subxt_signer::sr25519::dev;

Expand Down Expand Up @@ -235,46 +239,53 @@ async fn decode_signed_extensions_from_blocks() {
let alice = dev::alice();
let bob = dev::bob();

let submit_tranfer_extrinsic_and_get_it_back = || async {
let tx = node_runtime::tx()
.balances()
.transfer_allow_death(bob.public_key().into(), 10_000);

let signed_extrinsic = api
.tx()
.create_signed(&tx, &alice, Default::default())
.await
.unwrap();

let in_block = signed_extrinsic
.submit_and_watch()
.await
.unwrap()
.wait_for_in_block()
.await
.unwrap();

let block_hash = in_block.block_hash();
let block = api.blocks().at(block_hash).await.unwrap();
let extrinsics = block.extrinsics().await.unwrap();
let extrinsic_details = extrinsics.iter().next().unwrap().unwrap();
extrinsic_details
};

let first_transaction = submit_tranfer_extrinsic_and_get_it_back().await;
let first_transaction_nonce = first_transaction
.signed_extensions()
.unwrap()
.nonce()
.unwrap();
macro_rules! submit_transfer_extrinsic_and_get_it_back {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dq: What are the benefits of this macro over a plain function? Looks good otherwise!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were just some really stupid lifetime issues when using a Closure.
And I cannot use a normal function easily because then I need to specify all types.
And the type of api here depends on the unstable-light-client feature flag. And the type of api would be a generic type of the return value of that function as well. So I would have to provide two implementations of such a function depending on the feature flag. Also I cannot put the api a trait object easily, because OnlineClientT is not object safe. So in the end I just went with this macro.

($tip:expr) => {{
let tx = node_runtime::tx()
.balances()
.transfer_allow_death(bob.public_key().into(), 10_000);

let signed_extrinsic = api
.tx()
.create_signed(
&tx,
&alice,
DefaultExtrinsicParamsBuilder::new().tip($tip).build(),
)
.await
.unwrap();

let in_block = signed_extrinsic
.submit_and_watch()
.await
.unwrap()
.wait_for_finalized()
.await
.unwrap();

let block_hash = in_block.block_hash();
let block = api.blocks().at(block_hash).await.unwrap();
let extrinsics = block.extrinsics().await.unwrap();
let extrinsic_details = extrinsics
.iter()
.find_map(|e| e.ok().filter(|e| e.is_signed()))
.unwrap();
extrinsic_details
}};
}

let second_transaction = submit_tranfer_extrinsic_and_get_it_back().await;
let second_transaction_nonce = first_transaction
.signed_extensions()
.unwrap()
.nonce()
.unwrap();
let transaction1 = submit_transfer_extrinsic_and_get_it_back!(1234);
let extensions1 = transaction1.signed_extensions().unwrap();
let nonce1 = extensions1.nonce().unwrap();
let tip1 = extensions1.tip().unwrap();

let transaction2 = submit_transfer_extrinsic_and_get_it_back!(5678);
let extensions2 = transaction2.signed_extensions().unwrap();
let nonce2 = extensions2.nonce().unwrap();
let tip2 = extensions2.tip().unwrap();

assert_eq!(first_transaction_nonce, 0);
assert_eq!(second_transaction_nonce, 1);
assert_eq!(nonce1, 0);
assert_eq!(tip1, 1234);
assert_eq!(nonce2, 1);
assert_eq!(tip2, 5678);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like us to also test that iterating over signed extensions works; right now we have no way to really know if it's actually decoding them as we expect :)

The set of signed extensions on a node is fairly fixed, so we can just sanity test that each of the expected ones is decoded and that the names are right or something like that (in addition to the above).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added code for testing this to the end of the test :)

}