From 710b0bdf7e762ac21244d205c482d53296e7d270 Mon Sep 17 00:00:00 2001 From: Klaudiusz Dembler Date: Tue, 4 Jun 2024 13:35:41 +0200 Subject: [PATCH 1/3] add helper functions, require bridge to be paused to start thawn --- runtime-modules/argo-bridge/src/errors.rs | 6 +- runtime-modules/argo-bridge/src/lib.rs | 146 +++++++++++++----- runtime-modules/argo-bridge/src/tests/mock.rs | 2 +- runtime-modules/argo-bridge/src/tests/mod.rs | 86 ++++++----- 4 files changed, 162 insertions(+), 78 deletions(-) diff --git a/runtime-modules/argo-bridge/src/errors.rs b/runtime-modules/argo-bridge/src/errors.rs index 1819059f9b..79e971ff7a 100644 --- a/runtime-modules/argo-bridge/src/errors.rs +++ b/runtime-modules/argo-bridge/src/errors.rs @@ -4,10 +4,12 @@ use sp_std::convert::TryInto; decl_error! { pub enum Error for Module { - - /// Bridge not on active state + /// Bridge not in active state BridgeNotActive, + /// Bridge not in paused state + BridgeNotPaused, + /// Insufficient JOY Balance to cover the transaction costs InsufficientJoyBalance, diff --git a/runtime-modules/argo-bridge/src/lib.rs b/runtime-modules/argo-bridge/src/lib.rs index 8f8368991f..753df5c329 100644 --- a/runtime-modules/argo-bridge/src/lib.rs +++ b/runtime-modules/argo-bridge/src/lib.rs @@ -26,6 +26,7 @@ use frame_support::{ traits::{ConstU32, Currency, Get}, }; use frame_system::{ensure_root, ensure_signed}; +use sp_runtime::DispatchError; use sp_std::vec; @@ -95,16 +96,22 @@ decl_module! { #[weight = WeightInfoArgo::::request_outbound_transfer()] pub fn request_outbound_transfer(origin, dest_account: RemoteAccount, amount: BalanceOf, expected_fee: BalanceOf) -> DispatchResult { - ensure!(Self::status() == BridgeStatus::Active, Error::::BridgeNotActive); - ensure!(RemoteChains::get().contains(&dest_account.chain_id), Error::::NotSupportedRemoteChainId); + Self::ensure_bridge_active()?; + + Self::ensure_chain_supported(dest_account.chain_id)?; let fee = Self::bridging_fee(); ensure!(fee == expected_fee, Error::::FeeDifferentThanExpected); + // TODO: add overflow check let amount_with_fees = fee + amount; let sender = ensure_signed(origin)?; ensure!(has_sufficient_balance_for_payment::(&sender, amount_with_fees), Error::::InsufficientJoyBalance); + // + // == MUTATION SAFE == + // + burn_from_usable::(&sender, amount_with_fees)?; >::put(Self::mint_allowance() + amount); @@ -117,20 +124,19 @@ decl_module! { #[weight = WeightInfoArgo::::finalize_inbound_transfer()] pub fn finalize_inbound_transfer(origin, remote_transfer: RemoteTransfer, dest_account: T::AccountId, amount: BalanceOf) -> DispatchResult { - ensure!(Self::operator_account().is_some(), Error::::OperatorAccountNotSet); - let caller = ensure_signed(origin)?; - ensure!(caller == Self::operator_account().unwrap(), Error::::NotOperatorAccount); + Self::ensure_bridge_active()?; - ensure!(Self::status() == BridgeStatus::Active, Error::::BridgeNotActive); - ensure!(amount <= Self::mint_allowance(), Error::::InsufficientBridgeMintAllowance); + Self::ensure_operator_origin(origin)?; - ensure!(RemoteChains::get().contains(&remote_transfer.chain_id), Error::::NotSupportedRemoteChainId); + Self::ensure_chain_supported(remote_transfer.chain_id)?; - >::put(Self::mint_allowance() - amount); - let _ = balances::Pallet::::deposit_creating( - &dest_account, - amount - ); + Self::ensure_mint_allowance(amount)?; + + // + // == MUTATION SAFE == + // + + Self::mint_tokens(&dest_account, amount); Self::deposit_event(RawEvent::InboundTransferFinalized(remote_transfer, dest_account, amount)); @@ -145,18 +151,17 @@ decl_module! { revert_amount: BalanceOf, rationale: BoundedVec>, ) -> DispatchResult { - ensure!(Self::operator_account().is_some(), Error::::OperatorAccountNotSet); - let caller = ensure_signed(origin)?; - ensure!(caller == Self::operator_account().unwrap(), Error::::NotOperatorAccount); + Self::ensure_bridge_active()?; - ensure!(Self::status() == BridgeStatus::Active, Error::::BridgeNotActive); - ensure!(revert_amount <= Self::mint_allowance(), Error::::InsufficientBridgeMintAllowance); + Self::ensure_operator_origin(origin)?; - >::put(Self::mint_allowance() - revert_amount); - let _ = balances::Pallet::::deposit_creating( - &revert_account, - revert_amount - ); + Self::ensure_mint_allowance(revert_amount)?; + + // + // == MUTATION SAFE == + // + + Self::mint_tokens(&revert_account, revert_amount); Self::deposit_event(RawEvent::OutboundTransferReverted(transfer_id, revert_account, revert_amount, rationale)); @@ -165,24 +170,37 @@ decl_module! { #[weight = WeightInfoArgo::::pause_bridge()] pub fn pause_bridge(origin) -> DispatchResult { - let caller = ensure_signed(origin)?; - let accounts = Self::pauser_accounts(); - ensure!(accounts.contains(&caller), Error::::NotPauserAccount); + let caller = Self::ensure_pauser_origin(origin)?; + + // + // == MUTATION SAFE == + // >::put(BridgeStatus::Paused); + Self::deposit_event(RawEvent::BridgePaused(caller)); Ok(()) } #[weight = WeightInfoArgo::::init_unpause_bridge()] - pub fn init_unpause_bridge(origin) -> DispatchResult{ - let caller = ensure_signed(origin)?; - ensure!(Self::pauser_accounts().contains(&caller), Error::::NotPauserAccount); + pub fn init_unpause_bridge(origin) -> DispatchResult { + ensure!( + Self::status() == BridgeStatus::Paused, + Error::::BridgeNotPaused + ); + + let caller = Self::ensure_pauser_origin(origin)?; + + // + // == MUTATION SAFE == + // let current_block = >::block_number(); let thawn_end_block = current_block + Self::thawn_duration(); - >::put(BridgeStatus::Thawn { thawn_ends_at: thawn_end_block}); + + >::put(BridgeStatus::Thawn { thawn_ends_at: thawn_end_block }); + Self::deposit_event(RawEvent::BridgeThawnStarted(caller, thawn_end_block)); Ok(()) @@ -190,16 +208,23 @@ decl_module! { #[weight = WeightInfoArgo::::finish_unpause_bridge()] pub fn finish_unpause_bridge(origin) -> DispatchResult { - let caller = ensure_signed(origin)?; - ensure!(Self::operator_account().is_some(), Error::::OperatorAccountNotSet); - ensure!(caller == Self::operator_account().unwrap(), Error::::NotOperatorAccount); + Self::ensure_operator_origin(origin)?; let current_block = >::block_number(); ensure!( - matches!(Self::status(), BridgeStatus::Thawn { thawn_ends_at } - if current_block >= thawn_ends_at), Error::::ThawnNotFinished); + matches!( + Self::status(), + BridgeStatus::Thawn { thawn_ends_at } if current_block >= thawn_ends_at + ), + Error::::ThawnNotFinished + ); + + // + // == MUTATION SAFE == + //// >::put(BridgeStatus::Active); + Self::deposit_event(RawEvent::BridgeThawnFinished()); Ok(()) @@ -249,7 +274,56 @@ decl_module! { } /// Module implementation -impl Module {} +impl Module { + pub fn ensure_bridge_active() -> DispatchResult { + ensure!( + Self::status() == BridgeStatus::Active, + Error::::BridgeNotActive + ); + Ok(()) + } + + pub fn ensure_operator_origin(origin: T::RuntimeOrigin) -> Result { + let caller = ensure_signed(origin)?; + ensure!( + Self::operator_account().is_some(), + Error::::OperatorAccountNotSet + ); + ensure!( + caller == Self::operator_account().unwrap(), + Error::::NotOperatorAccount + ); + Ok(caller) + } + + pub fn ensure_pauser_origin(origin: T::RuntimeOrigin) -> Result { + let caller = ensure_signed(origin)?; + let accounts = Self::pauser_accounts(); + ensure!(accounts.contains(&caller), Error::::NotPauserAccount); + Ok(caller) + } + + pub fn ensure_mint_allowance(amount: BalanceOf) -> DispatchResult { + ensure!( + amount <= Self::mint_allowance(), + Error::::InsufficientBridgeMintAllowance + ); + Ok(()) + } + + pub fn ensure_chain_supported(chain_id: ChainId) -> DispatchResult { + ensure!( + RemoteChains::get().contains(&chain_id), + Error::::NotSupportedRemoteChainId + ); + Ok(()) + } + + pub fn mint_tokens(dest_account: &T::AccountId, amount: BalanceOf) { + >::put(Self::mint_allowance() - amount); + let _ = balances::Pallet::::deposit_creating(dest_account, amount); + } +} impl frame_support::traits::Hooks for Pallet { #[cfg(feature = "try-runtime")] diff --git a/runtime-modules/argo-bridge/src/tests/mock.rs b/runtime-modules/argo-bridge/src/tests/mock.rs index 09e3bc8001..0ef386e09b 100644 --- a/runtime-modules/argo-bridge/src/tests/mock.rs +++ b/runtime-modules/argo-bridge/src/tests/mock.rs @@ -114,7 +114,7 @@ impl Config for Test { pub fn default_genesis_config() -> argo_bridge::GenesisConfig { argo_bridge::GenesisConfig:: { - status: BridgeStatus::Active, + status: BridgeStatus::Paused, mint_allowance: 0, bridging_fee: DefaultBridgingFee::get(), thawn_duration: 1, diff --git a/runtime-modules/argo-bridge/src/tests/mod.rs b/runtime-modules/argo-bridge/src/tests/mod.rs index 69695bf5dd..37a420bd54 100644 --- a/runtime-modules/argo-bridge/src/tests/mod.rs +++ b/runtime-modules/argo-bridge/src/tests/mod.rs @@ -3,6 +3,7 @@ use core::convert::{TryFrom, TryInto}; use crate::tests::mock::*; +use frame_support::dispatch::DispatchResult; use frame_support::{assert_err, assert_ok}; use sp_runtime::BoundedVec; @@ -36,6 +37,8 @@ fn request_outbound_transfer_success() { RuntimeOrigin::root(), parameters )); + assert_ok!(activate_bridge(account!(2), account!(1))); + let initial_balance = joy!(1020); let sender = account!(1); Balances::set_balance(RuntimeOrigin::root(), sender, initial_balance, joy!(0)).unwrap(); @@ -83,8 +86,6 @@ fn request_outbound_transfer_with_bridge_paused() { }; ArgoBridge::update_bridge_constrains(RuntimeOrigin::root(), parameters).unwrap(); - ArgoBridge::pause_bridge(RuntimeOrigin::signed(account!(2))).unwrap(); - let sender = account!(1); let transfer_amount = joy!(1000); Balances::set_balance(RuntimeOrigin::root(), sender, transfer_amount, joy!(0)).unwrap(); @@ -119,6 +120,7 @@ fn request_outbound_transfer_with_insufficient_balance() { RuntimeOrigin::root(), parameters )); + assert_ok!(activate_bridge(account!(2), account!(1))); let transfer_amount = joy!(1000); assert_ok!(Balances::set_balance( @@ -157,6 +159,7 @@ fn request_outbound_transfer_with_unexpected_fee() { RuntimeOrigin::root(), parameters )); + assert_ok!(activate_bridge(account!(2), account!(1))); let transfer_amount = joy!(1000); assert_ok!(Balances::set_balance( @@ -194,6 +197,7 @@ fn request_outbound_transfer_with_not_supported_remote_chain() { RuntimeOrigin::root(), parameters )); + assert_ok!(activate_bridge(account!(2), account!(1))); let transfer_amount = joy!(1000); assert_ok!(Balances::set_balance( @@ -223,12 +227,13 @@ fn finalize_inbound_transfer_success() { let remote_chains = BoundedVec::try_from(vec![1u32]).unwrap(); let parameters = BridgeConstraints { operator_account: Some(account!(1)), - pauser_accounts: None, + pauser_accounts: Some(vec![account!(2)]), bridging_fee: None, thawn_duration: None, remote_chains: Some(remote_chains), }; ArgoBridge::update_bridge_constrains(RuntimeOrigin::root(), parameters).unwrap(); + assert_ok!(activate_bridge(account!(2), account!(1))); let transfer_amount = joy!(1000); let dest_account = account!(2); @@ -248,27 +253,13 @@ fn finalize_inbound_transfer_success() { }); } -#[test] -fn finalize_inbound_transfer_with_no_operator_account() { - with_test_externalities(|| { - let remote_transfer = RemoteTransfer { id: 0, chain_id: 1 }; - let result = ArgoBridge::finalize_inbound_transfer( - RuntimeOrigin::signed(account!(1)), - remote_transfer, - account!(2), - 1000, - ); - assert_err!(result, Error::::OperatorAccountNotSet); - }); -} - #[test] fn finalize_inbound_transfer_with_unauthorized_account() { with_test_externalities(|| { let remote_chains = BoundedVec::try_from(vec![1u32]).unwrap(); let parameters = BridgeConstraints { operator_account: Some(account!(1)), - pauser_accounts: None, + pauser_accounts: Some(vec![account!(2)]), bridging_fee: None, thawn_duration: None, remote_chains: Some(remote_chains), @@ -277,6 +268,7 @@ fn finalize_inbound_transfer_with_unauthorized_account() { RuntimeOrigin::root(), parameters )); + assert_ok!(activate_bridge(account!(2), account!(1))); let remote_transfer = RemoteTransfer { id: 0, chain_id: 1 }; let result = ArgoBridge::finalize_inbound_transfer( @@ -295,7 +287,7 @@ fn finalize_inbound_transfer_with_insufficient_bridge_mint() { let remote_chains = BoundedVec::try_from(vec![1u32]).unwrap(); let parameters = BridgeConstraints { operator_account: Some(account!(1)), - pauser_accounts: None, + pauser_accounts: Some(vec![account!(2)]), bridging_fee: None, thawn_duration: None, remote_chains: Some(remote_chains), @@ -304,6 +296,7 @@ fn finalize_inbound_transfer_with_insufficient_bridge_mint() { RuntimeOrigin::root(), parameters )); + assert_ok!(activate_bridge(account!(2), account!(1))); let remote_transfer = RemoteTransfer { id: 0, chain_id: 1 }; let result = ArgoBridge::finalize_inbound_transfer( @@ -322,12 +315,13 @@ fn revert_outbound_transfer_success() { let remote_chains = BoundedVec::try_from(vec![1u32]).unwrap(); let parameters = BridgeConstraints { operator_account: Some(account!(1)), - pauser_accounts: None, + pauser_accounts: Some(vec![account!(2)]), bridging_fee: None, thawn_duration: None, remote_chains: Some(remote_chains), }; ArgoBridge::update_bridge_constrains(RuntimeOrigin::root(), parameters).unwrap(); + assert_ok!(activate_bridge(account!(2), account!(1))); let transfer_id = 1u64; let revert_amount = joy!(123); @@ -351,27 +345,13 @@ fn revert_outbound_transfer_success() { }); } -#[test] -fn revert_outbound_transfer_with_no_operator_account() { - with_test_externalities(|| { - let result = ArgoBridge::revert_outbound_transfer( - RuntimeOrigin::signed(account!(1)), - 1u64, - account!(2), - joy!(123), - vec![].try_into().unwrap(), - ); - assert_err!(result, Error::::OperatorAccountNotSet); - }); -} - #[test] fn revert_outbound_transfer_with_unauthorized_account() { with_test_externalities(|| { let remote_chains = BoundedVec::try_from(vec![1u32]).unwrap(); let parameters = BridgeConstraints { operator_account: Some(account!(1)), - pauser_accounts: None, + pauser_accounts: Some(vec![account!(2)]), bridging_fee: None, thawn_duration: None, remote_chains: Some(remote_chains), @@ -380,6 +360,7 @@ fn revert_outbound_transfer_with_unauthorized_account() { RuntimeOrigin::root(), parameters )); + assert_ok!(activate_bridge(account!(2), account!(1))); let result = ArgoBridge::revert_outbound_transfer( RuntimeOrigin::signed(account!(2)), @@ -398,7 +379,7 @@ fn revert_outbound_transfer_with_insufficient_bridge_mint() { let remote_chains = BoundedVec::try_from(vec![1u32]).unwrap(); let parameters = BridgeConstraints { operator_account: Some(account!(1)), - pauser_accounts: None, + pauser_accounts: Some(vec![account!(2)]), bridging_fee: None, thawn_duration: None, remote_chains: Some(remote_chains), @@ -407,6 +388,7 @@ fn revert_outbound_transfer_with_insufficient_bridge_mint() { RuntimeOrigin::root(), parameters )); + assert_ok!(activate_bridge(account!(2), account!(1))); let result = ArgoBridge::revert_outbound_transfer( RuntimeOrigin::signed(account!(1)), @@ -424,7 +406,7 @@ fn pause_bridge_success() { with_test_externalities(|| { let pauser_account = account!(2); let parameters = BridgeConstraints { - operator_account: None, + operator_account: Some(account!(1)), pauser_accounts: Some(vec![pauser_account]), bridging_fee: None, thawn_duration: None, @@ -434,6 +416,8 @@ fn pause_bridge_success() { RuntimeOrigin::root(), parameters )); + assert_ok!(activate_bridge(pauser_account, account!(1))); + assert_eq!(ArgoBridge::status(), BridgeStatus::Active); assert_ok!(ArgoBridge::pause_bridge(RuntimeOrigin::signed( pauser_account @@ -475,8 +459,6 @@ fn unpause_bridge_success() { }; ArgoBridge::update_bridge_constrains(RuntimeOrigin::root(), parameters).unwrap(); - ArgoBridge::pause_bridge(RuntimeOrigin::signed(account!(2))).unwrap(); - ArgoBridge::init_unpause_bridge(RuntimeOrigin::signed(account!(2))).unwrap(); let current_block = >::block_number(); assert_eq!( @@ -493,6 +475,24 @@ fn unpause_bridge_success() { }); } +#[test] +fn init_unpause_bridge_active() { + with_test_externalities(|| { + let parameters = BridgeConstraints { + operator_account: Some(account!(1)), + pauser_accounts: Some(vec![account!(2)]), + bridging_fee: None, + thawn_duration: None, + remote_chains: None, + }; + ArgoBridge::update_bridge_constrains(RuntimeOrigin::root(), parameters).unwrap(); + assert_ok!(activate_bridge(account!(2), account!(1))); + + let result = ArgoBridge::init_unpause_bridge(RuntimeOrigin::signed(account!(2))); + assert_err!(result, Error::::BridgeNotPaused); + }); +} + #[test] fn unpause_bridge_during_thawn() { with_test_externalities(|| { @@ -562,3 +562,11 @@ fn init_unpause_bridge_with_unauthorized_account() { assert_err!(result, Error::::NotPauserAccount); }); } + +pub fn activate_bridge(pauser_account_id: u64, operator_account_id: u64) -> DispatchResult { + let pauser_origin = RuntimeOrigin::signed(pauser_account_id); + ArgoBridge::init_unpause_bridge(pauser_origin)?; + System::set_block_number(3u32.into()); + ArgoBridge::finish_unpause_bridge(RuntimeOrigin::signed(operator_account_id))?; + Ok(()) +} From c9e42694bbc2197586e9c61e2248ffa8ea384c95 Mon Sep 17 00:00:00 2001 From: Klaudiusz Dembler Date: Tue, 4 Jun 2024 13:57:15 +0200 Subject: [PATCH 2/3] add overflow protection to request_outbound_transfer --- runtime-modules/argo-bridge/src/errors.rs | 3 ++ runtime-modules/argo-bridge/src/lib.rs | 4 +-- runtime-modules/argo-bridge/src/tests/mod.rs | 35 ++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/runtime-modules/argo-bridge/src/errors.rs b/runtime-modules/argo-bridge/src/errors.rs index 79e971ff7a..b2f3b808e7 100644 --- a/runtime-modules/argo-bridge/src/errors.rs +++ b/runtime-modules/argo-bridge/src/errors.rs @@ -4,6 +4,9 @@ use sp_std::convert::TryInto; decl_error! { pub enum Error for Module { + /// Unexpected arithmetic error (overflow / underflow) + ArithmeticError, + /// Bridge not in active state BridgeNotActive, diff --git a/runtime-modules/argo-bridge/src/lib.rs b/runtime-modules/argo-bridge/src/lib.rs index 753df5c329..23e4142f67 100644 --- a/runtime-modules/argo-bridge/src/lib.rs +++ b/runtime-modules/argo-bridge/src/lib.rs @@ -26,6 +26,7 @@ use frame_support::{ traits::{ConstU32, Currency, Get}, }; use frame_system::{ensure_root, ensure_signed}; +use sp_runtime::traits::CheckedAdd; use sp_runtime::DispatchError; use sp_std::vec; @@ -103,8 +104,7 @@ decl_module! { let fee = Self::bridging_fee(); ensure!(fee == expected_fee, Error::::FeeDifferentThanExpected); - // TODO: add overflow check - let amount_with_fees = fee + amount; + let amount_with_fees = amount.checked_add(&fee).ok_or(Error::::ArithmeticError)?; let sender = ensure_signed(origin)?; ensure!(has_sufficient_balance_for_payment::(&sender, amount_with_fees), Error::::InsufficientJoyBalance); diff --git a/runtime-modules/argo-bridge/src/tests/mod.rs b/runtime-modules/argo-bridge/src/tests/mod.rs index 37a420bd54..0b209ac2f2 100644 --- a/runtime-modules/argo-bridge/src/tests/mod.rs +++ b/runtime-modules/argo-bridge/src/tests/mod.rs @@ -221,6 +221,41 @@ fn request_outbound_transfer_with_not_supported_remote_chain() { }); } +#[test] +fn request_outbound_transfer_with_overflow() { + with_test_externalities(|| { + let fee = joy!(10); + let remote_chains = BoundedVec::try_from(vec![1u32]).unwrap(); + let parameters = BridgeConstraints { + operator_account: Some(account!(1)), + pauser_accounts: Some(vec![account!(2), account!(3)]), + bridging_fee: Some(fee), + thawn_duration: None, + remote_chains: Some(remote_chains), + }; + assert_ok!(ArgoBridge::update_bridge_constrains( + RuntimeOrigin::root(), + parameters + )); + assert_ok!(activate_bridge(account!(2), account!(1))); + + let transfer_amount = Balance::MAX; + let sender = account!(1); + + let remote_account = RemoteAccount { + account: [0; 32], + chain_id: 1, + }; + let result = ArgoBridge::request_outbound_transfer( + RuntimeOrigin::signed(sender), + remote_account, + transfer_amount, + fee, + ); + assert_err!(result, Error::::ArithmeticError); + }); +} + #[test] fn finalize_inbound_transfer_success() { with_test_externalities_custom_mint_allowance(joy!(1000), || { From 67b6d368ede004fd227d1c7ad58f17bd7ea170ed Mon Sep 17 00:00:00 2001 From: Klaudiusz Dembler Date: Tue, 4 Jun 2024 14:57:05 +0200 Subject: [PATCH 3/3] add bridge paused tests for finalize and revert extrinsics --- runtime-modules/argo-bridge/src/tests/mod.rs | 48 ++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/runtime-modules/argo-bridge/src/tests/mod.rs b/runtime-modules/argo-bridge/src/tests/mod.rs index 0b209ac2f2..5d75e5669b 100644 --- a/runtime-modules/argo-bridge/src/tests/mod.rs +++ b/runtime-modules/argo-bridge/src/tests/mod.rs @@ -344,6 +344,30 @@ fn finalize_inbound_transfer_with_insufficient_bridge_mint() { }); } +#[test] +fn finalize_inbound_transfer_with_bridge_paused() { + with_test_externalities(|| { + let remote_chains = BoundedVec::try_from(vec![1u32]).unwrap(); + let parameters = BridgeConstraints { + operator_account: Some(account!(1)), + pauser_accounts: Some(vec![account!(2)]), + bridging_fee: None, + thawn_duration: None, + remote_chains: Some(remote_chains), + }; + ArgoBridge::update_bridge_constrains(RuntimeOrigin::root(), parameters).unwrap(); + + let remote_transfer = RemoteTransfer { id: 0, chain_id: 1 }; + let result = ArgoBridge::finalize_inbound_transfer( + RuntimeOrigin::signed(account!(1)), + remote_transfer, + account!(2), + 1000, + ); + assert_err!(result, Error::::BridgeNotActive); + }); +} + #[test] fn revert_outbound_transfer_success() { with_test_externalities_custom_mint_allowance(joy!(1000), || { @@ -436,6 +460,30 @@ fn revert_outbound_transfer_with_insufficient_bridge_mint() { }); } +#[test] +fn revert_outbound_transfer_with_bridge_paused() { + with_test_externalities(|| { + let remote_chains = BoundedVec::try_from(vec![1u32]).unwrap(); + let parameters = BridgeConstraints { + operator_account: Some(account!(1)), + pauser_accounts: Some(vec![account!(2)]), + bridging_fee: None, + thawn_duration: None, + remote_chains: Some(remote_chains), + }; + ArgoBridge::update_bridge_constrains(RuntimeOrigin::root(), parameters).unwrap(); + + let result = ArgoBridge::revert_outbound_transfer( + RuntimeOrigin::signed(account!(1)), + 1u64, + account!(2), + joy!(100), + vec![], + ); + assert_err!(result, Error::::BridgeNotActive); + }); +} + #[test] fn pause_bridge_success() { with_test_externalities(|| {