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/core/contracts/Inbox.sol

149 lines
5.0 KiB

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
// ============ Internal Imports ============
import {Version0} from "./Version0.sol";
import {Mailbox} from "./Mailbox.sol";
import {MerkleLib} from "./libs/Merkle.sol";
import {Message} from "./libs/Message.sol";
import {TypeCasts} from "./libs/TypeCasts.sol";
import {IMessageRecipient} from "../interfaces/IMessageRecipient.sol";
import {IInbox} from "../interfaces/IInbox.sol";
// ============ External Imports ============
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
/**
* @title Inbox
* @author Celo Labs Inc.
* @notice Track root updates on Outbox, prove and dispatch messages to end
* recipients.
*/
contract Inbox is IInbox, ReentrancyGuardUpgradeable, Version0, Mailbox {
// ============ Libraries ============
using MerkleLib for MerkleLib.Tree;
using Message for bytes;
using TypeCasts for bytes32;
// ============ Enums ============
// Status of Message:
// 0 - None - message has not been processed
// 1 - Processed - message has been dispatched to recipient
enum MessageStatus {
None,
Processed
}
// ============ Public Storage ============
// Domain of outbox chain
uint32 public override remoteDomain;
// Mapping of message leaves to MessageStatus
mapping(bytes32 => MessageStatus) public messages;
// ============ Upgrade Gap ============
// gap for upgrade safety
uint256[48] private __GAP;
// ============ Events ============
/**
* @notice Emitted when message is processed
* @dev This event allows watchers to observe the merkle proof they need
* to prove fraud on the Outbox.
* @param messageHash Hash of message that was processed.
*/
event Process(bytes32 indexed messageHash);
// ============ Constructor ============
// solhint-disable-next-line no-empty-blocks
constructor(uint32 _localDomain) Mailbox(_localDomain) {}
// ============ Initializer ============
function initialize(uint32 _remoteDomain, address _validatorManager)
public
initializer
{
__ReentrancyGuard_init();
__Mailbox_initialize(_validatorManager);
remoteDomain = _remoteDomain;
}
// ============ External Functions ============
/**
* @notice Attempts to process the provided formatted `message`. Performs
* verification against root of the proof
* @dev Called by the validator manager, which is responsible for verifying a
* quorum of validator signatures on the checkpoint.
* @dev Reverts if verification of the message fails.
* @param _root The merkle root of the checkpoint used to prove message inclusion.
* @param _index The index of the checkpoint used to prove message inclusion.
* @param _message Formatted message (refer to Mailbox.sol Message library)
* @param _proof Merkle proof of inclusion for message's leaf
* @param _leafIndex Index of leaf in outbox's merkle tree
*/
function process(
bytes32 _root,
uint256 _index,
bytes calldata _message,
bytes32[32] calldata _proof,
uint256 _leafIndex
) external override nonReentrant onlyValidatorManager {
require(_index >= _leafIndex, "!index");
bytes32 _messageHash = _message.leaf(_leafIndex);
// ensure that message has not been processed
require(
messages[_messageHash] == MessageStatus.None,
"!MessageStatus.None"
);
// calculate the expected root based on the proof
bytes32 _calculatedRoot = MerkleLib.branchRoot(
_messageHash,
_proof,
_leafIndex
);
// verify the merkle proof
require(_calculatedRoot == _root, "!proof");
_process(_message, _messageHash);
}
// ============ Internal Functions ============
/**
* @notice Marks a message as processed and calls handle on the recipient
* @dev Internal function that can be called by contracts like TestInbox
* @param _message Formatted message (refer to Mailbox.sol Message library)
* @param _messageHash keccak256 hash of the message
*/
function _process(bytes calldata _message, bytes32 _messageHash) internal {
(
uint32 origin,
bytes32 sender,
uint32 destination,
bytes32 recipient,
bytes calldata body
) = _message.destructure();
// ensure message came from the correct domain
require(origin == remoteDomain, "!origin");
// ensure message was meant for this domain
require(destination == localDomain, "!destination");
// update message status as processed
messages[_messageHash] = MessageStatus.Processed;
IMessageRecipient(recipient.bytes32ToAddress()).handle(
origin,
sender,
body
);
emit Process(_messageHash);
}
}