Adds `MockWormhole` contract for unit tests.
Also adds a `WormholeSimulator` interface for signing and non-signing mocks. The signing mock can be useful when writing a test for a specific testnet fork with a real wormhole deployment. In the unit tests, only the non signing mock is used.
This commit is contained in:
parent
45021b4691
commit
3f713d7efc
|
@ -0,0 +1,281 @@
|
||||||
|
// SPDX-License-Identifier: Apache 2
|
||||||
|
|
||||||
|
pragma solidity ^0.8.17;
|
||||||
|
|
||||||
|
import "../interfaces/IWormhole.sol";
|
||||||
|
import "../libraries/external/BytesLib.sol";
|
||||||
|
|
||||||
|
contract MockWormhole is IWormhole {
|
||||||
|
using BytesLib for bytes;
|
||||||
|
|
||||||
|
uint256 private constant VM_VERSION_SIZE = 1;
|
||||||
|
uint256 private constant VM_GUARDIAN_SET_SIZE = 4;
|
||||||
|
uint256 private constant VM_SIGNATURE_COUNT_SIZE = 1;
|
||||||
|
uint256 private constant VM_TIMESTAMP_SIZE = 4;
|
||||||
|
uint256 private constant VM_NONCE_SIZE = 4;
|
||||||
|
uint256 private constant VM_EMITTER_CHAIN_ID_SIZE = 2;
|
||||||
|
uint256 private constant VM_EMITTER_ADDRESS_SIZE = 32;
|
||||||
|
uint256 private constant VM_SEQUENCE_SIZE = 8;
|
||||||
|
uint256 private constant VM_CONSISTENCY_LEVEL_SIZE = 1;
|
||||||
|
uint256 private constant VM_SIZE_MINIMUM = VM_VERSION_SIZE + VM_GUARDIAN_SET_SIZE + VM_SIGNATURE_COUNT_SIZE
|
||||||
|
+ VM_TIMESTAMP_SIZE + VM_NONCE_SIZE + VM_EMITTER_CHAIN_ID_SIZE + VM_EMITTER_ADDRESS_SIZE + VM_SEQUENCE_SIZE
|
||||||
|
+ VM_CONSISTENCY_LEVEL_SIZE;
|
||||||
|
|
||||||
|
uint256 private constant SIGNATURE_GUARDIAN_INDEX_SIZE = 1;
|
||||||
|
uint256 private constant SIGNATURE_R_SIZE = 32;
|
||||||
|
uint256 private constant SIGNATURE_S_SIZE = 32;
|
||||||
|
uint256 private constant SIGNATURE_V_SIZE = 1;
|
||||||
|
uint256 private constant SIGNATURE_SIZE_TOTAL =
|
||||||
|
SIGNATURE_GUARDIAN_INDEX_SIZE + SIGNATURE_R_SIZE + SIGNATURE_S_SIZE + SIGNATURE_V_SIZE;
|
||||||
|
|
||||||
|
mapping(address => uint64) public sequences;
|
||||||
|
// Dictionary of VMs that must be mocked as invalid.
|
||||||
|
mapping(bytes32 => bool) public invalidVMs;
|
||||||
|
|
||||||
|
uint256 currentMsgFee;
|
||||||
|
uint16 immutable wormholeChainId;
|
||||||
|
uint256 immutable boundEvmChainId;
|
||||||
|
|
||||||
|
constructor(uint16 initChainId, uint256 initEvmChainId) {
|
||||||
|
wormholeChainId = initChainId;
|
||||||
|
boundEvmChainId = initEvmChainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function invalidateVM(bytes calldata encodedVm) external {
|
||||||
|
VM memory vm = _parseVM(encodedVm);
|
||||||
|
invalidVMs[vm.hash] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function publishMessage(uint32 nonce, bytes memory payload, uint8 consistencyLevel)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (uint64 sequence)
|
||||||
|
{
|
||||||
|
require(msg.value == currentMsgFee, "invalid fee");
|
||||||
|
sequence = sequences[msg.sender]++;
|
||||||
|
emit LogMessagePublished(msg.sender, sequence, nonce, payload, consistencyLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseVM(bytes calldata encodedVm) external pure returns (VM memory vm) {
|
||||||
|
vm = _parseVM(encodedVm);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseAndVerifyVM(bytes calldata encodedVm)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (VM memory vm, bool valid, string memory reason)
|
||||||
|
{
|
||||||
|
vm = _parseVM(encodedVm);
|
||||||
|
//behold the rigorous checking!
|
||||||
|
valid = !invalidVMs[vm.hash];
|
||||||
|
reason = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function _parseVM(bytes calldata encodedVm) internal pure returns (VM memory vm) {
|
||||||
|
require(encodedVm.length >= VM_SIZE_MINIMUM, "vm too small");
|
||||||
|
|
||||||
|
bytes memory body;
|
||||||
|
|
||||||
|
uint256 offset = 0;
|
||||||
|
vm.version = encodedVm.toUint8(offset);
|
||||||
|
offset += 1;
|
||||||
|
|
||||||
|
vm.guardianSetIndex = encodedVm.toUint32(offset);
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
|
(vm.signatures, offset) = parseSignatures(encodedVm, offset);
|
||||||
|
|
||||||
|
body = encodedVm[offset:];
|
||||||
|
vm.timestamp = encodedVm.toUint32(offset);
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
|
vm.nonce = encodedVm.toUint32(offset);
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
|
vm.emitterChainId = encodedVm.toUint16(offset);
|
||||||
|
offset += 2;
|
||||||
|
|
||||||
|
vm.emitterAddress = encodedVm.toBytes32(offset);
|
||||||
|
offset += 32;
|
||||||
|
|
||||||
|
vm.sequence = encodedVm.toUint64(offset);
|
||||||
|
offset += 8;
|
||||||
|
|
||||||
|
vm.consistencyLevel = encodedVm.toUint8(offset);
|
||||||
|
offset += 1;
|
||||||
|
|
||||||
|
vm.payload = encodedVm[offset:];
|
||||||
|
vm.hash = keccak256(abi.encodePacked(keccak256(body)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseSignatures(bytes calldata encodedVm, uint256 offset)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (Signature[] memory signatures, uint256 offsetAfterParse)
|
||||||
|
{
|
||||||
|
uint256 sigCount = uint256(encodedVm.toUint8(offset));
|
||||||
|
offset += 1;
|
||||||
|
|
||||||
|
require(encodedVm.length >= (VM_SIZE_MINIMUM + sigCount * SIGNATURE_SIZE_TOTAL), "vm too small");
|
||||||
|
|
||||||
|
signatures = new Signature[](sigCount);
|
||||||
|
for (uint256 i = 0; i < sigCount; ++i) {
|
||||||
|
uint8 guardianIndex = encodedVm.toUint8(offset);
|
||||||
|
offset += 1;
|
||||||
|
|
||||||
|
bytes32 r = encodedVm.toBytes32(offset);
|
||||||
|
offset += 32;
|
||||||
|
|
||||||
|
bytes32 s = encodedVm.toBytes32(offset);
|
||||||
|
offset += 32;
|
||||||
|
|
||||||
|
uint8 v = encodedVm.toUint8(offset);
|
||||||
|
offset += 1;
|
||||||
|
|
||||||
|
signatures[i] = Signature({
|
||||||
|
r: r,
|
||||||
|
s: s,
|
||||||
|
// The hardcoded 27 comes from the base offset for public key recovery ids, public key type and network
|
||||||
|
// used in ECDSA signatures for bitcoin and ethereum.
|
||||||
|
// See https://bitcoin.stackexchange.com/a/5089
|
||||||
|
v: v + 27,
|
||||||
|
guardianIndex: guardianIndex
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (signatures, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize() external {}
|
||||||
|
|
||||||
|
function quorum(uint256 /*numGuardians*/ ) external pure returns (uint256 /*numSignaturesRequiredForQuorum*/ ) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General state and chain observers
|
||||||
|
*/
|
||||||
|
function chainId() external view returns (uint16) {
|
||||||
|
return wormholeChainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function evmChainId() external view returns (uint256) {
|
||||||
|
return boundEvmChainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentGuardianSetIndex() external pure returns (uint32) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGuardianSet(uint32 /*index*/ ) external pure returns (GuardianSet memory) {
|
||||||
|
revert("unsupported getGuardianSet in wormhole mock");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGuardianSetExpiry() external pure returns (uint32) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function governanceActionIsConsumed(bytes32 /*hash*/ ) external pure returns (bool) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isInitialized(address /*impl*/ ) external pure returns (bool) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFork() external pure returns (bool) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function governanceChainId() external pure returns (uint16) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function governanceContract() external pure returns (bytes32) {
|
||||||
|
return bytes32(0x0000000000000000000000000000000000000000000000000000000000000004);
|
||||||
|
}
|
||||||
|
|
||||||
|
function messageFee() external view returns (uint256) {
|
||||||
|
return currentMsgFee;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextSequence(address emitter) external view returns (uint64) {
|
||||||
|
return sequences[emitter];
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyVM(VM memory /*vm*/ ) external pure returns (bool, /*valid*/ string memory /*reason*/ ) {
|
||||||
|
revert("unsupported verifyVM in wormhole mock");
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifySignatures(bytes32, /*hash*/ Signature[] memory, /*signatures*/ GuardianSet memory /*guardianSet*/ )
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
returns (bool, /*valid*/ string memory /*reason*/ )
|
||||||
|
{
|
||||||
|
revert("unsupported verifySignatures in wormhole mock");
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseContractUpgrade(bytes memory /*encodedUpgrade*/ )
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
returns (ContractUpgrade memory /*cu*/ )
|
||||||
|
{
|
||||||
|
revert("unsupported parseContractUpgrade in wormhole mock");
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseGuardianSetUpgrade(bytes memory /*encodedUpgrade*/ )
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
returns (GuardianSetUpgrade memory /*gsu*/ )
|
||||||
|
{
|
||||||
|
revert("unsupported parseGuardianSetUpgrade in wormhole mock");
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseSetMessageFee(bytes memory /*encodedSetMessageFee*/ )
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
returns (SetMessageFee memory /*smf*/ )
|
||||||
|
{
|
||||||
|
revert("unsupported parseSetMessageFee in wormhole mock");
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTransferFees(bytes memory /*encodedTransferFees*/ )
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
returns (TransferFees memory /*tf*/ )
|
||||||
|
{
|
||||||
|
revert("unsupported parseTransferFees in wormhole mock");
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseRecoverChainId(bytes memory /*encodedRecoverChainId*/ )
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
returns (RecoverChainId memory /*rci*/ )
|
||||||
|
{
|
||||||
|
revert("unsupported parseRecoverChainId in wormhole mock");
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitContractUpgrade(bytes memory /*_vm*/ ) external pure {
|
||||||
|
revert("unsupported submitContractUpgrade in wormhole mock");
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitSetMessageFee(bytes memory /*_vm*/ ) external pure {
|
||||||
|
revert("unsupported submitSetMessageFee in wormhole mock");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMessageFee(uint256 newFee) external {
|
||||||
|
currentMsgFee = newFee;
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitNewGuardianSet(bytes memory /*_vm*/ ) external pure {
|
||||||
|
revert("unsupported submitNewGuardianSet in wormhole mock");
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitTransferFees(bytes memory /*_vm*/ ) external pure {
|
||||||
|
revert("unsupported submitTransferFees in wormhole mock");
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitRecoverChainId(bytes memory /*_vm*/ ) external pure {
|
||||||
|
revert("unsupported submitRecoverChainId in wormhole mock");
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,11 +17,9 @@ import {CoreRelayerImplementation} from "../contracts/coreRelayer/CoreRelayerImp
|
||||||
import {CoreRelayerProxy} from "../contracts/coreRelayer/CoreRelayerProxy.sol";
|
import {CoreRelayerProxy} from "../contracts/coreRelayer/CoreRelayerProxy.sol";
|
||||||
import {CoreRelayerMessages} from "../contracts/coreRelayer/CoreRelayerMessages.sol";
|
import {CoreRelayerMessages} from "../contracts/coreRelayer/CoreRelayerMessages.sol";
|
||||||
import {CoreRelayerStructs} from "../contracts/coreRelayer/CoreRelayerStructs.sol";
|
import {CoreRelayerStructs} from "../contracts/coreRelayer/CoreRelayerStructs.sol";
|
||||||
import {Setup as WormholeSetup} from "../wormhole/ethereum/contracts/Setup.sol";
|
import {MockWormhole} from "../contracts/mock/MockWormhole.sol";
|
||||||
import {Implementation as WormholeImplementation} from "../wormhole/ethereum/contracts/Implementation.sol";
|
|
||||||
import {Wormhole} from "../wormhole/ethereum/contracts/Wormhole.sol";
|
|
||||||
import {IWormhole} from "../contracts/interfaces/IWormhole.sol";
|
import {IWormhole} from "../contracts/interfaces/IWormhole.sol";
|
||||||
import {WormholeSimulator} from "./WormholeSimulator.sol";
|
import {WormholeSimulator, FakeWormholeSimulator} from "./WormholeSimulator.sol";
|
||||||
import {IWormholeReceiver} from "../contracts/interfaces/IWormholeReceiver.sol";
|
import {IWormholeReceiver} from "../contracts/interfaces/IWormholeReceiver.sol";
|
||||||
import {AttackForwardIntegration} from "../contracts/mock/AttackForwardIntegration.sol";
|
import {AttackForwardIntegration} from "../contracts/mock/AttackForwardIntegration.sol";
|
||||||
import {MockRelayerIntegration} from "../contracts/mock/MockRelayerIntegration.sol";
|
import {MockRelayerIntegration} from "../contracts/mock/MockRelayerIntegration.sol";
|
||||||
|
@ -55,36 +53,15 @@ contract TestCoreRelayer is Test {
|
||||||
WormholeSimulator relayerWormholeSimulator;
|
WormholeSimulator relayerWormholeSimulator;
|
||||||
|
|
||||||
function setUp() public {
|
function setUp() public {
|
||||||
WormholeSetup setup = new WormholeSetup();
|
|
||||||
|
|
||||||
// deploy Implementation
|
|
||||||
WormholeImplementation implementation = new WormholeImplementation();
|
|
||||||
|
|
||||||
// set guardian set
|
|
||||||
address[] memory guardians = new address[](1);
|
|
||||||
guardians[0] = 0xbeFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe;
|
|
||||||
|
|
||||||
// deploy Wormhole
|
// deploy Wormhole
|
||||||
relayerWormhole = IWormhole(
|
MockWormhole wormhole = new MockWormhole({
|
||||||
address(
|
initChainId: 2,
|
||||||
new Wormhole(
|
initEvmChainId: block.chainid
|
||||||
address(setup),
|
});
|
||||||
abi.encodeWithSelector(
|
|
||||||
bytes4(keccak256("setup(address,address[],uint16,uint16,bytes32,uint256)")),
|
|
||||||
address(implementation),
|
|
||||||
guardians,
|
|
||||||
2, // wormhole chain id
|
|
||||||
uint16(1), // governance chain id
|
|
||||||
0x0000000000000000000000000000000000000000000000000000000000000004, // governance contract
|
|
||||||
block.chainid
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
relayerWormholeSimulator = new WormholeSimulator(
|
relayerWormhole = wormhole;
|
||||||
address(relayerWormhole),
|
relayerWormholeSimulator = new FakeWormholeSimulator(
|
||||||
uint256(0xcfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0)
|
wormhole
|
||||||
);
|
);
|
||||||
|
|
||||||
setUpChains(5);
|
setUpChains(5);
|
||||||
|
@ -94,37 +71,18 @@ contract TestCoreRelayer is Test {
|
||||||
internal
|
internal
|
||||||
returns (IWormhole wormholeContract, WormholeSimulator wormholeSimulator)
|
returns (IWormhole wormholeContract, WormholeSimulator wormholeSimulator)
|
||||||
{
|
{
|
||||||
// deploy Setup
|
|
||||||
WormholeSetup setup = new WormholeSetup();
|
|
||||||
|
|
||||||
// deploy Implementation
|
|
||||||
WormholeImplementation implementation = new WormholeImplementation();
|
|
||||||
|
|
||||||
// set guardian set
|
|
||||||
address[] memory guardians = new address[](1);
|
|
||||||
guardians[0] = 0xbeFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe;
|
|
||||||
|
|
||||||
// deploy Wormhole
|
// deploy Wormhole
|
||||||
Wormhole wormhole = new Wormhole(
|
MockWormhole wormhole = new MockWormhole({
|
||||||
address(setup),
|
initChainId: 2,
|
||||||
abi.encodeWithSelector(
|
initEvmChainId: block.chainid
|
||||||
bytes4(keccak256("setup(address,address[],uint16,uint16,bytes32,uint256)")),
|
});
|
||||||
address(implementation),
|
|
||||||
guardians,
|
|
||||||
chainId, // wormhole chain id
|
|
||||||
uint16(1), // governance chain id
|
|
||||||
0x0000000000000000000000000000000000000000000000000000000000000004, // governance contract
|
|
||||||
block.chainid
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// replace Wormhole with the Wormhole Simulator contract (giving access to some nice helper methods for signing)
|
// replace Wormhole with the Wormhole Simulator contract (giving access to some nice helper methods for signing)
|
||||||
wormholeSimulator = new WormholeSimulator(
|
wormholeSimulator = new FakeWormholeSimulator(
|
||||||
address(wormhole),
|
wormhole
|
||||||
uint256(0xcfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
wormholeContract = IWormhole(wormholeSimulator.wormhole());
|
wormholeContract = wormhole;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpRelayProvider(uint16 chainId) internal returns (RelayProvider relayProvider) {
|
function setUpRelayProvider(uint16 chainId) internal returns (RelayProvider relayProvider) {
|
||||||
|
@ -144,7 +102,7 @@ contract TestCoreRelayer is Test {
|
||||||
relayProvider = RelayProvider(address(myRelayProvider));
|
relayProvider = RelayProvider(address(myRelayProvider));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpCoreRelayer(uint16 chainId, address wormhole, address defaultRelayProvider)
|
function setUpCoreRelayer(uint16 chainId, IWormhole wormhole, address defaultRelayProvider)
|
||||||
internal
|
internal
|
||||||
returns (IWormholeRelayer coreRelayer)
|
returns (IWormholeRelayer coreRelayer)
|
||||||
{
|
{
|
||||||
|
@ -159,8 +117,8 @@ contract TestCoreRelayer is Test {
|
||||||
chainId,
|
chainId,
|
||||||
wormhole,
|
wormhole,
|
||||||
defaultRelayProvider,
|
defaultRelayProvider,
|
||||||
uint16(1), // governance chain id
|
wormhole.governanceChainId(),
|
||||||
0x0000000000000000000000000000000000000000000000000000000000000004, // governance contract
|
wormhole.governanceContract(),
|
||||||
block.chainid
|
block.chainid
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -274,7 +232,7 @@ contract TestCoreRelayer is Test {
|
||||||
Contracts memory mapEntry;
|
Contracts memory mapEntry;
|
||||||
(mapEntry.wormhole, mapEntry.wormholeSimulator) = setUpWormhole(i);
|
(mapEntry.wormhole, mapEntry.wormholeSimulator) = setUpWormhole(i);
|
||||||
mapEntry.relayProvider = setUpRelayProvider(i);
|
mapEntry.relayProvider = setUpRelayProvider(i);
|
||||||
mapEntry.coreRelayer = setUpCoreRelayer(i, address(mapEntry.wormhole), address(mapEntry.relayProvider));
|
mapEntry.coreRelayer = setUpCoreRelayer(i, mapEntry.wormhole, address(mapEntry.relayProvider));
|
||||||
mapEntry.coreRelayerFull = CoreRelayer(address(mapEntry.coreRelayer));
|
mapEntry.coreRelayerFull = CoreRelayer(address(mapEntry.coreRelayer));
|
||||||
mapEntry.integration = new MockRelayerIntegration(address(mapEntry.wormhole), address(mapEntry.coreRelayer));
|
mapEntry.integration = new MockRelayerIntegration(address(mapEntry.wormhole), address(mapEntry.coreRelayer));
|
||||||
mapEntry.relayer = address(uint160(uint256(keccak256(abi.encodePacked(bytes("relayer"), i)))));
|
mapEntry.relayer = address(uint160(uint256(keccak256(abi.encodePacked(bytes("relayer"), i)))));
|
||||||
|
@ -306,7 +264,7 @@ contract TestCoreRelayer is Test {
|
||||||
) internal {
|
) internal {
|
||||||
bytes32 coreRelayerModule = 0x000000000000000000000000000000000000000000436f726552656c61796572;
|
bytes32 coreRelayerModule = 0x000000000000000000000000000000000000000000436f726552656c61796572;
|
||||||
bytes memory message =
|
bytes memory message =
|
||||||
abi.encodePacked(coreRelayerModule, uint8(2), uint16(currentChainId), chainId, coreRelayerContractAddress);
|
abi.encodePacked(coreRelayerModule, uint8(2), currentChainId, chainId, coreRelayerContractAddress);
|
||||||
IWormhole.VM memory preSignedMessage = IWormhole.VM({
|
IWormhole.VM memory preSignedMessage = IWormhole.VM({
|
||||||
version: 1,
|
version: 1,
|
||||||
timestamp: uint32(block.timestamp),
|
timestamp: uint32(block.timestamp),
|
||||||
|
@ -815,6 +773,11 @@ contract TestCoreRelayer is Test {
|
||||||
CoreRelayer.RedeliveryByTxHashInstruction instruction;
|
CoreRelayer.RedeliveryByTxHashInstruction instruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function invalidateVM(bytes memory message, WormholeSimulator simulator) internal {
|
||||||
|
change(message, message.length - 1);
|
||||||
|
simulator.invalidateVM(message);
|
||||||
|
}
|
||||||
|
|
||||||
function change(bytes memory message, uint256 index) internal {
|
function change(bytes memory message, uint256 index) internal {
|
||||||
if (message[index] == 0x02) {
|
if (message[index] == 0x02) {
|
||||||
message[index] = 0x04;
|
message[index] = 0x04;
|
||||||
|
@ -899,7 +862,7 @@ contract TestCoreRelayer is Test {
|
||||||
|
|
||||||
bytes memory fakeVM = abi.encodePacked(stack.originalDelivery.encodedVMs[2]);
|
bytes memory fakeVM = abi.encodePacked(stack.originalDelivery.encodedVMs[2]);
|
||||||
bytes memory correctVM = abi.encodePacked(stack.originalDelivery.encodedVMs[2]);
|
bytes memory correctVM = abi.encodePacked(stack.originalDelivery.encodedVMs[2]);
|
||||||
change(fakeVM, fakeVM.length - 1);
|
invalidateVM(fakeVM, setup.target.wormholeSimulator);
|
||||||
stack.originalDelivery.encodedVMs[2] = fakeVM;
|
stack.originalDelivery.encodedVMs[2] = fakeVM;
|
||||||
|
|
||||||
stack.package = CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle(
|
stack.package = CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle(
|
||||||
|
@ -936,7 +899,7 @@ contract TestCoreRelayer is Test {
|
||||||
|
|
||||||
correctVM = abi.encodePacked(stack.redeliveryVM);
|
correctVM = abi.encodePacked(stack.redeliveryVM);
|
||||||
fakeVM = abi.encodePacked(correctVM);
|
fakeVM = abi.encodePacked(correctVM);
|
||||||
change(fakeVM, fakeVM.length - 1);
|
invalidateVM(fakeVM, setup.target.wormholeSimulator);
|
||||||
|
|
||||||
stack.package = CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle(
|
stack.package = CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle(
|
||||||
fakeVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer)
|
fakeVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer)
|
||||||
|
@ -1207,7 +1170,7 @@ contract TestCoreRelayer is Test {
|
||||||
|
|
||||||
bytes memory fakeVM = abi.encodePacked(stack.deliveryVM);
|
bytes memory fakeVM = abi.encodePacked(stack.deliveryVM);
|
||||||
|
|
||||||
change(fakeVM, fakeVM.length - 1);
|
invalidateVM(fakeVM, setup.target.wormholeSimulator);
|
||||||
|
|
||||||
stack.encodedVMs = new bytes[](3);
|
stack.encodedVMs = new bytes[](3);
|
||||||
stack.encodedVMs[0] = stack.actualVM1;
|
stack.encodedVMs[0] = stack.actualVM1;
|
||||||
|
|
|
@ -2,106 +2,19 @@
|
||||||
pragma solidity ^0.8.0;
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
import {IWormhole} from "../contracts/interfaces/IWormhole.sol";
|
import {IWormhole} from "../contracts/interfaces/IWormhole.sol";
|
||||||
|
import {MockWormhole} from "../contracts/mock/MockWormhole.sol";
|
||||||
import "../contracts/libraries/external/BytesLib.sol";
|
import "../contracts/libraries/external/BytesLib.sol";
|
||||||
|
|
||||||
import "forge-std/Vm.sol";
|
import "forge-std/Vm.sol";
|
||||||
import "forge-std/console.sol";
|
import "forge-std/console.sol";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title A Wormhole Guardian Simulator
|
* @notice These are the common parts for the signing and the non signing wormhole simulators.
|
||||||
* @notice This contract simulates signing Wormhole messages emitted in a forge test.
|
|
||||||
* It overrides the Wormhole guardian set to allow for signing messages with a single
|
|
||||||
* private key on any EVM where Wormhole core contracts are deployed.
|
|
||||||
* @dev This contract is meant to be used when testing against a mainnet fork.
|
* @dev This contract is meant to be used when testing against a mainnet fork.
|
||||||
*/
|
*/
|
||||||
contract WormholeSimulator {
|
abstract contract WormholeSimulator {
|
||||||
using BytesLib for bytes;
|
using BytesLib for bytes;
|
||||||
|
|
||||||
// Taken from forge-std/Script.sol
|
|
||||||
address private constant VM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code")))));
|
|
||||||
Vm public constant vm = Vm(VM_ADDRESS);
|
|
||||||
|
|
||||||
// Allow access to Wormhole
|
|
||||||
IWormhole public wormhole;
|
|
||||||
|
|
||||||
// Save the guardian PK to sign messages with
|
|
||||||
uint256 private devnetGuardianPK;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param wormhole_ address of the Wormhole core contract for the mainnet chain being forked
|
|
||||||
* @param devnetGuardian private key of the devnet Guardian
|
|
||||||
*/
|
|
||||||
constructor(address wormhole_, uint256 devnetGuardian) {
|
|
||||||
wormhole = IWormhole(wormhole_);
|
|
||||||
devnetGuardianPK = devnetGuardian;
|
|
||||||
overrideToDevnetGuardian(vm.addr(devnetGuardian));
|
|
||||||
}
|
|
||||||
|
|
||||||
function overrideToDevnetGuardian(address devnetGuardian) internal {
|
|
||||||
{
|
|
||||||
bytes32 data = vm.load(address(this), bytes32(uint256(2)));
|
|
||||||
require(data == bytes32(0), "incorrect slot");
|
|
||||||
|
|
||||||
// Get slot for Guardian Set at the current index
|
|
||||||
uint32 guardianSetIndex = wormhole.getCurrentGuardianSetIndex();
|
|
||||||
bytes32 guardianSetSlot = keccak256(abi.encode(guardianSetIndex, 2));
|
|
||||||
|
|
||||||
// Overwrite all but first guardian set to zero address. This isn't
|
|
||||||
// necessary, but just in case we inadvertently access these slots
|
|
||||||
// for any reason.
|
|
||||||
uint256 numGuardians = uint256(vm.load(address(wormhole), guardianSetSlot));
|
|
||||||
for (uint256 i = 1; i < numGuardians;) {
|
|
||||||
vm.store(
|
|
||||||
address(wormhole), bytes32(uint256(keccak256(abi.encodePacked(guardianSetSlot))) + i), bytes32(0)
|
|
||||||
);
|
|
||||||
unchecked {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now overwrite the first guardian key with the devnet key specified
|
|
||||||
// in the function argument.
|
|
||||||
vm.store(
|
|
||||||
address(wormhole),
|
|
||||||
bytes32(uint256(keccak256(abi.encodePacked(guardianSetSlot))) + 0), // just explicit w/ index 0
|
|
||||||
bytes32(uint256(uint160(devnetGuardian)))
|
|
||||||
);
|
|
||||||
|
|
||||||
// Change the length to 1 guardian
|
|
||||||
vm.store(
|
|
||||||
address(wormhole),
|
|
||||||
guardianSetSlot,
|
|
||||||
bytes32(uint256(1)) // length == 1
|
|
||||||
);
|
|
||||||
|
|
||||||
// Confirm guardian set override
|
|
||||||
address[] memory guardians = wormhole.getGuardianSet(guardianSetIndex).keys;
|
|
||||||
require(guardians.length == 1, "guardians.length != 1");
|
|
||||||
require(guardians[0] == devnetGuardian, "incorrect guardian set override");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setMessageFee(uint256 newFee) public {
|
|
||||||
bytes32 coreModule = 0x00000000000000000000000000000000000000000000000000000000436f7265;
|
|
||||||
bytes memory message = abi.encodePacked(coreModule, uint8(3), uint16(wormhole.chainId()), newFee);
|
|
||||||
IWormhole.VM memory preSignedMessage = IWormhole.VM({
|
|
||||||
version: 1,
|
|
||||||
timestamp: uint32(block.timestamp),
|
|
||||||
nonce: 0,
|
|
||||||
emitterChainId: wormhole.governanceChainId(),
|
|
||||||
emitterAddress: wormhole.governanceContract(),
|
|
||||||
sequence: 0,
|
|
||||||
consistencyLevel: 200,
|
|
||||||
payload: message,
|
|
||||||
guardianSetIndex: 0,
|
|
||||||
signatures: new IWormhole.Signature[](0),
|
|
||||||
hash: bytes32("")
|
|
||||||
});
|
|
||||||
|
|
||||||
bytes memory signed = encodeAndSignMessage(preSignedMessage);
|
|
||||||
wormhole.submitSetMessageFee(signed);
|
|
||||||
}
|
|
||||||
|
|
||||||
function doubleKeccak256(bytes memory body) internal pure returns (bytes32) {
|
function doubleKeccak256(bytes memory body) internal pure returns (bytes32) {
|
||||||
return keccak256(abi.encodePacked(keccak256(body)));
|
return keccak256(abi.encodePacked(keccak256(body)));
|
||||||
}
|
}
|
||||||
|
@ -192,11 +105,8 @@ contract WormholeSimulator {
|
||||||
public
|
public
|
||||||
returns (bytes memory signedMessage)
|
returns (bytes memory signedMessage)
|
||||||
{
|
{
|
||||||
// Create message instance
|
|
||||||
IWormhole.VM memory vm_;
|
|
||||||
|
|
||||||
// Parse wormhole message from ethereum logs
|
// Parse wormhole message from ethereum logs
|
||||||
vm_ = parseVMFromLogs(log);
|
IWormhole.VM memory vm_ = parseVMFromLogs(log);
|
||||||
|
|
||||||
// Set empty body values before computing the hash
|
// Set empty body values before computing the hash
|
||||||
vm_.version = uint8(1);
|
vm_.version = uint8(1);
|
||||||
|
@ -207,6 +117,67 @@ contract WormholeSimulator {
|
||||||
return encodeAndSignMessage(vm_);
|
return encodeAndSignMessage(vm_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functions that must be implemented by concrete wormhole simulators.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Sets the message fee for a wormhole message.
|
||||||
|
*/
|
||||||
|
function setMessageFee(uint256 newFee) public virtual;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Invalidates a VM. It must be executed before it is parsed and verified by the Wormhole instance to work.
|
||||||
|
*/
|
||||||
|
function invalidateVM(bytes memory message) public virtual;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Formats and signs a simulated Wormhole batch VAA given an array of Wormhole log entries
|
||||||
|
* @param logs The forge Vm.log entries captured when recording events during test execution
|
||||||
|
* @param nonce The nonce of the messages to be accumulated into the batch VAA
|
||||||
|
* @return signedMessage Formatted and signed Wormhole message
|
||||||
|
*/
|
||||||
|
function fetchSignedBatchVAAFromLogs(
|
||||||
|
Vm.Log[] memory logs,
|
||||||
|
uint32 nonce,
|
||||||
|
uint16 emitterChainId,
|
||||||
|
address emitterAddress
|
||||||
|
) public virtual returns (bytes memory signedMessage);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Signs and preformatted simulated Wormhole message
|
||||||
|
* @param vm_ The preformatted Wormhole message
|
||||||
|
* @return signedMessage Formatted and signed Wormhole message
|
||||||
|
*/
|
||||||
|
function encodeAndSignMessage(IWormhole.VM memory vm_) public virtual returns (bytes memory signedMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title A Wormhole Guardian Simulator
|
||||||
|
* @notice This contract simulates signing Wormhole messages emitted in a forge test.
|
||||||
|
* This particular version doesn't sign any message but just exists to keep a standard interface for tests.
|
||||||
|
* @dev This contract is meant to be used with the MockWormhole contract that validates any VM as long
|
||||||
|
* as its hash wasn't banned.
|
||||||
|
*/
|
||||||
|
contract FakeWormholeSimulator is WormholeSimulator {
|
||||||
|
// Allow access to Wormhole
|
||||||
|
MockWormhole public wormhole;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param initWormhole address of the Wormhole core contract for the mainnet chain being forked
|
||||||
|
*/
|
||||||
|
constructor(MockWormhole initWormhole) {
|
||||||
|
wormhole = initWormhole;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMessageFee(uint256 newFee) public override {
|
||||||
|
wormhole.setMessageFee(newFee);
|
||||||
|
}
|
||||||
|
|
||||||
|
function invalidateVM(bytes memory message) public override {
|
||||||
|
wormhole.invalidateVM(message);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @notice Formats and signs a simulated Wormhole batch VAA given an array of Wormhole log entries
|
* @notice Formats and signs a simulated Wormhole batch VAA given an array of Wormhole log entries
|
||||||
* @param logs The forge Vm.log entries captured when recording events during test execution
|
* @param logs The forge Vm.log entries captured when recording events during test execution
|
||||||
|
@ -218,7 +189,198 @@ contract WormholeSimulator {
|
||||||
uint32 nonce,
|
uint32 nonce,
|
||||||
uint16 emitterChainId,
|
uint16 emitterChainId,
|
||||||
address emitterAddress
|
address emitterAddress
|
||||||
) public returns (bytes memory signedMessage) {
|
) public override returns (bytes memory signedMessage) {
|
||||||
|
uint8 numObservations = 0;
|
||||||
|
IWormhole.VM[] memory vm_ = new IWormhole.VM[](logs.length);
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < logs.length; i++) {
|
||||||
|
vm_[i] = parseVMFromLogs(logs[i]);
|
||||||
|
vm_[i].timestamp = uint32(block.timestamp);
|
||||||
|
vm_[i].emitterChainId = emitterChainId;
|
||||||
|
vm_[i].emitterAddress = bytes32(uint256(uint160(emitterAddress)));
|
||||||
|
if (vm_[i].nonce == nonce) {
|
||||||
|
numObservations += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes memory packedObservations;
|
||||||
|
bytes32[] memory hashes = new bytes32[](numObservations);
|
||||||
|
|
||||||
|
uint8 counter = 0;
|
||||||
|
for (uint256 i = 0; i < logs.length; i++) {
|
||||||
|
if (vm_[i].nonce == nonce) {
|
||||||
|
bytes memory observation = abi.encodePacked(
|
||||||
|
vm_[i].timestamp,
|
||||||
|
vm_[i].nonce,
|
||||||
|
vm_[i].emitterChainId,
|
||||||
|
vm_[i].emitterAddress,
|
||||||
|
vm_[i].sequence,
|
||||||
|
vm_[i].consistencyLevel,
|
||||||
|
vm_[i].payload
|
||||||
|
);
|
||||||
|
hashes[counter] = doubleKeccak256(observation);
|
||||||
|
packedObservations =
|
||||||
|
abi.encodePacked(packedObservations, uint8(counter), uint32(observation.length), observation);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signedMessage = abi.encodePacked(
|
||||||
|
// vm version
|
||||||
|
uint8(2),
|
||||||
|
wormhole.getCurrentGuardianSetIndex(),
|
||||||
|
// length of signature array
|
||||||
|
uint8(1),
|
||||||
|
// guardian index
|
||||||
|
uint8(0),
|
||||||
|
// r sig argument
|
||||||
|
bytes32(uint256(0)),
|
||||||
|
// s sig argument
|
||||||
|
bytes32(uint256(0)),
|
||||||
|
// v sig argument (encodes public key recovery id, public key type and network of the signature)
|
||||||
|
uint8(0),
|
||||||
|
numObservations,
|
||||||
|
hashes,
|
||||||
|
numObservations,
|
||||||
|
packedObservations
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Signs and preformatted simulated Wormhole message
|
||||||
|
* @param vm_ The preformatted Wormhole message
|
||||||
|
* @return signedMessage Formatted and signed Wormhole message
|
||||||
|
*/
|
||||||
|
function encodeAndSignMessage(IWormhole.VM memory vm_) public override returns (bytes memory signedMessage) {
|
||||||
|
// Compute the hash of the body
|
||||||
|
bytes memory body = encodeObservation(vm_);
|
||||||
|
vm_.hash = doubleKeccak256(body);
|
||||||
|
|
||||||
|
signedMessage = abi.encodePacked(
|
||||||
|
vm_.version,
|
||||||
|
wormhole.getCurrentGuardianSetIndex(),
|
||||||
|
// length of signature array
|
||||||
|
uint8(1),
|
||||||
|
// guardian index
|
||||||
|
uint8(0),
|
||||||
|
// r sig argument
|
||||||
|
bytes32(uint256(0)),
|
||||||
|
// s sig argument
|
||||||
|
bytes32(uint256(0)),
|
||||||
|
// v sig argument (encodes public key recovery id, public key type and network of the signature)
|
||||||
|
uint8(0),
|
||||||
|
body
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title A Wormhole Guardian Simulator
|
||||||
|
* @notice This contract simulates signing Wormhole messages emitted in a forge test.
|
||||||
|
* It overrides the Wormhole guardian set to allow for signing messages with a single
|
||||||
|
* private key on any EVM where Wormhole core contracts are deployed.
|
||||||
|
* @dev This contract is meant to be used when testing against a mainnet fork.
|
||||||
|
*/
|
||||||
|
contract SigningWormholeSimulator is WormholeSimulator {
|
||||||
|
// Taken from forge-std/Script.sol
|
||||||
|
address private constant VM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code")))));
|
||||||
|
Vm public constant vm = Vm(VM_ADDRESS);
|
||||||
|
|
||||||
|
// Allow access to Wormhole
|
||||||
|
IWormhole public wormhole;
|
||||||
|
|
||||||
|
// Save the guardian PK to sign messages with
|
||||||
|
uint256 private devnetGuardianPK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param wormhole_ address of the Wormhole core contract for the mainnet chain being forked
|
||||||
|
* @param devnetGuardian private key of the devnet Guardian
|
||||||
|
*/
|
||||||
|
constructor(IWormhole wormhole_, uint256 devnetGuardian) {
|
||||||
|
wormhole = wormhole_;
|
||||||
|
devnetGuardianPK = devnetGuardian;
|
||||||
|
overrideToDevnetGuardian(vm.addr(devnetGuardian));
|
||||||
|
}
|
||||||
|
|
||||||
|
function overrideToDevnetGuardian(address devnetGuardian) internal {
|
||||||
|
{
|
||||||
|
// Get slot for Guardian Set at the current index
|
||||||
|
uint32 guardianSetIndex = wormhole.getCurrentGuardianSetIndex();
|
||||||
|
bytes32 guardianSetSlot = keccak256(abi.encode(guardianSetIndex, 2));
|
||||||
|
|
||||||
|
// Overwrite all but first guardian set to zero address. This isn't
|
||||||
|
// necessary, but just in case we inadvertently access these slots
|
||||||
|
// for any reason.
|
||||||
|
uint256 numGuardians = uint256(vm.load(address(wormhole), guardianSetSlot));
|
||||||
|
for (uint256 i = 1; i < numGuardians;) {
|
||||||
|
vm.store(
|
||||||
|
address(wormhole), bytes32(uint256(keccak256(abi.encodePacked(guardianSetSlot))) + i), bytes32(0)
|
||||||
|
);
|
||||||
|
unchecked {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now overwrite the first guardian key with the devnet key specified
|
||||||
|
// in the function argument.
|
||||||
|
vm.store(
|
||||||
|
address(wormhole),
|
||||||
|
bytes32(uint256(keccak256(abi.encodePacked(guardianSetSlot))) + 0), // just explicit w/ index 0
|
||||||
|
bytes32(uint256(uint160(devnetGuardian)))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Change the length to 1 guardian
|
||||||
|
vm.store(
|
||||||
|
address(wormhole),
|
||||||
|
guardianSetSlot,
|
||||||
|
bytes32(uint256(1)) // length == 1
|
||||||
|
);
|
||||||
|
|
||||||
|
// Confirm guardian set override
|
||||||
|
address[] memory guardians = wormhole.getGuardianSet(guardianSetIndex).keys;
|
||||||
|
require(guardians.length == 1, "guardians.length != 1");
|
||||||
|
require(guardians[0] == devnetGuardian, "incorrect guardian set override");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMessageFee(uint256 newFee) public override {
|
||||||
|
bytes32 coreModule = 0x00000000000000000000000000000000000000000000000000000000436f7265;
|
||||||
|
bytes memory message = abi.encodePacked(coreModule, uint8(3), uint16(wormhole.chainId()), newFee);
|
||||||
|
IWormhole.VM memory preSignedMessage = IWormhole.VM({
|
||||||
|
version: 1,
|
||||||
|
timestamp: uint32(block.timestamp),
|
||||||
|
nonce: 0,
|
||||||
|
emitterChainId: wormhole.governanceChainId(),
|
||||||
|
emitterAddress: wormhole.governanceContract(),
|
||||||
|
sequence: 0,
|
||||||
|
consistencyLevel: 200,
|
||||||
|
payload: message,
|
||||||
|
guardianSetIndex: 0,
|
||||||
|
signatures: new IWormhole.Signature[](0),
|
||||||
|
hash: bytes32("")
|
||||||
|
});
|
||||||
|
|
||||||
|
bytes memory signed = encodeAndSignMessage(preSignedMessage);
|
||||||
|
wormhole.submitSetMessageFee(signed);
|
||||||
|
}
|
||||||
|
|
||||||
|
function invalidateVM(bytes memory message) public pure override {
|
||||||
|
// Don't do anything. Signatures are easily invalidated modifying the payload.
|
||||||
|
// If it becomes necessary to prevent producing a good signature for this message, that can be done here.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Formats and signs a simulated Wormhole batch VAA given an array of Wormhole log entries
|
||||||
|
* @param logs The forge Vm.log entries captured when recording events during test execution
|
||||||
|
* @param nonce The nonce of the messages to be accumulated into the batch VAA
|
||||||
|
* @return signedMessage Formatted and signed Wormhole message
|
||||||
|
*/
|
||||||
|
function fetchSignedBatchVAAFromLogs(
|
||||||
|
Vm.Log[] memory logs,
|
||||||
|
uint32 nonce,
|
||||||
|
uint16 emitterChainId,
|
||||||
|
address emitterAddress
|
||||||
|
) public override returns (bytes memory signedMessage) {
|
||||||
uint8 numObservations = 0;
|
uint8 numObservations = 0;
|
||||||
IWormhole.VM[] memory vm_ = new IWormhole.VM[](logs.length);
|
IWormhole.VM[] memory vm_ = new IWormhole.VM[](logs.length);
|
||||||
|
|
||||||
|
@ -281,7 +443,7 @@ contract WormholeSimulator {
|
||||||
* @param vm_ The preformatted Wormhole message
|
* @param vm_ The preformatted Wormhole message
|
||||||
* @return signedMessage Formatted and signed Wormhole message
|
* @return signedMessage Formatted and signed Wormhole message
|
||||||
*/
|
*/
|
||||||
function encodeAndSignMessage(IWormhole.VM memory vm_) public returns (bytes memory signedMessage) {
|
function encodeAndSignMessage(IWormhole.VM memory vm_) public override returns (bytes memory signedMessage) {
|
||||||
// Compute the hash of the body
|
// Compute the hash of the body
|
||||||
bytes memory body = encodeObservation(vm_);
|
bytes memory body = encodeObservation(vm_);
|
||||||
vm_.hash = doubleKeccak256(body);
|
vm_.hash = doubleKeccak256(body);
|
||||||
|
|
Loading…
Reference in New Issue