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

158 lines
5.4 KiB

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
// ============ Internal Imports ============
import {Version0} from "./Version0.sol";
import {Common} from "./Common.sol";
import {MerkleLib} from "../libs/Merkle.sol";
import {Message} from "../libs/Message.sol";
import {IMessageRecipient} from "../interfaces/IMessageRecipient.sol";
import {IInbox} from "../interfaces/IInbox.sol";
// ============ External Imports ============
import {TypedMemView} from "@summa-tx/memview-sol/contracts/TypedMemView.sol";
/**
* @title Inbox
* @author Celo Labs Inc.
* @notice Track root updates on Outbox, prove and dispatch messages to end
* recipients.
*/
contract Inbox is IInbox, Version0, Common {
// ============ Libraries ============
using MerkleLib for MerkleLib.Tree;
using TypedMemView for bytes;
using TypedMemView for bytes29;
using Message for bytes29;
// ============ 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;
// re-entrancy guard
uint8 private entered;
// Mapping of message leaves to MessageStatus
mapping(bytes32 => MessageStatus) public messages;
// ============ Upgrade Gap ============
// gap for upgrade safety
uint256[47] private __GAP;
// ============ Events ============
/**
* @notice Emitted when message is processed
* @param messageHash Hash of message that failed to process
*/
event Process(bytes32 indexed messageHash);
// ============ Constructor ============
// solhint-disable-next-line no-empty-blocks
constructor(uint32 _localDomain) Common(_localDomain) {}
// ============ Initializer ============
function initialize(
uint32 _remoteDomain,
address _validatorManager,
bytes32 _checkpointedRoot,
uint256 _checkpointedIndex
) public initializer {
__Common_initialize(_validatorManager);
entered = 1;
remoteDomain = _remoteDomain;
_checkpoint(_checkpointedRoot, _checkpointedIndex);
}
// ============ External Functions ============
/**
* @notice Checkpoints the provided root and index.
* @dev Called by the validator manager, which is responsible for verifying a
* quorum of validator signatures on the checkpoint.
* @dev Reverts if checkpoints's index is not greater than our latest index.
* @param _root Checkpoint's merkle root.
* @param _index Checkpoint's index.
*/
function checkpoint(bytes32 _root, uint256 _index)
external
override
onlyValidatorManager
{
// Ensure that the checkpoint is more recent than the latest we've seen.
require(_index > checkpoints[checkpointedRoot], "old checkpoint");
_checkpoint(_root, _index);
}
/**
* @notice Attempts to process the provided formatted `message`. Performs
* verification against root of the proof
* @dev Reverts if verification of the message fails.
* @dev Includes the eventual function signature for Sovereign Consensus,
* but comments out the name to suppress compiler warning
* @param _message Formatted message (refer to Common.sol Message library)
* @param _proof Merkle proof of inclusion for message's leaf
* @param _index Index of leaf in outbox's merkle tree
*/
function process(
bytes calldata _message,
bytes32[32] calldata _proof,
uint256 _index,
bytes calldata /* _sovereignData */
) external override {
// check re-entrancy guard
require(entered == 1, "!reentrant");
entered = 0;
bytes32 _messageHash = keccak256(abi.encodePacked(_message, _index));
// 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,
_index
);
// ensure that the root has been checkpointed
require(checkpoints[_calculatedRoot] > 0, "!checkpointed root");
_process(_message, _messageHash);
// reset re-entrancy guard
entered = 1;
}
// ============ 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 Common.sol Message library)
* @param _messageHash keccak256 hash of the message
*/
function _process(bytes calldata _message, bytes32 _messageHash) internal {
bytes29 _m = _message.ref(0);
// ensure message was meant for this domain
require(_m.destination() == localDomain, "!destination");
// update message status as processed
messages[_messageHash] = MessageStatus.Processed;
IMessageRecipient _recipient = IMessageRecipient(_m.recipientAddress());
_recipient.handle(_m.origin(), _m.sender(), _m.body().clone());
// emit process results
emit Process(_messageHash);
}
}