From 5cbbd25b7ad6efba3eb48b8fe5c84f7e89150242 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 5 Oct 2023 12:31:38 +0300 Subject: [PATCH] Reject transactions if bridge pallets are halted (#2600) * reject transactions if bridge pallets are halted * fixed CI (#2598) --- Cargo.lock | 18 +-- bin/runtime-common/src/messages_call_ext.rs | 13 ++- .../src/refund_relayer_extension.rs | 107 +++++++++++++++++- modules/beefy/src/lib.rs | 6 +- modules/grandpa/src/call_ext.rs | 21 +++- modules/parachains/src/call_ext.rs | 19 +++- 6 files changed, 161 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1720bc61b7fc..41338afc10511 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4350,7 +4350,7 @@ checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" dependencies = [ "futures-io", "rustls 0.20.8", - "webpki 0.22.1", + "webpki 0.22.2", ] [[package]] @@ -5997,7 +5997,7 @@ dependencies = [ "ring 0.16.20", "rustls 0.20.8", "thiserror", - "webpki 0.22.1", + "webpki 0.22.2", "x509-parser 0.14.0", "yasna", ] @@ -10253,7 +10253,7 @@ dependencies = [ "thiserror", "tinyvec", "tracing", - "webpki 0.22.1", + "webpki 0.22.2", ] [[package]] @@ -11417,7 +11417,7 @@ dependencies = [ "log", "ring 0.16.20", "sct 0.7.0", - "webpki 0.22.1", + "webpki 0.22.2", ] [[package]] @@ -15265,7 +15265,7 @@ checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ "rustls 0.20.8", "tokio", - "webpki 0.22.1", + "webpki 0.22.2", ] [[package]] @@ -15658,7 +15658,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "digest 0.10.7", "rand 0.8.5", "static_assertions", @@ -16308,9 +16308,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.1" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e74f82d49d545ad128049b7e88f6576df2da6b02e9ce565c6f533be576957e" +checksum = "07ecc0cd7cac091bf682ec5efa18b1cff79d617b84181f38b3951dbe135f607f" dependencies = [ "ring 0.16.20", "untrusted", @@ -16322,7 +16322,7 @@ version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ - "webpki 0.22.1", + "webpki 0.22.2", ] [[package]] diff --git a/bin/runtime-common/src/messages_call_ext.rs b/bin/runtime-common/src/messages_call_ext.rs index 07a99d2c0a16c..435584d61f549 100644 --- a/bin/runtime-common/src/messages_call_ext.rs +++ b/bin/runtime-common/src/messages_call_ext.rs @@ -18,6 +18,7 @@ use crate::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; use bp_messages::{target_chain::MessageDispatch, InboundLaneData, LaneId, MessageNonce}; +use bp_runtime::OwnedBridgeModule; use frame_support::{ dispatch::CallableCallFor, traits::{Get, IsSubType}, @@ -278,7 +279,17 @@ impl< } fn check_obsolete_call(&self) -> TransactionValidity { + let is_pallet_halted = Pallet::::ensure_not_halted().is_err(); match self.call_info() { + Some(proof_info) if is_pallet_halted => { + log::trace!( + target: pallet_bridge_messages::LOG_TARGET, + "Rejecting messages transaction on halted pallet: {:?}", + proof_info + ); + + return sp_runtime::transaction_validity::InvalidTransaction::Call.into() + }, Some(CallInfo::ReceiveMessagesProof(proof_info)) if proof_info.is_obsolete(T::MessageDispatch::is_active()) => { @@ -291,7 +302,7 @@ impl< return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() }, Some(CallInfo::ReceiveMessagesDeliveryProof(proof_info)) - if proof_info.is_obsolete() => + if is_pallet_halted || proof_info.is_obsolete() => { log::trace!( target: pallet_bridge_messages::LOG_TARGET, diff --git a/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs index 876c069dc0f77..6d8b211480858 100644 --- a/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bin/runtime-common/src/refund_relayer_extension.rs @@ -838,21 +838,23 @@ mod tests { mock::*, }; use bp_messages::{ - DeliveredMessages, InboundLaneData, MessageNonce, OutboundLaneData, UnrewardedRelayer, - UnrewardedRelayersState, + DeliveredMessages, InboundLaneData, MessageNonce, MessagesOperatingMode, OutboundLaneData, + UnrewardedRelayer, UnrewardedRelayersState, }; use bp_parachains::{BestParaHeadHash, ParaInfo}; use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; - use bp_runtime::HeaderId; + use bp_runtime::{BasicOperatingMode, HeaderId}; use bp_test_utils::{make_default_justification, test_keyring}; use frame_support::{ assert_storage_noop, parameter_types, traits::{fungible::Mutate, ReservableCurrency}, weights::Weight, }; - use pallet_bridge_grandpa::{Call as GrandpaCall, StoredAuthoritySet}; - use pallet_bridge_messages::Call as MessagesCall; - use pallet_bridge_parachains::{Call as ParachainsCall, RelayBlockHash}; + use pallet_bridge_grandpa::{Call as GrandpaCall, Pallet as GrandpaPallet, StoredAuthoritySet}; + use pallet_bridge_messages::{Call as MessagesCall, Pallet as MessagesPallet}; + use pallet_bridge_parachains::{ + Call as ParachainsCall, Pallet as ParachainsPallet, RelayBlockHash, + }; use sp_runtime::{ traits::{ConstU64, Header as HeaderT}, transaction_validity::{InvalidTransaction, ValidTransaction}, @@ -1592,6 +1594,99 @@ mod tests { }); } + #[test] + fn ext_rejects_batch_with_grandpa_finality_proof_when_grandpa_pallet_is_halted() { + run_test(|| { + initialize_environment(100, 100, 100); + + GrandpaPallet::::set_operating_mode( + RuntimeOrigin::root(), + BasicOperatingMode::Halted, + ) + .unwrap(); + + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); + assert_eq!( + run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); + }); + } + + #[test] + fn ext_rejects_batch_with_parachain_finality_proof_when_parachains_pallet_is_halted() { + run_test(|| { + initialize_environment(100, 100, 100); + + ParachainsPallet::::set_operating_mode( + RuntimeOrigin::root(), + BasicOperatingMode::Halted, + ) + .unwrap(); + + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); + assert_eq!( + run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); + + assert_eq!( + run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); + assert_eq!( + run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); + }); + } + + #[test] + fn ext_rejects_transaction_when_messages_pallet_is_halted() { + run_test(|| { + initialize_environment(100, 100, 100); + + MessagesPallet::::set_operating_mode( + RuntimeOrigin::root(), + MessagesOperatingMode::Basic(BasicOperatingMode::Halted), + ) + .unwrap(); + + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); + assert_eq!( + run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); + + assert_eq!( + run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); + assert_eq!( + run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); + + assert_eq!( + run_pre_dispatch(message_delivery_call(200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); + assert_eq!( + run_pre_dispatch(message_confirmation_call(200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); + }); + } + #[test] fn pre_dispatch_parses_batch_with_relay_chain_and_parachain_headers() { run_test(|| { diff --git a/modules/beefy/src/lib.rs b/modules/beefy/src/lib.rs index 8637e9f190c18..686115a7b0ed5 100644 --- a/modules/beefy/src/lib.rs +++ b/modules/beefy/src/lib.rs @@ -601,7 +601,7 @@ mod tests { .is_some()); assert_eq!( ImportedBlockNumbers::::get(index), - Some(index + 1).map(Into::into) + Some(Into::into(index + 1)), ); } @@ -619,7 +619,7 @@ mod tests { .is_some()); assert_eq!( ImportedBlockNumbers::::get(0), - Some(commitments_to_keep + 1).map(Into::into) + Some(Into::into(commitments_to_keep + 1)), ); // the side effect of the import is that the commitment#1 is pruned assert!(ImportedCommitments::::get(1).is_none()); @@ -638,7 +638,7 @@ mod tests { .is_some()); assert_eq!( ImportedBlockNumbers::::get(1), - Some(commitments_to_keep + 2).map(Into::into) + Some(Into::into(commitments_to_keep + 2)), ); // the side effect of the import is that the commitment#2 is pruned assert!(ImportedCommitments::::get(1).is_none()); diff --git a/modules/grandpa/src/call_ext.rs b/modules/grandpa/src/call_ext.rs index e0648d5dd0f1d..f238064f92bca 100644 --- a/modules/grandpa/src/call_ext.rs +++ b/modules/grandpa/src/call_ext.rs @@ -16,7 +16,7 @@ use crate::{weights::WeightInfo, BridgedBlockNumber, BridgedHeader, Config, Error, Pallet}; use bp_header_chain::{justification::GrandpaJustification, ChainWithGrandpa}; -use bp_runtime::BlockNumberOf; +use bp_runtime::{BlockNumberOf, OwnedBridgeModule}; use codec::Encode; use frame_support::{dispatch::CallableCallFor, traits::IsSubType, weights::Weight}; use sp_runtime::{ @@ -126,6 +126,10 @@ pub trait CallSubType, I: 'static>: _ => return Ok(ValidTransaction::default()), }; + if Pallet::::ensure_not_halted().is_err() { + return InvalidTransaction::Call.into() + } + match SubmitFinalityProofHelper::::check_obsolete(finality_target.block_number) { Ok(_) => Ok(ValidTransaction::default()), Err(Error::::OldHeader) => InvalidTransaction::Stale.into(), @@ -192,10 +196,10 @@ mod tests { use crate::{ call_ext::CallSubType, mock::{run_test, test_header, RuntimeCall, TestBridgedChain, TestNumber, TestRuntime}, - BestFinalized, Config, WeightInfo, + BestFinalized, Config, PalletOperatingMode, WeightInfo, }; use bp_header_chain::ChainWithGrandpa; - use bp_runtime::HeaderId; + use bp_runtime::{BasicOperatingMode, HeaderId}; use bp_test_utils::{ make_default_justification, make_justification_for_header, JustificationGeneratorParams, }; @@ -238,6 +242,17 @@ mod tests { }); } + #[test] + fn extension_rejects_new_header_if_pallet_is_halted() { + run_test(|| { + // when pallet is halted => tx is rejected + sync_to_header_10(); + PalletOperatingMode::::put(BasicOperatingMode::Halted); + + assert!(!validate_block_submit(15)); + }); + } + #[test] fn extension_accepts_new_header() { run_test(|| { diff --git a/modules/parachains/src/call_ext.rs b/modules/parachains/src/call_ext.rs index 99640dadc61f4..198ff11be4951 100644 --- a/modules/parachains/src/call_ext.rs +++ b/modules/parachains/src/call_ext.rs @@ -17,6 +17,7 @@ use crate::{Config, Pallet, RelayBlockNumber}; use bp_parachains::BestParaHeadHash; use bp_polkadot_core::parachains::{ParaHash, ParaId}; +use bp_runtime::OwnedBridgeModule; use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; use sp_runtime::{ transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, @@ -141,6 +142,10 @@ pub trait CallSubType, I: 'static>: None => return Ok(ValidTransaction::default()), }; + if Pallet::::ensure_not_halted().is_err() { + return InvalidTransaction::Call.into() + } + if SubmitParachainHeadsHelper::::is_obsolete(&update) { return InvalidTransaction::Stale.into() } @@ -160,10 +165,11 @@ where mod tests { use crate::{ mock::{run_test, RuntimeCall, TestRuntime}, - CallSubType, ParaInfo, ParasInfo, RelayBlockNumber, + CallSubType, PalletOperatingMode, ParaInfo, ParasInfo, RelayBlockNumber, }; use bp_parachains::BestParaHeadHash; use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; + use bp_runtime::BasicOperatingMode; fn validate_submit_parachain_heads( num: RelayBlockNumber, @@ -221,6 +227,17 @@ mod tests { }); } + #[test] + fn extension_rejects_header_if_pallet_is_halted() { + run_test(|| { + // when pallet is halted => tx is rejected + sync_to_relay_header_10(); + PalletOperatingMode::::put(BasicOperatingMode::Halted); + + assert!(!validate_submit_parachain_heads(15, vec![(ParaId(1), [2u8; 32].into())])); + }); + } + #[test] fn extension_accepts_new_header() { run_test(|| {