// SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.8.0; pragma experimental ABIEncoderV2; /* Library Imports */ import { Lib_OVMCodec } from "@eth-optimism/contracts/build/contracts/libraries/codec/Lib_OVMCodec.sol"; import { Lib_AddressResolver } from "@eth-optimism/contracts/build/contracts/libraries/resolver/Lib_AddressResolver.sol"; import { Lib_AddressManager } from "@eth-optimism/contracts/build/contracts/libraries/resolver/Lib_AddressManager.sol"; import { Lib_SecureMerkleTrie } from "@eth-optimism/contracts/build/contracts/libraries/trie/Lib_SecureMerkleTrie.sol"; import { Lib_ReentrancyGuard } from "@eth-optimism/contracts/build/contracts/libraries/utils/Lib_ReentrancyGuard.sol"; /* Interface Imports */ import { iL1_CustomCrossDomainMessenger } from "./interfaces/iL1_CustomCrossDomainMessenger.sol"; import { iOVM_CanonicalTransactionChain } from "@eth-optimism/contracts/build/contracts/iOVM/chain/iOVM_CanonicalTransactionChain.sol"; import { iOVM_StateCommitmentChain } from "@eth-optimism/contracts/build/contracts/iOVM/chain/iOVM_StateCommitmentChain.sol"; import { BaseCustomCrossDomainMessenger } from "./BaseCustomCrossDomainMessenger.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; contract L1_CustomCrossDomainMessenger is iL1_CustomCrossDomainMessenger, BaseCustomCrossDomainMessenger, Lib_AddressResolver { using SafeMath for uint256; address l2_customCrossDomainMessenger; /*************** * Constructor * ***************/ /** * @param _libAddressManager Address of the Address Manager. */ constructor(address _libAddressManager) Lib_AddressResolver(_libAddressManager) {} /** * @param _l2_customCrossDomainMessenger Address of the L2_CustomCrossDomainMessenger contract this contract depends on. */ function initialize(address _l2_customCrossDomainMessenger) public { require( address(l2_customCrossDomainMessenger) == address(0), "L2_CustomCrossDomainMessenger already intialized." ); l2_customCrossDomainMessenger = _l2_customCrossDomainMessenger; } /******************** * Public Functions * ********************/ /** * Relays a cross domain message to a contract. * @inheritdoc iL1_CustomCrossDomainMessenger */ function relayMessage( address _target, address _sender, bytes memory _message, uint256 _messageNonce, uint256 _timelock, L2MessageInclusionProof memory _proof ) public override nonReentrant { bytes memory xDomainCalldata = _getXDomainCalldata( _target, _sender, _message, _messageNonce, _timelock ); require( _verifyXDomainMessage(xDomainCalldata, _timelock, _proof) == true, "Provided message could not be verified." ); bytes32 xDomainCalldataHash = keccak256(xDomainCalldata); require( successfulMessages[xDomainCalldataHash] == false, "Provided message has already been received." ); xDomainMessageSender = _sender; (bool success, ) = _target.call(_message); // Mark the message as received if the call was successful. Ensures that a message can be // relayed multiple times in the case that the call reverted. if (success == true) { successfulMessages[xDomainCalldataHash] = true; emit RelayedMessage(xDomainCalldataHash); } // Store an identifier that can be used to prove that the given message was relayed by some // user. Gives us an easy way to pay relayers for their work. bytes32 relayId = keccak256( abi.encodePacked(xDomainCalldata, msg.sender, block.number) ); relayedMessages[relayId] = true; } /** * Replays a cross domain message to the target messenger. * @inheritdoc iL1_CustomCrossDomainMessenger */ function replayMessage( address _target, address _sender, bytes memory _message, uint256 _messageNonce, uint256 timelock, uint32 _gasLimit ) public override { bytes memory xDomainCalldata = _getXDomainCalldata( _target, _sender, _message, _messageNonce, timelock ); require( sentMessages[keccak256(xDomainCalldata)] == true, "Provided message has not already been sent." ); _sendXDomainMessage(xDomainCalldata, _gasLimit); } /********************** * Internal Functions * **********************/ /** * Verifies that the given message is valid. * @param _xDomainCalldata Calldata to verify. * @param _proof Inclusion proof for the message. * @param _timelock Timelock in seconds that this message must have waited out before it can be relayed. * @return Whether or not the provided message is valid. */ function _verifyXDomainMessage( bytes memory _xDomainCalldata, uint256 _timelock, L2MessageInclusionProof memory _proof ) internal view returns (bool) { return (_verifyStateRootProof(_proof, _timelock) && _verifyStorageProof(_xDomainCalldata, _proof)); } /** * Verifies that the state root within an inclusion proof is valid. * @param _proof Message inclusion proof. * @param _timelock Timelock in seconds that this root is older than. * @return Whether or not the provided proof is valid. */ function _verifyStateRootProof( L2MessageInclusionProof memory _proof, uint256 _timelock ) internal view returns (bool) { iOVM_StateCommitmentChain ovmStateCommitmentChain = iOVM_StateCommitmentChain(resolve("OVM_StateCommitmentChain")); (uint256 timestamp, ) = abi.decode( _proof.stateRootBatchHeader.extraData, (uint256, address) ); require(timestamp != 0, "Batch header timestamp cannot be zero"); return timestamp.add(_timelock) < block.timestamp && ovmStateCommitmentChain.verifyStateCommitment( _proof.stateRoot, _proof.stateRootBatchHeader, _proof.stateRootProof ); } /** * Verifies that the storage proof within an inclusion proof is valid. * @param _xDomainCalldata Encoded message calldata. * @param _proof Message inclusion proof. * @return Whether or not the provided proof is valid. */ function _verifyStorageProof( bytes memory _xDomainCalldata, L2MessageInclusionProof memory _proof ) internal view returns (bool) { bytes32 storageKey = keccak256( abi.encodePacked( keccak256( abi.encodePacked( _xDomainCalldata, l2_customCrossDomainMessenger ) ), uint256(0) ) ); (bool exists, bytes memory encodedMessagePassingAccount) = Lib_SecureMerkleTrie.get( abi.encodePacked(0x4200000000000000000000000000000000000000), _proof.stateTrieWitness, _proof.stateRoot ); require( exists == true, "Message passing precompile has not been initialized or invalid proof provided." ); Lib_OVMCodec.EVMAccount memory account = Lib_OVMCodec.decodeEVMAccount(encodedMessagePassingAccount); return Lib_SecureMerkleTrie.verifyInclusionProof( abi.encodePacked(storageKey), abi.encodePacked(uint8(1)), _proof.storageTrieWitness, account.storageRoot ); } /** * Sends a cross domain message. * @param _message Message to send. * @param _gasLimit OVM gas limit for the message. */ function _sendXDomainMessage(bytes memory _message, uint256 _gasLimit) internal override { iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain")) .enqueue(l2_customCrossDomainMessenger, _gasLimit, _message); } }