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/OPStackIsm.t.sol

199 lines
6.5 KiB

// SPDX-License-Identifier: MIT or Apache-2.0
pragma solidity ^0.8.13;
import "forge-std/console.sol";
import {LibBit} from "../../contracts/libs/LibBit.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol";
import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol";
import {AbstractMessageIdAuthorizedIsm} from "../../contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol";
import {MockOptimismMessenger} from "../../contracts/mock/MockOptimism.sol";
import {TestMailbox} from "../../contracts/test/TestMailbox.sol";
import {Message} from "../../contracts/libs/Message.sol";
import {MessageUtils} from "./IsmTestUtils.sol";
import {OPStackIsm} from "../../contracts/isms/hook/OPStackIsm.sol";
import {OPStackHook} from "../../contracts/hooks/OPStackHook.sol";
import {TestRecipient} from "../../contracts/test/TestRecipient.sol";
import {NotCrossChainCall} from "@openzeppelin/contracts/crosschain/errors.sol";
import {AddressAliasHelper} from "@eth-optimism/contracts/standards/AddressAliasHelper.sol";
import {ICrossDomainMessenger} from "../../contracts/interfaces/optimism/ICrossDomainMessenger.sol";
import {ExternalBridgeTest} from "./ExternalBridgeTest.sol";
contract OPStackIsmTest is ExternalBridgeTest {
using LibBit for uint256;
using TypeCasts for address;
using MessageUtils for bytes;
address internal constant L1_MESSENGER_ADDRESS =
0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1;
address internal constant L1_CANNONICAL_CHAIN =
0x5E4e65926BA27467555EB562121fac00D24E9dD2;
address internal constant L2_MESSENGER_ADDRESS =
0x4200000000000000000000000000000000000007;
uint8 internal constant OPTIMISM_VERSION = 0;
uint256 internal constant DEFAULT_GAS_LIMIT = 1_920_000;
address internal alice = address(0x1);
MockOptimismMessenger internal l1Messenger;
MockOptimismMessenger internal l2Messenger;
event RelayedMessage(bytes32 indexed msgHash);
event FailedRelayedMessage(bytes32 indexed msgHash);
event ReceivedMessage(bytes32 indexed messageId);
function setUp() public override {
GAS_QUOTE = 0;
vm.etch(
L1_MESSENGER_ADDRESS,
address(new MockOptimismMessenger()).code
);
vm.etch(
L2_MESSENGER_ADDRESS,
address(new MockOptimismMessenger()).code
);
l1Messenger = MockOptimismMessenger(L1_MESSENGER_ADDRESS);
l2Messenger = MockOptimismMessenger(L2_MESSENGER_ADDRESS);
deployAll();
super.setUp();
}
///////////////////////////////////////////////////////////////////
/// SETUP ///
///////////////////////////////////////////////////////////////////
function deployHook() public {
originMailbox = new TestMailbox(ORIGIN_DOMAIN);
hook = new OPStackHook(
address(originMailbox),
DESTINATION_DOMAIN,
TypeCasts.addressToBytes32(address(ism)),
L1_MESSENGER_ADDRESS
);
}
function deployIsm() public {
ism = new OPStackIsm(L2_MESSENGER_ADDRESS);
}
function deployAll() public {
deployIsm();
deployHook();
ism.setAuthorizedHook(TypeCasts.addressToBytes32(address(hook)));
l2Messenger.setXDomainMessageSender(address(hook));
}
function test_verify_revertWhen_invalidMetadata() public override {
assertFalse(ism.verify(new bytes(0), encodedMessage));
}
function test_verify_revertsWhen_incorrectMessageId() public override {
bytes32 incorrectMessageId = keccak256("incorrect message id");
_externalBridgeDestinationCall(
_encodeHookData(incorrectMessageId, 0),
0
);
assertFalse(ism.isVerified(testMessage));
}
/* ============ helper functions ============ */
function _expectOriginExternalBridgeCall(
bytes memory _encodedHookData
) internal override {
vm.expectCall(
L1_MESSENGER_ADDRESS,
abi.encodeCall(
ICrossDomainMessenger.sendMessage,
(address(ism), _encodedHookData, uint32(DEFAULT_GAS_LIMIT))
)
);
}
function _externalBridgeDestinationCall(
bytes memory _encodedHookData,
uint256 _msgValue
) internal override {
vm.deal(L2_MESSENGER_ADDRESS, _msgValue);
l2Messenger.relayMessage(
0,
address(hook),
address(ism),
_msgValue,
uint32(GAS_QUOTE),
_encodedHookData
);
}
function _encodeExternalDestinationBridgeCall(
address /*_from*/,
address /*_to*/,
uint256 /*_msgValue*/,
bytes32 /*_messageId*/
) internal pure override returns (bytes memory) {
return new bytes(0);
}
// SKIP - no external bridge call
function test_preVerifyMessage_externalBridgeCall() public override {}
function test_verify_msgValue_externalBridgeCall() public override {}
function test_verify_revertsWhen_invalidIsm() public override {}
function test_verify_false_arbitraryCall() public override {}
/* ============ ISM.preVerifyMessage ============ */
function test_verify_revertsWhen_notAuthorizedHook() public override {
// needs to be called by the canonical messenger on Optimism
vm.expectRevert(NotCrossChainCall.selector);
ism.preVerifyMessage(messageId, 0);
vm.startPrank(L2_MESSENGER_ADDRESS);
_setExternalOriginSender(address(this));
// needs to be called by the authorized hook contract on Ethereum
vm.expectRevert(
"AbstractMessageIdAuthorizedIsm: sender is not the hook"
);
ism.preVerifyMessage(messageId, 0);
}
function _setExternalOriginSender(
address _sender
) internal override returns (bytes memory) {
l2Messenger.setXDomainMessageSender(_sender);
return "";
}
/* ============ ISM.verify ============ */
function test_verify_tooMuchValue() public {
uint256 _msgValue = 2 ** 255 + 1;
vm.expectRevert("AbstractMessageIdAuthorizedIsm: invalid msg.value");
_externalBridgeDestinationCall(
_encodeHookData(messageId, _msgValue),
_msgValue
);
assertFalse(ism.isVerified(encodedMessage));
assertEq(address(ism).balance, 0);
assertEq(address(testRecipient).balance, 0);
}
/* ============ helper functions ============ */
}