From 050bdf796bc26ea2da6b13ee53e695388809c80c Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 22 Jun 2023 03:13:27 +0200 Subject: [PATCH] Configure InboundQueue allow list (#862) * added extrinsic * removed array implementation, added remove item * fmt * add back in type info * fixes * fixes * updated cumulus --- ...igure-beacon.sh => configure-bridgehub.sh} | 13 +- core/packages/test/scripts/start-services.sh | 8 +- cumulus | 2 +- parachain/pallets/inbound-queue/src/lib.rs | 44 ++++++- parachain/pallets/inbound-queue/src/test.rs | 121 +++++++++++++++++- relayer/relays/execution/main.go | 6 +- smoketest/tests/create_token.rs | 50 ++++++++ 7 files changed, 229 insertions(+), 15 deletions(-) rename core/packages/test/scripts/{configure-beacon.sh => configure-bridgehub.sh} (75%) create mode 100644 smoketest/tests/create_token.rs diff --git a/core/packages/test/scripts/configure-beacon.sh b/core/packages/test/scripts/configure-bridgehub.sh similarity index 75% rename from core/packages/test/scripts/configure-beacon.sh rename to core/packages/test/scripts/configure-bridgehub.sh index 028cdb39fa250..aad7aecdb9f81 100755 --- a/core/packages/test/scripts/configure-beacon.sh +++ b/core/packages/test/scripts/configure-bridgehub.sh @@ -12,6 +12,14 @@ config_beacon_checkpoint() send_governance_transact_from_relaychain $bridgehub_para_id "$check_point_call" 180000000000 900000 } +config_inbound_queue() +{ + local pallet="30" + local callindex="01" + local payload="0x$pallet$callindex$(address_for OutboundQueue | cut -c3-)" + send_governance_transact_from_relaychain $bridgehub_para_id "$payload" 180000000000 900000 +} + wait_beacon_chain_ready() { local initial_beacon_block="" @@ -24,14 +32,15 @@ wait_beacon_chain_ready() done } -function configure_beacon() +function configure_bridgehub() { wait_beacon_chain_ready config_beacon_checkpoint + config_inbound_queue } if [ -z "${from_start_services:-}" ]; then echo "config beacon checkpoint only!" - configure_beacon + configure_bridgehub wait fi diff --git a/core/packages/test/scripts/start-services.sh b/core/packages/test/scripts/start-services.sh index cdfe5e1f47ede..0df5083423aa1 100755 --- a/core/packages/test/scripts/start-services.sh +++ b/core/packages/test/scripts/start-services.sh @@ -49,10 +49,10 @@ echo "Config beefy client" source scripts/configure-beefy.sh configure_beefy -# 6. config beacon client -echo "Config beacon client" -source scripts/configure-beacon.sh -configure_beacon +# 6. config bridgehub +echo "Config bridgehub" +source scripts/configure-bridgehub.sh +configure_bridgehub if [ "$skip_relayer" == "false" ]; then # 7. start relayer diff --git a/cumulus b/cumulus index 859fb16c2d937..e2f9758a633db 160000 --- a/cumulus +++ b/cumulus @@ -1 +1 @@ -Subproject commit 859fb16c2d9373c4acbfed630cd2d8ca1f20c4cc +Subproject commit e2f9758a633dbabbda4ff2089198d1fb43b78414 diff --git a/parachain/pallets/inbound-queue/src/lib.rs b/parachain/pallets/inbound-queue/src/lib.rs index 36345f1df791e..8801ded18a676 100644 --- a/parachain/pallets/inbound-queue/src/lib.rs +++ b/parachain/pallets/inbound-queue/src/lib.rs @@ -19,7 +19,7 @@ use frame_support::{ }; use frame_system::ensure_signed; use snowbridge_core::ParaId; -use sp_core::{ConstU32, H160}; +use sp_core::H160; use sp_runtime::traits::AccountIdConversion; use sp_std::{collections::btree_set::BTreeSet, convert::TryFrom, vec::Vec}; @@ -40,8 +40,6 @@ use scale_info::TypeInfo; type BalanceOf = <::Token as Inspect<::AccountId>>::Balance; -type AllowListLength = ConstU32<8>; - #[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] pub enum MessageDispatchResult { InvalidPayload, @@ -75,6 +73,8 @@ pub mod pallet { type XcmSender: SendXcm; type WeightInfo: WeightInfo; + + type AllowListLength: Get; } #[pallet::hooks] @@ -84,6 +84,8 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { MessageReceived { dest: ParaId, nonce: u64, result: MessageDispatchResult }, + AllowListAdded { address: sp_core::H160 }, + AllowListRemoved { address: sp_core::H160 }, } #[pallet::error] @@ -96,12 +98,14 @@ pub mod pallet { InvalidNonce, /// Cannot convert location InvalidAccountConversion, + // Allow list is full. + AllowListFull, } #[pallet::storage] #[pallet::getter(fn peer)] pub type AllowList = - StorageValue<_, BoundedBTreeSet, ValueQuery>; + StorageValue<_, BoundedBTreeSet, ValueQuery>; #[pallet::storage] pub type Nonce = StorageMap<_, Twox64Concat, ParaId, u64, ValueQuery>; @@ -120,7 +124,7 @@ pub mod pallet { #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - let allowlist: BoundedBTreeSet = + let allowlist: BoundedBTreeSet = BTreeSet::from_iter(self.allowlist.clone().into_iter()) .try_into() .expect("exceeded bound"); @@ -210,5 +214,35 @@ pub mod pallet { Ok(()) } + + #[pallet::call_index(1)] + #[pallet::weight({100_000_000})] + pub fn add_allow_list(origin: OriginFor, address: sp_core::H160) -> DispatchResult { + ensure_root(origin)?; + + let success = >::mutate(|allowlist| allowlist.try_insert(address).is_ok()); + + if success { + Self::deposit_event(Event::AllowListAdded { address }); + + Ok(()) + } else { + Err(Error::::AllowListFull.into()) + } + } + + #[pallet::call_index(2)] + #[pallet::weight({100_000_000})] + pub fn remove_allow_list(origin: OriginFor, address: sp_core::H160) -> DispatchResult { + ensure_root(origin)?; + + let removed = >::mutate(|allowlist| allowlist.remove(&address)); + + if removed { + Self::deposit_event(Event::AllowListRemoved { address }); + } + + Ok(()) + } } } diff --git a/parachain/pallets/inbound-queue/src/test.rs b/parachain/pallets/inbound-queue/src/test.rs index e3b07d4455a30..813fa98e37ebe 100644 --- a/parachain/pallets/inbound-queue/src/test.rs +++ b/parachain/pallets/inbound-queue/src/test.rs @@ -8,7 +8,7 @@ use frame_support::{ parameter_types, traits::{ConstU64, Everything, GenesisBuild}, }; -use sp_core::{H160, H256}; +use sp_core::{ConstU32, H160, H256}; use sp_keyring::AccountKeyring as Keyring; use sp_runtime::{ testing::Header, @@ -110,6 +110,7 @@ impl inbound_queue::Config for Test { type Reward = ConstU64<100>; type XcmSender = (); type WeightInfo = (); + type AllowListLength = ConstU32<2>; } fn last_events(n: usize) -> Vec { @@ -288,3 +289,121 @@ fn test_submit_no_funds_to_reward_relayers() { ); }); } + +#[test] +fn test_add_allow_list_without_root_yields_bad_origin() { + new_tester_with_config(Default::default()).execute_with(|| { + let contract_address = hex!("0000000000000000000000000000000000000000").into(); + let relayer: AccountId = Keyring::Bob.into(); + let origin = RuntimeOrigin::signed(relayer); + assert_noop!( + InboundQueue::add_allow_list(origin, contract_address), + sp_runtime::DispatchError::BadOrigin, + ); + }); +} + +#[test] +fn test_add_allow_list_with_root_succeeds() { + new_tester_with_config(Default::default()).execute_with(|| { + let origin = RuntimeOrigin::root(); + let contract_address = hex!("0000000000000000000000000000000000000000").into(); + + assert_eq!(>::get().len(), 0); + assert_ok!(InboundQueue::add_allow_list(origin, contract_address)); + + System::assert_last_event(RuntimeEvent::InboundQueue(crate::Event::AllowListAdded { + address: contract_address, + })); + + assert_eq!(>::get().len(), 1); + assert!(>::get().contains(&contract_address)); + }); +} + +#[test] +fn test_add_allow_list_ignores_duplicates() { + new_tester_with_config(Default::default()).execute_with(|| { + let origin = RuntimeOrigin::root(); + let contract_address = hex!("0000000000000000000000000000000000000000").into(); + + assert_eq!(>::get().len(), 0); + assert_ok!(InboundQueue::add_allow_list(origin.clone(), contract_address)); + assert_eq!(>::get().len(), 1); + assert!(>::get().contains(&contract_address)); + assert_ok!(InboundQueue::add_allow_list(origin, contract_address)); + assert_eq!(>::get().len(), 1); + assert!(>::get().contains(&contract_address)); + }); +} + +#[test] +fn test_add_allow_list_fails_when_exceeding_bounds() { + new_tester_with_config(Default::default()).execute_with(|| { + let origin = RuntimeOrigin::root(); + let contract_address1 = hex!("0000000000000000000000000000000000000000").into(); + let contract_address2 = hex!("1000000000000000000000000000000000000000").into(); + let contract_address3 = hex!("3000000000000000000000000000000000000000").into(); + + assert_eq!(>::get().len(), 0); + + assert_ok!(InboundQueue::add_allow_list(origin.clone(), contract_address1)); + assert_eq!(>::get().len(), 1); + + assert_ok!(InboundQueue::add_allow_list(origin.clone(), contract_address2)); + assert_eq!(>::get().len(), 2); + + assert_noop!( + InboundQueue::add_allow_list(origin, contract_address3), + Error::::AllowListFull, + ); + assert_eq!(>::get().len(), 2); + }); +} + +#[test] +fn test_remove_allow_list_without_root_yields_bad_origin() { + new_tester_with_config(Default::default()).execute_with(|| { + let contract_address = hex!("0000000000000000000000000000000000000000").into(); + let relayer: AccountId = Keyring::Bob.into(); + let origin = RuntimeOrigin::signed(relayer); + assert_noop!( + InboundQueue::remove_allow_list(origin, contract_address), + sp_runtime::DispatchError::BadOrigin, + ); + }); +} + +#[test] +fn test_remove_allow_list_with_root_succeeds() { + new_tester_with_config(Default::default()).execute_with(|| { + let origin = RuntimeOrigin::root(); + let contract_address = hex!("0000000000000000000000000000000000000000").into(); + + assert_eq!(>::get().len(), 0); + assert_ok!(InboundQueue::add_allow_list(origin.clone(), contract_address)); + assert_eq!(>::get().len(), 1); + + assert_ok!(InboundQueue::remove_allow_list(origin, contract_address)); + System::assert_last_event(RuntimeEvent::InboundQueue(crate::Event::AllowListRemoved { + address: contract_address, + })); + + assert_eq!(>::get().len(), 0); + assert!(!>::get().contains(&contract_address)); + }); +} + +#[test] +fn test_remove_allow_list_event_not_emitted_for_none_existent_item() { + new_tester_with_config(Default::default()).execute_with(|| { + let origin = RuntimeOrigin::root(); + let contract_address = hex!("0000000000000000000000000000000000000000").into(); + + let start = System::event_count(); + assert_ok!(InboundQueue::remove_allow_list(origin, contract_address)); + let end = System::event_count(); + + assert_eq!(start, end); // No new events + }); +} diff --git a/relayer/relays/execution/main.go b/relayer/relays/execution/main.go index aeeeb88f5fb78..c304dd0c0e5db 100644 --- a/relayer/relays/execution/main.go +++ b/relayer/relays/execution/main.go @@ -101,8 +101,10 @@ func (r *Relay) Start(ctx context.Context, eg *errgroup.Group) error { } log.WithFields(log.Fields{ - "paraNonce": paraNonce, - "ethNonce": ethNonce, + "ethBlockNumber": executionHeaderState.BlockNumber, + "laneId": r.config.Source.LaneID, + "paraNonce": paraNonce, + "ethNonce": ethNonce, }).Info("Polled Nonces") if paraNonce == ethNonce { diff --git a/smoketest/tests/create_token.rs b/smoketest/tests/create_token.rs new file mode 100644 index 0000000000000..0873ac77e0ce1 --- /dev/null +++ b/smoketest/tests/create_token.rs @@ -0,0 +1,50 @@ +use snowbridge_smoketest::contracts::{native_tokens, weth9}; + +use std::{sync::Arc, time::Duration}; + +use ethers::{ + core::types::Address, + middleware::SignerMiddleware, + providers::{Http, Provider}, + signers::{LocalWallet, Signer}, +}; + +// The deployment addresses of the following contracts are stable, unless we modify the order in +// contracts are deployed in DeployScript.sol. +const ETHEREUM_API: &str = "http://localhost:8545"; +const ETHEREUM_KEY: &str = "0x5e002a1af63fd31f1c25258f3082dc889762664cb8f218d86da85dff8b07b342"; +const NATIVE_TOKENS_CONTRACT: &str = "0x8cF6147918A5CBb672703F879f385036f8793a24"; +const WETH_CONTRACT: &str = "0x440eDFFA1352B13227e8eE646f3Ea37456deC701"; + +#[tokio::test] +async fn create_tokens() { + let provider = Provider::::try_from(ETHEREUM_API) + .unwrap() + .interval(Duration::from_millis(10u64)); + + let wallet: LocalWallet = ETHEREUM_KEY + .parse::() + .unwrap() + .with_chain_id(15u64); + + let client = SignerMiddleware::new(provider.clone(), wallet.clone()); + let client = Arc::new(client); + + let native_tokens_addr = NATIVE_TOKENS_CONTRACT.parse::
().unwrap(); + let native_tokens = native_tokens::NativeTokens::new(native_tokens_addr, client.clone()); + + let weth_addr = WETH_CONTRACT.parse::
().unwrap(); + let weth = weth9::WETH9::new(weth_addr, client.clone()); + + let receipt = native_tokens + .create(weth.address()) + .value(1000) + .send() + .await + .unwrap() + .await + .unwrap() + .unwrap(); + + assert_eq!(receipt.status.unwrap().as_u64(), 1u64); +}