Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: MessageBus manager #332

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions contracts/messaging/MessageBusManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import {IManager, IManageable} from "./interfaces/IManager.sol";

import {Ownable} from "@openzeppelin/contracts-4.5.0/access/Ownable.sol";

contract MessageBusManager is IManager, Ownable {
address public immutable MESSAGE_BUS;

error MessageBusManager__NotFailed(bytes32 messageId);
error MessageBusManager__ZeroAddress();

constructor(address messageBus_, address owner_) {
if (messageBus_ == address(0) || owner_ == address(0)) {
revert MessageBusManager__ZeroAddress();
}
MESSAGE_BUS = messageBus_;
transferOwnership(owner_);
}

function resetFailedMessages(bytes32[] calldata messageIds) external onlyOwner {
for (uint256 i = 0; i < messageIds.length; i++) {
bytes32 messageId = messageIds[i];
if (getExecutedMessage(messageId) != IManageable.TxStatus.Fail) {
revert MessageBusManager__NotFailed(messageId);
}
IManageable(MESSAGE_BUS).updateMessageStatus(messageId, IManageable.TxStatus.Null);
}
}

// ═════════════════════════════════════════════ GENERIC MANAGING ══════════════════════════════════════════════════

function updateMessageStatus(bytes32 messageId, TxStatus status) external onlyOwner {
IManageable(MESSAGE_BUS).updateMessageStatus(messageId, status);
}

function updateAuthVerifier(address authVerifier) external onlyOwner {
IManageable(MESSAGE_BUS).updateAuthVerifier(authVerifier);
}

function withdrawGasFees(address payable to) external onlyOwner {
IManageable(MESSAGE_BUS).withdrawGasFees(to);
}

function rescueGas(address payable to) external onlyOwner {
IManageable(MESSAGE_BUS).rescueGas(to);
}

function updateGasFeePricing(address gasFeePricing) external onlyOwner {
IManageable(MESSAGE_BUS).updateGasFeePricing(gasFeePricing);
}

function transferMessageBusOwnership(address newOwner) external onlyOwner {
Ownable(MESSAGE_BUS).transferOwnership(newOwner);
}

function getExecutedMessage(bytes32 messageId) public view returns (TxStatus) {
return IManageable(MESSAGE_BUS).getExecutedMessage(messageId);
}
}
22 changes: 22 additions & 0 deletions contracts/messaging/interfaces/IManageable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IManageable {
enum TxStatus {
Null,
Success,
Fail
}

function updateMessageStatus(bytes32 messageId, TxStatus status) external;

function updateAuthVerifier(address authVerifier) external;

function withdrawGasFees(address payable to) external;

function rescueGas(address payable to) external;

function updateGasFeePricing(address gasFeePricing) external;

function getExecutedMessage(bytes32 messageId) external view returns (TxStatus);
}
10 changes: 10 additions & 0 deletions contracts/messaging/interfaces/IManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IManageable} from "./IManageable.sol";

interface IManager is IManageable {
function resetFailedMessages(bytes32[] calldata messageIds) external;

function transferMessageBusOwnership(address newOwner) external;
}
17 changes: 17 additions & 0 deletions test/messaging/MessageBusHarness.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import {MessageBus} from "../../contracts/messaging/MessageBus.sol";

// DO NOT USE THIS CONTRACT IN PRODUCTION
contract MessageBusHarness is MessageBus {
constructor(address _gasFeePricing, address _authVerifier) MessageBus(_gasFeePricing, _authVerifier) {}

function setMessageStatus(bytes32 messageId, TxStatus status) external {
executedMessages[messageId] = status;
}

function setFees(uint256 fees_) external {
fees = fees_;
}
}
228 changes: 228 additions & 0 deletions test/messaging/MessageBusManager.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import {IManageable} from "../../contracts/messaging/interfaces/IManageable.sol";
import {MessageBusManager} from "../../contracts/messaging/MessageBusManager.sol";
import {MessageBusReceiver} from "../../contracts/messaging/MessageBusReceiver.sol";

import {MessageBusHarness} from "./MessageBusHarness.sol";
import {Test} from "forge-std/Test.sol";

// solhint-disable func-name-mixedcase
contract MessageBusManagerTest is Test {
bytes32 public constant MESSAGE_ID = bytes32("Test");

MessageBusHarness public messageBus;
MessageBusManager public manager;

address public authVerifier = makeAddr("authVerifier");
address public gasFeePricing = makeAddr("gasFeePricing");
address public owner = makeAddr("owner");

address payable public gasRecipient = payable(makeAddr("gasRecipient"));

function setUp() public {
messageBus = new MessageBusHarness({_gasFeePricing: gasFeePricing, _authVerifier: authVerifier});
manager = new MessageBusManager({messageBus_: address(messageBus), owner_: owner});
messageBus.transferOwnership(address(manager));
}

function assertEq(MessageBusReceiver.TxStatus status, IManageable.TxStatus expected) internal {
assertEq(uint8(status), uint8(expected));
}

function assertEq(IManageable.TxStatus status, IManageable.TxStatus expected) internal {
assertEq(uint8(status), uint8(expected));
}

function toArray(
bytes32 a,
bytes32 b,
bytes32 c
) public pure returns (bytes32[] memory arr) {
arr = new bytes32[](3);
arr[0] = a;
arr[1] = b;
arr[2] = c;
}

function test_constructor() public {
assertEq(manager.MESSAGE_BUS(), address(messageBus));
assertEq(manager.owner(), owner);
}

function test_constructor_revert_zeroMessageBus() public {
vm.expectRevert(MessageBusManager.MessageBusManager__ZeroAddress.selector);
new MessageBusManager({messageBus_: address(0), owner_: owner});
}

function test_constructor_revert_zeroOwner() public {
vm.expectRevert(MessageBusManager.MessageBusManager__ZeroAddress.selector);
new MessageBusManager({messageBus_: address(messageBus), owner_: address(0)});
}

function test_updateMessageStatus_success() public {
vm.prank(owner);
manager.updateMessageStatus(MESSAGE_ID, IManageable.TxStatus.Success);
assertEq(messageBus.getExecutedMessage(MESSAGE_ID), IManageable.TxStatus.Success);
}

function test_updateMessageStatus_fail() public {
vm.prank(owner);
manager.updateMessageStatus(MESSAGE_ID, IManageable.TxStatus.Fail);
assertEq(messageBus.getExecutedMessage(MESSAGE_ID), IManageable.TxStatus.Fail);
}

function test_updateMessageStatus_null() public {
messageBus.setMessageStatus(MESSAGE_ID, MessageBusReceiver.TxStatus.Fail);
vm.prank(owner);
manager.updateMessageStatus(MESSAGE_ID, IManageable.TxStatus.Null);
assertEq(messageBus.getExecutedMessage(MESSAGE_ID), IManageable.TxStatus.Null);
}

function test_updateMessageStatus_revert_callerNotOwner(address caller) public {
vm.assume(caller != owner);
vm.expectRevert();
vm.prank(caller);
manager.updateMessageStatus(MESSAGE_ID, IManageable.TxStatus.Success);
}

function test_updateAuthVerifier() public {
vm.prank(owner);
manager.updateAuthVerifier(address(1));
assertEq(messageBus.authVerifier(), address(1));
}

function test_updateAuthVerifier_revert_callerNotOwner(address caller) public {
vm.assume(caller != owner);
vm.expectRevert();
vm.prank(caller);
manager.updateAuthVerifier(address(1));
}

function test_withdrawGasFees() public {
deal(address(messageBus), 123456);
messageBus.setFees(123456);
vm.prank(owner);
manager.withdrawGasFees(gasRecipient);
assertEq(address(messageBus).balance, 0);
assertEq(gasRecipient.balance, 123456);
}

function test_withdrawGasFees_revert_callerNotOwner(address caller) public {
vm.assume(caller != owner);
deal(address(messageBus), 123456);
messageBus.setFees(123456);
vm.expectRevert();
vm.prank(caller);
manager.withdrawGasFees(gasRecipient);
}

function test_rescueGas() public {
deal(address(messageBus), 123456);
vm.prank(owner);
manager.rescueGas(gasRecipient);
assertEq(address(messageBus).balance, 0);
assertEq(gasRecipient.balance, 123456);
}

function test_rescueGas_revert_callerNotOwner(address caller) public {
vm.assume(caller != owner);
deal(address(messageBus), 123456);
vm.expectRevert();
vm.prank(caller);
manager.rescueGas(gasRecipient);
}

function test_updateGasFeePricing() public {
vm.prank(owner);
manager.updateGasFeePricing(address(1));
assertEq(messageBus.gasFeePricing(), address(1));
}

function test_updateGasFeePricing_revert_callerNotOwner(address caller) public {
vm.assume(caller != owner);
vm.expectRevert();
vm.prank(caller);
manager.updateGasFeePricing(address(1));
}

function test_transferOwnership() public {
vm.prank(owner);
manager.transferOwnership(address(1));
assertEq(manager.owner(), address(1));
}

function test_transferOwnership_revert_callerNotOwner(address caller) public {
vm.assume(caller != owner);
vm.expectRevert();
vm.prank(caller);
manager.transferOwnership(address(1));
}

function test_transferMessageBusOwnership() public {
vm.prank(owner);
manager.transferMessageBusOwnership(address(1));
assertEq(messageBus.owner(), address(1));
}

function test_transferMessageBusOwnership_revert_callerNotOwner(address caller) public {
vm.assume(caller != owner);
vm.expectRevert();
vm.prank(caller);
manager.transferMessageBusOwnership(address(1));
}

function test_getExecutedMessage() public {
messageBus.setMessageStatus(MESSAGE_ID, MessageBusReceiver.TxStatus.Success);
assertEq(manager.getExecutedMessage(MESSAGE_ID), IManageable.TxStatus.Success);
messageBus.setMessageStatus(MESSAGE_ID, MessageBusReceiver.TxStatus.Fail);
assertEq(manager.getExecutedMessage(MESSAGE_ID), IManageable.TxStatus.Fail);
messageBus.setMessageStatus(MESSAGE_ID, MessageBusReceiver.TxStatus.Null);
assertEq(manager.getExecutedMessage(MESSAGE_ID), IManageable.TxStatus.Null);
}

function test_resetFailedMessages() public {
bytes32[] memory messageIds = toArray("Test1", "Test2", "Test3");
for (uint256 i = 0; i < messageIds.length; i++) {
messageBus.setMessageStatus(messageIds[i], MessageBusReceiver.TxStatus.Fail);
}
vm.prank(owner);
manager.resetFailedMessages(messageIds);
for (uint256 i = 0; i < messageIds.length; i++) {
assertEq(manager.getExecutedMessage(messageIds[i]), IManageable.TxStatus.Null);
assertEq(messageBus.getExecutedMessage(messageIds[i]), IManageable.TxStatus.Null);
}
}

function test_resetFailedMessages_revert_hasNullMessage() public {
bytes32[] memory messageIds = toArray("Test1", "Test2", "Test3");
messageBus.setMessageStatus(messageIds[0], MessageBusReceiver.TxStatus.Fail);
messageBus.setMessageStatus(messageIds[1], MessageBusReceiver.TxStatus.Null);
messageBus.setMessageStatus(messageIds[2], MessageBusReceiver.TxStatus.Fail);
vm.expectRevert(abi.encodeWithSelector(MessageBusManager.MessageBusManager__NotFailed.selector, messageIds[1]));
vm.prank(owner);
manager.resetFailedMessages(messageIds);
}

function test_resetFailedMessages_revert_hasSuccessMessage() public {
bytes32[] memory messageIds = toArray("Test1", "Test2", "Test3");
messageBus.setMessageStatus(messageIds[0], MessageBusReceiver.TxStatus.Fail);
messageBus.setMessageStatus(messageIds[1], MessageBusReceiver.TxStatus.Fail);
messageBus.setMessageStatus(messageIds[2], MessageBusReceiver.TxStatus.Success);
vm.expectRevert(abi.encodeWithSelector(MessageBusManager.MessageBusManager__NotFailed.selector, messageIds[2]));
vm.prank(owner);
manager.resetFailedMessages(messageIds);
}

function test_resetFailedMessages_revert_callerNotOwner(address caller) public {
vm.assume(caller != owner);
bytes32[] memory messageIds = toArray("Test1", "Test2", "Test3");
for (uint256 i = 0; i < messageIds.length; i++) {
messageBus.setMessageStatus(messageIds[i], MessageBusReceiver.TxStatus.Fail);
}
vm.expectRevert();
vm.prank(caller);
manager.resetFailedMessages(messageIds);
}
}
Loading