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

165 lines
6.0 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-std/Test.sol";
contract ExportedMessages is Messages, Setters {
function storeGuardianSetPub(Structs.GuardianSet memory set, uint32 index) public {
return super.storeGuardianSet(set, index);
}
}
contract TestMessages is Test {
address constant testGuardianPub = 0xbeFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe;
// A valid VM with one signature from the testGuardianPublic key
bytes validVM = hex"01000000000100867b55fec41778414f0683e80a430b766b78801b7070f9198ded5e62f48ac7a44b379a6cf9920e42dbd06c5ebf5ec07a934a00a572aefc201e9f91c33ba766d900000003e800000001000b0000000000000000000000000000000000000000000000000000000000000eee00000000000005390faaaa";
uint256 constant testGuardian = 93941733246223705020089879371323733820373732307041878556247502674739205313440;
ExportedMessages messages;
Structs.GuardianSet guardianSet;
function setUp() public {
messages = new ExportedMessages();
// initialize guardian set with one guardian
address[] memory keys = new address[](1);
keys[0] = vm.addr(testGuardian);
guardianSet = Structs.GuardianSet(keys, 0);
require(messages.quorum(guardianSet.keys.length) == 1, "Quorum should be 1");
}
function testQuorum() public {
assertEq(messages.quorum(0), 1);
assertEq(messages.quorum(1), 1);
assertEq(messages.quorum(2), 2);
assertEq(messages.quorum(3), 3);
assertEq(messages.quorum(4), 3);
assertEq(messages.quorum(5), 4);
assertEq(messages.quorum(6), 5);
assertEq(messages.quorum(7), 5);
assertEq(messages.quorum(8), 6);
assertEq(messages.quorum(9), 7);
assertEq(messages.quorum(10), 7);
assertEq(messages.quorum(11), 8);
assertEq(messages.quorum(12), 9);
assertEq(messages.quorum(19), 13);
assertEq(messages.quorum(20), 14);
}
function testQuorumCanAlwaysBeReached(uint256 numGuardians) public {
vm.assume(numGuardians > 0);
if (numGuardians >= 256) {
vm.expectRevert("too many guardians");
}
// test that quorums is never greater than the number of guardians
assert(messages.quorum(numGuardians) <= numGuardians);
}
// This test ensures that submitting more signatures than expected will
// trigger a "guardian index out of bounds" error.
function testCannotVerifySignaturesWithOutOfBoundsSignature(bytes memory encoded) public {
vm.assume(encoded.length > 0);
bytes32 message = keccak256(encoded);
// First generate a legitimate signature.
Structs.Signature memory goodSignature = Structs.Signature(message, 0, 0, 0);
(goodSignature.v, goodSignature.r, goodSignature.s) = vm.sign(testGuardian, message);
assertEq(ecrecover(message, goodSignature.v, goodSignature.r, goodSignature.s), vm.addr(testGuardian));
// Reuse legitimate signature above for the next signature. This will
// bypass the "invalid signature" revert.
Structs.Signature memory outOfBoundsSignature = goodSignature;
outOfBoundsSignature.guardianIndex = 1;
// Attempt to verify signatures.
Structs.Signature[] memory sigs = new Structs.Signature[](2);
sigs[0] = goodSignature;
sigs[1] = outOfBoundsSignature;
vm.expectRevert("guardian index out of bounds");
messages.verifySignatures(message, sigs, guardianSet);
}
// This test ensures that submitting an invalid signature fails when
// verifySignatures is called. Calling ecrecover should fail.
function testCannotVerifySignaturesWithInvalidSignature(bytes memory encoded) public {
vm.assume(encoded.length > 0);
bytes32 message = keccak256(encoded);
// Generate an invalid signature.
Structs.Signature memory badSignature = Structs.Signature(message, 0, 0, 0);
assertEq(ecrecover(message, badSignature.v, badSignature.r, badSignature.s), address(0));
// Attempt to verify signatures.
Structs.Signature[] memory sigs = new Structs.Signature[](2);
sigs[0] = badSignature;
vm.expectRevert("ecrecover failed with signature");
messages.verifySignatures(message, sigs, guardianSet);
}
function testVerifySignatures(bytes memory encoded) public {
vm.assume(encoded.length > 0);
bytes32 message = keccak256(encoded);
// Generate legitimate signature.
Structs.Signature memory goodSignature;
(goodSignature.v, goodSignature.r, goodSignature.s) = vm.sign(testGuardian, message);
assertEq(ecrecover(message, goodSignature.v, goodSignature.r, goodSignature.s), vm.addr(testGuardian));
goodSignature.guardianIndex = 0;
// Attempt to verify signatures.
Structs.Signature[] memory sigs = new Structs.Signature[](1);
sigs[0] = goodSignature;
(bool valid, string memory reason) = messages.verifySignatures(message, sigs, guardianSet);
assertEq(valid, true);
assertEq(bytes(reason).length, 0);
}
// This test checks the possibility of getting a unsigned message verified through verifyVM
function testHashMismatchedVMIsNotVerified() public {
// Set the initial guardian set
address[] memory initialGuardians = new address[](1);
initialGuardians[0] = testGuardianPub;
// Create a guardian set
Structs.GuardianSet memory initialGuardianSet = Structs.GuardianSet({
keys: initialGuardians,
expirationTime: 0
});
messages.storeGuardianSetPub(initialGuardianSet, uint32(0));
// Confirm that the test VM is valid
(Structs.VM memory parsedValidVm, bool valid, string memory reason) = messages.parseAndVerifyVM(validVM);
require(valid, reason);
assertEq(valid, true);
assertEq(reason, "");
// Manipulate the payload of the vm
Structs.VM memory invalidVm = parsedValidVm;
invalidVm.payload = abi.encodePacked(
parsedValidVm.payload,
"malicious bytes in payload"
);
// Confirm that the verifyVM fails on invalid VM
(valid, reason) = messages.verifyVM(invalidVm);
assertEq(valid, false);
assertEq(reason, "vm.hash doesn't match body");
}
}