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/AggregationIsm.t.sol

137 lines
4.5 KiB

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import {IAggregationIsm} from "../../contracts/interfaces/isms/IAggregationIsm.sol";
import {StaticAggregationIsmFactory} from "../../contracts/isms/aggregation/StaticAggregationIsmFactory.sol";
import {AggregationIsmMetadata} from "../../contracts/isms/libs/AggregationIsmMetadata.sol";
import {TestIsm, ThresholdTestUtils} from "./IsmTestUtils.sol";
contract AggregationIsmTest is Test {
StaticAggregationIsmFactory factory;
IAggregationIsm ism;
function setUp() public {
factory = new StaticAggregationIsmFactory();
}
function deployIsms(
uint8 m,
uint8 n,
bytes32 seed
) internal returns (address[] memory) {
bytes32 randomness = seed;
address[] memory isms = new address[](n);
for (uint256 i = 0; i < n; i++) {
randomness = keccak256(abi.encode(randomness));
TestIsm subIsm = new TestIsm(abi.encode(randomness));
isms[i] = address(subIsm);
}
ism = IAggregationIsm(factory.deploy(isms, m));
return isms;
}
function getMetadata(uint8 m, bytes32 seed)
private
view
returns (bytes memory)
{
(address[] memory choices, ) = ism.modulesAndThreshold("");
address[] memory chosen = ThresholdTestUtils.choose(m, choices, seed);
bytes memory offsets;
uint32 start = 8 * uint32(choices.length);
bytes memory metametadata;
for (uint256 i = 0; i < choices.length; i++) {
bool included = false;
for (uint256 j = 0; j < chosen.length; j++) {
included = included || choices[i] == chosen[j];
}
if (included) {
bytes memory requiredMetadata = TestIsm(choices[i])
.requiredMetadata();
uint32 end = start + uint32(requiredMetadata.length);
uint64 offset = (uint64(start) << 32) | uint64(end);
offsets = bytes.concat(offsets, abi.encodePacked(offset));
start = end;
metametadata = abi.encodePacked(metametadata, requiredMetadata);
} else {
uint64 offset = 0;
offsets = bytes.concat(offsets, abi.encodePacked(offset));
}
}
return abi.encodePacked(offsets, metametadata);
}
function testVerify(
uint8 m,
uint8 n,
bytes32 seed
) public {
vm.assume(0 < m && m <= n && n < 10);
deployIsms(m, n, seed);
bytes memory metadata = getMetadata(m, seed);
assertTrue(ism.verify(metadata, ""));
}
function testVerifyNoMetadataRequired(
uint8 m,
uint8 n,
uint8 i,
bytes32 seed
) public {
vm.assume(0 < m && m <= n && n < 10 && i < n);
deployIsms(m, n, seed);
(address[] memory modules, ) = ism.modulesAndThreshold("");
bytes memory noMetadata;
TestIsm(modules[i]).setRequiredMetadata(noMetadata);
bytes memory metadata = getMetadata(m, seed);
assertTrue(ism.verify(metadata, ""));
}
function testVerifyMissingMetadata(
uint8 m,
uint8 n,
bytes32 seed
) public {
vm.assume(0 < m && m <= n && n < 10);
deployIsms(m, n, seed);
// Populate metadata for one fewer ISMs than needed.
bytes memory metadata = getMetadata(m - 1, seed);
vm.expectRevert(bytes("!threshold"));
ism.verify(metadata, "");
}
function testVerifyIncorrectMetadata(
uint8 m,
uint8 n,
bytes32 seed
) public {
vm.assume(0 < m && m <= n && n < 10);
deployIsms(m, n, seed);
bytes memory metadata = getMetadata(m, seed);
// Modify the last byte in metadata. This should affect
// the content of the metadata passed to the last ISM.
metadata[metadata.length - 1] = ~metadata[metadata.length - 1];
vm.expectRevert(bytes("!verify"));
ism.verify(metadata, "");
}
function testModulesAndThreshold(
uint8 m,
uint8 n,
bytes32 seed
) public {
vm.assume(0 < m && m <= n && n < 10);
address[] memory expectedIsms = deployIsms(m, n, seed);
(address[] memory actualIsms, uint8 actualThreshold) = ism
.modulesAndThreshold("");
assertEq(abi.encode(actualIsms), abi.encode(expectedIsms));
assertEq(actualThreshold, m);
}
}