wormhole/ethereum/forge-test/MessagesRV.t.sol

215 lines
6.8 KiB
Solidity

// test/Messages.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "../contracts/Messages.sol";
import "../contracts/Setters.sol";
import "../contracts/Structs.sol";
import "forge-test/rv-helpers/TestUtils.sol";
contract TestMessagesRV is TestUtils {
using BytesLib for bytes;
Messages messages;
struct GuardianSetParams {
uint256[] privateKeys;
uint8 guardianCount;
uint32 expirationTime;
}
function setUp() public {
messages = new Messages();
}
function paramsAreWellFormed(GuardianSetParams memory params)
internal
pure
returns (bool)
{
return params.guardianCount <= 19 &&
params.guardianCount <= params.privateKeys.length;
}
function generateGuardianSet(GuardianSetParams memory params)
internal pure
returns (Structs.GuardianSet memory)
{
for (uint8 i = 0; i < params.guardianCount; ++i)
vm.assume(0 < params.privateKeys[i] &&
params.privateKeys[i] < SECP256K1_CURVE_ORDER);
address[] memory guardians = new address[](params.guardianCount);
for (uint8 i = 0; i < params.guardianCount; ++i) {
guardians[i] = vm.addr(params.privateKeys[i]);
}
return Structs.GuardianSet(guardians, params.expirationTime);
}
function generateSignature(
uint8 index,
uint256 privateKey,
address guardian,
bytes32 message
)
internal
returns (Structs.Signature memory)
{
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, message);
assertEq(ecrecover(message, v, r, s), guardian);
return Structs.Signature(r, s, v, index);
}
function generateSignatures(
uint256[] memory privateKeys,
address[] memory guardians,
bytes32 message
)
internal
returns (Structs.Signature[] memory)
{
Structs.Signature[] memory sigs =
new Structs.Signature[](guardians.length);
for (uint8 i = 0; i < guardians.length; ++i) {
sigs[i] = generateSignature(
i,
privateKeys[i],
guardians[i],
message
);
}
return sigs;
}
function isProperSignature(Structs.Signature memory sig, bytes32 message)
internal
pure
returns (bool)
{
address signer = ecrecover(message, sig.v, sig.r, sig.s);
return signer != address(0);
}
function testCannotVerifySignaturesWithOutOfBoundsSignature(
bytes memory encoded,
GuardianSetParams memory params,
uint8 outOfBoundsGuardian,
uint8 outOfBoundsAmount
) public {
vm.assume(encoded.length > 0);
vm.assume(paramsAreWellFormed(params));
vm.assume(params.guardianCount > 0);
outOfBoundsGuardian = uint8(bound(outOfBoundsGuardian, 0, params.guardianCount - 1));
outOfBoundsAmount = uint8(bound(outOfBoundsAmount, 0, MAX_UINT8 - params.guardianCount));
bytes32 message = keccak256(encoded);
Structs.GuardianSet memory guardianSet = generateGuardianSet(params);
Structs.Signature[] memory sigs = generateSignatures(
params.privateKeys,
guardianSet.keys,
keccak256(encoded)
);
sigs[outOfBoundsGuardian].guardianIndex =
params.guardianCount + outOfBoundsAmount;
vm.expectRevert("guardian index out of bounds");
messages.verifySignatures(message, sigs, guardianSet);
}
function testCannotVerifySignaturesWithInvalidSignature1(
bytes memory encoded,
GuardianSetParams memory params,
Structs.Signature memory fakeSignature
) public {
vm.assume(encoded.length > 0);
vm.assume(paramsAreWellFormed(params));
vm.assume(fakeSignature.guardianIndex < params.guardianCount);
bytes32 message = keccak256(encoded);
Structs.GuardianSet memory guardianSet = generateGuardianSet(params);
Structs.Signature[] memory sigs = generateSignatures(
params.privateKeys,
guardianSet.keys,
message
);
sigs[fakeSignature.guardianIndex] = fakeSignature;
// It is very unlikely that the arbitrary fakeSignature will be the
// correct signature for the guardian at that index, so the below
// should be the only reasonable outcomes
if (isProperSignature(fakeSignature, message)) {
(bool valid, string memory reason) =
messages.verifySignatures(message, sigs, guardianSet);
assertEq(valid, false);
assertEq(reason, "VM signature invalid");
} else {
vm.expectRevert("ecrecover failed with signature");
messages.verifySignatures(message, sigs, guardianSet);
}
}
function testCannotVerifySignaturesWithInvalidSignature2(
bytes memory encoded,
GuardianSetParams memory params,
uint8 fakeGuardianIndex,
uint256 fakeGuardianPrivateKey
) public {
vm.assume(encoded.length > 0);
vm.assume(paramsAreWellFormed(params));
vm.assume(fakeGuardianIndex < params.guardianCount);
vm.assume(0 < fakeGuardianPrivateKey &&
fakeGuardianPrivateKey < SECP256K1_CURVE_ORDER);
vm.assume(fakeGuardianPrivateKey != params.privateKeys[fakeGuardianIndex]);
bytes32 message = keccak256(encoded);
Structs.GuardianSet memory guardianSet = generateGuardianSet(params);
Structs.Signature[] memory sigs = generateSignatures(
params.privateKeys,
guardianSet.keys,
message
);
address fakeGuardian = vm.addr(fakeGuardianPrivateKey);
sigs[fakeGuardianIndex] = generateSignature(
fakeGuardianIndex,
fakeGuardianPrivateKey,
fakeGuardian,
message
);
(bool valid, string memory reason) = messages.verifySignatures(message, sigs, guardianSet);
assertEq(valid, false);
assertEq(reason, "VM signature invalid");
}
function testVerifySignatures(
bytes memory encoded,
GuardianSetParams memory params
) public {
vm.assume(encoded.length > 0);
vm.assume(paramsAreWellFormed(params));
bytes32 message = keccak256(encoded);
Structs.GuardianSet memory guardianSet = generateGuardianSet(params);
Structs.Signature[] memory sigs = generateSignatures(
params.privateKeys,
guardianSet.keys,
message
);
(bool valid, string memory reason) = messages.verifySignatures(message, sigs, guardianSet);
assertEq(valid, true);
assertEq(bytes(reason).length, 0);
}
}