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.
192 lines
6.2 KiB
192 lines
6.2 KiB
4 years ago
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||
3 years ago
|
pragma solidity >=0.8.0;
|
||
4 years ago
|
|
||
3 years ago
|
// ============ Internal Imports ============
|
||
2 years ago
|
import {Versioned} from "./upgrade/Versioned.sol";
|
||
3 years ago
|
import {Mailbox} from "./Mailbox.sol";
|
||
2 years ago
|
import {MerkleLib} from "./libs/Merkle.sol";
|
||
|
import {Message} from "./libs/Message.sol";
|
||
|
import {TypeCasts} from "./libs/TypeCasts.sol";
|
||
3 years ago
|
import {MerkleTreeManager} from "./MerkleTreeManager.sol";
|
||
3 years ago
|
import {IOutbox} from "../interfaces/IOutbox.sol";
|
||
4 years ago
|
|
||
4 years ago
|
/**
|
||
3 years ago
|
* @title Outbox
|
||
4 years ago
|
* @author Celo Labs Inc.
|
||
3 years ago
|
* @notice Accepts messages to be dispatched to remote chains,
|
||
|
* constructs a Merkle tree of the messages,
|
||
3 years ago
|
* and accepts signatures from a bonded Validator
|
||
3 years ago
|
* which notarize the Merkle tree roots.
|
||
|
* Accepts submissions of fraudulent signatures
|
||
3 years ago
|
* by the Validator and slashes the Validator in this case.
|
||
4 years ago
|
*/
|
||
2 years ago
|
contract Outbox is IOutbox, Versioned, MerkleTreeManager, Mailbox {
|
||
3 years ago
|
// ============ Libraries ============
|
||
|
|
||
4 years ago
|
using MerkleLib for MerkleLib.Tree;
|
||
3 years ago
|
using TypeCasts for address;
|
||
4 years ago
|
|
||
3 years ago
|
// ============ Constants ============
|
||
|
|
||
|
// Maximum bytes per message = 2 KiB
|
||
|
// (somewhat arbitrarily set to begin)
|
||
4 years ago
|
uint256 public constant MAX_MESSAGE_BODY_BYTES = 2 * 2**10;
|
||
|
|
||
3 years ago
|
// ============ Enums ============
|
||
|
|
||
|
// States:
|
||
|
// 0 - UnInitialized - before initialize function is called
|
||
|
// note: the contract is initialized at deploy time, so it should never be in this state
|
||
|
// 1 - Active - as long as the contract has not become fraudulent
|
||
|
// 2 - Failed - after a valid fraud proof has been submitted;
|
||
|
// contract will no longer accept updates or new messages
|
||
|
enum States {
|
||
|
UnInitialized,
|
||
|
Active,
|
||
|
Failed
|
||
|
}
|
||
|
|
||
3 years ago
|
// ============ Public Storage Variables ============
|
||
4 years ago
|
|
||
3 years ago
|
// Cached checkpoints, mapping root => leaf index.
|
||
|
// Cached checkpoints must have index > 0 as the presence of such
|
||
|
// a checkpoint cannot be distinguished from its absence.
|
||
|
mapping(bytes32 => uint256) public cachedCheckpoints;
|
||
|
// The latest cached root
|
||
|
bytes32 public latestCachedRoot;
|
||
3 years ago
|
// Current state of contract
|
||
|
States public state;
|
||
3 years ago
|
|
||
3 years ago
|
// ============ Upgrade Gap ============
|
||
|
|
||
|
// gap for upgrade safety
|
||
3 years ago
|
uint256[47] private __GAP;
|
||
3 years ago
|
|
||
|
// ============ Events ============
|
||
4 years ago
|
|
||
3 years ago
|
/**
|
||
|
* @notice Emitted when a checkpoint is cached.
|
||
|
* @param root Merkle root
|
||
|
* @param index Leaf index
|
||
|
*/
|
||
|
event CheckpointCached(bytes32 indexed root, uint256 indexed index);
|
||
|
|
||
4 years ago
|
/**
|
||
3 years ago
|
* @notice Emitted when a new message is dispatched via Abacus
|
||
4 years ago
|
* @param leafIndex Index of message's leaf in merkle tree
|
||
3 years ago
|
* @param message Raw bytes of message
|
||
4 years ago
|
*/
|
||
3 years ago
|
event Dispatch(uint256 indexed leafIndex, bytes message);
|
||
4 years ago
|
|
||
3 years ago
|
event Fail();
|
||
4 years ago
|
|
||
3 years ago
|
// ============ Constructor ============
|
||
4 years ago
|
|
||
3 years ago
|
constructor(uint32 _localDomain) Mailbox(_localDomain) {} // solhint-disable-line no-empty-blocks
|
||
4 years ago
|
|
||
3 years ago
|
// ============ Initializer ============
|
||
|
|
||
2 years ago
|
function initialize(address _validatorManager) external initializer {
|
||
3 years ago
|
__Mailbox_initialize(_validatorManager);
|
||
3 years ago
|
state = States.Active;
|
||
4 years ago
|
}
|
||
|
|
||
3 years ago
|
// ============ Modifiers ============
|
||
|
|
||
|
/**
|
||
3 years ago
|
* @notice Ensures that contract state != FAILED when the function is called
|
||
3 years ago
|
*/
|
||
3 years ago
|
modifier notFailed() {
|
||
3 years ago
|
require(state != States.Failed, "failed state");
|
||
4 years ago
|
_;
|
||
|
}
|
||
|
|
||
3 years ago
|
// ============ External Functions ============
|
||
|
|
||
4 years ago
|
/**
|
||
3 years ago
|
* @notice Dispatch the message it to the destination domain & recipient
|
||
|
* @dev Format the message, insert its hash into Merkle tree,
|
||
3 years ago
|
* and emit `Dispatch` event with message information.
|
||
3 years ago
|
* @param _destinationDomain Domain of destination chain
|
||
|
* @param _recipientAddress Address of recipient on destination chain as bytes32
|
||
|
* @param _messageBody Raw bytes content of message
|
||
3 years ago
|
* @return The leaf index of the dispatched message's hash in the Merkle tree.
|
||
4 years ago
|
*/
|
||
3 years ago
|
function dispatch(
|
||
3 years ago
|
uint32 _destinationDomain,
|
||
|
bytes32 _recipientAddress,
|
||
3 years ago
|
bytes calldata _messageBody
|
||
3 years ago
|
) external override notFailed returns (uint256) {
|
||
3 years ago
|
require(_messageBody.length <= MAX_MESSAGE_BODY_BYTES, "msg too long");
|
||
2 years ago
|
// The leaf has not been inserted yet at this point
|
||
3 years ago
|
uint256 _leafIndex = count();
|
||
3 years ago
|
// format the message into packed bytes
|
||
3 years ago
|
bytes memory _message = Message.formatMessage(
|
||
|
localDomain,
|
||
3 years ago
|
msg.sender.addressToBytes32(),
|
||
3 years ago
|
_destinationDomain,
|
||
|
_recipientAddress,
|
||
|
_messageBody
|
||
3 years ago
|
);
|
||
3 years ago
|
// insert the hashed message into the Merkle tree
|
||
3 years ago
|
bytes32 _messageHash = keccak256(
|
||
|
abi.encodePacked(_message, _leafIndex)
|
||
|
);
|
||
3 years ago
|
tree.insert(_messageHash);
|
||
3 years ago
|
emit Dispatch(_leafIndex, _message);
|
||
3 years ago
|
return _leafIndex;
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
/**
|
||
3 years ago
|
* @notice Caches the current merkle root and index.
|
||
3 years ago
|
* @dev emits CheckpointCached event
|
||
4 years ago
|
*/
|
||
3 years ago
|
function cacheCheckpoint() external override notFailed {
|
||
3 years ago
|
(bytes32 _root, uint256 _index) = latestCheckpoint();
|
||
3 years ago
|
require(_index > 0, "!index");
|
||
3 years ago
|
cachedCheckpoints[_root] = _index;
|
||
|
latestCachedRoot = _root;
|
||
|
emit CheckpointCached(_root, _index);
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
/**
|
||
3 years ago
|
* @notice Set contract state to FAILED.
|
||
3 years ago
|
* @dev Called by the validator manager when fraud is proven.
|
||
4 years ago
|
*/
|
||
3 years ago
|
function fail() external override onlyValidatorManager {
|
||
3 years ago
|
// set contract to FAILED
|
||
|
state = States.Failed;
|
||
|
emit Fail();
|
||
4 years ago
|
}
|
||
4 years ago
|
|
||
3 years ago
|
/**
|
||
|
* @notice Returns the latest entry in the checkpoint cache.
|
||
|
* @return root Latest cached root
|
||
|
* @return index Latest cached index
|
||
|
*/
|
||
|
function latestCachedCheckpoint()
|
||
|
external
|
||
|
view
|
||
|
returns (bytes32 root, uint256 index)
|
||
|
{
|
||
|
root = latestCachedRoot;
|
||
|
index = cachedCheckpoints[root];
|
||
|
}
|
||
|
|
||
3 years ago
|
/**
|
||
3 years ago
|
* @notice Returns the number of inserted leaves in the tree
|
||
3 years ago
|
*/
|
||
3 years ago
|
function count() public view returns (uint256) {
|
||
|
return tree.count;
|
||
3 years ago
|
}
|
||
|
|
||
4 years ago
|
/**
|
||
3 years ago
|
* @notice Returns a checkpoint representing the current merkle tree.
|
||
|
* @return root The root of the Outbox's merkle tree.
|
||
|
* @return index The index of the last element in the tree.
|
||
4 years ago
|
*/
|
||
3 years ago
|
function latestCheckpoint() public view returns (bytes32, uint256) {
|
||
|
return (root(), count() - 1);
|
||
4 years ago
|
}
|
||
4 years ago
|
}
|