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.
118 lines
4.2 KiB
118 lines
4.2 KiB
2 years ago
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||
|
pragma solidity >=0.8.0;
|
||
|
|
||
|
// ============ Internal Imports ============
|
||
|
import {IValidatorAnnounce} from "../interfaces/IValidatorAnnounce.sol";
|
||
|
import {IMailbox} from "../interfaces/IMailbox.sol";
|
||
|
import {TypeCasts} from "./libs/TypeCasts.sol";
|
||
|
import {ValidatorAnnouncements} from "./libs/ValidatorAnnouncements.sol";
|
||
|
// ============ External Imports ============
|
||
|
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
||
|
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
||
|
|
||
|
/**
|
||
|
* @title ValidatorAnnounce
|
||
|
* @notice Stores the location(s) of validator signed checkpoints
|
||
|
*/
|
||
|
contract ValidatorAnnounce is IValidatorAnnounce {
|
||
|
// ============ Libraries ============
|
||
|
|
||
|
using EnumerableSet for EnumerableSet.AddressSet;
|
||
|
using TypeCasts for address;
|
||
|
|
||
|
// ============ Constants ============
|
||
|
|
||
|
// Address of the mailbox being validated
|
||
|
address public immutable mailbox;
|
||
|
// Domain of chain on which the contract is deployed
|
||
|
uint32 public immutable localDomain;
|
||
|
|
||
|
// ============ Public Storage ============
|
||
|
|
||
|
// The set of validators that have announced
|
||
|
EnumerableSet.AddressSet private validators;
|
||
|
// Storage locations of validator signed checkpoints
|
||
|
mapping(address => string[]) private storageLocations;
|
||
|
// Mapping to prevent the same announcement from being registered
|
||
|
// multiple times.
|
||
|
mapping(bytes32 => bool) private replayProtection;
|
||
|
|
||
|
// ============ Events ============
|
||
|
|
||
|
/**
|
||
|
* @notice Emitted when a new validator announcement is made
|
||
|
* @param validator The address of the announcing validator
|
||
|
* @param storageLocation The storage location being announced
|
||
|
*/
|
||
|
event ValidatorAnnouncement(
|
||
|
address indexed validator,
|
||
|
string storageLocation
|
||
|
);
|
||
|
|
||
|
// ============ Constructor ============
|
||
|
|
||
|
constructor(address _mailbox) {
|
||
|
mailbox = _mailbox;
|
||
|
localDomain = IMailbox(mailbox).localDomain();
|
||
|
}
|
||
|
|
||
|
// ============ External Functions ============
|
||
|
|
||
|
/**
|
||
|
* @notice Announces a validator signature storage location
|
||
|
* @param _storageLocation Information encoding the location of signed
|
||
|
* checkpoints
|
||
|
* @param _signature The signed validator announcement
|
||
|
* @return True upon success
|
||
|
*/
|
||
|
function announce(
|
||
|
address _validator,
|
||
|
string calldata _storageLocation,
|
||
|
bytes calldata _signature
|
||
|
) external returns (bool) {
|
||
|
// Ensure that the same storage metadata isn't being announced
|
||
|
// multiple times for the same validator.
|
||
|
bytes32 _replayId = keccak256(
|
||
|
abi.encodePacked(_validator, _storageLocation)
|
||
|
);
|
||
|
require(replayProtection[_replayId] == false, "replay");
|
||
|
replayProtection[_replayId] = true;
|
||
|
|
||
|
// Verify that the signature matches the declared validator
|
||
|
bytes32 _announcementDigest = ValidatorAnnouncements
|
||
|
.getAnnouncementDigest(mailbox, localDomain, _storageLocation);
|
||
|
address _signer = ECDSA.recover(_announcementDigest, _signature);
|
||
|
require(_signer == _validator, "!signature");
|
||
|
|
||
|
// Store the announcement
|
||
|
if (!validators.contains(_validator)) {
|
||
|
validators.add(_validator);
|
||
|
}
|
||
|
storageLocations[_validator].push(_storageLocation);
|
||
|
emit ValidatorAnnouncement(_validator, _storageLocation);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Returns a list of all announced storage locations
|
||
|
* @param _validators The list of validators to get registrations for
|
||
|
* @return A list of registered storage metadata
|
||
|
*/
|
||
|
function getAnnouncedStorageLocations(address[] calldata _validators)
|
||
|
external
|
||
|
view
|
||
|
returns (string[][] memory)
|
||
|
{
|
||
|
string[][] memory _metadata = new string[][](_validators.length);
|
||
|
for (uint256 i = 0; i < _validators.length; i++) {
|
||
|
_metadata[i] = storageLocations[_validators[i]];
|
||
|
}
|
||
|
return _metadata;
|
||
|
}
|
||
|
|
||
|
/// @notice Returns a list of validators that have made announcements
|
||
|
function getAnnouncedValidators() external view returns (address[] memory) {
|
||
|
return validators.values();
|
||
|
}
|
||
|
}
|