Skip to content

Commit b6b9ac6

Browse files
authored
Support constructing and submitting V5 transactions (#1931)
* TransactionExtensions basic support for V5 VerifySignature and renames * WIP: subxt-core v5 transaction support * Subxt to support V5 extrinsics * WIP tests failing with wsm trap error * Actually encode mortality to fix tx encode issue * fmt * rename to sign_with_account_and_signature * Add explicit methods for v4 and v5 ext construction * clippy * fix wasm example and no mut self where not needed * fix doc example * another doc fix * Add tests for tx encoding and fix v5 encode issue * add copyright and todo * refactor APIs to have clear v4/v5 split in core and slightly nicer split in subxt proper * rename Partial/SubmittableExtrinsic to *Transaction * Remove SignerT::address since it's not needed * doc fixes * fmt * doc fixes * Fix comment number * Clarify panic behaviour of inject_signature * fmt
1 parent dcb9c27 commit b6b9ac6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1337
-750
lines changed

core/src/blocks/extrinsic_signed_extensions.rs core/src/blocks/extrinsic_transaction_extensions.rs

+20-16
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@
22
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
33
// see LICENSE for license details.
44

5-
use crate::config::signed_extensions::{
5+
use crate::config::transaction_extensions::{
66
ChargeAssetTxPayment, ChargeTransactionPayment, CheckNonce,
77
};
8-
use crate::config::SignedExtension;
8+
use crate::config::TransactionExtension;
99
use crate::dynamic::Value;
1010
use crate::{config::Config, error::Error, Metadata};
1111
use frame_decode::extrinsics::ExtrinsicExtensions;
1212
use scale_decode::DecodeAsType;
1313

1414
/// The signed extensions of an extrinsic.
1515
#[derive(Debug, Clone)]
16-
pub struct ExtrinsicSignedExtensions<'a, T: Config> {
16+
pub struct ExtrinsicTransactionExtensions<'a, T: Config> {
1717
bytes: &'a [u8],
1818
metadata: &'a Metadata,
1919
decoded_info: &'a ExtrinsicExtensions<'static, u32>,
2020
_marker: core::marker::PhantomData<T>,
2121
}
2222

23-
impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> {
23+
impl<'a, T: Config> ExtrinsicTransactionExtensions<'a, T> {
2424
pub(crate) fn new(
2525
bytes: &'a [u8],
2626
metadata: &'a Metadata,
@@ -35,20 +35,22 @@ impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> {
3535
}
3636

3737
/// Returns an iterator over each of the signed extension details of the extrinsic.
38-
pub fn iter(&self) -> impl Iterator<Item = ExtrinsicSignedExtension<T>> {
39-
self.decoded_info.iter().map(|s| ExtrinsicSignedExtension {
40-
bytes: &self.bytes[s.range()],
41-
ty_id: *s.ty(),
42-
identifier: s.name(),
43-
metadata: self.metadata,
44-
_marker: core::marker::PhantomData,
45-
})
38+
pub fn iter(&self) -> impl Iterator<Item = ExtrinsicTransactionExtension<T>> {
39+
self.decoded_info
40+
.iter()
41+
.map(|s| ExtrinsicTransactionExtension {
42+
bytes: &self.bytes[s.range()],
43+
ty_id: *s.ty(),
44+
identifier: s.name(),
45+
metadata: self.metadata,
46+
_marker: core::marker::PhantomData,
47+
})
4648
}
4749

4850
/// Searches through all signed extensions to find a specific one.
4951
/// If the Signed Extension is not found `Ok(None)` is returned.
5052
/// If the Signed Extension is found but decoding failed `Err(_)` is returned.
51-
pub fn find<S: SignedExtension<T>>(&self) -> Result<Option<S::Decoded>, Error> {
53+
pub fn find<S: TransactionExtension<T>>(&self) -> Result<Option<S::Decoded>, Error> {
5254
for ext in self.iter() {
5355
match ext.as_signed_extension::<S>() {
5456
// We found a match; return it:
@@ -90,15 +92,15 @@ impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> {
9092

9193
/// A single signed extension
9294
#[derive(Debug, Clone)]
93-
pub struct ExtrinsicSignedExtension<'a, T: Config> {
95+
pub struct ExtrinsicTransactionExtension<'a, T: Config> {
9496
bytes: &'a [u8],
9597
ty_id: u32,
9698
identifier: &'a str,
9799
metadata: &'a Metadata,
98100
_marker: core::marker::PhantomData<T>,
99101
}
100102

101-
impl<'a, T: Config> ExtrinsicSignedExtension<'a, T> {
103+
impl<'a, T: Config> ExtrinsicTransactionExtension<'a, T> {
102104
/// The bytes representing this signed extension.
103105
pub fn bytes(&self) -> &'a [u8] {
104106
self.bytes
@@ -127,7 +129,9 @@ impl<'a, T: Config> ExtrinsicSignedExtension<'a, T> {
127129
/// Decodes the bytes of this Signed Extension into its associated `Decoded` type.
128130
/// Returns `Ok(None)` if the data we have doesn't match the Signed Extension we're asking to
129131
/// decode with.
130-
pub fn as_signed_extension<S: SignedExtension<T>>(&self) -> Result<Option<S::Decoded>, Error> {
132+
pub fn as_signed_extension<S: TransactionExtension<T>>(
133+
&self,
134+
) -> Result<Option<S::Decoded>, Error> {
131135
if !S::matches(self.identifier, self.ty_id, self.metadata.types()) {
132136
return Ok(None);
133137
}

core/src/blocks/extrinsics.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// see LICENSE for license details.
44

55
use super::BlockError;
6-
use crate::blocks::extrinsic_signed_extensions::ExtrinsicSignedExtensions;
6+
use crate::blocks::extrinsic_transaction_extensions::ExtrinsicTransactionExtensions;
77
use crate::{
88
config::{Config, Hasher},
99
error::{Error, MetadataError},
@@ -232,17 +232,17 @@ where
232232
/// They do *not* include the `additional` signed bytes that are used as part of the payload that is signed.
233233
///
234234
/// Note: Returns `None` if the extrinsic is not signed.
235-
pub fn signed_extensions_bytes(&self) -> Option<&[u8]> {
235+
pub fn transaction_extensions_bytes(&self) -> Option<&[u8]> {
236236
self.decoded_info()
237237
.transaction_extension_payload()
238238
.map(|t| &self.bytes()[t.range()])
239239
}
240240

241241
/// Returns `None` if the extrinsic is not signed.
242-
pub fn signed_extensions(&self) -> Option<ExtrinsicSignedExtensions<'_, T>> {
242+
pub fn transaction_extensions(&self) -> Option<ExtrinsicTransactionExtensions<'_, T>> {
243243
self.decoded_info()
244244
.transaction_extension_payload()
245-
.map(|t| ExtrinsicSignedExtensions::new(self.bytes(), &self.metadata, t))
245+
.map(|t| ExtrinsicTransactionExtensions::new(self.bytes(), &self.metadata, t))
246246
}
247247

248248
/// The index of the pallet that the extrinsic originated from.
@@ -544,7 +544,7 @@ mod tests {
544544
);
545545

546546
// Encoded TX ready to submit.
547-
let tx_encoded = crate::tx::create_unsigned::<SubstrateConfig, _>(&tx, &metadata)
547+
let tx_encoded = crate::tx::create_v4_unsigned::<SubstrateConfig, _>(&tx, &metadata)
548548
.expect("Valid dynamic parameters are provided");
549549

550550
// Extrinsic details ready to decode.
@@ -575,7 +575,7 @@ mod tests {
575575
Value::string("SomeValue"),
576576
],
577577
);
578-
let tx_encoded = crate::tx::create_unsigned::<SubstrateConfig, _>(&tx, &metadata)
578+
let tx_encoded = crate::tx::create_v4_unsigned::<SubstrateConfig, _>(&tx, &metadata)
579579
.expect("Valid dynamic parameters are provided");
580580

581581
// Note: `create_unsigned` produces the extrinsic bytes by prefixing the extrinsic length.

core/src/blocks/mod.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
//! # ]);
6565
//! ```
6666
67-
mod extrinsic_signed_extensions;
67+
mod extrinsic_transaction_extensions;
6868
mod extrinsics;
6969
mod static_extrinsic;
7070

@@ -74,7 +74,9 @@ use crate::Metadata;
7474
use alloc::vec::Vec;
7575

7676
pub use crate::error::BlockError;
77-
pub use extrinsic_signed_extensions::{ExtrinsicSignedExtension, ExtrinsicSignedExtensions};
77+
pub use extrinsic_transaction_extensions::{
78+
ExtrinsicTransactionExtension, ExtrinsicTransactionExtensions,
79+
};
7880
pub use extrinsics::{ExtrinsicDetails, ExtrinsicMetadataDetails, Extrinsics, FoundExtrinsic};
7981
pub use static_extrinsic::StaticExtrinsic;
8082

core/src/client.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use crate::{config::Config, metadata::Metadata};
88
use derive_where::derive_where;
99

10-
/// This provides access to some relevant client state in signed extensions,
10+
/// This provides access to some relevant client state in transaction extensions,
1111
/// and is just a combination of some of the available properties.
1212
#[derive_where(Clone, Debug)]
1313
pub struct ClientState<C: Config> {

core/src/config/default_extrinsic_params.rs

+43-64
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,32 @@
22
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
33
// see LICENSE for license details.
44

5-
use super::signed_extensions::CheckNonceParams;
6-
use super::{signed_extensions, ExtrinsicParams};
7-
use super::{Config, Header};
5+
use super::Config;
6+
use super::{transaction_extensions, ExtrinsicParams};
87

98
/// The default [`super::ExtrinsicParams`] implementation understands common signed extensions
109
/// and how to apply them to a given chain.
11-
pub type DefaultExtrinsicParams<T> = signed_extensions::AnyOf<
10+
pub type DefaultExtrinsicParams<T> = transaction_extensions::AnyOf<
1211
T,
1312
(
14-
signed_extensions::CheckSpecVersion,
15-
signed_extensions::CheckTxVersion,
16-
signed_extensions::CheckNonce,
17-
signed_extensions::CheckGenesis<T>,
18-
signed_extensions::CheckMortality<T>,
19-
signed_extensions::ChargeAssetTxPayment<T>,
20-
signed_extensions::ChargeTransactionPayment,
21-
signed_extensions::CheckMetadataHash,
13+
transaction_extensions::VerifySignature<T>,
14+
transaction_extensions::CheckSpecVersion,
15+
transaction_extensions::CheckTxVersion,
16+
transaction_extensions::CheckNonce,
17+
transaction_extensions::CheckGenesis<T>,
18+
transaction_extensions::CheckMortality<T>,
19+
transaction_extensions::ChargeAssetTxPayment<T>,
20+
transaction_extensions::ChargeTransactionPayment,
21+
transaction_extensions::CheckMetadataHash,
2222
),
2323
>;
2424

2525
/// A builder that outputs the set of [`super::ExtrinsicParams::Params`] required for
2626
/// [`DefaultExtrinsicParams`]. This may expose methods that aren't applicable to the current
2727
/// chain; such values will simply be ignored if so.
2828
pub struct DefaultExtrinsicParamsBuilder<T: Config> {
29-
/// `None` means the tx will be immortal.
30-
mortality: Option<Mortality<T::Hash>>,
29+
/// `None` means the tx will be immortal, else it's mortal for N blocks (if possible).
30+
mortality: Option<u64>,
3131
/// `None` means the nonce will be automatically set.
3232
nonce: Option<u64>,
3333
/// `None` means we'll use the native token.
@@ -36,16 +36,6 @@ pub struct DefaultExtrinsicParamsBuilder<T: Config> {
3636
tip_of: u128,
3737
}
3838

39-
struct Mortality<Hash> {
40-
/// Block hash that mortality starts from
41-
checkpoint_hash: Hash,
42-
/// Block number that mortality starts from (must
43-
// point to the same block as the hash above)
44-
checkpoint_number: u64,
45-
/// How many blocks the tx is mortal for
46-
period: u64,
47-
}
48-
4939
impl<T: Config> Default for DefaultExtrinsicParamsBuilder<T> {
5040
fn default() -> Self {
5141
Self {
@@ -65,15 +55,10 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
6555
Default::default()
6656
}
6757

68-
/// Make the transaction mortal, given a block header that it should be mortal from,
69-
/// and the number of blocks (roughly; it'll be rounded to a power of two) that it will
70-
/// be mortal for.
71-
pub fn mortal(mut self, from_block: &T::Header, for_n_blocks: u64) -> Self {
72-
self.mortality = Some(Mortality {
73-
checkpoint_hash: from_block.hash(),
74-
checkpoint_number: from_block.number().into(),
75-
period: for_n_blocks,
76-
});
58+
/// Make the transaction mortal, given a number of blocks it will be mortal for from
59+
/// the current block at the time of submission.
60+
pub fn mortal(mut self, for_n_blocks: u64) -> Self {
61+
self.mortality = Some(for_n_blocks);
7762
self
7863
}
7964

@@ -83,26 +68,6 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
8368
self
8469
}
8570

86-
/// Make the transaction mortal, given a block number and block hash (which must both point to
87-
/// the same block) that it should be mortal from, and the number of blocks (roughly; it'll be
88-
/// rounded to a power of two) that it will be mortal for.
89-
///
90-
/// Prefer to use [`DefaultExtrinsicParamsBuilder::mortal()`], which ensures that the block hash
91-
/// and number align.
92-
pub fn mortal_unchecked(
93-
mut self,
94-
from_block_number: u64,
95-
from_block_hash: T::Hash,
96-
for_n_blocks: u64,
97-
) -> Self {
98-
self.mortality = Some(Mortality {
99-
checkpoint_hash: from_block_hash,
100-
checkpoint_number: from_block_number,
101-
period: for_n_blocks,
102-
});
103-
self
104-
}
105-
10671
/// Provide a tip to the block author in the chain's native token.
10772
pub fn tip(mut self, tip: u128) -> Self {
10873
self.tip = tip;
@@ -123,28 +88,29 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
12388

12489
/// Build the extrinsic parameters.
12590
pub fn build(self) -> <DefaultExtrinsicParams<T> as ExtrinsicParams<T>>::Params {
126-
let check_mortality_params = if let Some(mortality) = self.mortality {
127-
signed_extensions::CheckMortalityParams::mortal(
128-
mortality.period,
129-
mortality.checkpoint_number,
130-
mortality.checkpoint_hash,
131-
)
91+
let check_mortality_params = if let Some(for_n_blocks) = self.mortality {
92+
transaction_extensions::CheckMortalityParams::mortal(for_n_blocks)
13293
} else {
133-
signed_extensions::CheckMortalityParams::immortal()
94+
transaction_extensions::CheckMortalityParams::immortal()
13495
};
13596

13697
let charge_asset_tx_params = if let Some(asset_id) = self.tip_of_asset_id {
137-
signed_extensions::ChargeAssetTxPaymentParams::tip_of(self.tip, asset_id)
98+
transaction_extensions::ChargeAssetTxPaymentParams::tip_of(self.tip, asset_id)
13899
} else {
139-
signed_extensions::ChargeAssetTxPaymentParams::tip(self.tip)
100+
transaction_extensions::ChargeAssetTxPaymentParams::tip(self.tip)
140101
};
141102

142103
let charge_transaction_params =
143-
signed_extensions::ChargeTransactionPaymentParams::tip(self.tip);
104+
transaction_extensions::ChargeTransactionPaymentParams::tip(self.tip);
144105

145-
let check_nonce_params = CheckNonceParams(self.nonce);
106+
let check_nonce_params = if let Some(nonce) = self.nonce {
107+
transaction_extensions::CheckNonceParams::with_nonce(nonce)
108+
} else {
109+
transaction_extensions::CheckNonceParams::from_chain()
110+
};
146111

147112
(
113+
(),
148114
(),
149115
(),
150116
check_nonce_params,
@@ -156,3 +122,16 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
156122
)
157123
}
158124
}
125+
126+
#[cfg(test)]
127+
mod test {
128+
use super::*;
129+
130+
fn assert_default<T: Default>(_t: T) {}
131+
132+
#[test]
133+
fn params_are_default() {
134+
let params = DefaultExtrinsicParamsBuilder::<crate::config::PolkadotConfig>::new().build();
135+
assert_default(params)
136+
}
137+
}

0 commit comments

Comments
 (0)