parent
a9b64260c8
commit
e1d5133ae6
@ -1,131 +0,0 @@ |
||||
// SPDX-License-Identifier: MIT OR Apache-2.0 |
||||
pragma solidity >=0.6.11; |
||||
|
||||
// ============ Internal Imports ============ |
||||
import {IValidatorManager} from "../interfaces/IValidatorManager.sol"; |
||||
import {Outbox} from "./Outbox.sol"; |
||||
// ============ External Imports ============ |
||||
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; |
||||
import {ECDSA} from "@openzeppelin/contracts/cryptography/ECDSA.sol"; |
||||
|
||||
/** |
||||
* @title ValidatorManager |
||||
* @author Celo Labs Inc. |
||||
* @notice MVP version of contract that will manage Validator selection and |
||||
* rotataion. |
||||
*/ |
||||
contract ValidatorManager is IValidatorManager, Ownable { |
||||
// Mapping of domain -> validator address. |
||||
mapping(uint32 => address) public validators; |
||||
|
||||
// ============ Events ============ |
||||
|
||||
/** |
||||
* @notice Emitted when a validator is enrolled |
||||
* @param domain The domain for which the validator is being enrolled |
||||
* @param validator The address of the validator |
||||
*/ |
||||
event ValidatorEnrolled(uint32 indexed domain, address indexed validator); |
||||
|
||||
/** |
||||
* @notice Emitted when proof of an improper checkpoint is submitted, |
||||
* which sets the contract to FAILED state |
||||
* @param root Root of the improper checkpoint |
||||
* @param index Index of the improper checkpoint |
||||
* @param signature Signature on `root` and `index` |
||||
*/ |
||||
event ImproperCheckpoint( |
||||
address indexed outbox, |
||||
uint32 indexed domain, |
||||
address indexed validator, |
||||
bytes32 root, |
||||
uint256 index, |
||||
bytes signature |
||||
); |
||||
|
||||
// ============ Constructor ============ |
||||
|
||||
constructor() Ownable() {} |
||||
|
||||
// ============ External Functions ============ |
||||
|
||||
/** |
||||
* @notice Enroll a validator for the given domain |
||||
* @dev only callable by trusted owner |
||||
* @param _domain The domain for which the validator is being set |
||||
* @param _validator The address of the validator |
||||
*/ |
||||
function enrollValidator(uint32 _domain, address _validator) |
||||
external |
||||
onlyOwner |
||||
{ |
||||
validators[_domain] = _validator; |
||||
emit ValidatorEnrolled(_domain, _validator); |
||||
} |
||||
|
||||
/** |
||||
* @notice Check if an Checkpoint is an Improper Checkpoint; |
||||
* if so, set the provided Outbox contract to FAILED state. |
||||
* |
||||
* An Improper Checkpoint is an checkpoint that was not previously checkpointed. |
||||
* @param _outbox Address of the Outbox contract to set to FAILED. |
||||
* @param _root Merkle root of the improper checkpoint |
||||
* @param _index Index root of the improper checkpoint |
||||
* @param _signature Validator signature on `_root` and `_index` |
||||
* @return TRUE if checkpoint was an Improper Checkpoint (implying Validator was slashed) |
||||
*/ |
||||
function improperCheckpoint( |
||||
address _outbox, |
||||
bytes32 _root, |
||||
uint256 _index, |
||||
bytes calldata _signature |
||||
) external returns (bool) { |
||||
uint32 _domain = Outbox(_outbox).localDomain(); |
||||
require( |
||||
isValidatorSignature(_domain, _root, _index, _signature), |
||||
"!validator sig" |
||||
); |
||||
require(Outbox(_outbox).checkpoints(_root) != _index, "!improper"); |
||||
Outbox(_outbox).fail(); |
||||
emit ImproperCheckpoint( |
||||
_outbox, |
||||
_domain, |
||||
validators[_domain], |
||||
_root, |
||||
_index, |
||||
_signature |
||||
); |
||||
return true; |
||||
} |
||||
|
||||
// ============ Public Functions ============ |
||||
|
||||
/** |
||||
* @notice Checks that signature was signed by Validator |
||||
* @param _domain Domain of Outbox contract |
||||
* @param _root Merkle root |
||||
* @param _index Corresponding leaf index |
||||
* @param _signature Signature on `_root` and `_index` |
||||
* @return TRUE iff signature is valid signed by validator |
||||
**/ |
||||
function isValidatorSignature( |
||||
uint32 _domain, |
||||
bytes32 _root, |
||||
uint256 _index, |
||||
bytes calldata _signature |
||||
) public view override returns (bool) { |
||||
bytes32 _digest = keccak256( |
||||
abi.encodePacked(domainHash(_domain), _root, _index) |
||||
); |
||||
_digest = ECDSA.toEthSignedMessageHash(_digest); |
||||
return (ECDSA.recover(_digest, _signature) == validators[_domain]); |
||||
} |
||||
|
||||
/** |
||||
* @notice Hash of domain concatenated with "ABACUS" |
||||
* @param _domain the domain to hash |
||||
*/ |
||||
function domainHash(uint32 _domain) public pure returns (bytes32) { |
||||
return keccak256(abi.encodePacked(_domain, "ABACUS")); |
||||
} |
||||
} |
Loading…
Reference in new issue