diff --git a/Cargo.lock b/Cargo.lock index c2dc042ab89..7abd46c0b24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5805,6 +5805,7 @@ dependencies = [ name = "millau-bridge-node" version = "0.1.0" dependencies = [ + "bp-messages", "clap 4.3.4", "frame-benchmarking", "frame-benchmarking-cli", @@ -10102,6 +10103,7 @@ dependencies = [ name = "rialto-bridge-node" version = "0.1.0" dependencies = [ + "bp-messages", "clap 4.3.4", "frame-benchmarking", "frame-benchmarking-cli", @@ -10129,6 +10131,7 @@ dependencies = [ name = "rialto-parachain-collator" version = "0.1.0" dependencies = [ + "bp-messages", "clap 4.3.4", "cumulus-client-cli", "cumulus-client-consensus-aura", diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml index ed4b479d72d..db5f83ac7fa 100644 --- a/bin/millau/node/Cargo.toml +++ b/bin/millau/node/Cargo.toml @@ -15,6 +15,7 @@ serde_json = "1.0.97" # Bridge dependencies +bp-messages = { path = "../../../primitives/messages" } millau-runtime = { path = "../runtime" } # Substrate Dependencies diff --git a/bin/millau/node/src/chain_spec.rs b/bin/millau/node/src/chain_spec.rs index 8669ca92cc8..5d5be3c3c3d 100644 --- a/bin/millau/node/src/chain_spec.rs +++ b/bin/millau/node/src/chain_spec.rs @@ -222,12 +222,14 @@ fn testnet_genesis( }, bridge_rialto_messages: BridgeRialtoMessagesConfig { owner: Some(get_account_id_from_seed::(RIALTO_MESSAGES_PALLET_OWNER)), + opened_lanes: vec![bp_messages::LaneId::default()], ..Default::default() }, bridge_rialto_parachain_messages: BridgeRialtoParachainMessagesConfig { owner: Some(get_account_id_from_seed::( RIALTO_PARACHAIN_MESSAGES_PALLET_OWNER, )), + opened_lanes: vec![bp_messages::LaneId::default()], ..Default::default() }, xcm_pallet: Default::default(), diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index 626061508e8..05ae9e5116f 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -428,8 +428,6 @@ impl pallet_shift_session_manager::Config for Runtime {} parameter_types! { pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; pub const RootAccountForPayments: Option = None; - pub RialtoActiveOutboundLanes: &'static [bp_messages::LaneId] = &[rialto_messages::XCM_LANE]; - pub RialtoParachainActiveOutboundLanes: &'static [bp_messages::LaneId] = &[rialto_parachain_messages::XCM_LANE]; } /// Instance of the messages pallet used to relay messages to/from Rialto chain. @@ -443,8 +441,6 @@ impl pallet_bridge_messages::Config for Runtime { type BridgedChain = bp_rialto::Rialto; type BridgedHeaderChain = BridgeRialtoGrandpa; - type ActiveOutboundLanes = RialtoActiveOutboundLanes; - type OutboundPayload = bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; type InboundPayload = bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; @@ -473,8 +469,6 @@ impl pallet_bridge_messages::Config for Run bp_rialto_parachain::RialtoParachain, >; - type ActiveOutboundLanes = RialtoParachainActiveOutboundLanes; - type OutboundPayload = bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; type InboundPayload = bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; diff --git a/bin/millau/runtime/src/xcm_config.rs b/bin/millau/runtime/src/xcm_config.rs index d6b763b116c..75169d8b970 100644 --- a/bin/millau/runtime/src/xcm_config.rs +++ b/bin/millau/runtime/src/xcm_config.rs @@ -242,7 +242,7 @@ mod tests { }; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - LaneId, MessageKey, + LaneId, MessageKey, OutboundLaneData, }; use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; use codec::Encode; @@ -268,10 +268,15 @@ mod tests { fn xcm_messages_to_rialto_are_sent_using_bridge_exporter() { new_test_ext().execute_with(|| { // ensure that the there are no messages queued + OutboundLanes::::insert( + crate::rialto_messages::XCM_LANE, + OutboundLaneData::opened(), + ); assert_eq!( OutboundLanes::::get( crate::rialto_messages::XCM_LANE ) + .unwrap() .latest_generated_nonce, 0, ); @@ -292,6 +297,7 @@ mod tests { OutboundLanes::::get( crate::rialto_messages::XCM_LANE ) + .unwrap() .latest_generated_nonce, 1, ); @@ -302,10 +308,15 @@ mod tests { fn xcm_messages_to_rialto_parachain_are_sent_using_bridge_exporter() { new_test_ext().execute_with(|| { // ensure that the there are no messages queued + OutboundLanes::::insert( + crate::rialto_parachain_messages::XCM_LANE, + OutboundLaneData::opened(), + ); assert_eq!( OutboundLanes::::get( crate::rialto_parachain_messages::XCM_LANE ) + .unwrap() .latest_generated_nonce, 0, ); @@ -326,6 +337,7 @@ mod tests { OutboundLanes::::get( crate::rialto_parachain_messages::XCM_LANE ) + .unwrap() .latest_generated_nonce, 1, ); diff --git a/bin/rialto-parachain/node/Cargo.toml b/bin/rialto-parachain/node/Cargo.toml index eeb18b5767d..90331a22ea4 100644 --- a/bin/rialto-parachain/node/Cargo.toml +++ b/bin/rialto-parachain/node/Cargo.toml @@ -26,6 +26,8 @@ serde = { version = '1.0', features = ['derive'] } jsonrpsee = { version = "0.16.2", features = ["server"] } # Local Dependencies + +bp-messages = { path = "../../../primitives/messages" } rialto-parachain-runtime = { path = '../runtime' } # Substrate Dependencies diff --git a/bin/rialto-parachain/node/src/chain_spec.rs b/bin/rialto-parachain/node/src/chain_spec.rs index bfce4f717c6..34451d6ed6d 100644 --- a/bin/rialto-parachain/node/src/chain_spec.rs +++ b/bin/rialto-parachain/node/src/chain_spec.rs @@ -192,6 +192,7 @@ fn testnet_genesis( aura_ext: Default::default(), bridge_millau_messages: BridgeMillauMessagesConfig { owner: Some(get_account_id_from_seed::(MILLAU_MESSAGES_PALLET_OWNER)), + opened_lanes: vec![bp_messages::LaneId::default()], ..Default::default() }, } diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs index e9857a70b89..48d05bf1831 100644 --- a/bin/rialto-parachain/runtime/src/lib.rs +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -552,7 +552,6 @@ impl pallet_bridge_grandpa::Config for Runtime { parameter_types! { pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; pub const RootAccountForPayments: Option = None; - pub ActiveOutboundLanes: &'static [bp_messages::LaneId] = &[millau_messages::XCM_LANE]; } /// Instance of the messages pallet used to relay messages to/from Millau chain. @@ -566,8 +565,6 @@ impl pallet_bridge_messages::Config for Runtime { type BridgedChain = bp_millau::Millau; type BridgedHeaderChain = BridgeMillauGrandpa; - type ActiveOutboundLanes = ActiveOutboundLanes; - type OutboundPayload = bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; type InboundPayload = bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; @@ -845,7 +842,7 @@ mod tests { use crate::millau_messages::{FromMillauMessageDispatch, XCM_LANE}; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - LaneId, MessageKey, + LaneId, MessageKey, OutboundLaneData, }; use bp_runtime::Chain; use bridge_runtime_common::{ @@ -883,8 +880,13 @@ mod tests { fn xcm_messages_to_millau_are_sent_using_bridge_exporter() { new_test_ext().execute_with(|| { // ensure that the there are no messages queued + OutboundLanes::::insert( + XCM_LANE, + OutboundLaneData::opened(), + ); assert_eq!( OutboundLanes::::get(XCM_LANE) + .unwrap() .latest_generated_nonce, 0, ); @@ -903,6 +905,7 @@ mod tests { // ensure that the message has been queued assert_eq!( OutboundLanes::::get(XCM_LANE) + .unwrap() .latest_generated_nonce, 1, ); diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml index fa648483c41..bf0f145b6e4 100644 --- a/bin/rialto/node/Cargo.toml +++ b/bin/rialto/node/Cargo.toml @@ -14,6 +14,7 @@ serde_json = "1.0.97" # Bridge dependencies +bp-messages = { path = "../../../primitives/messages" } rialto-runtime = { path = "../runtime" } # Substrate Dependencies diff --git a/bin/rialto/node/src/chain_spec.rs b/bin/rialto/node/src/chain_spec.rs index c5364019a0e..19070aaf206 100644 --- a/bin/rialto/node/src/chain_spec.rs +++ b/bin/rialto/node/src/chain_spec.rs @@ -277,6 +277,7 @@ fn testnet_genesis( paras: Default::default(), bridge_millau_messages: BridgeMillauMessagesConfig { owner: Some(get_account_id_from_seed::(MILLAU_MESSAGES_PALLET_OWNER)), + opened_lanes: vec![bp_messages::LaneId::default()], ..Default::default() }, xcm_pallet: Default::default(), diff --git a/bin/rialto/runtime/src/lib.rs b/bin/rialto/runtime/src/lib.rs index c86fa1e945b..70d6926f773 100644 --- a/bin/rialto/runtime/src/lib.rs +++ b/bin/rialto/runtime/src/lib.rs @@ -408,7 +408,6 @@ impl pallet_shift_session_manager::Config for Runtime {} parameter_types! { pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; pub const RootAccountForPayments: Option = None; - pub ActiveOutboundLanes: &'static [bp_messages::LaneId] = &[millau_messages::XCM_LANE]; } /// Instance of the messages pallet used to relay messages to/from Millau chain. @@ -422,8 +421,6 @@ impl pallet_bridge_messages::Config for Runtime { type BridgedChain = bp_millau::Millau; type BridgedHeaderChain = BridgeMillauGrandpa; - type ActiveOutboundLanes = ActiveOutboundLanes; - type OutboundPayload = bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; type InboundPayload = bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; diff --git a/bin/rialto/runtime/src/xcm_config.rs b/bin/rialto/runtime/src/xcm_config.rs index cee246ad67a..5ae6817fc2a 100644 --- a/bin/rialto/runtime/src/xcm_config.rs +++ b/bin/rialto/runtime/src/xcm_config.rs @@ -197,7 +197,7 @@ mod tests { }; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - LaneId, MessageKey, + LaneId, MessageKey, OutboundLaneData, }; use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; use codec::Encode; @@ -223,8 +223,13 @@ mod tests { fn xcm_messages_to_millau_are_sent_using_bridge_exporter() { new_test_ext().execute_with(|| { // ensure that the there are no messages queued + OutboundLanes::::insert( + XCM_LANE, + OutboundLaneData::opened(), + ); assert_eq!( OutboundLanes::::get(XCM_LANE) + .unwrap() .latest_generated_nonce, 0, ); @@ -243,6 +248,7 @@ mod tests { // ensure that the message has been queued assert_eq!( OutboundLanes::::get(XCM_LANE) + .unwrap() .latest_generated_nonce, 1, ); diff --git a/bin/runtime-common/src/integrity.rs b/bin/runtime-common/src/integrity.rs index 5335fdab569..95215f1a6a0 100644 --- a/bin/runtime-common/src/integrity.rs +++ b/bin/runtime-common/src/integrity.rs @@ -166,12 +166,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/bin/runtime-common/src/messages_call_ext.rs b/bin/runtime-common/src/messages_call_ext.rs index b505f7bea35..1314e0c1cb8 100644 --- a/bin/runtime-common/src/messages_call_ext.rs +++ b/bin/runtime-common/src/messages_call_ext.rs @@ -135,7 +135,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); @@ -151,7 +154,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() }, } @@ -194,7 +200,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 { @@ -218,7 +224,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, @@ -312,14 +319,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(), @@ -327,14 +336,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 { @@ -343,15 +352,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, + }, ); } @@ -368,7 +381,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, }, @@ -378,9 +391,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(); @@ -390,7 +417,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(); @@ -400,7 +427,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(); @@ -410,7 +437,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(); @@ -421,7 +448,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)); }); @@ -430,7 +457,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)); @@ -440,7 +467,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, @@ -451,7 +478,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(); @@ -461,8 +488,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, @@ -476,7 +504,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, @@ -490,7 +518,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(); @@ -500,7 +528,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(); @@ -510,7 +538,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)); @@ -519,7 +547,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(); @@ -534,7 +562,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` }, @@ -553,7 +581,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)); }); @@ -562,14 +590,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)); }); @@ -577,7 +605,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)); }); @@ -585,7 +613,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)); }); @@ -594,7 +622,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` }), @@ -603,7 +631,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)); }); @@ -611,7 +639,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)); }); @@ -619,7 +647,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/bin/runtime-common/src/mock.rs b/bin/runtime-common/src/mock.rs index b2516dbeda0..b67543ad145 100644 --- a/bin/runtime-common/src/mock.rs +++ b/bin/runtime-common/src/mock.rs @@ -119,7 +119,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 }; @@ -219,7 +218,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/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs index 91e0b77c3e7..e3cb56b45d0 100644 --- a/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bin/runtime-common/src/refund_relayer_extension.rs @@ -806,6 +806,7 @@ mod tests { nonces_start: pallet_bridge_messages::InboundLanes::::get( TEST_LANE_ID, ) + .unwrap() .last_delivered_nonce() + 1, nonces_end: best_message, diff --git a/modules/messages/README.md b/modules/messages/README.md index 9903968bd28..0168df6e508 100644 --- a/modules/messages/README.md +++ b/modules/messages/README.md @@ -155,10 +155,9 @@ implements all required traits and will simply reject all transactions, related ### 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/modules/messages/src/benchmarking.rs b/modules/messages/src/benchmarking.rs index 74fb685f695..1d39b49b5b9 100644 --- a/modules/messages/src/benchmarking.rs +++ b/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(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/modules/messages/src/inbound_lane.rs b/modules/messages/src/inbound_lane.rs index 51569169918..5df48cad0ba 100644 --- a/modules/messages/src/inbound_lane.rs +++ b/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, ) -> ReceivalResult { - 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 ReceivalResult::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 { ), ReceivalResult::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 { ReceivalResult::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/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index 2f976f324e3..95c021c8a22 100644 --- a/modules/messages/src/lib.rs +++ b/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::{ @@ -114,9 +114,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. @@ -243,7 +240,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 @@ -260,7 +257,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()), ); } } @@ -371,7 +368,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( @@ -444,8 +441,14 @@ pub mod pallet { pub enum Error { /// Pallet is not in Normal operating mode. NotOperatingNormally, - /// The outbound lane is inactive. - InactiveOutboundLane, + /// 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 lane verifier. @@ -492,10 +495,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] @@ -503,9 +509,7 @@ pub mod pallet { Hasher = Blake2_128Concat, Key = LaneId, Value = OutboundLaneData, - QueryKind = ValueQuery, - OnEmpty = GetDefault, - MaxValues = MaybeOutboundLanesCount, + QueryKind = OptionQuery, >; /// All queued outbound messages. @@ -520,6 +524,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, } @@ -531,6 +537,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()); + } } } @@ -558,17 +569,8 @@ pub mod pallet { /// Return inbound lane data. pub fn inbound_lane_data( lane: LaneId, - ) -> InboundLaneData>> { - InboundLanes::::get(lane).0 - } - } - - /// Get-parameter that returns number of active outbound lanes that the pallet maintains. - pub struct MaybeOutboundLanesCount(PhantomData<(T, I)>); - - impl, I: 'static> Get> for MaybeOutboundLanesCount { - fn get() -> Option { - Some(T::ActiveOutboundLanes::get().len() as u32) + ) -> Option>>> { + InboundLanes::::get(lane).map(|lane| lane.0) } } } @@ -609,11 +611,8 @@ fn send_message, I: 'static>( // we can't accept any messages if the pallet is halted ensure_normal_operating_mode::()?; - // let's check if outbound lane is active - ensure!(T::ActiveOutboundLanes::get().contains(&lane_id), Error::::InactiveOutboundLane,); - // finally, save message in outbound storage and emit event - let mut lane = outbound_lane::(lane_id); + let mut lane = outbound_lane::(lane_id)?; let encoded_payload = payload.encode(); let encoded_payload_len = encoded_payload.len(); @@ -650,28 +649,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(), + }) } } @@ -686,7 +692,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); @@ -709,20 +715,12 @@ 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)) } } @@ -730,19 +728,31 @@ impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage< /// Runtime outbound lane storage. 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 { fn id(&self) -> LaneId { self.lane_id } 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) } diff --git a/modules/messages/src/outbound_lane.rs b/modules/messages/src/outbound_lane.rs index 44ee39afbe3..92a356a0a66 100644 --- a/modules/messages/src/outbound_lane.rs +++ b/modules/messages/src/outbound_lane.rs @@ -219,7 +219,7 @@ mod tests { relayers: &VecDeque>, ) -> Result, ReceivalConfirmationError> { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = outbound_lane::(TEST_LANE_ID).unwrap(); assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD))); assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD))); assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD))); @@ -235,7 +235,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)), Ok(1)); assert!(lane.storage.message(&1).is_some()); @@ -246,7 +246,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)), Ok(1)); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), Ok(2)); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), Ok(3)); @@ -266,7 +266,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)), Ok(1)); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), Ok(2)); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), Ok(3)); @@ -295,7 +295,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(); assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD))); assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD))); assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD))); @@ -375,7 +375,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(); assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD))); assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD))); assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD))); diff --git a/modules/messages/src/proofs.rs b/modules/messages/src/proofs.rs index f2bf1d1b082..0e5edac635e 100644 --- a/modules/messages/src/proofs.rs +++ b/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/modules/messages/src/tests/mock.rs b/modules/messages/src/tests/mock.rs index 1b47bcbab79..2e04c7c9786 100644 --- a/modules/messages/src/tests/mock.rs +++ b/modules/messages/src/tests/mock.rs @@ -33,7 +33,7 @@ use bp_messages::{ DeliveryPayments, DispatchMessage, DispatchMessageData, FromBridgedChainMessagesProof, MessageDispatch, }, - ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, + ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, LaneState, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Size, StorageProofSize}; @@ -226,7 +226,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. @@ -240,8 +239,6 @@ impl Config for TestRuntime { type BridgedChain = BridgedChain; type BridgedHeaderChain = BridgedChainGrandpa; - type ActiveOutboundLanes = ActiveOutboundLanes; - type OutboundPayload = TestPayload; type InboundPayload = TestPayload; @@ -308,11 +305,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); @@ -448,7 +445,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) } @@ -463,7 +460,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 @@ -480,7 +489,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/modules/messages/src/tests/pallet_tests.rs b/modules/messages/src/tests/pallet_tests.rs index ca790348cb3..120435af4f1 100644 --- a/modules/messages/src/tests/pallet_tests.rs +++ b/modules/messages/src/tests/pallet_tests.rs @@ -17,12 +17,8 @@ //! Pallet-level tests. use crate::{ - outbound_lane, - outbound_lane::ReceivalConfirmationError, - send_message, - tests::mock::{self, *}, - weights_ext::WeightInfoExt, - Call, Config, Error, Event, InboundLanes, MaybeOutboundLanesCount, OutboundLanes, + outbound_lane, outbound_lane::ReceivalConfirmationError, send_message, tests::mock::*, + weights_ext::WeightInfoExt, Call, Config, Error, Event, InboundLanes, OutboundLanes, OutboundMessages, Pallet, PalletOperatingMode, PalletOwner, RuntimeInboundLaneStorage, StoredInboundLaneData, }; @@ -30,9 +26,9 @@ use crate::{ use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, target_chain::FromBridgedChainMessagesProof, BridgeMessagesCall, ChainWithMessages, - DeliveredMessages, InboundLaneData, InboundMessageDetails, MessageKey, MessageNonce, - MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, UnrewardedRelayer, - UnrewardedRelayersState, VerificationError, + DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId, LaneState, MessageKey, + MessageNonce, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, + UnrewardedRelayer, UnrewardedRelayersState, VerificationError, }; use bp_runtime::{BasicOperatingMode, PreComputedSize, Size}; use bp_test_utils::generate_owned_bridge_module_tests; @@ -44,7 +40,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,8 +50,11 @@ fn get_ready_for_events() { fn send_regular_message() { get_ready_for_events(); - let message_nonce = - outbound_lane::(TEST_LANE_ID).data().latest_generated_nonce + 1; + let message_nonce = outbound_lane::(TEST_LANE_ID) + .unwrap() + .data() + .latest_generated_nonce + + 1; send_message::(TEST_LANE_ID, REGULAR_PAYLOAD) .expect("send_message has failed"); @@ -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(), }, @@ -246,7 +247,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)); }); @@ -259,6 +263,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), @@ -290,8 +295,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), @@ -329,7 +335,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 + ); }); } @@ -375,7 +384,12 @@ fn receive_messages_delivery_proof_works() { send_regular_message(); 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, + ); }); } @@ -577,7 +591,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, + ); }); } @@ -598,7 +615,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, + ); }); } @@ -621,7 +641,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 + ); }); } @@ -700,6 +723,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, @@ -728,6 +752,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, @@ -773,7 +798,11 @@ fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messa // numer of 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( @@ -828,16 +857,6 @@ fn inbound_message_details_works() { }); } -#[test] -fn outbound_message_from_unconfigured_lane_is_rejected() { - run_test(|| { - assert_noop!( - send_message::(TEST_LANE_ID_3, REGULAR_PAYLOAD,), - Error::::InactiveOutboundLane, - ); - }); -} - #[test] fn test_bridge_messages_call_is_correctly_defined() { run_test(|| { @@ -846,6 +865,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, @@ -916,10 +936,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(), } } @@ -945,9 +966,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!( + send_message::(UNKNOWN_LANE_ID, REGULAR_PAYLOAD), + Error::::UnknownOutboundLane, + ); + + assert_noop!( + send_message::(CLOSED_LANE_ID, REGULAR_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/primitives/messages/src/lib.rs b/primitives/messages/src/lib.rs index 3668010a32b..4344a127d3a 100644 --- a/primitives/messages/src/lib.rs +++ b/primitives/messages/src/lib.rs @@ -158,6 +158,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 { @@ -176,6 +177,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; @@ -206,6 +222,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). /// @@ -238,11 +259,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. /// @@ -439,6 +469,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, @@ -448,9 +482,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, @@ -537,9 +579,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 { @@ -567,6 +616,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,