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/contracts/ValidatorAnnounce.sol

118 lines
4.2 KiB

// 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();
}
}