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

188 lines
5.8 KiB

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
// ============ Internal Imports ============
import {QueueManager} from "./Queue.sol";
import {Message} from "../libs/Message.sol";
// ============ External Imports ============
import {ECDSA} from "@openzeppelin/contracts/cryptography/ECDSA.sol";
/**
* @title Common
* @author Celo Labs Inc.
* @notice Shared utilities between Home and Replica.
*/
abstract contract Common is QueueManager {
// ============ 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
}
// ============ Immutable Variables ============
// Domain of chain on which the contract is deployed
uint32 public immutable localDomain;
// ============ Public Variables ============
// Address of bonded Updater
address public updater;
// Current state of contract
States public state;
// The latest root that has been signed by the Updater
bytes32 public current;
// ============ Upgrade Gap ============
// gap for upgrade safety
uint256[47] private __GAP;
// ============ Events ============
/**
* @notice Event emitted when update is made on Home or unconfirmed update
* root is enqueued on Replica
* @param homeDomain Domain of home contract
* @param oldRoot Old merkle root
* @param newRoot New merkle root
* @param signature Updater's signature on `oldRoot` and `newRoot`
*/
event Update(
uint32 indexed homeDomain,
bytes32 indexed oldRoot,
bytes32 indexed newRoot,
bytes signature
);
/**
* @notice Emitted when proof of a double update is submitted,
* which sets the contract to FAILED state
* @param oldRoot Old root shared between two conflicting updates
* @param newRoot Array containing two conflicting new roots
* @param signature Signature on `oldRoot` and `newRoot`[0]
* @param signature2 Signature on `oldRoot` and `newRoot`[1]
*/
event DoubleUpdate(
bytes32 oldRoot,
bytes32[2] newRoot,
bytes signature,
bytes signature2
);
// ============ Modifiers ============
/**
* @notice Ensures that contract state != FAILED when the function is called
*/
modifier notFailed() {
require(state != States.FAILED, "failed state");
_;
}
// ============ Constructor ============
constructor(uint32 _localDomain) {
localDomain = _localDomain;
}
// ============ Initializer ============
function __Common_initialize(address _updater) internal initializer {
__QueueManager_intialize();
updater = _updater;
state = States.ACTIVE;
}
// ============ External Functions ============
/**
* @notice Called by external agent. Checks that signatures on two sets of
* roots are valid and that the new roots conflict with each other. If both
* cases hold true, the contract is failed and a `DoubleUpdate` event is
* emitted.
* @dev When `fail()` is called on Home, updater is slashed.
* @param _oldRoot Old root shared between two conflicting updates
* @param _newRoot Array containing two conflicting new roots
* @param _signature Signature on `_oldRoot` and `_newRoot`[0]
* @param _signature2 Signature on `_oldRoot` and `_newRoot`[1]
*/
function doubleUpdate(
bytes32 _oldRoot,
bytes32[2] calldata _newRoot,
bytes calldata _signature,
bytes calldata _signature2
) external notFailed {
if (
Common._isUpdaterSignature(_oldRoot, _newRoot[0], _signature) &&
Common._isUpdaterSignature(_oldRoot, _newRoot[1], _signature2) &&
_newRoot[0] != _newRoot[1]
) {
_fail();
emit DoubleUpdate(_oldRoot, _newRoot, _signature, _signature2);
}
}
// ============ Public Functions ============
/**
* @notice Hash of Home domain concatenated with "OPTICS"
*/
function homeDomainHash() public view virtual returns (bytes32);
// ============ Internal Functions ============
/**
* @notice Hash of Home domain concatenated with "OPTICS"
* @param _homeDomain the Home domain to hash
*/
function _homeDomainHash(uint32 _homeDomain)
internal
pure
returns (bytes32)
{
return keccak256(abi.encodePacked(_homeDomain, "OPTICS"));
}
/**
* @notice Set contract state to FAILED
* @dev Called when a valid fraud proof is submitted
*/
function _setFailed() internal {
state = States.FAILED;
}
/**
* @notice Performs the state modifications necessary
* to move the contract into failed state
* @dev Called when a double update or fraudulent update is detected
*/
function _fail() internal virtual;
/**
* @notice Checks that signature was signed by Updater
* @param _oldRoot Old merkle root
* @param _newRoot New merkle root
* @param _signature Signature on `_oldRoot` and `_newRoot`
* @return TRUE iff signature is valid signed by updater
**/
function _isUpdaterSignature(
bytes32 _oldRoot,
bytes32 _newRoot,
bytes memory _signature
) internal view returns (bool) {
bytes32 _digest = keccak256(
abi.encodePacked(homeDomainHash(), _oldRoot, _newRoot)
);
_digest = ECDSA.toEthSignedMessageHash(_digest);
return (ECDSA.recover(_digest, _signature) == updater);
}
}