From 67478f71655f57cdc3a71ad13c63dc9b33c23e26 Mon Sep 17 00:00:00 2001 From: curlypoint Date: Tue, 9 Jan 2024 02:19:02 +0530 Subject: [PATCH 01/10] WIP : Polygon ZKevm hook and ism --- solidity/contracts/hooks/PolygonZkevmHook.sol | 78 ++++++++++++ .../polygonZkevm/IPolygonZkEVMBridge.sol | 118 ++++++++++++++++++ .../contracts/isms/hook/PolygonZkevmIsm.sol | 49 ++++++++ 3 files changed, 245 insertions(+) create mode 100644 solidity/contracts/hooks/PolygonZkevmHook.sol create mode 100644 solidity/contracts/interfaces/polygonZkevm/IPolygonZkEVMBridge.sol create mode 100644 solidity/contracts/isms/hook/PolygonZkevmIsm.sol diff --git a/solidity/contracts/hooks/PolygonZkevmHook.sol b/solidity/contracts/hooks/PolygonZkevmHook.sol new file mode 100644 index 0000000000..72845ebb08 --- /dev/null +++ b/solidity/contracts/hooks/PolygonZkevmHook.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +/*@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ HYPERLANE @@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ +@@@@@@@@@ @@@@@@@@*/ + +// ============ Internal Imports ============ +import {AbstractMessageIdAuthHook} from "./libs/AbstractMessageIdAuthHook.sol"; +import {StandardHookMetadata} from "./libs/StandardHookMetadata.sol"; +import {TypeCasts} from "../libs/TypeCasts.sol"; +import {Message} from "../libs/Message.sol"; +import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; + +// ============ External Imports ============ +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {IPolygonZkEVMBridge} from "../interfaces/polygonZkevm/IPolygonZkEvmBridge.sol"; + +/** + * @title PolygonZkevmHook + * @notice Message hook to inform the {Polygon zkEVM chain Ism} of messages published through + * the native Polygon zkEVM bridge bridge. + */ +contract PolygonZkevmHook is AbstractMessageIdAuthHook { + using StandardHookMetadata for bytes; + using Message for bytes; + using TypeCasts for bytes32; + + // ============ Immutable Variables ============ + IPolygonZkEVMBridge public immutable zkEvmBridge; + + constructor( + address _mailbox, + uint32 _destinationDomain, + bytes32 _ism, + address _zkEvmBridge + ) AbstractMessageIdAuthHook(_mailbox, _destinationDomain, _ism) { + require( + Address.isContract(_zkEvmBridge), + "PolygonzkEVMHook: invalid cpManager contract" + ); + zkEvmBridge = IPolygonZkEVMBridge(_zkEvmBridge); + } + + /// @dev This value is hardcoded to 0 because the Polygon zkEVM bridge does not support fee quotes + function _quoteDispatch( + bytes calldata, + bytes calldata + ) internal pure override returns (uint256) { + return 0; + } + + /// @inheritdoc AbstractMessageIdAuthHook + function _sendMessageId( + bytes calldata metadata, + bytes memory payload + ) internal override { + require( + metadata.msgValue(0) < 2 ** 255, + "PolygonzkEVMHook: msgValue must be less than 2 ** 255" + ); + + zkEvmBridge.bridgeMessage( + destinationDomain, + TypeCasts.bytes32ToAddress(ism), + false, + payload + ); + } +} diff --git a/solidity/contracts/interfaces/polygonZkevm/IPolygonZkEVMBridge.sol b/solidity/contracts/interfaces/polygonZkevm/IPolygonZkEVMBridge.sol new file mode 100644 index 0000000000..cfef1c54d8 --- /dev/null +++ b/solidity/contracts/interfaces/polygonZkevm/IPolygonZkEVMBridge.sol @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: AGPL-3.0 + +pragma solidity ^0.8.0; + +interface IPolygonZkEVMBridge { + /** + * @dev Thrown when sender is not the PolygonZkEVM address + */ + error OnlyPolygonZkEVM(); + + /** + * @dev Thrown when the destination network is invalid + */ + error DestinationNetworkInvalid(); + + /** + * @dev Thrown when the amount does not match msg.value + */ + error AmountDoesNotMatchMsgValue(); + + /** + * @dev Thrown when user is bridging tokens and is also sending a value + */ + error MsgValueNotZero(); + + /** + * @dev Thrown when the Ether transfer on claimAsset fails + */ + error EtherTransferFailed(); + + /** + * @dev Thrown when the message transaction on claimMessage fails + */ + error MessageFailed(); + + /** + * @dev Thrown when the global exit root does not exist + */ + error GlobalExitRootInvalid(); + + /** + * @dev Thrown when the smt proof does not match + */ + error InvalidSmtProof(); + + /** + * @dev Thrown when an index is already claimed + */ + error AlreadyClaimed(); + + /** + * @dev Thrown when the owner of permit does not match the sender + */ + error NotValidOwner(); + + /** + * @dev Thrown when the spender of the permit does not match this contract address + */ + error NotValidSpender(); + + /** + * @dev Thrown when the amount of the permit does not match + */ + error NotValidAmount(); + + /** + * @dev Thrown when the permit data contains an invalid signature + */ + error NotValidSignature(); + + function bridgeAsset( + uint32 destinationNetwork, + address destinationAddress, + uint256 amount, + address token, + bool forceUpdateGlobalExitRoot, + bytes calldata permitData + ) external payable; + + function bridgeMessage( + uint32 destinationNetwork, + address destinationAddress, + bool forceUpdateGlobalExitRoot, + bytes calldata metadata + ) external payable; + + function claimAsset( + bytes32[32] calldata smtProof, + uint32 index, + bytes32 mainnetExitRoot, + bytes32 rollupExitRoot, + uint32 originNetwork, + address originTokenAddress, + uint32 destinationNetwork, + address destinationAddress, + uint256 amount, + bytes calldata metadata + ) external; + + function claimMessage( + bytes32[32] calldata smtProof, + uint32 index, + bytes32 mainnetExitRoot, + bytes32 rollupExitRoot, + uint32 originNetwork, + address originAddress, + uint32 destinationNetwork, + address destinationAddress, + uint256 amount, + bytes calldata metadata + ) external; + + function updateGlobalExitRoot() external; + + function activateEmergencyState() external; + + function deactivateEmergencyState() external; +} diff --git a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol new file mode 100644 index 0000000000..0fe606e86b --- /dev/null +++ b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +/*@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ HYPERLANE @@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ +@@@@@@@@@ @@@@@@@@*/ + +// ============ Internal Imports ============ + +import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol"; +import {Message} from "../../libs/Message.sol"; +import {TypeCasts} from "../../libs/TypeCasts.sol"; +import {AbstractMessageIdAuthorizedIsm} from "./AbstractMessageIdAuthorizedIsm.sol"; + +// ============ External Imports ============ +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; + +contract PolygonZkevmIsm is AbstractMessageIdAuthorizedIsm { + // ============ Constants ============ + + uint8 public constant moduleType = + uint8(IInterchainSecurityModule.Types.NULL); + + // ============ Constructor ============ + + constructor(address _l2Messenger) { + require( + Address.isContract(_l2Messenger), + "PolygonZkevmIsm: invalid L2Messenger" + ); + } + + // ============ Internal function ============ + + /** + * @notice Check if sender is authorized to message `verifyMessageId`. + */ + function _isAuthorized() internal view override returns (bool) { + // TODO: implement + } +} From bc02510c211e8cc007e80b3ae701af971a9951bf Mon Sep 17 00:00:00 2001 From: curlypoint Date: Fri, 12 Jan 2024 00:13:55 +0530 Subject: [PATCH 02/10] WIP : Use CCIP protocol to fetch proofs --- solidity/contracts/hooks/PolygonZkevmHook.sol | 5 +- .../contracts/isms/hook/PolygonZkevmIsm.sol | 92 ++++++++++++++++--- 2 files changed, 83 insertions(+), 14 deletions(-) diff --git a/solidity/contracts/hooks/PolygonZkevmHook.sol b/solidity/contracts/hooks/PolygonZkevmHook.sol index 72845ebb08..6957446021 100644 --- a/solidity/contracts/hooks/PolygonZkevmHook.sol +++ b/solidity/contracts/hooks/PolygonZkevmHook.sol @@ -22,7 +22,8 @@ import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; // ============ External Imports ============ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; -import {IPolygonZkEVMBridge} from "../interfaces/polygonZkevm/IPolygonZkEvmBridge.sol"; + +import {IPolygonZkEVMBridge} from "../interfaces/polygonzkevm/IPolygonZkEVMBridge.sol"; /** * @title PolygonZkevmHook @@ -71,7 +72,7 @@ contract PolygonZkevmHook is AbstractMessageIdAuthHook { zkEvmBridge.bridgeMessage( destinationDomain, TypeCasts.bytes32ToAddress(ism), - false, + true, payload ); } diff --git a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol index 0fe606e86b..3008e973a2 100644 --- a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol +++ b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol @@ -18,32 +18,100 @@ pragma solidity >=0.8.0; import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol"; import {Message} from "../../libs/Message.sol"; import {TypeCasts} from "../../libs/TypeCasts.sol"; -import {AbstractMessageIdAuthorizedIsm} from "./AbstractMessageIdAuthorizedIsm.sol"; +import {AbstractCcipReadIsm} from "../ccip-read/AbstractCcipReadIsm.sol"; +import {IMailbox} from "../../interfaces/IMailbox.sol"; +import {IPolygonZkEVMBridge} from "../../interfaces/polygonzkevm/IPolygonZkEVMBridge.sol"; +import {AbstractMessageIdAuthorizedIsm} from "../hook/AbstractMessageIdAuthorizedIsm.sol"; // ============ External Imports ============ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; -contract PolygonZkevmIsm is AbstractMessageIdAuthorizedIsm { +contract PolygonZkevmIsm is AbstractCcipReadIsm { + using Message for bytes; + IMailbox public mailbox; + string[] public offchainUrls; + uint256 public constant _DEPOSIT_CONTRACT_TREE_DEPTH = 32; // ============ Constants ============ - uint8 public constant moduleType = - uint8(IInterchainSecurityModule.Types.NULL); + IPolygonZkEVMBridge public immutable zkEvmBridge; // ============ Constructor ============ - constructor(address _l2Messenger) { + constructor( + address _zkEvmBridge, + IMailbox _mailbox, + string[] memory _offchainUrls + ) { require( - Address.isContract(_l2Messenger), + Address.isContract(_zkEvmBridge), "PolygonZkevmIsm: invalid L2Messenger" ); + zkEvmBridge = IPolygonZkEVMBridge(_zkEvmBridge); + mailbox = _mailbox; + offchainUrls = _offchainUrls; } - // ============ Internal function ============ + function getOffchainVerifyInfo( + bytes calldata _message + ) external view override { + revert OffchainLookup( + address(this), + offchainUrls, + _message, + PolygonZkevmIsm.process.selector, + _message + ); + } + + function verify( + bytes calldata _metadata, + bytes calldata + ) external returns (bool) { + ( + bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] memory smtProof, + uint32 index, + bytes32 mainnetExitRoot, + bytes32 rollupExitRoot, + uint32 originNetwork, + address originAddress, + uint32 destinationNetwork, + address destinationAddress, + uint256 amount, + bytes memory payload + ) = abi.decode( + _metadata, + ( + bytes32[32], + uint32, + bytes32, + bytes32, + uint32, + address, + uint32, + address, + uint256, + bytes + ) + ); + zkEvmBridge.claimMessage( + smtProof, + index, + mainnetExitRoot, + rollupExitRoot, + originNetwork, + originAddress, + destinationNetwork, + destinationAddress, + amount, + payload + ); + return true; + } - /** - * @notice Check if sender is authorized to message `verifyMessageId`. - */ - function _isAuthorized() internal view override returns (bool) { - // TODO: implement + function process( + bytes calldata _metadata, + bytes calldata _message + ) external { + mailbox.process(_metadata, _message); } } From b5bb2158cb5733d7e6e80dbe2ee026675eef2245 Mon Sep 17 00:00:00 2001 From: curlypoint Date: Sat, 13 Jan 2024 01:10:32 +0530 Subject: [PATCH 03/10] fixes for hook --- solidity/contracts/hooks/PolygonZkevmHook.sol | 49 ++++++++++++++----- .../contracts/isms/hook/PolygonZkevmIsm.sol | 42 +++++++++++----- 2 files changed, 66 insertions(+), 25 deletions(-) diff --git a/solidity/contracts/hooks/PolygonZkevmHook.sol b/solidity/contracts/hooks/PolygonZkevmHook.sol index 6957446021..9af6a70927 100644 --- a/solidity/contracts/hooks/PolygonZkevmHook.sol +++ b/solidity/contracts/hooks/PolygonZkevmHook.sol @@ -14,7 +14,6 @@ pragma solidity >=0.8.0; @@@@@@@@@ @@@@@@@@*/ // ============ Internal Imports ============ -import {AbstractMessageIdAuthHook} from "./libs/AbstractMessageIdAuthHook.sol"; import {StandardHookMetadata} from "./libs/StandardHookMetadata.sol"; import {TypeCasts} from "../libs/TypeCasts.sol"; import {Message} from "../libs/Message.sol"; @@ -24,13 +23,14 @@ import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {IPolygonZkEVMBridge} from "../interfaces/polygonzkevm/IPolygonZkEVMBridge.sol"; +import {MailboxClient} from "../client/MailboxClient.sol"; /** * @title PolygonZkevmHook * @notice Message hook to inform the {Polygon zkEVM chain Ism} of messages published through * the native Polygon zkEVM bridge bridge. */ -contract PolygonZkevmHook is AbstractMessageIdAuthHook { +contract PolygonZkevmHook is IPostDispatchHook, MailboxClient { using StandardHookMetadata for bytes; using Message for bytes; using TypeCasts for bytes32; @@ -38,42 +38,67 @@ contract PolygonZkevmHook is AbstractMessageIdAuthHook { // ============ Immutable Variables ============ IPolygonZkEVMBridge public immutable zkEvmBridge; + // left-padded address for ISM to verify messages + bytes32 public immutable ism; + // Domain of chain on which the ISM is deployed + uint32 public immutable destinationDomain; + + uint32 public immutable zkBridgeChainIdDestination; + constructor( address _mailbox, uint32 _destinationDomain, bytes32 _ism, - address _zkEvmBridge - ) AbstractMessageIdAuthHook(_mailbox, _destinationDomain, _ism) { + address _zkEvmBridge, + uint32 _zkBridgeChainId + ) MailboxClient(_mailbox) { require( Address.isContract(_zkEvmBridge), "PolygonzkEVMHook: invalid cpManager contract" ); + require(_ism != bytes32(0), "PolygonzkEVMHook: invalid ISM"); + require( + _destinationDomain != 0, + "PolygonzkEVMHook: invalid destination domain" + ); + ism = _ism; + destinationDomain = _destinationDomain; zkEvmBridge = IPolygonZkEVMBridge(_zkEvmBridge); + zkBridgeChainIdDestination = uint8(_zkBridgeChainId); + } + + /// @inheritdoc IPostDispatchHook + function supportsMetadata( + bytes calldata + ) public pure virtual override returns (bool) { + return true; } /// @dev This value is hardcoded to 0 because the Polygon zkEVM bridge does not support fee quotes - function _quoteDispatch( + function quoteDispatch( bytes calldata, bytes calldata - ) internal pure override returns (uint256) { + ) external pure override returns (uint256) { return 0; } - /// @inheritdoc AbstractMessageIdAuthHook - function _sendMessageId( + /// @inheritdoc IPostDispatchHook + function postDispatch( bytes calldata metadata, - bytes memory payload - ) internal override { + bytes calldata message + ) external payable override { require( metadata.msgValue(0) < 2 ** 255, "PolygonzkEVMHook: msgValue must be less than 2 ** 255" ); zkEvmBridge.bridgeMessage( - destinationDomain, + zkBridgeChainIdDestination, TypeCasts.bytes32ToAddress(ism), true, - payload + message ); } + + function hookType() external view override returns (uint8) {} } diff --git a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol index 3008e973a2..b8f8ee5a8d 100644 --- a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol +++ b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol @@ -22,21 +22,30 @@ import {AbstractCcipReadIsm} from "../ccip-read/AbstractCcipReadIsm.sol"; import {IMailbox} from "../../interfaces/IMailbox.sol"; import {IPolygonZkEVMBridge} from "../../interfaces/polygonzkevm/IPolygonZkEVMBridge.sol"; import {AbstractMessageIdAuthorizedIsm} from "../hook/AbstractMessageIdAuthorizedIsm.sol"; +import {ICcipReadIsm} from "../../interfaces/isms/ICcipReadIsm.sol"; +import {LibBit} from "../../libs/LibBit.sol"; // ============ External Imports ============ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; -contract PolygonZkevmIsm is AbstractCcipReadIsm { +/** + * @title PolygonZkevmIsm + * @notice Polygon zkEVM chain Ism that uses the Polygon zkEVM bridge to verify messages + */ +contract PolygonZkevmIsm is ICcipReadIsm, AbstractMessageIdAuthorizedIsm { using Message for bytes; + using LibBit for uint256; + IMailbox public mailbox; string[] public offchainUrls; uint256 public constant _DEPOSIT_CONTRACT_TREE_DEPTH = 32; - // ============ Constants ============ + // ============ Constants ============ IPolygonZkEVMBridge public immutable zkEvmBridge; + uint8 public constant override moduleType = + uint8(IInterchainSecurityModule.Types.CCIP_READ); // ============ Constructor ============ - constructor( address _zkEvmBridge, IMailbox _mailbox, @@ -51,6 +60,11 @@ contract PolygonZkevmIsm is AbstractCcipReadIsm { offchainUrls = _offchainUrls; } + /// @inheritdoc AbstractMessageIdAuthorizedIsm + function _isAuthorized() internal view override returns (bool) { + return msg.sender == address(zkEvmBridge); + } + function getOffchainVerifyInfo( bytes calldata _message ) external view override { @@ -58,15 +72,19 @@ contract PolygonZkevmIsm is AbstractCcipReadIsm { address(this), offchainUrls, _message, - PolygonZkevmIsm.process.selector, + PolygonZkevmIsm.verify.selector, _message ); } function verify( bytes calldata _metadata, - bytes calldata - ) external returns (bool) { + bytes calldata _message + ) + external + override(AbstractMessageIdAuthorizedIsm, IInterchainSecurityModule) + returns (bool) + { ( bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] memory smtProof, uint32 index, @@ -93,6 +111,8 @@ contract PolygonZkevmIsm is AbstractCcipReadIsm { bytes ) ); + + bytes32 messageId = _message.id(); zkEvmBridge.claimMessage( smtProof, index, @@ -105,13 +125,9 @@ contract PolygonZkevmIsm is AbstractCcipReadIsm { amount, payload ); + verifiedMessages[messageId] = verifiedMessages[messageId].setBit( + VERIFIED_MASK_INDEX + ); return true; } - - function process( - bytes calldata _metadata, - bytes calldata _message - ) external { - mailbox.process(_metadata, _message); - } } From 959b8194a45e1cdc7391cb94c373be06f0c4bd72 Mon Sep 17 00:00:00 2001 From: curlypoint Date: Sat, 13 Jan 2024 17:10:23 +0530 Subject: [PATCH 04/10] Add PolygonZkEVM bridge message receiver interface, pass message id instead of message through brigde --- solidity/contracts/hooks/PolygonZkevmHook.sol | 3 +- .../polygonZkevm/IBridgeMessageReceiver.sol | 14 ++++++ .../hook/AbstractMessageIdAuthorizedIsm.sol | 2 +- .../contracts/isms/hook/PolygonZkevmIsm.sol | 47 +++++++++++++++---- solidity/test/isms/PolygonZkevmIsm.t.sol | 34 ++++++++++++++ 5 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 solidity/contracts/interfaces/polygonZkevm/IBridgeMessageReceiver.sol create mode 100644 solidity/test/isms/PolygonZkevmIsm.t.sol diff --git a/solidity/contracts/hooks/PolygonZkevmHook.sol b/solidity/contracts/hooks/PolygonZkevmHook.sol index 9af6a70927..4d1751da94 100644 --- a/solidity/contracts/hooks/PolygonZkevmHook.sol +++ b/solidity/contracts/hooks/PolygonZkevmHook.sol @@ -91,12 +91,13 @@ contract PolygonZkevmHook is IPostDispatchHook, MailboxClient { metadata.msgValue(0) < 2 ** 255, "PolygonzkEVMHook: msgValue must be less than 2 ** 255" ); + bytes32 messageId = message.id(); zkEvmBridge.bridgeMessage( zkBridgeChainIdDestination, TypeCasts.bytes32ToAddress(ism), true, - message + abi.encodePacked(messageId) ); } diff --git a/solidity/contracts/interfaces/polygonZkevm/IBridgeMessageReceiver.sol b/solidity/contracts/interfaces/polygonZkevm/IBridgeMessageReceiver.sol new file mode 100644 index 0000000000..22c3f0b044 --- /dev/null +++ b/solidity/contracts/interfaces/polygonZkevm/IBridgeMessageReceiver.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: AGPL-3.0 + +pragma solidity >=0.8.0; + +/** + * @dev Define interface for PolygonZkEVM Bridge message receiver + */ +interface IBridgeMessageReceiver { + function onMessageReceived( + address originAddress, + uint32 originNetwork, + bytes memory data + ) external payable; +} diff --git a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol index 2f9caf8a3f..fb97eb20ed 100644 --- a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol +++ b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol @@ -74,7 +74,7 @@ abstract contract AbstractMessageIdAuthorizedIsm is bytes calldata, /*_metadata*/ bytes calldata message - ) external returns (bool) { + ) external virtual returns (bool) { bytes32 messageId = message.id(); // check for the first bit (used for verification) diff --git a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol index b8f8ee5a8d..80d849659a 100644 --- a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol +++ b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol @@ -24,6 +24,7 @@ import {IPolygonZkEVMBridge} from "../../interfaces/polygonzkevm/IPolygonZkEVMBr import {AbstractMessageIdAuthorizedIsm} from "../hook/AbstractMessageIdAuthorizedIsm.sol"; import {ICcipReadIsm} from "../../interfaces/isms/ICcipReadIsm.sol"; import {LibBit} from "../../libs/LibBit.sol"; +import {IBridgeMessageReceiver} from "../../interfaces/polygonzkevm/IBridgeMessageReceiver.sol"; // ============ External Imports ============ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; @@ -32,13 +33,16 @@ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; * @title PolygonZkevmIsm * @notice Polygon zkEVM chain Ism that uses the Polygon zkEVM bridge to verify messages */ -contract PolygonZkevmIsm is ICcipReadIsm, AbstractMessageIdAuthorizedIsm { +contract PolygonZkevmIsm is + ICcipReadIsm, + AbstractMessageIdAuthorizedIsm, + IBridgeMessageReceiver +{ using Message for bytes; using LibBit for uint256; IMailbox public mailbox; string[] public offchainUrls; - uint256 public constant _DEPOSIT_CONTRACT_TREE_DEPTH = 32; // ============ Constants ============ IPolygonZkEVMBridge public immutable zkEvmBridge; @@ -68,10 +72,12 @@ contract PolygonZkevmIsm is ICcipReadIsm, AbstractMessageIdAuthorizedIsm { function getOffchainVerifyInfo( bytes calldata _message ) external view override { + bytes memory messageId = abi.encodePacked(_message.id()); + revert OffchainLookup( address(this), offchainUrls, - _message, + messageId, PolygonZkevmIsm.verify.selector, _message ); @@ -79,14 +85,18 @@ contract PolygonZkevmIsm is ICcipReadIsm, AbstractMessageIdAuthorizedIsm { function verify( bytes calldata _metadata, - bytes calldata _message + bytes calldata ) external - override(AbstractMessageIdAuthorizedIsm, IInterchainSecurityModule) + override( + // bytes calldata _message + AbstractMessageIdAuthorizedIsm, + IInterchainSecurityModule + ) returns (bool) { ( - bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] memory smtProof, + bytes32[32] memory smtProof, uint32 index, bytes32 mainnetExitRoot, bytes32 rollupExitRoot, @@ -112,7 +122,7 @@ contract PolygonZkevmIsm is ICcipReadIsm, AbstractMessageIdAuthorizedIsm { ) ); - bytes32 messageId = _message.id(); + // bytes32 messageId = _message.id(); zkEvmBridge.claimMessage( smtProof, index, @@ -125,9 +135,30 @@ contract PolygonZkevmIsm is ICcipReadIsm, AbstractMessageIdAuthorizedIsm { amount, payload ); + // verifiedMessages[messageId] = verifiedMessages[messageId].setBit( + // VERIFIED_MASK_INDEX + // ); + return true; + } + + /// @inheritdoc IBridgeMessageReceiver + function onMessageReceived( + address, + uint32 originNetwork, + bytes memory data + ) external payable override { + require( + originNetwork == 0, + "PolygonZkevmIsm: origin network must be 0" + ); + require(data.length == 32, "PolygonZkevmIsm: data must be 32 bytes"); + bytes32 messageId = abi.decode(data, (bytes32)); + require( + !verifiedMessages[messageId].isBitSet(VERIFIED_MASK_INDEX), + "PolygonZkevmIsm: message already verified" + ); verifiedMessages[messageId] = verifiedMessages[messageId].setBit( VERIFIED_MASK_INDEX ); - return true; } } diff --git a/solidity/test/isms/PolygonZkevmIsm.t.sol b/solidity/test/isms/PolygonZkevmIsm.t.sol new file mode 100644 index 0000000000..d9e745059a --- /dev/null +++ b/solidity/test/isms/PolygonZkevmIsm.t.sol @@ -0,0 +1,34 @@ +pragma solidity ^0.8.13; + +import {Test} from "forge-std/Test.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 {TestMailbox} from "../../contracts/test/TestMailbox.sol"; +import {Message} from "../../contracts/libs/Message.sol"; +import {MessageUtils} from "./IsmTestUtils.sol"; + +import {IPolygonZkEVMBridge} from "../../contracts/interfaces/polygonzkevm/IPolygonZkEVMBridge.sol"; +import {PolygonZkevmHook} from "../../contracts/hooks/PolygonZkevmHook.sol"; +import {PolygonZkevmIsm} from "../../contracts/isms/hook/PolygonZkevmIsm.sol"; + +contract PolygonZkevmIsmTest is Test { + using LibBit for uint256; + using TypeCasts for bytes32; + using StandardHookMetadata for bytes; + using Message for bytes; + + uint256 internal mainnetFork; + uint256 internal polygonZkevmFork; + + uint256 internal constant DEFAULT_GAS_LIMIT = 1_920_000; + + address internal alice = address(0x1); + + // ============ Immutable Variables ============ + + uint32 constant DESTINATION_DOMAIN = 1; +} From 6f89d45811bd007fe75becb70021009de36515d0 Mon Sep 17 00:00:00 2001 From: curlypoint Date: Sun, 14 Jan 2024 05:45:00 +0530 Subject: [PATCH 05/10] TODO: test cases --- .../hook/AbstractMessageIdAuthorizedIsm.sol | 2 +- .../contracts/isms/hook/PolygonZkevmIsm.sol | 32 ++++++------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol index fb97eb20ed..a81bbdedf2 100644 --- a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol +++ b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol @@ -99,7 +99,7 @@ abstract contract AbstractMessageIdAuthorizedIsm is * @dev Only callable by the authorized hook. * @param messageId Hyperlane Id of the message. */ - function verifyMessageId(bytes32 messageId) external payable virtual { + function verifyMessageId(bytes32 messageId) public payable virtual { require( _isAuthorized(), "AbstractMessageIdAuthorizedIsm: sender is not the hook" diff --git a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol index 80d849659a..00822ad7dc 100644 --- a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol +++ b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol @@ -40,6 +40,7 @@ contract PolygonZkevmIsm is { using Message for bytes; using LibBit for uint256; + using TypeCasts for bytes32; IMailbox public mailbox; string[] public offchainUrls; @@ -64,11 +65,6 @@ contract PolygonZkevmIsm is offchainUrls = _offchainUrls; } - /// @inheritdoc AbstractMessageIdAuthorizedIsm - function _isAuthorized() internal view override returns (bool) { - return msg.sender == address(zkEvmBridge); - } - function getOffchainVerifyInfo( bytes calldata _message ) external view override { @@ -88,11 +84,7 @@ contract PolygonZkevmIsm is bytes calldata ) external - override( - // bytes calldata _message - AbstractMessageIdAuthorizedIsm, - IInterchainSecurityModule - ) + override(AbstractMessageIdAuthorizedIsm, IInterchainSecurityModule) returns (bool) { ( @@ -144,21 +136,17 @@ contract PolygonZkevmIsm is /// @inheritdoc IBridgeMessageReceiver function onMessageReceived( address, - uint32 originNetwork, + uint32, bytes memory data ) external payable override { - require( - originNetwork == 0, - "PolygonZkevmIsm: origin network must be 0" - ); require(data.length == 32, "PolygonZkevmIsm: data must be 32 bytes"); bytes32 messageId = abi.decode(data, (bytes32)); - require( - !verifiedMessages[messageId].isBitSet(VERIFIED_MASK_INDEX), - "PolygonZkevmIsm: message already verified" - ); - verifiedMessages[messageId] = verifiedMessages[messageId].setBit( - VERIFIED_MASK_INDEX - ); + verifyMessageId(messageId); + } + + /// @inheritdoc AbstractMessageIdAuthorizedIsm + function _isAuthorized() internal view override returns (bool) { + bytes32 originSender = abi.decode(msg.data[4:], (bytes32)); + return originSender == authorizedHook; } } From 47f90ea73eaec2e60b67ee914fe27610f57957cb Mon Sep 17 00:00:00 2001 From: curlypoint Date: Sun, 14 Jan 2024 16:02:29 +0530 Subject: [PATCH 06/10] add some docs --- .../contracts/isms/hook/PolygonZkevmIsm.sol | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol index 00822ad7dc..ae0e1fc21a 100644 --- a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol +++ b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol @@ -65,11 +65,14 @@ contract PolygonZkevmIsm is offchainUrls = _offchainUrls; } + /** + * @dev off-chain verification information for a given message. + * @param _message The message for which off-chain verification information is requested. + */ function getOffchainVerifyInfo( bytes calldata _message ) external view override { bytes memory messageId = abi.encodePacked(_message.id()); - revert OffchainLookup( address(this), offchainUrls, @@ -79,6 +82,11 @@ contract PolygonZkevmIsm is ); } + /** + * @dev Calls the Polygon zkEVM bridge to claim the message. + * @param _metadata from CCIP call + * @return A boolean indicating whether the message was successfully verified and processed. + */ function verify( bytes calldata _metadata, bytes calldata @@ -114,7 +122,6 @@ contract PolygonZkevmIsm is ) ); - // bytes32 messageId = _message.id(); zkEvmBridge.claimMessage( smtProof, index, @@ -127,13 +134,15 @@ contract PolygonZkevmIsm is amount, payload ); - // verifiedMessages[messageId] = verifiedMessages[messageId].setBit( - // VERIFIED_MASK_INDEX - // ); + return true; } - /// @inheritdoc IBridgeMessageReceiver + /** + * @dev Callback function for Zkevm bridge. + * Verifies the received message. + * @inheritdoc IBridgeMessageReceiver + */ function onMessageReceived( address, uint32, @@ -144,7 +153,10 @@ contract PolygonZkevmIsm is verifyMessageId(messageId); } - /// @inheritdoc AbstractMessageIdAuthorizedIsm + /** + * @dev Checks if the origin chain message sender is the hook address. + * @inheritdoc AbstractMessageIdAuthorizedIsm + */ function _isAuthorized() internal view override returns (bool) { bytes32 originSender = abi.decode(msg.data[4:], (bytes32)); return originSender == authorizedHook; From 3b85f3d2907ef85eaa6de9915ede806943672f4a Mon Sep 17 00:00:00 2001 From: curlypoint Date: Mon, 15 Jan 2024 23:03:36 +0530 Subject: [PATCH 07/10] Try testing --- solidity/contracts/hooks/PolygonZkevmHook.sol | 2 +- solidity/test/isms/PolygonZkevmIsm.t.sol | 83 ++++++++++++++++++- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/solidity/contracts/hooks/PolygonZkevmHook.sol b/solidity/contracts/hooks/PolygonZkevmHook.sol index 4d1751da94..1e0117af77 100644 --- a/solidity/contracts/hooks/PolygonZkevmHook.sol +++ b/solidity/contracts/hooks/PolygonZkevmHook.sol @@ -54,7 +54,7 @@ contract PolygonZkevmHook is IPostDispatchHook, MailboxClient { ) MailboxClient(_mailbox) { require( Address.isContract(_zkEvmBridge), - "PolygonzkEVMHook: invalid cpManager contract" + "PolygonzkEVMHook: invalid PolygonZkEVMBridge contract" ); require(_ism != bytes32(0), "PolygonzkEVMHook: invalid ISM"); require( diff --git a/solidity/test/isms/PolygonZkevmIsm.t.sol b/solidity/test/isms/PolygonZkevmIsm.t.sol index d9e745059a..11444cc56e 100644 --- a/solidity/test/isms/PolygonZkevmIsm.t.sol +++ b/solidity/test/isms/PolygonZkevmIsm.t.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT or Apache-2.0 pragma solidity ^0.8.13; import {Test} from "forge-std/Test.sol"; @@ -10,10 +11,12 @@ import {AbstractMessageIdAuthorizedIsm} from "../../contracts/isms/hook/Abstract import {TestMailbox} from "../../contracts/test/TestMailbox.sol"; import {Message} from "../../contracts/libs/Message.sol"; import {MessageUtils} from "./IsmTestUtils.sol"; +import {TestRecipient} from "../../contracts/test/TestRecipient.sol"; +import "forge-std/console.sol"; -import {IPolygonZkEVMBridge} from "../../contracts/interfaces/polygonzkevm/IPolygonZkEVMBridge.sol"; import {PolygonZkevmHook} from "../../contracts/hooks/PolygonZkevmHook.sol"; import {PolygonZkevmIsm} from "../../contracts/isms/hook/PolygonZkevmIsm.sol"; +import {IPolygonZkEVMBridge} from "../../contracts/interfaces/polygonzkevm/IPolygonZkEVMBridge.sol"; contract PolygonZkevmIsmTest is Test { using LibBit for uint256; @@ -29,6 +32,82 @@ contract PolygonZkevmIsmTest is Test { address internal alice = address(0x1); // ============ Immutable Variables ============ - + uint32 internal constant ORIGIN_DOMAIN = 0; uint32 constant DESTINATION_DOMAIN = 1; + + address internal constant L0_POLYGON_ZK_EVM_BRIDGE = address(0); + address internal constant L1_POLYGON_ZK_EVM_BRIDGE = address(0); + + address internal constant L0_MESSENGER_ADDRESS = address(0); + address internal constant L1_MESSENGER_ADDRESS = address(0); + + TestRecipient internal testRecipient; + TestMailbox internal testMailbox; + + PolygonZkevmIsm internal polygonZkevmIsm; + PolygonZkevmHook internal polygonZkevmHook; + + IPolygonZkEVMBridge internal zkEvmBridge; + + uint256 internal isAccount; + + function setUp() public { + testRecipient = new TestRecipient(); + testMailbox = new TestMailbox(ORIGIN_DOMAIN); + zkEvmBridge = IPolygonZkEVMBridge(L0_POLYGON_ZK_EVM_BRIDGE); + + isAccount = address(zkEvmBridge).code.length; + + console.logAddress(address(zkEvmBridge)); + + console.logUint(isAccount); + } + + function testFork_quoteDispatch() public { + polygonZkevmIsm = new PolygonZkevmIsm( + IPolygonZkEVMBridge(zkEvmBridge), + testMailbox, + new string[](0) + ); + + polygonZkevmHook = new PolygonZkevmHook( + address(testMailbox), + DESTINATION_DOMAIN, + TypeCasts.addressToBytes32(address(polygonZkevmIsm)), + L1_POLYGON_ZK_EVM_BRIDGE, + 0 + ); + + // testMailbox.setHook(address(polygonZkevmHook)); + polygonZkevmIsm.setAuthorizedHook( + TypeCasts.addressToBytes32(address(polygonZkevmHook)) + ); + + uint256 fee = polygonZkevmHook.quoteDispatch("0x", "0x"); + assertEq(fee, 0); + } + + function testFork_supportsMetadata() public { + bool supportsMetadata = polygonZkevmHook.supportsMetadata("0x"); + assertTrue(supportsMetadata); + } + + function testFork_postDispatch() public { + bytes memory metadata = "0x"; + bytes memory message = "0x"; + polygonZkevmHook.postDispatch(metadata, message); + + // expectEmit + vm.expectEmit( + address(polygonZkevmHook) + // , + // 'bridgeMessageEvent', + // abi.encode( + // DESTINATION_DOMAIN, + // address(polygonZkevmHook), + // true, + // message.id() + // ) + ); + } } From ab9a927e5336a701e85257267c81582a6023317b Mon Sep 17 00:00:00 2001 From: curlypoint Date: Tue, 16 Jan 2024 02:42:53 +0530 Subject: [PATCH 08/10] revert extra changes to abstractmessageidautharizedism --- .../hook/AbstractMessageIdAuthorizedIsm.sol | 2 +- .../contracts/isms/hook/PolygonZkevmIsm.sol | 22 +++++++++++++++---- solidity/test/isms/PolygonZkevmIsm.t.sol | 20 ++++++++--------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol index a81bbdedf2..519d4803bc 100644 --- a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol +++ b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol @@ -74,7 +74,7 @@ abstract contract AbstractMessageIdAuthorizedIsm is bytes calldata, /*_metadata*/ bytes calldata message - ) external virtual returns (bool) { + ) external returns (bool) { bytes32 messageId = message.id(); // check for the first bit (used for verification) diff --git a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol index ae0e1fc21a..f9913509f6 100644 --- a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol +++ b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol @@ -53,15 +53,19 @@ contract PolygonZkevmIsm is // ============ Constructor ============ constructor( address _zkEvmBridge, - IMailbox _mailbox, + address _mailbox, string[] memory _offchainUrls ) { require( Address.isContract(_zkEvmBridge), - "PolygonZkevmIsm: invalid L2Messenger" + "PolygonZkevmIsm: invalid ZkEVMBridge" + ); + require( + Address.isContract(_zkEvmBridge), + "PolygonZkevmIsm: invalid IMailbox" ); zkEvmBridge = IPolygonZkEVMBridge(_zkEvmBridge); - mailbox = _mailbox; + mailbox = IMailbox(_mailbox); offchainUrls = _offchainUrls; } @@ -150,7 +154,17 @@ contract PolygonZkevmIsm is ) external payable override { require(data.length == 32, "PolygonZkevmIsm: data must be 32 bytes"); bytes32 messageId = abi.decode(data, (bytes32)); - verifyMessageId(messageId); + require( + _isAuthorized(), + "AbstractMessageIdAuthorizedIsm: sender is not the hook" + ); + require( + msg.value < 2 ** VERIFIED_MASK_INDEX, + "AbstractMessageIdAuthorizedIsm: msg.value must be less than 2^255" + ); + + verifiedMessages[messageId] = msg.value.setBit(VERIFIED_MASK_INDEX); + emit ReceivedMessage(messageId); } /** diff --git a/solidity/test/isms/PolygonZkevmIsm.t.sol b/solidity/test/isms/PolygonZkevmIsm.t.sol index 11444cc56e..eba260b8ce 100644 --- a/solidity/test/isms/PolygonZkevmIsm.t.sol +++ b/solidity/test/isms/PolygonZkevmIsm.t.sol @@ -55,18 +55,9 @@ contract PolygonZkevmIsmTest is Test { testRecipient = new TestRecipient(); testMailbox = new TestMailbox(ORIGIN_DOMAIN); zkEvmBridge = IPolygonZkEVMBridge(L0_POLYGON_ZK_EVM_BRIDGE); - - isAccount = address(zkEvmBridge).code.length; - - console.logAddress(address(zkEvmBridge)); - - console.logUint(isAccount); - } - - function testFork_quoteDispatch() public { polygonZkevmIsm = new PolygonZkevmIsm( - IPolygonZkEVMBridge(zkEvmBridge), - testMailbox, + address(zkEvmBridge), + address(testMailbox), new string[](0) ); @@ -77,7 +68,14 @@ contract PolygonZkevmIsmTest is Test { L1_POLYGON_ZK_EVM_BRIDGE, 0 ); + isAccount = address(zkEvmBridge).code.length; + + console.logAddress(address(zkEvmBridge)); + console.logUint(isAccount); + } + + function testFork_quoteDispatch() public { // testMailbox.setHook(address(polygonZkevmHook)); polygonZkevmIsm.setAuthorizedHook( TypeCasts.addressToBytes32(address(polygonZkevmHook)) From ebc73a6c9d83cf32f18ef5e286590ee55f8bd8f8 Mon Sep 17 00:00:00 2001 From: curlypoint Date: Thu, 18 Jan 2024 20:04:31 +0530 Subject: [PATCH 09/10] Fix: requested changes --- solidity/contracts/hooks/PolygonZkevmHook.sol | 17 ++- .../hook/AbstractMessageIdAuthorizedIsm.sol | 2 +- .../contracts/isms/hook/PolygonZkevmIsm.sol | 11 +- solidity/test/hooks/PolygonZkevmHook.t.sol | 85 ++++++++++++ solidity/test/isms/PolygonZkevmIsm.t.sol | 128 +++++++----------- 5 files changed, 152 insertions(+), 91 deletions(-) create mode 100644 solidity/test/hooks/PolygonZkevmHook.t.sol diff --git a/solidity/contracts/hooks/PolygonZkevmHook.sol b/solidity/contracts/hooks/PolygonZkevmHook.sol index 1e0117af77..873c974e76 100644 --- a/solidity/contracts/hooks/PolygonZkevmHook.sol +++ b/solidity/contracts/hooks/PolygonZkevmHook.sol @@ -39,24 +39,23 @@ contract PolygonZkevmHook is IPostDispatchHook, MailboxClient { IPolygonZkEVMBridge public immutable zkEvmBridge; // left-padded address for ISM to verify messages - bytes32 public immutable ism; + address public immutable ism; // Domain of chain on which the ISM is deployed uint32 public immutable destinationDomain; - - uint32 public immutable zkBridgeChainIdDestination; + // Polygon ZkevmBridge uses networkId 0 for Mainnet and 1 for rollup + uint32 public immutable zkEvmBridgeDestinationNetId; constructor( address _mailbox, uint32 _destinationDomain, - bytes32 _ism, + address _ism, address _zkEvmBridge, - uint32 _zkBridgeChainId + uint32 _zkEvmBridgeDestinationNetId ) MailboxClient(_mailbox) { require( Address.isContract(_zkEvmBridge), "PolygonzkEVMHook: invalid PolygonZkEVMBridge contract" ); - require(_ism != bytes32(0), "PolygonzkEVMHook: invalid ISM"); require( _destinationDomain != 0, "PolygonzkEVMHook: invalid destination domain" @@ -64,7 +63,7 @@ contract PolygonZkevmHook is IPostDispatchHook, MailboxClient { ism = _ism; destinationDomain = _destinationDomain; zkEvmBridge = IPolygonZkEVMBridge(_zkEvmBridge); - zkBridgeChainIdDestination = uint8(_zkBridgeChainId); + zkEvmBridgeDestinationNetId = uint8(_zkEvmBridgeDestinationNetId); } /// @inheritdoc IPostDispatchHook @@ -94,8 +93,8 @@ contract PolygonZkevmHook is IPostDispatchHook, MailboxClient { bytes32 messageId = message.id(); zkEvmBridge.bridgeMessage( - zkBridgeChainIdDestination, - TypeCasts.bytes32ToAddress(ism), + zkEvmBridgeDestinationNetId, + address(ism), true, abi.encodePacked(messageId) ); diff --git a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol index 519d4803bc..a81bbdedf2 100644 --- a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol +++ b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol @@ -74,7 +74,7 @@ abstract contract AbstractMessageIdAuthorizedIsm is bytes calldata, /*_metadata*/ bytes calldata message - ) external returns (bool) { + ) external virtual returns (bool) { bytes32 messageId = message.id(); // check for the first bit (used for verification) diff --git a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol index f9913509f6..93c719d4e7 100644 --- a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol +++ b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol @@ -61,8 +61,8 @@ contract PolygonZkevmIsm is "PolygonZkevmIsm: invalid ZkEVMBridge" ); require( - Address.isContract(_zkEvmBridge), - "PolygonZkevmIsm: invalid IMailbox" + Address.isContract(_mailbox), + "PolygonZkevmIsm: invalid Mailbox" ); zkEvmBridge = IPolygonZkEVMBridge(_zkEvmBridge); mailbox = IMailbox(_mailbox); @@ -153,7 +153,6 @@ contract PolygonZkevmIsm is bytes memory data ) external payable override { require(data.length == 32, "PolygonZkevmIsm: data must be 32 bytes"); - bytes32 messageId = abi.decode(data, (bytes32)); require( _isAuthorized(), "AbstractMessageIdAuthorizedIsm: sender is not the hook" @@ -163,6 +162,7 @@ contract PolygonZkevmIsm is "AbstractMessageIdAuthorizedIsm: msg.value must be less than 2^255" ); + bytes32 messageId = abi.decode(data, (bytes32)); verifiedMessages[messageId] = msg.value.setBit(VERIFIED_MASK_INDEX); emit ReceivedMessage(messageId); } @@ -172,6 +172,11 @@ contract PolygonZkevmIsm is * @inheritdoc AbstractMessageIdAuthorizedIsm */ function _isAuthorized() internal view override returns (bool) { + require( + msg.sender == address(zkEvmBridge), + "PolygonZkevmIsm: invalid sender" + ); + bytes32 originSender = abi.decode(msg.data[4:], (bytes32)); return originSender == authorizedHook; } diff --git a/solidity/test/hooks/PolygonZkevmHook.t.sol b/solidity/test/hooks/PolygonZkevmHook.t.sol new file mode 100644 index 0000000000..fc8b366d37 --- /dev/null +++ b/solidity/test/hooks/PolygonZkevmHook.t.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.13; + +import {Test} from "forge-std/Test.sol"; +import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; +import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol"; +import {TestMailbox} from "../../contracts/test/TestMailbox.sol"; +import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol"; +import {TestIsm} from "../../contracts/test/TestIsm.sol"; +import {IPostDispatchHook} from "../../contracts/interfaces/hooks/IPostDispatchHook.sol"; +import {Message} from "../../contracts/libs/Message.sol"; +import {TestRecipient} from "../../contracts/test/TestRecipient.sol"; + +import {PolygonZkevmHook} from "../../contracts/hooks/PolygonZkevmHook.sol"; + +import "forge-std/console.sol"; + +contract PolygonZkEVMBridge { + function bridgeMessage( + uint32, + address, + bool, + bytes calldata + ) external payable {} +} + +contract PolygonZkevmHooktest is Test { + using TypeCasts for bytes32; + using StandardHookMetadata for bytes; + using Message for bytes; + + // Contracts + TestPostDispatchHook public requiredHook; + TestMailbox public mailbox; + TestIsm public ism; + PolygonZkevmHook public hook; + + TestRecipient internal testRecipient; + + PolygonZkEVMBridge internal polygonZkevmBridge; + + bytes internal testMessage = + abi.encodePacked("Hello from the other chain!"); + bytes internal testMetadata = + StandardHookMetadata.overrideRefundAddress(address(this)); + + function setUp() public { + // Setup Hyperlane + requiredHook = new TestPostDispatchHook(); + mailbox = new TestMailbox(0); + ism = new TestIsm(); + polygonZkevmBridge = new PolygonZkEVMBridge(); + hook = new PolygonZkevmHook( + address(mailbox), + 1, + address(ism), + address(polygonZkevmBridge), + 1 + ); + } + + function test_postDispatch() public { + vm.expectCall( + address(polygonZkevmBridge), + abi.encodeCall( + polygonZkevmBridge.bridgeMessage, + (uint32(1), address(ism), true, abi.encode(testMessage.id())) + ) + ); + + hook.postDispatch(testMetadata, testMessage); + } + + function test_postDispatch_msgValue() public { + vm.expectRevert( + "PolygonzkEVMHook: msgValue must be less than 2 ** 255" + ); + testMetadata = StandardHookMetadata.overrideMsgValue(2 ** 255); + hook.postDispatch(testMetadata, testMessage); + } + + function test_postDispatch_supportsMetadata() public { + assertTrue(hook.supportsMetadata(testMetadata)); + } +} diff --git a/solidity/test/isms/PolygonZkevmIsm.t.sol b/solidity/test/isms/PolygonZkevmIsm.t.sol index eba260b8ce..a7c356a58e 100644 --- a/solidity/test/isms/PolygonZkevmIsm.t.sol +++ b/solidity/test/isms/PolygonZkevmIsm.t.sol @@ -1,111 +1,83 @@ -// SPDX-License-Identifier: MIT or Apache-2.0 +// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import {Test} from "forge-std/Test.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 {TestMailbox} from "../../contracts/test/TestMailbox.sol"; +import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol"; +import {TestIsm} from "../../contracts/test/TestIsm.sol"; +import {IPostDispatchHook} from "../../contracts/interfaces/hooks/IPostDispatchHook.sol"; import {Message} from "../../contracts/libs/Message.sol"; -import {MessageUtils} from "./IsmTestUtils.sol"; import {TestRecipient} from "../../contracts/test/TestRecipient.sol"; -import "forge-std/console.sol"; + +import {IInterchainSecurityModule} from "../../contracts/interfaces/IInterchainSecurityModule.sol"; import {PolygonZkevmHook} from "../../contracts/hooks/PolygonZkevmHook.sol"; import {PolygonZkevmIsm} from "../../contracts/isms/hook/PolygonZkevmIsm.sol"; -import {IPolygonZkEVMBridge} from "../../contracts/interfaces/polygonzkevm/IPolygonZkEVMBridge.sol"; -contract PolygonZkevmIsmTest is Test { - using LibBit for uint256; +import "forge-std/console.sol"; + +contract PolygonZkEVMBridge { + function bridgeMessage( + uint32, + address, + bool, + bytes calldata + ) external payable {} +} + +contract PolygonZkevmIsmtest is Test { using TypeCasts for bytes32; using StandardHookMetadata for bytes; using Message for bytes; - uint256 internal mainnetFork; - uint256 internal polygonZkevmFork; - - uint256 internal constant DEFAULT_GAS_LIMIT = 1_920_000; - - address internal alice = address(0x1); - - // ============ Immutable Variables ============ - uint32 internal constant ORIGIN_DOMAIN = 0; - uint32 constant DESTINATION_DOMAIN = 1; - - address internal constant L0_POLYGON_ZK_EVM_BRIDGE = address(0); - address internal constant L1_POLYGON_ZK_EVM_BRIDGE = address(0); - - address internal constant L0_MESSENGER_ADDRESS = address(0); - address internal constant L1_MESSENGER_ADDRESS = address(0); + // Contracts + TestPostDispatchHook public requiredHook; + TestMailbox public mailbox; + PolygonZkevmIsm public ism; TestRecipient internal testRecipient; - TestMailbox internal testMailbox; - PolygonZkevmIsm internal polygonZkevmIsm; - PolygonZkevmHook internal polygonZkevmHook; + PolygonZkEVMBridge internal polygonZkevmBridge; - IPolygonZkEVMBridge internal zkEvmBridge; + address internal hook; - uint256 internal isAccount; + bytes internal testMessage = + abi.encodePacked("Hello from the other chain!"); + bytes internal testMetadata = + StandardHookMetadata.overrideRefundAddress(address(this)); function setUp() public { - testRecipient = new TestRecipient(); - testMailbox = new TestMailbox(ORIGIN_DOMAIN); - zkEvmBridge = IPolygonZkEVMBridge(L0_POLYGON_ZK_EVM_BRIDGE); - polygonZkevmIsm = new PolygonZkevmIsm( - address(zkEvmBridge), - address(testMailbox), + // Setup Hyperlane + requiredHook = new TestPostDispatchHook(); + mailbox = new TestMailbox(0); + polygonZkevmBridge = new PolygonZkEVMBridge(); + ism = new PolygonZkevmIsm( + address(polygonZkevmBridge), + address(mailbox), new string[](0) ); - - polygonZkevmHook = new PolygonZkevmHook( - address(testMailbox), - DESTINATION_DOMAIN, - TypeCasts.addressToBytes32(address(polygonZkevmIsm)), - L1_POLYGON_ZK_EVM_BRIDGE, - 0 - ); - isAccount = address(zkEvmBridge).code.length; - - console.logAddress(address(zkEvmBridge)); - - console.logUint(isAccount); + hook = address(0x1); + ism.setAuthorizedHook(TypeCasts.addressToBytes32(address(hook))); + testRecipient = new TestRecipient(); } - function testFork_quoteDispatch() public { - // testMailbox.setHook(address(polygonZkevmHook)); - polygonZkevmIsm.setAuthorizedHook( - TypeCasts.addressToBytes32(address(polygonZkevmHook)) + function test_moduleType() public { + assertEq( + ism.moduleType(), + uint8(IInterchainSecurityModule.Types.CCIP_READ) ); - - uint256 fee = polygonZkevmHook.quoteDispatch("0x", "0x"); - assertEq(fee, 0); } - function testFork_supportsMetadata() public { - bool supportsMetadata = polygonZkevmHook.supportsMetadata("0x"); - assertTrue(supportsMetadata); - } + function test_verify() public { + bytes memory message = testMessage; + bytes memory metadata = testMetadata; - function testFork_postDispatch() public { - bytes memory metadata = "0x"; - bytes memory message = "0x"; - polygonZkevmHook.postDispatch(metadata, message); - - // expectEmit - vm.expectEmit( - address(polygonZkevmHook) - // , - // 'bridgeMessageEvent', - // abi.encode( - // DESTINATION_DOMAIN, - // address(polygonZkevmHook), - // true, - // message.id() - // ) - ); + // verify message + bool verified = ism.verify(metadata, message); + + // check that message is verified + assertEq(verified, true); } } From c4130b5245e08f437f0ab9adc3745786c32a30df Mon Sep 17 00:00:00 2001 From: curlypoint Date: Fri, 19 Jan 2024 02:11:50 +0530 Subject: [PATCH 10/10] add test, add check for messageId in verify ism (ran into stack too deep) --- solidity/contracts/hooks/PolygonZkevmHook.sol | 6 +- .../contracts/isms/hook/PolygonZkevmIsm.sol | 54 ++++-- solidity/test/isms/PolygonZkevmIsm.t.sol | 166 +++++++++++++++++- 3 files changed, 201 insertions(+), 25 deletions(-) diff --git a/solidity/contracts/hooks/PolygonZkevmHook.sol b/solidity/contracts/hooks/PolygonZkevmHook.sol index 873c974e76..7c984fe39d 100644 --- a/solidity/contracts/hooks/PolygonZkevmHook.sol +++ b/solidity/contracts/hooks/PolygonZkevmHook.sol @@ -60,6 +60,10 @@ contract PolygonZkevmHook is IPostDispatchHook, MailboxClient { _destinationDomain != 0, "PolygonzkEVMHook: invalid destination domain" ); + require( + _zkEvmBridgeDestinationNetId <= 1, + "PolygonZkevmIsm: invalid ZkEVMBridge destination network id" + ); ism = _ism; destinationDomain = _destinationDomain; zkEvmBridge = IPolygonZkEVMBridge(_zkEvmBridge); @@ -92,7 +96,7 @@ contract PolygonZkevmHook is IPostDispatchHook, MailboxClient { ); bytes32 messageId = message.id(); - zkEvmBridge.bridgeMessage( + zkEvmBridge.bridgeMessage{value: msg.value}( zkEvmBridgeDestinationNetId, address(ism), true, diff --git a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol index 93c719d4e7..8ce046f523 100644 --- a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol +++ b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol @@ -15,18 +15,21 @@ pragma solidity >=0.8.0; // ============ Internal Imports ============ -import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol"; +import {LibBit} from "../../libs/LibBit.sol"; import {Message} from "../../libs/Message.sol"; import {TypeCasts} from "../../libs/TypeCasts.sol"; + import {AbstractCcipReadIsm} from "../ccip-read/AbstractCcipReadIsm.sol"; -import {IMailbox} from "../../interfaces/IMailbox.sol"; -import {IPolygonZkEVMBridge} from "../../interfaces/polygonzkevm/IPolygonZkEVMBridge.sol"; import {AbstractMessageIdAuthorizedIsm} from "../hook/AbstractMessageIdAuthorizedIsm.sol"; + +import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol"; +import {IMailbox} from "../../interfaces/IMailbox.sol"; import {ICcipReadIsm} from "../../interfaces/isms/ICcipReadIsm.sol"; -import {LibBit} from "../../libs/LibBit.sol"; -import {IBridgeMessageReceiver} from "../../interfaces/polygonzkevm/IBridgeMessageReceiver.sol"; // ============ External Imports ============ + +import {IPolygonZkEVMBridge} from "../../interfaces/polygonzkevm/IPolygonZkEVMBridge.sol"; +import {IBridgeMessageReceiver} from "../../interfaces/polygonzkevm/IBridgeMessageReceiver.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; /** @@ -41,6 +44,7 @@ contract PolygonZkevmIsm is using Message for bytes; using LibBit for uint256; using TypeCasts for bytes32; + using Address for address payable; IMailbox public mailbox; string[] public offchainUrls; @@ -49,10 +53,12 @@ contract PolygonZkevmIsm is IPolygonZkEVMBridge public immutable zkEvmBridge; uint8 public constant override moduleType = uint8(IInterchainSecurityModule.Types.CCIP_READ); + uint32 public immutable zkEvmBridgeDestinationNetId; // ============ Constructor ============ constructor( address _zkEvmBridge, + uint32 _zkEvmBridgeDestinationNetId, address _mailbox, string[] memory _offchainUrls ) { @@ -64,6 +70,11 @@ contract PolygonZkevmIsm is Address.isContract(_mailbox), "PolygonZkevmIsm: invalid Mailbox" ); + require( + _zkEvmBridgeDestinationNetId <= 1, + "PolygonZkevmIsm: invalid ZkEVMBridge destination network id" + ); + zkEvmBridgeDestinationNetId = _zkEvmBridgeDestinationNetId; zkEvmBridge = IPolygonZkEVMBridge(_zkEvmBridge); mailbox = IMailbox(_mailbox); offchainUrls = _offchainUrls; @@ -93,12 +104,13 @@ contract PolygonZkevmIsm is */ function verify( bytes calldata _metadata, - bytes calldata + bytes calldata _message ) external override(AbstractMessageIdAuthorizedIsm, IInterchainSecurityModule) returns (bool) { + bytes32 messageId = _message.id(); ( bytes32[32] memory smtProof, uint32 index, @@ -106,8 +118,8 @@ contract PolygonZkevmIsm is bytes32 rollupExitRoot, uint32 originNetwork, address originAddress, - uint32 destinationNetwork, - address destinationAddress, + , + , uint256 amount, bytes memory payload ) = abi.decode( @@ -125,7 +137,10 @@ contract PolygonZkevmIsm is bytes ) ); - + require( + messageId == abi.decode(payload, (bytes32)), + "PolygonZkevmIsm: message id does not match payload" + ); zkEvmBridge.claimMessage( smtProof, index, @@ -133,11 +148,18 @@ contract PolygonZkevmIsm is rollupExitRoot, originNetwork, originAddress, - destinationNetwork, - destinationAddress, + zkEvmBridgeDestinationNetId, + address(this), amount, payload ); + uint256 _msgValue = verifiedMessages[messageId].clearBit( + VERIFIED_MASK_INDEX + ); + if (_msgValue > 0) { + verifiedMessages[messageId] -= _msgValue; + payable(_message.recipientAddress()).sendValue(_msgValue); + } return true; } @@ -152,6 +174,10 @@ contract PolygonZkevmIsm is uint32, bytes memory data ) external payable override { + require( + msg.sender == address(zkEvmBridge), + "PolygonZkevmIsm: invalid sender" + ); require(data.length == 32, "PolygonZkevmIsm: data must be 32 bytes"); require( _isAuthorized(), @@ -164,6 +190,7 @@ contract PolygonZkevmIsm is bytes32 messageId = abi.decode(data, (bytes32)); verifiedMessages[messageId] = msg.value.setBit(VERIFIED_MASK_INDEX); + emit ReceivedMessage(messageId); } @@ -172,11 +199,6 @@ contract PolygonZkevmIsm is * @inheritdoc AbstractMessageIdAuthorizedIsm */ function _isAuthorized() internal view override returns (bool) { - require( - msg.sender == address(zkEvmBridge), - "PolygonZkevmIsm: invalid sender" - ); - bytes32 originSender = abi.decode(msg.data[4:], (bytes32)); return originSender == authorizedHook; } diff --git a/solidity/test/isms/PolygonZkevmIsm.t.sol b/solidity/test/isms/PolygonZkevmIsm.t.sol index a7c356a58e..9de6f5b354 100644 --- a/solidity/test/isms/PolygonZkevmIsm.t.sol +++ b/solidity/test/isms/PolygonZkevmIsm.t.sol @@ -10,21 +10,48 @@ import {TestIsm} from "../../contracts/test/TestIsm.sol"; import {IPostDispatchHook} from "../../contracts/interfaces/hooks/IPostDispatchHook.sol"; import {Message} from "../../contracts/libs/Message.sol"; import {TestRecipient} from "../../contracts/test/TestRecipient.sol"; +import {ICcipReadIsm} from "../../contracts/interfaces/isms/ICcipReadIsm.sol"; import {IInterchainSecurityModule} from "../../contracts/interfaces/IInterchainSecurityModule.sol"; - +import {AbstractMessageIdAuthorizedIsm} from "../../contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol"; import {PolygonZkevmHook} from "../../contracts/hooks/PolygonZkevmHook.sol"; import {PolygonZkevmIsm} from "../../contracts/isms/hook/PolygonZkevmIsm.sol"; import "forge-std/console.sol"; contract PolygonZkEVMBridge { + PolygonZkevmIsm public ism; + bytes public returnData; + + function setIsm(PolygonZkevmIsm _ism) public { + ism = _ism; + } + + function setReturnData(bytes memory _returnData) public { + returnData = _returnData; + } + function bridgeMessage( uint32, address, bool, bytes calldata ) external payable {} + + function claimMessage( + bytes32[32] calldata, + uint32, + bytes32, + bytes32, + uint32, + address, + uint32, + address, + uint256, + bytes calldata + ) external payable { + ism.onMessageReceived(address(0x1), uint32(0), returnData); + } } contract PolygonZkevmIsmtest is Test { @@ -39,6 +66,7 @@ contract PolygonZkevmIsmtest is Test { TestRecipient internal testRecipient; + // address internal polygonZkevmBridge; PolygonZkEVMBridge internal polygonZkevmBridge; address internal hook; @@ -55,12 +83,19 @@ contract PolygonZkevmIsmtest is Test { polygonZkevmBridge = new PolygonZkEVMBridge(); ism = new PolygonZkevmIsm( address(polygonZkevmBridge), + uint32(0), address(mailbox), new string[](0) ); + hook = address(0x1); + ism.setAuthorizedHook(TypeCasts.addressToBytes32(address(hook))); testRecipient = new TestRecipient(); + + bytes memory messageId = abi.encodePacked(testMessage.id()); + polygonZkevmBridge.setIsm(ism); + polygonZkevmBridge.setReturnData(abi.encodePacked(messageId)); } function test_moduleType() public { @@ -70,14 +105,129 @@ contract PolygonZkevmIsmtest is Test { ); } - function test_verify() public { - bytes memory message = testMessage; - bytes memory metadata = testMetadata; + function test_getOffchainVerifyInfo() external { + bytes memory messageId = abi.encodePacked(testMessage.id()); + + vm.expectRevert( + abi.encodeWithSelector( + ICcipReadIsm.OffchainLookup.selector, + address(ism), + new string[](0), + messageId, + PolygonZkevmIsm.verify.selector, + testMessage + ) + ); - // verify message - bool verified = ism.verify(metadata, message); + ism.getOffchainVerifyInfo(testMessage); + } + + // ================== NEED HELP ================== + // function test_verifyPolygonIsm() public { + // bytes32[32] memory smtProof = [ + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0) + // ]; + // uint32 index = 0; + // bytes32 mainnetExitRoot = bytes32(0x0); + // bytes32 rollupExitRoot = bytes32(0x0); + // uint32 originNetwork = uint32(0); + // address originAddress = address(0x0); + // uint32 destinationNetwork = 1; + // address destinationAddress = address(0x0); + // uint256 amount = 0; + // bytes memory payload = abi.encodePacked(testMessage.id()); + + // bytes memory metadata = abi.encodePacked( + // smtProof, + // index, + // mainnetExitRoot, + // rollupExitRoot, + // originNetwork, + // originAddress, + // destinationNetwork, + // destinationAddress, + // amount, + // payload + // ); + // console.logBytes(metadata); + // ism.verify(metadata, testMessage); + + // } + + function test_onMessageReceived() public { + bytes32 messageId = testMessage.id(); + vm.prank(address(polygonZkevmBridge)); + ism.onMessageReceived(address(0x1), uint32(0), abi.encode(messageId)); + } + + function test_onMessageReceived_revertNotAuthBridge() public { + bytes32 messageId = testMessage.id(); + + vm.expectRevert("PolygonZkevmIsm: invalid sender"); + + ism.onMessageReceived(address(0x1), uint32(0), abi.encode(messageId)); + } + + function test_onMessageReceived_revertNot32Bytes() public { + vm.expectRevert("PolygonZkevmIsm: data must be 32 bytes"); + vm.prank(address(polygonZkevmBridge)); + + ism.onMessageReceived(address(0x1), uint32(0), abi.encode(testMessage)); + } + + function test_onMessageReceived_revertNoOriginHook() public { + bytes32 messageId = testMessage.id(); + vm.expectRevert( + "AbstractMessageIdAuthorizedIsm: sender is not the hook" + ); + vm.prank(address(polygonZkevmBridge)); + + ism.onMessageReceived(address(0x2), uint32(0), abi.encode(messageId)); + } - // check that message is verified - assertEq(verified, true); + function test_onMessageReceived_revertMsgTooBig() public { + bytes32 messageId = testMessage.id(); + hoax(address(polygonZkevmBridge), 2 ** 255); + + vm.expectRevert( + "AbstractMessageIdAuthorizedIsm: msg.value must be less than 2^255" + ); + + ism.onMessageReceived{value: 2 ** 255}( + address(0x1), + uint32(0), + abi.encode(messageId) + ); } }