From 733e50152c48d3a2ffc0f4eeef54b75e81f734c6 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 11 Nov 2022 15:31:01 +0300 Subject: [PATCH] Reintroduce header chain trait (#1622) * reintroduce header chain trait * renive BridgedChainWithMessages::maximal_extrinsic_size --- .../bin/millau/runtime/src/rialto_messages.rs | 39 +- .../runtime/src/rialto_parachain_messages.rs | 46 +- .../runtime/src/millau_messages.rs | 37 +- .../bin/rialto/runtime/src/millau_messages.rs | 37 +- bridges/bin/runtime-common/Cargo.toml | 2 + bridges/bin/runtime-common/src/integrity.rs | 31 - bridges/bin/runtime-common/src/lib.rs | 2 + bridges/bin/runtime-common/src/messages.rs | 861 +++++++----------- .../src/messages_benchmarking.rs | 131 +-- .../runtime-common/src/messages_generation.rs | 154 ++++ .../src/parachains_benchmarking.rs | 4 +- bridges/modules/grandpa/src/lib.rs | 34 +- bridges/modules/parachains/Cargo.toml | 2 + bridges/modules/parachains/src/lib.rs | 41 +- .../chain-rialto-parachain/src/lib.rs | 6 +- bridges/primitives/header-chain/Cargo.toml | 2 + bridges/primitives/header-chain/src/lib.rs | 42 +- bridges/primitives/runtime/src/chain.rs | 6 + bridges/primitives/runtime/src/lib.rs | 2 +- 19 files changed, 648 insertions(+), 831 deletions(-) create mode 100644 bridges/bin/runtime-common/src/messages_generation.rs diff --git a/bridges/bin/millau/runtime/src/rialto_messages.rs b/bridges/bin/millau/runtime/src/rialto_messages.rs index cf2bd4badaf1e..c989de67b1763 100644 --- a/bridges/bin/millau/runtime/src/rialto_messages.rs +++ b/bridges/bin/millau/runtime/src/rialto_messages.rs @@ -16,14 +16,14 @@ //! Everything required to serve Millau <-> Rialto messages. -use crate::{OriginCaller, Runtime, RuntimeCall, RuntimeOrigin}; +use crate::{OriginCaller, RialtoGrandpaInstance, Runtime, RuntimeCall, RuntimeOrigin}; use bp_messages::{ source_chain::TargetHeaderChain, target_chain::{ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter, }; -use bp_runtime::{Chain, ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; +use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; use bridge_runtime_common::messages::{ self, BasicConfirmationTransactionEstimation, MessageBridge, MessageTransaction, }; @@ -98,6 +98,8 @@ impl MessageBridge for WithRialtoMessageBridge { type ThisChain = Millau; type BridgedChain = Rialto; + type BridgedHeaderChain = + pallet_bridge_grandpa::GrandpaChainHeaders; fn bridged_balance_to_this_balance( bridged_balance: bp_rialto::Balance, @@ -115,18 +117,14 @@ impl MessageBridge for WithRialtoMessageBridge { pub struct Millau; impl messages::ChainWithMessages for Millau { - type Hash = bp_millau::Hash; - type AccountId = bp_millau::AccountId; - type Signer = bp_millau::AccountSigner; - type Signature = bp_millau::Signature; - type Balance = bp_millau::Balance; + type Chain = bp_millau::Millau; } impl messages::ThisChainWithMessages for Millau { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< - Self::AccountId, + bp_millau::AccountId, { bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() }, { bp_rialto::EXTRA_STORAGE_PROOF_SIZE }, { bp_millau::TX_EXTRA_BYTES }, @@ -177,18 +175,10 @@ impl messages::ThisChainWithMessages for Millau { pub struct Rialto; impl messages::ChainWithMessages for Rialto { - type Hash = bp_rialto::Hash; - type AccountId = bp_rialto::AccountId; - type Signer = bp_rialto::AccountSigner; - type Signature = bp_rialto::Signature; - type Balance = bp_rialto::Balance; + type Chain = bp_rialto::Rialto; } impl messages::BridgedChainWithMessages for Rialto { - fn maximal_extrinsic_size() -> u32 { - bp_rialto::Rialto::max_extrinsic_size() - } - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { true } @@ -248,11 +238,7 @@ impl TargetHeaderChain for Rialto fn verify_messages_delivery_proof( proof: Self::MessagesDeliveryProof, ) -> Result<(LaneId, InboundLaneData), Self::Error> { - messages::source::verify_messages_delivery_proof::< - WithRialtoMessageBridge, - Runtime, - crate::RialtoGrandpaInstance, - >(proof) + messages::source::verify_messages_delivery_proof::(proof) } } @@ -269,11 +255,8 @@ impl SourceHeaderChain for Rialto { proof: Self::MessagesProof, messages_count: u32, ) -> Result>, Self::Error> { - messages::target::verify_messages_proof::< - WithRialtoMessageBridge, - Runtime, - crate::RialtoGrandpaInstance, - >(proof, messages_count) + messages::target::verify_messages_proof::(proof, messages_count) + .map_err(Into::into) } } @@ -296,7 +279,7 @@ impl MessagesParameter for MillauToRialtoMessagesParameter { #[cfg(test)] mod tests { use super::*; - use crate::{DbWeight, RialtoGrandpaInstance, Runtime, WithRialtoMessagesInstance}; + use crate::{DbWeight, Runtime, WithRialtoMessagesInstance}; use bp_runtime::Chain; use bridge_runtime_common::{ diff --git a/bridges/bin/millau/runtime/src/rialto_parachain_messages.rs b/bridges/bin/millau/runtime/src/rialto_parachain_messages.rs index cce7aa79bad25..70e8db89d3a27 100644 --- a/bridges/bin/millau/runtime/src/rialto_parachain_messages.rs +++ b/bridges/bin/millau/runtime/src/rialto_parachain_messages.rs @@ -16,15 +16,14 @@ //! Everything required to serve Millau <-> RialtoParachain messages. -use crate::{Runtime, RuntimeCall, RuntimeOrigin}; +use crate::{Runtime, RuntimeCall, RuntimeOrigin, WithRialtoParachainsInstance}; use bp_messages::{ source_chain::TargetHeaderChain, target_chain::{ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter, }; -use bp_polkadot_core::parachains::ParaId; -use bp_runtime::{Chain, ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID}; +use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID}; use bridge_runtime_common::messages::{ self, BasicConfirmationTransactionEstimation, MessageBridge, MessageTransaction, }; @@ -103,6 +102,11 @@ impl MessageBridge for WithRialtoParachainMessageBridge { type ThisChain = Millau; type BridgedChain = RialtoParachain; + type BridgedHeaderChain = pallet_bridge_parachains::ParachainHeaders< + Runtime, + WithRialtoParachainsInstance, + bp_rialto_parachain::RialtoParachain, + >; fn bridged_balance_to_this_balance( bridged_balance: bp_rialto_parachain::Balance, @@ -120,18 +124,14 @@ impl MessageBridge for WithRialtoParachainMessageBridge { pub struct Millau; impl messages::ChainWithMessages for Millau { - type Hash = bp_millau::Hash; - type AccountId = bp_millau::AccountId; - type Signer = bp_millau::AccountSigner; - type Signature = bp_millau::Signature; - type Balance = bp_millau::Balance; + type Chain = bp_millau::Millau; } impl messages::ThisChainWithMessages for Millau { type RuntimeCall = RuntimeCall; type RuntimeOrigin = RuntimeOrigin; type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< - Self::AccountId, + bp_rialto_parachain::AccountId, { bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() }, { bp_rialto_parachain::EXTRA_STORAGE_PROOF_SIZE }, { bp_millau::TX_EXTRA_BYTES }, @@ -166,18 +166,10 @@ impl messages::ThisChainWithMessages for Millau { pub struct RialtoParachain; impl messages::ChainWithMessages for RialtoParachain { - type Hash = bp_rialto_parachain::Hash; - type AccountId = bp_rialto_parachain::AccountId; - type Signer = bp_rialto_parachain::AccountSigner; - type Signature = bp_rialto_parachain::Signature; - type Balance = bp_rialto_parachain::Balance; + type Chain = bp_rialto_parachain::RialtoParachain; } impl messages::BridgedChainWithMessages for RialtoParachain { - fn maximal_extrinsic_size() -> u32 { - bp_rialto_parachain::RialtoParachain::max_extrinsic_size() - } - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { true } @@ -241,12 +233,7 @@ impl TargetHeaderChain fo fn verify_messages_delivery_proof( proof: Self::MessagesDeliveryProof, ) -> Result<(LaneId, InboundLaneData), Self::Error> { - messages::source::verify_messages_delivery_proof_from_parachain::< - WithRialtoParachainMessageBridge, - bp_rialto_parachain::Header, - Runtime, - crate::WithRialtoParachainsInstance, - >(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof) + messages::source::verify_messages_delivery_proof::(proof) } } @@ -263,12 +250,11 @@ impl SourceHeaderChain for RialtoParachain { proof: Self::MessagesProof, messages_count: u32, ) -> Result>, Self::Error> { - messages::target::verify_messages_proof_from_parachain::< - WithRialtoParachainMessageBridge, - bp_rialto_parachain::Header, - Runtime, - crate::WithRialtoParachainsInstance, - >(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof, messages_count) + messages::target::verify_messages_proof::( + proof, + messages_count, + ) + .map_err(Into::into) } } diff --git a/bridges/bin/rialto-parachain/runtime/src/millau_messages.rs b/bridges/bin/rialto-parachain/runtime/src/millau_messages.rs index 4688f89a7087a..a13a17a1ea281 100644 --- a/bridges/bin/rialto-parachain/runtime/src/millau_messages.rs +++ b/bridges/bin/rialto-parachain/runtime/src/millau_messages.rs @@ -19,14 +19,14 @@ // TODO: this is almost exact copy of `millau_messages.rs` from Rialto runtime. // Should be extracted to a separate crate and reused here. -use crate::{OriginCaller, Runtime, RuntimeCall, RuntimeOrigin}; +use crate::{MillauGrandpaInstance, OriginCaller, Runtime, RuntimeCall, RuntimeOrigin}; use bp_messages::{ source_chain::TargetHeaderChain, target_chain::{ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter, }; -use bp_runtime::{Chain, ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID}; +use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID}; use bridge_runtime_common::messages::{ self, BasicConfirmationTransactionEstimation, MessageBridge, MessageTransaction, }; @@ -102,6 +102,8 @@ impl MessageBridge for WithMillauMessageBridge { type ThisChain = RialtoParachain; type BridgedChain = Millau; + type BridgedHeaderChain = + pallet_bridge_grandpa::GrandpaChainHeaders; fn bridged_balance_to_this_balance( bridged_balance: bp_millau::Balance, @@ -119,18 +121,14 @@ impl MessageBridge for WithMillauMessageBridge { pub struct RialtoParachain; impl messages::ChainWithMessages for RialtoParachain { - type Hash = bp_rialto_parachain::Hash; - type AccountId = bp_rialto_parachain::AccountId; - type Signer = bp_rialto_parachain::AccountSigner; - type Signature = bp_rialto_parachain::Signature; - type Balance = bp_rialto_parachain::Balance; + type Chain = bp_rialto_parachain::RialtoParachain; } impl messages::ThisChainWithMessages for RialtoParachain { type RuntimeCall = RuntimeCall; type RuntimeOrigin = RuntimeOrigin; type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< - Self::AccountId, + bp_rialto_parachain::AccountId, { bp_rialto_parachain::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() }, { bp_millau::EXTRA_STORAGE_PROOF_SIZE }, { bp_rialto_parachain::TX_EXTRA_BYTES }, @@ -184,18 +182,10 @@ impl messages::ThisChainWithMessages for RialtoParachain { pub struct Millau; impl messages::ChainWithMessages for Millau { - type Hash = bp_millau::Hash; - type AccountId = bp_millau::AccountId; - type Signer = bp_millau::AccountSigner; - type Signature = bp_millau::Signature; - type Balance = bp_millau::Balance; + type Chain = bp_millau::Millau; } impl messages::BridgedChainWithMessages for Millau { - fn maximal_extrinsic_size() -> u32 { - bp_millau::Millau::max_extrinsic_size() - } - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { true } @@ -255,11 +245,7 @@ impl TargetHeaderChain f fn verify_messages_delivery_proof( proof: Self::MessagesDeliveryProof, ) -> Result<(LaneId, InboundLaneData), Self::Error> { - messages::source::verify_messages_delivery_proof::< - WithMillauMessageBridge, - Runtime, - crate::MillauGrandpaInstance, - >(proof) + messages::source::verify_messages_delivery_proof::(proof) } } @@ -276,11 +262,8 @@ impl SourceHeaderChain for Millau { proof: Self::MessagesProof, messages_count: u32, ) -> Result>, Self::Error> { - messages::target::verify_messages_proof::< - WithMillauMessageBridge, - Runtime, - crate::MillauGrandpaInstance, - >(proof, messages_count) + messages::target::verify_messages_proof::(proof, messages_count) + .map_err(Into::into) } } diff --git a/bridges/bin/rialto/runtime/src/millau_messages.rs b/bridges/bin/rialto/runtime/src/millau_messages.rs index 43c95744165f3..b27cce40074aa 100644 --- a/bridges/bin/rialto/runtime/src/millau_messages.rs +++ b/bridges/bin/rialto/runtime/src/millau_messages.rs @@ -16,14 +16,14 @@ //! Everything required to serve Millau <-> Rialto messages. -use crate::{OriginCaller, Runtime, RuntimeCall, RuntimeOrigin}; +use crate::{MillauGrandpaInstance, OriginCaller, Runtime, RuntimeCall, RuntimeOrigin}; use bp_messages::{ source_chain::TargetHeaderChain, target_chain::{ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter, }; -use bp_runtime::{Chain, ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; +use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; use bridge_runtime_common::messages::{ self, BasicConfirmationTransactionEstimation, MessageBridge, MessageTransaction, }; @@ -96,6 +96,8 @@ impl MessageBridge for WithMillauMessageBridge { type ThisChain = Rialto; type BridgedChain = Millau; + type BridgedHeaderChain = + pallet_bridge_grandpa::GrandpaChainHeaders; fn bridged_balance_to_this_balance( bridged_balance: bp_millau::Balance, @@ -113,18 +115,14 @@ impl MessageBridge for WithMillauMessageBridge { pub struct Rialto; impl messages::ChainWithMessages for Rialto { - type Hash = bp_rialto::Hash; - type AccountId = bp_rialto::AccountId; - type Signer = bp_rialto::AccountSigner; - type Signature = bp_rialto::Signature; - type Balance = bp_rialto::Balance; + type Chain = bp_rialto::Rialto; } impl messages::ThisChainWithMessages for Rialto { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< - Self::AccountId, + bp_rialto::AccountId, { bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() }, { bp_millau::EXTRA_STORAGE_PROOF_SIZE }, { bp_rialto::TX_EXTRA_BYTES }, @@ -175,18 +173,10 @@ impl messages::ThisChainWithMessages for Rialto { pub struct Millau; impl messages::ChainWithMessages for Millau { - type Hash = bp_millau::Hash; - type AccountId = bp_millau::AccountId; - type Signer = bp_millau::AccountSigner; - type Signature = bp_millau::Signature; - type Balance = bp_millau::Balance; + type Chain = bp_millau::Millau; } impl messages::BridgedChainWithMessages for Millau { - fn maximal_extrinsic_size() -> u32 { - bp_millau::Millau::max_extrinsic_size() - } - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { true } @@ -246,11 +236,7 @@ impl TargetHeaderChain for Millau fn verify_messages_delivery_proof( proof: Self::MessagesDeliveryProof, ) -> Result<(LaneId, InboundLaneData), Self::Error> { - messages::source::verify_messages_delivery_proof::< - WithMillauMessageBridge, - Runtime, - crate::MillauGrandpaInstance, - >(proof) + messages::source::verify_messages_delivery_proof::(proof) } } @@ -267,11 +253,8 @@ impl SourceHeaderChain for Millau { proof: Self::MessagesProof, messages_count: u32, ) -> Result>, Self::Error> { - messages::target::verify_messages_proof::< - WithMillauMessageBridge, - Runtime, - crate::MillauGrandpaInstance, - >(proof, messages_count) + messages::target::verify_messages_proof::(proof, messages_count) + .map_err(Into::into) } } diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index 2fb64a5b73325..4f8f7e34c4042 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -15,6 +15,7 @@ static_assertions = { version = "1.1", optional = true } # Bridge dependencies +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } bp-messages = { path = "../../primitives/messages", default-features = false } bp-parachains = { path = "../../primitives/parachains", default-features = false } bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } @@ -47,6 +48,7 @@ millau-runtime = { path = "../millau/runtime" } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-messages/std", "bp-parachains/std", "bp-polkadot-core/std", diff --git a/bridges/bin/runtime-common/src/integrity.rs b/bridges/bin/runtime-common/src/integrity.rs index 90f048cb9cc30..3b64d1e8afea5 100644 --- a/bridges/bin/runtime-common/src/integrity.rs +++ b/bridges/bin/runtime-common/src/integrity.rs @@ -49,36 +49,6 @@ macro_rules! assert_chain_types( } ); -/// Macro that ensures that the bridge configuration and chain primitives crates are sharing -/// the same types (hash, account id, ...). -#[macro_export] -macro_rules! assert_bridge_types( - ( bridge: $bridge:path, this_chain: $this:path, bridged_chain: $bridged:path ) => { - { - // if one of this asserts fail, then all chains, bridged with this chain and bridge relays are now broken - // - // `frame_support::weights::Weight` is used here directly, because all chains we know are using this - // primitive (may be changed in the future) - use $crate::messages::{ - AccountIdOf, BalanceOf, BridgedChain, HashOf, SignatureOf, SignerOf, ThisChain, - }; - use static_assertions::assert_type_eq_all; - - assert_type_eq_all!(HashOf>, bp_runtime::HashOf<$this>); - assert_type_eq_all!(AccountIdOf>, bp_runtime::AccountIdOf<$this>); - assert_type_eq_all!(SignerOf>, bp_runtime::AccountPublicOf<$this>); - assert_type_eq_all!(SignatureOf>, bp_runtime::SignatureOf<$this>); - assert_type_eq_all!(BalanceOf>, bp_runtime::BalanceOf<$this>); - - assert_type_eq_all!(HashOf>, bp_runtime::HashOf<$bridged>); - assert_type_eq_all!(AccountIdOf>, bp_runtime::AccountIdOf<$bridged>); - assert_type_eq_all!(SignerOf>, bp_runtime::AccountPublicOf<$bridged>); - assert_type_eq_all!(SignatureOf>, bp_runtime::SignatureOf<$bridged>); - assert_type_eq_all!(BalanceOf>, bp_runtime::BalanceOf<$bridged>); - } - } -); - /// Macro that ensures that the bridge GRANDPA pallet is configured properly to bridge with given /// chain. #[macro_export] @@ -145,7 +115,6 @@ macro_rules! assert_complete_bridge_types( bridged_chain: $bridged:path, ) => { $crate::assert_chain_types!(runtime: $r, this_chain: $this); - $crate::assert_bridge_types!(bridge: $bridge, this_chain: $this, bridged_chain: $bridged); $crate::assert_bridge_grandpa_pallet_types!( runtime: $r, with_bridged_chain_grandpa_instance: $gi, diff --git a/bridges/bin/runtime-common/src/lib.rs b/bridges/bin/runtime-common/src/lib.rs index e56b887885aa7..ca8f2268404e5 100644 --- a/bridges/bin/runtime-common/src/lib.rs +++ b/bridges/bin/runtime-common/src/lib.rs @@ -28,6 +28,8 @@ pub mod messages_benchmarking; pub mod messages_extension; pub mod parachains_benchmarking; +mod messages_generation; + #[cfg(feature = "integrity-test")] pub mod integrity; diff --git a/bridges/bin/runtime-common/src/messages.rs b/bridges/bin/runtime-common/src/messages.rs index 3744d7153eef8..75c3ec6e57f30 100644 --- a/bridges/bin/runtime-common/src/messages.rs +++ b/bridges/bin/runtime-common/src/messages.rs @@ -20,22 +20,22 @@ //! pallet is used to dispatch incoming messages. Message identified by a tuple //! of to elements - message lane id and message nonce. +use bp_header_chain::{HeaderChain, HeaderChainError}; use bp_messages::{ source_chain::LaneMessageVerifier, target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages}, InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData, }; -use bp_polkadot_core::parachains::{ParaHash, ParaHasher, ParaId}; -use bp_runtime::{messages::MessageDispatchResult, ChainId, Size, StorageProofChecker}; +use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Size, StorageProofChecker}; use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; use frame_support::{traits::Get, weights::Weight, RuntimeDebug}; use hash_db::Hasher; use scale_info::TypeInfo; use sp_runtime::{ - traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul, Header as HeaderT}, + traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul}, FixedPointNumber, FixedPointOperand, FixedU128, }; -use sp_std::{cmp::PartialOrd, convert::TryFrom, fmt::Debug, marker::PhantomData, vec::Vec}; +use sp_std::{convert::TryFrom, fmt::Debug, marker::PhantomData, vec::Vec}; use sp_trie::StorageProof; use xcm::latest::prelude::*; @@ -57,6 +57,8 @@ pub trait MessageBridge { type ThisChain: ThisChainWithMessages; /// Bridged chain in context of message bridge. type BridgedChain: BridgedChainWithMessages; + /// Bridged header chain. + type BridgedHeaderChain: HeaderChain<::Chain>; /// Convert Bridged chain balance into This chain balance. fn bridged_balance_to_this_balance( @@ -65,27 +67,6 @@ pub trait MessageBridge { ) -> BalanceOf>; } -/// Chain that has `pallet-bridge-messages` and `dispatch` modules. -pub trait ChainWithMessages { - /// Hash used in the chain. - type Hash: Decode; - /// Accound id on the chain. - type AccountId: Encode + Decode + MaxEncodedLen; - /// Public key of the chain account that may be used to verify signatures. - type Signer: Encode + Decode; - /// Signature type used on the chain. - type Signature: Encode + Decode; - /// Type of balances that is used on the chain. - type Balance: Encode - + Decode - + CheckedAdd - + CheckedDiv - + CheckedMul - + PartialOrd - + From - + Copy; -} - /// Message related transaction parameters estimation. #[derive(RuntimeDebug)] pub struct MessageTransaction { @@ -134,7 +115,13 @@ impl< } } -/// This chain that has `pallet-bridge-messages` and `dispatch` modules. +/// Chain that has `pallet-bridge-messages` module. +pub trait ChainWithMessages { + /// Underlying chain type. + type Chain: Chain; +} + +/// This chain that has `pallet-bridge-messages` module. pub trait ThisChainWithMessages: ChainWithMessages { /// Call origin on the chain. type RuntimeOrigin; @@ -161,11 +148,8 @@ pub trait ThisChainWithMessages: ChainWithMessages { fn transaction_payment(transaction: MessageTransaction) -> BalanceOf; } -/// Bridged chain that has `pallet-bridge-messages` and `dispatch` modules. +/// Bridged chain that has `pallet-bridge-messages` module. pub trait BridgedChainWithMessages: ChainWithMessages { - /// Maximal extrinsic size at Bridged chain. - fn maximal_extrinsic_size() -> u32; - /// Returns `true` if message dispatch weight is withing expected limits. `false` means /// that the message is too heavy to be sent over the bridge and shall be rejected. fn verify_dispatch_weight(message_payload: &[u8]) -> bool; @@ -186,16 +170,16 @@ pub trait BridgedChainWithMessages: ChainWithMessages { pub type ThisChain = ::ThisChain; /// Bridged chain in context of message bridge. pub type BridgedChain = ::BridgedChain; +/// Underlying chain type. +pub type UnderlyingChainOf = ::Chain; /// Hash used on the chain. -pub type HashOf = ::Hash; +pub type HashOf = bp_runtime::HashOf<::Chain>; +/// Hasher used on the chain. +pub type HasherOf = bp_runtime::HasherOf<::Chain>; /// Account id used on the chain. -pub type AccountIdOf = ::AccountId; -/// Public key of the chain account that may be used to verify signature. -pub type SignerOf = ::Signer; -/// Signature type used on the chain. -pub type SignatureOf = ::Signature; +pub type AccountIdOf = bp_runtime::AccountIdOf<::Chain>; /// Type of balances that is used on the chain. -pub type BalanceOf = ::Balance; +pub type BalanceOf = bp_runtime::BalanceOf<::Chain>; /// Type of origin that is used on the chain. pub type OriginOf = ::RuntimeOrigin; /// Type of call that is used on this chain. @@ -358,7 +342,9 @@ pub mod source { /// Return maximal message size of This -> Bridged chain message. pub fn maximal_message_size() -> u32 { - super::target::maximal_incoming_message_size(BridgedChain::::maximal_extrinsic_size()) + super::target::maximal_incoming_message_size( + UnderlyingChainOf::>::max_extrinsic_size(), + ) } /// Do basic Bridged-chain specific verification of This -> Bridged chain message. @@ -440,87 +426,35 @@ pub mod source { /// /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged /// parachains, please use the `verify_messages_delivery_proof_from_parachain`. - pub fn verify_messages_delivery_proof( - proof: FromBridgedChainMessagesDeliveryProof>>, - ) -> Result, &'static str> - where - ThisRuntime: pallet_bridge_grandpa::Config, - HashOf>: Into< - bp_runtime::HashOf< - >::BridgedChain, - >, - >, - { - let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } = - proof; - pallet_bridge_grandpa::Pallet::::parse_finalized_storage_proof( - bridged_header_hash.into(), - StorageProof::new(storage_proof), - |storage| do_verify_messages_delivery_proof::< - B, - bp_runtime::HasherOf< - >::BridgedChain, - >, - >(lane, storage), - ) - .map_err(<&'static str>::from)? - } - - /// Verify proof of This -> Bridged chain messages delivery. - /// - /// This function is used when Bridged chain is using parachain finality. For Bridged - /// chains with direct GRANDPA finality, please use the `verify_messages_delivery_proof`. - /// - /// This function currently only supports parachains, which are using header type that - /// implements `sp_runtime::traits::Header` trait. - pub fn verify_messages_delivery_proof_from_parachain< - B, - BridgedHeader, - ThisRuntime, - ParachainsInstance: 'static, - >( - bridged_parachain: ParaId, + pub fn verify_messages_delivery_proof( proof: FromBridgedChainMessagesDeliveryProof>>, - ) -> Result, &'static str> - where - B: MessageBridge, - B::BridgedChain: ChainWithMessages, - BridgedHeader: HeaderT>>, - ThisRuntime: pallet_bridge_parachains::Config, - { + ) -> Result, &'static str> { let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } = proof; - pallet_bridge_parachains::Pallet::::parse_finalized_storage_proof( - bridged_parachain, + B::BridgedHeaderChain::parse_finalized_storage_proof( bridged_header_hash, StorageProof::new(storage_proof), - |para_head| BridgedHeader::decode(&mut ¶_head.0[..]).ok().map(|h| *h.state_root()), - |storage| do_verify_messages_delivery_proof::(lane, storage), + |storage| { + // Messages delivery proof is just proof of single storage key read => any error + // is fatal. + let storage_inbound_lane_data_key = + bp_messages::storage_keys::inbound_lane_data_key( + B::BRIDGED_MESSAGES_PALLET_NAME, + &lane, + ); + let raw_inbound_lane_data = storage + .read_value(storage_inbound_lane_data_key.0.as_ref()) + .map_err(|_| "Failed to read inbound lane state from storage proof")? + .ok_or("Inbound lane state is missing from the messages proof")?; + let inbound_lane_data = InboundLaneData::decode(&mut &raw_inbound_lane_data[..]) + .map_err(|_| "Failed to decode inbound lane state from the proof")?; + + Ok((lane, inbound_lane_data)) + }, ) .map_err(<&'static str>::from)? } - /// The essense of This -> Bridged chain messages delivery proof verification. - fn do_verify_messages_delivery_proof( - lane: LaneId, - storage: bp_runtime::StorageProofChecker, - ) -> Result, &'static str> { - // Messages delivery proof is just proof of single storage key read => any error - // is fatal. - let storage_inbound_lane_data_key = bp_messages::storage_keys::inbound_lane_data_key( - B::BRIDGED_MESSAGES_PALLET_NAME, - &lane, - ); - let raw_inbound_lane_data = storage - .read_value(storage_inbound_lane_data_key.0.as_ref()) - .map_err(|_| "Failed to read inbound lane state from storage proof")? - .ok_or("Inbound lane state is missing from the messages proof")?; - let inbound_lane_data = InboundLaneData::decode(&mut &raw_inbound_lane_data[..]) - .map_err(|_| "Failed to decode inbound lane state from the proof")?; - - Ok((lane, inbound_lane_data)) - } - /// XCM bridge. pub trait XcmBridge { /// Runtime message bridge configuration. @@ -801,98 +735,106 @@ pub mod target { /// The `messages_count` argument verification (sane limits) is supposed to be made /// outside of this function. This function only verifies that the proof declares exactly /// `messages_count` messages. - pub fn verify_messages_proof( + pub fn verify_messages_proof( proof: FromBridgedChainMessagesProof>>, messages_count: u32, - ) -> Result>>>, &'static str> - where - ThisRuntime: pallet_bridge_grandpa::Config, - HashOf>: Into< - bp_runtime::HashOf< - >::BridgedChain, - >, - >, - { - verify_messages_proof_with_parser::( - proof, - messages_count, - |bridged_header_hash, bridged_storage_proof| { - pallet_bridge_grandpa::Pallet::::parse_finalized_storage_proof( - bridged_header_hash.into(), - StorageProof::new(bridged_storage_proof), - |storage_adapter| storage_adapter, - ) - .map(|storage| StorageProofCheckerAdapter::<_, B> { - storage, - _dummy: Default::default(), - }) - .map_err(|err| MessageProofError::Custom(err.into())) - }, - ) - .map_err(Into::into) - } + ) -> Result>>>, MessageProofError> { + let FromBridgedChainMessagesProof { + bridged_header_hash, + storage_proof, + lane, + nonces_start, + nonces_end, + } = proof; - /// Verify proof of Bridged -> This chain messages. - /// - /// This function is used when Bridged chain is using parachain finality. For Bridged - /// chains with direct GRANDPA finality, please use the `verify_messages_proof`. - /// - /// The `messages_count` argument verification (sane limits) is supposed to be made - /// outside of this function. This function only verifies that the proof declares exactly - /// `messages_count` messages. - /// - /// This function currently only supports parachains, which are using header type that - /// implements `sp_runtime::traits::Header` trait. - pub fn verify_messages_proof_from_parachain< - B, - BridgedHeader, - ThisRuntime, - ParachainsInstance: 'static, - >( - bridged_parachain: ParaId, - proof: FromBridgedChainMessagesProof>>, - messages_count: u32, - ) -> Result>>>, &'static str> - where - B: MessageBridge, - B::BridgedChain: ChainWithMessages, - BridgedHeader: HeaderT>>, - ThisRuntime: pallet_bridge_parachains::Config, - { - verify_messages_proof_with_parser::( - proof, - messages_count, - |bridged_header_hash, bridged_storage_proof| { - pallet_bridge_parachains::Pallet::::parse_finalized_storage_proof( - bridged_parachain, - bridged_header_hash, - StorageProof::new(bridged_storage_proof), - |para_head| BridgedHeader::decode(&mut ¶_head.0[..]).ok().map(|h| *h.state_root()), - |storage_adapter| storage_adapter, - ) - .map(|storage| StorageProofCheckerAdapter::<_, B> { - storage, - _dummy: Default::default(), - }) - .map_err(|err| MessageProofError::Custom(err.into())) + B::BridgedHeaderChain::parse_finalized_storage_proof( + bridged_header_hash, + StorageProof::new(storage_proof), + |storage| { + let parser = + StorageProofCheckerAdapter::<_, B> { storage, _dummy: Default::default() }; + + // receiving proofs where end < begin is ok (if proof includes outbound lane state) + let messages_in_the_proof = + if let Some(nonces_difference) = nonces_end.checked_sub(nonces_start) { + // let's check that the user (relayer) has passed correct `messages_count` + // (this bounds maximal capacity of messages vec below) + let messages_in_the_proof = nonces_difference.saturating_add(1); + if messages_in_the_proof != MessageNonce::from(messages_count) { + return Err(MessageProofError::MessagesCountMismatch) + } + + messages_in_the_proof + } else { + 0 + }; + + // Read messages first. All messages that are claimed to be in the proof must + // be in the proof. So any error in `read_value`, or even missing value is fatal. + // + // Mind that we allow proofs with no messages if outbound lane state is proved. + let mut messages = Vec::with_capacity(messages_in_the_proof as _); + for nonce in nonces_start..=nonces_end { + let message_key = MessageKey { lane_id: lane, nonce }; + let raw_message_data = parser + .read_raw_message(&message_key) + .ok_or(MessageProofError::MissingRequiredMessage)?; + let message_data = MessageData::>>::decode( + &mut &raw_message_data[..], + ) + .map_err(|_| MessageProofError::FailedToDecodeMessage)?; + messages.push(Message { key: message_key, data: message_data }); + } + + // Now let's check if proof contains outbound lane state proof. It is optional, so + // we simply ignore `read_value` errors and missing value. + let mut proved_lane_messages = ProvedLaneMessages { lane_state: None, messages }; + let raw_outbound_lane_data = parser.read_raw_outbound_lane_data(&lane); + if let Some(raw_outbound_lane_data) = raw_outbound_lane_data { + proved_lane_messages.lane_state = Some( + OutboundLaneData::decode(&mut &raw_outbound_lane_data[..]) + .map_err(|_| MessageProofError::FailedToDecodeOutboundLaneState)?, + ); + } + + // Now we may actually check if the proof is empty or not. + if proved_lane_messages.lane_state.is_none() && + proved_lane_messages.messages.is_empty() + { + return Err(MessageProofError::Empty) + } + + // We only support single lane messages in this generated_schema + let mut proved_messages = ProvedMessages::new(); + proved_messages.insert(lane, proved_lane_messages); + + Ok(proved_messages) }, ) - .map_err(Into::into) + .map_err(MessageProofError::HeaderChain)? } + /// Error that happens during message proof verification. #[derive(Debug, PartialEq, Eq)] - pub(crate) enum MessageProofError { + pub enum MessageProofError { + /// Error returned by the bridged header chain. + HeaderChain(HeaderChainError), + /// The message proof is empty. Empty, + /// Declared messages count doesn't match actual value. MessagesCountMismatch, + /// Message is missing from the proof. MissingRequiredMessage, + /// Failed to decode message from the proof. FailedToDecodeMessage, + /// Failed to decode outbound lane data from the proof. FailedToDecodeOutboundLaneState, - Custom(&'static str), } impl From for &'static str { fn from(err: MessageProofError) -> &'static str { match err { + MessageProofError::HeaderChain(err) => err.into(), MessageProofError::Empty => "Messages proof is empty", MessageProofError::MessagesCountMismatch => "Declared messages count doesn't match actual value", @@ -901,26 +843,16 @@ pub mod target { "Failed to decode message from the proof", MessageProofError::FailedToDecodeOutboundLaneState => "Failed to decode outbound lane data from the proof", - MessageProofError::Custom(err) => err, } } } - pub(crate) trait MessageProofParser { - fn read_raw_outbound_lane_data(&self, lane_id: &LaneId) -> Option>; - fn read_raw_message(&self, message_key: &MessageKey) -> Option>; - } - struct StorageProofCheckerAdapter { storage: StorageProofChecker, _dummy: sp_std::marker::PhantomData, } - impl MessageProofParser for StorageProofCheckerAdapter - where - H: Hasher, - B: MessageBridge, - { + impl StorageProofCheckerAdapter { fn read_raw_outbound_lane_data(&self, lane_id: &LaneId) -> Option> { let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key( B::BRIDGED_MESSAGES_PALLET_NAME, @@ -938,89 +870,20 @@ pub mod target { self.storage.read_value(storage_message_key.0.as_ref()).ok()? } } - - /// Verify proof of Bridged -> This chain messages using given message proof parser. - pub(crate) fn verify_messages_proof_with_parser( - proof: FromBridgedChainMessagesProof>>, - messages_count: u32, - build_parser: BuildParser, - ) -> Result>>>, MessageProofError> - where - BuildParser: - FnOnce(HashOf>, RawStorageProof) -> Result, - Parser: MessageProofParser, - { - let FromBridgedChainMessagesProof { - bridged_header_hash, - storage_proof, - lane, - nonces_start, - nonces_end, - } = proof; - - // receiving proofs where end < begin is ok (if proof includes outbound lane state) - let messages_in_the_proof = - if let Some(nonces_difference) = nonces_end.checked_sub(nonces_start) { - // let's check that the user (relayer) has passed correct `messages_count` - // (this bounds maximal capacity of messages vec below) - let messages_in_the_proof = nonces_difference.saturating_add(1); - if messages_in_the_proof != MessageNonce::from(messages_count) { - return Err(MessageProofError::MessagesCountMismatch) - } - - messages_in_the_proof - } else { - 0 - }; - - let parser = build_parser(bridged_header_hash, storage_proof)?; - - // Read messages first. All messages that are claimed to be in the proof must - // be in the proof. So any error in `read_value`, or even missing value is fatal. - // - // Mind that we allow proofs with no messages if outbound lane state is proved. - let mut messages = Vec::with_capacity(messages_in_the_proof as _); - for nonce in nonces_start..=nonces_end { - let message_key = MessageKey { lane_id: lane, nonce }; - let raw_message_data = parser - .read_raw_message(&message_key) - .ok_or(MessageProofError::MissingRequiredMessage)?; - let message_data = - MessageData::>>::decode(&mut &raw_message_data[..]) - .map_err(|_| MessageProofError::FailedToDecodeMessage)?; - messages.push(Message { key: message_key, data: message_data }); - } - - // Now let's check if proof contains outbound lane state proof. It is optional, so we - // simply ignore `read_value` errors and missing value. - let mut proved_lane_messages = ProvedLaneMessages { lane_state: None, messages }; - let raw_outbound_lane_data = parser.read_raw_outbound_lane_data(&lane); - if let Some(raw_outbound_lane_data) = raw_outbound_lane_data { - proved_lane_messages.lane_state = Some( - OutboundLaneData::decode(&mut &raw_outbound_lane_data[..]) - .map_err(|_| MessageProofError::FailedToDecodeOutboundLaneState)?, - ); - } - - // Now we may actually check if the proof is empty or not. - if proved_lane_messages.lane_state.is_none() && proved_lane_messages.messages.is_empty() { - return Err(MessageProofError::Empty) - } - - // We only support single lane messages in this generated_schema - let mut proved_messages = ProvedMessages::new(); - proved_messages.insert(lane, proved_lane_messages); - - Ok(proved_messages) - } } #[cfg(test)] mod tests { use super::*; + use crate::messages_generation::{ + encode_all_messages, encode_lane_data, prepare_messages_storage_proof, + }; + use bp_runtime::HeaderOf; use codec::{Decode, Encode}; use frame_support::weights::Weight; - use std::ops::RangeInclusive; + use sp_core::H256; + use sp_runtime::traits::{BlakeTwo256, Header as _}; + use std::cell::RefCell; const DELIVERY_TRANSACTION_WEIGHT: Weight = Weight::from_ref_time(100); const DELIVERY_CONFIRMATION_TRANSACTION_WEIGHT: u64 = 100; @@ -1032,7 +895,7 @@ mod tests { const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; /// Bridge that is deployed on ThisChain and allows sending/receiving messages to/from - /// BridgedChain; + /// BridgedChain. #[derive(Debug, PartialEq, Eq)] struct OnThisChainBridge; @@ -1044,6 +907,7 @@ mod tests { type ThisChain = ThisChain; type BridgedChain = BridgedChain; + type BridgedHeaderChain = BridgedHeaderChain; fn bridged_balance_to_this_balance( bridged_balance: BridgedChainBalance, @@ -1052,7 +916,7 @@ mod tests { let conversion_rate = bridged_to_this_conversion_rate_override .map(|r| r.to_float() as u32) .unwrap_or(BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE); - ThisChainBalance(bridged_balance.0 * conversion_rate) + bridged_balance as ThisChainBalance * conversion_rate as ThisChainBalance } } @@ -1069,6 +933,7 @@ mod tests { type ThisChain = BridgedChain; type BridgedChain = ThisChain; + type BridgedHeaderChain = ThisHeaderChain; fn bridged_balance_to_this_balance( _this_balance: ThisChainBalance, @@ -1078,19 +943,6 @@ mod tests { } } - #[derive(Debug, PartialEq, Eq, Decode, Encode, Clone, MaxEncodedLen)] - struct ThisChainAccountId(u32); - #[derive(Debug, PartialEq, Eq, Decode, Encode)] - struct ThisChainSigner(u32); - #[derive(Debug, PartialEq, Eq, Decode, Encode)] - struct ThisChainSignature(u32); - #[derive(Debug, PartialEq, Eq, Decode, Encode)] - enum ThisChainCall { - #[codec(index = 42)] - Transfer, - #[codec(index = 84)] - Mint, - } #[derive(Clone, Debug)] struct ThisChainOrigin(Result, ()>); @@ -1104,14 +956,6 @@ mod tests { } } - #[derive(Debug, PartialEq, Eq, Decode, Encode, MaxEncodedLen)] - struct BridgedChainAccountId(u32); - #[derive(Debug, PartialEq, Eq, Decode, Encode)] - struct BridgedChainSigner(u32); - #[derive(Debug, PartialEq, Eq, Decode, Encode)] - struct BridgedChainSignature(u32); - #[derive(Debug, PartialEq, Eq, Decode, Encode)] - enum BridgedChainCall {} #[derive(Clone, Debug)] struct BridgedChainOrigin; @@ -1125,85 +969,42 @@ mod tests { } } - macro_rules! impl_wrapped_balance { - ($name:ident) => { - #[derive(Debug, PartialEq, Eq, Decode, Encode, Clone, Copy)] - struct $name(u32); - - impl From for $name { - fn from(balance: u32) -> Self { - Self(balance) - } - } - - impl sp_std::ops::Add for $name { - type Output = $name; - - fn add(self, other: Self) -> Self { - Self(self.0 + other.0) - } - } - - impl sp_std::ops::Div for $name { - type Output = $name; - - fn div(self, other: Self) -> Self { - Self(self.0 / other.0) - } - } - - impl sp_std::ops::Mul for $name { - type Output = $name; + struct ThisUnderlyingChain; + type ThisChainHeader = sp_runtime::generic::Header; + type ThisChainAccountId = u32; + type ThisChainBalance = u32; + #[derive(Decode, Encode)] + struct ThisChainCall; - fn mul(self, other: Self) -> Self { - Self(self.0 * other.0) - } - } - - impl sp_std::cmp::PartialOrd for $name { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } - } - - impl CheckedAdd for $name { - fn checked_add(&self, other: &Self) -> Option { - self.0.checked_add(other.0).map(Self) - } - } - - impl CheckedDiv for $name { - fn checked_div(&self, other: &Self) -> Option { - self.0.checked_div(other.0).map(Self) - } - } + impl Chain for ThisUnderlyingChain { + type BlockNumber = u64; + type Hash = H256; + type Hasher = BlakeTwo256; + type Header = ThisChainHeader; + type AccountId = ThisChainAccountId; + type Balance = ThisChainBalance; + type Index = u32; + type Signature = sp_runtime::MultiSignature; - impl CheckedMul for $name { - fn checked_mul(&self, other: &Self) -> Option { - self.0.checked_mul(other.0).map(Self) - } - } - }; + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } } - impl_wrapped_balance!(ThisChainBalance); - impl_wrapped_balance!(BridgedChainBalance); - struct ThisChain; impl ChainWithMessages for ThisChain { - type Hash = (); - type AccountId = ThisChainAccountId; - type Signer = ThisChainSigner; - type Signature = ThisChainSignature; - type Balance = ThisChainBalance; + type Chain = ThisUnderlyingChain; } impl ThisChainWithMessages for ThisChain { type RuntimeOrigin = ThisChainOrigin; type RuntimeCall = ThisChainCall; type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< - ::AccountId, + ThisChainAccountId, { DELIVERY_CONFIRMATION_TRANSACTION_WEIGHT }, 0, 0, @@ -1218,20 +1019,14 @@ mod tests { } fn transaction_payment(transaction: MessageTransaction) -> BalanceOf { - ThisChainBalance( - transaction - .dispatch_weight - .saturating_mul(THIS_CHAIN_WEIGHT_TO_BALANCE_RATE as u64) - .ref_time() as _, - ) + transaction + .dispatch_weight + .saturating_mul(THIS_CHAIN_WEIGHT_TO_BALANCE_RATE as u64) + .ref_time() as _ } } impl BridgedChainWithMessages for ThisChain { - fn maximal_extrinsic_size() -> u32 { - unreachable!() - } - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { unreachable!() } @@ -1249,25 +1044,42 @@ mod tests { } } - struct BridgedChain; + struct BridgedUnderlyingChain; + type BridgedChainHeader = sp_runtime::generic::Header; + type BridgedChainAccountId = u128; + type BridgedChainBalance = u128; + #[derive(Decode, Encode)] + struct BridgedChainCall; - impl ChainWithMessages for BridgedChain { - type Hash = (); + impl Chain for BridgedUnderlyingChain { + type BlockNumber = u64; + type Hash = H256; + type Hasher = BlakeTwo256; + type Header = BridgedChainHeader; type AccountId = BridgedChainAccountId; - type Signer = BridgedChainSigner; - type Signature = BridgedChainSignature; type Balance = BridgedChainBalance; + type Index = u32; + type Signature = sp_runtime::MultiSignature; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } + } + + struct BridgedChain; + + impl ChainWithMessages for BridgedChain { + type Chain = BridgedUnderlyingChain; } impl ThisChainWithMessages for BridgedChain { type RuntimeOrigin = BridgedChainOrigin; type RuntimeCall = BridgedChainCall; - type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< - ::AccountId, - 0, - 0, - 0, - >; + type ConfirmationTransactionEstimation = + BasicConfirmationTransactionEstimation; fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool { unreachable!() @@ -1283,10 +1095,6 @@ mod tests { } impl BridgedChainWithMessages for BridgedChain { - fn maximal_extrinsic_size() -> u32 { - BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE - } - fn verify_dispatch_weight(message_payload: &[u8]) -> bool { message_payload.len() >= BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT && message_payload.len() <= BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT @@ -1304,12 +1112,32 @@ mod tests { } fn transaction_payment(transaction: MessageTransaction) -> BalanceOf { - BridgedChainBalance( - transaction - .dispatch_weight - .saturating_mul(BRIDGED_CHAIN_WEIGHT_TO_BALANCE_RATE as u64) - .ref_time() as _, - ) + transaction + .dispatch_weight + .saturating_mul(BRIDGED_CHAIN_WEIGHT_TO_BALANCE_RATE as u64) + .ref_time() as _ + } + } + + thread_local! { + static TEST_BRIDGED_HEADER: RefCell> = RefCell::new(None); + } + + struct BridgedHeaderChain; + + impl HeaderChain for BridgedHeaderChain { + fn finalized_header( + _hash: HashOf, + ) -> Option> { + TEST_BRIDGED_HEADER.with(|h| h.borrow().clone()) + } + } + + struct ThisHeaderChain; + + impl HeaderChain for ThisHeaderChain { + fn finalized_header(_hash: HashOf) -> Option> { + unreachable!() } } @@ -1338,14 +1166,14 @@ mod tests { OnThisChainBridge::RELAYER_FEE_PERCENT, None, ), - Ok(ThisChainBalance(EXPECTED_MINIMAL_FEE)), + Ok(EXPECTED_MINIMAL_FEE), ); // and now check that the verifier checks the fee assert_eq!( source::FromThisChainMessageVerifier::::verify_message( &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), - &ThisChainBalance(1), + &1, TEST_LANE_ID, &test_lane_outbound_data(), &payload, @@ -1354,7 +1182,7 @@ mod tests { ); assert!(source::FromThisChainMessageVerifier::::verify_message( &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), - &ThisChainBalance(1_000_000), + &1_000_000, TEST_LANE_ID, &test_lane_outbound_data(), &payload, @@ -1367,7 +1195,7 @@ mod tests { assert_eq!( source::FromThisChainMessageVerifier::::verify_message( &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), - &ThisChainBalance(1_000_000), + &1_000_000, b"dsbl", &test_lane_outbound_data(), ®ular_outbound_message_payload(), @@ -1381,7 +1209,7 @@ mod tests { assert_eq!( source::FromThisChainMessageVerifier::::verify_message( &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), - &ThisChainBalance(1_000_000), + &1_000_000, TEST_LANE_ID, &OutboundLaneData { latest_received_nonce: 100, @@ -1436,62 +1264,48 @@ mod tests { ); } - #[derive(Debug)] - struct TestMessageProofParser { - failing: bool, - messages: RangeInclusive, + fn using_messages_proof( + nonces_end: MessageNonce, outbound_lane_data: Option, - } - - impl target::MessageProofParser for TestMessageProofParser { - fn read_raw_outbound_lane_data(&self, _lane_id: &LaneId) -> Option> { - if self.failing { - Some(vec![]) - } else { - self.outbound_lane_data.clone().map(|data| data.encode()) - } - } - - fn read_raw_message(&self, message_key: &MessageKey) -> Option> { - if self.failing { - Some(vec![]) - } else if self.messages.contains(&message_key.nonce) { - Some( - MessageData:: { - payload: message_key.nonce.encode(), - fee: BridgedChainBalance(0), - } - .encode(), - ) - } else { - None - } - } - } + encode_message: impl Fn(MessageNonce, &MessageData) -> Option>, + encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec, + test: impl Fn(target::FromBridgedChainMessagesProof) -> R, + ) -> R { + let (state_root, storage_proof) = prepare_messages_storage_proof::( + *TEST_LANE_ID, + 1..=nonces_end, + outbound_lane_data, + bp_runtime::StorageProofSize::Minimal(0), + vec![42], + encode_message, + encode_outbound_lane_data, + ); - #[allow(clippy::reversed_empty_ranges)] - fn no_messages_range() -> RangeInclusive { - 1..=0 - } + TEST_BRIDGED_HEADER.with(|h| { + *h.borrow_mut() = Some(BridgedChainHeader::new( + 0, + Default::default(), + state_root, + Default::default(), + Default::default(), + )) + }); - fn messages_proof(nonces_end: MessageNonce) -> target::FromBridgedChainMessagesProof<()> { - target::FromBridgedChainMessagesProof { - bridged_header_hash: (), - storage_proof: vec![], - lane: Default::default(), + test(target::FromBridgedChainMessagesProof { + bridged_header_hash: Default::default(), + storage_proof, + lane: *TEST_LANE_ID, nonces_start: 1, nonces_end, - } + }) } #[test] fn messages_proof_is_rejected_if_declared_less_than_actual_number_of_messages() { assert_eq!( - target::verify_messages_proof_with_parser::( - messages_proof(10), - 5, - |_, _| unreachable!(), - ), + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { + target::verify_messages_proof::(proof, 5) + }), Err(target::MessageProofError::MessagesCountMismatch), ); } @@ -1499,38 +1313,45 @@ mod tests { #[test] fn messages_proof_is_rejected_if_declared_more_than_actual_number_of_messages() { assert_eq!( - target::verify_messages_proof_with_parser::( - messages_proof(10), - 15, - |_, _| unreachable!(), - ), + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { + target::verify_messages_proof::(proof, 15) + }), Err(target::MessageProofError::MessagesCountMismatch), ); } #[test] - fn message_proof_is_rejected_if_build_parser_fails() { + fn message_proof_is_rejected_if_header_is_missing_from_the_chain() { assert_eq!( - target::verify_messages_proof_with_parser::( - messages_proof(10), - 10, - |_, _| Err(target::MessageProofError::Custom("test")), - ), - Err(target::MessageProofError::Custom("test")), + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { + TEST_BRIDGED_HEADER.with(|h| *h.borrow_mut() = None); + target::verify_messages_proof::(proof, 10) + }), + Err(target::MessageProofError::HeaderChain(HeaderChainError::UnknownHeader)), + ); + } + + #[test] + fn message_proof_is_rejected_if_header_state_root_mismatches() { + assert_eq!( + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { + TEST_BRIDGED_HEADER + .with(|h| h.borrow_mut().as_mut().unwrap().state_root = Default::default()); + target::verify_messages_proof::(proof, 10) + }), + Err(target::MessageProofError::HeaderChain(HeaderChainError::StorageRootMismatch)), ); } #[test] fn message_proof_is_rejected_if_required_message_is_missing() { assert_eq!( - target::verify_messages_proof_with_parser::( - messages_proof(10), + using_messages_proof( 10, - |_, _| Ok(TestMessageProofParser { - failing: false, - messages: 1..=5, - outbound_lane_data: None, - }), + None, + |n, m| if n != 5 { Some(m.encode()) } else { None }, + encode_lane_data, + |proof| target::verify_messages_proof::(proof, 10) ), Err(target::MessageProofError::MissingRequiredMessage), ); @@ -1539,14 +1360,19 @@ mod tests { #[test] fn message_proof_is_rejected_if_message_decode_fails() { assert_eq!( - target::verify_messages_proof_with_parser::( - messages_proof(10), + using_messages_proof( 10, - |_, _| Ok(TestMessageProofParser { - failing: true, - messages: 1..=10, - outbound_lane_data: None, - }), + None, + |n, m| { + let mut m = m.encode(); + if n == 5 { + m = MessageData { fee: 0, payload: vec![0u8; 42] }.encode(); + m.truncate(2); + } + Some(m.encode()) + }, + encode_lane_data, + |proof| target::verify_messages_proof::(proof, 10), ), Err(target::MessageProofError::FailedToDecodeMessage), ); @@ -1555,18 +1381,20 @@ mod tests { #[test] fn message_proof_is_rejected_if_outbound_lane_state_decode_fails() { assert_eq!( - target::verify_messages_proof_with_parser::( - messages_proof(0), - 0, - |_, _| Ok(TestMessageProofParser { - failing: true, - messages: no_messages_range(), - outbound_lane_data: Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), + using_messages_proof( + 10, + Some(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 1, + latest_generated_nonce: 1, }), + encode_all_messages, + |d| { + let mut d = d.encode(); + d.truncate(1); + d + }, + |proof| target::verify_messages_proof::(proof, 10), ), Err(target::MessageProofError::FailedToDecodeOutboundLaneState), ); @@ -1575,15 +1403,9 @@ mod tests { #[test] fn message_proof_is_rejected_if_it_is_empty() { assert_eq!( - target::verify_messages_proof_with_parser::( - messages_proof(0), - 0, - |_, _| Ok(TestMessageProofParser { - failing: false, - messages: no_messages_range(), - outbound_lane_data: None, - }), - ), + using_messages_proof(0, None, encode_all_messages, encode_lane_data, |proof| { + target::verify_messages_proof::(proof, 0) + },), Err(target::MessageProofError::Empty), ); } @@ -1591,21 +1413,19 @@ mod tests { #[test] fn non_empty_message_proof_without_messages_is_accepted() { assert_eq!( - target::verify_messages_proof_with_parser::( - messages_proof(0), + using_messages_proof( 0, - |_, _| Ok(TestMessageProofParser { - failing: false, - messages: no_messages_range(), - outbound_lane_data: Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), + Some(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 1, + latest_generated_nonce: 1, }), + encode_all_messages, + encode_lane_data, + |proof| target::verify_messages_proof::(proof, 0), ), Ok(vec![( - Default::default(), + *TEST_LANE_ID, ProvedLaneMessages { lane_state: Some(OutboundLaneData { oldest_unpruned_nonce: 1, @@ -1623,21 +1443,19 @@ mod tests { #[test] fn non_empty_message_proof_is_accepted() { assert_eq!( - target::verify_messages_proof_with_parser::( - messages_proof(1), + using_messages_proof( 1, - |_, _| Ok(TestMessageProofParser { - failing: false, - messages: 1..=1, - outbound_lane_data: Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), + Some(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 1, + latest_generated_nonce: 1, }), + encode_all_messages, + encode_lane_data, + |proof| target::verify_messages_proof::(proof, 1), ), Ok(vec![( - Default::default(), + *TEST_LANE_ID, ProvedLaneMessages { lane_state: Some(OutboundLaneData { oldest_unpruned_nonce: 1, @@ -1645,8 +1463,8 @@ mod tests { latest_generated_nonce: 1, }), messages: vec![Message { - key: MessageKey { lane_id: Default::default(), nonce: 1 }, - data: MessageData { payload: 1u64.encode(), fee: BridgedChainBalance(0) }, + key: MessageKey { lane_id: *TEST_LANE_ID, nonce: 1 }, + data: MessageData { payload: vec![42], fee: 0 }, }], }, )] @@ -1656,21 +1474,12 @@ mod tests { } #[test] - fn verify_messages_proof_with_parser_does_not_panic_if_messages_count_mismatches() { + fn verify_messages_proof_does_not_panic_if_messages_count_mismatches() { assert_eq!( - target::verify_messages_proof_with_parser::( - messages_proof(u64::MAX), - 0, - |_, _| Ok(TestMessageProofParser { - failing: false, - messages: 0..=u64::MAX, - outbound_lane_data: Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), - }), - ), + using_messages_proof(1, None, encode_all_messages, encode_lane_data, |mut proof| { + proof.nonces_end = u64::MAX; + target::verify_messages_proof::(proof, u32::MAX) + },), Err(target::MessageProofError::MessagesCountMismatch), ); } diff --git a/bridges/bin/runtime-common/src/messages_benchmarking.rs b/bridges/bin/runtime-common/src/messages_benchmarking.rs index 71eed881a063d..24df69dc90a5e 100644 --- a/bridges/bin/runtime-common/src/messages_benchmarking.rs +++ b/bridges/bin/runtime-common/src/messages_benchmarking.rs @@ -19,14 +19,18 @@ #![cfg(feature = "runtime-benchmarks")] -use crate::messages::{ - source::{FromBridgedChainMessagesDeliveryProof, FromThisChainMessagePayload}, - target::FromBridgedChainMessagesProof, - AccountIdOf, BalanceOf, BridgedChain, CallOf, HashOf, MessageBridge, RawStorageProof, - SignatureOf, SignerOf, ThisChain, +use crate::{ + messages::{ + source::{FromBridgedChainMessagesDeliveryProof, FromThisChainMessagePayload}, + target::FromBridgedChainMessagesProof, + AccountIdOf, BalanceOf, BridgedChain, CallOf, HashOf, MessageBridge, ThisChain, + }, + messages_generation::{ + encode_all_messages, encode_lane_data, grow_trie, prepare_messages_storage_proof, + }, }; -use bp_messages::{storage_keys, MessageData, MessageKey, MessagePayload}; +use bp_messages::storage_keys; use bp_runtime::{record_all_trie_keys, StorageProofSize}; use codec::Encode; use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; @@ -34,7 +38,7 @@ use pallet_bridge_messages::benchmarking::{ MessageDeliveryProofParams, MessageParams, MessageProofParams, }; use sp_core::Hasher; -use sp_runtime::traits::{Header, IdentifyAccount, MaybeSerializeDeserialize, Zero}; +use sp_runtime::traits::{Header, MaybeSerializeDeserialize, Zero}; use sp_std::{fmt::Debug, prelude::*}; use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; @@ -71,10 +75,6 @@ where BalanceOf>: Debug + MaybeSerializeDeserialize, CallOf>: From> + GetDispatchInfo, HashOf>: Copy + Default, - SignatureOf>: From, - SignerOf>: Clone - + From - + IdentifyAccount>>, { let message_payload = match params.size { StorageProofSize::Minimal(ref size) => vec![0u8; *size as _], @@ -82,8 +82,15 @@ where }; // finally - prepare storage proof and update environment - let (state_root, storage_proof) = - prepare_messages_storage_proof::(¶ms, message_payload); + let (state_root, storage_proof) = prepare_messages_storage_proof::( + params.lane, + params.message_nonces.clone(), + params.outbound_lane_data, + params.size, + message_payload, + encode_all_messages, + encode_lane_data, + ); let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); ( @@ -141,69 +148,6 @@ where } } -/// Prepare storage proof of given messages. -/// -/// Returns state trie root and nodes with prepared messages. -fn prepare_messages_storage_proof( - params: &MessageProofParams, - message_payload: MessagePayload, -) -> (HashOf>, RawStorageProof) -where - B: MessageBridge, - BHH: Hasher>>, - HashOf>: Copy + Default, -{ - // prepare Bridged chain storage with messages and (optionally) outbound lane state - let message_count = - params.message_nonces.end().saturating_sub(*params.message_nonces.start()) + 1; - let mut storage_keys = Vec::with_capacity(message_count as usize + 1); - let mut root = Default::default(); - let mut mdb = MemoryDB::default(); - { - let mut trie = TrieDBMutBuilderV1::::new(&mut mdb, &mut root).build(); - - // insert messages - for nonce in params.message_nonces.clone() { - let message_key = MessageKey { lane_id: params.lane, nonce }; - let message_data = MessageData { - fee: BalanceOf::>::from(0), - payload: message_payload.clone(), - }; - let storage_key = storage_keys::message_key( - B::BRIDGED_MESSAGES_PALLET_NAME, - &message_key.lane_id, - message_key.nonce, - ) - .0; - trie.insert(&storage_key, &message_data.encode()) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - storage_keys.push(storage_key); - } - - // insert outbound lane state - if let Some(ref outbound_lane_data) = params.outbound_lane_data { - let storage_key = - storage_keys::outbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, ¶ms.lane) - .0; - trie.insert(&storage_key, &outbound_lane_data.encode()) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - storage_keys.push(storage_key); - } - } - root = grow_trie(root, &mut mdb, params.size); - - // generate storage proof to be delivered to This chain - let mut proof_recorder = Recorder::>::new(); - record_all_trie_keys::, _>(&mdb, &root, &mut proof_recorder) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); - - (root, storage_proof) -} - /// Insert header to the bridge GRANDPA pallet. pub(crate) fn insert_header_to_grandpa_pallet( state_root: bp_runtime::HashOf, @@ -225,38 +169,3 @@ where pallet_bridge_grandpa::initialize_for_benchmarks::(bridged_header); (bridged_block_number, bridged_header_hash) } - -/// Populate trie with dummy keys+values until trie has at least given size. -pub fn grow_trie( - mut root: H::Out, - mdb: &mut MemoryDB, - trie_size: StorageProofSize, -) -> H::Out { - let (iterations, leaf_size, minimal_trie_size) = match trie_size { - StorageProofSize::Minimal(_) => return root, - StorageProofSize::HasLargeLeaf(size) => (1, size, size), - StorageProofSize::HasExtraNodes(size) => (8, 1, size), - }; - - let mut key_index = 0; - loop { - // generate storage proof to be delivered to This chain - let mut proof_recorder = Recorder::>::new(); - record_all_trie_keys::, _>(mdb, &root, &mut proof_recorder) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - let size: usize = proof_recorder.drain().into_iter().map(|n| n.data.len()).sum(); - if size > minimal_trie_size as _ { - return root - } - - let mut trie = TrieDBMutBuilderV1::::from_existing(mdb, &mut root).build(); - for _ in 0..iterations { - trie.insert(&key_index.encode(), &vec![42u8; leaf_size as _]) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - key_index += 1; - } - trie.commit(); - } -} diff --git a/bridges/bin/runtime-common/src/messages_generation.rs b/bridges/bin/runtime-common/src/messages_generation.rs new file mode 100644 index 0000000000000..822b635cc9b63 --- /dev/null +++ b/bridges/bin/runtime-common/src/messages_generation.rs @@ -0,0 +1,154 @@ +// Copyright 2019-2022 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 . + +//! Helpers for generating message storage proofs, that are used by tests and by benchmarks. + +#![cfg(any(feature = "runtime-benchmarks", test))] + +use crate::messages::{BalanceOf, BridgedChain, HashOf, HasherOf, MessageBridge, RawStorageProof}; + +use bp_messages::{ + storage_keys, LaneId, MessageData, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, +}; +use bp_runtime::{record_all_trie_keys, StorageProofSize}; +use codec::Encode; +use sp_core::Hasher; +use sp_runtime::traits::Zero; +use sp_std::{ops::RangeInclusive, prelude::*}; +use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; + +/// Simple and correct message data encode function. +pub(crate) fn encode_all_messages( + _: MessageNonce, + m: &MessageData, +) -> Option> { + Some(m.encode()) +} + +/// Simple and correct outbound lane data encode function. +pub(crate) fn encode_lane_data(d: &OutboundLaneData) -> Vec { + d.encode() +} + +/// Prepare storage proof of given messages. +/// +/// Returns state trie root and nodes with prepared messages. +pub(crate) fn prepare_messages_storage_proof( + lane: LaneId, + message_nonces: RangeInclusive, + outbound_lane_data: Option, + size: StorageProofSize, + message_payload: MessagePayload, + encode_message: impl Fn(MessageNonce, &MessageData>>) -> Option>, + encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec, +) -> (HashOf>, RawStorageProof) +where + B: MessageBridge, + HashOf>: Copy + Default, +{ + // prepare Bridged chain storage with messages and (optionally) outbound lane state + let message_count = message_nonces.end().saturating_sub(*message_nonces.start()) + 1; + let mut storage_keys = Vec::with_capacity(message_count as usize + 1); + let mut root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = + TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); + + // insert messages + for nonce in message_nonces { + let message_key = MessageKey { lane_id: lane, nonce }; + let message_data = MessageData { + fee: BalanceOf::>::zero(), + payload: message_payload.clone(), + }; + let message_data = match encode_message(nonce, &message_data) { + Some(message_data) => message_data, + None => continue, + }; + let storage_key = storage_keys::message_key( + B::BRIDGED_MESSAGES_PALLET_NAME, + &message_key.lane_id, + message_key.nonce, + ) + .0; + trie.insert(&storage_key, &message_data) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + storage_keys.push(storage_key); + } + + // insert outbound lane state + if let Some(outbound_lane_data) = outbound_lane_data.as_ref().map(encode_outbound_lane_data) + { + let storage_key = + storage_keys::outbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane).0; + trie.insert(&storage_key, &outbound_lane_data) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + storage_keys.push(storage_key); + } + } + root = grow_trie(root, &mut mdb, size); + + // generate storage proof to be delivered to This chain + let mut proof_recorder = Recorder::>>>::new(); + record_all_trie_keys::>>, _>( + &mdb, + &root, + &mut proof_recorder, + ) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); + let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); + + (root, storage_proof) +} + +/// Populate trie with dummy keys+values until trie has at least given size. +pub fn grow_trie( + mut root: H::Out, + mdb: &mut MemoryDB, + trie_size: StorageProofSize, +) -> H::Out { + let (iterations, leaf_size, minimal_trie_size) = match trie_size { + StorageProofSize::Minimal(_) => return root, + StorageProofSize::HasLargeLeaf(size) => (1, size, size), + StorageProofSize::HasExtraNodes(size) => (8, 1, size), + }; + + let mut key_index = 0; + loop { + // generate storage proof to be delivered to This chain + let mut proof_recorder = Recorder::>::new(); + record_all_trie_keys::, _>(mdb, &root, &mut proof_recorder) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); + let size: usize = proof_recorder.drain().into_iter().map(|n| n.data.len()).sum(); + if size > minimal_trie_size as _ { + return root + } + + let mut trie = TrieDBMutBuilderV1::::from_existing(mdb, &mut root).build(); + for _ in 0..iterations { + trie.insert(&key_index.encode(), &vec![42u8; leaf_size as _]) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + key_index += 1; + } + trie.commit(); + } +} diff --git a/bridges/bin/runtime-common/src/parachains_benchmarking.rs b/bridges/bin/runtime-common/src/parachains_benchmarking.rs index 59e2ef6962c83..fcd32ea28b86e 100644 --- a/bridges/bin/runtime-common/src/parachains_benchmarking.rs +++ b/bridges/bin/runtime-common/src/parachains_benchmarking.rs @@ -18,7 +18,9 @@ #![cfg(feature = "runtime-benchmarks")] -use crate::messages_benchmarking::{grow_trie, insert_header_to_grandpa_pallet}; +use crate::{ + messages_benchmarking::insert_header_to_grandpa_pallet, messages_generation::grow_trie, +}; use bp_parachains::parachain_head_storage_key_at_source; use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs index e061b702a11e0..2402b2512e27c 100644 --- a/bridges/modules/grandpa/src/lib.rs +++ b/bridges/modules/grandpa/src/lib.rs @@ -38,7 +38,7 @@ use storage_types::StoredAuthoritySet; -use bp_header_chain::{justification::GrandpaJustification, InitializationData}; +use bp_header_chain::{justification::GrandpaJustification, HeaderChain, InitializationData}; use bp_runtime::{ BlockNumberOf, BoundedStorageValue, Chain, HashOf, HasherOf, HeaderOf, OwnedBridgeModule, }; @@ -67,6 +67,8 @@ pub use weights::WeightInfo; /// The target that will be used when publishing logs related to this pallet. pub const LOG_TARGET: &str = "runtime::bridge-grandpa"; +/// Bridged chain from the pallet configuration. +pub type BridgedChain = >::BridgedChain; /// Block number of the bridged chain. pub type BridgedBlockNumber = BlockNumberOf<>::BridgedChain>; /// Block hash of the bridged chain. @@ -382,8 +384,6 @@ pub mod pallet { TooManyRequests, /// The header being imported is older than the best finalized header known to the pallet. OldHeader, - /// The header is unknown to the pallet. - UnknownHeader, /// The scheduled authority set change found in the header is unsupported by the pallet. /// /// This is the case for non-standard (e.g forced) authority set changes. @@ -392,8 +392,6 @@ pub mod pallet { NotInitialized, /// The pallet has already been initialized. AlreadyInitialized, - /// The storage proof doesn't contains storage root. So it is invalid for given header. - StorageRootMismatch, /// Too many authorities in the set. TooManyAuthoritiesInSet, /// Too large header. @@ -581,9 +579,6 @@ pub mod pallet { impl, I: 'static> Pallet { /// Get the best finalized header the pallet knows of. - /// - /// Returns a dummy header if there is no best header. This can only happen - /// if the pallet has not been initialized yet. pub fn best_finalized() -> Option> { let (_, hash) = >::get()?; >::get(hash).map(|h| h.into_inner()) @@ -593,21 +588,14 @@ impl, I: 'static> Pallet { pub fn is_known_header(hash: BridgedBlockHash) -> bool { >::contains_key(hash) } +} - /// Verify that the passed storage proof is valid, given it is crafted using - /// known finalized header. If the proof is valid, then the `parse` callback - /// is called and the function returns its result. - pub fn parse_finalized_storage_proof( - hash: BridgedBlockHash, - storage_proof: sp_trie::StorageProof, - parse: impl FnOnce(bp_runtime::StorageProofChecker>) -> R, - ) -> Result { - let header = >::get(hash).ok_or(Error::::UnknownHeader)?; - let storage_proof_checker = - bp_runtime::StorageProofChecker::new(*header.state_root(), storage_proof) - .map_err(|_| Error::::StorageRootMismatch)?; - - Ok(parse(storage_proof_checker)) +/// Bridge GRANDPA pallet as header chain. +pub type GrandpaChainHeaders = Pallet; + +impl, I: 'static> HeaderChain> for GrandpaChainHeaders { + fn finalized_header(hash: HashOf>) -> Option>> { + ImportedHeaders::::get(hash).map(|h| h.into_inner()) } } @@ -1119,7 +1107,7 @@ mod tests { sp_trie::StorageProof::new(vec![]), |_| (), ), - Error::::UnknownHeader, + bp_header_chain::HeaderChainError::UnknownHeader, ); }); } diff --git a/bridges/modules/parachains/Cargo.toml b/bridges/modules/parachains/Cargo.toml index bccd16f3d593d..ce674459eacfc 100644 --- a/bridges/modules/parachains/Cargo.toml +++ b/bridges/modules/parachains/Cargo.toml @@ -12,6 +12,7 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" # Bridge Dependencies +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } bp-parachains = { path = "../../primitives/parachains", default-features = false } bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } @@ -34,6 +35,7 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-parachains/std", "bp-polkadot-core/std", "bp-runtime/std", diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs index 4b2be1b71707f..e6625476a3de0 100644 --- a/bridges/modules/parachains/src/lib.rs +++ b/bridges/modules/parachains/src/lib.rs @@ -26,12 +26,14 @@ pub use weights::WeightInfo; pub use weights_ext::WeightInfoExt; +use bp_header_chain::HeaderChain; use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo}; -use bp_polkadot_core::parachains::{ParaHash, ParaHasher, ParaHead, ParaHeadsProof, ParaId}; -use bp_runtime::StorageProofError; +use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::{HashOf, HeaderOf, Parachain, StorageProofError}; +use codec::Decode; use frame_support::{dispatch::PostDispatchInfo, traits::Contains}; use sp_runtime::traits::Header as HeaderT; -use sp_std::vec::Vec; +use sp_std::{marker::PhantomData, vec::Vec}; // Re-export in crate namespace for `construct_runtime!`. pub use pallet::*; @@ -410,27 +412,6 @@ pub mod pallet { ImportedParaHeads::::get(parachain, hash).map(|h| h.into_inner()) } - /// Verify that the passed storage proof is valid, given it is crafted using - /// known finalized header. If the proof is valid, then the `parse` callback - /// is called and the function returns its result. - pub fn parse_finalized_storage_proof( - parachain: ParaId, - hash: ParaHash, - storage_proof: sp_trie::StorageProof, - decode_state_root: impl FnOnce(ParaHead) -> Option, - parse: impl FnOnce(bp_runtime::StorageProofChecker) -> R, - ) -> Result { - let para_head = - Self::parachain_head(parachain, hash).ok_or(Error::::UnknownParaHead)?; - let state_root = - decode_state_root(para_head).ok_or(Error::::FailedToExtractStateRoot)?; - let storage_proof_checker = - bp_runtime::StorageProofChecker::new(state_root, storage_proof) - .map_err(|_| Error::::StorageRootMismatch)?; - - Ok(parse(storage_proof_checker)) - } - /// Read parachain head from storage proof. fn read_parachain_head( storage: &bp_runtime::StorageProofChecker, @@ -617,6 +598,18 @@ pub mod pallet { } } +/// Single parachain header chain adapter. +pub struct ParachainHeaders(PhantomData<(T, I, C)>); + +impl, I: 'static, C: Parachain> HeaderChain + for ParachainHeaders +{ + fn finalized_header(hash: HashOf) -> Option> { + Pallet::::parachain_head(ParaId(C::PARACHAIN_ID), hash) + .and_then(|head| Decode::decode(&mut &head.0[..]).ok()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/bridges/primitives/chain-rialto-parachain/src/lib.rs b/bridges/primitives/chain-rialto-parachain/src/lib.rs index 3a54a2fb54805..584882425b4db 100644 --- a/bridges/primitives/chain-rialto-parachain/src/lib.rs +++ b/bridges/primitives/chain-rialto-parachain/src/lib.rs @@ -21,7 +21,7 @@ use bp_messages::{ InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, }; -use bp_runtime::{decl_bridge_runtime_apis, Chain}; +use bp_runtime::{decl_bridge_runtime_apis, Chain, Parachain}; use frame_support::{ dispatch::DispatchClass, weights::{constants::WEIGHT_PER_SECOND, IdentityFee, Weight}, @@ -160,6 +160,10 @@ impl Chain for RialtoParachain { } } +impl Parachain for RialtoParachain { + const PARACHAIN_ID: u32 = RIALTO_PARACHAIN_ID; +} + frame_support::parameter_types! { pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); diff --git a/bridges/primitives/header-chain/Cargo.toml b/bridges/primitives/header-chain/Cargo.toml index be87cce460e38..3b7ea58cb96a7 100644 --- a/bridges/primitives/header-chain/Cargo.toml +++ b/bridges/primitives/header-chain/Cargo.toml @@ -23,6 +23,7 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [dev-dependencies] bp-test-utils = { path = "../test-utils" } @@ -42,4 +43,5 @@ std = [ "sp-finality-grandpa/std", "sp-runtime/std", "sp-std/std", + "sp-trie/std", ] diff --git a/bridges/primitives/header-chain/src/lib.rs b/bridges/primitives/header-chain/src/lib.rs index 21a42874b719d..2b4e0802a5a70 100644 --- a/bridges/primitives/header-chain/src/lib.rs +++ b/bridges/primitives/header-chain/src/lib.rs @@ -19,19 +19,58 @@ #![cfg_attr(not(feature = "std"), no_std)] -use bp_runtime::BasicOperatingMode; +use bp_runtime::{BasicOperatingMode, Chain, HashOf, HasherOf, HeaderOf, StorageProofChecker}; use codec::{Codec, Decode, Encode, EncodeLike}; use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug}; +use frame_support::PalletError; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_finality_grandpa::{AuthorityList, ConsensusLog, SetId, GRANDPA_ENGINE_ID}; use sp_runtime::{traits::Header as HeaderT, Digest, RuntimeDebug}; use sp_std::boxed::Box; +use sp_trie::StorageProof; pub mod justification; pub mod storage_keys; +/// Header chain error. +#[derive(Clone, Copy, Decode, Encode, Eq, PalletError, PartialEq, RuntimeDebug, TypeInfo)] +pub enum HeaderChainError { + /// Header with given hash is missing from the chain. + UnknownHeader, + /// The storage proof doesn't contains storage root. + StorageRootMismatch, +} + +impl From for &'static str { + fn from(err: HeaderChainError) -> &'static str { + match err { + HeaderChainError::UnknownHeader => "UnknownHeader", + HeaderChainError::StorageRootMismatch => "StorageRootMismatch", + } + } +} + +/// Substrate header chain, abstracted from the way it is stored. +pub trait HeaderChain { + /// Returns finalized header by its hash. + fn finalized_header(hash: HashOf) -> Option>; + /// Parse storage proof using finalized header. + fn parse_finalized_storage_proof( + hash: HashOf, + storage_proof: StorageProof, + parse: impl FnOnce(StorageProofChecker>) -> R, + ) -> Result { + let header = Self::finalized_header(hash).ok_or(HeaderChainError::UnknownHeader)?; + let storage_proof_checker = + bp_runtime::StorageProofChecker::new(*header.state_root(), storage_proof) + .map_err(|_| HeaderChainError::StorageRootMismatch)?; + + Ok(parse(storage_proof_checker)) + } +} + /// A type that can be used as a parameter in a dispatchable function. /// /// When using `decl_module` all arguments for call functions must implement this trait. @@ -79,6 +118,7 @@ pub trait FinalityProof: Clone + Send + Sync + Debug { /// A trait that provides helper methods for querying the consensus log. pub trait ConsensusLogReader { + /// Returns true if digest contains item that schedules authorities set change. fn schedules_authorities_change(digest: &Digest) -> bool; } diff --git a/bridges/primitives/runtime/src/chain.rs b/bridges/primitives/runtime/src/chain.rs index be95f17480d01..6f43c0f354900 100644 --- a/bridges/primitives/runtime/src/chain.rs +++ b/bridges/primitives/runtime/src/chain.rs @@ -195,6 +195,12 @@ pub trait Chain: Send + Sync + 'static { fn max_extrinsic_weight() -> Weight; } +/// Minimal parachain representation that may be used from no_std environment. +pub trait Parachain: Chain { + /// Parachain identifier. + const PARACHAIN_ID: u32; +} + /// Block number used by the chain. pub type BlockNumberOf = ::BlockNumber; diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index 9f525f5ea8563..83043f80621f6 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -32,7 +32,7 @@ use sp_std::{convert::TryFrom, fmt::Debug, vec, vec::Vec}; pub use chain::{ AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf, - HasherOf, HeaderOf, IndexOf, SignatureOf, TransactionEraOf, + HasherOf, HeaderOf, IndexOf, Parachain, SignatureOf, TransactionEraOf, }; pub use frame_support::storage::storage_prefix as storage_value_final_key; use num_traits::{CheckedSub, One};