// Copyright 2019 Parity Technologies (UK) Ltd. // This file is part of substrate-subxt. // // subxt is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // subxt is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with substrate-subxt. If not, see . use crate::paint::{ balances::Balances, system::System, }; use parity_scale_codec::{ Codec, Decode, Encode, }; use runtime_primitives::{ generic::{ Era, SignedPayload, UncheckedExtrinsic, }, traits::{ IdentifyAccount, SignedExtension, Verify, }, transaction_validity::TransactionValidityError, }; use std::marker::PhantomData; use substrate_primitives::Pair; /// SignedExtra checks copied from substrate, in order to remove requirement to implement /// substrate's `paint_system::Trait` /// Ensure the runtime version registered in the transaction is the same as at present. /// /// # Note /// /// This is modified from the substrate version to allow passing in of the version, which is /// returned via `additional_signed()`. #[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] pub struct CheckVersion( PhantomData, /// Local version to be used for `AdditionalSigned` #[codec(skip)] u32, ); impl SignedExtension for CheckVersion where T: System + Send + Sync, { type AccountId = u64; type Call = (); type AdditionalSigned = u32; type Pre = (); fn additional_signed( &self, ) -> Result { Ok(self.1) } } /// Check genesis hash /// /// # Note /// /// This is modified from the substrate version to allow passing in of the genesis hash, which is /// returned via `additional_signed()`. #[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] pub struct CheckGenesis( PhantomData, /// Local genesis hash to be used for `AdditionalSigned` #[codec(skip)] T::Hash, ); impl SignedExtension for CheckGenesis where T: System + Send + Sync, { type AccountId = u64; type Call = (); type AdditionalSigned = T::Hash; type Pre = (); fn additional_signed( &self, ) -> Result { Ok(self.1) } } /// Check for transaction mortality. /// /// # Note /// /// This is modified from the substrate version to allow passing in of the genesis hash, which is /// returned via `additional_signed()`. It assumes therefore `Era::Immortal` (The transaction is /// valid forever) #[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] pub struct CheckEra( /// The default structure for the Extra encoding (Era, PhantomData), /// Local genesis hash to be used for `AdditionalSigned` #[codec(skip)] T::Hash, ); impl SignedExtension for CheckEra where T: System + Send + Sync, { type AccountId = u64; type Call = (); type AdditionalSigned = T::Hash; type Pre = (); fn additional_signed( &self, ) -> Result { Ok(self.1) } } /// Nonce check and increment to give replay protection for transactions. #[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] pub struct CheckNonce(#[codec(compact)] T::Index); impl SignedExtension for CheckNonce where T: System + Send + Sync, { type AccountId = u64; type Call = (); type AdditionalSigned = (); type Pre = (); fn additional_signed( &self, ) -> Result { Ok(()) } } /// Resource limit check. #[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] pub struct CheckWeight(PhantomData); impl SignedExtension for CheckWeight where T: System + Send + Sync, { type AccountId = u64; type Call = (); type AdditionalSigned = (); type Pre = (); fn additional_signed( &self, ) -> Result { Ok(()) } } /// Require the transactor pay for themselves and maybe include a tip to gain additional priority /// in the queue. #[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] pub struct ChargeTransactionPayment(#[codec(compact)] T::Balance); impl SignedExtension for ChargeTransactionPayment where T: Balances + Send + Sync, { type AccountId = u64; type Call = (); type AdditionalSigned = (); type Pre = (); fn additional_signed( &self, ) -> Result { Ok(()) } } /// Checks if a transaction would exhausts the block gas limit. #[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] pub struct CheckBlockGasLimit(PhantomData); impl SignedExtension for CheckBlockGasLimit where T: System + Send + Sync, { type AccountId = u64; type Call = (); type AdditionalSigned = (); type Pre = (); fn additional_signed( &self, ) -> Result { Ok(()) } } pub trait SignedExtra { type Extra: SignedExtension; fn extra(&self) -> Self::Extra; } #[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] pub struct DefaultExtra { version: u32, nonce: T::Index, genesis_hash: T::Hash, } impl DefaultExtra { pub fn new(version: u32, nonce: T::Index, genesis_hash: T::Hash) -> Self { DefaultExtra { version, nonce, genesis_hash, } } } impl SignedExtra for DefaultExtra { type Extra = ( CheckVersion, CheckGenesis, CheckEra, CheckNonce, CheckWeight, ChargeTransactionPayment, CheckBlockGasLimit, ); fn extra(&self) -> Self::Extra { ( CheckVersion(PhantomData, self.version), CheckGenesis(PhantomData, self.genesis_hash), CheckEra((Era::Immortal, PhantomData), self.genesis_hash), CheckNonce(self.nonce), CheckWeight(PhantomData), ChargeTransactionPayment(::Balance::default()), CheckBlockGasLimit(PhantomData), ) } } impl SignedExtension for DefaultExtra { type AccountId = T::AccountId; type Call = (); type AdditionalSigned = <>::Extra as SignedExtension>::AdditionalSigned; type Pre = (); fn additional_signed( &self, ) -> Result { self.extra().additional_signed() } } pub fn create_and_sign( signer: P, call: C, extra: E, ) -> Result< UncheckedExtrinsic>::Extra>, TransactionValidityError, > where P: Pair, S: Verify + Codec + From, S::Signer: From + IdentifyAccount, C: Encode, E: SignedExtra + SignedExtension, T::Address: From, { let raw_payload = SignedPayload::new(call, extra.extra())?; let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); let (call, extra, _) = raw_payload.deconstruct(); let account_id = S::Signer::from(signer.public()).into_account(); Ok(UncheckedExtrinsic::new_signed( call, account_id.into(), signature.into(), extra, )) }