From 18c6fc166b221b8336cb5f55ebcb9ce281772864 Mon Sep 17 00:00:00 2001 From: Michael Zhou Date: Wed, 4 Oct 2023 19:45:41 -0700 Subject: [PATCH 1/5] Fix operation order in quorum / receiver update Fixes #93 --- src/MultiBridgeMessageReceiver.sol | 12 +++-- .../MultiBridgeMessageReceiver.t.sol | 47 +++++++++++++++++++ 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/MultiBridgeMessageReceiver.sol b/src/MultiBridgeMessageReceiver.sol index e9e0843..0ac5cb9 100644 --- a/src/MultiBridgeMessageReceiver.sol +++ b/src/MultiBridgeMessageReceiver.sol @@ -183,11 +183,13 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar address[] calldata _receiverAdapters, bool[] calldata _operations ) external override onlyGlobalOwner { - /// @dev updates quorum here - _updateQuorum(_newQuorum); - - /// @dev updates receiver adapter here - _updateReceiverAdapters(_receiverAdapters, _operations); + if (_newQuorum > quorum) { + _updateReceiverAdapters(_receiverAdapters, _operations); + _updateQuorum(_newQuorum); + } else { + _updateQuorum(_newQuorum); + _updateReceiverAdapters(_receiverAdapters, _operations); + } } /// @notice Update power quorum threshold of message execution. diff --git a/test/unit-tests/MultiBridgeMessageReceiver.t.sol b/test/unit-tests/MultiBridgeMessageReceiver.t.sol index ea2db45..1bcf0b6 100644 --- a/test/unit-tests/MultiBridgeMessageReceiver.t.sol +++ b/test/unit-tests/MultiBridgeMessageReceiver.t.sol @@ -457,6 +457,53 @@ contract MultiBridgeMessageReceiverTest is Setup { assertEq(receiver.isTrustedExecutor(adapters[0]), false); } + /// @dev valid quorum and receiver updater in one single call, adding adapters and increasing quorum + function test_quorum_and_receiver_updater_add_increase() public { + vm.startPrank(timelockAddr); + + // First, add one adapter + address[] memory addOneAdapter = new address[](1); + addOneAdapter[0] = address(42); + bool[] memory addOneOps = new bool[](1); + addOneOps[0] = true; + + // Add two more and update quorum to 4 + address[] memory addTwoAdapters = new address[](2); + addTwoAdapters[0] = address(420); + addTwoAdapters[1] = address(421); + + bool[] memory addTwoOps = new bool[](2); + addTwoOps[0] = true; + addTwoOps[1] = true; + + uint64 newQuorum = 4; + + receiver.updateQuorumAndReceiverAdapter(newQuorum, addTwoAdapters, addTwoOps); + + /// @dev asserts the quorum and adapter lengths + assertEq(receiver.quorum(), newQuorum); + assertEq(receiver.isTrustedExecutor(addTwoAdapters[0]), true); + assertEq(receiver.isTrustedExecutor(addTwoAdapters[1]), true); + } + + /// @dev valid quorum and receiver updater in one single call, removing adapter and decreasing quorum + function test_quorum_and_receiver_updater_remove_decrease() public { + vm.startPrank(timelockAddr); + + // Remove one adapter and update quorum to 1 + address[] memory removeOneAdapter = new address[](1); + removeOneAdapter[0] = axelarAdapterAddr; + + uint64 newQuorum = 1; + + receiver.updateQuorumAndReceiverAdapter(newQuorum, removeOneAdapter, new bool[](1)); + + /// @dev asserts the quorum and adapter lengths + assertEq(receiver.quorum(), newQuorum); + assertEq(receiver.isTrustedExecutor(wormholeAdapterAddr), true); + assertEq(receiver.isTrustedExecutor(axelarAdapterAddr), false); + } + /// @dev should get message info function test_get_message_info() public { vm.startPrank(wormholeAdapterAddr); From 6b2534242faf8167d53867e6cea8294d5a2e5365 Mon Sep 17 00:00:00 2001 From: Michael Zhou Date: Thu, 5 Oct 2023 16:14:06 -0700 Subject: [PATCH 2/5] Cleanup update receiver adapter / quorum logic --- src/MultiBridgeMessageReceiver.sol | 28 ++++++++----------- test/Setup.t.sol | 3 +- .../MultiBridgeMessageReceiver.t.sol | 26 +++++++++++++++++ 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/MultiBridgeMessageReceiver.sol b/src/MultiBridgeMessageReceiver.sol index 0ac5cb9..808c24d 100644 --- a/src/MultiBridgeMessageReceiver.sol +++ b/src/MultiBridgeMessageReceiver.sol @@ -174,6 +174,7 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar onlyGlobalOwner { _updateReceiverAdapters(_receiverAdapters, _operations); + _validateQuorum(quorum); } /// @notice Update bridge receiver adapters after quorum update @@ -183,18 +184,13 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar address[] calldata _receiverAdapters, bool[] calldata _operations ) external override onlyGlobalOwner { - if (_newQuorum > quorum) { - _updateReceiverAdapters(_receiverAdapters, _operations); - _updateQuorum(_newQuorum); - } else { - _updateQuorum(_newQuorum); - _updateReceiverAdapters(_receiverAdapters, _operations); - } + _updateReceiverAdapters(_receiverAdapters, _operations); + _validateAndUpdateQuorum(_newQuorum); } /// @notice Update power quorum threshold of message execution. function updateQuorum(uint64 _quorum) external override onlyGlobalOwner { - _updateQuorum(_quorum); + _validateAndUpdateQuorum(_quorum); } /*///////////////////////////////////////////////////////////////// @@ -228,10 +224,8 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar PRIVATE/INTERNAL FUNCTIONS ////////////////////////////////////////////////////////////////*/ - function _updateQuorum(uint64 _quorum) internal { - if (_quorum > trustedExecutorsCount() || _quorum == 0) { - revert Error.INVALID_QUORUM_THRESHOLD(); - } + function _validateAndUpdateQuorum(uint64 _quorum) internal { + _validateQuorum(_quorum); uint64 oldValue = quorum; @@ -268,11 +262,13 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar _addTrustedExecutor(_receiverAdapter); } else { _removeTrustedExecutor(_receiverAdapter); - - if (quorum > trustedExecutorsCount()) { - revert Error.INVALID_QUORUM_THRESHOLD(); - } } emit BridgeReceiverAdapterUpdated(_receiverAdapter, _add); } + + function _validateQuorum(uint256 _quorum) private view { + if (_quorum > trustedExecutorsCount() || _quorum == 0) { + revert Error.INVALID_QUORUM_THRESHOLD(); + } + } } diff --git a/test/Setup.t.sol b/test/Setup.t.sol index b29d3b9..a96db6a 100644 --- a/test/Setup.t.sol +++ b/test/Setup.t.sol @@ -316,9 +316,8 @@ abstract contract Setup is Test { MultiBridgeMessageReceiver dstMMReceiver = MultiBridgeMessageReceiver(contractAddress[chainId][bytes("MMA_RECEIVER")]); - dstMMReceiver.updateReceiverAdapters(_receiverAdapters, _operations); + dstMMReceiver.updateQuorumAndReceiverAdapter(2, _receiverAdapters, _operations); dstMMReceiver.updateGovernanceTimelock(contractAddress[chainId]["TIMELOCK"]); - dstMMReceiver.updateQuorum(2); MessageReceiverGAC receiverGAC = MessageReceiverGAC(contractAddress[chainId][bytes("GAC")]); receiverGAC.setMultiBridgeMessageReceiver(address(dstMMReceiver)); diff --git a/test/unit-tests/MultiBridgeMessageReceiver.t.sol b/test/unit-tests/MultiBridgeMessageReceiver.t.sol index 1bcf0b6..e3ac673 100644 --- a/test/unit-tests/MultiBridgeMessageReceiver.t.sol +++ b/test/unit-tests/MultiBridgeMessageReceiver.t.sol @@ -504,6 +504,32 @@ contract MultiBridgeMessageReceiverTest is Setup { assertEq(receiver.isTrustedExecutor(axelarAdapterAddr), false); } + /// @dev valid quorum and receiver updater in one single call, removing one, adding two and increasing quorum + function test_quorum_and_receiver_updater_remove_add_increase() public { + vm.startPrank(timelockAddr); + + // Remove one adapter and update quorum to 1 + address[] memory removeAddAdapters = new address[](3); + removeAddAdapters[0] = axelarAdapterAddr; + removeAddAdapters[1] = address(42); + removeAddAdapters[2] = address(43); + bool[] memory removeAddOps = new bool[](3); + removeAddOps[0] = false; + removeAddOps[1] = true; + removeAddOps[2] = true; + + uint64 newQuorum = 3; + + receiver.updateQuorumAndReceiverAdapter(newQuorum, removeAddAdapters, removeAddOps); + + /// @dev asserts the quorum and adapter lengths + assertEq(receiver.quorum(), newQuorum); + assertEq(receiver.isTrustedExecutor(wormholeAdapterAddr), true); + assertEq(receiver.isTrustedExecutor(axelarAdapterAddr), false); + assertEq(receiver.isTrustedExecutor(removeAddAdapters[1]), true); + assertEq(receiver.isTrustedExecutor(removeAddAdapters[2]), true); + } + /// @dev should get message info function test_get_message_info() public { vm.startPrank(wormholeAdapterAddr); From c4973e8a38b069adc7a0fd06da2dc6017aa80094 Mon Sep 17 00:00:00 2001 From: Michael Zhou Date: Fri, 6 Oct 2023 16:00:36 -0700 Subject: [PATCH 3/5] Add initial adapters and quorum to constructor --- src/MultiBridgeMessageReceiver.sol | 27 +++++++++++++------ .../IMultiBridgeMessageReceiver.sol | 8 +++--- test/Setup.t.sol | 16 +++++------ .../RemoteAdapterRemove.t.sol | 6 ++--- .../MultiBridgeMessageReceiver.t.sol | 8 +++--- 5 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/MultiBridgeMessageReceiver.sol b/src/MultiBridgeMessageReceiver.sol index 808c24d..4e14778 100644 --- a/src/MultiBridgeMessageReceiver.sol +++ b/src/MultiBridgeMessageReceiver.sol @@ -73,9 +73,20 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar ////////////////////////////////////////////////////////////////*/ /// @notice sets the initial parameters - constructor(uint256 _srcChainId, address _gac) { + constructor(uint256 _srcChainId, address _gac, address[] memory _receiverAdapters, uint64 _quorum) { srcChainId = _srcChainId; gac = IGAC(_gac); + + uint256 numAdapters = _receiverAdapters.length; + bool[] memory operations = new bool[](numAdapters); + for (uint256 i; i < numAdapters;) { + operations[i] = true; + unchecked { + ++i; + } + } + _updateReceiverAdapters(_receiverAdapters, operations); + _validateAndUpdateQuorum(_quorum); } /*///////////////////////////////////////////////////////////////// @@ -177,12 +188,12 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar _validateQuorum(quorum); } - /// @notice Update bridge receiver adapters after quorum update - /// @dev called by admin to update receiver bridge adapters on all other chains - function updateQuorumAndReceiverAdapter( - uint64 _newQuorum, + /// @notice Update bridge receiver adapters and quorum + /// @dev called by admin to update receiver bridge adapters on all other chains along with quorum + function updateReceiverAdaptersAndQuorum( address[] calldata _receiverAdapters, - bool[] calldata _operations + bool[] calldata _operations, + uint64 _newQuorum ) external override onlyGlobalOwner { _updateReceiverAdapters(_receiverAdapters, _operations); _validateAndUpdateQuorum(_newQuorum); @@ -224,7 +235,7 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar PRIVATE/INTERNAL FUNCTIONS ////////////////////////////////////////////////////////////////*/ - function _validateAndUpdateQuorum(uint64 _quorum) internal { + function _validateAndUpdateQuorum(uint64 _quorum) private { _validateQuorum(_quorum); uint64 oldValue = quorum; @@ -233,7 +244,7 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar emit QuorumUpdated(oldValue, _quorum); } - function _updateReceiverAdapters(address[] memory _receiverAdapters, bool[] memory _operations) internal { + function _updateReceiverAdapters(address[] memory _receiverAdapters, bool[] memory _operations) private { uint256 len = _receiverAdapters.length; if (len == 0) { diff --git a/src/interfaces/IMultiBridgeMessageReceiver.sol b/src/interfaces/IMultiBridgeMessageReceiver.sol index 42af4f9..02a8e2c 100644 --- a/src/interfaces/IMultiBridgeMessageReceiver.sol +++ b/src/interfaces/IMultiBridgeMessageReceiver.sol @@ -67,12 +67,12 @@ interface IMultiBridgeMessageReceiver { function updateQuorum(uint64 _quorum) external; /// @notice updates the the list of receiver adapters and the quorum for message validity. - /// @param _newQuorum is the new quorum value /// @param _receiverAdapters the list of receiver adapters to add or remove /// @param _operations the list of operations to perform for corresponding receiver adapters, true for add, false for remove - function updateQuorumAndReceiverAdapter( - uint64 _newQuorum, + /// @param _newQuorum is the new quorum value + function updateReceiverAdaptersAndQuorum( address[] calldata _receiverAdapters, - bool[] calldata _operations + bool[] calldata _operations, + uint64 _newQuorum ) external; } diff --git a/test/Setup.t.sol b/test/Setup.t.sol index a96db6a..88c7c39 100644 --- a/test/Setup.t.sol +++ b/test/Setup.t.sol @@ -275,8 +275,13 @@ abstract contract Setup is Test { uint256 chainId = DST_CHAINS[i]; vm.selectFork(fork[chainId]); + + address[] memory _receiverAdapters = new address[](2); + _receiverAdapters[0] = contractAddress[chainId][bytes("WORMHOLE_RECEIVER_ADAPTER")]; + _receiverAdapters[1] = contractAddress[chainId][bytes("AXELAR_RECEIVER_ADAPTER")]; + address mmaReceiver = address( - new MultiBridgeMessageReceiver{salt: _salt}(SRC_CHAIN_ID, contractAddress[chainId][bytes("GAC")]) + new MultiBridgeMessageReceiver{salt: _salt}(SRC_CHAIN_ID, contractAddress[chainId][bytes("GAC")], _receiverAdapters, 2) ); contractAddress[chainId][bytes("MMA_RECEIVER")] = mmaReceiver; contractAddress[chainId][bytes("TIMELOCK")] = @@ -304,19 +309,10 @@ abstract contract Setup is Test { for (uint256 i; i < DST_CHAINS.length;) { uint256 chainId = DST_CHAINS[i]; - address[] memory _receiverAdapters = new address[](2); - _receiverAdapters[0] = contractAddress[chainId][bytes("WORMHOLE_RECEIVER_ADAPTER")]; - _receiverAdapters[1] = contractAddress[chainId][bytes("AXELAR_RECEIVER_ADAPTER")]; - - bool[] memory _operations = new bool[](2); - _operations[0] = true; - _operations[1] = true; - vm.selectFork(fork[chainId]); MultiBridgeMessageReceiver dstMMReceiver = MultiBridgeMessageReceiver(contractAddress[chainId][bytes("MMA_RECEIVER")]); - dstMMReceiver.updateQuorumAndReceiverAdapter(2, _receiverAdapters, _operations); dstMMReceiver.updateGovernanceTimelock(contractAddress[chainId]["TIMELOCK"]); MessageReceiverGAC receiverGAC = MessageReceiverGAC(contractAddress[chainId][bytes("GAC")]); diff --git a/test/integration-tests/RemoteAdapterRemove.t.sol b/test/integration-tests/RemoteAdapterRemove.t.sol index ee08c72..077a1d6 100644 --- a/test/integration-tests/RemoteAdapterRemove.t.sol +++ b/test/integration-tests/RemoteAdapterRemove.t.sol @@ -74,10 +74,10 @@ contract RemoteAdapterRemove is Setup { DST_CHAIN_ID, address(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")]), abi.encodeWithSelector( - MultiBridgeMessageReceiver.updateQuorumAndReceiverAdapter.selector, - newQuorum, + MultiBridgeMessageReceiver.updateReceiverAdaptersAndQuorum.selector, adaptersToRemove, - operation + operation, + newQuorum ), 0, EXPIRATION_CONSTANT, diff --git a/test/unit-tests/MultiBridgeMessageReceiver.t.sol b/test/unit-tests/MultiBridgeMessageReceiver.t.sol index e3ac673..53f0f82 100644 --- a/test/unit-tests/MultiBridgeMessageReceiver.t.sol +++ b/test/unit-tests/MultiBridgeMessageReceiver.t.sol @@ -450,7 +450,7 @@ contract MultiBridgeMessageReceiverTest is Setup { uint64 newQuorum = 1; /// @dev removes the newly updated adapter by reducing quorum by one - receiver.updateQuorumAndReceiverAdapter(newQuorum, adapters, new bool[](1)); + receiver.updateReceiverAdaptersAndQuorum(adapters, new bool[](1), newQuorum); /// @dev asserts the quorum and adapter lengths assertEq(receiver.quorum(), newQuorum); @@ -478,7 +478,7 @@ contract MultiBridgeMessageReceiverTest is Setup { uint64 newQuorum = 4; - receiver.updateQuorumAndReceiverAdapter(newQuorum, addTwoAdapters, addTwoOps); + receiver.updateReceiverAdaptersAndQuorum(addTwoAdapters, addTwoOps, newQuorum); /// @dev asserts the quorum and adapter lengths assertEq(receiver.quorum(), newQuorum); @@ -496,7 +496,7 @@ contract MultiBridgeMessageReceiverTest is Setup { uint64 newQuorum = 1; - receiver.updateQuorumAndReceiverAdapter(newQuorum, removeOneAdapter, new bool[](1)); + receiver.updateReceiverAdaptersAndQuorum(removeOneAdapter, new bool[](1), newQuorum); /// @dev asserts the quorum and adapter lengths assertEq(receiver.quorum(), newQuorum); @@ -520,7 +520,7 @@ contract MultiBridgeMessageReceiverTest is Setup { uint64 newQuorum = 3; - receiver.updateQuorumAndReceiverAdapter(newQuorum, removeAddAdapters, removeAddOps); + receiver.updateReceiverAdaptersAndQuorum(removeAddAdapters, removeAddOps, newQuorum); /// @dev asserts the quorum and adapter lengths assertEq(receiver.quorum(), newQuorum); From cf9f48cba6f28b1527933b0d9c5c79b5d2fb262a Mon Sep 17 00:00:00 2001 From: Michael Zhou Date: Fri, 6 Oct 2023 18:57:51 -0700 Subject: [PATCH 4/5] Minor refactor to address comment --- src/MultiBridgeMessageReceiver.sol | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/MultiBridgeMessageReceiver.sol b/src/MultiBridgeMessageReceiver.sol index 73bb75b..0051214 100644 --- a/src/MultiBridgeMessageReceiver.sol +++ b/src/MultiBridgeMessageReceiver.sol @@ -84,16 +84,13 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar srcChainId = _srcChainId; gac = IGAC(_gac); - uint256 numAdapters = _receiverAdapters.length; - bool[] memory operations = new bool[](numAdapters); - for (uint256 i; i < numAdapters;) { - operations[i] = true; + for (uint256 i; i < _receiverAdapters.length;) { + _updateReceiverAdapter(_receiverAdapters[i], true); unchecked { ++i; } } - _updateReceiverAdapters(_receiverAdapters, operations); - _validateAndUpdateQuorum(_quorum); + _updateQuorum(_quorum); } /*///////////////////////////////////////////////////////////////// @@ -204,12 +201,12 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar uint64 _newQuorum ) external override onlyGlobalOwner { _updateReceiverAdapters(_receiverAdapters, _operations); - _validateAndUpdateQuorum(_newQuorum); + _updateQuorum(_newQuorum); } /// @notice Update power quorum threshold of message execution. function updateQuorum(uint64 _quorum) external override onlyGlobalOwner { - _validateAndUpdateQuorum(_quorum); + _updateQuorum(_quorum); } /*///////////////////////////////////////////////////////////////// @@ -246,7 +243,7 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar PRIVATE/INTERNAL FUNCTIONS ////////////////////////////////////////////////////////////////*/ - function _validateAndUpdateQuorum(uint64 _quorum) private { + function _updateQuorum(uint64 _quorum) private { _validateQuorum(_quorum); uint64 oldValue = quorum; @@ -267,10 +264,6 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar } for (uint256 i; i < len;) { - if (_receiverAdapters[i] == address(0)) { - revert Error.ZERO_ADDRESS_INPUT(); - } - _updateReceiverAdapter(_receiverAdapters[i], _operations[i]); unchecked { @@ -280,6 +273,9 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar } function _updateReceiverAdapter(address _receiverAdapter, bool _add) private { + if (_receiverAdapter == address(0)) { + revert Error.ZERO_ADDRESS_INPUT(); + } if (_add) { _addTrustedExecutor(_receiverAdapter); } else { From dd98e571865f790808efd30c9f9ded2ad88c0a04 Mon Sep 17 00:00:00 2001 From: Michael Zhou Date: Sat, 7 Oct 2023 15:01:46 -0700 Subject: [PATCH 5/5] Increase unit test coverage for receiver --- .../MultiBridgeMessageReceiver.t.sol | 79 ++++++++++++++++++- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/test/unit-tests/MultiBridgeMessageReceiver.t.sol b/test/unit-tests/MultiBridgeMessageReceiver.t.sol index 357afa1..cc733ff 100644 --- a/test/unit-tests/MultiBridgeMessageReceiver.t.sol +++ b/test/unit-tests/MultiBridgeMessageReceiver.t.sol @@ -37,24 +37,58 @@ contract MultiBridgeMessageReceiverTest is Setup { timelockAddr = contractAddress[DST_CHAIN_ID]["TIMELOCK"]; } + /// @dev verifies default setup + function test_constructor() public { + assertEq(receiver.srcChainId(), SRC_CHAIN_ID); + assertEq(address(receiver.gac()), contractAddress[DST_CHAIN_ID]["GAC"]); + assertEq(receiver.quorum(), 2); + assertTrue(receiver.isTrustedExecutor(wormholeAdapterAddr)); + assertTrue(receiver.isTrustedExecutor(axelarAdapterAddr)); + } + /// @dev cannot be called with zero source chain id function test_constructor_zero_chain_id_input() public { - vm.expectRevert(Error.INVALID_SENDER_CHAIN_ID.selector); - address[] memory receiverAdapters = new address[](1); receiverAdapters[0] = address(43); + vm.expectRevert(Error.INVALID_SENDER_CHAIN_ID.selector); new MultiBridgeMessageReceiver(0, address(42), receiverAdapters, 1); } /// @dev cannot be called with zero address GAC function test_constructor_zero_gac_address_input() public { + address[] memory receiverAdapters = new address[](1); + receiverAdapters[0] = address(43); + vm.expectRevert(Error.ZERO_ADDRESS_INPUT.selector); + new MultiBridgeMessageReceiver(SRC_CHAIN_ID, address(0), receiverAdapters, 1); + } + /// @dev cannot be called with receiver adapters containing zero address + function test_constructor_zero_address_adapter() public { address[] memory receiverAdapters = new address[](1); - receiverAdapters[0] = address(43); + receiverAdapters[0] = address(0); - new MultiBridgeMessageReceiver(1, address(0), receiverAdapters, 1); + vm.expectRevert(Error.ZERO_ADDRESS_INPUT.selector); + new MultiBridgeMessageReceiver(SRC_CHAIN_ID, address(42), receiverAdapters, 1); + } + + /// @dev cannot be called with zero quorum + function test_constructor_zero_quorum() public { + address[] memory receiverAdapters = new address[](1); + receiverAdapters[0] = address(42); + + vm.expectRevert(Error.INVALID_QUORUM_THRESHOLD.selector); + new MultiBridgeMessageReceiver(SRC_CHAIN_ID, address(43), receiverAdapters, 0); + } + + /// @dev cannot be called with quorum too large + function test_constructor_quorum_too_large() public { + address[] memory receiverAdapters = new address[](1); + receiverAdapters[0] = address(42); + + vm.expectRevert(Error.INVALID_QUORUM_THRESHOLD.selector); + new MultiBridgeMessageReceiver(SRC_CHAIN_ID, address(43), receiverAdapters, 2); } /// @dev receives message from one adapter @@ -330,6 +364,22 @@ contract MultiBridgeMessageReceiverTest is Setup { receiver.scheduleMessageExecution(msgId); } + /// @dev updates governance timelock + function test_update_governance_timelock() public { + vm.startPrank(timelockAddr); + + receiver.updateGovernanceTimelock(address(42)); + assertEq(receiver.governanceTimelock(), address(42)); + } + + /// @dev cannot update governance timelock with zero address + function test_update_governance_timelock_zero_address() public { + vm.startPrank(timelockAddr); + + vm.expectRevert(Error.ZERO_GOVERNANCE_TIMELOCK.selector); + receiver.updateGovernanceTimelock(address(0)); + } + /// @dev adds one receiver adapter function test_update_receiver_adapter_add() public { vm.startPrank(timelockAddr); @@ -391,6 +441,27 @@ contract MultiBridgeMessageReceiverTest is Setup { receiver.updateReceiverAdapters(adapters, operations); } + /// @dev cannot update with empty adapters list + function test_update_receiver_adapter_empty_lst() public { + vm.startPrank(timelockAddr); + + vm.expectRevert(Error.ZERO_RECEIVER_ADAPTER.selector); + + receiver.updateReceiverAdapters(new address[](0), new bool[](0)); + } + + /// @dev cannot update with zero adapter address + function test_update_receiver_adapter_zero_address() public { + vm.startPrank(timelockAddr); + + address[] memory adapters = new address[](1); + adapters[0] = address(0); + + vm.expectRevert(Error.ZERO_ADDRESS_INPUT.selector); + + receiver.updateReceiverAdapters(adapters, new bool[](1)); + } + /// @dev cannot remove one receiver adapter without reducing quorum first function test_update_receiver_adapter_remove_invalid_quorum_threshold() public { vm.startPrank(timelockAddr);