mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-16 21:21:03 +00:00
Add ‘transaction_version’ to the signed transaction (#5979)
* Add ‘transaction_version’ to the signed transaction This allows hardware wallets to know which transactions they can safely sign. To reduce transaction size, I reduced it to a ‘u8’ from a ‘u32’. Fixes #5951. * Restore transaction_version to a u32 * Fix comments `transaction_version` is not part of a tx, but is still signed. Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Fix the test suite I had forgotten to change the production of transactions in the test code. * Fix benchmarks * Improve docs for `CheckTxVersion` in `frame_system` Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Remove spurious cast Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
This commit is contained in:
@@ -281,7 +281,8 @@ pub type SignedBlock = generic::SignedBlock<Block>;
|
|||||||
pub type BlockId = generic::BlockId<Block>;
|
pub type BlockId = generic::BlockId<Block>;
|
||||||
/// The SignedExtension to the basic transaction logic.
|
/// The SignedExtension to the basic transaction logic.
|
||||||
pub type SignedExtra = (
|
pub type SignedExtra = (
|
||||||
system::CheckVersion<Runtime>,
|
system::CheckSpecVersion<Runtime>,
|
||||||
|
system::CheckTxVersion<Runtime>,
|
||||||
system::CheckGenesis<Runtime>,
|
system::CheckGenesis<Runtime>,
|
||||||
system::CheckEra<Runtime>,
|
system::CheckEra<Runtime>,
|
||||||
system::CheckNonce<Runtime>,
|
system::CheckNonce<Runtime>,
|
||||||
|
|||||||
@@ -602,12 +602,16 @@ mod tests {
|
|||||||
let from: Address = AccountPublic::from(charlie.public()).into_account().into();
|
let from: Address = AccountPublic::from(charlie.public()).into_account().into();
|
||||||
let genesis_hash = service.client().block_hash(0).unwrap().unwrap();
|
let genesis_hash = service.client().block_hash(0).unwrap().unwrap();
|
||||||
let best_block_id = BlockId::number(service.client().chain_info().best_number);
|
let best_block_id = BlockId::number(service.client().chain_info().best_number);
|
||||||
let version = service.client().runtime_version_at(&best_block_id).unwrap().spec_version;
|
let (spec_version, transaction_version) = {
|
||||||
|
let version = service.client().runtime_version_at(&best_block_id).unwrap();
|
||||||
|
(version.spec_version, version.transaction_version)
|
||||||
|
};
|
||||||
let signer = charlie.clone();
|
let signer = charlie.clone();
|
||||||
|
|
||||||
let function = Call::Balances(BalancesCall::transfer(to.into(), amount));
|
let function = Call::Balances(BalancesCall::transfer(to.into(), amount));
|
||||||
|
|
||||||
let check_version = frame_system::CheckVersion::new();
|
let check_spec_version = frame_system::CheckSpecVersion::new();
|
||||||
|
let check_tx_version = frame_system::CheckTxVersion::new();
|
||||||
let check_genesis = frame_system::CheckGenesis::new();
|
let check_genesis = frame_system::CheckGenesis::new();
|
||||||
let check_era = frame_system::CheckEra::from(Era::Immortal);
|
let check_era = frame_system::CheckEra::from(Era::Immortal);
|
||||||
let check_nonce = frame_system::CheckNonce::from(index);
|
let check_nonce = frame_system::CheckNonce::from(index);
|
||||||
@@ -615,7 +619,8 @@ mod tests {
|
|||||||
let payment = pallet_transaction_payment::ChargeTransactionPayment::from(0);
|
let payment = pallet_transaction_payment::ChargeTransactionPayment::from(0);
|
||||||
let validate_grandpa_equivocation = pallet_grandpa::ValidateEquivocationReport::new();
|
let validate_grandpa_equivocation = pallet_grandpa::ValidateEquivocationReport::new();
|
||||||
let extra = (
|
let extra = (
|
||||||
check_version,
|
check_spec_version,
|
||||||
|
check_tx_version,
|
||||||
check_genesis,
|
check_genesis,
|
||||||
check_era,
|
check_era,
|
||||||
check_nonce,
|
check_nonce,
|
||||||
@@ -626,7 +631,7 @@ mod tests {
|
|||||||
let raw_payload = SignedPayload::from_raw(
|
let raw_payload = SignedPayload::from_raw(
|
||||||
function,
|
function,
|
||||||
extra,
|
extra,
|
||||||
(version, genesis_hash, genesis_hash, (), (), (), ())
|
(spec_version, transaction_version, genesis_hash, genesis_hash, (), (), (), ())
|
||||||
);
|
);
|
||||||
let signature = raw_payload.using_encoded(|payload| {
|
let signature = raw_payload.using_encoded(|payload| {
|
||||||
signer.sign(payload)
|
signer.sign(payload)
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ const COMPACT_CODE: &[u8] = node_runtime::WASM_BINARY;
|
|||||||
|
|
||||||
const GENESIS_HASH: [u8; 32] = [69u8; 32];
|
const GENESIS_HASH: [u8; 32] = [69u8; 32];
|
||||||
|
|
||||||
const VERSION: u32 = node_runtime::VERSION.spec_version;
|
const TRANSACTION_VERSION: u32 = node_runtime::VERSION.transaction_version;
|
||||||
|
|
||||||
|
const SPEC_VERSION: u32 = node_runtime::VERSION.spec_version;
|
||||||
|
|
||||||
const HEAP_PAGES: u64 = 20;
|
const HEAP_PAGES: u64 = 20;
|
||||||
|
|
||||||
@@ -52,7 +54,7 @@ enum ExecutionMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic {
|
fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic {
|
||||||
node_testing::keyring::sign(xt, VERSION, GENESIS_HASH)
|
node_testing::keyring::sign(xt, SPEC_VERSION, TRANSACTION_VERSION, GENESIS_HASH)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_test_ext(genesis_config: &GenesisConfig) -> TestExternalities<BlakeTwo256> {
|
fn new_test_ext(genesis_config: &GenesisConfig) -> TestExternalities<BlakeTwo256> {
|
||||||
|
|||||||
@@ -71,12 +71,14 @@ pub const COMPACT_CODE: &[u8] = node_runtime::WASM_BINARY;
|
|||||||
|
|
||||||
pub const GENESIS_HASH: [u8; 32] = [69u8; 32];
|
pub const GENESIS_HASH: [u8; 32] = [69u8; 32];
|
||||||
|
|
||||||
pub const VERSION: u32 = node_runtime::VERSION.spec_version;
|
pub const SPEC_VERSION: u32 = node_runtime::VERSION.spec_version;
|
||||||
|
|
||||||
|
pub const TRANSACTION_VERSION: u32 = node_runtime::VERSION.transaction_version;
|
||||||
|
|
||||||
pub type TestExternalities<H> = CoreTestExternalities<H, u64>;
|
pub type TestExternalities<H> = CoreTestExternalities<H, u64>;
|
||||||
|
|
||||||
pub fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic {
|
pub fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic {
|
||||||
node_testing::keyring::sign(xt, VERSION, GENESIS_HASH)
|
node_testing::keyring::sign(xt, SPEC_VERSION, TRANSACTION_VERSION, GENESIS_HASH)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_transfer_call() -> pallet_balances::Call<Runtime> {
|
pub fn default_transfer_call() -> pallet_balances::Call<Runtime> {
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ fn should_submit_signed_twice_from_the_same_account() {
|
|||||||
let s = state.read();
|
let s = state.read();
|
||||||
fn nonce(tx: UncheckedExtrinsic) -> frame_system::CheckNonce<Runtime> {
|
fn nonce(tx: UncheckedExtrinsic) -> frame_system::CheckNonce<Runtime> {
|
||||||
let extra = tx.signature.unwrap().2;
|
let extra = tx.signature.unwrap().2;
|
||||||
extra.3
|
extra.4
|
||||||
}
|
}
|
||||||
let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap());
|
let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap());
|
||||||
let nonce2 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[1]).unwrap());
|
let nonce2 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[1]).unwrap());
|
||||||
@@ -170,7 +170,7 @@ fn should_submit_signed_twice_from_all_accounts() {
|
|||||||
let s = state.read();
|
let s = state.read();
|
||||||
fn nonce(tx: UncheckedExtrinsic) -> frame_system::CheckNonce<Runtime> {
|
fn nonce(tx: UncheckedExtrinsic) -> frame_system::CheckNonce<Runtime> {
|
||||||
let extra = tx.signature.unwrap().2;
|
let extra = tx.signature.unwrap().2;
|
||||||
extra.3
|
extra.4
|
||||||
}
|
}
|
||||||
let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap());
|
let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap());
|
||||||
let nonce2 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[1]).unwrap());
|
let nonce2 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[1]).unwrap());
|
||||||
|
|||||||
@@ -535,7 +535,8 @@ impl<LocalCall> frame_system::offchain::CreateSignedTransaction<LocalCall> for R
|
|||||||
.saturating_sub(1);
|
.saturating_sub(1);
|
||||||
let tip = 0;
|
let tip = 0;
|
||||||
let extra: SignedExtra = (
|
let extra: SignedExtra = (
|
||||||
frame_system::CheckVersion::<Runtime>::new(),
|
frame_system::CheckSpecVersion::<Runtime>::new(),
|
||||||
|
frame_system::CheckTxVersion::<Runtime>::new(),
|
||||||
frame_system::CheckGenesis::<Runtime>::new(),
|
frame_system::CheckGenesis::<Runtime>::new(),
|
||||||
frame_system::CheckEra::<Runtime>::from(generic::Era::mortal(period, current_block)),
|
frame_system::CheckEra::<Runtime>::from(generic::Era::mortal(period, current_block)),
|
||||||
frame_system::CheckNonce::<Runtime>::from(nonce),
|
frame_system::CheckNonce::<Runtime>::from(nonce),
|
||||||
@@ -745,8 +746,13 @@ pub type SignedBlock = generic::SignedBlock<Block>;
|
|||||||
/// BlockId type as expected by this runtime.
|
/// BlockId type as expected by this runtime.
|
||||||
pub type BlockId = generic::BlockId<Block>;
|
pub type BlockId = generic::BlockId<Block>;
|
||||||
/// The SignedExtension to the basic transaction logic.
|
/// The SignedExtension to the basic transaction logic.
|
||||||
|
///
|
||||||
|
/// When you change this, you **MUST** modify [`sign`] in `bin/node/testing/src/keyring.rs`!
|
||||||
|
///
|
||||||
|
/// [`sign`]: <../../testing/src/keyring.rs.html>
|
||||||
pub type SignedExtra = (
|
pub type SignedExtra = (
|
||||||
frame_system::CheckVersion<Runtime>,
|
frame_system::CheckSpecVersion<Runtime>,
|
||||||
|
frame_system::CheckTxVersion<Runtime>,
|
||||||
frame_system::CheckGenesis<Runtime>,
|
frame_system::CheckGenesis<Runtime>,
|
||||||
frame_system::CheckEra<Runtime>,
|
frame_system::CheckEra<Runtime>,
|
||||||
frame_system::CheckNonce<Runtime>,
|
frame_system::CheckNonce<Runtime>,
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ pub fn to_session_keys(
|
|||||||
/// Returns transaction extra.
|
/// Returns transaction extra.
|
||||||
pub fn signed_extra(nonce: Index, extra_fee: Balance) -> SignedExtra {
|
pub fn signed_extra(nonce: Index, extra_fee: Balance) -> SignedExtra {
|
||||||
(
|
(
|
||||||
frame_system::CheckVersion::new(),
|
frame_system::CheckSpecVersion::new(),
|
||||||
|
frame_system::CheckTxVersion::new(),
|
||||||
frame_system::CheckGenesis::new(),
|
frame_system::CheckGenesis::new(),
|
||||||
frame_system::CheckEra::from(Era::mortal(256, 0)),
|
frame_system::CheckEra::from(Era::mortal(256, 0)),
|
||||||
frame_system::CheckNonce::from(nonce),
|
frame_system::CheckNonce::from(nonce),
|
||||||
@@ -79,10 +80,10 @@ pub fn signed_extra(nonce: Index, extra_fee: Balance) -> SignedExtra {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sign given `CheckedExtrinsic`.
|
/// Sign given `CheckedExtrinsic`.
|
||||||
pub fn sign(xt: CheckedExtrinsic, version: u32, genesis_hash: [u8; 32]) -> UncheckedExtrinsic {
|
pub fn sign(xt: CheckedExtrinsic, spec_version: u32, tx_version: u32, genesis_hash: [u8; 32]) -> UncheckedExtrinsic {
|
||||||
match xt.signed {
|
match xt.signed {
|
||||||
Some((signed, extra)) => {
|
Some((signed, extra)) => {
|
||||||
let payload = (xt.function, extra.clone(), version, genesis_hash, genesis_hash);
|
let payload = (xt.function, extra.clone(), spec_version, tx_version, genesis_hash, genesis_hash);
|
||||||
let key = AccountKeyring::from_account_id(&signed).unwrap();
|
let key = AccountKeyring::from_account_id(&signed).unwrap();
|
||||||
let signature = payload.using_encoded(|b| {
|
let signature = payload.using_encoded(|b| {
|
||||||
if b.len() > 256 {
|
if b.len() > 256 {
|
||||||
|
|||||||
@@ -702,7 +702,8 @@ fn create_extrinsic<C: Crypto>(
|
|||||||
{
|
{
|
||||||
let extra = |i: Index, f: Balance| {
|
let extra = |i: Index, f: Balance| {
|
||||||
(
|
(
|
||||||
frame_system::CheckVersion::<Runtime>::new(),
|
frame_system::CheckSpecVersion::<Runtime>::new(),
|
||||||
|
frame_system::CheckTxVersion::<Runtime>::new(),
|
||||||
frame_system::CheckGenesis::<Runtime>::new(),
|
frame_system::CheckGenesis::<Runtime>::new(),
|
||||||
frame_system::CheckEra::<Runtime>::from(Era::Immortal),
|
frame_system::CheckEra::<Runtime>::from(Era::Immortal),
|
||||||
frame_system::CheckNonce::<Runtime>::from(i),
|
frame_system::CheckNonce::<Runtime>::from(i),
|
||||||
@@ -715,7 +716,8 @@ fn create_extrinsic<C: Crypto>(
|
|||||||
function,
|
function,
|
||||||
extra(index, 0),
|
extra(index, 0),
|
||||||
(
|
(
|
||||||
VERSION.spec_version as u32,
|
VERSION.spec_version,
|
||||||
|
VERSION.transaction_version,
|
||||||
genesis_hash,
|
genesis_hash,
|
||||||
genesis_hash,
|
genesis_hash,
|
||||||
(),
|
(),
|
||||||
|
|||||||
@@ -53,7 +53,9 @@
|
|||||||
//! - [`CheckEra`]: Checks the era of the transaction. Contains a single payload of type `Era`.
|
//! - [`CheckEra`]: Checks the era of the transaction. Contains a single payload of type `Era`.
|
||||||
//! - [`CheckGenesis`]: Checks the provided genesis hash of the transaction. Must be a part of the
|
//! - [`CheckGenesis`]: Checks the provided genesis hash of the transaction. Must be a part of the
|
||||||
//! signed payload of the transaction.
|
//! signed payload of the transaction.
|
||||||
//! - [`CheckVersion`]: Checks that the runtime version is the same as the one encoded in the
|
//! - [`CheckSpecVersion`]: Checks that the runtime version is the same as the one used to sign the
|
||||||
|
//! transaction.
|
||||||
|
//! - [`CheckTxVersion`]: Checks that the transaction version is the same as the one used to sign the
|
||||||
//! transaction.
|
//! transaction.
|
||||||
//!
|
//!
|
||||||
//! Lookup the runtime aggregator file (e.g. `node/runtime`) to see the full list of signed
|
//! Lookup the runtime aggregator file (e.g. `node/runtime`) to see the full list of signed
|
||||||
@@ -1735,14 +1737,14 @@ impl<T: Trait + Send + Sync> SignedExtension for CheckGenesis<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure the runtime version registered in the transaction is the same as at present.
|
/// Ensure the transaction version registered in the transaction is the same as at present.
|
||||||
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
|
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
|
||||||
pub struct CheckVersion<T: Trait + Send + Sync>(sp_std::marker::PhantomData<T>);
|
pub struct CheckTxVersion<T: Trait + Send + Sync>(sp_std::marker::PhantomData<T>);
|
||||||
|
|
||||||
impl<T: Trait + Send + Sync> Debug for CheckVersion<T> {
|
impl<T: Trait + Send + Sync> Debug for CheckTxVersion<T> {
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
||||||
write!(f, "CheckVersion")
|
write!(f, "CheckTxVersion")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
@@ -1751,19 +1753,54 @@ impl<T: Trait + Send + Sync> Debug for CheckVersion<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Trait + Send + Sync> CheckVersion<T> {
|
impl<T: Trait + Send + Sync> CheckTxVersion<T> {
|
||||||
|
/// Create new `SignedExtension` to check transaction version.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self(sp_std::marker::PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Trait + Send + Sync> SignedExtension for CheckTxVersion<T> {
|
||||||
|
type AccountId = T::AccountId;
|
||||||
|
type Call = <T as Trait>::Call;
|
||||||
|
type AdditionalSigned = u32;
|
||||||
|
type Pre = ();
|
||||||
|
const IDENTIFIER: &'static str = "CheckTxVersion";
|
||||||
|
|
||||||
|
fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
|
||||||
|
Ok(<Module<T>>::runtime_version().transaction_version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensure the runtime version registered in the transaction is the same as at present.
|
||||||
|
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
|
||||||
|
pub struct CheckSpecVersion<T: Trait + Send + Sync>(sp_std::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T: Trait + Send + Sync> Debug for CheckSpecVersion<T> {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
||||||
|
write!(f, "CheckSpecVersion")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Trait + Send + Sync> CheckSpecVersion<T> {
|
||||||
/// Create new `SignedExtension` to check runtime version.
|
/// Create new `SignedExtension` to check runtime version.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(sp_std::marker::PhantomData)
|
Self(sp_std::marker::PhantomData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Trait + Send + Sync> SignedExtension for CheckVersion<T> {
|
impl<T: Trait + Send + Sync> SignedExtension for CheckSpecVersion<T> {
|
||||||
type AccountId = T::AccountId;
|
type AccountId = T::AccountId;
|
||||||
type Call = <T as Trait>::Call;
|
type Call = <T as Trait>::Call;
|
||||||
type AdditionalSigned = u32;
|
type AdditionalSigned = u32;
|
||||||
type Pre = ();
|
type Pre = ();
|
||||||
const IDENTIFIER: &'static str = "CheckVersion";
|
const IDENTIFIER: &'static str = "CheckSpecVersion";
|
||||||
|
|
||||||
fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
|
fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
|
||||||
Ok(<Module<T>>::runtime_version().spec_version)
|
Ok(<Module<T>>::runtime_version().spec_version)
|
||||||
|
|||||||
Reference in New Issue
Block a user