diff --git a/Cargo.lock b/Cargo.lock index bf0ea6fec12..bdbfa407da9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -997,6 +997,7 @@ dependencies = [ "frame-system", "hash-db", "hex-literal", + "impl-trait-for-tuples", "num-traits", "parity-scale-codec", "scale-info", diff --git a/primitives/chain-bridge-hub-cumulus/src/lib.rs b/primitives/chain-bridge-hub-cumulus/src/lib.rs index 72faabe43cd..e33131ff8a2 100644 --- a/primitives/chain-bridge-hub-cumulus/src/lib.rs +++ b/primitives/chain-bridge-hub-cumulus/src/lib.rs @@ -18,9 +18,9 @@ use bp_messages::*; pub use bp_polkadot_core::{ - AccountId, AccountInfoStorageMapKeyProvider, AccountPublic, Balance, BlockNumber, Hash, Hasher, - Hashing, Header, Index, Nonce, Perbill, Signature, SignedBlock, SignedExtensions, - UncheckedExtrinsic, TX_EXTRA_BYTES, + AccountId, AccountInfoStorageMapKeyProvider, AccountPublic, Balance, BlockNumber, + BridgeSignedExtension, Hash, Hasher, Hashing, Header, Index, Nonce, Perbill, + PolkadotSignedExtension, Signature, SignedBlock, UncheckedExtrinsic, TX_EXTRA_BYTES, }; use frame_support::{ dispatch::DispatchClass, diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs index 7558ce762d0..3d4b72fccb9 100644 --- a/primitives/polkadot-core/src/lib.rs +++ b/primitives/polkadot-core/src/lib.rs @@ -18,7 +18,6 @@ use bp_messages::MessageNonce; use bp_runtime::{Chain, EncodedOrDecodedCall, StorageMapKeyProvider}; -use codec::Compact; use frame_support::{ dispatch::DispatchClass, parameter_types, @@ -29,17 +28,16 @@ use frame_support::{ Blake2_128Concat, RuntimeDebug, }; use frame_system::limits; -use scale_info::TypeInfo; use sp_core::{storage::StorageKey, Hasher as HasherT}; use sp_runtime::{ generic, - traits::{BlakeTwo256, DispatchInfoOf, IdentifyAccount, Verify}, - transaction_validity::TransactionValidityError, + traits::{BlakeTwo256, IdentifyAccount, Verify}, MultiAddress, MultiSignature, OpaqueExtrinsic, }; use sp_std::prelude::Vec; // Re-export's to avoid extra substrate dependencies in chain-specific crates. +use bp_runtime::extensions::*; pub use frame_support::{weights::constants::ExtrinsicBaseWeight, Parameter}; pub use sp_runtime::{traits::Convert, Perbill}; @@ -184,39 +182,72 @@ pub type SignedBlock = generic::SignedBlock; pub type Balance = u128; /// Unchecked Extrinsic type. -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic< - AccountAddress, - EncodedOrDecodedCall, - Signature, - SignedExtensions, ->; +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic, Signature, SignedExt>; /// Account address, used by the Polkadot-like chain. pub type Address = MultiAddress; -/// A type of the data encoded as part of the transaction. -pub type SignedExtra = - ((), (), (), (), sp_runtime::generic::Era, Compact, (), Compact); - -/// Parameters which are part of the payload used to produce transaction signature, -/// but don't end up in the transaction itself (i.e. inherent part of the runtime). -pub type AdditionalSigned = ((), u32, u32, Hash, Hash, (), (), ()); - -/// A simplified version of signed extensions meant for producing signed transactions -/// and signed payload in the client code. -#[derive(codec::Encode, codec::Decode, PartialEq, Eq, Clone, RuntimeDebug, TypeInfo)] -pub struct SignedExtensions { - encode_payload: SignedExtra, - // It may be set to `None` if extensions are decoded. We are never reconstructing transactions - // (and it makes no sense to do that) => decoded version of `SignedExtensions` is only used to - // read fields of `encode_payload`. And when resigning transaction, we're reconstructing - // `SignedExtensions` from the scratch. - #[codec(skip)] - additional_signed: Option, +/// Polkadot-like chain. +#[derive(RuntimeDebug)] +pub struct PolkadotLike; + +impl Chain for PolkadotLike { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +/// Some functionality associated with the default signed extension used by Polkadot and +/// Polkadot-like chains. +pub trait PolkadotSignedExtension { + fn from_params( + spec_version: u32, + transaction_version: u32, + era: bp_runtime::TransactionEraOf, + genesis_hash: Hash, + nonce: Nonce, + tip: Balance, + ) -> Self; + + fn nonce(&self) -> Nonce; + + fn tip(&self) -> Balance; } -impl SignedExtensions { - pub fn new( +type DefaultSignedExtra = ( + CheckNonZeroSender, + CheckSpecVersion, + CheckTxVersion, + CheckGenesis, + CheckEra, + CheckNonce, + CheckWeight, + ChargeTransactionPayment, +); + +/// The default signed extension used by Polkadot and Polkadot-like chains. +pub type DefaultSignedExtension = GenericSignedExtension; + +impl PolkadotSignedExtension for DefaultSignedExtension { + fn from_params( spec_version: u32, transaction_version: u32, era: bp_runtime::TransactionEraOf, @@ -224,8 +255,8 @@ impl SignedExtensions { nonce: Nonce, tip: Balance, ) -> Self { - Self { - encode_payload: ( + Self::new( + ( (), // non-zero sender (), // spec version (), // tx version @@ -235,7 +266,7 @@ impl SignedExtensions { (), // Check weight tip.into(), // transaction payment / tip (compact encoding) ), - additional_signed: Some(( + ( (), spec_version, transaction_version, @@ -244,78 +275,79 @@ impl SignedExtensions { (), (), (), - )), - } + ), + ) } -} -impl SignedExtensions { /// Return signer nonce, used to craft transaction. - pub fn nonce(&self) -> Nonce { - self.encode_payload.5.into() + fn nonce(&self) -> Nonce { + self.payload.5.into() } /// Return transaction tip. - pub fn tip(&self) -> Balance { - self.encode_payload.7.into() + fn tip(&self) -> Balance { + self.payload.7.into() } } -impl sp_runtime::traits::SignedExtension for SignedExtensions { - const IDENTIFIER: &'static str = "Not needed."; - - type AccountId = AccountId; - type Call = (); - type AdditionalSigned = AdditionalSigned; - type Pre = (); - - fn additional_signed( - &self, - ) -> Result { - // we shall not ever see this error in relay, because we are never signing decoded - // transactions. Instead we're constructing and signing new transactions. So the error code - // is kinda random here - self.additional_signed - .ok_or(frame_support::unsigned::TransactionValidityError::Unknown( - frame_support::unsigned::UnknownTransaction::Custom(0xFF), - )) - } - - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) +type BridgeSignedExtra = ( + CheckNonZeroSender, + CheckSpecVersion, + CheckTxVersion, + CheckGenesis, + CheckEra, + CheckNonce, + CheckWeight, + ChargeTransactionPayment, + BridgeRejectObsoleteHeadersAndMessages, +); + +/// The default signed extension used by Polkadot and Polkadot-like chains with bridging. +pub type BridgeSignedExtension = GenericSignedExtension; + +impl PolkadotSignedExtension for BridgeSignedExtension { + fn from_params( + spec_version: u32, + transaction_version: u32, + era: bp_runtime::TransactionEraOf, + genesis_hash: Hash, + nonce: Nonce, + tip: Balance, + ) -> Self { + Self::new( + ( + (), // non-zero sender + (), // spec version + (), // tx version + (), // genesis + era.frame_era(), // era + nonce.into(), // nonce (compact encoding) + (), // Check weight + tip.into(), // transaction payment / tip (compact encoding) + (), // bridge reject obsolete headers and msgs + ), + ( + (), + spec_version, + transaction_version, + genesis_hash, + era.signed_payload(genesis_hash), + (), + (), + (), + (), + ), + ) } -} - -/// Polkadot-like chain. -#[derive(RuntimeDebug)] -pub struct PolkadotLike; - -impl Chain for PolkadotLike { - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - type AccountId = AccountId; - type Balance = Balance; - type Index = Index; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) + /// Return signer nonce, used to craft transaction. + fn nonce(&self) -> Nonce { + self.payload.5.into() } - fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) + /// Return transaction tip. + fn tip(&self) -> Balance { + self.payload.7.into() } } diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 79f2b9fe03c..f8d698696de 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -9,6 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } hash-db = { version = "0.15.2", default-features = false } +impl-trait-for-tuples = "0.2.2" num-traits = { version = "0.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } diff --git a/primitives/runtime/src/extensions.rs b/primitives/runtime/src/extensions.rs new file mode 100644 index 00000000000..287f484db4a --- /dev/null +++ b/primitives/runtime/src/extensions.rs @@ -0,0 +1,136 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives that may be used for creating signed extensions for indirect runtimes. + +use crate::{BalanceOf, HashOf}; +use codec::{Compact, Decode, Encode}; +use impl_trait_for_tuples::impl_for_tuples; +use scale_info::{StaticTypeInfo, TypeInfo}; +use sp_runtime::{ + traits::{DispatchInfoOf, SignedExtension}, + transaction_validity::TransactionValidityError, +}; +use sp_std::{fmt::Debug, marker::PhantomData}; + +/// Trait that describes some properties of a `SignedExtension` that are needed in order to send a +/// transaction to the chain. +pub trait SignedExtensionSchema: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo { + /// A type of the data encoded as part of the transaction. + type Payload: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo; + /// Parameters which are part of the payload used to produce transaction signature, + /// but don't end up in the transaction itself (i.e. inherent part of the runtime). + type AdditionalSigned: Encode + Debug + Eq + Clone + StaticTypeInfo; +} + +// An implementation of `SignedExtensionSchema` using generic params. +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)] +pub struct GenericSignedExtensionSchema(PhantomData<(P, S)>); + +impl SignedExtensionSchema for GenericSignedExtensionSchema +where + P: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo, + S: Encode + Debug + Eq + Clone + StaticTypeInfo, +{ + type Payload = P; + type AdditionalSigned = S; +} + +/// The `SignedExtensionSchema` for `frame_system::CheckNonZeroSender`. +pub type CheckNonZeroSender = GenericSignedExtensionSchema<(), ()>; + +/// The `SignedExtensionSchema` for `frame_system::CheckSpecVersion`. +pub type CheckSpecVersion = GenericSignedExtensionSchema<(), u32>; + +/// The `SignedExtensionSchema` for `frame_system::CheckTxVersion`. +pub type CheckTxVersion = GenericSignedExtensionSchema<(), u32>; + +/// The `SignedExtensionSchema` for `frame_system::CheckGenesis`. +pub type CheckGenesis = GenericSignedExtensionSchema<(), HashOf>; + +/// The `SignedExtensionSchema` for `frame_system::CheckEra`. +pub type CheckEra = GenericSignedExtensionSchema>; + +/// The `SignedExtensionSchema` for `frame_system::CheckNonce`. +pub type CheckNonce = GenericSignedExtensionSchema, ()>; + +/// The `SignedExtensionSchema` for `frame_system::CheckWeight`. +pub type CheckWeight = GenericSignedExtensionSchema<(), ()>; + +/// The `SignedExtensionSchema` for `pallet_transaction_payment::ChargeTransactionPayment`. +pub type ChargeTransactionPayment = GenericSignedExtensionSchema>, ()>; + +/// The `SignedExtensionSchema` for `BridgeRejectObsoleteHeadersAndMessages`. +pub type BridgeRejectObsoleteHeadersAndMessages = GenericSignedExtensionSchema<(), ()>; + +#[impl_for_tuples(1, 12)] +impl SignedExtensionSchema for Tuple { + for_tuples!( type Payload = ( #( Tuple::Payload ),* ); ); + for_tuples!( type AdditionalSigned = ( #( Tuple::AdditionalSigned ),* ); ); +} + +/// A simplified version of signed extensions meant for producing signed transactions +/// and signed payloads in the client code. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub struct GenericSignedExtension { + pub payload: S::Payload, + #[codec(skip)] + // It may be set to `None` if extensions are decoded. We are never reconstructing transactions + // (and it makes no sense to do that) => decoded version of `SignedExtensions` is only used to + // read fields of the `payload`. And when resigning transaction, we're reconstructing + // `SignedExtensions` from the scratch. + additional_signed: Option, +} + +impl GenericSignedExtension { + pub fn new(payload: S::Payload, additional_signed: S::AdditionalSigned) -> Self { + Self { payload, additional_signed: Some(additional_signed) } + } +} + +impl SignedExtension for GenericSignedExtension +where + S: SignedExtensionSchema, + S::Payload: Send + Sync, + S::AdditionalSigned: Send + Sync, +{ + const IDENTIFIER: &'static str = "Not needed."; + type AccountId = (); + type Call = (); + type AdditionalSigned = S::AdditionalSigned; + type Pre = (); + + fn additional_signed(&self) -> Result { + // we shall not ever see this error in relay, because we are never signing decoded + // transactions. Instead we're constructing and signing new transactions. So the error code + // is kinda random here + self.additional_signed.clone().ok_or( + frame_support::unsigned::TransactionValidityError::Unknown( + frame_support::unsigned::UnknownTransaction::Custom(0xFF), + ), + ) + } + + fn pre_dispatch( + self, + _who: &Self::AccountId, + _call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + Ok(()) + } +} diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 340088a85b1..5eb1de2e3ac 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -47,6 +47,7 @@ pub use storage_types::BoundedStorageValue; #[cfg(feature = "std")] pub use storage_proof::craft_valid_storage_proof; +pub mod extensions; pub mod messages; mod chain; diff --git a/relays/client-bridge-hub-rococo/src/lib.rs b/relays/client-bridge-hub-rococo/src/lib.rs index 653c5af2c5e..8e6e9712925 100644 --- a/relays/client-bridge-hub-rococo/src/lib.rs +++ b/relays/client-bridge-hub-rococo/src/lib.rs @@ -16,6 +16,7 @@ //! Types used to connect to the BridgeHub-Rococo-Substrate parachain. +use bp_bridge_hub_wococo::PolkadotSignedExtension; use bp_messages::MessageNonce; use codec::Encode; use relay_substrate_client::{ @@ -65,7 +66,7 @@ impl ChainWithTransactions for BridgeHubRococo { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - bp_bridge_hub_rococo::SignedExtensions::new( + bp_bridge_hub_rococo::BridgeSignedExtension::from_params( param.spec_version, param.transaction_version, unsigned.era, diff --git a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs index 2c50128201b..1bb32a4089c 100644 --- a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs @@ -21,14 +21,14 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; +use bp_bridge_hub_rococo::BridgeSignedExtension; pub use bp_header_chain::BridgeGrandpaCallOf; pub use bp_parachains::BridgeParachainCall; pub use bridge_runtime_common::messages::BridgeMessagesCallOf; pub use relay_substrate_client::calls::SystemCall; -// TODO:check-parameter - check SignedExtension /// Unchecked BridgeHubRococo extrinsic. -pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic; +pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic; // The indirect pallet call used to sync `Wococo` GRANDPA finality to `BHRococo`. pub type BridgeWococoGrandpaCall = BridgeGrandpaCallOf; diff --git a/relays/client-bridge-hub-wococo/src/lib.rs b/relays/client-bridge-hub-wococo/src/lib.rs index 516f5d9fb80..3fd8187fa1f 100644 --- a/relays/client-bridge-hub-wococo/src/lib.rs +++ b/relays/client-bridge-hub-wococo/src/lib.rs @@ -16,6 +16,7 @@ //! Types used to connect to the BridgeHub-Wococo-Substrate parachain. +use bp_bridge_hub_wococo::PolkadotSignedExtension; use bp_messages::MessageNonce; use codec::Encode; use relay_substrate_client::{ @@ -65,7 +66,7 @@ impl ChainWithTransactions for BridgeHubWococo { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - bp_bridge_hub_wococo::SignedExtensions::new( + bp_bridge_hub_wococo::BridgeSignedExtension::from_params( param.spec_version, param.transaction_version, unsigned.era, diff --git a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs index 0d73a91029d..2158ad0e659 100644 --- a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs @@ -19,14 +19,14 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; +use bp_bridge_hub_wococo::BridgeSignedExtension; pub use bp_header_chain::BridgeGrandpaCallOf; pub use bp_parachains::BridgeParachainCall; pub use bridge_runtime_common::messages::BridgeMessagesCallOf; pub use relay_substrate_client::calls::SystemCall; -// TODO:check-parameter - check SignedExtension /// Unchecked BridgeHubWococo extrinsic. -pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic; +pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic; // The indirect pallet call used to sync `Rococo` GRANDPA finality to `BHWococo`. pub type BridgeRococoGrandpaCall = BridgeGrandpaCallOf; diff --git a/relays/client-rialto-parachain/src/lib.rs b/relays/client-rialto-parachain/src/lib.rs index c3fb0da7418..ebaac73e036 100644 --- a/relays/client-rialto-parachain/src/lib.rs +++ b/relays/client-rialto-parachain/src/lib.rs @@ -19,6 +19,7 @@ pub mod runtime_wrapper; use bp_messages::MessageNonce; +use bp_polkadot_core::{DefaultSignedExtension, PolkadotSignedExtension}; use codec::Encode; use relay_substrate_client::{ Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, Error as SubstrateError, @@ -75,7 +76,8 @@ impl ChainWithMessages for RialtoParachain { impl ChainWithTransactions for RialtoParachain { type AccountKeyPair = sp_core::sr25519::Pair; - type SignedTransaction = bp_polkadot_core::UncheckedExtrinsic; + type SignedTransaction = + bp_polkadot_core::UncheckedExtrinsic; fn sign_transaction( param: SignParam, @@ -83,7 +85,7 @@ impl ChainWithTransactions for RialtoParachain { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - bp_polkadot_core::SignedExtensions::new( + bp_polkadot_core::DefaultSignedExtension::from_params( param.spec_version, param.transaction_version, unsigned.era,