The home for Hyperlane core contracts, sdk packages, and other infrastructure
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
hyperlane-monorepo/solidity/test/isms/ERC5164ISM.t.sol

201 lines
6.1 KiB

// SPDX-License-Identifier: MIT or Apache-2.0
pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol";
import {Message} from "../../contracts/libs/Message.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {IMessageDispatcher} from "../../contracts/hooks/ERC5164/interfaces/IMessageDispatcher.sol";
import {ERC5164MessageHook} from "../../contracts/hooks/ERC5164/ERC5164MessageHook.sol";
import {ERC5164ISM} from "../../contracts/isms/hook/ERC5164ISM.sol";
import {TestRecipient} from "../../contracts/test/TestRecipient.sol";
import {MockMessageDispatcher, MockMessageExecutor} from "../../contracts/mock/MockERC5164.sol";
contract ERC5164ISMTest is Test {
using TypeCasts for address;
IMessageDispatcher internal dispatcher;
MockMessageExecutor internal executor;
ERC5164MessageHook internal hook;
ERC5164ISM internal ism;
TestRecipient internal testRecipient;
uint32 internal constant TEST1_DOMAIN = 1;
uint32 internal constant TEST2_DOMAIN = 2;
uint8 internal constant VERSION = 0;
bytes internal testMessage =
abi.encodePacked("Hello from the other chain!");
address internal alice = address(0x1);
// req for most tests
bytes encodedMessage = _encodeTestMessage(0, address(testRecipient));
bytes32 messageId = Message.id(encodedMessage);
event MessageDispatched(
bytes32 indexed messageId,
address indexed from,
uint256 indexed toChainId,
address to,
bytes data
);
///////////////////////////////////////////////////////////////////
/// SETUP ///
///////////////////////////////////////////////////////////////////
function setUp() public {
dispatcher = new MockMessageDispatcher();
executor = new MockMessageExecutor();
testRecipient = new TestRecipient();
}
function deployContracts() public {
ism = new ERC5164ISM(address(executor));
hook = new ERC5164MessageHook(
TEST2_DOMAIN,
address(dispatcher),
address(ism)
);
}
///////////////////////////////////////////////////////////////////
/// TESTS ///
///////////////////////////////////////////////////////////////////
function test_constructor() public {
vm.expectRevert("ERC5164ISM: invalid executor");
ism = new ERC5164ISM(alice);
vm.expectRevert("ERC5164Hook: invalid destination domain");
hook = new ERC5164MessageHook(0, address(dispatcher), address(ism));
vm.expectRevert("ERC5164Hook: invalid dispatcher");
hook = new ERC5164MessageHook(TEST2_DOMAIN, alice, address(ism));
vm.expectRevert("ERC5164Hook: invalid ISM");
hook = new ERC5164MessageHook(
TEST2_DOMAIN,
address(dispatcher),
address(0)
);
}
function test_postDispatch() public {
deployContracts();
bytes memory encodedHookData = abi.encodeCall(
ERC5164ISM.verifyMessageId,
(address(this).addressToBytes32(), messageId)
);
// note: not checking for messageId since this is implementation dependent on each vendor
vm.expectEmit(false, true, true, true, address(dispatcher));
emit MessageDispatched(
messageId,
address(hook),
TEST2_DOMAIN,
address(ism),
encodedHookData
);
hook.postDispatch(TEST2_DOMAIN, messageId);
}
function test_postDispatch_RevertWhen_ChainIDNotSupported() public {
deployContracts();
vm.expectRevert("ERC5164Hook: invalid destination domain");
hook.postDispatch(3, messageId);
}
/* ============ ISM.verifyMessageId ============ */
function test_verifyMessageId() public {
deployContracts();
vm.startPrank(address(executor));
ism.verifyMessageId(address(this).addressToBytes32(), messageId);
assertTrue(
ism.verifiedMessageIds(messageId, address(this).addressToBytes32())
);
vm.stopPrank();
}
function test_verifyMessageId_RevertWhen_NotAuthorized() public {
deployContracts();
vm.startPrank(alice);
// needs to be called by the authorized hook contract on Ethereum
vm.expectRevert("ERC5164ISM: sender is not the executor");
ism.verifyMessageId(alice.addressToBytes32(), messageId);
vm.stopPrank();
}
/* ============ ISM.verify ============ */
function test_verify() public {
deployContracts();
vm.startPrank(address(executor));
ism.verifyMessageId(address(this).addressToBytes32(), messageId);
bool verified = ism.verify(new bytes(0), encodedMessage);
assertTrue(verified);
vm.stopPrank();
}
function test_verify_RevertWhen_InvalidMessage() public {
deployContracts();
vm.startPrank(address(executor));
ism.verifyMessageId(address(this).addressToBytes32(), messageId);
bytes memory invalidMessage = _encodeTestMessage(0, address(this));
bool verified = ism.verify(new bytes(0), invalidMessage);
assertFalse(verified);
vm.stopPrank();
}
function test_verify_RevertWhen_InvalidSender() public {
deployContracts();
vm.startPrank(address(executor));
ism.verifyMessageId(alice.addressToBytes32(), messageId);
bool verified = ism.verify(new bytes(0), encodedMessage);
assertFalse(verified);
vm.stopPrank();
}
/* ============ helper functions ============ */
function _encodeTestMessage(uint32 _msgCount, address _receipient)
internal
view
returns (bytes memory)
{
return
abi.encodePacked(
VERSION,
_msgCount,
TEST1_DOMAIN,
TypeCasts.addressToBytes32(address(this)),
TEST2_DOMAIN,
TypeCasts.addressToBytes32(_receipient),
testMessage
);
}
}