Improve forge test coverage + Seperate tests into individual tests (#119)
* fast tests * forge fmt * Remove parameter from forge test generic relayer * WIP * WIP * Move Forge Mock Generic Relayer to seperate file * Split revert redelivery tests into first few seperate tests * redelivery test changes * WIP * tests pass * Redelivery tests less D.R.Y and seperated into individual tests * Delivery tests less D.R.Y and seperated into individual tests * Resend checks in seperate tests * Forward test coverage! * 100% test coverage in CoreRelayer and CoreRelayerDelivery! * remove test coverage files * Governance tests * governance stack * forge fmt * removed console.sol
This commit is contained in:
parent
48e206168b
commit
9e282508f7
|
@ -1,4 +1,3 @@
|
|||
// contracts/Bridge.sol
|
||||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
|
|
@ -292,9 +292,8 @@ contract CoreRelayerDelivery is CoreRelayerGovernance {
|
|||
// - the permissioned address allowed to execute this redelivery instruction is the permissioned address allowed to execute the old instruction
|
||||
valid = checkRedeliveryInstructionTarget(redeliveryInstruction, originalInstruction);
|
||||
|
||||
// Emit an 'Invalid Redelivery' event if one of the following five checks failed:
|
||||
// - msg.sender is the permissioned address allowed to execute this redelivery instruction
|
||||
// - the redelivery instruction's target chain = this chain
|
||||
// Emit an 'Invalid Redelivery' event if one of the following four checks failed:
|
||||
// - the permissioned address allowed to execute this redelivery instruction is the permissioned address allowed to execute the old instruction
|
||||
// - the original instruction's target chain = this chain
|
||||
// - the new redelivery instruction's 'receiver value' amount >= the original instruction's 'receiver value' amount
|
||||
// - the new redelivery instruction's upper bound on gas >= the original instruction's upper bound on gas
|
||||
|
@ -345,9 +344,16 @@ contract CoreRelayerDelivery is CoreRelayerGovernance {
|
|||
) internal view returns (bool isValid) {
|
||||
address providerAddress = fromWormholeFormat(redeliveryInstruction.executionParameters.providerDeliveryAddress);
|
||||
|
||||
// Check that the permissioned address allowed to execute this redelivery instruction is the permissioned address allowed to execute the old instruction
|
||||
if ((providerAddress != fromWormholeFormat(originalInstruction.executionParameters.providerDeliveryAddress))) {
|
||||
revert IDelivery.MismatchingRelayProvidersInRedelivery();
|
||||
// Check that msg.sender is the permissioned address allowed to execute this redelivery instruction
|
||||
if (providerAddress != msg.sender) {
|
||||
revert IDelivery.UnexpectedRelayer();
|
||||
}
|
||||
|
||||
uint16 whChainId = chainId();
|
||||
|
||||
// Check that the redelivery instruction's target chain = this chain
|
||||
if (whChainId != redeliveryInstruction.targetChain) {
|
||||
revert IDelivery.TargetChainIsNotThisChain(redeliveryInstruction.targetChain);
|
||||
}
|
||||
|
||||
uint256 wormholeMessageFee = wormhole().messageFee();
|
||||
|
@ -361,12 +367,10 @@ contract CoreRelayerDelivery is CoreRelayerGovernance {
|
|||
revert IDelivery.InsufficientRelayerFunds();
|
||||
}
|
||||
|
||||
uint16 whChainId = chainId();
|
||||
|
||||
// Check that msg.sender is the permissioned address allowed to execute this redelivery instruction
|
||||
isValid = msg.sender == providerAddress
|
||||
// Check that the redelivery instruction's target chain = this chain
|
||||
&& whChainId == redeliveryInstruction.targetChain
|
||||
// Check that the permissioned address allowed to execute this redelivery instruction is the permissioned address allowed to execute the old instruction
|
||||
isValid = (
|
||||
providerAddress == fromWormholeFormat(originalInstruction.executionParameters.providerDeliveryAddress)
|
||||
)
|
||||
// Check that the original instruction's target chain = this chain
|
||||
&& whChainId == originalInstruction.targetChain
|
||||
// Check that the new redelivery instruction's 'receiver value' amount >= the original instruction's 'receiver value' amount
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// contracts/Messages.sol
|
||||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// contracts/Messages.sol
|
||||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// contracts/Messages.sol
|
||||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// contracts/Messages.sol
|
||||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
|
|
@ -153,7 +153,7 @@ contract MockRelayerIntegration is IWormholeReceiver {
|
|||
(IWormhole.VM memory parsed, bool valid, string memory reason) =
|
||||
wormhole.parseAndVerifyVM(wormholeObservations[i]);
|
||||
require(valid, reason);
|
||||
require(registeredContracts[parsed.emitterChainId] == parsed.emitterAddress);
|
||||
require(registeredContracts[parsed.emitterChainId] == parsed.emitterAddress, "Emitter address not valid");
|
||||
emitterChainId = parsed.emitterChainId;
|
||||
messages[i] = parsed.payload;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../contracts/interfaces/IWormhole.sol";
|
||||
import "../contracts/interfaces/IWormholeReceiver.sol";
|
||||
import "../contracts/interfaces/IWormholeRelayer.sol";
|
||||
import "../contracts/interfaces/IRelayProvider.sol";
|
||||
import "../contracts/libraries/external/BytesLib.sol";
|
||||
import "./MockGenericRelayer.sol";
|
||||
import "forge-std/console.sol";
|
||||
import "forge-std/Vm.sol";
|
||||
|
||||
contract ForwardTester is IWormholeReceiver {
|
||||
using BytesLib for bytes;
|
||||
|
||||
IWormhole wormhole;
|
||||
IWormholeRelayer wormholeRelayer;
|
||||
MockGenericRelayer genericRelayer;
|
||||
|
||||
address private constant VM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
|
||||
Vm public constant vm = Vm(VM_ADDRESS);
|
||||
|
||||
constructor(address _wormhole, address _wormholeRelayer, address _wormholeSimulator) {
|
||||
wormhole = IWormhole(_wormhole);
|
||||
wormholeRelayer = IWormholeRelayer(_wormholeRelayer);
|
||||
genericRelayer = new MockGenericRelayer(_wormhole, _wormholeSimulator, _wormholeRelayer);
|
||||
genericRelayer.setWormholeRelayerContract(wormhole.chainId(), address(wormholeRelayer));
|
||||
genericRelayer.setProviderDeliveryAddress(
|
||||
wormhole.chainId(),
|
||||
wormholeRelayer.fromWormholeFormat(
|
||||
IRelayProvider(wormholeRelayer.getDefaultRelayProvider()).getDeliveryAddress(wormhole.chainId())
|
||||
)
|
||||
);
|
||||
genericRelayer.setWormholeFee(wormhole.chainId(), wormhole.messageFee());
|
||||
}
|
||||
|
||||
enum Action {
|
||||
MultipleForwardsRequested,
|
||||
ForwardRequestFromWrongAddress,
|
||||
NonceIsZero,
|
||||
MultichainSendEmpty,
|
||||
MaxTransactionFeeNotEnough,
|
||||
FundsTooMuch,
|
||||
ReentrantCall,
|
||||
WorksCorrectly
|
||||
}
|
||||
|
||||
function receiveWormholeMessages(bytes[] memory vaas, bytes[] memory additionalData) public payable override {
|
||||
(IWormhole.VM memory vaa, bool valid, string memory reason) = wormhole.parseAndVerifyVM(vaas[0]);
|
||||
require(valid, reason);
|
||||
|
||||
bytes memory payload = vaa.payload;
|
||||
Action action = Action(payload.toUint8(0));
|
||||
|
||||
if (action == Action.MultipleForwardsRequested) {
|
||||
uint256 maxTransactionFee =
|
||||
wormholeRelayer.quoteGas(vaa.emitterChainId, 10000, wormholeRelayer.getDefaultRelayProvider());
|
||||
wormholeRelayer.forward(vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, maxTransactionFee, 0, 1);
|
||||
wormholeRelayer.forward(vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, maxTransactionFee, 0, 1);
|
||||
} else if (action == Action.ForwardRequestFromWrongAddress) {
|
||||
// Emitter must be a wormhole relayer
|
||||
uint256 maxTransactionFee =
|
||||
wormholeRelayer.quoteGas(vaa.emitterChainId, 10000, wormholeRelayer.getDefaultRelayProvider());
|
||||
DummyContract dc = new DummyContract(address(wormholeRelayer));
|
||||
dc.forward(vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, maxTransactionFee, 0, 1);
|
||||
} else if (action == Action.NonceIsZero) {
|
||||
uint256 maxTransactionFee =
|
||||
wormholeRelayer.quoteGas(vaa.emitterChainId, 10000, wormholeRelayer.getDefaultRelayProvider());
|
||||
wormholeRelayer.forward(vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, maxTransactionFee, 0, 0);
|
||||
} else if (action == Action.MultichainSendEmpty) {
|
||||
wormholeRelayer.multichainForward(
|
||||
IWormholeRelayer.MultichainSend(
|
||||
wormholeRelayer.getDefaultRelayProvider(), new IWormholeRelayer.Send[](0)
|
||||
),
|
||||
1
|
||||
);
|
||||
} else if (action == Action.MaxTransactionFeeNotEnough) {
|
||||
uint256 maxTransactionFee =
|
||||
wormholeRelayer.quoteGas(vaa.emitterChainId, 1, wormholeRelayer.getDefaultRelayProvider()) - 1;
|
||||
wormholeRelayer.forward(vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, maxTransactionFee, 0, 1);
|
||||
} else if (action == Action.FundsTooMuch) {
|
||||
// set maximum budget to less than this
|
||||
uint256 maxTransactionFee =
|
||||
wormholeRelayer.quoteGas(vaa.emitterChainId, 10000, wormholeRelayer.getDefaultRelayProvider());
|
||||
wormholeRelayer.forward(vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, maxTransactionFee, 0, 1);
|
||||
} else if (action == Action.ReentrantCall) {
|
||||
uint256 maxTransactionFee =
|
||||
wormholeRelayer.quoteGas(wormhole.chainId(), 10000, wormholeRelayer.getDefaultRelayProvider());
|
||||
vm.recordLogs();
|
||||
wormholeRelayer.send{value: maxTransactionFee + wormhole.messageFee()}(
|
||||
wormhole.chainId(), vaa.emitterAddress, vaa.emitterAddress, maxTransactionFee, 0, 1
|
||||
);
|
||||
genericRelayer.relay(wormhole.chainId());
|
||||
} else {
|
||||
uint256 maxTransactionFee =
|
||||
wormholeRelayer.quoteGas(vaa.emitterChainId, 10000, wormholeRelayer.getDefaultRelayProvider());
|
||||
wormholeRelayer.forward(vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, maxTransactionFee, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
receive() external payable {}
|
||||
}
|
||||
|
||||
contract DummyContract {
|
||||
IWormholeRelayer wormholeRelayer;
|
||||
|
||||
constructor(address _wormholeRelayer) {
|
||||
wormholeRelayer = IWormholeRelayer(_wormholeRelayer);
|
||||
}
|
||||
|
||||
function forward(
|
||||
uint16 chainId,
|
||||
bytes32 targetAddress,
|
||||
bytes32 refundAddress,
|
||||
uint256 maxTransactionFee,
|
||||
uint256 receiverValue,
|
||||
uint32 nonce
|
||||
) public {
|
||||
wormholeRelayer.forward(chainId, targetAddress, refundAddress, maxTransactionFee, receiverValue, nonce);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
interface IWormholeRelayerInstructionParser {
|
||||
struct DeliveryInstructionsContainer {
|
||||
uint8 payloadId; //1
|
||||
bool sufficientlyFunded;
|
||||
DeliveryInstruction[] instructions;
|
||||
}
|
||||
|
||||
struct DeliveryInstruction {
|
||||
uint16 targetChain;
|
||||
bytes32 targetAddress;
|
||||
bytes32 refundAddress;
|
||||
uint256 maximumRefundTarget;
|
||||
uint256 receiverValueTarget;
|
||||
ExecutionParameters executionParameters;
|
||||
}
|
||||
|
||||
struct ExecutionParameters {
|
||||
uint8 version;
|
||||
uint32 gasLimit;
|
||||
bytes32 providerDeliveryAddress;
|
||||
}
|
||||
|
||||
struct RedeliveryByTxHashInstruction {
|
||||
uint8 payloadId; //2
|
||||
uint16 sourceChain;
|
||||
bytes32 sourceTxHash;
|
||||
uint32 sourceNonce;
|
||||
uint16 targetChain;
|
||||
uint8 deliveryIndex;
|
||||
uint8 multisendIndex;
|
||||
uint256 newMaximumRefundTarget;
|
||||
uint256 newReceiverValueTarget;
|
||||
ExecutionParameters executionParameters;
|
||||
}
|
||||
|
||||
function decodeDeliveryInstructionsContainer(bytes memory encoded)
|
||||
external
|
||||
pure
|
||||
returns (DeliveryInstructionsContainer memory);
|
||||
|
||||
function decodeRedeliveryInstruction(bytes memory encoded)
|
||||
external
|
||||
pure
|
||||
returns (RedeliveryByTxHashInstruction memory instruction);
|
||||
|
||||
function toWormholeFormat(address addr) external pure returns (bytes32 whFormat);
|
||||
|
||||
function fromWormholeFormat(bytes32 whFormatAddress) external pure returns (address addr);
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {IWormholeRelayer} from "../contracts/interfaces/IWormholeRelayer.sol";
|
||||
import {IDelivery} from "../contracts/interfaces/IDelivery.sol";
|
||||
import {IWormholeRelayerInstructionParser} from "./IWormholeRelayerInstructionParser.sol";
|
||||
import {IWormhole} from "../contracts/interfaces/IWormhole.sol";
|
||||
import {WormholeSimulator} from "./WormholeSimulator.sol";
|
||||
import "../contracts/libraries/external/BytesLib.sol";
|
||||
import "forge-std/Vm.sol";
|
||||
import "forge-std/console.sol";
|
||||
|
||||
contract MockGenericRelayer {
|
||||
using BytesLib for bytes;
|
||||
|
||||
IWormhole relayerWormhole;
|
||||
WormholeSimulator relayerWormholeSimulator;
|
||||
IWormholeRelayerInstructionParser parser;
|
||||
|
||||
address private constant VM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
|
||||
Vm public constant vm = Vm(VM_ADDRESS);
|
||||
|
||||
mapping(uint16 => address) wormholeRelayerContracts;
|
||||
|
||||
mapping(uint16 => address) relayers;
|
||||
|
||||
mapping(uint16 => uint256) wormholeFees;
|
||||
|
||||
mapping(uint256 => bool) nonceCompleted;
|
||||
|
||||
mapping(bytes32 => bytes[]) pastEncodedVMs;
|
||||
|
||||
constructor(address _wormhole, address _wormholeSimulator, address wormholeRelayer) {
|
||||
// deploy Wormhole
|
||||
|
||||
relayerWormhole = IWormhole(_wormhole);
|
||||
relayerWormholeSimulator = WormholeSimulator(_wormholeSimulator);
|
||||
parser = IWormholeRelayerInstructionParser(wormholeRelayer);
|
||||
}
|
||||
|
||||
function getPastEncodedVMs(bytes32 vaaHash) public view returns (bytes[] memory) {
|
||||
return pastEncodedVMs[vaaHash];
|
||||
}
|
||||
|
||||
function setWormholeRelayerContract(uint16 chainId, address contractAddress) public {
|
||||
wormholeRelayerContracts[chainId] = contractAddress;
|
||||
}
|
||||
|
||||
function setProviderDeliveryAddress(uint16 chainId, address deliveryAddress) public {
|
||||
relayers[chainId] = deliveryAddress;
|
||||
}
|
||||
|
||||
function setWormholeFee(uint16 chainId, uint256 fee) public {
|
||||
wormholeFees[chainId] = fee;
|
||||
}
|
||||
|
||||
function relay(uint16 chainId) public {
|
||||
relay(vm.getRecordedLogs(), chainId);
|
||||
}
|
||||
|
||||
function relay(Vm.Log[] memory logs, uint16 chainId) public {
|
||||
Vm.Log[] memory entries = relayerWormholeSimulator.fetchWormholeMessageFromLog(logs);
|
||||
bytes[] memory encodedVMs = new bytes[](entries.length);
|
||||
for (uint256 i = 0; i < encodedVMs.length; i++) {
|
||||
encodedVMs[i] = relayerWormholeSimulator.fetchSignedMessageFromLogs(
|
||||
entries[i], chainId, address(uint160(uint256(bytes32(entries[i].topics[1]))))
|
||||
);
|
||||
}
|
||||
IWormhole.VM[] memory parsed = new IWormhole.VM[](encodedVMs.length);
|
||||
for (uint16 i = 0; i < encodedVMs.length; i++) {
|
||||
parsed[i] = relayerWormhole.parseVM(encodedVMs[i]);
|
||||
}
|
||||
for (uint16 i = 0; i < encodedVMs.length; i++) {
|
||||
if (!nonceCompleted[parsed[i].nonce]) {
|
||||
nonceCompleted[parsed[i].nonce] = true;
|
||||
uint8 length = 1;
|
||||
for (uint16 j = i + 1; j < encodedVMs.length; j++) {
|
||||
if (parsed[i].nonce == parsed[j].nonce) {
|
||||
length++;
|
||||
}
|
||||
}
|
||||
bytes[] memory encodedVMsToBeDelivered = new bytes[](length);
|
||||
uint8 counter = 0;
|
||||
for (uint16 j = i; j < encodedVMs.length; j++) {
|
||||
if (parsed[i].nonce == parsed[j].nonce) {
|
||||
encodedVMsToBeDelivered[counter] = encodedVMs[j];
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
counter = 0;
|
||||
for (uint16 j = i; j < encodedVMs.length; j++) {
|
||||
if (parsed[i].nonce == parsed[j].nonce) {
|
||||
if (
|
||||
parsed[j].emitterAddress == parser.toWormholeFormat(wormholeRelayerContracts[chainId])
|
||||
&& (parsed[j].emitterChainId == chainId)
|
||||
) {
|
||||
genericRelay(counter, encodedVMs[j], encodedVMsToBeDelivered, parsed[j]);
|
||||
}
|
||||
counter += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint8 i = 0; i < encodedVMs.length; i++) {
|
||||
nonceCompleted[parsed[i].nonce] = false;
|
||||
}
|
||||
}
|
||||
|
||||
function genericRelay(
|
||||
uint8 counter,
|
||||
bytes memory encodedDeliveryInstructionsContainer,
|
||||
bytes[] memory encodedVMsToBeDelivered,
|
||||
IWormhole.VM memory parsedInstruction
|
||||
) internal {
|
||||
uint8 payloadId = parsedInstruction.payload.toUint8(0);
|
||||
if (payloadId == 1) {
|
||||
IWormholeRelayerInstructionParser.DeliveryInstructionsContainer memory container =
|
||||
parser.decodeDeliveryInstructionsContainer(parsedInstruction.payload);
|
||||
for (uint8 k = 0; k < container.instructions.length; k++) {
|
||||
uint256 budget =
|
||||
container.instructions[k].maximumRefundTarget + container.instructions[k].receiverValueTarget;
|
||||
uint16 targetChain = container.instructions[k].targetChain;
|
||||
IDelivery.TargetDeliveryParametersSingle memory package = IDelivery.TargetDeliveryParametersSingle({
|
||||
encodedVMs: encodedVMsToBeDelivered,
|
||||
deliveryIndex: counter,
|
||||
multisendIndex: k,
|
||||
relayerRefundAddress: payable(relayers[targetChain])
|
||||
});
|
||||
if (container.sufficientlyFunded) {
|
||||
vm.prank(relayers[targetChain]);
|
||||
IDelivery(wormholeRelayerContracts[targetChain]).deliverSingle{
|
||||
value: (budget + wormholeFees[targetChain])
|
||||
}(package);
|
||||
}
|
||||
}
|
||||
pastEncodedVMs[parsedInstruction.hash] = encodedVMsToBeDelivered;
|
||||
} else if (payloadId == 2) {
|
||||
IWormholeRelayerInstructionParser.RedeliveryByTxHashInstruction memory instruction =
|
||||
parser.decodeRedeliveryInstruction(parsedInstruction.payload);
|
||||
bytes[] memory originalEncodedVMs = pastEncodedVMs[instruction.sourceTxHash];
|
||||
uint16 targetChain = instruction.targetChain;
|
||||
uint256 budget =
|
||||
instruction.newMaximumRefundTarget + instruction.newReceiverValueTarget + wormholeFees[targetChain];
|
||||
IDelivery.TargetRedeliveryByTxHashParamsSingle memory package = IDelivery
|
||||
.TargetRedeliveryByTxHashParamsSingle({
|
||||
redeliveryVM: encodedDeliveryInstructionsContainer,
|
||||
sourceEncodedVMs: originalEncodedVMs,
|
||||
relayerRefundAddress: payable(relayers[targetChain])
|
||||
});
|
||||
vm.prank(relayers[targetChain]);
|
||||
IDelivery(wormholeRelayerContracts[targetChain]).redeliverSingle{value: budget}(package);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {IRelayProvider} from "../contracts/interfaces/IRelayProvider.sol";
|
||||
import {RelayProvider} from "../contracts/relayProvider/RelayProvider.sol";
|
||||
import {RelayProviderSetup} from "../contracts/relayProvider/RelayProviderSetup.sol";
|
||||
import {RelayProviderImplementation} from "../contracts/relayProvider/RelayProviderImplementation.sol";
|
||||
import {RelayProviderProxy} from "../contracts/relayProvider/RelayProviderProxy.sol";
|
||||
import {IWormholeRelayer} from "../contracts/interfaces/IWormholeRelayer.sol";
|
||||
import {CoreRelayer} from "../contracts/coreRelayer/CoreRelayer.sol";
|
||||
import {CoreRelayerSetup} from "../contracts/coreRelayer/CoreRelayerSetup.sol";
|
||||
import {CoreRelayerImplementation} from "../contracts/coreRelayer/CoreRelayerImplementation.sol";
|
||||
import {CoreRelayerProxy} from "../contracts/coreRelayer/CoreRelayerProxy.sol";
|
||||
import {CoreRelayerGovernance} from "../contracts/coreRelayer/CoreRelayerGovernance.sol";
|
||||
import {MockGenericRelayer} from "./MockGenericRelayer.sol";
|
||||
import {MockWormhole} from "../contracts/mock/MockWormhole.sol";
|
||||
import {IWormhole} from "../contracts/interfaces/IWormhole.sol";
|
||||
import {WormholeSimulator, FakeWormholeSimulator} from "./WormholeSimulator.sol";
|
||||
import "../contracts/libraries/external/BytesLib.sol";
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
import "forge-std/console.sol";
|
||||
import "forge-std/Vm.sol";
|
||||
|
||||
contract TestHelpers {
|
||||
using BytesLib for bytes;
|
||||
|
||||
address private constant VM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
|
||||
Vm public constant vm = Vm(VM_ADDRESS);
|
||||
|
||||
WormholeSimulator helperWormholeSimulator;
|
||||
|
||||
constructor() {
|
||||
(, helperWormholeSimulator) = setUpWormhole(1);
|
||||
}
|
||||
|
||||
function registerCoreRelayerContract(
|
||||
CoreRelayer governance,
|
||||
IWormhole wormhole,
|
||||
uint16 currentChainId,
|
||||
uint16 chainId,
|
||||
bytes32 coreRelayerContractAddress
|
||||
) public {
|
||||
bytes32 coreRelayerModule = 0x000000000000000000000000000000000000000000436F726552656C61796572;
|
||||
bytes memory message =
|
||||
abi.encodePacked(coreRelayerModule, uint8(2), currentChainId, chainId, coreRelayerContractAddress);
|
||||
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 = helperWormholeSimulator.encodeAndSignMessage(preSignedMessage);
|
||||
governance.registerCoreRelayerContract(signed);
|
||||
}
|
||||
|
||||
function setUpWormhole(uint16 chainId)
|
||||
public
|
||||
returns (IWormhole wormholeContract, WormholeSimulator wormholeSimulator)
|
||||
{
|
||||
// deploy Wormhole
|
||||
MockWormhole wormhole = new MockWormhole({
|
||||
initChainId: chainId,
|
||||
initEvmChainId: block.chainid
|
||||
});
|
||||
|
||||
// replace Wormhole with the Wormhole Simulator contract (giving access to some nice helper methods for signing)
|
||||
wormholeSimulator = new FakeWormholeSimulator(
|
||||
wormhole
|
||||
);
|
||||
|
||||
wormholeContract = wormhole;
|
||||
}
|
||||
|
||||
function setUpRelayProvider(uint16 chainId) public returns (RelayProvider relayProvider) {
|
||||
vm.prank(msg.sender);
|
||||
RelayProviderSetup relayProviderSetup = new RelayProviderSetup();
|
||||
vm.prank(msg.sender);
|
||||
RelayProviderImplementation relayProviderImplementation = new RelayProviderImplementation();
|
||||
vm.prank(msg.sender);
|
||||
RelayProviderProxy myRelayProvider = new RelayProviderProxy(
|
||||
address(relayProviderSetup),
|
||||
abi.encodeCall(
|
||||
RelayProviderSetup.setup,
|
||||
(
|
||||
address(relayProviderImplementation),
|
||||
chainId
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
relayProvider = RelayProvider(address(myRelayProvider));
|
||||
}
|
||||
|
||||
function setUpCoreRelayer(uint16 chainId, IWormhole wormhole, address defaultRelayProvider)
|
||||
public
|
||||
returns (IWormholeRelayer coreRelayer)
|
||||
{
|
||||
CoreRelayerSetup coreRelayerSetup = new CoreRelayerSetup();
|
||||
CoreRelayerImplementation coreRelayerImplementation = new CoreRelayerImplementation();
|
||||
CoreRelayerProxy myCoreRelayer = new CoreRelayerProxy(
|
||||
address(coreRelayerSetup),
|
||||
abi.encodeCall(
|
||||
CoreRelayerSetup.setup,
|
||||
(
|
||||
address(coreRelayerImplementation),
|
||||
chainId,
|
||||
address(wormhole),
|
||||
defaultRelayProvider,
|
||||
wormhole.governanceChainId(),
|
||||
wormhole.governanceContract(),
|
||||
block.chainid
|
||||
)
|
||||
)
|
||||
);
|
||||
coreRelayer = IWormholeRelayer(address(myCoreRelayer));
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,217 @@
|
|||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {IRelayProvider} from "../contracts/interfaces/IRelayProvider.sol";
|
||||
import {RelayProvider} from "../contracts/relayProvider/RelayProvider.sol";
|
||||
import {RelayProviderSetup} from "../contracts/relayProvider/RelayProviderSetup.sol";
|
||||
import {RelayProviderImplementation} from "../contracts/relayProvider/RelayProviderImplementation.sol";
|
||||
import {RelayProviderProxy} from "../contracts/relayProvider/RelayProviderProxy.sol";
|
||||
import {RelayProviderMessages} from "../contracts/relayProvider/RelayProviderMessages.sol";
|
||||
import {RelayProviderStructs} from "../contracts/relayProvider/RelayProviderStructs.sol";
|
||||
import {IWormholeRelayer} from "../contracts/interfaces/IWormholeRelayer.sol";
|
||||
import {IDelivery} from "../contracts/interfaces/IDelivery.sol";
|
||||
import {CoreRelayer} from "../contracts/coreRelayer/CoreRelayer.sol";
|
||||
import {CoreRelayerStructs} from "../contracts/coreRelayer/CoreRelayerStructs.sol";
|
||||
import {CoreRelayerSetup} from "../contracts/coreRelayer/CoreRelayerSetup.sol";
|
||||
import {CoreRelayerImplementation} from "../contracts/coreRelayer/CoreRelayerImplementation.sol";
|
||||
import {CoreRelayerProxy} from "../contracts/coreRelayer/CoreRelayerProxy.sol";
|
||||
import {CoreRelayerMessages} from "../contracts/coreRelayer/CoreRelayerMessages.sol";
|
||||
import {CoreRelayerStructs} from "../contracts/coreRelayer/CoreRelayerStructs.sol";
|
||||
import {CoreRelayerGovernance} from "../contracts/coreRelayer/CoreRelayerGovernance.sol";
|
||||
import {MockGenericRelayer} from "./MockGenericRelayer.sol";
|
||||
import {MockWormhole} from "../contracts/mock/MockWormhole.sol";
|
||||
import {IWormhole} from "../contracts/interfaces/IWormhole.sol";
|
||||
import {WormholeSimulator, FakeWormholeSimulator} from "./WormholeSimulator.sol";
|
||||
import {IWormholeReceiver} from "../contracts/interfaces/IWormholeReceiver.sol";
|
||||
import {AttackForwardIntegration} from "../contracts/mock/AttackForwardIntegration.sol";
|
||||
import {MockRelayerIntegration, Structs} from "../contracts/mock/MockRelayerIntegration.sol";
|
||||
import {ForwardTester} from "./ForwardTester.sol";
|
||||
import {TestHelpers} from "./TestHelpers.sol";
|
||||
import "../contracts/libraries/external/BytesLib.sol";
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
import "forge-std/console.sol";
|
||||
import "forge-std/Vm.sol";
|
||||
|
||||
contract WormholeRelayerGovernanceTests is Test {
|
||||
using BytesLib for bytes;
|
||||
|
||||
TestHelpers helpers;
|
||||
|
||||
bytes32 relayerModule = 0x000000000000000000000000000000000000000000436F726552656C61796572;
|
||||
IWormhole wormhole;
|
||||
IRelayProvider relayProvider;
|
||||
WormholeSimulator wormholeSimulator;
|
||||
IWormholeRelayer wormholeRelayer;
|
||||
|
||||
function setUp() public {
|
||||
helpers = new TestHelpers();
|
||||
(wormhole, wormholeSimulator) = helpers.setUpWormhole(1);
|
||||
relayProvider = helpers.setUpRelayProvider(1);
|
||||
wormholeRelayer = helpers.setUpCoreRelayer(1, wormhole, address(relayProvider));
|
||||
}
|
||||
|
||||
struct GovernanceStack {
|
||||
bytes message;
|
||||
IWormhole.VM preSignedMessage;
|
||||
bytes signed;
|
||||
}
|
||||
|
||||
function signMessage(bytes memory message) internal returns (bytes memory signed) {
|
||||
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("")
|
||||
});
|
||||
signed = wormholeSimulator.encodeAndSignMessage(preSignedMessage);
|
||||
}
|
||||
|
||||
function fillInGovernanceStack(bytes memory message) internal returns (GovernanceStack memory stack) {
|
||||
stack.message = message;
|
||||
stack.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("")
|
||||
});
|
||||
stack.signed = wormholeSimulator.encodeAndSignMessage(stack.preSignedMessage);
|
||||
}
|
||||
|
||||
function testSetDefaultRelayProvider() public {
|
||||
IRelayProvider relayProviderB = helpers.setUpRelayProvider(1);
|
||||
IRelayProvider relayProviderC = helpers.setUpRelayProvider(1);
|
||||
|
||||
bytes memory signed = signMessage(
|
||||
abi.encodePacked(relayerModule, uint8(3), uint16(1), bytes32(uint256(uint160(address(relayProviderB)))))
|
||||
);
|
||||
|
||||
CoreRelayerGovernance(address(wormholeRelayer)).setDefaultRelayProvider(signed);
|
||||
|
||||
assertTrue(wormholeRelayer.getDefaultRelayProvider() == address(relayProviderB));
|
||||
|
||||
signed = signMessage(
|
||||
abi.encodePacked(relayerModule, uint8(3), uint16(1), bytes32(uint256(uint160(address(relayProviderC)))))
|
||||
);
|
||||
|
||||
CoreRelayerGovernance(address(wormholeRelayer)).setDefaultRelayProvider(signed);
|
||||
|
||||
assertTrue(wormholeRelayer.getDefaultRelayProvider() == address(relayProviderC));
|
||||
}
|
||||
|
||||
function testRegisterChain() public {
|
||||
IWormholeRelayer wormholeRelayer1 = helpers.setUpCoreRelayer(1, wormhole, address(relayProvider));
|
||||
IWormholeRelayer wormholeRelayer2 = helpers.setUpCoreRelayer(1, wormhole, address(relayProvider));
|
||||
IWormholeRelayer wormholeRelayer3 = helpers.setUpCoreRelayer(1, wormhole, address(relayProvider));
|
||||
|
||||
helpers.registerCoreRelayerContract(
|
||||
CoreRelayer(address(wormholeRelayer1)),
|
||||
wormhole,
|
||||
1,
|
||||
2,
|
||||
wormholeRelayer.toWormholeFormat(address(wormholeRelayer2))
|
||||
);
|
||||
|
||||
helpers.registerCoreRelayerContract(
|
||||
CoreRelayer(address(wormholeRelayer1)),
|
||||
wormhole,
|
||||
1,
|
||||
3,
|
||||
wormholeRelayer.toWormholeFormat(address(wormholeRelayer3))
|
||||
);
|
||||
|
||||
assertTrue(
|
||||
CoreRelayer(address(wormholeRelayer1)).registeredCoreRelayerContract(2)
|
||||
== wormholeRelayer1.toWormholeFormat(address(wormholeRelayer2))
|
||||
);
|
||||
|
||||
assertTrue(
|
||||
CoreRelayer(address(wormholeRelayer1)).registeredCoreRelayerContract(3)
|
||||
== wormholeRelayer1.toWormholeFormat(address(wormholeRelayer3))
|
||||
);
|
||||
|
||||
helpers.registerCoreRelayerContract(
|
||||
CoreRelayer(address(wormholeRelayer1)),
|
||||
wormhole,
|
||||
1,
|
||||
3,
|
||||
wormholeRelayer.toWormholeFormat(address(wormholeRelayer2))
|
||||
);
|
||||
|
||||
assertTrue(
|
||||
CoreRelayer(address(wormholeRelayer1)).registeredCoreRelayerContract(3)
|
||||
== wormholeRelayer1.toWormholeFormat(address(wormholeRelayer2))
|
||||
);
|
||||
}
|
||||
|
||||
function testUpgradeContractToItself() public {
|
||||
CoreRelayerSetup coreRelayerSetup = new CoreRelayerSetup();
|
||||
CoreRelayerImplementation coreRelayerImplementation = new CoreRelayerImplementation();
|
||||
CoreRelayerProxy myCoreRelayer = new CoreRelayerProxy(
|
||||
address(coreRelayerSetup),
|
||||
abi.encodeCall(
|
||||
CoreRelayerSetup.setup,
|
||||
(
|
||||
address(coreRelayerImplementation),
|
||||
1,
|
||||
address(wormhole),
|
||||
address(relayProvider),
|
||||
wormhole.governanceChainId(),
|
||||
wormhole.governanceContract(),
|
||||
block.chainid
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
for (uint256 i = 0; i < 10; i++) {
|
||||
CoreRelayerImplementation coreRelayerImplementationNew = new CoreRelayerImplementation();
|
||||
|
||||
bytes memory message = abi.encodePacked(
|
||||
relayerModule,
|
||||
uint8(1),
|
||||
uint16(1),
|
||||
wormholeRelayer.toWormholeFormat(address(coreRelayerImplementationNew))
|
||||
);
|
||||
|
||||
bytes memory signed = signMessage(message);
|
||||
|
||||
CoreRelayerGovernance(address(myCoreRelayer)).submitContractUpgrade(signed);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
function testRevertUpgradeFork() {
|
||||
CoreRelayerSetup coreRelayerSetup = new CoreRelayerSetup();
|
||||
CoreRelayerImplementation coreRelayerImplementation = new CoreRelayerImplementation();
|
||||
CoreRelayerProxy myCoreRelayer = new CoreRelayerProxy(
|
||||
address(coreRelayerSetup),
|
||||
abi.encodeCall(
|
||||
CoreRelayerSetup.setup,
|
||||
(
|
||||
address(coreRelayerImplementation),
|
||||
1,
|
||||
address(wormhole),
|
||||
address(relayProvider),
|
||||
wormhole.governanceChainId(),
|
||||
wormhole.governanceContract(),
|
||||
block.chainid
|
||||
)
|
||||
)
|
||||
);
|
||||
}*/
|
||||
}
|
Loading…
Reference in New Issue