diff --git a/Cargo.lock b/Cargo.lock index 2782cae79181..8c1564a17398 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2141,6 +2141,8 @@ dependencies = [ "parachains-common", "parachains-runtimes-test-utils", "parity-scale-codec", + "snowbridge-control", + "snowbridge-core", "snowbridge-outbound-queue", "sp-core", "sp-io", diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index cbea8dcc4fda..146471782424 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -94,7 +94,6 @@ fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys( + collator_session_keys(), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + H160::random(), // use a random contract address, not the configured gateway address + H160::random(), + H160::random(), + ) + } + + #[test] + fn setting_ethereum_operating_mode_via_goverance_works() { + bridge_hub_test_utils::test_cases::set_bridge_operating_mode_works::< + Runtime, + >( + collator_session_keys(), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + Box::new(|call| RuntimeCall::EthereumControl(call).encode()), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::EthereumControl(event)) => Some(event), + _ => None, + } + }) + ) + } } mod bridge_hub_wococo_tests { diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml index d8ace23115c1..0bc480b223e6 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -55,7 +55,11 @@ pallet-bridge-parachains = { path = "../../../../../bridges/modules/parachains", pallet-bridge-messages = { path = "../../../../../bridges/modules/messages", default-features = false } pallet-bridge-relayers = { path = "../../../../../bridges/modules/relayers", default-features = false } bridge-runtime-common = { path = "../../../../../bridges/bin/runtime-common", default-features = false } + +# Ethereum Bridge (Snowbridge) +snowbridge-core = { path = "../../../../../../parachain/primitives/core", default-features = false } snowbridge-outbound-queue = { path = "../../../../../../parachain/pallets/outbound-queue", default-features = false } +snowbridge-control = { path = "../../../../../../parachain/pallets/control", default-features = false } [features] default = [ "std" ] diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs index 630571365662..a29cf273a222 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs @@ -33,10 +33,7 @@ use bridge_runtime_common::{ messages_xcm_extension::{XcmAsPlainPayload, XcmBlobMessageDispatchResult}, }; use codec::Encode; -use frame_support::{ - assert_ok, - traits::{Get, OnFinalize, OnInitialize, OriginTrait, PalletInfoAccess}, -}; +use frame_support::{assert_err, assert_ok, traits::{Get, OnFinalize, OnInitialize, OriginTrait, PalletInfoAccess}}; use frame_system::pallet_prelude::{BlockNumberFor, HeaderFor}; use pallet_bridge_grandpa::BridgedHeader; use parachains_common::AccountId; @@ -56,6 +53,7 @@ use xcm_executor::{ traits::{TransactAsset, WeightBounds}, XcmExecutor, }; +use snowbridge_core::outbound::OperatingMode; // Re-export test_case from assets pub use asset_test_utils::include_teleports_for_native_asset_works; @@ -65,6 +63,7 @@ type RuntimeHelper = // Re-export test_case from `parachains-runtimes-test-utils` pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works; +use xcm::v3::SendError::Unroutable; /// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call pub fn initialize_bridge_by_governance_works( @@ -1024,6 +1023,134 @@ pub fn handle_transfer_token_message< }); } +pub fn transfer_token_message_fails< + Runtime, + XcmConfig, +>( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + bridghub_parachain_id: u32, + gateway_proxy_address: H160, + weth_contract_address: H160, + destination_contract: H160, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_dmp_queue::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_outbound_queue::Config, + XcmConfig: xcm_executor::Config, + ValidatorIdOf: From>, +{ + let bridgehub_parachain_location = MultiLocation::new(1, Parachain(bridghub_parachain_id)); + + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + let assets = vec![MultiAsset { + id: Concrete(MultiLocation { + parents: 0, + interior: X2(AccountKey20{ network: None, key: gateway_proxy_address.into()}, AccountKey20{ network: None, key: weth_contract_address.into() }), + }), + fun: Fungible(1000000000), + }]; + + let inner_xcm = Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + WithdrawAsset(MultiAssets::from(assets.clone())), + DepositAsset { + assets: MultiAssetFilter::from(assets), + beneficiary: MultiLocation { + parents: 0, + interior: X1(AccountKey20{ network: None, key: destination_contract.into()}), + } + }, + SetTopic([0; 32]) + ]); + + // prepare transfer token message + let xcm = Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + ExportMessage { + network: Ethereum { chain_id: 15 }, + destination: Here, + xcm: inner_xcm + } + ]); + + // execute XCM + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + assert_err!(XcmExecutor::::execute_xcm( + bridgehub_parachain_location, + xcm, + hash, + RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + ).ensure_complete(), Unroutable); + }); +} + +pub fn set_bridge_operating_mode_works( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + runtime_call_encode: Box< + dyn Fn(snowbridge_control::Call) -> Vec, + >, + snowbridge_control_events: Box< + dyn Fn(Vec) -> Option>, + >, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_dmp_queue::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_control::Config, + ValidatorIdOf: From>, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + // encode `set_operating_mode` call + let set_operating_mode_call = runtime_call_encode(snowbridge_control::Call::< + Runtime, + >::set_operating_mode { + mode: OperatingMode::RejectingOutboundMessages, + }); + + let require_weight_at_most = + Weight::from_parts(429000000, 3517); + + assert_ok!(RuntimeHelper::::execute_as_governance( + set_operating_mode_call, + require_weight_at_most + ) + .ensure_complete()); + + // check events + let mut events = >::events() + .into_iter() + .filter_map(|e| snowbridge_control_events(e.event.encode())); + assert!( + events.any(|e| matches!(e, snowbridge_control::Event::SetOperatingMode { mode: OperatingMode::RejectingOutboundMessages })) + ); + }) +} + pub mod test_data { use super::*; use bp_header_chain::justification::GrandpaJustification;