From e27c998d0af3bbd3df05e5aafc1e5fd987501fc9 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 20 Jun 2023 12:10:18 +0300 Subject: [PATCH] Add state to message lanes (#2214) * add LaneState enum and a field to lanes data * use OptionQuery in InboundLanes and OutboundLanes * ensure that lanes are opened when accessing them * an option to specify opened lanes in genesis config + fixed tests * added PR reference to TODO * fix failing benchmarks * spelling * get_or_init_data -> data --- .../extensions/refund_relayer_extension.rs | 4 +- bridges/bin/runtime-common/src/integrity.rs | 6 - .../runtime-common/src/messages_call_ext.rs | 102 +- .../src/messages_xcm_extension.rs | 3 +- bridges/bin/runtime-common/src/mock.rs | 2 - bridges/modules/messages/README.md | 7 +- bridges/modules/messages/src/benchmarking.rs | 63 +- bridges/modules/messages/src/inbound_lane.rs | 60 +- bridges/modules/messages/src/lib.rs | 1318 +---------------- bridges/modules/messages/src/outbound_lane.rs | 13 +- bridges/modules/messages/src/proofs.rs | 6 + bridges/modules/messages/src/tests/mock.rs | 31 +- .../messages/src/tests/pallet_tests.rs | 176 ++- .../modules/xcm-bridge-hub/src/exporter.rs | 16 +- bridges/modules/xcm-bridge-hub/src/mock.rs | 1 - bridges/primitives/messages/src/lib.rs | 52 +- 16 files changed, 457 insertions(+), 1403 deletions(-) diff --git a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs b/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs index 24b9ce9b34c28..d6ad32da835d5 100644 --- a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs +++ b/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs @@ -943,7 +943,7 @@ pub(crate) mod tests { use bp_header_chain::StoredHeaderDataBuilder; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, DeliveredMessages, InboundLaneData, + target_chain::FromBridgedChainMessagesProof, DeliveredMessages, InboundLaneData, LaneState, MessageNonce, MessagesOperatingMode, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; @@ -1149,6 +1149,7 @@ pub(crate) mod tests { nonces_start: pallet_bridge_messages::InboundLanes::::get( TEST_LANE_ID, ) + .unwrap() .last_delivered_nonce() + 1, nonces_end: best_message, @@ -2884,6 +2885,7 @@ pub(crate) mod tests { // allow empty message delivery transactions let lane_id = TestLaneId::get(); let in_lane_data = InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 0, relayers: vec![UnrewardedRelayer { relayer: relayer_account_at_bridged_chain(), diff --git a/bridges/bin/runtime-common/src/integrity.rs b/bridges/bin/runtime-common/src/integrity.rs index f661db8a22057..339da4dd39e47 100644 --- a/bridges/bin/runtime-common/src/integrity.rs +++ b/bridges/bin/runtime-common/src/integrity.rs @@ -165,12 +165,6 @@ where R: pallet_bridge_messages::Config, MI: 'static, { - assert!( - !R::ActiveOutboundLanes::get().is_empty(), - "ActiveOutboundLanes ({:?}) must not be empty", - R::ActiveOutboundLanes::get(), - ); - assert!( pallet_bridge_messages::BridgedChainOf::::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX <= pallet_bridge_messages::BridgedChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, diff --git a/bridges/bin/runtime-common/src/messages_call_ext.rs b/bridges/bin/runtime-common/src/messages_call_ext.rs index 8fc0d5794fd90..9f21910803818 100644 --- a/bridges/bin/runtime-common/src/messages_call_ext.rs +++ b/bridges/bin/runtime-common/src/messages_call_ext.rs @@ -146,7 +146,10 @@ impl, I: 'static> CallHelper { match info { CallInfo::ReceiveMessagesProof(info) => { let inbound_lane_data = - pallet_bridge_messages::InboundLanes::::get(info.base.lane_id); + match pallet_bridge_messages::InboundLanes::::get(info.base.lane_id) { + Some(inbound_lane_data) => inbound_lane_data, + None => return false, + }; if info.base.bundled_range.is_empty() { let post_occupation = unrewarded_relayers_occupation::(&inbound_lane_data); @@ -162,7 +165,10 @@ impl, I: 'static> CallHelper { }, CallInfo::ReceiveMessagesDeliveryProof(info) => { let outbound_lane_data = - pallet_bridge_messages::OutboundLanes::::get(info.0.lane_id); + match pallet_bridge_messages::OutboundLanes::::get(info.0.lane_id) { + Some(outbound_lane_data) => outbound_lane_data, + None => return false, + }; outbound_lane_data.latest_received_nonce == *info.0.bundled_range.end() }, } @@ -219,7 +225,7 @@ impl< .. }) = self.is_sub_type() { - let inbound_lane_data = pallet_bridge_messages::InboundLanes::::get(proof.lane); + let inbound_lane_data = pallet_bridge_messages::InboundLanes::::get(proof.lane)?; return Some(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { @@ -243,7 +249,8 @@ impl< .. }) = self.is_sub_type() { - let outbound_lane_data = pallet_bridge_messages::OutboundLanes::::get(proof.lane); + let outbound_lane_data = + pallet_bridge_messages::OutboundLanes::::get(proof.lane)?; return Some(ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { lane_id: proof.lane, @@ -349,14 +356,16 @@ mod tests { }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, DeliveredMessages, UnrewardedRelayer, - UnrewardedRelayersState, + target_chain::FromBridgedChainMessagesProof, DeliveredMessages, InboundLaneData, LaneState, + OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use sp_std::ops::RangeInclusive; + const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]); + fn fill_unrewarded_relayers() { let mut inbound_lane_state = - pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); + pallet_bridge_messages::InboundLanes::::get(TEST_LANE_ID).unwrap(); for n in 0..BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX { inbound_lane_state.relayers.push_back(UnrewardedRelayer { relayer: Default::default(), @@ -364,14 +373,14 @@ mod tests { }); } pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), + TEST_LANE_ID, inbound_lane_state, ); } fn fill_unrewarded_messages() { let mut inbound_lane_state = - pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); + pallet_bridge_messages::InboundLanes::::get(TEST_LANE_ID).unwrap(); inbound_lane_state.relayers.push_back(UnrewardedRelayer { relayer: Default::default(), messages: DeliveredMessages { @@ -380,15 +389,19 @@ mod tests { }, }); pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), + TEST_LANE_ID, inbound_lane_state, ); } fn deliver_message_10() { pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - bp_messages::InboundLaneData { relayers: Default::default(), last_confirmed_nonce: 10 }, + TEST_LANE_ID, + bp_messages::InboundLaneData { + state: LaneState::Opened, + relayers: Default::default(), + last_confirmed_nonce: 10, + }, ); } @@ -405,7 +418,7 @@ mod tests { proof: FromBridgedChainMessagesProof { bridged_header_hash: Default::default(), storage: Default::default(), - lane: LaneId([0, 0, 0, 0]), + lane: TEST_LANE_ID, nonces_start, nonces_end, }, @@ -415,9 +428,23 @@ mod tests { .is_ok() } + fn run_test(test: impl Fn() -> T) -> T { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + pallet_bridge_messages::InboundLanes::::insert( + TEST_LANE_ID, + InboundLaneData::opened(), + ); + pallet_bridge_messages::OutboundLanes::::insert( + TEST_LANE_ID, + OutboundLaneData::opened(), + ); + test() + }) + } + #[test] fn extension_rejects_obsolete_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver messages 8..=9 // => tx is rejected deliver_message_10(); @@ -427,7 +454,7 @@ mod tests { #[test] fn extension_rejects_same_message() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to import messages 10..=10 // => tx is rejected deliver_message_10(); @@ -437,7 +464,7 @@ mod tests { #[test] fn extension_rejects_call_with_some_obsolete_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver messages // 10..=15 => tx is rejected deliver_message_10(); @@ -447,7 +474,7 @@ mod tests { #[test] fn extension_rejects_call_with_future_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver messages // 13..=15 => tx is rejected deliver_message_10(); @@ -470,7 +497,7 @@ mod tests { #[test] fn extension_rejects_empty_delivery_with_rewards_confirmations_if_there_are_free_relayer_and_message_slots( ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(!validate_message_delivery(10, 9)); }); @@ -479,7 +506,7 @@ mod tests { #[test] fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_relayer_slots( ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); fill_unrewarded_relayers(); assert!(validate_message_delivery(10, 9)); @@ -489,7 +516,7 @@ mod tests { #[test] fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_message_slots( ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { fill_unrewarded_messages(); assert!(validate_message_delivery( BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, @@ -500,7 +527,7 @@ mod tests { #[test] fn extension_accepts_new_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver message 11..=15 // => tx is accepted deliver_message_10(); @@ -510,8 +537,9 @@ mod tests { fn confirm_message_10() { pallet_bridge_messages::OutboundLanes::::insert( - LaneId([0, 0, 0, 0]), + TEST_LANE_ID, bp_messages::OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 0, latest_received_nonce: 10, latest_generated_nonce: 10, @@ -525,7 +553,7 @@ mod tests { proof: FromBridgedChainMessagesDeliveryProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: LaneId([0, 0, 0, 0]), + lane: TEST_LANE_ID, }, relayers_state: UnrewardedRelayersState { last_delivered_nonce, @@ -539,7 +567,7 @@ mod tests { #[test] fn extension_rejects_obsolete_confirmations() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best confirmed is message#10 and we're trying to confirm message#5 => tx // is rejected confirm_message_10(); @@ -549,7 +577,7 @@ mod tests { #[test] fn extension_rejects_same_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best confirmed is message#10 and we're trying to confirm message#10 => // tx is rejected confirm_message_10(); @@ -559,7 +587,7 @@ mod tests { #[test] fn extension_rejects_empty_confirmation_even_if_there_are_no_free_unrewarded_entries() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); fill_unrewarded_relayers(); assert!(!validate_message_confirmation(10)); @@ -568,7 +596,7 @@ mod tests { #[test] fn extension_accepts_new_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best confirmed is message#10 and we're trying to confirm message#15 => // tx is accepted confirm_message_10(); @@ -583,7 +611,7 @@ mod tests { CallHelper::::was_successful(&CallInfo::ReceiveMessagesProof( ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: LaneId([0, 0, 0, 0]), + lane_id: TEST_LANE_ID, bundled_range, best_stored_nonce: 0, // doesn't matter for `was_successful` }, @@ -602,7 +630,7 @@ mod tests { #[test] #[allow(clippy::reversed_empty_ranges)] fn was_successful_returns_false_for_failed_reward_confirmation_transaction() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { fill_unrewarded_messages(); assert!(!was_message_delivery_successful(10..=9, true)); }); @@ -611,14 +639,14 @@ mod tests { #[test] #[allow(clippy::reversed_empty_ranges)] fn was_successful_returns_true_for_successful_reward_confirmation_transaction() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { assert!(was_message_delivery_successful(10..=9, true)); }); } #[test] fn was_successful_returns_false_for_failed_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(!was_message_delivery_successful(10..=12, false)); }); @@ -626,7 +654,7 @@ mod tests { #[test] fn was_successful_returns_false_for_partially_successful_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(!was_message_delivery_successful(9..=12, false)); }); @@ -634,7 +662,7 @@ mod tests { #[test] fn was_successful_returns_true_for_successful_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(was_message_delivery_successful(9..=10, false)); }); @@ -643,7 +671,7 @@ mod tests { fn was_message_confirmation_successful(bundled_range: RangeInclusive) -> bool { CallHelper::::was_successful(&CallInfo::ReceiveMessagesDeliveryProof( ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { - lane_id: LaneId([0, 0, 0, 0]), + lane_id: TEST_LANE_ID, bundled_range, best_stored_nonce: 0, // doesn't matter for `was_successful` }), @@ -652,7 +680,7 @@ mod tests { #[test] fn was_successful_returns_false_for_failed_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); assert!(!was_message_confirmation_successful(10..=12)); }); @@ -660,7 +688,7 @@ mod tests { #[test] fn was_successful_returns_false_for_partially_successful_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); assert!(!was_message_confirmation_successful(9..=12)); }); @@ -668,7 +696,7 @@ mod tests { #[test] fn was_successful_returns_true_for_successful_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); assert!(was_message_confirmation_successful(9..=10)); }); diff --git a/bridges/bin/runtime-common/src/messages_xcm_extension.rs b/bridges/bin/runtime-common/src/messages_xcm_extension.rs index 46ed4da0d8548..0ea2f8b9619af 100644 --- a/bridges/bin/runtime-common/src/messages_xcm_extension.rs +++ b/bridges/bin/runtime-common/src/messages_xcm_extension.rs @@ -339,7 +339,7 @@ mod tests { use super::*; use crate::mock::*; - use bp_messages::OutboundLaneData; + use bp_messages::{LaneState, OutboundLaneData}; use frame_support::parameter_types; use pallet_bridge_messages::OutboundLanes; @@ -397,6 +397,7 @@ mod tests { OutboundLanes::::insert( TEST_LANE_ID, OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 0, latest_received_nonce: 0, latest_generated_nonce, diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs index 3225115cb6f29..e6bd73f21f18d 100644 --- a/bridges/bin/runtime-common/src/mock.rs +++ b/bridges/bin/runtime-common/src/mock.rs @@ -111,7 +111,6 @@ crate::generate_bridge_reject_obsolete_headers_and_messages! { } parameter_types! { - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; pub const BridgedParasPalletName: &'static str = "Paras"; pub const ExistentialDeposit: ThisChainBalance = 500; pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; @@ -186,7 +185,6 @@ impl pallet_bridge_parachains::Config for TestRuntime { impl pallet_bridge_messages::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; - type ActiveOutboundLanes = ActiveOutboundLanes; type OutboundPayload = XcmAsPlainPayload; diff --git a/bridges/modules/messages/README.md b/bridges/modules/messages/README.md index 80fd92eb0e5a7..bf5cced438540 100644 --- a/bridges/modules/messages/README.md +++ b/bridges/modules/messages/README.md @@ -142,10 +142,9 @@ and will simply reject all transactions, related to inbound messages. ### What about other Constants in the Messages Module Configuration Trait? -Two settings that are used to check messages in the `send_message()` function. The -`pallet_bridge_messages::Config::ActiveOutboundLanes` is an array of all message lanes, that -may be used to send messages. All messages sent using other lanes are rejected. All messages that have -size above `pallet_bridge_messages::Config::MaximalOutboundPayloadSize` will also be rejected. +`pallet_bridge_messages::Config::MaximalOutboundPayloadSize` constant defines the maximal size +of outbound message that may be sent. If the message size is above this limit, the message is +rejected. To be able to reward the relayer for delivering messages, we store a map of message nonces range => identifier of the relayer that has delivered this range at the target chain runtime storage. If a diff --git a/bridges/modules/messages/src/benchmarking.rs b/bridges/modules/messages/src/benchmarking.rs index 1dfaa5b002856..f1b672c41a526 100644 --- a/bridges/modules/messages/src/benchmarking.rs +++ b/bridges/modules/messages/src/benchmarking.rs @@ -19,14 +19,14 @@ #![cfg(feature = "runtime-benchmarks")] use crate::{ - inbound_lane::InboundLaneStorage, outbound_lane, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, - BridgedChainOf, Call, OutboundLanes, RuntimeInboundLaneStorage, + outbound_lane, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, BridgedChainOf, Call, + InboundLanes, OutboundLanes, }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, target_chain::FromBridgedChainMessagesProof, ChainWithMessages, DeliveredMessages, - InboundLaneData, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer, + InboundLaneData, LaneId, LaneState, MessageNonce, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use bp_runtime::{AccountIdOf, HashOf, StorageProofSize}; @@ -113,22 +113,32 @@ pub trait Config: crate::Config { } fn send_regular_message, I: 'static>() { - let mut outbound_lane = outbound_lane::(T::bench_lane_id()); + OutboundLanes::::insert( + T::bench_lane_id(), + OutboundLaneData { + state: LaneState::Opened, + latest_generated_nonce: 1, + ..Default::default() + }, + ); + + let mut outbound_lane = outbound_lane::(T::bench_lane_id()).unwrap(); outbound_lane.send_message(BoundedVec::try_from(vec![]).expect("We craft valid messages")); } fn receive_messages, I: 'static>(nonce: MessageNonce) { - let mut inbound_lane_storage = - RuntimeInboundLaneStorage::::from_lane_id(T::bench_lane_id()); - inbound_lane_storage.set_data(InboundLaneData { - relayers: vec![UnrewardedRelayer { - relayer: T::bridged_relayer_id(), - messages: DeliveredMessages::new(nonce), - }] - .into_iter() - .collect(), - last_confirmed_nonce: 0, - }); + InboundLanes::::insert( + T::bench_lane_id(), + InboundLaneData { + state: LaneState::Opened, + relayers: vec![UnrewardedRelayer { + relayer: T::bridged_relayer_id(), + messages: DeliveredMessages::new(nonce), + }] + .into(), + last_confirmed_nonce: 0, + }, + ); } struct ReceiveMessagesProofSetup, I: 'static> { @@ -173,8 +183,8 @@ impl, I: 'static> ReceiveMessagesProofSetup { fn check_last_nonce(&self) { assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - self.last_nonce(), + crate::InboundLanes::::get(&T::bench_lane_id()).map(|d| d.last_delivered_nonce()), + Some(self.last_nonce()), ); } } @@ -273,6 +283,7 @@ mod benchmarks { lane: T::bench_lane_id(), message_nonces: setup.nonces(), outbound_lane_data: Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: setup.last_nonce(), latest_received_nonce: ReceiveMessagesProofSetup::::LATEST_RECEIVED_NONCE, latest_generated_nonce: setup.last_nonce(), @@ -350,6 +361,7 @@ mod benchmarks { let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), inbound_lane_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![UnrewardedRelayer { relayer: relayer_id.clone(), messages: DeliveredMessages::new(1), @@ -368,7 +380,10 @@ mod benchmarks { relayers_state, ); - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 1); + assert_eq!( + OutboundLanes::::get(T::bench_lane_id()).map(|s| s.latest_received_nonce), + Some(1) + ); assert!(T::is_relayer_rewarded(&relayer_id)); } @@ -398,6 +413,7 @@ mod benchmarks { let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), inbound_lane_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![UnrewardedRelayer { relayer: relayer_id.clone(), messages: delivered_messages, @@ -416,7 +432,10 @@ mod benchmarks { relayers_state, ); - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + assert_eq!( + OutboundLanes::::get(T::bench_lane_id()).map(|s| s.latest_received_nonce), + Some(2) + ); assert!(T::is_relayer_rewarded(&relayer_id)); } @@ -445,6 +464,7 @@ mod benchmarks { let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), inbound_lane_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: relayer1_id.clone(), @@ -469,7 +489,10 @@ mod benchmarks { relayers_state, ); - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + assert_eq!( + OutboundLanes::::get(T::bench_lane_id()).map(|s| s.latest_received_nonce), + Some(2) + ); assert!(T::is_relayer_rewarded(&relayer1_id)); assert!(T::is_relayer_rewarded(&relayer2_id)); } diff --git a/bridges/modules/messages/src/inbound_lane.rs b/bridges/modules/messages/src/inbound_lane.rs index 7ef4599a93c48..5061d63bd30e4 100644 --- a/bridges/modules/messages/src/inbound_lane.rs +++ b/bridges/modules/messages/src/inbound_lane.rs @@ -41,7 +41,7 @@ pub trait InboundLaneStorage { /// Return maximal number of unconfirmed messages in inbound lane. fn max_unconfirmed_messages(&self) -> MessageNonce; /// Get lane data from the storage. - fn get_or_init_data(&mut self) -> InboundLaneData; + fn data(&self) -> InboundLaneData; /// Update lane data in the storage. fn set_data(&mut self, data: InboundLaneData); } @@ -130,7 +130,7 @@ impl InboundLane { &mut self, outbound_lane_data: OutboundLaneData, ) -> Option { - let mut data = self.storage.get_or_init_data(); + let mut data = self.storage.data(); let last_delivered_nonce = data.last_delivered_nonce(); if outbound_lane_data.latest_received_nonce > last_delivered_nonce { @@ -173,7 +173,7 @@ impl InboundLane { nonce: MessageNonce, message_data: DispatchMessageData, ) -> ReceptionResult { - let mut data = self.storage.get_or_init_data(); + let mut data = self.storage.data(); if Some(nonce) != data.last_delivered_nonce().checked_add(1) { return ReceptionResult::InvalidNonce } @@ -244,7 +244,7 @@ mod tests { #[test] fn receive_status_update_ignores_status_from_the_future() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = inbound_lane::(TEST_LANE_ID).unwrap(); receive_regular_message(&mut lane, 1); assert_eq!( lane.receive_state_update(OutboundLaneData { @@ -254,14 +254,14 @@ mod tests { None, ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 0); + assert_eq!(lane.storage.data().last_confirmed_nonce, 0); }); } #[test] fn receive_status_update_ignores_obsolete_status() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = inbound_lane::(TEST_LANE_ID).unwrap(); receive_regular_message(&mut lane, 1); receive_regular_message(&mut lane, 2); receive_regular_message(&mut lane, 3); @@ -272,7 +272,7 @@ mod tests { }), Some(3), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); assert_eq!( lane.receive_state_update(OutboundLaneData { @@ -281,20 +281,20 @@ mod tests { }), None, ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); }); } #[test] fn receive_status_update_works() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = inbound_lane::(TEST_LANE_ID).unwrap(); receive_regular_message(&mut lane, 1); receive_regular_message(&mut lane, 2); receive_regular_message(&mut lane, 3); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 0); + assert_eq!(lane.storage.data().last_confirmed_nonce, 0); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![unrewarded_relayer(1, 3, TEST_RELAYER_A)] ); @@ -305,9 +305,9 @@ mod tests { }), Some(2), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 2); + assert_eq!(lane.storage.data().last_confirmed_nonce, 2); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![unrewarded_relayer(3, 3, TEST_RELAYER_A)] ); @@ -318,16 +318,16 @@ mod tests { }), Some(3), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); - assert_eq!(lane.storage.get_or_init_data().relayers, vec![]); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().relayers, vec![]); }); } #[test] fn receive_status_update_works_with_batches_from_relayers() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - let mut seed_storage_data = lane.storage.get_or_init_data(); + let mut lane = inbound_lane::(TEST_LANE_ID).unwrap(); + let mut seed_storage_data = lane.storage.data(); // Prepare data seed_storage_data.last_confirmed_nonce = 0; seed_storage_data.relayers.push_back(unrewarded_relayer(1, 1, TEST_RELAYER_A)); @@ -343,9 +343,9 @@ mod tests { }), Some(3), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![ unrewarded_relayer(4, 4, TEST_RELAYER_B), unrewarded_relayer(5, 5, TEST_RELAYER_C) @@ -357,7 +357,7 @@ mod tests { #[test] fn fails_to_receive_message_with_incorrect_nonce() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = inbound_lane::(TEST_LANE_ID).unwrap(); assert_eq!( lane.receive_message::( &TEST_RELAYER_A, @@ -366,14 +366,14 @@ mod tests { ), ReceptionResult::InvalidNonce ); - assert_eq!(lane.storage.get_or_init_data().last_delivered_nonce(), 0); + assert_eq!(lane.storage.data().last_delivered_nonce(), 0); }); } #[test] fn fails_to_receive_messages_above_unrewarded_relayer_entries_limit_per_lane() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = inbound_lane::(TEST_LANE_ID).unwrap(); let max_nonce = BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; for current_nonce in 1..max_nonce + 1 { assert_eq!( @@ -409,7 +409,7 @@ mod tests { #[test] fn fails_to_receive_messages_above_unconfirmed_messages_limit_per_lane() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = inbound_lane::(TEST_LANE_ID).unwrap(); let max_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; for current_nonce in 1..=max_nonce { assert_eq!( @@ -445,7 +445,7 @@ mod tests { #[test] fn correctly_receives_following_messages_from_two_relayers_alternately() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = inbound_lane::(TEST_LANE_ID).unwrap(); assert_eq!( lane.receive_message::( &TEST_RELAYER_A, @@ -471,7 +471,7 @@ mod tests { ReceptionResult::Dispatched(dispatch_result(0)) ); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), unrewarded_relayer(2, 2, TEST_RELAYER_B), @@ -484,7 +484,7 @@ mod tests { #[test] fn rejects_same_message_from_two_different_relayers() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = inbound_lane::(TEST_LANE_ID).unwrap(); assert_eq!( lane.receive_message::( &TEST_RELAYER_A, @@ -507,16 +507,16 @@ mod tests { #[test] fn correct_message_is_processed_instantly() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = inbound_lane::(TEST_LANE_ID).unwrap(); receive_regular_message(&mut lane, 1); - assert_eq!(lane.storage.get_or_init_data().last_delivered_nonce(), 1); + assert_eq!(lane.storage.data().last_delivered_nonce(), 1); }); } #[test] fn unspent_weight_is_returned_by_receive_message() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = inbound_lane::(TEST_LANE_ID).unwrap(); let mut payload = REGULAR_PAYLOAD; *payload.dispatch_result.unspent_weight.ref_time_mut() = 1; assert_eq!( @@ -533,7 +533,7 @@ mod tests { #[test] fn first_message_is_confirmed_correctly() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = inbound_lane::(TEST_LANE_ID).unwrap(); receive_regular_message(&mut lane, 1); receive_regular_message(&mut lane, 2); assert_eq!( diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs index 25278714666cd..880b5553e3b68 100644 --- a/bridges/modules/messages/src/lib.rs +++ b/bridges/modules/messages/src/lib.rs @@ -61,7 +61,7 @@ use bp_messages::{ ProvedLaneMessages, ProvedMessages, }, ChainWithMessages, DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId, - MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData, + LaneState, MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, UnrewardedRelayersState, VerificationError, }; use bp_runtime::{ @@ -115,9 +115,6 @@ pub mod pallet { /// Bridged chain headers provider. type BridgedHeaderChain: HeaderChain; - /// Get all active outbound lanes that the message pallet is serving. - type ActiveOutboundLanes: Get<&'static [LaneId]>; - /// Payload type of outbound messages. This payload is dispatched on the bridged chain. type OutboundPayload: Parameter + Size; /// Payload type of inbound messages. This payload is dispatched on this chain. @@ -249,7 +246,7 @@ pub mod pallet { let mut messages_received_status = Vec::with_capacity(messages.len()); let mut dispatch_weight_left = dispatch_weight; for (lane_id, lane_data) in messages { - let mut lane = inbound_lane::(lane_id); + let mut lane = inbound_lane::(lane_id)?; // subtract extra storage proof bytes from the actual PoV size - there may be // less unrewarded relayers than the maximal configured value @@ -266,7 +263,7 @@ pub mod pallet { "Received lane {:?} state update: latest_confirmed_nonce={}. Unrewarded relayers: {:?}", lane_id, updated_latest_confirmed_nonce, - UnrewardedRelayersState::from(&lane.storage_mut().get_or_init_data()), + UnrewardedRelayersState::from(&lane.storage_mut().data()), ); } } @@ -377,7 +374,7 @@ pub mod pallet { ); // mark messages as delivered - let mut lane = outbound_lane::(lane_id); + let mut lane = outbound_lane::(lane_id)?; let last_delivered_nonce = lane_data.last_delivered_nonce(); let confirmed_messages = lane .confirm_delivery( @@ -468,10 +465,16 @@ pub mod pallet { pub enum Error { /// Pallet is not in Normal operating mode. NotOperatingNormally, - /// The outbound lane is inactive. - InactiveOutboundLane, /// The inbound message dispatcher is inactive. MessageDispatchInactive, + /// The outbound lane is unknown. + UnknownOutboundLane, + /// The outbound lane exists, but it is currently closed. + ClosedOutboundLane, + /// The inbound lane is unknown. + UnknownInboundLane, + /// The inbound lane exists, but it is currently closed. + ClosedInboundLane, /// Message has been treated as invalid by chain verifier. MessageRejectedByChainVerifier(VerificationError), /// Message has been treated as invalid by the pallet logic. @@ -516,10 +519,13 @@ pub mod pallet { pub type PalletOperatingMode, I: 'static = ()> = StorageValue<_, MessagesOperatingMode, ValueQuery>; + // TODO: https://github.com/paritytech/parity-bridges-common/pull/2213: let's limit number of + // possible opened lanes && use it to constraint maps below + /// Map of lane id => inbound lane data. #[pallet::storage] pub type InboundLanes, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData, ValueQuery>; + StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData, OptionQuery>; /// Map of lane id => outbound lane data. #[pallet::storage] @@ -527,11 +533,10 @@ pub mod pallet { Hasher = Blake2_128Concat, Key = LaneId, Value = OutboundLaneData, - QueryKind = ValueQuery, - OnEmpty = GetDefault, - MaxValues = MaybeOutboundLanesCount, + QueryKind = OptionQuery, >; + // TODO:(bridges-v2) - do we still need this? /// Map of lane id => is congested signal sent. It is managed by the /// `bridge_runtime_common::LocalXcmQueueManager`. /// @@ -563,6 +568,8 @@ pub mod pallet { pub operating_mode: MessagesOperatingMode, /// Initial pallet owner. pub owner: Option, + /// Opened lanes. + pub opened_lanes: Vec, /// Dummy marker. pub phantom: sp_std::marker::PhantomData, } @@ -574,6 +581,11 @@ pub mod pallet { if let Some(ref owner) = self.owner { PalletOwner::::put(owner); } + + for lane_id in &self.opened_lanes { + InboundLanes::::insert(lane_id, InboundLaneData::opened()); + OutboundLanes::::insert(lane_id, OutboundLaneData::opened()); + } } } @@ -599,15 +611,15 @@ pub mod pallet { } /// Return outbound lane data. - pub fn outbound_lane_data(lane: LaneId) -> OutboundLaneData { + pub fn outbound_lane_data(lane: LaneId) -> Option { OutboundLanes::::get(lane) } /// Return inbound lane data. pub fn inbound_lane_data( lane: LaneId, - ) -> InboundLaneData>> { - InboundLanes::::get(lane).0 + ) -> Option>>> { + InboundLanes::::get(lane).map(|lane| lane.0) } } @@ -616,7 +628,7 @@ pub mod pallet { impl, I: 'static> Get> for MaybeOutboundLanesCount { fn get() -> Option { - Some(T::ActiveOutboundLanes::get().len() as u32) + Some(OutboundLanes::::iter().count() as u32) } } } @@ -626,6 +638,7 @@ pub mod pallet { #[derive(Debug, PartialEq, Eq)] pub struct SendMessageArgs, I: 'static> { lane_id: LaneId, + lane: OutboundLane>, payload: StoredMessagePayload, } @@ -638,16 +651,17 @@ where type SendMessageArgs = SendMessageArgs; fn validate_message( - lane: LaneId, + lane_id: LaneId, message: &T::OutboundPayload, ) -> Result, Self::Error> { ensure_normal_operating_mode::()?; - // let's check if outbound lane is active - ensure!(T::ActiveOutboundLanes::get().contains(&lane), Error::::InactiveOutboundLane); + // check lane + let lane = outbound_lane::(lane_id)?; Ok(SendMessageArgs { - lane_id: lane, + lane_id, + lane, payload: StoredMessagePayload::::try_from(message.encode()).map_err(|_| { Error::::MessageRejectedByPallet(VerificationError::MessageTooLarge) })?, @@ -656,7 +670,7 @@ where fn send_message(args: SendMessageArgs) -> SendMessageArtifacts { // save message in outbound storage and emit event - let mut lane = outbound_lane::(args.lane_id); + let mut lane = args.lane; let message_len = args.payload.len(); let nonce = lane.send_message(args.payload); @@ -691,28 +705,35 @@ fn ensure_normal_operating_mode, I: 'static>() -> Result<(), Error< /// Creates new inbound lane object, backed by runtime storage. fn inbound_lane, I: 'static>( lane_id: LaneId, -) -> InboundLane> { - InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id)) +) -> Result>, Error> { + Ok(InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id)?)) } /// Creates new outbound lane object, backed by runtime storage. fn outbound_lane, I: 'static>( lane_id: LaneId, -) -> OutboundLane> { - OutboundLane::new(RuntimeOutboundLaneStorage { lane_id, _phantom: Default::default() }) +) -> Result>, Error> { + Ok(OutboundLane::new(RuntimeOutboundLaneStorage::from_lane_id(lane_id)?)) } /// Runtime inbound lane storage. struct RuntimeInboundLaneStorage, I: 'static = ()> { lane_id: LaneId, - cached_data: Option>>>, + cached_data: InboundLaneData>>, _phantom: PhantomData, } impl, I: 'static> RuntimeInboundLaneStorage { - /// Creates new runtime inbound lane storage. - fn from_lane_id(lane_id: LaneId) -> RuntimeInboundLaneStorage { - RuntimeInboundLaneStorage { lane_id, cached_data: None, _phantom: Default::default() } + /// Creates new runtime inbound lane storage for given **existing** lane. + fn from_lane_id(lane_id: LaneId) -> Result, Error> { + let cached_data = + InboundLanes::::get(lane_id).ok_or(Error::::UnknownInboundLane)?; + ensure!(cached_data.state == LaneState::Opened, Error::::ClosedInboundLane); + Ok(RuntimeInboundLaneStorage { + lane_id, + cached_data: cached_data.into(), + _phantom: Default::default(), + }) } } @@ -727,7 +748,7 @@ impl, I: 'static> RuntimeInboundLaneStorage { /// we may subtract extra bytes from this component. pub fn extra_proof_size_bytes(&mut self) -> u64 { let max_encoded_len = StoredInboundLaneData::::max_encoded_len(); - let relayers_count = self.get_or_init_data().relayers.len(); + let relayers_count = self.data().relayers.len(); let actual_encoded_len = InboundLaneData::>>::encoded_size_hint(relayers_count) .unwrap_or(usize::MAX); @@ -750,30 +771,34 @@ impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage< BridgedChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX } - fn get_or_init_data(&mut self) -> InboundLaneData>> { - match self.cached_data { - Some(ref data) => data.clone(), - None => { - let data: InboundLaneData>> = - InboundLanes::::get(self.lane_id).into(); - self.cached_data = Some(data.clone()); - data - }, - } + fn data(&self) -> InboundLaneData>> { + self.cached_data.clone() } fn set_data(&mut self, data: InboundLaneData>>) { - self.cached_data = Some(data.clone()); + self.cached_data = data.clone(); InboundLanes::::insert(self.lane_id, StoredInboundLaneData::(data)) } } /// Runtime outbound lane storage. +#[derive(Debug, PartialEq, Eq)] struct RuntimeOutboundLaneStorage { lane_id: LaneId, + cached_data: OutboundLaneData, _phantom: PhantomData<(T, I)>, } +impl, I: 'static> RuntimeOutboundLaneStorage { + /// Creates new runtime outbound lane storage for given **existing** lane. + fn from_lane_id(lane_id: LaneId) -> Result> { + let cached_data = + OutboundLanes::::get(lane_id).ok_or(Error::::UnknownOutboundLane)?; + ensure!(cached_data.state == LaneState::Opened, Error::::ClosedOutboundLane); + Ok(Self { lane_id, cached_data, _phantom: PhantomData }) + } +} + impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { type StoredMessagePayload = StoredMessagePayload; @@ -782,10 +807,11 @@ impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorag } fn data(&self) -> OutboundLaneData { - OutboundLanes::::get(self.lane_id) + self.cached_data.clone() } fn set_data(&mut self, data: OutboundLaneData) { + self.cached_data = data.clone(); OutboundLanes::::insert(self.lane_id, data) } @@ -829,1209 +855,3 @@ fn verify_and_decode_messages_proof, I: 'static>( .collect() }) } - -// #[cfg(test)] -// mod tests { -// use super::*; -// use crate::{ -// mock::{ -// inbound_unrewarded_relayers_state, message, message_payload, run_test, -// unrewarded_relayer, AccountId, DbWeight, RuntimeEvent as TestEvent, RuntimeOrigin, -// TestDeliveryConfirmationPayments, TestDeliveryPayments, TestMessageDispatch, -// TestMessagesDeliveryProof, TestMessagesProof, TestOnMessagesDelivered, TestRelayer, -// TestRuntime, TestWeightInfo, MAX_OUTBOUND_PAYLOAD_SIZE, -// PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_LANE_ID_2, -// TEST_LANE_ID_3, TEST_RELAYER_A, TEST_RELAYER_B, -// }, -// outbound_lane::ReceptionConfirmationError, -// }; -// use bp_messages::{ -// source_chain::MessagesBridge, BridgeMessagesCall, UnrewardedRelayer, -// UnrewardedRelayersState, -// }; -// use bp_test_utils::generate_owned_bridge_module_tests; -// use frame_support::{ -// assert_noop, assert_ok, -// dispatch::Pays, -// storage::generator::{StorageMap, StorageValue}, -// traits::Hooks, -// weights::Weight, -// }; -// use frame_system::{EventRecord, Pallet as System, Phase}; -// use sp_runtime::DispatchError; -// -// fn get_ready_for_events() { -// System::::set_block_number(1); -// System::::reset_events(); -// } -// -// fn send_regular_message(lane_id: LaneId) { -// get_ready_for_events(); -// -// let outbound_lane = outbound_lane::(lane_id); -// let message_nonce = outbound_lane.data().latest_generated_nonce + 1; -// let prev_enqueued_messages = outbound_lane.data().queued_messages().saturating_len(); -// let valid_message = Pallet::::validate_message(lane_id, ®ULAR_PAYLOAD) -// .expect("validate_message has failed"); -// let artifacts = Pallet::::send_message(valid_message); -// assert_eq!(artifacts.enqueued_messages, prev_enqueued_messages + 1); -// -// // check event with assigned nonce -// assert_eq!( -// System::::events(), -// vec![EventRecord { -// phase: Phase::Initialization, -// event: TestEvent::Messages(Event::MessageAccepted { -// lane_id, -// nonce: message_nonce -// }), -// topics: vec![], -// }], -// ); -// } -// -// fn receive_messages_delivery_proof() { -// System::::set_block_number(1); -// System::::reset_events(); -// -// assert_ok!(Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// TestMessagesDeliveryProof(Ok(( -// TEST_LANE_ID, -// InboundLaneData { -// last_confirmed_nonce: 1, -// relayers: vec![UnrewardedRelayer { -// relayer: 0, -// messages: DeliveredMessages::new(1), -// }] -// .into_iter() -// .collect(), -// }, -// ))), -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 1, -// messages_in_oldest_entry: 1, -// total_messages: 1, -// last_delivered_nonce: 1, -// }, -// )); -// -// assert_eq!( -// System::::events(), -// vec![EventRecord { -// phase: Phase::Initialization, -// event: TestEvent::Messages(Event::MessagesDelivered { -// lane_id: TEST_LANE_ID, -// messages: DeliveredMessages::new(1), -// }), -// topics: vec![], -// }], -// ); -// } -// -// #[test] -// fn pallet_rejects_transactions_if_halted() { -// run_test(|| { -// // send message first to be able to check that delivery_proof fails later -// send_regular_message(TEST_LANE_ID); -// -// PalletOperatingMode::::put(MessagesOperatingMode::Basic( -// BasicOperatingMode::Halted, -// )); -// -// assert_noop!( -// Pallet::::validate_message(TEST_LANE_ID, ®ULAR_PAYLOAD), -// Error::::NotOperatingNormally, -// ); -// -// assert_noop!( -// Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// Ok(vec![message(2, REGULAR_PAYLOAD)]).into(), -// 1, -// REGULAR_PAYLOAD.declared_weight, -// ), -// Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), -// ); -// -// assert_noop!( -// Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// TestMessagesDeliveryProof(Ok(( -// TEST_LANE_ID, -// InboundLaneData { -// last_confirmed_nonce: 1, -// relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] -// .into_iter() -// .collect(), -// }, -// ))), -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 1, -// messages_in_oldest_entry: 1, -// total_messages: 1, -// last_delivered_nonce: 1, -// }, -// ), -// Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), -// ); -// }); -// } -// -// #[test] -// fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { -// run_test(|| { -// // send message first to be able to check that delivery_proof fails later -// send_regular_message(TEST_LANE_ID); -// -// PalletOperatingMode::::put( -// MessagesOperatingMode::RejectingOutboundMessages, -// ); -// -// assert_noop!( -// Pallet::::validate_message(TEST_LANE_ID, ®ULAR_PAYLOAD), -// Error::::NotOperatingNormally, -// ); -// -// assert_ok!(Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), -// 1, -// REGULAR_PAYLOAD.declared_weight, -// ),); -// -// assert_ok!(Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// TestMessagesDeliveryProof(Ok(( -// TEST_LANE_ID, -// InboundLaneData { -// last_confirmed_nonce: 1, -// relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] -// .into_iter() -// .collect(), -// }, -// ))), -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 1, -// messages_in_oldest_entry: 1, -// total_messages: 1, -// last_delivered_nonce: 1, -// }, -// )); -// }); -// } -// -// #[test] -// fn send_message_works() { -// run_test(|| { -// send_regular_message(TEST_LANE_ID); -// }); -// } -// -// #[test] -// fn send_message_rejects_too_large_message() { -// run_test(|| { -// let mut message_payload = message_payload(1, 0); -// // the payload isn't simply extra, so it'll definitely overflow -// // `MAX_OUTBOUND_PAYLOAD_SIZE` if we add `MAX_OUTBOUND_PAYLOAD_SIZE` bytes to extra -// message_payload -// .extra -// .extend_from_slice(&[0u8; MAX_OUTBOUND_PAYLOAD_SIZE as usize]); -// assert_noop!( -// Pallet::::validate_message(TEST_LANE_ID, &message_payload.clone(),), -// Error::::MessageRejectedByPallet( -// VerificationError::MessageTooLarge -// ), -// ); -// -// // let's check that we're able to send `MAX_OUTBOUND_PAYLOAD_SIZE` messages -// while message_payload.encoded_size() as u32 > MAX_OUTBOUND_PAYLOAD_SIZE { -// message_payload.extra.pop(); -// } -// assert_eq!(message_payload.encoded_size() as u32, MAX_OUTBOUND_PAYLOAD_SIZE); -// -// let valid_message = -// Pallet::::validate_message(TEST_LANE_ID, &message_payload) -// .expect("validate_message has failed"); -// Pallet::::send_message(valid_message); -// }) -// } -// -// #[test] -// fn chain_verifier_rejects_invalid_message_in_send_message() { -// run_test(|| { -// // messages with this payload are rejected by target chain verifier -// assert_noop!( -// Pallet::::validate_message( -// TEST_LANE_ID, -// &PAYLOAD_REJECTED_BY_TARGET_CHAIN, -// ), -// Error::::MessageRejectedByChainVerifier(VerificationError::Other( -// mock::TEST_ERROR -// )), -// ); -// }); -// } -// -// #[test] -// fn receive_messages_proof_works() { -// run_test(|| { -// assert_ok!(Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), -// 1, -// REGULAR_PAYLOAD.declared_weight, -// )); -// -// assert_eq!(InboundLanes::::get(TEST_LANE_ID).0.last_delivered_nonce(), 1); -// -// assert!(TestDeliveryPayments::is_reward_paid(1)); -// }); -// } -// -// #[test] -// fn receive_messages_proof_updates_confirmed_message_nonce() { -// run_test(|| { -// // say we have received 10 messages && last confirmed message is 8 -// InboundLanes::::insert( -// TEST_LANE_ID, -// InboundLaneData { -// last_confirmed_nonce: 8, -// relayers: vec![ -// unrewarded_relayer(9, 9, TEST_RELAYER_A), -// unrewarded_relayer(10, 10, TEST_RELAYER_B), -// ] -// .into_iter() -// .collect(), -// }, -// ); -// assert_eq!( -// inbound_unrewarded_relayers_state(TEST_LANE_ID), -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 2, -// messages_in_oldest_entry: 1, -// total_messages: 2, -// last_delivered_nonce: 10, -// }, -// ); -// -// // message proof includes outbound lane state with latest confirmed message updated to 9 -// let mut message_proof: TestMessagesProof = -// Ok(vec![message(11, REGULAR_PAYLOAD)]).into(); -// message_proof.result.as_mut().unwrap()[0].1.lane_state = -// Some(OutboundLaneData { latest_received_nonce: 9, ..Default::default() }); -// -// assert_ok!(Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// message_proof, -// 1, -// REGULAR_PAYLOAD.declared_weight, -// )); -// -// assert_eq!( -// InboundLanes::::get(TEST_LANE_ID).0, -// InboundLaneData { -// last_confirmed_nonce: 9, -// relayers: vec![ -// unrewarded_relayer(10, 10, TEST_RELAYER_B), -// unrewarded_relayer(11, 11, TEST_RELAYER_A) -// ] -// .into_iter() -// .collect(), -// }, -// ); -// assert_eq!( -// inbound_unrewarded_relayers_state(TEST_LANE_ID), -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 2, -// messages_in_oldest_entry: 1, -// total_messages: 2, -// last_delivered_nonce: 11, -// }, -// ); -// }); -// } -// -// #[test] -// fn receive_messages_fails_if_dispatcher_is_inactive() { -// run_test(|| { -// TestMessageDispatch::deactivate(); -// assert_noop!( -// Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), -// 1, -// REGULAR_PAYLOAD.declared_weight, -// ), -// Error::::MessageDispatchInactive, -// ); -// }); -// } -// -// #[test] -// fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enough() { -// run_test(|| { -// let mut declared_weight = REGULAR_PAYLOAD.declared_weight; -// *declared_weight.ref_time_mut() -= 1; -// assert_noop!( -// Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), -// 1, -// declared_weight, -// ), -// Error::::InsufficientDispatchWeight -// ); -// assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); -// }); -// } -// -// #[test] -// fn receive_messages_proof_rejects_invalid_proof() { -// run_test(|| { -// assert_noop!( -// Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// Err(()).into(), -// 1, -// Weight::zero(), -// ), -// Error::::InvalidMessagesProof, -// ); -// }); -// } -// -// #[test] -// fn receive_messages_proof_rejects_proof_with_too_many_messages() { -// run_test(|| { -// assert_noop!( -// Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), -// u32::MAX, -// Weight::zero(), -// ), -// Error::::TooManyMessagesInTheProof, -// ); -// }); -// } -// -// #[test] -// fn receive_messages_delivery_proof_works() { -// run_test(|| { -// send_regular_message(TEST_LANE_ID); -// receive_messages_delivery_proof(); -// -// assert_eq!( -// OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, -// 1, -// ); -// }); -// } -// -// #[test] -// fn receive_messages_delivery_proof_rewards_relayers() { -// run_test(|| { -// send_regular_message(TEST_LANE_ID); -// send_regular_message(TEST_LANE_ID); -// -// // this reports delivery of message 1 => reward is paid to TEST_RELAYER_A -// let single_message_delivery_proof = TestMessagesDeliveryProof(Ok(( -// TEST_LANE_ID, -// InboundLaneData { -// relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into_iter().collect(), -// ..Default::default() -// }, -// ))); -// let single_message_delivery_proof_size = single_message_delivery_proof.size(); -// let result = Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// single_message_delivery_proof, -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 1, -// messages_in_oldest_entry: 1, -// total_messages: 1, -// last_delivered_nonce: 1, -// }, -// ); -// assert_ok!(result); -// assert_eq!( -// result.unwrap().actual_weight.unwrap(), -// TestWeightInfo::receive_messages_delivery_proof_weight( -// &PreComputedSize(single_message_delivery_proof_size as _), -// &UnrewardedRelayersState { -// unrewarded_relayer_entries: 1, -// total_messages: 1, -// ..Default::default() -// }, -// ) -// ); -// assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); -// assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); -// assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((TEST_LANE_ID, 1))); -// -// // this reports delivery of both message 1 and message 2 => reward is paid only to -// // TEST_RELAYER_B -// let two_messages_delivery_proof = TestMessagesDeliveryProof(Ok(( -// TEST_LANE_ID, -// InboundLaneData { -// relayers: vec![ -// unrewarded_relayer(1, 1, TEST_RELAYER_A), -// unrewarded_relayer(2, 2, TEST_RELAYER_B), -// ] -// .into_iter() -// .collect(), -// ..Default::default() -// }, -// ))); -// let two_messages_delivery_proof_size = two_messages_delivery_proof.size(); -// let result = Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// two_messages_delivery_proof, -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 2, -// messages_in_oldest_entry: 1, -// total_messages: 2, -// last_delivered_nonce: 2, -// }, -// ); -// assert_ok!(result); -// // even though the pre-dispatch weight was for two messages, the actual weight is -// // for single message only -// assert_eq!( -// result.unwrap().actual_weight.unwrap(), -// TestWeightInfo::receive_messages_delivery_proof_weight( -// &PreComputedSize(two_messages_delivery_proof_size as _), -// &UnrewardedRelayersState { -// unrewarded_relayer_entries: 1, -// total_messages: 1, -// ..Default::default() -// }, -// ) -// ); -// assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); -// assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); -// assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((TEST_LANE_ID, 0))); -// }); -// } -// -// #[test] -// fn receive_messages_delivery_proof_rejects_invalid_proof() { -// run_test(|| { -// assert_noop!( -// Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// TestMessagesDeliveryProof(Err(())), -// Default::default(), -// ), -// Error::::InvalidMessagesDeliveryProof, -// ); -// }); -// } -// -// #[test] -// fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_invalid() { -// run_test(|| { -// // when number of relayers entries is invalid -// assert_noop!( -// Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// TestMessagesDeliveryProof(Ok(( -// TEST_LANE_ID, -// InboundLaneData { -// relayers: vec![ -// unrewarded_relayer(1, 1, TEST_RELAYER_A), -// unrewarded_relayer(2, 2, TEST_RELAYER_B) -// ] -// .into_iter() -// .collect(), -// ..Default::default() -// } -// ))), -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 1, -// total_messages: 2, -// last_delivered_nonce: 2, -// ..Default::default() -// }, -// ), -// Error::::InvalidUnrewardedRelayersState, -// ); -// -// // when number of messages is invalid -// assert_noop!( -// Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// TestMessagesDeliveryProof(Ok(( -// TEST_LANE_ID, -// InboundLaneData { -// relayers: vec![ -// unrewarded_relayer(1, 1, TEST_RELAYER_A), -// unrewarded_relayer(2, 2, TEST_RELAYER_B) -// ] -// .into_iter() -// .collect(), -// ..Default::default() -// } -// ))), -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 2, -// total_messages: 1, -// last_delivered_nonce: 2, -// ..Default::default() -// }, -// ), -// Error::::InvalidUnrewardedRelayersState, -// ); -// -// // when last delivered nonce is invalid -// assert_noop!( -// Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// TestMessagesDeliveryProof(Ok(( -// TEST_LANE_ID, -// InboundLaneData { -// relayers: vec![ -// unrewarded_relayer(1, 1, TEST_RELAYER_A), -// unrewarded_relayer(2, 2, TEST_RELAYER_B) -// ] -// .into_iter() -// .collect(), -// ..Default::default() -// } -// ))), -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 2, -// total_messages: 2, -// last_delivered_nonce: 8, -// ..Default::default() -// }, -// ), -// Error::::InvalidUnrewardedRelayersState, -// ); -// }); -// } -// -// #[test] -// fn receive_messages_accepts_single_message_with_invalid_payload() { -// run_test(|| { -// let mut invalid_message = message(1, REGULAR_PAYLOAD); -// invalid_message.payload = Vec::new(); -// -// assert_ok!(Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// Ok(vec![invalid_message]).into(), -// 1, -// Weight::zero(), /* weight may be zero in this case (all messages are -// * improperly encoded) */ -// ),); -// -// assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 1,); -// }); -// } -// -// #[test] -// fn receive_messages_accepts_batch_with_message_with_invalid_payload() { -// run_test(|| { -// let mut invalid_message = message(2, REGULAR_PAYLOAD); -// invalid_message.payload = Vec::new(); -// -// assert_ok!(Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// Ok( -// vec![message(1, REGULAR_PAYLOAD), invalid_message, message(3, REGULAR_PAYLOAD),] -// ) -// .into(), -// 3, -// REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight, -// ),); -// -// assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 3,); -// }); -// } -// -// #[test] -// fn actual_dispatch_weight_does_not_overflow() { -// run_test(|| { -// let message1 = message(1, message_payload(0, u64::MAX / 2)); -// let message2 = message(2, message_payload(0, u64::MAX / 2)); -// let message3 = message(3, message_payload(0, u64::MAX / 2)); -// -// assert_noop!( -// Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// // this may cause overflow if source chain storage is invalid -// Ok(vec![message1, message2, message3]).into(), -// 3, -// Weight::MAX, -// ), -// Error::::InsufficientDispatchWeight -// ); -// assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); -// }); -// } -// -// #[test] -// fn ref_time_refund_from_receive_messages_proof_works() { -// run_test(|| { -// fn submit_with_unspent_weight( -// nonce: MessageNonce, -// unspent_weight: u64, -// ) -> (Weight, Weight) { -// let mut payload = REGULAR_PAYLOAD; -// *payload.dispatch_result.unspent_weight.ref_time_mut() = unspent_weight; -// let proof = Ok(vec![message(nonce, payload)]).into(); -// let messages_count = 1; -// let pre_dispatch_weight = -// ::WeightInfo::receive_messages_proof_weight( -// &proof, -// messages_count, -// REGULAR_PAYLOAD.declared_weight, -// ); -// let result = Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// proof, -// messages_count, -// REGULAR_PAYLOAD.declared_weight, -// ) -// .expect("delivery has failed"); -// let post_dispatch_weight = -// result.actual_weight.expect("receive_messages_proof always returns Some"); -// -// // message delivery transactions are never free -// assert_eq!(result.pays_fee, Pays::Yes); -// -// (pre_dispatch_weight, post_dispatch_weight) -// } -// -// // when dispatch is returning `unspent_weight < declared_weight` -// let (pre, post) = submit_with_unspent_weight(1, 1); -// assert_eq!(post.ref_time(), pre.ref_time() - 1); -// -// // when dispatch is returning `unspent_weight = declared_weight` -// let (pre, post) = -// submit_with_unspent_weight(2, REGULAR_PAYLOAD.declared_weight.ref_time()); -// assert_eq!( -// post.ref_time(), -// pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time() -// ); -// -// // when dispatch is returning `unspent_weight > declared_weight` -// let (pre, post) = -// submit_with_unspent_weight(3, REGULAR_PAYLOAD.declared_weight.ref_time() + 1); -// assert_eq!( -// post.ref_time(), -// pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time() -// ); -// -// // when there's no unspent weight -// let (pre, post) = submit_with_unspent_weight(4, 0); -// assert_eq!(post.ref_time(), pre.ref_time()); -// -// // when dispatch is returning `unspent_weight < declared_weight` -// let (pre, post) = submit_with_unspent_weight(5, 1); -// assert_eq!(post.ref_time(), pre.ref_time() - 1); -// }); -// } -// -// #[test] -// fn proof_size_refund_from_receive_messages_proof_works() { -// run_test(|| { -// let max_entries = crate::mock::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize; -// -// // if there's maximal number of unrewarded relayer entries at the inbound lane, then -// // `proof_size` is unchanged in post-dispatch weight -// let proof: TestMessagesProof = Ok(vec![message(101, REGULAR_PAYLOAD)]).into(); -// let messages_count = 1; -// let pre_dispatch_weight = -// ::WeightInfo::receive_messages_proof_weight( -// &proof, -// messages_count, -// REGULAR_PAYLOAD.declared_weight, -// ); -// InboundLanes::::insert( -// TEST_LANE_ID, -// StoredInboundLaneData(InboundLaneData { -// relayers: vec![ -// UnrewardedRelayer { -// relayer: 42, -// messages: DeliveredMessages { begin: 0, end: 100 } -// }; -// max_entries -// ] -// .into_iter() -// .collect(), -// last_confirmed_nonce: 0, -// }), -// ); -// let post_dispatch_weight = Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// proof.clone(), -// messages_count, -// REGULAR_PAYLOAD.declared_weight, -// ) -// .unwrap() -// .actual_weight -// .unwrap(); -// assert_eq!(post_dispatch_weight.proof_size(), pre_dispatch_weight.proof_size()); -// -// // if count of unrewarded relayer entries is less than maximal, then some `proof_size` -// // must be refunded -// InboundLanes::::insert( -// TEST_LANE_ID, -// StoredInboundLaneData(InboundLaneData { -// relayers: vec![ -// UnrewardedRelayer { -// relayer: 42, -// messages: DeliveredMessages { begin: 0, end: 100 } -// }; -// max_entries - 1 -// ] -// .into_iter() -// .collect(), -// last_confirmed_nonce: 0, -// }), -// ); -// let post_dispatch_weight = Pallet::::receive_messages_proof( -// RuntimeOrigin::signed(1), -// TEST_RELAYER_A, -// proof, -// messages_count, -// REGULAR_PAYLOAD.declared_weight, -// ) -// .unwrap() -// .actual_weight -// .unwrap(); -// assert!( -// post_dispatch_weight.proof_size() < pre_dispatch_weight.proof_size(), -// "Expected post-dispatch PoV {} to be less than pre-dispatch PoV {}", -// post_dispatch_weight.proof_size(), -// pre_dispatch_weight.proof_size(), -// ); -// }); -// } -// -// #[test] -// fn messages_delivered_callbacks_are_called() { -// run_test(|| { -// send_regular_message(TEST_LANE_ID); -// send_regular_message(TEST_LANE_ID); -// send_regular_message(TEST_LANE_ID); -// -// // messages 1+2 are confirmed in 1 tx, message 3 in a separate tx -// // dispatch of message 2 has failed -// let mut delivered_messages_1_and_2 = DeliveredMessages::new(1); -// delivered_messages_1_and_2.note_dispatched_message(); -// let messages_1_and_2_proof = Ok(( -// TEST_LANE_ID, -// InboundLaneData { -// last_confirmed_nonce: 0, -// relayers: vec![UnrewardedRelayer { -// relayer: 0, -// messages: delivered_messages_1_and_2.clone(), -// }] -// .into_iter() -// .collect(), -// }, -// )); -// let delivered_message_3 = DeliveredMessages::new(3); -// let messages_3_proof = Ok(( -// TEST_LANE_ID, -// InboundLaneData { -// last_confirmed_nonce: 0, -// relayers: vec![UnrewardedRelayer { relayer: 0, messages: delivered_message_3 }] -// .into_iter() -// .collect(), -// }, -// )); -// -// // first tx with messages 1+2 -// assert_ok!(Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// TestMessagesDeliveryProof(messages_1_and_2_proof), -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 1, -// messages_in_oldest_entry: 2, -// total_messages: 2, -// last_delivered_nonce: 2, -// }, -// )); -// // second tx with message 3 -// assert_ok!(Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// TestMessagesDeliveryProof(messages_3_proof), -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 1, -// messages_in_oldest_entry: 1, -// total_messages: 1, -// last_delivered_nonce: 3, -// }, -// )); -// }); -// } -// -// #[test] -// fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messages_than_expected( -// ) { -// run_test(|| { -// // send message first to be able to check that delivery_proof fails later -// send_regular_message(TEST_LANE_ID); -// -// // 1) InboundLaneData declares that the `last_confirmed_nonce` is 1; -// // 2) InboundLaneData has no entries => `InboundLaneData::last_delivered_nonce()` -// // returns `last_confirmed_nonce`; -// // 3) it means that we're going to confirm delivery of messages 1..=1; -// // 4) so the number of declared messages (see `UnrewardedRelayersState`) is `0` and -// // number of actually confirmed messages is `1`. -// assert_noop!( -// Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// TestMessagesDeliveryProof(Ok(( -// TEST_LANE_ID, -// InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() }, -// ))), -// UnrewardedRelayersState { last_delivered_nonce: 1, ..Default::default() }, -// ), -// Error::::ReceptionConfirmation( -// ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected -// ), -// ); -// }); -// } -// -// #[test] -// fn storage_keys_computed_properly() { -// assert_eq!( -// PalletOperatingMode::::storage_value_final_key().to_vec(), -// bp_messages::storage_keys::operating_mode_key("Messages").0, -// ); -// -// assert_eq!( -// OutboundMessages::::storage_map_final_key(MessageKey { -// lane_id: TEST_LANE_ID, -// nonce: 42 -// }), -// bp_messages::storage_keys::message_key("Messages", &TEST_LANE_ID, 42).0, -// ); -// -// assert_eq!( -// OutboundLanes::::storage_map_final_key(TEST_LANE_ID), -// bp_messages::storage_keys::outbound_lane_data_key("Messages", &TEST_LANE_ID).0, -// ); -// -// assert_eq!( -// InboundLanes::::storage_map_final_key(TEST_LANE_ID), -// bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0, -// ); -// } -// -// #[test] -// fn inbound_message_details_works() { -// run_test(|| { -// assert_eq!( -// Pallet::::inbound_message_data( -// TEST_LANE_ID, -// REGULAR_PAYLOAD.encode(), -// OutboundMessageDetails { nonce: 0, dispatch_weight: Weight::zero(), size: 0 }, -// ), -// InboundMessageDetails { dispatch_weight: REGULAR_PAYLOAD.declared_weight }, -// ); -// }); -// } -// -// #[test] -// fn on_idle_callback_respects_remaining_weight() { -// run_test(|| { -// send_regular_message(TEST_LANE_ID); -// send_regular_message(TEST_LANE_ID); -// send_regular_message(TEST_LANE_ID); -// send_regular_message(TEST_LANE_ID); -// -// assert_ok!(Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// TestMessagesDeliveryProof(Ok(( -// TEST_LANE_ID, -// InboundLaneData { -// last_confirmed_nonce: 4, -// relayers: vec![unrewarded_relayer(1, 4, TEST_RELAYER_A)] -// .into_iter() -// .collect(), -// }, -// ))), -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 1, -// messages_in_oldest_entry: 4, -// total_messages: 4, -// last_delivered_nonce: 4, -// }, -// )); -// -// // all 4 messages may be pruned now -// assert_eq!( -// outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, -// 4 -// ); -// assert_eq!( -// outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, -// 1 -// ); -// System::::set_block_number(2); -// -// // if passed wight is too low to do anything -// let dbw = DbWeight::get(); -// assert_eq!( -// Pallet::::on_idle(0, dbw.reads_writes(1, 1)), -// Weight::zero(), -// ); -// assert_eq!( -// outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, -// 1 -// ); -// -// // if passed wight is enough to prune single message -// assert_eq!( -// Pallet::::on_idle(0, dbw.reads_writes(1, 2)), -// dbw.reads_writes(1, 2), -// ); -// assert_eq!( -// outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, -// 2 -// ); -// -// // if passed wight is enough to prune two more messages -// assert_eq!( -// Pallet::::on_idle(0, dbw.reads_writes(1, 3)), -// dbw.reads_writes(1, 3), -// ); -// assert_eq!( -// outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, -// 4 -// ); -// -// // if passed wight is enough to prune many messages -// assert_eq!( -// Pallet::::on_idle(0, dbw.reads_writes(100, 100)), -// dbw.reads_writes(1, 2), -// ); -// assert_eq!( -// outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, -// 5 -// ); -// }); -// } -// -// #[test] -// fn on_idle_callback_is_rotating_lanes_to_prune() { -// run_test(|| { -// // send + receive confirmation for lane 1 -// send_regular_message(TEST_LANE_ID); -// receive_messages_delivery_proof(); -// // send + receive confirmation for lane 2 -// send_regular_message(TEST_LANE_ID_2); -// assert_ok!(Pallet::::receive_messages_delivery_proof( -// RuntimeOrigin::signed(1), -// TestMessagesDeliveryProof(Ok(( -// TEST_LANE_ID_2, -// InboundLaneData { -// last_confirmed_nonce: 1, -// relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] -// .into_iter() -// .collect(), -// }, -// ))), -// UnrewardedRelayersState { -// unrewarded_relayer_entries: 1, -// messages_in_oldest_entry: 1, -// total_messages: 1, -// last_delivered_nonce: 1, -// }, -// )); -// -// // nothing is pruned yet -// assert_eq!( -// outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, -// 1 -// ); -// assert_eq!( -// outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, -// 1 -// ); -// assert_eq!( -// outbound_lane::(TEST_LANE_ID_2).data().latest_received_nonce, -// 1 -// ); -// assert_eq!( -// outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, -// 1 -// ); -// -// // in block#2.on_idle lane messages of lane 1 are pruned -// let dbw = DbWeight::get(); -// System::::set_block_number(2); -// assert_eq!( -// Pallet::::on_idle(0, dbw.reads_writes(100, 100)), -// dbw.reads_writes(1, 2), -// ); -// assert_eq!( -// outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, -// 2 -// ); -// assert_eq!( -// outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, -// 1 -// ); -// -// // in block#3.on_idle lane messages of lane 2 are pruned -// System::::set_block_number(3); -// -// assert_eq!( -// Pallet::::on_idle(0, dbw.reads_writes(100, 100)), -// dbw.reads_writes(1, 2), -// ); -// assert_eq!( -// outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, -// 2 -// ); -// assert_eq!( -// outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, -// 2 -// ); -// }); -// } -// -// #[test] -// fn outbound_message_from_unconfigured_lane_is_rejected() { -// run_test(|| { -// assert_noop!( -// Pallet::::validate_message(TEST_LANE_ID_3, ®ULAR_PAYLOAD,), -// Error::::InactiveOutboundLane, -// ); -// }); -// } -// -// #[test] -// fn test_bridge_messages_call_is_correctly_defined() { -// let account_id = 1; -// let message_proof: TestMessagesProof = Ok(vec![message(1, REGULAR_PAYLOAD)]).into(); -// let message_delivery_proof = TestMessagesDeliveryProof(Ok(( -// TEST_LANE_ID, -// InboundLaneData { -// last_confirmed_nonce: 1, -// relayers: vec![UnrewardedRelayer { -// relayer: 0, -// messages: DeliveredMessages::new(1), -// }] -// .into_iter() -// .collect(), -// }, -// ))); -// let unrewarded_relayer_state = UnrewardedRelayersState { -// unrewarded_relayer_entries: 1, -// total_messages: 1, -// last_delivered_nonce: 1, -// ..Default::default() -// }; -// -// let direct_receive_messages_proof_call = Call::::receive_messages_proof { -// relayer_id_at_bridged_chain: account_id, -// proof: message_proof.clone(), -// messages_count: 1, -// dispatch_weight: REGULAR_PAYLOAD.declared_weight, -// }; -// let indirect_receive_messages_proof_call = BridgeMessagesCall::< -// AccountId, -// TestMessagesProof, -// TestMessagesDeliveryProof, -// >::receive_messages_proof { -// relayer_id_at_bridged_chain: account_id, -// proof: message_proof, -// messages_count: 1, -// dispatch_weight: REGULAR_PAYLOAD.declared_weight, -// }; -// assert_eq!( -// direct_receive_messages_proof_call.encode(), -// indirect_receive_messages_proof_call.encode() -// ); -// -// let direct_receive_messages_delivery_proof_call = -// Call::::receive_messages_delivery_proof { -// proof: message_delivery_proof.clone(), -// relayers_state: unrewarded_relayer_state.clone(), -// }; -// let indirect_receive_messages_delivery_proof_call = BridgeMessagesCall::< -// AccountId, -// TestMessagesProof, -// TestMessagesDeliveryProof, -// >::receive_messages_delivery_proof { -// proof: message_delivery_proof, -// relayers_state: unrewarded_relayer_state, -// }; -// assert_eq!( -// direct_receive_messages_delivery_proof_call.encode(), -// indirect_receive_messages_delivery_proof_call.encode() -// ); -// } -// -// generate_owned_bridge_module_tests!( -// MessagesOperatingMode::Basic(BasicOperatingMode::Normal), -// MessagesOperatingMode::Basic(BasicOperatingMode::Halted) -// ); -// -// #[test] -// fn inbound_storage_extra_proof_size_bytes_works() { -// fn relayer_entry() -> UnrewardedRelayer { -// UnrewardedRelayer { relayer: 42u64, messages: DeliveredMessages { begin: 0, end: 100 } } -// } -// -// fn storage(relayer_entries: usize) -> RuntimeInboundLaneStorage { -// RuntimeInboundLaneStorage { -// lane_id: Default::default(), -// cached_data: Some(InboundLaneData { -// relayers: vec![relayer_entry(); relayer_entries].into_iter().collect(), -// last_confirmed_nonce: 0, -// }), -// _phantom: Default::default(), -// } -// } -// -// let max_entries = crate::mock::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize; -// -// // when we have exactly `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers -// assert_eq!(storage(max_entries).extra_proof_size_bytes(), 0); -// -// // when we have less than `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers -// assert_eq!( -// storage(max_entries - 1).extra_proof_size_bytes(), -// relayer_entry().encode().len() as u64 -// ); -// assert_eq!( -// storage(max_entries - 2).extra_proof_size_bytes(), -// 2 * relayer_entry().encode().len() as u64 -// ); -// -// // when we have more than `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers -// // (shall not happen in practice) -// assert_eq!(storage(max_entries + 1).extra_proof_size_bytes(), 0); -// } -// -// #[test] -// fn maybe_outbound_lanes_count_returns_correct_value() { -// assert_eq!( -// MaybeOutboundLanesCount::::get(), -// Some(mock::ActiveOutboundLanes::get().len() as u32) -// ); -// } -// } diff --git a/bridges/modules/messages/src/outbound_lane.rs b/bridges/modules/messages/src/outbound_lane.rs index 0ad3e041800b1..f7beeb17fbf10 100644 --- a/bridges/modules/messages/src/outbound_lane.rs +++ b/bridges/modules/messages/src/outbound_lane.rs @@ -75,6 +75,7 @@ pub enum ReceptionConfirmationError { } /// Outbound messages lane. +#[derive(Debug, PartialEq, Eq)] pub struct OutboundLane { storage: S, } @@ -212,7 +213,7 @@ mod tests { relayers: &VecDeque>, ) -> Result, ReceptionConfirmationError> { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = outbound_lane::(TEST_LANE_ID).unwrap(); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); @@ -228,7 +229,7 @@ mod tests { #[test] fn send_message_works() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = outbound_lane::(TEST_LANE_ID).unwrap(); assert_eq!(lane.storage.data().latest_generated_nonce, 0); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); assert!(lane.storage.message(&1).is_some()); @@ -239,7 +240,7 @@ mod tests { #[test] fn confirm_delivery_works() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = outbound_lane::(TEST_LANE_ID).unwrap(); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3); @@ -259,7 +260,7 @@ mod tests { #[test] fn confirm_partial_delivery_works() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = outbound_lane::(TEST_LANE_ID).unwrap(); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3); @@ -288,7 +289,7 @@ mod tests { #[test] fn confirm_delivery_rejects_nonce_lesser_than_latest_received() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = outbound_lane::(TEST_LANE_ID).unwrap(); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); @@ -368,7 +369,7 @@ mod tests { #[test] fn confirm_delivery_detects_when_more_than_expected_messages_are_confirmed() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = outbound_lane::(TEST_LANE_ID).unwrap(); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); diff --git a/bridges/modules/messages/src/proofs.rs b/bridges/modules/messages/src/proofs.rs index f2bf1d1b08211..0e5edac635eb6 100644 --- a/bridges/modules/messages/src/proofs.rs +++ b/bridges/modules/messages/src/proofs.rs @@ -169,6 +169,7 @@ mod tests { }; use bp_header_chain::{HeaderChainError, StoredHeaderDataBuilder}; + use bp_messages::LaneState; use bp_runtime::{HeaderId, StorageProofError}; use codec::Encode; use sp_runtime::traits::Header; @@ -388,6 +389,7 @@ mod tests { using_messages_proof( 10, Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, @@ -428,6 +430,7 @@ mod tests { using_messages_proof( 0, Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, @@ -442,6 +445,7 @@ mod tests { TEST_LANE_ID, ProvedLaneMessages { lane_state: Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, @@ -460,6 +464,7 @@ mod tests { using_messages_proof( 1, Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, @@ -474,6 +479,7 @@ mod tests { TEST_LANE_ID, ProvedLaneMessages { lane_state: Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, diff --git a/bridges/modules/messages/src/tests/mock.rs b/bridges/modules/messages/src/tests/mock.rs index b646972cb18ea..fd76673f21e18 100644 --- a/bridges/modules/messages/src/tests/mock.rs +++ b/bridges/modules/messages/src/tests/mock.rs @@ -35,7 +35,7 @@ use bp_messages::{ DeliveryPayments, DispatchMessage, DispatchMessageData, FromBridgedChainMessagesProof, MessageDispatch, }, - ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, + ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, LaneState, Message, MessageKey, MessageNonce, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Size, StorageProofSize}; @@ -185,7 +185,6 @@ impl pallet_bridge_grandpa::Config for TestRuntime { parameter_types! { pub const MaxMessagesToPruneAtOnce: u64 = 10; pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID, TEST_LANE_ID_2]; } /// weights of messages pallet calls we use in tests. @@ -199,8 +198,6 @@ impl Config for TestRuntime { type BridgedChain = BridgedChain; type BridgedHeaderChain = BridgedChainGrandpa; - type ActiveOutboundLanes = ActiveOutboundLanes; - type OutboundPayload = TestPayload; type InboundPayload = TestPayload; @@ -268,11 +265,11 @@ pub const TEST_RELAYER_C: AccountId = 102; /// Lane that we're using in tests. pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 1]); -/// Secondary lane that we're using in tests. -pub const TEST_LANE_ID_2: LaneId = LaneId([0, 0, 0, 2]); +/// Lane that is completely unknown to our runtime. +pub const UNKNOWN_LANE_ID: LaneId = LaneId([0, 0, 0, 2]); -/// Inactive outbound lane. -pub const TEST_LANE_ID_3: LaneId = LaneId([0, 0, 0, 3]); +/// Lane that is registered, but it is closed. +pub const CLOSED_LANE_ID: LaneId = LaneId([0, 0, 0, 3]); /// Regular message payload. pub const REGULAR_PAYLOAD: TestPayload = message_payload(0, 50); @@ -438,7 +435,7 @@ pub fn unrewarded_relayer( /// Returns unrewarded relayers state at given lane. pub fn inbound_unrewarded_relayers_state(lane: bp_messages::LaneId) -> UnrewardedRelayersState { - let inbound_lane_data = crate::InboundLanes::::get(lane).0; + let inbound_lane_data = crate::InboundLanes::::get(lane).unwrap().0; UnrewardedRelayersState::from(&inbound_lane_data) } @@ -453,7 +450,19 @@ pub fn new_test_ext() -> sp_io::TestExternalities { /// Run pallet test. pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(test) + new_test_ext().execute_with(|| { + crate::InboundLanes::::insert(TEST_LANE_ID, InboundLaneData::opened()); + crate::OutboundLanes::::insert(TEST_LANE_ID, OutboundLaneData::opened()); + crate::InboundLanes::::insert( + CLOSED_LANE_ID, + InboundLaneData { state: LaneState::Closed, ..Default::default() }, + ); + crate::OutboundLanes::::insert( + CLOSED_LANE_ID, + OutboundLaneData { state: LaneState::Closed, ..Default::default() }, + ); + test() + }) } /// Prepare valid storage proof for given messages and insert appropriate header to the @@ -470,7 +479,7 @@ pub fn prepare_messages_proof( let nonces_start = messages.first().unwrap().key.nonce; let nonces_end = messages.last().unwrap().key.nonce; let (storage_root, storage) = prepare_messages_storage_proof::( - TEST_LANE_ID, + lane, nonces_start..=nonces_end, outbound_lane_data, StorageProofSize::Minimal(0), diff --git a/bridges/modules/messages/src/tests/pallet_tests.rs b/bridges/modules/messages/src/tests/pallet_tests.rs index 0b4715f6902de..613d551a3ac77 100644 --- a/bridges/modules/messages/src/tests/pallet_tests.rs +++ b/bridges/modules/messages/src/tests/pallet_tests.rs @@ -19,18 +19,17 @@ use crate::{ outbound_lane, outbound_lane::ReceptionConfirmationError, - tests::mock::{self, RuntimeEvent as TestEvent, *}, + tests::mock::{RuntimeEvent as TestEvent, *}, weights_ext::WeightInfoExt, - Call, Config, Error, Event, InboundLanes, MaybeOutboundLanesCount, OutboundLanes, - OutboundMessages, Pallet, PalletOperatingMode, PalletOwner, RuntimeInboundLaneStorage, - StoredInboundLaneData, + Call, Config, Error, Event, InboundLanes, OutboundLanes, OutboundMessages, Pallet, + PalletOperatingMode, PalletOwner, RuntimeInboundLaneStorage, StoredInboundLaneData, }; use bp_messages::{ source_chain::{FromBridgedChainMessagesDeliveryProof, MessagesBridge}, target_chain::FromBridgedChainMessagesProof, BridgeMessagesCall, ChainWithMessages, DeliveredMessages, InboundLaneData, - InboundMessageDetails, LaneId, MessageKey, MessageNonce, MessagesOperatingMode, + InboundMessageDetails, LaneId, LaneState, MessageKey, MessageNonce, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, UnrewardedRelayer, UnrewardedRelayersState, VerificationError, }; @@ -44,7 +43,6 @@ use frame_support::{ weights::Weight, }; use frame_system::{EventRecord, Pallet as System, Phase}; -use sp_core::Get; use sp_runtime::DispatchError; fn get_ready_for_events() { @@ -55,7 +53,7 @@ fn get_ready_for_events() { fn send_regular_message(lane_id: LaneId) { get_ready_for_events(); - let outbound_lane = outbound_lane::(lane_id); + let outbound_lane = outbound_lane::(lane_id).unwrap(); let message_nonce = outbound_lane.data().latest_generated_nonce + 1; let prev_enqueued_messages = outbound_lane.data().queued_messages().saturating_len(); let valid_message = Pallet::::validate_message(lane_id, ®ULAR_PAYLOAD) @@ -83,6 +81,7 @@ fn receive_messages_delivery_proof() { prepare_messages_delivery_proof( TEST_LANE_ID, InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![UnrewardedRelayer { relayer: 0, @@ -142,6 +141,7 @@ fn pallet_rejects_transactions_if_halted() { let delivery_proof = prepare_messages_delivery_proof( TEST_LANE_ID, InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(), }, @@ -190,6 +190,7 @@ fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { prepare_messages_delivery_proof( TEST_LANE_ID, InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(), }, @@ -250,7 +251,10 @@ fn receive_messages_proof_works() { REGULAR_PAYLOAD.declared_weight, )); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).0.last_delivered_nonce(), 1); + assert_eq!( + InboundLanes::::get(TEST_LANE_ID).unwrap().0.last_delivered_nonce(), + 1 + ); assert!(TestDeliveryPayments::is_reward_paid(1)); }); @@ -263,6 +267,7 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { InboundLanes::::insert( TEST_LANE_ID, InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 8, relayers: vec![ unrewarded_relayer(9, 9, TEST_RELAYER_A), @@ -294,8 +299,9 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { )); assert_eq!( - InboundLanes::::get(TEST_LANE_ID).0, + InboundLanes::::get(TEST_LANE_ID).unwrap().0, InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 9, relayers: vec![ unrewarded_relayer(10, 10, TEST_RELAYER_B), @@ -333,7 +339,10 @@ fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enou ), Error::::InsufficientDispatchWeight ); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); + assert_eq!( + InboundLanes::::get(TEST_LANE_ID).unwrap().last_delivered_nonce(), + 0 + ); }); } @@ -379,7 +388,12 @@ fn receive_messages_delivery_proof_works() { send_regular_message(TEST_LANE_ID); receive_messages_delivery_proof(); - assert_eq!(OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, 1,); + assert_eq!( + OutboundLanes::::get(TEST_LANE_ID) + .unwrap() + .latest_received_nonce, + 1, + ); }); } @@ -582,7 +596,10 @@ fn receive_messages_accepts_single_message_with_invalid_payload() { * improperly encoded) */ ),); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 1,); + assert_eq!( + InboundLanes::::get(TEST_LANE_ID).unwrap().last_delivered_nonce(), + 1, + ); }); } @@ -603,7 +620,10 @@ fn receive_messages_accepts_batch_with_message_with_invalid_payload() { REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight, ),); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 3,); + assert_eq!( + InboundLanes::::get(TEST_LANE_ID).unwrap().last_delivered_nonce(), + 3, + ); }); } @@ -626,7 +646,10 @@ fn actual_dispatch_weight_does_not_overlow() { ), Error::::InsufficientDispatchWeight ); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); + assert_eq!( + InboundLanes::::get(TEST_LANE_ID).unwrap().last_delivered_nonce(), + 0 + ); }); } @@ -705,6 +728,7 @@ fn proof_size_refund_from_receive_messages_proof_works() { InboundLanes::::insert( TEST_LANE_ID, StoredInboundLaneData(InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: 42, @@ -733,6 +757,7 @@ fn proof_size_refund_from_receive_messages_proof_works() { InboundLanes::::insert( TEST_LANE_ID, StoredInboundLaneData(InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: 42, @@ -778,7 +803,11 @@ fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messa // actually confirmed messages is `1`. let proof = prepare_messages_delivery_proof( TEST_LANE_ID, - InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() }, + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: 1, + relayers: Default::default(), + }, ); assert_noop!( Pallet::::receive_messages_delivery_proof( @@ -833,16 +862,6 @@ fn inbound_message_details_works() { }); } -#[test] -fn outbound_message_from_unconfigured_lane_is_rejected() { - run_test(|| { - assert_noop!( - Pallet::::validate_message(TEST_LANE_ID_3, ®ULAR_PAYLOAD,), - Error::::InactiveOutboundLane, - ); - }); -} - #[test] fn test_bridge_messages_call_is_correctly_defined() { run_test(|| { @@ -851,6 +870,7 @@ fn test_bridge_messages_call_is_correctly_defined() { let message_delivery_proof = prepare_messages_delivery_proof( TEST_LANE_ID, InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![UnrewardedRelayer { relayer: 0, @@ -921,10 +941,11 @@ fn inbound_storage_extra_proof_size_bytes_works() { fn storage(relayer_entries: usize) -> RuntimeInboundLaneStorage { RuntimeInboundLaneStorage { lane_id: Default::default(), - cached_data: Some(InboundLaneData { + cached_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![relayer_entry(); relayer_entries].into(), last_confirmed_nonce: 0, - }), + }, _phantom: Default::default(), } } @@ -950,9 +971,100 @@ fn inbound_storage_extra_proof_size_bytes_works() { } #[test] -fn maybe_outbound_lanes_count_returns_correct_value() { - assert_eq!( - MaybeOutboundLanesCount::::get(), - Some(mock::ActiveOutboundLanes::get().len() as u32) - ); +fn send_messages_fails_if_outbound_lane_is_not_opened() { + run_test(|| { + assert_noop!( + Pallet::::validate_message(UNKNOWN_LANE_ID, ®ULAR_PAYLOAD), + Error::::UnknownOutboundLane, + ); + + assert_noop!( + Pallet::::validate_message(CLOSED_LANE_ID, ®ULAR_PAYLOAD), + Error::::ClosedOutboundLane, + ); + }); +} + +#[test] +fn receive_messages_proof_fails_if_inbound_lane_is_not_opened() { + run_test(|| { + let mut message = message(1, REGULAR_PAYLOAD); + message.key.lane_id = UNKNOWN_LANE_ID; + let proof = prepare_messages_proof(vec![message.clone()], None); + + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof, + 1, + REGULAR_PAYLOAD.declared_weight, + ), + Error::::UnknownInboundLane, + ); + + message.key.lane_id = CLOSED_LANE_ID; + let proof = prepare_messages_proof(vec![message], None); + + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof, + 1, + REGULAR_PAYLOAD.declared_weight, + ), + Error::::ClosedInboundLane, + ); + }); +} + +#[test] +fn receive_messages_delivery_proof_fails_if_outbound_lane_is_unknown() { + run_test(|| { + let make_proof = |lane: LaneId| { + prepare_messages_delivery_proof( + lane, + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: 1, + relayers: vec![UnrewardedRelayer { + relayer: 0, + messages: DeliveredMessages::new(1), + }] + .into(), + }, + ) + }; + + let proof = make_proof(UNKNOWN_LANE_ID); + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + proof, + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }, + ), + Error::::UnknownOutboundLane, + ); + + let proof = make_proof(CLOSED_LANE_ID); + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + proof, + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }, + ), + Error::::ClosedOutboundLane, + ); + }); } diff --git a/bridges/modules/xcm-bridge-hub/src/exporter.rs b/bridges/modules/xcm-bridge-hub/src/exporter.rs index 94ec8b5f106fd..2ed8c46e7890c 100644 --- a/bridges/modules/xcm-bridge-hub/src/exporter.rs +++ b/bridges/modules/xcm-bridge-hub/src/exporter.rs @@ -77,7 +77,7 @@ where let bridge_message = MessagesPallet::::validate_message(sender_and_lane.lane, &blob) .map_err(|e| { - log::debug!( + log::error!( target: LOG_TARGET, "XCM message {:?} cannot be exported because of bridge error {:?} on bridge {:?}", id, @@ -131,7 +131,8 @@ impl HaulBlob for DummyHaulBlob { mod tests { use super::*; use crate::mock::*; - use frame_support::assert_ok; + use bp_messages::{LaneState, OutboundLaneData}; + use frame_support::{__private::sp_tracing, assert_ok}; use xcm_executor::traits::export_xcm; fn universal_source() -> InteriorLocation { @@ -145,6 +146,11 @@ mod tests { #[test] fn export_works() { run_test(|| { + sp_tracing::try_init_simple(); + pallet_bridge_messages::OutboundLanes::::insert( + TEST_LANE_ID, + OutboundLaneData { state: LaneState::Opened, ..Default::default() }, + ); assert_ok!(export_xcm::( BridgedRelayNetwork::get(), 0, @@ -185,8 +191,14 @@ mod tests { #[test] fn exporter_computes_correct_lane_id() { run_test(|| { + sp_tracing::try_init_simple(); let expected_lane_id = TEST_LANE_ID; + pallet_bridge_messages::OutboundLanes::::insert( + expected_lane_id, + OutboundLaneData { state: LaneState::Opened, ..Default::default() }, + ); + assert_eq!( XcmOverBridge::validate( BridgedRelayNetwork::get(), diff --git a/bridges/modules/xcm-bridge-hub/src/mock.rs b/bridges/modules/xcm-bridge-hub/src/mock.rs index 6f022533b0a8e..c988c8167a328 100644 --- a/bridges/modules/xcm-bridge-hub/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub/src/mock.rs @@ -85,7 +85,6 @@ impl pallet_bridge_messages::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type WeightInfo = TestMessagesWeights; - type ActiveOutboundLanes = ActiveOutboundLanes; type OutboundPayload = Vec; type InboundPayload = Vec; type DeliveryPayments = (); diff --git a/bridges/primitives/messages/src/lib.rs b/bridges/primitives/messages/src/lib.rs index 61d30d2aa74da..6a86563cd9b14 100644 --- a/bridges/primitives/messages/src/lib.rs +++ b/bridges/primitives/messages/src/lib.rs @@ -169,6 +169,7 @@ impl OperatingMode for MessagesOperatingMode { #[derive( Clone, Copy, Decode, Default, Encode, Eq, Ord, PartialOrd, PartialEq, TypeInfo, MaxEncodedLen, )] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct LaneId(pub [u8; 4]); impl core::fmt::Debug for LaneId { @@ -187,6 +188,21 @@ impl TypeId for LaneId { const TYPE_ID: [u8; 4] = *b"blan"; } +/// Lane state. +#[derive(Clone, Copy, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub enum LaneState { + /// Lane is closed and all attempts to send/receive messages to/from this lane + /// will fail. + /// + /// Keep in mind that the lane has two ends and the state of the same lane at + /// its ends may be different. Those who are controlling/serving the lane + /// and/or sending messages over the lane, have to coordinate their actions on + /// both ends to make sure that lane is operating smoothly on both ends. + Closed, + /// Lane is opened and messages may be sent/received over it. + Opened, +} + /// Message nonce. Valid messages will never have 0 nonce. pub type MessageNonce = u64; @@ -217,6 +233,11 @@ pub struct Message { /// Inbound lane data. #[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct InboundLaneData { + /// Inbound lane state. + /// + /// If state is `Closed`, then all attempts to deliver messages to this end will fail. + pub state: LaneState, + /// Identifiers of relayers and messages that they have delivered to this lane (ordered by /// message nonce). /// @@ -249,11 +270,20 @@ pub struct InboundLaneData { impl Default for InboundLaneData { fn default() -> Self { - InboundLaneData { relayers: VecDeque::new(), last_confirmed_nonce: 0 } + InboundLaneData { + state: LaneState::Closed, + relayers: VecDeque::new(), + last_confirmed_nonce: 0, + } } } impl InboundLaneData { + /// Returns default inbound lane data with opened state. + pub fn opened() -> Self { + InboundLaneData { state: LaneState::Opened, ..Default::default() } + } + /// Returns approximate size of the struct, given a number of entries in the `relayers` set and /// size of each entry. /// @@ -452,6 +482,10 @@ impl From<&InboundLaneData> for UnrewardedRelayersState { /// Outbound lane data. #[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] pub struct OutboundLaneData { + /// Lane state. + /// + /// If state is `Closed`, then all attempts to send messages messages at this end will fail. + pub state: LaneState, /// Nonce of the oldest message that we haven't yet pruned. May point to not-yet-generated /// message if all sent messages are already pruned. pub oldest_unpruned_nonce: MessageNonce, @@ -461,9 +495,17 @@ pub struct OutboundLaneData { pub latest_generated_nonce: MessageNonce, } +impl OutboundLaneData { + /// Returns default outbound lane data with opened state. + pub fn opened() -> Self { + OutboundLaneData { state: LaneState::Opened, ..Default::default() } + } +} + impl Default for OutboundLaneData { fn default() -> Self { OutboundLaneData { + state: LaneState::Closed, // it is 1 because we're pruning everything in [oldest_unpruned_nonce; // latest_received_nonce] oldest_unpruned_nonce: 1, @@ -564,9 +606,16 @@ pub enum VerificationError { mod tests { use super::*; + #[test] + fn lane_is_closed_by_default() { + assert_eq!(InboundLaneData::<()>::default().state, LaneState::Closed); + assert_eq!(OutboundLaneData::default().state, LaneState::Closed); + } + #[test] fn total_unrewarded_messages_does_not_overflow() { let lane_data = InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: 1, messages: DeliveredMessages::new(0) }, UnrewardedRelayer { @@ -594,6 +643,7 @@ mod tests { for (relayer_entries, messages_count) in test_cases { let expected_size = InboundLaneData::::encoded_size_hint(relayer_entries as _); let actual_size = InboundLaneData { + state: LaneState::Opened, relayers: (1u8..=relayer_entries) .map(|i| UnrewardedRelayer { relayer: i,