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/isms/multisig/AbstractMultisigIsm.sol

125 lines
4.6 KiB

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
// ============ External Imports ============
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
// ============ Internal Imports ============
import {IInterchainSecurityModule} from "../../../interfaces/IInterchainSecurityModule.sol";
import {IMultisigIsm} from "../../../interfaces/isms/IMultisigIsm.sol";
import {Message} from "../../libs/Message.sol";
import {MultisigIsmMetadata} from "../../libs/isms/MultisigIsmMetadata.sol";
import {CheckpointLib} from "../../libs/CheckpointLib.sol";
import {MerkleLib} from "../../libs/Merkle.sol";
/**
* @title MultisigIsm
* @notice Manages per-domain m-of-n Validator sets that are used to verify
* interchain messages.
*/
abstract contract AbstractMultisigIsm is IMultisigIsm {
// ============ Constants ============
uint8 public constant moduleType =
uint8(IInterchainSecurityModule.Types.MULTISIG);
// ============ Virtual Functions ============
// ======= OVERRIDE THESE TO IMPLEMENT =======
/**
* @notice Returns the set of validators responsible for verifying _message
* and the number of signatures required
* @dev Can change based on the content of _message
* @param _message Hyperlane formatted interchain message
* @return validators The array of validator addresses
* @return threshold The number of validator signatures needed
*/
function validatorsAndThreshold(bytes calldata _message)
public
view
virtual
returns (address[] memory, uint8);
// ============ Public Functions ============
/**
* @notice Requires that m-of-n validators verify a merkle root,
* and verifies a merkle proof of `_message` against that root.
* @param _metadata ABI encoded module metadata (see MultisigIsmMetadata.sol)
* @param _message Formatted Hyperlane message (see Message.sol).
*/
function verify(bytes calldata _metadata, bytes calldata _message)
public
view
returns (bool)
{
require(_verifyMerkleProof(_metadata, _message), "!merkle");
require(_verifyValidatorSignatures(_metadata, _message), "!sigs");
return true;
}
// ============ Internal Functions ============
/**
* @notice Verifies the merkle proof of `_message` against the provided
* checkpoint.
* @param _metadata ABI encoded module metadata (see MultisigIsmMetadata.sol)
* @param _message Formatted Hyperlane message (see Message.sol).
*/
function _verifyMerkleProof(
bytes calldata _metadata,
bytes calldata _message
) internal pure returns (bool) {
// calculate the expected root based on the proof
bytes32 _calculatedRoot = MerkleLib.branchRoot(
Message.id(_message),
MultisigIsmMetadata.proof(_metadata),
Message.nonce(_message)
);
return _calculatedRoot == MultisigIsmMetadata.root(_metadata);
}
/**
* @notice Verifies that a quorum of the origin domain's validators signed
* the provided checkpoint.
* @param _metadata ABI encoded module metadata (see MultisigIsmMetadata.sol)
* @param _message Formatted Hyperlane message (see Message.sol).
*/
function _verifyValidatorSignatures(
bytes calldata _metadata,
bytes calldata _message
) internal view returns (bool) {
(
address[] memory _validators,
uint8 _threshold
) = validatorsAndThreshold(_message);
require(_threshold > 0, "No MultisigISM threshold present for message");
bytes32 _digest = CheckpointLib.digest(
Message.origin(_message),
MultisigIsmMetadata.originMailbox(_metadata),
MultisigIsmMetadata.root(_metadata),
MultisigIsmMetadata.index(_metadata)
);
uint256 _validatorCount = _validators.length;
uint256 _validatorIndex = 0;
// Assumes that signatures are ordered by validator
for (uint256 i = 0; i < _threshold; ++i) {
address _signer = ECDSA.recover(
_digest,
MultisigIsmMetadata.signatureAt(_metadata, i)
);
// Loop through remaining validators until we find a match
for (
;
_validatorIndex < _validatorCount &&
_signer != _validators[_validatorIndex];
++_validatorIndex
) {}
// Fail if we never found a match
require(_validatorIndex < _validatorCount, "!threshold");
++_validatorIndex;
}
return true;
}
}