2283 lines
87 KiB
Solidity
2283 lines
87 KiB
Solidity
// SPDX-License-Identifier: Apache 2
|
|
|
|
pragma solidity ^0.8.0;
|
|
|
|
import {IDeliveryProvider} from "../../contracts/interfaces/relayer/IDeliveryProvider.sol";
|
|
import {DeliveryProvider} from "../../contracts/relayer/deliveryProvider/DeliveryProvider.sol";
|
|
import {DeliveryProviderSetup} from
|
|
"../../contracts/relayer/deliveryProvider/DeliveryProviderSetup.sol";
|
|
import {DeliveryProviderImplementation} from
|
|
"../../contracts/relayer/deliveryProvider/DeliveryProviderImplementation.sol";
|
|
import {DeliveryProviderProxy} from
|
|
"../../contracts/relayer/deliveryProvider/DeliveryProviderProxy.sol";
|
|
import {DeliveryProviderStructs} from
|
|
"../../contracts/relayer/deliveryProvider/DeliveryProviderStructs.sol";
|
|
import "../../contracts/interfaces/relayer/IWormholeRelayerTyped.sol";
|
|
import {
|
|
DeliveryInstruction,
|
|
RedeliveryInstruction,
|
|
DeliveryOverride,
|
|
EvmDeliveryInstruction
|
|
} from "../../contracts/relayer/libraries/RelayerInternalStructs.sol";
|
|
import {WormholeRelayer} from "../../contracts/relayer/wormholeRelayer/WormholeRelayer.sol";
|
|
import {MockGenericRelayer} from "./MockGenericRelayer.sol";
|
|
import {MockWormhole} from "./MockWormhole.sol";
|
|
import {IWormhole} from "../../contracts/interfaces/IWormhole.sol";
|
|
import {WormholeSimulator, FakeWormholeSimulator} from "./WormholeSimulator.sol";
|
|
import {IWormholeReceiver} from "../../contracts/interfaces/relayer/IWormholeReceiver.sol";
|
|
import {
|
|
MockRelayerIntegration,
|
|
XAddress,
|
|
DeliveryData
|
|
} from "../../contracts/mock/relayer/MockRelayerIntegration.sol";
|
|
import {BigRevertBufferIntegration} from "./BigRevertBufferIntegration.sol";
|
|
import {TestHelpers} from "./TestHelpers.sol";
|
|
import {WormholeRelayerSerde} from
|
|
"../../contracts/relayer/wormholeRelayer/WormholeRelayerSerde.sol";
|
|
import {
|
|
EvmExecutionInfoV1,
|
|
ExecutionInfoVersion,
|
|
decodeEvmExecutionInfoV1,
|
|
encodeEvmExecutionInfoV1
|
|
} from "../../contracts/relayer/libraries/ExecutionParameters.sol";
|
|
import {toWormholeFormat, fromWormholeFormat} from "../../contracts/relayer/libraries/Utils.sol";
|
|
import {BytesParsing} from "../../contracts/relayer/libraries/BytesParsing.sol";
|
|
import "../../contracts/interfaces/relayer/TypedUnits.sol";
|
|
|
|
import "forge-std/Test.sol";
|
|
import "forge-std/console.sol";
|
|
import "forge-std/Vm.sol";
|
|
|
|
contract WormholeRelayerTests is Test {
|
|
using BytesParsing for bytes;
|
|
using WeiLib for Wei;
|
|
using GasLib for Gas;
|
|
using WeiPriceLib for WeiPrice;
|
|
using GasPriceLib for GasPrice;
|
|
using TargetNativeLib for TargetNative;
|
|
using LocalNativeLib for LocalNative;
|
|
|
|
Gas REASONABLE_GAS_LIMIT = Gas.wrap(500000);
|
|
Gas REASONABLE_GAS_LIMIT_FORWARDS = Gas.wrap(1000000);
|
|
Gas TOO_LOW_GAS_LIMIT = Gas.wrap(10000);
|
|
|
|
struct GasParameters {
|
|
uint32 evmGasOverhead;
|
|
uint32 targetGasLimit;
|
|
uint56 targetGasPrice;
|
|
uint56 sourceGasPrice;
|
|
}
|
|
|
|
struct FeeParameters {
|
|
uint56 targetNativePrice;
|
|
uint56 sourceNativePrice;
|
|
uint32 wormholeFeeOnSource;
|
|
uint32 wormholeFeeOnTarget;
|
|
uint64 receiverValueTarget;
|
|
}
|
|
|
|
struct GasParametersTyped {
|
|
Gas evmGasOverhead;
|
|
Gas targetGasLimit;
|
|
GasPrice targetGasPrice;
|
|
GasPrice sourceGasPrice;
|
|
}
|
|
|
|
struct FeeParametersTyped {
|
|
WeiPrice targetNativePrice;
|
|
WeiPrice sourceNativePrice;
|
|
Wei wormholeFeeOnSource;
|
|
Wei wormholeFeeOnTarget;
|
|
Wei receiverValueTarget;
|
|
}
|
|
|
|
function toGasParametersTyped(GasParameters memory gasParams)
|
|
internal
|
|
pure
|
|
returns (GasParametersTyped memory)
|
|
{
|
|
return GasParametersTyped({
|
|
evmGasOverhead: Gas.wrap(gasParams.evmGasOverhead),
|
|
targetGasLimit: Gas.wrap(gasParams.targetGasLimit),
|
|
targetGasPrice: GasPrice.wrap(gasParams.targetGasPrice),
|
|
sourceGasPrice: GasPrice.wrap(gasParams.sourceGasPrice)
|
|
});
|
|
}
|
|
|
|
function toFeeParametersTyped(FeeParameters memory feeParams)
|
|
internal
|
|
pure
|
|
returns (FeeParametersTyped memory)
|
|
{
|
|
return FeeParametersTyped({
|
|
targetNativePrice: WeiPrice.wrap(feeParams.targetNativePrice),
|
|
sourceNativePrice: WeiPrice.wrap(feeParams.sourceNativePrice),
|
|
wormholeFeeOnSource: Wei.wrap(feeParams.wormholeFeeOnSource),
|
|
wormholeFeeOnTarget: Wei.wrap(feeParams.wormholeFeeOnTarget),
|
|
receiverValueTarget: Wei.wrap(feeParams.receiverValueTarget)
|
|
});
|
|
}
|
|
|
|
IWormhole relayerWormhole;
|
|
WormholeSimulator relayerWormholeSimulator;
|
|
MockGenericRelayer genericRelayer;
|
|
TestHelpers helpers;
|
|
|
|
/**
|
|
*
|
|
* SETUP
|
|
*
|
|
*/
|
|
|
|
function setUp() public {
|
|
// deploy Wormhole
|
|
MockWormhole wormhole = new MockWormhole({
|
|
initChainId: 2,
|
|
initEvmChainId: block.chainid
|
|
});
|
|
|
|
relayerWormhole = wormhole;
|
|
relayerWormholeSimulator = new FakeWormholeSimulator(
|
|
wormhole
|
|
);
|
|
|
|
helpers = new TestHelpers();
|
|
|
|
genericRelayer =
|
|
new MockGenericRelayer(address(wormhole), address(relayerWormholeSimulator));
|
|
|
|
setUpChains(5);
|
|
}
|
|
|
|
struct StandardSetupTwoChains {
|
|
uint16 sourceChain;
|
|
uint16 targetChain;
|
|
uint16 differentChainId;
|
|
Contracts source;
|
|
Contracts target;
|
|
}
|
|
|
|
function standardAssume(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
uint32 minTargetGasLimit
|
|
) public pure {
|
|
vm.assume(gasParams.evmGasOverhead > 0);
|
|
vm.assume(gasParams.targetGasLimit > 0);
|
|
vm.assume(feeParams.targetNativePrice > 0);
|
|
vm.assume(gasParams.targetGasPrice > 0);
|
|
vm.assume(gasParams.sourceGasPrice > 0);
|
|
vm.assume(feeParams.sourceNativePrice > 0);
|
|
|
|
vm.assume(
|
|
(
|
|
uint256(gasParams.sourceGasPrice) * feeParams.sourceNativePrice
|
|
+ feeParams.targetNativePrice - 1
|
|
) / uint256(feeParams.targetNativePrice) < type(uint88).max
|
|
);
|
|
vm.assume(
|
|
(
|
|
uint256(gasParams.targetGasPrice) * feeParams.targetNativePrice
|
|
+ feeParams.sourceNativePrice - 1
|
|
) / uint256(feeParams.sourceNativePrice) < type(uint88).max
|
|
);
|
|
|
|
vm.assume(
|
|
feeParams.targetNativePrice
|
|
< (uint256(2) ** 126)
|
|
/ (
|
|
uint256(1) * gasParams.targetGasPrice
|
|
* (uint256(0) + gasParams.targetGasLimit + gasParams.evmGasOverhead)
|
|
+ feeParams.wormholeFeeOnTarget
|
|
)
|
|
);
|
|
vm.assume(
|
|
feeParams.sourceNativePrice
|
|
< (uint256(2) ** 126)
|
|
/ (
|
|
uint256(1) * gasParams.sourceGasPrice
|
|
* (uint256(gasParams.targetGasLimit) + gasParams.evmGasOverhead)
|
|
+ feeParams.wormholeFeeOnSource
|
|
)
|
|
);
|
|
|
|
vm.assume(gasParams.targetGasLimit >= minTargetGasLimit);
|
|
}
|
|
|
|
function standardAssumeAndSetupTwoChains(
|
|
GasParameters memory gasParams_,
|
|
FeeParameters memory feeParams_,
|
|
uint32 minTargetGasLimit
|
|
) public returns (StandardSetupTwoChains memory s) {
|
|
return standardAssumeAndSetupTwoChains(gasParams_, feeParams_, Gas.wrap(minTargetGasLimit));
|
|
}
|
|
|
|
function standardAssumeAndSetupTwoChains(
|
|
GasParameters memory gasParams_,
|
|
FeeParameters memory feeParams_,
|
|
Gas minTargetGasLimit
|
|
) public returns (StandardSetupTwoChains memory s) {
|
|
standardAssume(gasParams_, feeParams_, uint32(minTargetGasLimit.unwrap()));
|
|
GasParametersTyped memory gasParams = toGasParametersTyped(gasParams_);
|
|
FeeParametersTyped memory feeParams = toFeeParametersTyped(feeParams_);
|
|
|
|
s.sourceChain = 1;
|
|
s.targetChain = 2;
|
|
s.differentChainId = 3;
|
|
s.source = map[s.sourceChain];
|
|
s.target = map[s.targetChain];
|
|
|
|
vm.deal(s.source.relayer, type(uint256).max / 2);
|
|
vm.deal(s.target.relayer, type(uint256).max / 2);
|
|
vm.deal(address(this), type(uint256).max / 2);
|
|
// vm.deal(address(s.target.integration), type(uint256).max / 2);
|
|
// vm.deal(address(s.source.integration), type(uint256).max / 2);
|
|
|
|
// set deliveryProvider prices
|
|
s.source.deliveryProvider.updatePrice(
|
|
s.targetChain, gasParams.targetGasPrice, feeParams.targetNativePrice
|
|
);
|
|
s.source.deliveryProvider.updatePrice(
|
|
s.sourceChain, gasParams.sourceGasPrice, feeParams.sourceNativePrice
|
|
);
|
|
s.target.deliveryProvider.updatePrice(
|
|
s.targetChain, gasParams.targetGasPrice, feeParams.targetNativePrice
|
|
);
|
|
s.target.deliveryProvider.updatePrice(
|
|
s.sourceChain, gasParams.sourceGasPrice, feeParams.sourceNativePrice
|
|
);
|
|
|
|
s.source.deliveryProvider.updateDeliverGasOverhead(s.targetChain, gasParams.evmGasOverhead);
|
|
s.target.deliveryProvider.updateDeliverGasOverhead(s.sourceChain, gasParams.evmGasOverhead);
|
|
|
|
s.source.wormholeSimulator.setMessageFee(feeParams.wormholeFeeOnSource.unwrap());
|
|
s.target.wormholeSimulator.setMessageFee(feeParams.wormholeFeeOnTarget.unwrap());
|
|
}
|
|
|
|
struct Contracts {
|
|
IWormhole wormhole;
|
|
WormholeSimulator wormholeSimulator;
|
|
DeliveryProvider deliveryProvider;
|
|
IWormholeRelayer coreRelayer;
|
|
WormholeRelayer coreRelayerFull;
|
|
MockRelayerIntegration integration;
|
|
address relayer;
|
|
address payable rewardAddress;
|
|
address payable refundAddress;
|
|
uint16 chainId;
|
|
}
|
|
|
|
mapping(uint16 => Contracts) map;
|
|
|
|
function setUpChains(uint16 numChains) internal {
|
|
for (uint16 i = 1; i <= numChains; i++) {
|
|
Contracts memory mapEntry;
|
|
(mapEntry.wormhole, mapEntry.wormholeSimulator) = helpers.setUpWormhole(i);
|
|
mapEntry.deliveryProvider = helpers.setUpDeliveryProvider(i);
|
|
mapEntry.deliveryProvider.updateSupportedMessageKeyTypes(VAA_KEY_TYPE, true);
|
|
mapEntry.coreRelayer =
|
|
helpers.setUpWormholeRelayer(mapEntry.wormhole, address(mapEntry.deliveryProvider));
|
|
mapEntry.coreRelayerFull = WormholeRelayer(payable(address(mapEntry.coreRelayer)));
|
|
genericRelayer.setWormholeRelayerContract(i, address(mapEntry.coreRelayer));
|
|
mapEntry.integration =
|
|
new MockRelayerIntegration(address(mapEntry.wormhole), address(mapEntry.coreRelayer));
|
|
mapEntry.relayer =
|
|
address(uint160(uint256(keccak256(abi.encodePacked(bytes("relayer"), i)))));
|
|
genericRelayer.setProviderDeliveryAddress(i, mapEntry.relayer);
|
|
mapEntry.refundAddress = payable(
|
|
address(uint160(uint256(keccak256(abi.encodePacked(bytes("refundAddress"), i)))))
|
|
);
|
|
mapEntry.rewardAddress = payable(
|
|
address(uint160(uint256(keccak256(abi.encodePacked(bytes("rewardAddress"), i)))))
|
|
);
|
|
mapEntry.chainId = i;
|
|
map[i] = mapEntry;
|
|
}
|
|
|
|
uint192 maxBudget = uint192(2 ** 192 - 1);
|
|
for (uint16 i = 1; i <= numChains; i++) {
|
|
for (uint16 j = 1; j <= numChains; j++) {
|
|
map[i].deliveryProvider.updateSupportedChain(j, true);
|
|
map[i].deliveryProvider.updateAssetConversionBuffer(j, 500, 10000);
|
|
map[i].deliveryProvider.updateTargetChainAddress(
|
|
j, bytes32(uint256(uint160(address(map[j].deliveryProvider))))
|
|
);
|
|
map[i].deliveryProvider.updateRewardAddress(map[i].rewardAddress);
|
|
helpers.registerWormholeRelayerContract(
|
|
map[i].coreRelayerFull,
|
|
map[i].wormhole,
|
|
i,
|
|
j,
|
|
bytes32(uint256(uint160(address(map[j].coreRelayer))))
|
|
);
|
|
map[i].deliveryProvider.updateMaximumBudget(j, Wei.wrap(maxBudget));
|
|
map[i].integration.registerEmitter(
|
|
j, bytes32(uint256(uint160(address(map[j].integration))))
|
|
);
|
|
XAddress[] memory addresses = new XAddress[](1);
|
|
addresses[0] = XAddress(j, bytes32(uint256(uint160(address(map[j].integration)))));
|
|
map[i].integration.registerEmitters(addresses);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getDeliveryVAAHash(Vm.Log[] memory logs) internal pure returns (bytes32 vaaHash) {
|
|
(vaaHash,) = logs[0].data.asBytes32(0);
|
|
}
|
|
|
|
function getDeliveryStatus(Vm.Log memory log)
|
|
internal
|
|
view
|
|
returns (IWormholeRelayerDelivery.DeliveryStatus status)
|
|
{
|
|
(uint256 parsed,) = log.data.asUint256(32);
|
|
console.log(parsed);
|
|
status = IWormholeRelayerDelivery.DeliveryStatus(parsed);
|
|
}
|
|
|
|
function getDeliveryStatus()
|
|
internal
|
|
returns (IWormholeRelayerDelivery.DeliveryStatus status)
|
|
{
|
|
Vm.Log[] memory logs = vm.getRecordedLogs();
|
|
status = getDeliveryStatus(logs[logs.length - 1]);
|
|
}
|
|
|
|
function getRefundStatus(Vm.Log memory log)
|
|
internal
|
|
pure
|
|
returns (IWormholeRelayerDelivery.RefundStatus status)
|
|
{
|
|
(uint256 parsed,) = log.data.asUint256(32 + 32 + 32);
|
|
status = IWormholeRelayerDelivery.RefundStatus(parsed);
|
|
}
|
|
|
|
function getRefundStatus() internal returns (IWormholeRelayerDelivery.RefundStatus status) {
|
|
Vm.Log[] memory logs = vm.getRecordedLogs();
|
|
status = getRefundStatus(logs[logs.length - 1]);
|
|
}
|
|
|
|
function vaaKeyArray(
|
|
uint16 chainId,
|
|
uint64 sequence,
|
|
address emitterAddress
|
|
) internal pure returns (VaaKey[] memory vaaKeys) {
|
|
vaaKeys = new VaaKey[](1);
|
|
vaaKeys[0] = VaaKey(chainId, toWormholeFormat(emitterAddress), sequence);
|
|
}
|
|
|
|
function sendMessageToTargetChain(
|
|
StandardSetupTwoChains memory setup,
|
|
Gas gasLimit,
|
|
uint128 receiverValue,
|
|
bytes memory message
|
|
) internal returns (uint64 sequence) {
|
|
return sendMessageToTargetChain(setup, uint32(gasLimit.unwrap()), receiverValue, message);
|
|
}
|
|
|
|
function sendMessageToTargetChain(
|
|
StandardSetupTwoChains memory setup,
|
|
uint32 gasLimit,
|
|
uint128 receiverValue,
|
|
bytes memory message
|
|
) internal returns (uint64 sequence) {
|
|
(LocalNative deliveryCost,) = setup.source.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.targetChain, TargetNative.wrap(receiverValue), Gas.wrap(gasLimit)
|
|
);
|
|
sequence = setup.source.integration.sendMessage{value: LocalNative.unwrap(deliveryCost)}(
|
|
message, setup.targetChain, gasLimit, receiverValue
|
|
);
|
|
}
|
|
|
|
function sendMessageToTargetChainExpectingForwardedResponse(
|
|
StandardSetupTwoChains memory setup,
|
|
uint32 gasLimit,
|
|
uint128 receiverValue,
|
|
bytes memory message,
|
|
bytes memory forwardedMessage,
|
|
bool forwardShouldSucceed
|
|
) internal returns (uint64 sequence) {
|
|
LocalNative forwardDeliveryCost;
|
|
if (forwardShouldSucceed) {
|
|
(forwardDeliveryCost,) = setup.target.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.sourceChain, TargetNative.wrap(0), REASONABLE_GAS_LIMIT
|
|
);
|
|
} else {
|
|
(forwardDeliveryCost,) = setup.target.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.sourceChain, TargetNative.wrap(0), Gas.wrap(10_000)
|
|
);
|
|
}
|
|
|
|
uint256 neededReceiverValue = forwardDeliveryCost.unwrap();
|
|
vm.assume(neededReceiverValue <= type(uint128).max);
|
|
if (forwardShouldSucceed) {
|
|
vm.assume(receiverValue >= neededReceiverValue);
|
|
} else {
|
|
vm.assume(receiverValue < neededReceiverValue);
|
|
}
|
|
|
|
(LocalNative deliveryCost,) = setup.source.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.targetChain, TargetNative.wrap(receiverValue), Gas.wrap(gasLimit)
|
|
);
|
|
|
|
sequence = setup.source.integration.sendMessageWithForwardedResponse{
|
|
value: deliveryCost.unwrap()
|
|
}(message, forwardedMessage, setup.targetChain, gasLimit, receiverValue);
|
|
}
|
|
|
|
function resendMessageToTargetChain(
|
|
StandardSetupTwoChains memory setup,
|
|
uint64 sequence,
|
|
uint32 gasLimit,
|
|
uint128 receiverValue,
|
|
bytes memory
|
|
) internal {
|
|
(LocalNative newDeliveryCost,) = setup.source.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.targetChain, TargetNative.wrap(receiverValue), Gas.wrap(gasLimit)
|
|
);
|
|
|
|
setup.source.integration.resend{value: newDeliveryCost.unwrap()}(
|
|
setup.sourceChain, sequence, setup.targetChain, gasLimit, receiverValue
|
|
);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* TEST SUITE!
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Basic Functionality Tests: Send, Forward, and Resend
|
|
*/
|
|
|
|
function testSend(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, REASONABLE_GAS_LIMIT);
|
|
|
|
vm.recordLogs();
|
|
|
|
sendMessageToTargetChain(setup, gasParams.targetGasLimit, 0, message);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) == keccak256(message));
|
|
assertTrue(getDeliveryStatus() == IWormholeRelayerDelivery.DeliveryStatus.SUCCESS);
|
|
}
|
|
|
|
function testSendWithResponse(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message,
|
|
bytes memory forwardedMessage
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, REASONABLE_GAS_LIMIT_FORWARDS);
|
|
|
|
vm.recordLogs();
|
|
|
|
sendMessageToTargetChainExpectingForwardedResponse(
|
|
setup,
|
|
gasParams.targetGasLimit,
|
|
feeParams.receiverValueTarget,
|
|
message,
|
|
forwardedMessage,
|
|
true
|
|
);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) == keccak256(message));
|
|
|
|
genericRelayer.relay(setup.targetChain);
|
|
|
|
assertTrue(keccak256(setup.source.integration.getMessage()) == keccak256(forwardedMessage));
|
|
}
|
|
|
|
function testResend(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, REASONABLE_GAS_LIMIT);
|
|
|
|
vm.recordLogs();
|
|
|
|
vm.assume(keccak256(message) != keccak256(bytes("")));
|
|
|
|
uint64 sequence = sendMessageToTargetChain(setup, TOO_LOW_GAS_LIMIT, 0, message);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) != keccak256(message));
|
|
assertTrue(getDeliveryStatus() == IWormholeRelayerDelivery.DeliveryStatus.RECEIVER_FAILURE);
|
|
|
|
resendMessageToTargetChain(
|
|
setup, sequence, uint32(REASONABLE_GAS_LIMIT.unwrap()), 0, message
|
|
);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) == keccak256(message));
|
|
assertTrue(getDeliveryStatus() == IWormholeRelayerDelivery.DeliveryStatus.SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* More functionality tests
|
|
*/
|
|
|
|
function testRevertUnsupportedMessageKeyType(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams
|
|
) public {
|
|
StandardSetupTwoChains memory setup = standardAssumeAndSetupTwoChains(
|
|
gasParams, feeParams, Gas.wrap(gasParams.targetGasLimit)
|
|
);
|
|
|
|
MessageKey[] memory messageKeys = new MessageKey[](2);
|
|
messageKeys[0] = MessageKey(VAA_KEY_TYPE, bytes(""));
|
|
messageKeys[1] = MessageKey(RANDOM_KEY_TYPE, RANDOM_KEY_TYPE_BODY);
|
|
|
|
(LocalNative cost,) = setup.source.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.targetChain, TargetNative.wrap(0), Gas.wrap(0)
|
|
);
|
|
|
|
vm.expectRevert(
|
|
abi.encodeWithSelector(
|
|
DeliveryProviderDoesNotSupportMessageKeyType.selector, RANDOM_KEY_TYPE
|
|
)
|
|
);
|
|
setup.source.coreRelayer.sendToEvm{value: cost.unwrap()}(
|
|
setup.targetChain,
|
|
address(0x0),
|
|
bytes(""),
|
|
TargetNative.wrap(0),
|
|
LocalNative.wrap(0),
|
|
Gas.wrap(0),
|
|
setup.sourceChain,
|
|
address(0x0),
|
|
address(setup.source.deliveryProvider),
|
|
messageKeys,
|
|
15
|
|
);
|
|
}
|
|
|
|
function testForwardFailure(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams
|
|
) public {
|
|
feeParams.receiverValueTarget = 0;
|
|
vm.assume(
|
|
uint256(20) * feeParams.targetNativePrice * gasParams.targetGasPrice
|
|
< uint256(1) * feeParams.sourceNativePrice * gasParams.sourceGasPrice
|
|
);
|
|
|
|
vm.recordLogs();
|
|
gasParams.targetGasLimit = 600000;
|
|
StandardSetupTwoChains memory setup = standardAssumeAndSetupTwoChains(
|
|
gasParams, feeParams, Gas.wrap(gasParams.targetGasLimit)
|
|
);
|
|
|
|
sendMessageToTargetChainExpectingForwardedResponse(
|
|
setup,
|
|
gasParams.targetGasLimit,
|
|
feeParams.receiverValueTarget,
|
|
bytes("Hello!"),
|
|
bytes("Forwarded Message!"),
|
|
false
|
|
);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) != keccak256(bytes("Hello!")));
|
|
assertTrue(getDeliveryStatus() == IWormholeRelayerDelivery.DeliveryStatus.RECEIVER_FAILURE);
|
|
console.log(uint256(IWormholeRelayerDelivery.DeliveryStatus.RECEIVER_FAILURE));
|
|
}
|
|
|
|
function testMultipleForwards(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message,
|
|
bytes memory forwardedMessage
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, REASONABLE_GAS_LIMIT);
|
|
|
|
(LocalNative firstForwardDeliveryCost,) = setup.target.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.sourceChain, TargetNative.wrap(0), REASONABLE_GAS_LIMIT
|
|
);
|
|
(LocalNative secondForwardDeliveryCost,) = setup.target.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.targetChain, TargetNative.wrap(0), REASONABLE_GAS_LIMIT
|
|
);
|
|
|
|
uint256 receiverValue =
|
|
firstForwardDeliveryCost.unwrap() + secondForwardDeliveryCost.unwrap();
|
|
vm.assume(receiverValue <= type(uint128).max);
|
|
|
|
(LocalNative deliveryCost,) = setup.source.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.targetChain,
|
|
TargetNative.wrap(receiverValue),
|
|
Gas.wrap(REASONABLE_GAS_LIMIT_FORWARDS.unwrap() * 2)
|
|
);
|
|
|
|
vm.recordLogs();
|
|
|
|
setup.source.integration.sendMessageWithMultiForwardedResponse{value: deliveryCost.unwrap()}(
|
|
message,
|
|
forwardedMessage,
|
|
setup.targetChain,
|
|
uint32(REASONABLE_GAS_LIMIT_FORWARDS.unwrap() * 2),
|
|
uint128(receiverValue)
|
|
);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) == keccak256(message));
|
|
|
|
genericRelayer.relay(setup.targetChain);
|
|
|
|
assertTrue(keccak256(setup.source.integration.getMessage()) == keccak256(forwardedMessage));
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) == keccak256(forwardedMessage));
|
|
}
|
|
|
|
function testResendFailAndSucceed(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, REASONABLE_GAS_LIMIT);
|
|
|
|
vm.recordLogs();
|
|
|
|
vm.assume(keccak256(message) != keccak256(bytes("")));
|
|
|
|
uint64 sequence = sendMessageToTargetChain(setup, TOO_LOW_GAS_LIMIT, 0, message);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) != keccak256(message));
|
|
assertTrue(getDeliveryStatus() == IWormholeRelayerDelivery.DeliveryStatus.RECEIVER_FAILURE);
|
|
|
|
for (uint32 i = 2; i < 10; i++) {
|
|
resendMessageToTargetChain(
|
|
setup, sequence, uint32(TOO_LOW_GAS_LIMIT.unwrap() * i), 0, message
|
|
);
|
|
genericRelayer.relay(setup.sourceChain);
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) != keccak256(message));
|
|
assertTrue(
|
|
getDeliveryStatus() == IWormholeRelayerDelivery.DeliveryStatus.RECEIVER_FAILURE
|
|
);
|
|
}
|
|
|
|
resendMessageToTargetChain(
|
|
setup, sequence, uint32(REASONABLE_GAS_LIMIT.unwrap()), 0, message
|
|
);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) == keccak256(message));
|
|
assertTrue(getDeliveryStatus() == IWormholeRelayerDelivery.DeliveryStatus.SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* Funds correct test (testing each address receives the correct payment)
|
|
*/
|
|
|
|
struct FundsCorrectTest {
|
|
uint256 refundAddressBalance;
|
|
uint256 relayerBalance;
|
|
uint256 rewardAddressBalance;
|
|
uint256 destinationBalance;
|
|
uint256 sourceContractBalance;
|
|
uint256 targetContractBalance;
|
|
uint128 receiverValue;
|
|
uint256 deliveryPrice;
|
|
uint256 targetChainRefundPerGasUnused;
|
|
uint32 gasAmount;
|
|
uint256 refundAddressAmount;
|
|
uint256 relayerPayment;
|
|
uint256 rewardAddressAmount;
|
|
uint256 destinationAmount;
|
|
}
|
|
|
|
function setupFundsCorrectTest(
|
|
GasParameters memory gasParams_,
|
|
FeeParameters memory feeParams_,
|
|
Gas minGasLimit
|
|
) public returns (StandardSetupTwoChains memory s, FundsCorrectTest memory test) {
|
|
return setupFundsCorrectTest(gasParams_, feeParams_, uint32(minGasLimit.unwrap()));
|
|
}
|
|
|
|
function setupFundsCorrectTest(
|
|
GasParameters memory gasParams_,
|
|
FeeParameters memory feeParams_,
|
|
uint32 minGasLimit
|
|
) public returns (StandardSetupTwoChains memory s, FundsCorrectTest memory test) {
|
|
s = standardAssumeAndSetupTwoChains(gasParams_, feeParams_, Gas.wrap(minGasLimit));
|
|
|
|
test.refundAddressBalance = s.target.refundAddress.balance;
|
|
test.relayerBalance = s.target.relayer.balance;
|
|
test.rewardAddressBalance = s.source.rewardAddress.balance;
|
|
test.destinationBalance = address(s.target.integration).balance;
|
|
test.sourceContractBalance = address(s.source.coreRelayer).balance;
|
|
test.targetContractBalance = address(s.target.coreRelayer).balance;
|
|
test.receiverValue = feeParams_.receiverValueTarget;
|
|
(LocalNative deliveryPrice, GasPrice targetChainRefundPerGasUnused) = s
|
|
.source
|
|
.coreRelayer
|
|
.quoteEVMDeliveryPrice(
|
|
s.targetChain,
|
|
TargetNative.wrap(test.receiverValue),
|
|
Gas.wrap(gasParams_.targetGasLimit)
|
|
);
|
|
test.deliveryPrice = deliveryPrice.unwrap();
|
|
test.targetChainRefundPerGasUnused = targetChainRefundPerGasUnused.unwrap();
|
|
vm.assume(test.targetChainRefundPerGasUnused > 0);
|
|
}
|
|
|
|
function testFundsCorrectForASend(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams
|
|
) public {
|
|
vm.recordLogs();
|
|
(StandardSetupTwoChains memory setup, FundsCorrectTest memory test) =
|
|
setupFundsCorrectTest(gasParams, feeParams, 170000);
|
|
|
|
setup.source.integration.sendMessageWithRefund{value: test.deliveryPrice}(
|
|
bytes("Hello!"),
|
|
setup.targetChain,
|
|
gasParams.targetGasLimit,
|
|
test.receiverValue,
|
|
setup.targetChain,
|
|
setup.target.refundAddress
|
|
);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) == keccak256("Hello!"));
|
|
assertTrue(getDeliveryStatus() == IWormholeRelayerDelivery.DeliveryStatus.SUCCESS);
|
|
|
|
test.refundAddressAmount = setup.target.refundAddress.balance - test.refundAddressBalance;
|
|
test.rewardAddressAmount = setup.source.rewardAddress.balance - test.rewardAddressBalance;
|
|
test.relayerPayment = test.relayerBalance - setup.target.relayer.balance;
|
|
test.destinationAmount = address(setup.target.integration).balance - test.destinationBalance;
|
|
|
|
assertTrue(test.sourceContractBalance == address(setup.source.coreRelayer).balance);
|
|
assertTrue(test.targetContractBalance == address(setup.target.coreRelayer).balance);
|
|
assertTrue(
|
|
test.destinationAmount == test.receiverValue, "Receiver value was sent to the contract"
|
|
);
|
|
assertTrue(
|
|
test.rewardAddressAmount + feeParams.wormholeFeeOnSource == test.deliveryPrice,
|
|
"Reward address was paid correctly"
|
|
);
|
|
|
|
test.gasAmount = uint32(
|
|
gasParams.targetGasLimit - test.refundAddressAmount / test.targetChainRefundPerGasUnused
|
|
);
|
|
console.log(test.gasAmount);
|
|
assertTrue(
|
|
test.gasAmount >= 140000,
|
|
"Gas amount (calculated from refund address payment) lower than expected. NOTE: This assert is purely to ensure the gas usage is consistent, and thus (since this was computed using the refund amount) the refund amount is correct."
|
|
);
|
|
assertTrue(
|
|
test.gasAmount <= 160000,
|
|
"Gas amount (calculated from refund address payment) higher than expected. NOTE: This assert is purely to ensure the gas usage is consistent, and thus (since this was computed using the refund amount) the refund amount is correct."
|
|
);
|
|
assertTrue(
|
|
test.relayerPayment == test.destinationAmount + test.refundAddressAmount,
|
|
"Relayer paid the correct amount"
|
|
);
|
|
}
|
|
|
|
function testFundsCorrectForASendFailureDueToGasExceeded(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams
|
|
) public {
|
|
vm.recordLogs();
|
|
gasParams.targetGasLimit = uint32(TOO_LOW_GAS_LIMIT.unwrap());
|
|
(StandardSetupTwoChains memory setup, FundsCorrectTest memory test) =
|
|
setupFundsCorrectTest(gasParams, feeParams, 0);
|
|
|
|
setup.source.integration.sendMessageWithRefund{value: test.deliveryPrice}(
|
|
bytes("Hello!"),
|
|
setup.targetChain,
|
|
gasParams.targetGasLimit,
|
|
test.receiverValue,
|
|
setup.targetChain,
|
|
setup.target.refundAddress
|
|
);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) != keccak256(bytes("Hello!")));
|
|
assertTrue(getDeliveryStatus() == IWormholeRelayerDelivery.DeliveryStatus.RECEIVER_FAILURE);
|
|
|
|
test.refundAddressAmount = setup.target.refundAddress.balance - test.refundAddressBalance;
|
|
test.rewardAddressAmount = setup.source.rewardAddress.balance - test.rewardAddressBalance;
|
|
test.relayerPayment = test.relayerBalance - setup.target.relayer.balance;
|
|
test.destinationAmount = address(setup.target.integration).balance - test.destinationBalance;
|
|
|
|
assertTrue(test.sourceContractBalance == address(setup.source.coreRelayer).balance);
|
|
assertTrue(test.targetContractBalance == address(setup.target.coreRelayer).balance);
|
|
assertTrue(test.destinationAmount == 0, "No receiver value was sent to the contract");
|
|
assertTrue(
|
|
test.rewardAddressAmount + feeParams.wormholeFeeOnSource == test.deliveryPrice,
|
|
"Reward address was paid correctly"
|
|
);
|
|
assertTrue(test.refundAddressAmount == test.receiverValue, "Receiver value was refunded");
|
|
assertTrue(
|
|
test.relayerPayment == test.destinationAmount + test.refundAddressAmount,
|
|
"Relayer paid the correct amount"
|
|
);
|
|
}
|
|
|
|
function testFundsCorrectForASendFailureDueToRevert(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams
|
|
) public {
|
|
vm.recordLogs();
|
|
(StandardSetupTwoChains memory setup, FundsCorrectTest memory test) =
|
|
setupFundsCorrectTest(gasParams, feeParams, REASONABLE_GAS_LIMIT_FORWARDS);
|
|
|
|
setup.target.deliveryProvider.updateSupportedChain(1, false);
|
|
|
|
setup.source.integration.sendMessageWithForwardedResponse{value: test.deliveryPrice}(
|
|
bytes("Hello!"),
|
|
bytes("Forwarded Message"),
|
|
setup.targetChain,
|
|
gasParams.targetGasLimit,
|
|
test.receiverValue,
|
|
setup.targetChain,
|
|
setup.target.refundAddress
|
|
);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) != keccak256(bytes("Hello!")));
|
|
assertTrue(getDeliveryStatus() == IWormholeRelayerDelivery.DeliveryStatus.RECEIVER_FAILURE);
|
|
|
|
test.refundAddressAmount = setup.target.refundAddress.balance - test.refundAddressBalance;
|
|
test.rewardAddressAmount = setup.source.rewardAddress.balance - test.rewardAddressBalance;
|
|
test.relayerPayment = test.relayerBalance - setup.target.relayer.balance;
|
|
test.destinationAmount = address(setup.target.integration).balance - test.destinationBalance;
|
|
|
|
assertTrue(test.sourceContractBalance == address(setup.source.coreRelayer).balance);
|
|
assertTrue(test.targetContractBalance == address(setup.target.coreRelayer).balance);
|
|
assertTrue(test.destinationAmount == 0, "No receiver value was sent to the contract");
|
|
assertTrue(
|
|
test.rewardAddressAmount + feeParams.wormholeFeeOnSource == test.deliveryPrice,
|
|
"Reward address was paid correctly"
|
|
);
|
|
test.gasAmount = uint32(
|
|
gasParams.targetGasLimit
|
|
- (test.refundAddressAmount - test.receiverValue) / test.targetChainRefundPerGasUnused
|
|
);
|
|
console.log(test.gasAmount);
|
|
assertTrue(
|
|
test.gasAmount >= 165_000,
|
|
"Gas amount (calculated from refund address payment) lower than expected. NOTE: This assert is purely to ensure the gas usage is consistent, and thus (since this was computed using the refund amount) the refund amount is correct."
|
|
);
|
|
assertTrue(
|
|
test.gasAmount <= 280_000,
|
|
"Gas amount (calculated from refund address payment) higher than expected. NOTE: This assert is purely to ensure the gas usage is consistent, and thus (since this was computed using the refund amount) the refund amount is correct."
|
|
);
|
|
assertTrue(
|
|
test.relayerPayment == test.destinationAmount + test.refundAddressAmount,
|
|
"Relayer paid the correct amount"
|
|
);
|
|
}
|
|
|
|
function testFundsCorrectForASendCrossChainRefundSuccess(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams
|
|
) public {
|
|
vm.recordLogs();
|
|
(StandardSetupTwoChains memory setup, FundsCorrectTest memory test) =
|
|
setupFundsCorrectTest(gasParams, feeParams, 170000);
|
|
|
|
uint256 refundRewardAddressBalance = setup.target.rewardAddress.balance;
|
|
uint256 refundAddressBalance = setup.source.refundAddress.balance;
|
|
|
|
setup.source.integration.sendMessageWithRefund{value: test.deliveryPrice}(
|
|
bytes("Hello!"),
|
|
setup.targetChain,
|
|
gasParams.targetGasLimit,
|
|
test.receiverValue,
|
|
setup.sourceChain,
|
|
setup.source.refundAddress
|
|
);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) == keccak256(bytes("Hello!")));
|
|
|
|
genericRelayer.relay(setup.targetChain);
|
|
|
|
assertTrue(
|
|
test.deliveryPrice
|
|
== setup.source.rewardAddress.balance - test.rewardAddressBalance
|
|
+ feeParams.wormholeFeeOnSource,
|
|
"The source to target relayer's reward address was paid appropriately"
|
|
);
|
|
|
|
uint256 amountToGetInRefundTarget =
|
|
(setup.target.rewardAddress.balance - refundRewardAddressBalance);
|
|
|
|
vm.assume(amountToGetInRefundTarget > 0);
|
|
|
|
uint256 refundSource;
|
|
(LocalNative baseFee,) = setup.target.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.sourceChain, TargetNative.wrap(0), Gas.wrap(0)
|
|
);
|
|
|
|
TargetNative tmp = setup.target.coreRelayer.quoteNativeForChain(
|
|
setup.sourceChain,
|
|
LocalNative.wrap(
|
|
amountToGetInRefundTarget + feeParams.wormholeFeeOnTarget - baseFee.unwrap()
|
|
),
|
|
setup.target.coreRelayer.getDefaultDeliveryProvider()
|
|
);
|
|
refundSource = tmp.unwrap();
|
|
|
|
// Calculate amount that must have been spent on gas, by reverse engineering from the amount that was paid to the provider's reward address on the target chain
|
|
test.gasAmount = uint32(
|
|
gasParams.targetGasLimit
|
|
- (amountToGetInRefundTarget + feeParams.wormholeFeeOnTarget)
|
|
/ test.targetChainRefundPerGasUnused
|
|
);
|
|
test.relayerPayment = test.relayerBalance - setup.target.relayer.balance;
|
|
test.destinationAmount = address(setup.target.integration).balance - test.destinationBalance;
|
|
|
|
assertTrue(
|
|
test.destinationAmount == feeParams.receiverValueTarget,
|
|
"Receiver value was sent to the contract"
|
|
);
|
|
assertTrue(
|
|
test.relayerPayment
|
|
== amountToGetInRefundTarget + feeParams.wormholeFeeOnTarget
|
|
+ feeParams.receiverValueTarget,
|
|
"Relayer paid the correct amount"
|
|
);
|
|
assertTrue(
|
|
refundSource == setup.source.refundAddress.balance - refundAddressBalance,
|
|
"Refund wasn't the correct amount"
|
|
);
|
|
console.log(test.gasAmount);
|
|
assertTrue(
|
|
test.gasAmount >= 140000,
|
|
"Gas amount (calculated from refund address payment) lower than expected. NOTE: This assert is purely to ensure the gas usage is consistent, and thus (since this was computed using the refund amount) the refund amount is correct."
|
|
);
|
|
assertTrue(
|
|
test.gasAmount <= 160000,
|
|
"Gas amount (calculated from refund address payment) higher than expected. NOTE: This assert is purely to ensure the gas usage is consistent, and thus (since this was computed using the refund amount) the refund amount is correct."
|
|
);
|
|
}
|
|
|
|
function testFundsCorrectForASendCrossChainRefundFailProviderNotSupported(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams
|
|
) public {
|
|
vm.recordLogs();
|
|
(StandardSetupTwoChains memory setup, FundsCorrectTest memory test) = setupFundsCorrectTest(
|
|
gasParams, feeParams, uint32(170000 + REASONABLE_GAS_LIMIT.unwrap())
|
|
);
|
|
|
|
setup.target.deliveryProvider.updateSupportedChain(setup.sourceChain, false);
|
|
vm.assume(
|
|
test.targetChainRefundPerGasUnused * REASONABLE_GAS_LIMIT.unwrap()
|
|
>= feeParams.wormholeFeeOnTarget
|
|
+ uint256(1) * gasParams.evmGasOverhead * gasParams.sourceGasPrice
|
|
* (uint256(feeParams.sourceNativePrice) / feeParams.targetNativePrice + 1)
|
|
);
|
|
|
|
setup.source.integration.sendMessageWithRefund{value: test.deliveryPrice}(
|
|
bytes("Hello!"),
|
|
setup.targetChain,
|
|
gasParams.targetGasLimit,
|
|
test.receiverValue,
|
|
setup.sourceChain,
|
|
setup.source.refundAddress
|
|
);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) == keccak256(bytes("Hello!")));
|
|
|
|
assertTrue(
|
|
test.deliveryPrice
|
|
== setup.source.rewardAddress.balance - test.rewardAddressBalance
|
|
+ feeParams.wormholeFeeOnSource,
|
|
"The source to target relayer's reward address was paid appropriately"
|
|
);
|
|
|
|
test.relayerPayment = test.relayerBalance - setup.target.relayer.balance;
|
|
test.destinationAmount = address(setup.target.integration).balance - test.destinationBalance;
|
|
|
|
assertTrue(
|
|
test.destinationAmount == feeParams.receiverValueTarget,
|
|
"Receiver value was sent to the contract"
|
|
);
|
|
assertTrue(
|
|
test.relayerPayment == feeParams.receiverValueTarget,
|
|
"Relayer only paid the receiver value, and received the full transaction fee refund"
|
|
);
|
|
uint8 refundStatus = uint8(getRefundStatus());
|
|
assertTrue(
|
|
refundStatus
|
|
== uint8(
|
|
IWormholeRelayerDelivery.RefundStatus.CROSS_CHAIN_REFUND_FAIL_PROVIDER_NOT_SUPPORTED
|
|
)
|
|
);
|
|
}
|
|
|
|
function testFundsCorrectForASendCrossChainRefundNotEnough(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams
|
|
) public {
|
|
vm.recordLogs();
|
|
(StandardSetupTwoChains memory setup, FundsCorrectTest memory test) =
|
|
setupFundsCorrectTest(gasParams, feeParams, 170000);
|
|
vm.assume(
|
|
uint256(1) * gasParams.evmGasOverhead * gasParams.sourceGasPrice
|
|
* feeParams.sourceNativePrice
|
|
> uint256(1) * feeParams.targetNativePrice * test.targetChainRefundPerGasUnused
|
|
* gasParams.targetGasLimit
|
|
);
|
|
|
|
setup.source.integration.sendMessageWithRefund{value: test.deliveryPrice}(
|
|
bytes("Hello!"),
|
|
setup.targetChain,
|
|
gasParams.targetGasLimit,
|
|
test.receiverValue,
|
|
setup.sourceChain,
|
|
setup.source.refundAddress
|
|
);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) == keccak256(bytes("Hello!")));
|
|
|
|
assertTrue(
|
|
test.deliveryPrice
|
|
== setup.source.rewardAddress.balance - test.rewardAddressBalance
|
|
+ feeParams.wormholeFeeOnSource,
|
|
"The source to target relayer's reward address was paid appropriately"
|
|
);
|
|
|
|
test.relayerPayment = test.relayerBalance - setup.target.relayer.balance;
|
|
test.destinationAmount = address(setup.target.integration).balance - test.destinationBalance;
|
|
|
|
assertTrue(
|
|
test.destinationAmount == feeParams.receiverValueTarget,
|
|
"Receiver value was sent to the contract"
|
|
);
|
|
assertTrue(
|
|
test.relayerPayment == feeParams.receiverValueTarget,
|
|
"Relayer only paid the receiver value, and received the full transaction fee refund"
|
|
);
|
|
|
|
assertTrue(
|
|
uint8(getRefundStatus())
|
|
== uint8(IWormholeRelayerDelivery.RefundStatus.CROSS_CHAIN_REFUND_FAIL_NOT_ENOUGH)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Unit tests for Send and Resend: Ensuring the correct struct is logged
|
|
*/
|
|
|
|
struct UnitTestParams {
|
|
address targetAddress;
|
|
bytes payload;
|
|
uint128 receiverValue;
|
|
uint128 paymentForExtraReceiverValue;
|
|
uint32 gasLimit;
|
|
uint16 refundChain;
|
|
address refundAddress;
|
|
VaaKey[3] vaaKeysFixed;
|
|
}
|
|
|
|
uint8 constant RANDOM_KEY_TYPE = 159;
|
|
bytes constant RANDOM_KEY_TYPE_BODY = hex"12911894719274912740817248912740817240";
|
|
|
|
function testUnitTestSend(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
UnitTestParams memory params
|
|
) public {
|
|
gasParams.targetGasLimit = params.gasLimit;
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, params.gasLimit);
|
|
MessageKey[] memory messageKeys = new MessageKey[](4);
|
|
for (uint256 j = 0; j < 3; j++) {
|
|
messageKeys[j] =
|
|
MessageKey(VAA_KEY_TYPE, WormholeRelayerSerde.encodeVaaKey(params.vaaKeysFixed[j]));
|
|
}
|
|
setup.source.deliveryProvider.updateSupportedMessageKeyTypes(RANDOM_KEY_TYPE, true);
|
|
messageKeys[3] = MessageKey(RANDOM_KEY_TYPE, RANDOM_KEY_TYPE_BODY); // random keyType and encodedKey
|
|
vm.recordLogs();
|
|
|
|
(LocalNative deliveryCost, GasPrice targetChainRefundPerGasUnused) = setup
|
|
.source
|
|
.coreRelayer
|
|
.quoteEVMDeliveryPrice(
|
|
setup.targetChain, TargetNative.wrap(params.receiverValue), Gas.wrap(params.gasLimit)
|
|
);
|
|
uint256 value = deliveryCost.unwrap() + params.paymentForExtraReceiverValue;
|
|
setup.source.integration.sendToEvm{value: value}(
|
|
setup.targetChain,
|
|
params.targetAddress,
|
|
params.gasLimit,
|
|
params.refundChain,
|
|
params.refundAddress,
|
|
params.receiverValue,
|
|
params.paymentForExtraReceiverValue,
|
|
params.payload,
|
|
messageKeys
|
|
);
|
|
|
|
bytes memory encodedExecutionInfo = abi.encode(
|
|
uint8(ExecutionInfoVersion.EVM_V1), params.gasLimit, targetChainRefundPerGasUnused
|
|
);
|
|
TargetNative extraReceiverValue = setup.source.coreRelayer.quoteNativeForChain(
|
|
setup.targetChain,
|
|
LocalNative.wrap(params.paymentForExtraReceiverValue),
|
|
address(setup.source.deliveryProvider)
|
|
);
|
|
|
|
DeliveryInstruction memory expectedInstruction = DeliveryInstruction({
|
|
targetChain: setup.targetChain,
|
|
targetAddress: toWormholeFormat(params.targetAddress),
|
|
payload: params.payload,
|
|
requestedReceiverValue: TargetNative.wrap(params.receiverValue),
|
|
extraReceiverValue: extraReceiverValue,
|
|
encodedExecutionInfo: encodedExecutionInfo,
|
|
refundChain: params.refundChain,
|
|
refundAddress: toWormholeFormat(params.refundAddress),
|
|
refundDeliveryProvider: setup.source.deliveryProvider.getTargetChainAddress(
|
|
setup.targetChain
|
|
),
|
|
sourceDeliveryProvider: toWormholeFormat(address(setup.source.deliveryProvider)),
|
|
senderAddress: toWormholeFormat(address(setup.source.integration)),
|
|
messageKeys: messageKeys
|
|
});
|
|
|
|
checkInstructionEquality(
|
|
relayerWormholeSimulator.parseVMFromLogs(vm.getRecordedLogs()[0]).payload,
|
|
expectedInstruction
|
|
);
|
|
}
|
|
|
|
struct UnitTestResendParams {
|
|
VaaKey deliveryVaaKey;
|
|
uint128 newReceiverValue;
|
|
uint32 newGasLimit;
|
|
address senderAddress;
|
|
}
|
|
|
|
function testUnitTestResend(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
UnitTestResendParams memory params
|
|
) public {
|
|
gasParams.targetGasLimit = params.newGasLimit;
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, params.newGasLimit);
|
|
|
|
vm.recordLogs();
|
|
|
|
(LocalNative deliveryCost, GasPrice targetChainRefundPerGasUnused) = setup
|
|
.source
|
|
.coreRelayer
|
|
.quoteEVMDeliveryPrice(
|
|
setup.targetChain,
|
|
TargetNative.wrap(params.newReceiverValue),
|
|
Gas.wrap(params.newGasLimit)
|
|
);
|
|
uint256 value = deliveryCost.unwrap();
|
|
vm.deal(params.senderAddress, value);
|
|
vm.prank(params.senderAddress);
|
|
setup.source.coreRelayer.resendToEvm{value: value}(
|
|
params.deliveryVaaKey,
|
|
setup.targetChain,
|
|
TargetNative.wrap(params.newReceiverValue),
|
|
Gas.wrap(params.newGasLimit),
|
|
address(setup.source.deliveryProvider)
|
|
);
|
|
|
|
bytes memory encodedExecutionInfo = abi.encode(
|
|
uint8(ExecutionInfoVersion.EVM_V1), params.newGasLimit, targetChainRefundPerGasUnused
|
|
);
|
|
|
|
RedeliveryInstruction memory expectedInstruction = RedeliveryInstruction({
|
|
deliveryVaaKey: params.deliveryVaaKey,
|
|
targetChain: setup.targetChain,
|
|
newRequestedReceiverValue: TargetNative.wrap(params.newReceiverValue),
|
|
newEncodedExecutionInfo: encodedExecutionInfo,
|
|
newSourceDeliveryProvider: toWormholeFormat(address(setup.source.deliveryProvider)),
|
|
newSenderAddress: toWormholeFormat(params.senderAddress)
|
|
});
|
|
|
|
checkRedeliveryInstructionEquality(
|
|
relayerWormholeSimulator.parseVMFromLogs(vm.getRecordedLogs()[0]).payload,
|
|
expectedInstruction
|
|
);
|
|
}
|
|
|
|
function checkMessageKey(
|
|
bytes memory data,
|
|
uint256 _index,
|
|
MessageKey memory messageKey
|
|
) public returns (uint256 index) {
|
|
MessageKey memory decodedMessageKey;
|
|
index = _index;
|
|
(decodedMessageKey.keyType, index) = data.asUint8(index);
|
|
assertEq(
|
|
decodedMessageKey.keyType, messageKey.keyType, "decodedMessageKey.keyType incorrect"
|
|
);
|
|
if (decodedMessageKey.keyType == VAA_KEY_TYPE) {
|
|
(VaaKey memory vaaKey,) = WormholeRelayerSerde.decodeVaaKey(messageKey.encodedKey, 0);
|
|
index = checkVaaKey(data, index, vaaKey);
|
|
} else if (decodedMessageKey.keyType == RANDOM_KEY_TYPE) {
|
|
uint32 encodedKeyLen;
|
|
(encodedKeyLen, index) = data.asUint32(index);
|
|
bytes memory encodedKey;
|
|
(encodedKey, index) = data.sliceUnchecked(index, encodedKeyLen);
|
|
assertEq(
|
|
encodedKeyLen,
|
|
RANDOM_KEY_TYPE_BODY.length,
|
|
"encodedKeyLen for RANDOM_KEY_TYPE must be RANDOM_KEY_TYPE_BODY.length"
|
|
);
|
|
assertEq(encodedKey, messageKey.encodedKey, "decoded encodedKey must equal expected");
|
|
assertEq(encodedKey, RANDOM_KEY_TYPE_BODY);
|
|
} else {
|
|
assertFalse(true, "Unsupported keyType found");
|
|
}
|
|
}
|
|
|
|
function checkVaaKey(
|
|
bytes memory data,
|
|
uint256 _index,
|
|
VaaKey memory vaaKey
|
|
) public returns (uint256 index) {
|
|
VaaKey memory decodedVaaKey;
|
|
index = _index;
|
|
console.log(data.length, index);
|
|
(decodedVaaKey.chainId, index) = data.asUint16(index);
|
|
console.log(data.length, index);
|
|
assertTrue(decodedVaaKey.chainId == vaaKey.chainId, "Wrong chain id");
|
|
(decodedVaaKey.emitterAddress, index) = data.asBytes32(index);
|
|
console.log(data.length, index);
|
|
assertTrue(decodedVaaKey.emitterAddress == vaaKey.emitterAddress, "Wrong emitter address");
|
|
(decodedVaaKey.sequence, index) = data.asUint64(index);
|
|
console.log(data.length, index);
|
|
assertTrue(decodedVaaKey.sequence == vaaKey.sequence, "Wrong sequence");
|
|
}
|
|
|
|
function checkInstructionEquality(
|
|
bytes memory data,
|
|
DeliveryInstruction memory expectedInstruction
|
|
) public {
|
|
uint256 index = 0;
|
|
uint32 length = 0;
|
|
uint8 payloadId;
|
|
DeliveryInstruction memory decodedInstruction;
|
|
(payloadId, index) = data.asUint8(index);
|
|
assertTrue(payloadId == 1, "Is a delivery instruction");
|
|
(decodedInstruction.targetChain, index) = data.asUint16(index);
|
|
assertTrue(
|
|
decodedInstruction.targetChain == expectedInstruction.targetChain,
|
|
"Wrong target chain id"
|
|
);
|
|
(decodedInstruction.targetAddress, index) = data.asBytes32(index);
|
|
assertTrue(
|
|
decodedInstruction.targetAddress == expectedInstruction.targetAddress,
|
|
"Wrong target address"
|
|
);
|
|
(length, index) = data.asUint32(index);
|
|
(decodedInstruction.payload, index) = data.slice(index, length);
|
|
assertTrue(
|
|
keccak256(decodedInstruction.payload) == keccak256(expectedInstruction.payload),
|
|
"Wrong payload"
|
|
);
|
|
uint256 requestedReceiverValue;
|
|
uint256 extraReceiverValue;
|
|
(requestedReceiverValue, index) = data.asUint256(index);
|
|
assertTrue(
|
|
requestedReceiverValue == expectedInstruction.requestedReceiverValue.unwrap(),
|
|
"Wrong requested receiver value"
|
|
);
|
|
(extraReceiverValue, index) = data.asUint256(index);
|
|
assertTrue(
|
|
extraReceiverValue == expectedInstruction.extraReceiverValue.unwrap(),
|
|
"Wrong extra receiver value"
|
|
);
|
|
(length, index) = data.asUint32(index);
|
|
(decodedInstruction.encodedExecutionInfo, index) = data.slice(index, length);
|
|
assertTrue(
|
|
keccak256(decodedInstruction.encodedExecutionInfo)
|
|
== keccak256(expectedInstruction.encodedExecutionInfo),
|
|
"Wrong encoded execution info"
|
|
);
|
|
(decodedInstruction.refundChain, index) = data.asUint16(index);
|
|
assertTrue(
|
|
decodedInstruction.refundChain == expectedInstruction.refundChain,
|
|
"Wrong refund chain id"
|
|
);
|
|
(decodedInstruction.refundAddress, index) = data.asBytes32(index);
|
|
assertTrue(
|
|
decodedInstruction.refundAddress == expectedInstruction.refundAddress,
|
|
"Wrong refund address"
|
|
);
|
|
(decodedInstruction.refundDeliveryProvider, index) = data.asBytes32(index);
|
|
assertTrue(
|
|
decodedInstruction.refundDeliveryProvider == expectedInstruction.refundDeliveryProvider,
|
|
"Wrong refund relay provider"
|
|
);
|
|
(decodedInstruction.sourceDeliveryProvider, index) = data.asBytes32(index);
|
|
assertTrue(
|
|
decodedInstruction.sourceDeliveryProvider == expectedInstruction.sourceDeliveryProvider,
|
|
"Wrong source relay provider"
|
|
);
|
|
(decodedInstruction.senderAddress, index) = data.asBytes32(index);
|
|
assertTrue(
|
|
decodedInstruction.senderAddress == expectedInstruction.senderAddress,
|
|
"Wrong sender address"
|
|
);
|
|
uint8 messageKeysLength;
|
|
(messageKeysLength, index) = data.asUint8(index);
|
|
decodedInstruction.messageKeys = new MessageKey[](messageKeysLength);
|
|
for (uint256 i = 0; i < messageKeysLength; i++) {
|
|
index = checkMessageKey(data, index, expectedInstruction.messageKeys[i]);
|
|
}
|
|
assertTrue(index == data.length, "Wrong length of data");
|
|
}
|
|
|
|
function checkRedeliveryInstructionEquality(
|
|
bytes memory data,
|
|
RedeliveryInstruction memory expectedInstruction
|
|
) public {
|
|
uint256 index = 0;
|
|
uint32 length = 0;
|
|
uint8 payloadId;
|
|
RedeliveryInstruction memory decodedInstruction;
|
|
(payloadId, index) = data.asUint8(index);
|
|
assertTrue(payloadId == 2, "Is a redelivery instruction");
|
|
uint8 vaaKeyType;
|
|
(vaaKeyType, index) = data.asUint8(index);
|
|
assertTrue(vaaKeyType == 1, "Is a vaa key");
|
|
index = checkVaaKey(data, index, expectedInstruction.deliveryVaaKey);
|
|
(decodedInstruction.targetChain, index) = data.asUint16(index);
|
|
assertTrue(
|
|
decodedInstruction.targetChain == expectedInstruction.targetChain,
|
|
"Wrong target chain id"
|
|
);
|
|
uint256 requestedReceiverValue;
|
|
(requestedReceiverValue, index) = data.asUint256(index);
|
|
assertTrue(
|
|
requestedReceiverValue == expectedInstruction.newRequestedReceiverValue.unwrap(),
|
|
"Wrong requested receiver value"
|
|
);
|
|
(length, index) = data.asUint32(index);
|
|
(decodedInstruction.newEncodedExecutionInfo, index) = data.slice(index, length);
|
|
assertTrue(
|
|
keccak256(decodedInstruction.newEncodedExecutionInfo)
|
|
== keccak256(expectedInstruction.newEncodedExecutionInfo),
|
|
"Wrong encoded execution info"
|
|
);
|
|
(decodedInstruction.newSourceDeliveryProvider, index) = data.asBytes32(index);
|
|
assertTrue(
|
|
decodedInstruction.newSourceDeliveryProvider
|
|
== expectedInstruction.newSourceDeliveryProvider,
|
|
"Wrong source relay provider"
|
|
);
|
|
(decodedInstruction.newSenderAddress, index) = data.asBytes32(index);
|
|
assertTrue(
|
|
decodedInstruction.newSenderAddress == expectedInstruction.newSenderAddress,
|
|
"Wrong sender address"
|
|
);
|
|
assertTrue(index == data.length, "Wrong length of data");
|
|
}
|
|
|
|
/**
|
|
* Tests related to reverts in deliver()
|
|
*
|
|
*/
|
|
|
|
function invalidateVM(bytes memory message, WormholeSimulator simulator) internal {
|
|
change(message, message.length - 1);
|
|
simulator.invalidateVM(message);
|
|
}
|
|
|
|
function change(bytes memory message, uint256 index) internal pure {
|
|
if (message[index] == 0x02) {
|
|
message[index] = 0x04;
|
|
} else {
|
|
message[index] = 0x02;
|
|
}
|
|
}
|
|
|
|
struct DeliveryStack {
|
|
bytes32 deliveryVaaHash;
|
|
uint256 payment;
|
|
Vm.Log[] entries;
|
|
bytes encodedDeliveryVAA;
|
|
bytes[] encodedVMs;
|
|
IWormhole.VM parsed;
|
|
uint256 budget;
|
|
address payable relayerRefundAddress;
|
|
DeliveryInstruction instruction;
|
|
}
|
|
|
|
function prepareDeliveryStack(
|
|
DeliveryStack memory stack,
|
|
StandardSetupTwoChains memory setup,
|
|
uint256 numVaas
|
|
) internal {
|
|
stack.entries = vm.getRecordedLogs();
|
|
stack.encodedVMs = new bytes[](0);
|
|
|
|
stack.encodedDeliveryVAA = relayerWormholeSimulator.fetchSignedMessageFromLogs(
|
|
stack.entries[numVaas], setup.sourceChain, address(setup.source.coreRelayer)
|
|
);
|
|
|
|
stack.relayerRefundAddress = payable(setup.target.relayer);
|
|
stack.parsed = relayerWormhole.parseVM(stack.encodedDeliveryVAA);
|
|
stack.instruction = WormholeRelayerSerde.decodeDeliveryInstruction(stack.parsed.payload);
|
|
stack.deliveryVaaHash = stack.parsed.hash;
|
|
EvmExecutionInfoV1 memory executionInfo =
|
|
decodeEvmExecutionInfoV1(stack.instruction.encodedExecutionInfo);
|
|
stack.budget = Wei.unwrap(
|
|
executionInfo.gasLimit.toWei(executionInfo.targetChainRefundPerGasUnused)
|
|
+ stack.instruction.extraReceiverValue.asNative()
|
|
);
|
|
}
|
|
|
|
function testRevertDeliveryInvalidDeliveryVAA(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.recordLogs();
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
sendMessageToTargetChain(setup, gasParams.targetGasLimit, 0, message);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
bytes memory fakeVM = abi.encodePacked(stack.encodedDeliveryVAA);
|
|
|
|
invalidateVM(fakeVM, setup.target.wormholeSimulator);
|
|
|
|
stack.encodedDeliveryVAA = fakeVM;
|
|
|
|
vm.prank(setup.target.relayer);
|
|
vm.expectRevert(abi.encodeWithSignature("InvalidDeliveryVaa(string)", ""));
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs, stack.encodedDeliveryVAA, stack.relayerRefundAddress, bytes("")
|
|
);
|
|
}
|
|
|
|
function testRevertDeliveryInvalidEmitter(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.recordLogs();
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
sendMessageToTargetChain(setup, gasParams.targetGasLimit, 0, message);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
// Create valid VAA with wrong emitter address
|
|
IWormhole.VM memory vm_ = relayerWormholeSimulator.parseVMFromLogs(stack.entries[0]);
|
|
vm_.version = uint8(1);
|
|
vm_.timestamp = uint32(block.timestamp);
|
|
vm_.emitterChainId = setup.sourceChain;
|
|
vm_.emitterAddress = toWormholeFormat(address(setup.source.integration));
|
|
bytes memory deliveryVaaWithWrongEmitter =
|
|
relayerWormholeSimulator.encodeAndSignMessage(vm_);
|
|
|
|
vm.prank(setup.target.relayer);
|
|
vm.expectRevert(
|
|
abi.encodeWithSelector(
|
|
InvalidEmitter.selector,
|
|
setup.source.integration,
|
|
setup.source.coreRelayer,
|
|
setup.source.chainId
|
|
)
|
|
);
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs, deliveryVaaWithWrongEmitter, stack.relayerRefundAddress, bytes("")
|
|
);
|
|
}
|
|
|
|
function testRevertDeliveryInsufficientRelayerFunds(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.recordLogs();
|
|
|
|
vm.assume(gasParams.targetGasPrice > 1);
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
sendMessageToTargetChain(setup, gasParams.targetGasLimit, 0, message);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
vm.prank(setup.target.relayer);
|
|
vm.expectRevert(
|
|
abi.encodeWithSelector(
|
|
InsufficientRelayerFunds.selector, stack.budget - 1, stack.budget
|
|
)
|
|
);
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget - 1}(
|
|
stack.encodedVMs, stack.encodedDeliveryVAA, stack.relayerRefundAddress, bytes("")
|
|
);
|
|
}
|
|
|
|
function testRevertDeliveryTargetChainIsNotThisChain(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.recordLogs();
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
sendMessageToTargetChain(setup, gasParams.targetGasLimit, 0, message);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
vm.prank(setup.target.relayer);
|
|
vm.expectRevert(abi.encodeWithSignature("TargetChainIsNotThisChain(uint16)", 2));
|
|
map[setup.differentChainId].coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs, stack.encodedDeliveryVAA, stack.relayerRefundAddress, bytes("")
|
|
);
|
|
}
|
|
|
|
// aka: replay protection doesn't fire when it shouldn't
|
|
function testNoFalseFiresReplayProtection(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.recordLogs();
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
sendMessageToTargetChain(setup, gasParams.targetGasLimit, 0, message);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
vm.prank(setup.target.relayer);
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs, stack.encodedDeliveryVAA, stack.relayerRefundAddress, bytes("")
|
|
);
|
|
assertEq(
|
|
setup.target.coreRelayerFull.deliverySuccessBlock(stack.deliveryVaaHash), block.number
|
|
);
|
|
assertEq(setup.target.coreRelayerFull.deliveryFailureBlock(stack.deliveryVaaHash), 0);
|
|
|
|
vm.recordLogs();
|
|
sendMessageToTargetChain(setup, gasParams.targetGasLimit, 0, message);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
vm.prank(setup.target.relayer);
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs, stack.encodedDeliveryVAA, stack.relayerRefundAddress, bytes("")
|
|
);
|
|
assertEq(
|
|
setup.target.coreRelayerFull.deliverySuccessBlock(stack.deliveryVaaHash), block.number
|
|
);
|
|
assertEq(setup.target.coreRelayerFull.deliveryFailureBlock(stack.deliveryVaaHash), 0);
|
|
}
|
|
|
|
function testReplayProtection(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.recordLogs();
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
sendMessageToTargetChain(setup, gasParams.targetGasLimit, 0, message);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
vm.prank(setup.target.relayer);
|
|
assertFalse(setup.target.coreRelayerFull.deliveryAttempted(stack.deliveryVaaHash));
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs, stack.encodedDeliveryVAA, stack.relayerRefundAddress, bytes("")
|
|
);
|
|
assertTrue(setup.target.coreRelayerFull.deliveryAttempted(stack.deliveryVaaHash));
|
|
assertEq(
|
|
setup.target.coreRelayerFull.deliverySuccessBlock(stack.deliveryVaaHash), block.number
|
|
);
|
|
assertEq(setup.target.coreRelayerFull.deliveryFailureBlock(stack.deliveryVaaHash), 0);
|
|
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs, stack.encodedDeliveryVAA, stack.relayerRefundAddress, bytes("")
|
|
);
|
|
|
|
assertTrue(
|
|
getDeliveryStatus()
|
|
== IWormholeRelayerDelivery.DeliveryStatus.RECEIVER_FAILURE,
|
|
"Should have failed due to Replay Protection"
|
|
);
|
|
}
|
|
|
|
function testRevertDeliveryVaaKeysLengthDoesNotMatchVaasLength(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.recordLogs();
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
sendMessageToTargetChain(setup, gasParams.targetGasLimit, 0, message);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
stack.encodedVMs = new bytes[](1);
|
|
stack.encodedVMs[0] = stack.encodedDeliveryVAA;
|
|
|
|
vm.prank(setup.target.relayer);
|
|
vm.expectRevert(
|
|
abi.encodeWithSignature(
|
|
"MessageKeysLengthDoesNotMatchMessagesLength(uint256,uint256)", 0, 1
|
|
)
|
|
);
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs, stack.encodedDeliveryVAA, stack.relayerRefundAddress, bytes("")
|
|
);
|
|
}
|
|
|
|
function testRevertDeliveryVaaKeysDoNotMatchVaas(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.recordLogs();
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
(LocalNative payment_,) = setup.source.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.targetChain, TargetNative.wrap(0), Gas.wrap(gasParams.targetGasLimit)
|
|
);
|
|
stack.payment = payment_.unwrap();
|
|
|
|
uint64 sequence = setup.source.wormhole.publishMessage{value: feeParams.wormholeFeeOnSource}(
|
|
1, bytes(""), 200
|
|
);
|
|
setup.source.integration.sendToEvm{value: stack.payment}(
|
|
setup.targetChain,
|
|
address(setup.target.integration),
|
|
gasParams.targetGasLimit,
|
|
setup.sourceChain,
|
|
address(this),
|
|
0,
|
|
0,
|
|
message,
|
|
vaaKeyArray(setup.sourceChain, sequence, address(this))
|
|
);
|
|
|
|
prepareDeliveryStack(stack, setup, 1);
|
|
|
|
stack.encodedVMs = new bytes[](1);
|
|
stack.encodedVMs[0] = stack.encodedDeliveryVAA;
|
|
|
|
vm.prank(setup.target.relayer);
|
|
vm.expectRevert(abi.encodeWithSignature("VaaKeysDoNotMatchVaas(uint8)", 0));
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs, stack.encodedDeliveryVAA, stack.relayerRefundAddress, bytes("")
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Tests related to reverts due to delivering with deliveryOverrides
|
|
*/
|
|
|
|
function testDeliveryWithOverrides(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.recordLogs();
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
sendMessageToTargetChain(setup, gasParams.targetGasLimit, 0, message);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
DeliveryOverride memory deliveryOverride = DeliveryOverride(
|
|
stack.instruction.requestedReceiverValue,
|
|
stack.instruction.encodedExecutionInfo,
|
|
stack.deliveryVaaHash //really redeliveryHash
|
|
);
|
|
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs,
|
|
stack.encodedDeliveryVAA,
|
|
stack.relayerRefundAddress,
|
|
WormholeRelayerSerde.encode(deliveryOverride)
|
|
);
|
|
assertTrue(keccak256(setup.target.integration.getMessage()) == keccak256(message));
|
|
}
|
|
|
|
function testRevertDeliveryWithOverrideGasLimit(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.recordLogs();
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
sendMessageToTargetChain(setup, gasParams.targetGasLimit, 0, message);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
EvmExecutionInfoV1 memory executionInfo =
|
|
decodeEvmExecutionInfoV1(stack.instruction.encodedExecutionInfo);
|
|
|
|
DeliveryOverride memory deliveryOverride = DeliveryOverride(
|
|
stack.instruction.requestedReceiverValue,
|
|
encodeEvmExecutionInfoV1(
|
|
EvmExecutionInfoV1({
|
|
gasLimit: executionInfo.gasLimit - Gas.wrap(1),
|
|
targetChainRefundPerGasUnused: executionInfo.targetChainRefundPerGasUnused
|
|
})
|
|
),
|
|
stack.deliveryVaaHash //really redeliveryHash
|
|
);
|
|
|
|
vm.expectRevert(abi.encodeWithSignature("InvalidOverrideGasLimit()"));
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs,
|
|
stack.encodedDeliveryVAA,
|
|
stack.relayerRefundAddress,
|
|
WormholeRelayerSerde.encode(deliveryOverride)
|
|
);
|
|
}
|
|
|
|
function testRevertDeliveryWithOverrideReceiverValue(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.recordLogs();
|
|
|
|
vm.assume(feeParams.receiverValueTarget > 0);
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
sendMessageToTargetChain(
|
|
setup, gasParams.targetGasLimit, feeParams.receiverValueTarget, message
|
|
);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
DeliveryOverride memory deliveryOverride = DeliveryOverride(
|
|
stack.instruction.requestedReceiverValue - TargetNative.wrap(1),
|
|
stack.instruction.encodedExecutionInfo,
|
|
stack.deliveryVaaHash //really redeliveryHash
|
|
);
|
|
|
|
vm.expectRevert(abi.encodeWithSignature("InvalidOverrideReceiverValue()"));
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs,
|
|
stack.encodedDeliveryVAA,
|
|
stack.relayerRefundAddress,
|
|
WormholeRelayerSerde.encode(deliveryOverride)
|
|
);
|
|
}
|
|
|
|
function testAllowDeliveryWithOverrideMaximumRefund(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.assume(gasParams.targetGasPrice > 1);
|
|
|
|
vm.recordLogs();
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
sendMessageToTargetChain(setup, gasParams.targetGasLimit, 0, message);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
EvmExecutionInfoV1 memory executionInfo =
|
|
decodeEvmExecutionInfoV1(stack.instruction.encodedExecutionInfo);
|
|
|
|
DeliveryOverride memory deliveryOverride = DeliveryOverride(
|
|
stack.instruction.requestedReceiverValue,
|
|
encodeEvmExecutionInfoV1(
|
|
EvmExecutionInfoV1({
|
|
gasLimit: executionInfo.gasLimit,
|
|
targetChainRefundPerGasUnused: GasPrice.wrap(
|
|
executionInfo.targetChainRefundPerGasUnused.unwrap() - 1
|
|
)
|
|
})
|
|
),
|
|
stack.deliveryVaaHash //really redeliveryHash
|
|
);
|
|
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs,
|
|
stack.encodedDeliveryVAA,
|
|
stack.relayerRefundAddress,
|
|
WormholeRelayerSerde.encode(deliveryOverride)
|
|
);
|
|
}
|
|
|
|
function testRevertDeliveryWithOverrideUnexpectedExecutionInfoVersion(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.recordLogs();
|
|
|
|
vm.assume(feeParams.receiverValueTarget > 0);
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
sendMessageToTargetChain(
|
|
setup, gasParams.targetGasLimit, feeParams.receiverValueTarget, message
|
|
);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
DeliveryOverride memory deliveryOverride = DeliveryOverride(
|
|
stack.instruction.requestedReceiverValue,
|
|
abi.encodePacked(uint8(4), stack.instruction.encodedExecutionInfo),
|
|
stack.deliveryVaaHash //really redeliveryHash
|
|
);
|
|
|
|
// Note: Reverts when trying to abi.decode the ExecutionInfoVersion. No revert message
|
|
vm.expectRevert();
|
|
setup.target.coreRelayerFull.deliver{value: stack.budget}(
|
|
stack.encodedVMs,
|
|
stack.encodedDeliveryVAA,
|
|
stack.relayerRefundAddress,
|
|
WormholeRelayerSerde.encode(deliveryOverride)
|
|
);
|
|
}
|
|
|
|
function testRevertSendMsgValueTooLow(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, REASONABLE_GAS_LIMIT);
|
|
|
|
vm.recordLogs();
|
|
|
|
(LocalNative deliveryCost,) = setup.source.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.targetChain, TargetNative.wrap(0), Gas.wrap(gasParams.targetGasLimit)
|
|
);
|
|
|
|
vm.expectRevert(
|
|
abi.encodeWithSelector(
|
|
InvalidMsgValue.selector, deliveryCost.unwrap() - 1, deliveryCost.unwrap()
|
|
)
|
|
);
|
|
setup.source.integration.sendMessage{value: deliveryCost.unwrap() - 1}(
|
|
message, setup.targetChain, gasParams.targetGasLimit, 0
|
|
);
|
|
}
|
|
|
|
function testRevertSendMsgValueTooHigh(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, REASONABLE_GAS_LIMIT);
|
|
|
|
vm.recordLogs();
|
|
|
|
(LocalNative deliveryCost,) = setup.source.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.targetChain, TargetNative.wrap(0), Gas.wrap(gasParams.targetGasLimit)
|
|
);
|
|
|
|
vm.expectRevert(
|
|
abi.encodeWithSelector(
|
|
InvalidMsgValue.selector, deliveryCost.unwrap() + 1, deliveryCost.unwrap()
|
|
)
|
|
);
|
|
setup.source.integration.sendMessage{value: deliveryCost.unwrap() + 1}(
|
|
message, setup.targetChain, gasParams.targetGasLimit, 0
|
|
);
|
|
}
|
|
|
|
function testRevertSendProviderNotSupported(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, REASONABLE_GAS_LIMIT);
|
|
|
|
vm.recordLogs();
|
|
|
|
(LocalNative deliveryCost,) = setup.source.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.targetChain, TargetNative.wrap(0), Gas.wrap(gasParams.targetGasLimit)
|
|
);
|
|
|
|
vm.expectRevert(
|
|
abi.encodeWithSelector(
|
|
DeliveryProviderDoesNotSupportTargetChain.selector,
|
|
address(setup.source.deliveryProvider),
|
|
uint16(32)
|
|
)
|
|
);
|
|
setup.source.integration.sendMessage{value: deliveryCost.unwrap() - 1}(
|
|
message, 32, gasParams.targetGasLimit, 0
|
|
);
|
|
}
|
|
|
|
function testRevertResendProviderNotSupported(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, REASONABLE_GAS_LIMIT);
|
|
|
|
vm.recordLogs();
|
|
|
|
vm.expectRevert(
|
|
abi.encodeWithSelector(
|
|
DeliveryProviderDoesNotSupportTargetChain.selector,
|
|
address(setup.source.deliveryProvider),
|
|
uint16(32)
|
|
)
|
|
);
|
|
setup.source.integration.resend{value: 0}(
|
|
setup.sourceChain, 1, 32, uint32(REASONABLE_GAS_LIMIT.unwrap()), 0
|
|
);
|
|
}
|
|
|
|
function testSendCheckConsistencyLevel(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
uint8 consistencyLevel
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, REASONABLE_GAS_LIMIT);
|
|
|
|
vm.recordLogs();
|
|
|
|
(LocalNative deliveryCost,) = setup.source.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.targetChain, TargetNative.wrap(0), Gas.wrap(0)
|
|
);
|
|
|
|
setup.source.coreRelayer.sendToEvm{value: deliveryCost.unwrap()}(
|
|
setup.targetChain,
|
|
address(0x0),
|
|
bytes(""),
|
|
TargetNative.wrap(0),
|
|
LocalNative.wrap(0),
|
|
Gas.wrap(0),
|
|
setup.sourceChain,
|
|
address(0x0),
|
|
address(setup.source.deliveryProvider),
|
|
vaaKeyArray(setup.sourceChain, 22345, address(this)),
|
|
consistencyLevel
|
|
);
|
|
|
|
Vm.Log memory log = vm.getRecordedLogs()[0];
|
|
|
|
// Parse the consistency level from the published VAA
|
|
(uint8 actualConsistencyLevel,) = log.data.asUint8(32 + 32 + 32 + 32 - 1);
|
|
|
|
assertTrue(consistencyLevel == actualConsistencyLevel);
|
|
}
|
|
|
|
function testToAndFromWormholeFormat(address msg1) public {
|
|
assertTrue(toWormholeFormat(msg1) == bytes32(uint256(uint160(msg1))));
|
|
assertTrue(fromWormholeFormat(toWormholeFormat(msg1)) == msg1);
|
|
}
|
|
|
|
function testRevertDeliveryReentrantCall(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
vm.recordLogs();
|
|
|
|
(LocalNative deliveryCost,) = setup.source.coreRelayer.quoteEVMDeliveryPrice(
|
|
setup.targetChain, TargetNative.wrap(0), Gas.wrap(500_000)
|
|
);
|
|
|
|
DeliveryStack memory stack;
|
|
|
|
setup.source.integration.sendMessageWithReentrantDelivery{value: deliveryCost.unwrap()}(
|
|
setup.targetChain, 500_000, 0
|
|
);
|
|
|
|
prepareDeliveryStack(stack, setup, 0);
|
|
|
|
vm.deal(payable(address(setup.target.integration)), 1e30);
|
|
vm.recordLogs();
|
|
setup.target.integration.deliverReentrant{value: 1e28}(stack.encodedDeliveryVAA);
|
|
Vm.Log[] memory logs = vm.getRecordedLogs();
|
|
assertTrue(
|
|
getDeliveryStatus(logs[logs.length - 1])
|
|
== IWormholeRelayerDelivery.DeliveryStatus.RECEIVER_FAILURE,
|
|
"Outer delivery should have failed because inner call reverts"
|
|
);
|
|
}
|
|
|
|
function testEncodeAndDecodeVaaKey() public {
|
|
VaaKey memory vaaKey = VaaKey({chainId: 1, emitterAddress: bytes32(""), sequence: 23});
|
|
(VaaKey memory newVaaKey,) =
|
|
WormholeRelayerSerde.decodeVaaKey(WormholeRelayerSerde.encodeVaaKey(vaaKey), 0);
|
|
checkVaaKey(WormholeRelayerSerde.encodeVaaKey(newVaaKey), 0, vaaKey);
|
|
}
|
|
|
|
function testEncodeAndDecodeMessageKey() public {
|
|
VaaKey memory vaaKey = VaaKey({chainId: 1, emitterAddress: bytes32(""), sequence: 23});
|
|
MessageKey memory messageKey = MessageKey({
|
|
keyType: VAA_KEY_TYPE,
|
|
encodedKey: WormholeRelayerSerde.encodeVaaKey(vaaKey)
|
|
});
|
|
|
|
(MessageKey memory newMessageKey,) = WormholeRelayerSerde.decodeMessageKey(
|
|
WormholeRelayerSerde.encodeMessageKey(messageKey), 0
|
|
);
|
|
checkMessageKey(WormholeRelayerSerde.encodeMessageKey(newMessageKey), 0, messageKey);
|
|
}
|
|
|
|
function testRevertEncodeAndDecodeTooLongMessageKeyArray() public {
|
|
uint256 len = uint256(type(uint8).max) + 1;
|
|
MessageKey[] memory messageKeys = new MessageKey[](len);
|
|
for (uint256 i = 0; i < len; ++i) {
|
|
messageKeys[i] =
|
|
MessageKey({keyType: RANDOM_KEY_TYPE, encodedKey: RANDOM_KEY_TYPE_BODY});
|
|
}
|
|
vm.expectRevert(abi.encodeWithSignature("TooManyMessageKeys(uint256)", len));
|
|
WormholeRelayerSerde.encodeMessageKeyArray(messageKeys);
|
|
}
|
|
|
|
function testEncodeAndDecodeMessageKeyArray(uint8 len, uint8 idx) public {
|
|
vm.assume(idx < len || len == 0);
|
|
MessageKey[] memory messageKeys = new MessageKey[](len);
|
|
for (uint256 i = 0; i < len; ++i) {
|
|
messageKeys[i] =
|
|
MessageKey({keyType: RANDOM_KEY_TYPE, encodedKey: RANDOM_KEY_TYPE_BODY});
|
|
}
|
|
VaaKey memory vaaKey = VaaKey({chainId: 1, emitterAddress: bytes32(""), sequence: 23});
|
|
if (len > 0) {
|
|
messageKeys[idx] = MessageKey(VAA_KEY_TYPE, WormholeRelayerSerde.encodeVaaKey(vaaKey));
|
|
}
|
|
|
|
(MessageKey[] memory newMessageKeys,) = WormholeRelayerSerde.decodeMessageKeyArray(
|
|
WormholeRelayerSerde.encodeMessageKeyArray(messageKeys), 0
|
|
);
|
|
for (uint256 i = 0; i < len; ++i) {
|
|
checkMessageKey(
|
|
WormholeRelayerSerde.encodeMessageKey(newMessageKeys[i]), 0, messageKeys[i]
|
|
);
|
|
}
|
|
}
|
|
|
|
function testEncodeAndDecodeMessageKeyDifferentType() public {
|
|
MessageKey memory messageKey =
|
|
MessageKey({keyType: VAA_KEY_TYPE, encodedKey: bytes("my USDC transfer")});
|
|
|
|
(MessageKey memory newMessageKey,) = WormholeRelayerSerde.decodeMessageKey(
|
|
WormholeRelayerSerde.encodeMessageKey(messageKey), 0
|
|
);
|
|
checkMessageKey(WormholeRelayerSerde.encodeMessageKey(newMessageKey), 0, messageKey);
|
|
}
|
|
|
|
function testEncodeAndDecodeDeliveryInstruction(bytes memory payload) public {
|
|
MessageKey[] memory messageKeys = new MessageKey[](3);
|
|
messageKeys[0] = MessageKey({
|
|
keyType: VAA_KEY_TYPE,
|
|
encodedKey: WormholeRelayerSerde.encodeVaaKey(
|
|
VaaKey({chainId: 1, emitterAddress: bytes32(""), sequence: 23})
|
|
)
|
|
});
|
|
messageKeys[1] = messageKeys[0];
|
|
messageKeys[2] = messageKeys[0];
|
|
|
|
DeliveryInstruction memory instruction = DeliveryInstruction({
|
|
targetChain: 1,
|
|
targetAddress: bytes32(""),
|
|
payload: payload,
|
|
requestedReceiverValue: TargetNative.wrap(456),
|
|
extraReceiverValue: TargetNative.wrap(123),
|
|
encodedExecutionInfo: bytes("abcdefghijklmnopqrstuvwxyz"),
|
|
refundChain: 2,
|
|
refundAddress: keccak256(bytes("refundAddress")),
|
|
refundDeliveryProvider: keccak256(bytes("refundRelayProvider")),
|
|
sourceDeliveryProvider: keccak256(bytes("sourceRelayProvider")),
|
|
senderAddress: keccak256(bytes("senderAddress")),
|
|
messageKeys: messageKeys
|
|
});
|
|
|
|
DeliveryInstruction memory newInstruction =
|
|
WormholeRelayerSerde.decodeDeliveryInstruction(WormholeRelayerSerde.encode(instruction));
|
|
|
|
checkInstructionEquality(WormholeRelayerSerde.encode(instruction), newInstruction);
|
|
checkInstructionEquality(WormholeRelayerSerde.encode(newInstruction), instruction);
|
|
}
|
|
|
|
function testDeliveryData(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup =
|
|
standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
|
|
|
|
vm.recordLogs();
|
|
|
|
sendMessageToTargetChain(setup, gasParams.targetGasLimit, 0, message);
|
|
|
|
genericRelayer.relay(setup.sourceChain);
|
|
|
|
bytes32 deliveryVaaHash = getDeliveryVAAHash(vm.getRecordedLogs());
|
|
|
|
DeliveryData memory deliveryData = setup.target.integration.getDeliveryData();
|
|
|
|
assertTrue(
|
|
fromWormholeFormat(deliveryData.sourceAddress) == address(setup.source.integration),
|
|
"Source address wrong"
|
|
);
|
|
assertTrue(deliveryData.sourceChain == setup.sourceChain, "Source chain id wrong");
|
|
assertTrue(deliveryData.deliveryHash == deliveryVaaHash, "delivery vaa hash wrong");
|
|
assertTrue(
|
|
keccak256(setup.target.integration.getMessage()) == keccak256(message), "payload wrong"
|
|
);
|
|
}
|
|
|
|
function testProviderRefundAddressZeros(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup = standardAssumeAndSetupTwoChains(
|
|
gasParams,
|
|
feeParams,
|
|
1000000
|
|
);
|
|
vm.recordLogs();
|
|
setup.source.deliveryProvider.updateTargetChainAddress(
|
|
setup.targetChain,
|
|
bytes32(0x0)
|
|
);
|
|
(LocalNative deliveryCost, ) = setup
|
|
.source
|
|
.coreRelayer
|
|
.quoteEVMDeliveryPrice(
|
|
setup.targetChain,
|
|
TargetNative.wrap(0),
|
|
Gas.wrap(gasParams.targetGasLimit)
|
|
);
|
|
setup.source.integration.sendMessageWithRefund{
|
|
value: LocalNative.unwrap(deliveryCost)
|
|
}(
|
|
message,
|
|
setup.targetChain,
|
|
gasParams.targetGasLimit,
|
|
0,
|
|
setup.sourceChain,
|
|
address(this)
|
|
);
|
|
genericRelayer.relay(setup.sourceChain);
|
|
}
|
|
|
|
function testSendTargetAddressZeros(
|
|
GasParameters memory gasParams,
|
|
FeeParameters memory feeParams,
|
|
bytes memory message
|
|
) public {
|
|
StandardSetupTwoChains memory setup = standardAssumeAndSetupTwoChains(
|
|
gasParams,
|
|
feeParams,
|
|
1000000
|
|
);
|
|
vm.recordLogs();
|
|
(LocalNative deliveryCost, ) = setup
|
|
.source
|
|
.coreRelayer
|
|
.quoteEVMDeliveryPrice(
|
|
setup.targetChain,
|
|
TargetNative.wrap(25),
|
|
Gas.wrap(gasParams.targetGasLimit)
|
|
);
|
|
setup.source.coreRelayer.sendPayloadToEvm{
|
|
value: LocalNative.unwrap(deliveryCost)
|
|
}(
|
|
setup.targetChain,
|
|
address(0x1234123412341234123412341234123412341234),
|
|
message,
|
|
TargetNative.wrap(25),
|
|
Gas.wrap(gasParams.targetGasLimit)
|
|
);
|
|
genericRelayer.relay(setup.sourceChain);
|
|
}
|
|
}
|