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.
288 lines
9.1 KiB
288 lines
9.1 KiB
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
pragma solidity >=0.6.11;
|
|
|
|
import "./Common.sol";
|
|
import "./Merkle.sol";
|
|
import "./Queue.sol";
|
|
import "../interfaces/IUpdaterManager.sol";
|
|
|
|
import {Initializable} from "@openzeppelin/contracts/proxy/Initializable.sol";
|
|
import "@openzeppelin/contracts/utils/Address.sol";
|
|
|
|
/**
|
|
* @title Home
|
|
* @author Celo Labs Inc.
|
|
* @notice Contract responsible for managing production of the message tree and
|
|
* holding custody of the updater bond.
|
|
*/
|
|
contract Home is Initializable, MerkleTreeManager, QueueManager, Common {
|
|
using QueueLib for QueueLib.Queue;
|
|
using MerkleLib for MerkleLib.Tree;
|
|
|
|
/// @notice Set arbitrarily to 2 KiB
|
|
uint256 public constant MAX_MESSAGE_BODY_BYTES = 2 * 2**10;
|
|
|
|
/// @notice Mapping of sequence numbers for each destination
|
|
mapping(uint32 => uint32) public sequences;
|
|
|
|
IUpdaterManager public updaterManager;
|
|
address public owner;
|
|
|
|
/**
|
|
* @notice Event emitted when new message is enqueued
|
|
* @param leafIndex Index of message's leaf in merkle tree
|
|
* @param destinationAndSequence Destination and destination-specific
|
|
* sequence combined in single field ((destination << 32) & sequence)
|
|
* @param leaf Hash of formatted message
|
|
* @param message Raw bytes of enqueued message
|
|
*/
|
|
event Dispatch(
|
|
uint256 indexed leafIndex,
|
|
uint64 indexed destinationAndSequence,
|
|
bytes32 indexed leaf,
|
|
bytes message
|
|
);
|
|
|
|
/// @notice Event emitted when improper update detected
|
|
event ImproperUpdate();
|
|
|
|
/**
|
|
* @notice Event emitted when the UpdaterManager sets a new updater on Home
|
|
* @param updater The address of the new updater
|
|
*/
|
|
event NewUpdater(address updater);
|
|
|
|
/**
|
|
* @notice Event emitted when a new UpdaterManager is set
|
|
* @param updaterManager The address of the new updaterManager
|
|
*/
|
|
event NewUpdaterManager(address updaterManager);
|
|
|
|
/**
|
|
* @notice Event emitted when an updater is slashed
|
|
* @param updater The address of the updater
|
|
* @param reporter The address of the entity that reported the updater misbehavior
|
|
*/
|
|
event UpdaterSlashed(address indexed updater, address indexed reporter);
|
|
|
|
/**
|
|
* @notice Event emitted when a new owner is set
|
|
* @param previousOwner The address of the previous owner
|
|
* @param newOwner The address of the new owner
|
|
*/
|
|
event OwnershipTransferred(
|
|
address indexed previousOwner,
|
|
address indexed newOwner
|
|
);
|
|
|
|
constructor(uint32 _localDomain) Common(_localDomain) {} // solhint-disable-line no-empty-blocks
|
|
|
|
function initialize(IUpdaterManager _updaterManager) public initializer {
|
|
_transferOwnership(msg.sender);
|
|
|
|
_setUpdaterManager(_updaterManager);
|
|
|
|
queue.initialize();
|
|
|
|
address _updater = updaterManager.updater();
|
|
Common.initialize(_updater);
|
|
emit NewUpdater(_updater);
|
|
}
|
|
|
|
modifier onlyUpdaterManager() {
|
|
require(msg.sender == address(updaterManager), "!updaterManager");
|
|
_;
|
|
}
|
|
|
|
modifier onlyOwner() {
|
|
require(msg.sender == owner, "!owner");
|
|
_;
|
|
}
|
|
|
|
/// @notice Sets updater
|
|
function setUpdater(address _updater) external onlyUpdaterManager {
|
|
_setUpdater(_updater);
|
|
}
|
|
|
|
/// @notice sets a new updaterManager
|
|
function setUpdaterManager(address _updaterManager) external onlyOwner {
|
|
_setUpdaterManager(IUpdaterManager(_updaterManager));
|
|
}
|
|
|
|
/// @notice transfer owner role
|
|
function transferOwnership(address _newOwner) external onlyOwner {
|
|
_transferOwnership(_newOwner);
|
|
}
|
|
|
|
/**
|
|
* @notice Formats message, adds its leaf into merkle tree, enqueues new
|
|
* merkle root, and emits `Dispatch` event with data regarding message.
|
|
* @param _destination Domain of destination chain
|
|
* @param _recipient Address or recipient on destination chain
|
|
* @param _body Raw bytes of message
|
|
*/
|
|
function enqueue(
|
|
uint32 _destination,
|
|
bytes32 _recipient,
|
|
bytes memory _body
|
|
) external notFailed {
|
|
require(_body.length <= MAX_MESSAGE_BODY_BYTES, "!too big");
|
|
uint32 _sequence = sequences[_destination];
|
|
|
|
bytes memory _message = Message.formatMessage(
|
|
localDomain,
|
|
bytes32(uint256(uint160(msg.sender))),
|
|
_sequence,
|
|
_destination,
|
|
_recipient,
|
|
_body
|
|
);
|
|
bytes32 _leaf = keccak256(_message);
|
|
|
|
tree.insert(_leaf);
|
|
queue.enqueue(root());
|
|
|
|
// leafIndex is count() - 1 since new leaf has already been inserted
|
|
emit Dispatch(
|
|
count() - 1,
|
|
_destinationAndSequence(_destination, _sequence),
|
|
_leaf,
|
|
_message
|
|
);
|
|
|
|
sequences[_destination] = _sequence + 1;
|
|
}
|
|
|
|
/**
|
|
* @notice Called with updater's signature. Updates home's `current` root from `_oldRoot`
|
|
* to `_newRoot` and emits `Update` event. If fraudulent update
|
|
* detected in `improperUpdate`, updater is slashed and home is
|
|
* failed. Invalid signed roots on a Replica can be submitted
|
|
* by anyone here on Home and will lead to slashing as well.
|
|
* @param _oldRoot Old merkle root (should equal home's current root)
|
|
* @param _newRoot New merkle root
|
|
* @param _signature Updater's signature on `_oldRoot` and `_newRoot`
|
|
*/
|
|
function update(
|
|
bytes32 _oldRoot,
|
|
bytes32 _newRoot,
|
|
bytes memory _signature
|
|
) external notFailed {
|
|
if (improperUpdate(_oldRoot, _newRoot, _signature)) return;
|
|
while (true) {
|
|
bytes32 _next = queue.dequeue();
|
|
if (_next == _newRoot) break;
|
|
}
|
|
|
|
current = _newRoot;
|
|
emit Update(localDomain, _oldRoot, _newRoot, _signature);
|
|
}
|
|
|
|
/**
|
|
* @notice Suggests an update to caller. If queue is non-empty, returns the
|
|
* home's current root as `_current` and the queue's latest root as
|
|
* `_new`. Null bytes returned if queue is empty.
|
|
* @return _current Current root
|
|
* @return _new New root
|
|
*/
|
|
function suggestUpdate()
|
|
external
|
|
view
|
|
returns (bytes32 _current, bytes32 _new)
|
|
{
|
|
if (queue.length() != 0) {
|
|
_current = current;
|
|
_new = queue.lastItem();
|
|
}
|
|
}
|
|
|
|
/// @notice Hash of home's domain concatenated with "OPTICS"
|
|
function homeDomainHash() public view override returns (bytes32) {
|
|
return _homeDomainHash(localDomain);
|
|
}
|
|
|
|
/**
|
|
* @notice Checks that `_newRoot` in update currently exists in queue. If
|
|
* `_newRoot` doesn't exist in queue, update is fraudulent, causing
|
|
* updater to be slashed and home to be failed.
|
|
* @dev Reverts (and doesn't slash updater) if signature is invalid or
|
|
* update not current
|
|
* @param _oldRoot Old merkle tree root (should equal home's current root)
|
|
* @param _newRoot New merkle tree root
|
|
* @param _signature Updater's signature on `_oldRoot` and `_newRoot`
|
|
* @return Returns true if update was fraudulent
|
|
*/
|
|
function improperUpdate(
|
|
bytes32 _oldRoot,
|
|
bytes32 _newRoot,
|
|
bytes memory _signature
|
|
) public notFailed returns (bool) {
|
|
require(
|
|
Common._isUpdaterSignature(_oldRoot, _newRoot, _signature),
|
|
"bad sig"
|
|
);
|
|
require(_oldRoot == current, "not a current update");
|
|
if (!queue.contains(_newRoot)) {
|
|
_fail();
|
|
emit ImproperUpdate();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @notice sets a new owner
|
|
* @param _newOwner Address of new owner
|
|
*/
|
|
function _transferOwnership(address _newOwner) internal {
|
|
emit OwnershipTransferred(owner, _newOwner);
|
|
owner = _newOwner;
|
|
}
|
|
|
|
/**
|
|
* @notice sets a new updaterManager
|
|
* @param _updaterManager Address of new UpdaterManager
|
|
*/
|
|
function _setUpdaterManager(IUpdaterManager _updaterManager) internal {
|
|
require(
|
|
Address.isContract(address(_updaterManager)),
|
|
"!contract updaterManager"
|
|
);
|
|
|
|
updaterManager = IUpdaterManager(_updaterManager);
|
|
emit NewUpdaterManager(address(_updaterManager));
|
|
}
|
|
|
|
/**
|
|
* @notice sets a new updater
|
|
* @param _updater Address of new Updater
|
|
*/
|
|
function _setUpdater(address _updater) internal {
|
|
updater = _updater;
|
|
emit NewUpdater(_updater);
|
|
}
|
|
|
|
/// @notice Sets contract state to FAILED and slashes updater
|
|
function _fail() internal override {
|
|
_setFailed();
|
|
updaterManager.slashUpdater(msg.sender);
|
|
|
|
emit UpdaterSlashed(updater, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @notice Internal utility function that combines provided `_destination`
|
|
* and `_sequence`.
|
|
* @dev Both destination and sequence should be < 2^32 - 1
|
|
* @param _destination Domain of destination chain
|
|
* @param _sequence Current sequence for given destination chain
|
|
* @return Returns (`_destination` << 32) & `_sequence`
|
|
*/
|
|
function _destinationAndSequence(uint32 _destination, uint32 _sequence)
|
|
internal
|
|
pure
|
|
returns (uint64)
|
|
{
|
|
return (uint64(_destination) << 32) | _sequence;
|
|
}
|
|
}
|
|
|