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.
203 lines
6.1 KiB
203 lines
6.1 KiB
1 year ago
|
// 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/ERC5164MessageHook.sol";
|
||
|
import {ERC5164ISM} from "../../contracts/isms/native/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);
|
||
|
assertEq(
|
||
|
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
|
||
|
);
|
||
|
}
|
||
|
}
|