// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // 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 . //! Integrity tests for chain constants and pallets configuration. //! //! Most of the tests in this module assume that the bridge is using standard (see `crate::messages` //! module for details) configuration. use codec::Encode; use pezbp_header_pez_chain::ChainWithGrandpa; use pezbp_messages::{ChainWithMessages, InboundLaneData, MessageNonce}; use pezbp_runtime::{AccountIdOf, Chain}; use pezframe_support::{storage::generator::StorageValue, traits::Get, weights::Weight}; use pezframe_system::limits; use pezpallet_bridge_messages::{ThisChainOf, WeightInfoExt as _}; // Re-export to avoid include all dependencies everywhere. #[doc(hidden)] pub mod __private { pub use static_assertions; } /// Macro that ensures that the runtime configuration and chain primitives crate are sharing /// the same types (nonce, block number, hash, hasher, account id and header). #[macro_export] macro_rules! assert_chain_types( ( runtime: $r:path, this_chain: $this:path ) => { { use pezframe_system::{Config as SystemConfig, pezpallet_prelude::{BlockNumberFor, HeaderFor}}; use $crate::integrity::__private::static_assertions::assert_type_eq_all; // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard // configuration is used), or something has broke existing configuration (meaning that all bridged chains // and relays will stop functioning) assert_type_eq_all!(<$r as SystemConfig>::Nonce, pezbp_runtime::NonceOf<$this>); assert_type_eq_all!(BlockNumberFor<$r>, pezbp_runtime::BlockNumberOf<$this>); assert_type_eq_all!(<$r as SystemConfig>::Hash, pezbp_runtime::HashOf<$this>); assert_type_eq_all!(<$r as SystemConfig>::Hashing, pezbp_runtime::HasherOf<$this>); assert_type_eq_all!(<$r as SystemConfig>::AccountId, pezbp_runtime::AccountIdOf<$this>); assert_type_eq_all!(HeaderFor<$r>, pezbp_runtime::HeaderOf<$this>); } } ); /// Macro that ensures that the bridge messages pezpallet is configured properly to bridge using /// given configuration. #[macro_export] macro_rules! assert_bridge_messages_pallet_types( ( runtime: $r:path, with_bridged_chain_messages_instance: $i:path, this_chain: $this:path, bridged_chain: $bridged:path, expected_payload_type: $payload:path, ) => { { use $crate::integrity::__private::static_assertions::assert_type_eq_all; use pezbp_messages::ChainWithMessages; use pezbp_runtime::Chain; use pezpallet_bridge_messages::Config as BridgeMessagesConfig; // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard // configuration is used), or something has broke existing configuration (meaning that all bridged chains // and relays will stop functioning) assert_type_eq_all!(<$r as BridgeMessagesConfig<$i>>::ThisChain, $this); assert_type_eq_all!(<$r as BridgeMessagesConfig<$i>>::BridgedChain, $bridged); assert_type_eq_all!(<$r as BridgeMessagesConfig<$i>>::OutboundPayload, $payload); assert_type_eq_all!(<$r as BridgeMessagesConfig<$i>>::InboundPayload, $payload); } } ); /// Macro that combines four other macro calls - `assert_chain_types`, `assert_bridge_types`, /// and `assert_bridge_messages_pallet_types`. It may be used /// at the chain that is implementing standard messages bridge with messages pallets deployed. #[macro_export] macro_rules! assert_complete_bridge_types( ( runtime: $r:path, with_bridged_chain_messages_instance: $mi:path, this_chain: $this:path, bridged_chain: $bridged:path, expected_payload_type: $payload:path, ) => { $crate::assert_chain_types!(runtime: $r, this_chain: $this); $crate::assert_bridge_messages_pallet_types!( runtime: $r, with_bridged_chain_messages_instance: $mi, this_chain: $this, bridged_chain: $bridged, expected_payload_type: $payload, ); } ); /// Parameters for asserting chain-related constants. #[derive(Debug)] pub struct AssertChainConstants { /// Block length limits of the chain. pub block_length: limits::BlockLength, /// Block weight limits of the chain. pub block_weights: limits::BlockWeights, } /// Test that our hardcoded, chain-related constants, are matching chain runtime configuration. /// /// In particular, this test ensures that: /// /// 1) block weight limits are matching; /// 2) block size limits are matching. pub fn assert_chain_constants(params: AssertChainConstants) where R: pezframe_system::Config, { // we don't check runtime version here, because in our case we'll be building relay from one // repo and runtime will live in another repo, along with outdated relay version. To avoid // unneeded commits, let's not raise an error in case of version mismatch. // if one of following assert fails, it means that we may need to upgrade bridged chain and // relay to use updated constants. If constants are now smaller than before, it may lead to // undeliverable messages. // `BlockLength` struct is not implementing `PartialEq`, so we compare encoded values here. assert_eq!( R::BlockLength::get().encode(), params.block_length.encode(), "BlockLength from runtime ({:?}) differ from hardcoded: {:?}", R::BlockLength::get(), params.block_length, ); // `BlockWeights` struct is not implementing `PartialEq`, so we compare encoded values here assert_eq!( R::BlockWeights::get().encode(), params.block_weights.encode(), "BlockWeights from runtime ({:?}) differ from hardcoded: {:?}", R::BlockWeights::get(), params.block_weights, ); } /// Test that the constants, used in GRANDPA pezpallet configuration are valid. pub fn assert_bridge_grandpa_pallet_constants() where R: pezpallet_bridge_grandpa::Config, GI: 'static, { assert!( R::HeadersToKeep::get() > 0, "HeadersToKeep ({}) must be larger than zero", R::HeadersToKeep::get(), ); } /// Test that the constants, used in messages pezpallet configuration are valid. pub fn assert_bridge_messages_pallet_constants() where R: pezpallet_bridge_messages::Config, MI: 'static, { assert!( pezpallet_bridge_messages::BridgedChainOf::::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX <= pezpallet_bridge_messages::BridgedChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, "MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX ({}) of {:?} is larger than \ its MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX ({}). This makes \ no sense", pezpallet_bridge_messages::BridgedChainOf::::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, pezpallet_bridge_messages::BridgedChainOf::::ID, pezpallet_bridge_messages::BridgedChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, ); } /// Parameters for asserting bridge GRANDPA pezpallet names. #[derive(Debug)] struct AssertBridgeGrandpaPalletNames<'a> { /// Name of the GRANDPA pezpallet, deployed at this chain and used to bridge with the bridged /// chain. pub with_bridged_chain_grandpa_pallet_name: &'a str, } /// Tests that bridge pezpallet names used in `construct_runtime!()` macro call are matching /// constants from chain primitives crates. fn assert_bridge_grandpa_pallet_names(params: AssertBridgeGrandpaPalletNames) where R: pezpallet_bridge_grandpa::Config, GI: 'static, { // check that the bridge GRANDPA pezpallet has required name assert_eq!( pezpallet_bridge_grandpa::PalletOwner::::storage_value_final_key().to_vec(), pezbp_runtime::storage_value_key( params.with_bridged_chain_grandpa_pallet_name, "PalletOwner", ) .0, ); assert_eq!( pezpallet_bridge_grandpa::PalletOperatingMode::::storage_value_final_key().to_vec(), pezbp_runtime::storage_value_key( params.with_bridged_chain_grandpa_pallet_name, "PalletOperatingMode", ) .0, ); } /// Parameters for asserting bridge messages pezpallet names. #[derive(Debug)] struct AssertBridgeMessagesPalletNames<'a> { /// Name of the messages pezpallet, deployed at this chain and used to bridge with the bridged /// chain. pub with_bridged_chain_messages_pallet_name: &'a str, } /// Tests that bridge pezpallet names used in `construct_runtime!()` macro call are matching /// constants from chain primitives crates. fn assert_bridge_messages_pallet_names(params: AssertBridgeMessagesPalletNames) where R: pezpallet_bridge_messages::Config, MI: 'static, { // check that the bridge messages pezpallet has required name assert_eq!( pezpallet_bridge_messages::PalletOwner::::storage_value_final_key().to_vec(), pezbp_runtime::storage_value_key( params.with_bridged_chain_messages_pallet_name, "PalletOwner", ) .0, ); assert_eq!( pezpallet_bridge_messages::PalletOperatingMode::::storage_value_final_key().to_vec(), pezbp_runtime::storage_value_key( params.with_bridged_chain_messages_pallet_name, "PalletOperatingMode", ) .0, ); } /// Parameters for asserting complete standard messages bridge. #[derive(Debug)] pub struct AssertCompleteBridgeConstants { /// Parameters to assert this chain constants. pub this_chain_constants: AssertChainConstants, } /// All bridge-related constants tests for the complete standard relay-chain messages bridge /// (i.e. with bridge GRANDPA and messages pallets deployed). pub fn assert_complete_with_relay_chain_bridge_constants( params: AssertCompleteBridgeConstants, ) where R: pezframe_system::Config + pezpallet_bridge_grandpa::Config + pezpallet_bridge_messages::Config, GI: 'static, MI: 'static, { assert_chain_constants::(params.this_chain_constants); assert_bridge_grandpa_pallet_constants::(); assert_bridge_messages_pallet_constants::(); assert_bridge_grandpa_pallet_names::(AssertBridgeGrandpaPalletNames { with_bridged_chain_grandpa_pallet_name: >::BridgedChain::WITH_CHAIN_GRANDPA_PALLET_NAME, }); assert_bridge_messages_pallet_names::(AssertBridgeMessagesPalletNames { with_bridged_chain_messages_pallet_name: >::BridgedChain::WITH_CHAIN_MESSAGES_PALLET_NAME, }); } /// All bridge-related constants tests for the complete standard teyrchain messages bridge /// (i.e. with bridge GRANDPA, teyrchains and messages pallets deployed). pub fn assert_complete_with_teyrchain_bridge_constants( params: AssertCompleteBridgeConstants, ) where R: pezframe_system::Config + pezpallet_bridge_teyrchains::Config + pezpallet_bridge_messages::Config, >::BridgedRelayChain: ChainWithGrandpa, PI: 'static, MI: 'static, { assert_chain_constants::(params.this_chain_constants); assert_bridge_grandpa_pallet_constants::(); assert_bridge_messages_pallet_constants::(); assert_bridge_grandpa_pallet_names::( AssertBridgeGrandpaPalletNames { with_bridged_chain_grandpa_pallet_name: <>::BridgedRelayChain>::WITH_CHAIN_GRANDPA_PALLET_NAME, }, ); assert_bridge_messages_pallet_names::(AssertBridgeMessagesPalletNames { with_bridged_chain_messages_pallet_name: >::BridgedChain::WITH_CHAIN_MESSAGES_PALLET_NAME, }); } /// All bridge-related constants tests for the standalone messages bridge deployment (only with /// messages pallets deployed). pub fn assert_standalone_messages_bridge_constants(params: AssertCompleteBridgeConstants) where R: pezframe_system::Config + pezpallet_bridge_messages::Config, MI: 'static, { assert_chain_constants::(params.this_chain_constants); assert_bridge_messages_pallet_constants::(); assert_bridge_messages_pallet_names::(AssertBridgeMessagesPalletNames { with_bridged_chain_messages_pallet_name: >::BridgedChain::WITH_CHAIN_MESSAGES_PALLET_NAME, }); } /// Check that the message lane weights are correct. pub fn check_message_lane_weights< C: ChainWithMessages, T: pezframe_system::Config + pezpallet_bridge_messages::Config, MessagesPalletInstance: 'static, >( bridged_chain_extra_storage_proof_size: u32, this_chain_max_unrewarded_relayers: MessageNonce, this_chain_max_unconfirmed_messages: MessageNonce, // whether `RefundBridgedTeyrchainMessages` extension is deployed at runtime and is used for // refunding this bridge transactions? // // in other words: pass true for all known production chains runtime_includes_refund_extension: bool, ) { type Weights = >::WeightInfo; // check basic weight assumptions pezpallet_bridge_messages::ensure_weights_are_correct::>(); // check that the maximal message dispatch weight is below hardcoded limit pezpallet_bridge_messages::ensure_maximal_message_dispatch::>( C::maximal_incoming_message_size(), C::maximal_incoming_message_dispatch_weight(), ); // check that weights allow us to receive messages let max_incoming_message_proof_size = bridged_chain_extra_storage_proof_size.saturating_add(C::maximal_incoming_message_size()); pezpallet_bridge_messages::ensure_able_to_receive_message::>( C::max_extrinsic_size(), C::max_extrinsic_weight(), max_incoming_message_proof_size, C::maximal_incoming_message_dispatch_weight(), ); // check that weights allow us to receive delivery confirmations let max_incoming_inbound_lane_data_proof_size = InboundLaneData::< AccountIdOf>, >::encoded_size_hint_u32( this_chain_max_unrewarded_relayers as _ ); pezpallet_bridge_messages::ensure_able_to_receive_confirmation::< Weights, >( C::max_extrinsic_size(), C::max_extrinsic_weight(), max_incoming_inbound_lane_data_proof_size, this_chain_max_unrewarded_relayers, this_chain_max_unconfirmed_messages, ); // check that extra weights of delivery/confirmation transactions include the weight // of `RefundBridgedTeyrchainMessages` operations. This signed extension assumes the worst case // (i.e. slashing if delivery transaction was invalid) and refunds some weight if // assumption was wrong (i.e. if we did refund instead of slashing). This check // ensures the extension will not refund weight when it doesn't need to (i.e. if pezpallet // weights do not account weights of refund extension). if runtime_includes_refund_extension { assert_ne!( Weights::::receive_messages_proof_overhead_from_runtime(), Weight::zero() ); assert_ne!( Weights::::receive_messages_delivery_proof_overhead_from_runtime(), Weight::zero() ); } }