Skip to content

Commit

Permalink
evm: add parse and verify test for the optimized end point
Browse files Browse the repository at this point in the history
  • Loading branch information
gator-boi committed Sep 8, 2023
1 parent c473b9e commit 4a4ced1
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 23 deletions.
2 changes: 1 addition & 1 deletion ethereum/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ node_modules: package-lock.json
forge_dependencies: lib/forge-std lib/openzeppelin-contracts

lib/forge-std:
forge install foundry-rs/forge-std@v1.5.5 --no-git --no-commit
forge install foundry-rs/forge-std@v1.6.1 --no-git --no-commit

lib/openzeppelin-contracts:
forge install openzeppelin/openzeppelin-contracts@0457042d93d9dfd760dbaa06a4d2f1216fdbe297 --no-git --no-commit
Expand Down
169 changes: 147 additions & 22 deletions ethereum/forge-test/Messages.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,50 @@ pragma solidity ^0.8.0;
import "../contracts/Messages.sol";
import "../contracts/Setters.sol";
import "../contracts/Structs.sol";

import "forge-std/Test.sol";
import "forge-std/Vm.sol";

contract WormholeSigner is Test {
// Signer wallet.
struct Wallet {
address addr;
uint256 key;
}

function encodeAndSignMessage(
Structs.VM memory vm_,
uint256[] memory guardianKeys,
uint32 guardianSetIndex
) public pure returns (bytes memory signedMessage) {
// Compute the hash of the body
bytes memory body = abi.encodePacked(
vm_.timestamp,
vm_.nonce,
vm_.emitterChainId,
vm_.emitterAddress,
vm_.sequence,
vm_.consistencyLevel,
vm_.payload
);
vm_.hash = keccak256(abi.encodePacked(keccak256(body)));

// Sign the hash with the specified guardian private keys.
uint256 guardianCount = guardianKeys.length;
bytes memory signatures = abi.encodePacked(uint8(guardianCount));
for (uint256 i = 0; i < guardianCount; ++i) {
(uint8 v, bytes32 r, bytes32 s) = vm.sign(guardianKeys[i], vm_.hash);
signatures = abi.encodePacked(signatures, uint8(i), r, s, v - 27);
}

signedMessage = abi.encodePacked(
vm_.version,
guardianSetIndex,
signatures,
body
);
}
}

contract ExportedMessages is Messages, Setters {
function storeGuardianSetPub(Structs.GuardianSet memory set, uint32 index) public {
Expand All @@ -23,9 +66,13 @@ contract TestMessages is Test {
uint256 constant testGuardian = 93941733246223705020089879371323733820373732307041878556247502674739205313440;

ExportedMessages messages;
WormholeSigner wormholeSimulator;

Structs.GuardianSet guardianSet;

// Guardian set with 19 guardians and wallets with each signing key.
Structs.GuardianSet guardianSetOpt;
uint256[] guardianKeys = new uint256[](19);

function setupSingleGuardian() internal {
// initialize guardian set with one guardian
Expand All @@ -39,36 +86,49 @@ contract TestMessages is Test {
// initialize guardian set with 19 guardians
address[] memory keys = new address[](19);
for (uint256 i = 0; i < 19; ++i) {
keys[i] = makeAddr(string(abi.encodePacked("guarian", i)));
// create a keypair for each guardian
VmSafe.Wallet memory wallet = vm.createWallet(string(abi.encodePacked("guardian", i)));
keys[i] = wallet.addr;
guardianKeys[i] = wallet.privateKey;
}
guardianSetOpt = Structs.GuardianSet(keys, 0);
require(messages.quorum(guardianSetOpt.keys.length) == 13, "Quorum should be 13");
require(messages.quorum(guardianSetOpt.keys.length) == 13, "Quorum should be 13");
}

function setUp() public {
messages = new ExportedMessages();
wormholeSimulator = new WormholeSigner();
setupSingleGuardian();
setupMultiGuardian();
}

function testParseGuardianSetOptimized(uint8 guardianCount) public view {
vm.assume(guardianCount > 0 && guardianCount <= 19);

// Encode the guardian set.
bytes memory encodedGuardianSet;
for (uint256 i = 0; i < guardianCount; ++i) {
encodedGuardianSet = abi.encodePacked(encodedGuardianSet, guardianSetOpt.keys[i]);
}
encodedGuardianSet = abi.encodePacked(encodedGuardianSet, guardianSetOpt.expirationTime);

// Parse the guardian set.
Structs.GuardianSet memory parsedSet = messages.parseGuardianSetOptimized(encodedGuardianSet);

// Validate the results by comparing the parsed set to the original set.
for (uint256 i = 0; i < guardianCount; ++i) {
assert(parsedSet.keys[i] == guardianSetOpt.keys[i]);
}
assert(parsedSet.expirationTime == guardianSetOpt.expirationTime);
}

function getSignedVM(
bytes memory payload,
bytes32 emitterAddress,
uint16 emitterChainId,
uint256[] memory _guardianKeys,
uint32 guardianSetIndex
) internal view returns (bytes memory signedTransfer) {
// construct `TransferWithPayload` Wormhole message
Structs.VM memory vm;

// set the vm values inline
vm.version = uint8(1);
vm.timestamp = uint32(block.timestamp);
vm.emitterChainId = emitterChainId;
vm.emitterAddress = emitterAddress;
vm.sequence = messages.nextSequence(
address(uint160(uint256(emitterAddress)))
);
vm.consistencyLevel = 15;
vm.payload = payload;

// encode the bservation
signedTransfer = wormholeSimulator.encodeAndSignMessage(
vm,
_guardianKeys,
guardianSetIndex
);
}

function testQuorum() public {
Expand Down Expand Up @@ -196,4 +256,69 @@ contract TestMessages is Test {
assertEq(valid, false);
assertEq(reason, "vm.hash doesn't match body");
}

function testParseGuardianSetOptimized(uint8 guardianCount) public view {
vm.assume(guardianCount > 0 && guardianCount <= 19);

// Encode the guardian set.
bytes memory encodedGuardianSet;
for (uint256 i = 0; i < guardianCount; ++i) {
encodedGuardianSet = abi.encodePacked(encodedGuardianSet, guardianSetOpt.keys[i]);
}
encodedGuardianSet = abi.encodePacked(encodedGuardianSet, guardianSetOpt.expirationTime);

// Parse the guardian set.
Structs.GuardianSet memory parsedSet = messages.parseGuardianSetOptimized(encodedGuardianSet);

// Validate the results by comparing the parsed set to the original set.
for (uint256 i = 0; i < guardianCount; ++i) {
assert(parsedSet.keys[i] == guardianSetOpt.keys[i]);
}
assert(parsedSet.expirationTime == guardianSetOpt.expirationTime);
}

function testParseAndVerifyVMOptimized(bytes memory payload) public {
vm.assume(payload.length > 0 && payload.length < 1000);

uint16 emitterChainId = 16;
bytes32 emitterAddress = bytes32(uint256(uint160(makeAddr("foreignEmitter"))));
uint32 currentSetIndex = messages.getCurrentGuardianSetIndex();

// Set the guardian set to the optimized guardian set.
messages.storeGuardianSetPub(guardianSetOpt, currentSetIndex);
messages.setGuardianSetHash(currentSetIndex);

// Create a message with an arbitrary payload.
bytes memory signedMessage = getSignedVM(
payload,
emitterAddress,
emitterChainId,
guardianKeys,
currentSetIndex
);

// Parse and verify the VM.
(Structs.VM memory vm_, bool valid,) = messages.parseAndVerifyVM(signedMessage);
assertEq(valid, true);

// Parse and verify the VM using the optimized endpoint.
(Structs.VM memory vmOptimized, bool valid_,) = messages.parseAndVerifyVMOptimized(
signedMessage,
messages.getEncodedGuardianSet(currentSetIndex),
currentSetIndex
);
assertEq(valid_, true);

// Validate the results by comparing the parsed VM to the optimized VM.
assertEq(vm_.version, vmOptimized.version);
assertEq(vm_.timestamp, vmOptimized.timestamp);
assertEq(vm_.nonce, vmOptimized.nonce);
assertEq(vm_.emitterChainId, vmOptimized.emitterChainId);
assertEq(vm_.emitterAddress, vmOptimized.emitterAddress);
assertEq(vm_.sequence, vmOptimized.sequence);
assertEq(vm_.consistencyLevel, vmOptimized.consistencyLevel);
assertEq(vm_.payload, vmOptimized.payload);
assertEq(vm_.guardianSetIndex, vmOptimized.guardianSetIndex);
assertEq(vm_.signatures.length, vmOptimized.signatures.length);
}
}

0 comments on commit 4a4ced1

Please sign in to comment.