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/test/isms/WeightedMultisigIsm.t.sol

317 lines
9.7 KiB

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import {IInterchainSecurityModule} from "../../contracts/interfaces/IInterchainSecurityModule.sol";
import {Message} from "../../contracts/libs/Message.sol";
import {IStaticWeightedMultisigIsm} from "../../contracts/interfaces/isms/IWeightedMultisigIsm.sol";
import {StaticMerkleRootWeightedMultisigIsmFactory, StaticMessageIdWeightedMultisigIsmFactory} from "../../contracts/isms/multisig/WeightedMultisigIsm.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {CheckpointLib} from "../../contracts/libs/CheckpointLib.sol";
import {StaticWeightedValidatorSetFactory} from "../../contracts/libs/StaticWeightedValidatorSetFactory.sol";
import {AbstractStaticWeightedMultisigIsm} from "../../contracts/isms/multisig/AbstractWeightedMultisigIsm.sol";
import {TestMailbox} from "../../contracts/test/TestMailbox.sol";
import {TestMerkleTreeHook} from "../../contracts/test/TestMerkleTreeHook.sol";
import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol";
import {MessageIdMultisigIsmMetadata} from "../../contracts/isms/libs/MessageIdMultisigIsmMetadata.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {AbstractMultisigIsmTest, MerkleRootMultisigIsmTest, MessageIdMultisigIsmTest} from "./MultisigIsm.t.sol";
abstract contract AbstractStaticWeightedMultisigIsmTest is
AbstractMultisigIsmTest
{
using Math for uint256;
using Message for bytes;
using TypeCasts for address;
StaticWeightedValidatorSetFactory weightedFactory;
AbstractStaticWeightedMultisigIsm weightedIsm;
uint96 public constant TOTAL_WEIGHT = 1e10;
function addValidators(
uint96 threshold,
uint8 n,
bytes32 seed
)
internal
returns (
uint256[] memory,
IStaticWeightedMultisigIsm.ValidatorInfo[] memory
)
{
bound(threshold, 0, TOTAL_WEIGHT);
uint256[] memory keys = new uint256[](n);
IStaticWeightedMultisigIsm.ValidatorInfo[]
memory validators = new IStaticWeightedMultisigIsm.ValidatorInfo[](
n
);
uint256 remainingWeight = TOTAL_WEIGHT;
for (uint256 i = 0; i < n; i++) {
uint256 key = uint256(keccak256(abi.encode(seed, i)));
keys[i] = key;
validators[i].signingAddress = vm.addr(key);
if (i == n - 1) {
validators[i].weight = uint96(remainingWeight);
} else {
uint256 weight = (uint256(
keccak256(abi.encode(seed, "weight", i))
) % (remainingWeight + 1));
validators[i].weight = uint96(weight);
remainingWeight -= weight;
}
}
ism = IInterchainSecurityModule(
weightedFactory.deploy(validators, threshold)
);
weightedIsm = AbstractStaticWeightedMultisigIsm(address(ism));
return (keys, validators);
}
function getMetadata(
uint8 m,
uint8 n,
bytes32 seed,
bytes memory message
) internal virtual override returns (bytes memory) {
bytes32 digest;
{
uint32 domain = mailbox.localDomain();
(bytes32 root, uint32 index) = merkleTreeHook.latestCheckpoint();
bytes32 messageId = message.id();
bytes32 merkleTreeAddress = address(merkleTreeHook)
.addressToBytes32();
digest = CheckpointLib.digest(
domain,
merkleTreeAddress,
root,
index,
messageId
);
}
uint96 threshold = uint96(
(uint256(m)).mulDiv(TOTAL_WEIGHT, type(uint8).max)
);
(
uint256[] memory keys,
IStaticWeightedMultisigIsm.ValidatorInfo[] memory allValidators
) = addValidators(threshold, n, seed);
(, uint96 thresholdWeight) = weightedIsm.validatorsAndThresholdWeight(
message
);
bytes memory metadata = metadataPrefix(message);
fixtureInit();
uint96 totalWeight = 0;
uint256 signerCount = 0;
while (
totalWeight < thresholdWeight && signerCount < allValidators.length
) {
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
keys[signerCount],
digest
);
metadata = abi.encodePacked(metadata, r, s, v);
fixtureAppendSignature(signerCount, v, r, s);
totalWeight += allValidators[signerCount].weight;
signerCount++;
}
writeFixture(metadata, uint8(signerCount), n);
return metadata;
}
function test_verify_revertInsufficientWeight(
uint32 destination,
bytes32 recipient,
bytes calldata body,
uint8 m,
uint8 n,
bytes32 seed
) public {
vm.assume(0 < m && m <= n && n < 10);
bytes memory message = getMessage(destination, recipient, body);
bytes memory metadata = getMetadata(m, n, seed, message);
uint256 signatureCount = weightedIsm.signatureCount(metadata);
vm.assume(signatureCount >= 1);
uint256 newLength = metadata.length - 65;
bytes memory insufficientMetadata = new bytes(newLength);
for (uint256 i = 0; i < newLength; i++) {
insufficientMetadata[i] = metadata[i];
}
vm.expectRevert("Insufficient validator weight");
ism.verify(insufficientMetadata, message);
}
function test_verify_revertWhen_duplicateSignatures(
uint32 destination,
bytes32 recipient,
bytes calldata body,
uint8 m,
uint8 n,
bytes32 seed
) public virtual override {
vm.assume(1 < m && m <= n && n < 10);
bytes memory message = getMessage(destination, recipient, body);
bytes memory metadata = getMetadata(m, n, seed, message);
bytes memory duplicateMetadata = new bytes(metadata.length);
for (uint256 i = 0; i < metadata.length - 65; i++) {
duplicateMetadata[i] = metadata[i];
}
for (uint256 i = 0; i < 65; i++) {
duplicateMetadata[metadata.length - 65 + i] = metadata[
metadata.length - 130 + i
];
}
if (weightedIsm.signatureCount(metadata) >= 2) {
vm.expectRevert("Invalid signer");
ism.verify(duplicateMetadata, message);
}
}
}
contract StaticMerkleRootWeightedMultisigIsmTest is
MerkleRootMultisigIsmTest,
AbstractStaticWeightedMultisigIsmTest
{
function setUp() public override {
mailbox = new TestMailbox(ORIGIN);
merkleTreeHook = new TestMerkleTreeHook(address(mailbox));
noopHook = new TestPostDispatchHook();
weightedFactory = new StaticMerkleRootWeightedMultisigIsmFactory();
mailbox.setDefaultHook(address(merkleTreeHook));
mailbox.setRequiredHook(address(noopHook));
}
function getMetadata(
uint8 m,
uint8 n,
bytes32 seed,
bytes memory message
)
internal
override(AbstractMultisigIsmTest, AbstractStaticWeightedMultisigIsmTest)
returns (bytes memory)
{
return
AbstractStaticWeightedMultisigIsmTest.getMetadata(
m,
n,
seed,
message
);
}
function test_verify_revertWhen_duplicateSignatures(
uint32 destination,
bytes32 recipient,
bytes calldata body,
uint8 m,
uint8 n,
bytes32 seed
)
public
override(AbstractMultisigIsmTest, AbstractStaticWeightedMultisigIsmTest)
{
AbstractStaticWeightedMultisigIsmTest
.test_verify_revertWhen_duplicateSignatures(
destination,
recipient,
body,
m,
n,
seed
);
}
function testThresholdExceedsLength() public override {
// no-op
}
function testZeroThreshold() public override {
// no-op
}
}
contract StaticMessageIdWeightedMultisigIsmTest is
MessageIdMultisigIsmTest,
AbstractStaticWeightedMultisigIsmTest
{
function setUp() public override {
mailbox = new TestMailbox(ORIGIN);
merkleTreeHook = new TestMerkleTreeHook(address(mailbox));
noopHook = new TestPostDispatchHook();
weightedFactory = new StaticMessageIdWeightedMultisigIsmFactory();
mailbox.setDefaultHook(address(merkleTreeHook));
mailbox.setRequiredHook(address(noopHook));
}
function getMetadata(
uint8 m,
uint8 n,
bytes32 seed,
bytes memory message
)
internal
override(AbstractMultisigIsmTest, AbstractStaticWeightedMultisigIsmTest)
returns (bytes memory)
{
return
AbstractStaticWeightedMultisigIsmTest.getMetadata(
m,
n,
seed,
message
);
}
function test_verify_revertWhen_duplicateSignatures(
uint32 destination,
bytes32 recipient,
bytes calldata body,
uint8 m,
uint8 n,
bytes32 seed
)
public
override(AbstractMultisigIsmTest, AbstractStaticWeightedMultisigIsmTest)
{
AbstractStaticWeightedMultisigIsmTest
.test_verify_revertWhen_duplicateSignatures(
destination,
recipient,
body,
m,
n,
seed
);
}
function testThresholdExceedsLength() public override {
// no-op
}
function testZeroThreshold() public override {
// no-op
}
}