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 constructing and submitting V5 transactions #1931

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
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.

Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

use crate::config::signed_extensions::{
use crate::config::transaction_extensions::{
ChargeAssetTxPayment, ChargeTransactionPayment, CheckNonce,
};
use crate::config::SignedExtension;
use crate::config::TransactionExtension;
use crate::dynamic::Value;
use crate::{config::Config, error::Error, Metadata};
use frame_decode::extrinsics::ExtrinsicExtensions;
use scale_decode::DecodeAsType;

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

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

/// Returns an iterator over each of the signed extension details of the extrinsic.
pub fn iter(&self) -> impl Iterator<Item = ExtrinsicSignedExtension<T>> {
self.decoded_info.iter().map(|s| ExtrinsicSignedExtension {
bytes: &self.bytes[s.range()],
ty_id: *s.ty(),
identifier: s.name(),
metadata: self.metadata,
_marker: core::marker::PhantomData,
})
pub fn iter(&self) -> impl Iterator<Item = ExtrinsicTransactionExtension<T>> {
self.decoded_info
.iter()
.map(|s| ExtrinsicTransactionExtension {
bytes: &self.bytes[s.range()],
ty_id: *s.ty(),
identifier: s.name(),
metadata: self.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> {
pub fn find<S: TransactionExtension<T>>(&self) -> Result<Option<S::Decoded>, Error> {
for ext in self.iter() {
match ext.as_signed_extension::<S>() {
// We found a match; return it:
Expand Down Expand Up @@ -90,15 +92,15 @@ impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> {

/// A single signed extension
#[derive(Debug, Clone)]
pub struct ExtrinsicSignedExtension<'a, T: Config> {
pub struct ExtrinsicTransactionExtension<'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> {
impl<'a, T: Config> ExtrinsicTransactionExtension<'a, T> {
/// The bytes representing this signed extension.
pub fn bytes(&self) -> &'a [u8] {
self.bytes
Expand Down Expand Up @@ -127,7 +129,9 @@ impl<'a, T: Config> ExtrinsicSignedExtension<'a, T> {
/// 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> {
pub fn as_signed_extension<S: TransactionExtension<T>>(
&self,
) -> Result<Option<S::Decoded>, Error> {
if !S::matches(self.identifier, self.ty_id, self.metadata.types()) {
return Ok(None);
}
Expand Down
12 changes: 6 additions & 6 deletions core/src/blocks/extrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// see LICENSE for license details.

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

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

/// The index of the pallet that the extrinsic originated from.
Expand Down Expand Up @@ -544,7 +544,7 @@ mod tests {
);

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

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

// Note: `create_unsigned` produces the extrinsic bytes by prefixing the extrinsic length.
Expand Down
6 changes: 4 additions & 2 deletions core/src/blocks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
//! # ]);
//! ```

mod extrinsic_signed_extensions;
mod extrinsic_transaction_extensions;
mod extrinsics;
mod static_extrinsic;

Expand All @@ -74,7 +74,9 @@ use crate::Metadata;
use alloc::vec::Vec;

pub use crate::error::BlockError;
pub use extrinsic_signed_extensions::{ExtrinsicSignedExtension, ExtrinsicSignedExtensions};
pub use extrinsic_transaction_extensions::{
ExtrinsicTransactionExtension, ExtrinsicTransactionExtensions,
};
pub use extrinsics::{ExtrinsicDetails, ExtrinsicMetadataDetails, Extrinsics, FoundExtrinsic};
pub use static_extrinsic::StaticExtrinsic;

Expand Down
2 changes: 1 addition & 1 deletion core/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use crate::{config::Config, metadata::Metadata};
use derive_where::derive_where;

/// This provides access to some relevant client state in signed extensions,
/// This provides access to some relevant client state in transaction extensions,
/// and is just a combination of some of the available properties.
#[derive_where(Clone, Debug)]
pub struct ClientState<C: Config> {
Expand Down
107 changes: 43 additions & 64 deletions core/src/config/default_extrinsic_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,32 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

use super::signed_extensions::CheckNonceParams;
use super::{signed_extensions, ExtrinsicParams};
use super::{Config, Header};
use super::Config;
use super::{transaction_extensions, ExtrinsicParams};

/// The default [`super::ExtrinsicParams`] implementation understands common signed extensions
/// and how to apply them to a given chain.
pub type DefaultExtrinsicParams<T> = signed_extensions::AnyOf<
pub type DefaultExtrinsicParams<T> = transaction_extensions::AnyOf<
T,
(
signed_extensions::CheckSpecVersion,
signed_extensions::CheckTxVersion,
signed_extensions::CheckNonce,
signed_extensions::CheckGenesis<T>,
signed_extensions::CheckMortality<T>,
signed_extensions::ChargeAssetTxPayment<T>,
signed_extensions::ChargeTransactionPayment,
signed_extensions::CheckMetadataHash,
transaction_extensions::VerifySignature<T>,
transaction_extensions::CheckSpecVersion,
transaction_extensions::CheckTxVersion,
transaction_extensions::CheckNonce,
transaction_extensions::CheckGenesis<T>,
transaction_extensions::CheckMortality<T>,
transaction_extensions::ChargeAssetTxPayment<T>,
transaction_extensions::ChargeTransactionPayment,
transaction_extensions::CheckMetadataHash,
),
>;

/// A builder that outputs the set of [`super::ExtrinsicParams::Params`] required for
/// [`DefaultExtrinsicParams`]. This may expose methods that aren't applicable to the current
/// chain; such values will simply be ignored if so.
pub struct DefaultExtrinsicParamsBuilder<T: Config> {
/// `None` means the tx will be immortal.
mortality: Option<Mortality<T::Hash>>,
/// `None` means the tx will be immortal, else it's mortal for N blocks (if possible).
mortality: Option<u64>,
Copy link
Member

Choose a reason for hiding this comment

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

I guess this is related to changes of RefineParams stuff, I like that the API is much easier to use block number instead of both block hash and block number

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I messed with everything a bunch and then realised I had to undo a load of stuff, but wanted to keep not needing the block hash too :)

/// `None` means the nonce will be automatically set.
nonce: Option<u64>,
/// `None` means we'll use the native token.
Expand All @@ -36,16 +36,6 @@ pub struct DefaultExtrinsicParamsBuilder<T: Config> {
tip_of: u128,
}

struct Mortality<Hash> {
/// Block hash that mortality starts from
checkpoint_hash: Hash,
/// Block number that mortality starts from (must
// point to the same block as the hash above)
checkpoint_number: u64,
/// How many blocks the tx is mortal for
period: u64,
}

impl<T: Config> Default for DefaultExtrinsicParamsBuilder<T> {
fn default() -> Self {
Self {
Expand All @@ -65,15 +55,10 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
Default::default()
}

/// Make the transaction mortal, given a block header that it should be mortal from,
/// and the number of blocks (roughly; it'll be rounded to a power of two) that it will
/// be mortal for.
pub fn mortal(mut self, from_block: &T::Header, for_n_blocks: u64) -> Self {
self.mortality = Some(Mortality {
checkpoint_hash: from_block.hash(),
checkpoint_number: from_block.number().into(),
period: for_n_blocks,
});
/// Make the transaction mortal, given a number of blocks it will be mortal for from
/// the current block at the time of submission.
pub fn mortal(mut self, for_n_blocks: u64) -> Self {
self.mortality = Some(for_n_blocks);
self
}

Expand All @@ -83,26 +68,6 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
self
}

/// Make the transaction mortal, given a block number and block hash (which must both point to
/// the same block) that it should be mortal from, and the number of blocks (roughly; it'll be
/// rounded to a power of two) that it will be mortal for.
///
/// Prefer to use [`DefaultExtrinsicParamsBuilder::mortal()`], which ensures that the block hash
/// and number align.
pub fn mortal_unchecked(
mut self,
from_block_number: u64,
from_block_hash: T::Hash,
for_n_blocks: u64,
) -> Self {
self.mortality = Some(Mortality {
checkpoint_hash: from_block_hash,
checkpoint_number: from_block_number,
period: for_n_blocks,
});
self
}

/// Provide a tip to the block author in the chain's native token.
pub fn tip(mut self, tip: u128) -> Self {
self.tip = tip;
Expand All @@ -123,28 +88,29 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {

/// Build the extrinsic parameters.
pub fn build(self) -> <DefaultExtrinsicParams<T> as ExtrinsicParams<T>>::Params {
let check_mortality_params = if let Some(mortality) = self.mortality {
signed_extensions::CheckMortalityParams::mortal(
mortality.period,
mortality.checkpoint_number,
mortality.checkpoint_hash,
)
let check_mortality_params = if let Some(for_n_blocks) = self.mortality {
transaction_extensions::CheckMortalityParams::mortal(for_n_blocks)
} else {
signed_extensions::CheckMortalityParams::immortal()
transaction_extensions::CheckMortalityParams::immortal()
};

let charge_asset_tx_params = if let Some(asset_id) = self.tip_of_asset_id {
signed_extensions::ChargeAssetTxPaymentParams::tip_of(self.tip, asset_id)
transaction_extensions::ChargeAssetTxPaymentParams::tip_of(self.tip, asset_id)
} else {
signed_extensions::ChargeAssetTxPaymentParams::tip(self.tip)
transaction_extensions::ChargeAssetTxPaymentParams::tip(self.tip)
};

let charge_transaction_params =
signed_extensions::ChargeTransactionPaymentParams::tip(self.tip);
transaction_extensions::ChargeTransactionPaymentParams::tip(self.tip);

let check_nonce_params = CheckNonceParams(self.nonce);
let check_nonce_params = if let Some(nonce) = self.nonce {
transaction_extensions::CheckNonceParams::with_nonce(nonce)
} else {
transaction_extensions::CheckNonceParams::from_chain()
};

(
(),
(),
(),
check_nonce_params,
Expand All @@ -156,3 +122,16 @@ impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
)
}
}

#[cfg(test)]
mod test {
use super::*;

fn assert_default<T: Default>(_t: T) {}

#[test]
fn params_are_default() {
let params = DefaultExtrinsicParamsBuilder::<crate::config::PolkadotConfig>::new().build();
assert_default(params)
}
}
Loading
Loading