Merge pull request #4 from wormhole-foundation/core-relayer/integration

Clean up send (#2)
This commit is contained in:
A5 Pickle 2022-10-06 12:19:04 -05:00 committed by GitHub
commit 684912a8ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 301 additions and 2754 deletions

View File

@ -6,6 +6,7 @@ all: build
build: sdk/node_modules
cd ethereum && make build
cd sdk && npm run build
cd offchain-relayer && bash regenerate-abi.sh
sdk/node_modules:
cd sdk && npm ci

View File

@ -61,20 +61,10 @@ struct DeliveryInstructions {
// Chain ID of the receiver
ToChain uint16
// Length of user Payload
PayloadLen uint16
// Payload
Payload bytes
// Length of chain-specific payload
ChainPayloadLen uint16
// chain-specific delivery payload (accounts, storage slots...)
ChainPayload bytes
// Length of VAA whitelist
WhitelistLen uint16
// VAA whitelist
Whitelist []VAAId
Whitelist []AllowedEmitterSequence
// Length of Relayer Parameters
RelayerParamsLen uint16
@ -82,7 +72,7 @@ struct DeliveryInstructions {
RelayerParams bytes
}
struct VAAId {
struct AllowedEmitterSequence {
// VAA emitter
EmitterAddress bytes32
// VAA sequence
@ -96,7 +86,7 @@ struct DeliveryStatus {
// Hash of the relayed batch
BatchHash bytes32
// Delivery
Delivery VAAId
Delivery AllowedEmitterSequence
// Delivery try
DeliveryCount uint16
@ -112,7 +102,7 @@ struct ReDeliveryInstructions {
// Hash of the batch to re-deliver
BatchHash bytes32
// Point to the original delivery instruction
OriginalDelivery VAAId
OriginalDelivery AllowedEmitterSequence
// Current deliverycount
DeliveryCount uint16
@ -136,14 +126,14 @@ A relaying system that handles payment on the source chain could have minimal Re
struct MinimalRelayerParamsExample {
// All payloads should be versioned
Version uint8
// Limit the gas amount forwarded to wormholeReceiver()
// Limit the gas amount forwarded to receiveWormholeMessages()
DeliveryGasAmount uint32
}
struct RelayerParamsExample {
// All payloads should be versioned
Version uint8
// Limit the gas amount forwarded to wormholeReceiver()
// Limit the gas amount forwarded to receiveWormholeMessages()
DeliveryGasAmount uint32
// Limit the max batch size that was paid for -> fail delivery if batch is too big
MaxVAAsInBatch bytes32
@ -156,7 +146,7 @@ struct RelayerParamsExample {
### Relaying Contract Endpoints
`send(uint16 targetChain, bytes32 targetAddress, bytes payload, VAAWhitelist VAAId[], bytes relayerParams, bytes[] chainPayload, uint32 nonce, uint8 consistencyLevel) payable`
`send(uint16 targetChain, bytes32 targetAddress, bytes payload, VAAWhitelist AllowedEmitterSequence[], bytes relayerParams, bytes[] chainPayload, uint32 nonce, uint8 consistencyLevel) payable`
- Optionally: handle payment
- Emit `DeliveryInstructions` VAA with specified `targetChain, targetAddress, payload, relayerParams, nonce, VAAWhitelist` and `msg.sender`, the entry-points `chainId`
@ -166,19 +156,19 @@ struct RelayerParamsExample {
- Optionally: handle payment
- Emit `RedeliveryInstructions` VAA with the batch hash & delivery reference and new `relayerParams`
`estimateCost(uint16 targetChainId, bytes[] relayerParams) view`
`estimateEvmCost(uint16 targetChainId, bytes[] relayerParams) view`
- provide a quote for given relayerParams
- Gas Price, Limit should be encoded in `relayerParams`
`deliver(VAAv2 batchVAA, VAAId delivery, uint targetCallGasOverwrite)`
`deliver(VAAv2 batchVAA, AllowedEmitterSequence delivery, uint targetCallGasOverwrite)`
- Check the delivery hash (`hash(batchHash, VAAId)`) against already successfully delivered ones and revert if it was successfully delivered before
- Check the delivery hash (`hash(batchHash, AllowedEmitterSequence)`) against already successfully delivered ones and revert if it was successfully delivered before
- Parse and verify VAAv2 at Wormhole contract with caching enabled
- Check if the emitter of the `DeliveryInstructions` VAA is a known and trusted relaying contract
- Check if the `DeliveryInstructions.ToChain` is the right chain
- If a whitelist is specified, check if all specified VAAs are in the delivered VAAv2, if no whitelist is specified check that the full batch was delivered
- Call `wormholeReceiver` on the `DeliveryInstructions.ToAddress`
- Call `receiveWormholeMessages` on the `DeliveryInstructions.ToAddress`
- If the length of the VAA whitelist is > 0, just forward the whitelist of VAAs, otherwise the full batch
- If `targetCallGasOverwrite` is higher than what might be specified in `DeliveryInstructions.RelayerParams` take the overwrite value
- Emit `DeliveryStatus` depending on success or failure of the `receive` call
@ -195,7 +185,7 @@ struct RelayerParamsExample {
### Receiver Contract Endpoints
`wormholeReceiver(VAAv4[] vaas, uint16 sourceChain, bytes32 sourceAddress, bytes payload)`
`receiveWormholeMessages(VAAv4[] vaas, uint16 sourceChain, bytes32 sourceAddress, bytes payload)`
- verify `msg.sender == relayer contract`
- verify `sourceAddress` and `sourceChain`

View File

@ -12,10 +12,10 @@ contract CoreRelayer is CoreRelayerGovernance {
using BytesLib for bytes;
/**
* @dev `estimateCost` computes the estimated cost of delivering a batch VAA to a target chain.
* @dev `estimateEvmCost` computes the estimated cost of delivering a batch VAA to a target chain.
* it fetches the gas price in native currency for one unit of gas on the target chain
*/
function estimateCost(uint16 chainId, uint256 gasLimit) public view returns (uint256 gasEstimate) {
function estimateEvmCost(uint16 chainId, uint256 gasLimit) public view returns (uint256 gasEstimate) {
return (gasOracle().computeGasCost(chainId, gasLimit + evmDeliverGasOverhead()) + wormhole().messageFee());
}
@ -32,16 +32,8 @@ contract CoreRelayer is CoreRelayerGovernance {
// decode the relay parameters
RelayParameters memory relayParams = decodeRelayParameters(deliveryParams.relayParameters);
require(relayParams.deliveryGasLimit > 0, "invalid deliveryGasLimit in relayParameters");
// Estimate the gas costs of the delivery, and confirm the user sent the right amount of gas.
// The user needs to make sure to send a little extra value to cover the wormhole messageFee on this chain.
uint256 deliveryCostEstimate = estimateCost(deliveryParams.targetChain, relayParams.deliveryGasLimit);
require(
relayParams.nativePayment == deliveryCostEstimate && msg.value == deliveryCostEstimate,
"insufficient fee specified in msg.value"
);
// estimate relay cost and check to see if the user sent enough eth to cover the relay
collectRelayerParameterPayment(relayParams, deliveryParams.targetChain, relayParams.deliveryGasLimit);
// sanity check a few of the values before composing the DeliveryInstructions
require(deliveryParams.targetAddress != bytes32(0), "invalid targetAddress");
@ -76,6 +68,8 @@ contract CoreRelayer is CoreRelayerGovernance {
incrementRedeliveryAttempt(deliveryHash);
RelayParameters memory relayParams = decodeRelayParameters(newRelayerParams);
// estimate relay cost and check to see if the user sent enough eth to cover the relay
collectRelayerParameterPayment(relayParams, vm.emitterChainId, relayParams.deliveryGasLimit);
RedeliveryInstructions memory redeliveryInstructions = RedeliveryInstructions({
@ -98,13 +92,11 @@ contract CoreRelayer is CoreRelayerGovernance {
RelayParameters memory relayParams,
uint16 targetChain,
uint32 targetGasLimit
)
internal
{
) internal {
require(relayParams.deliveryGasLimit > 0, "invalid deliveryGasLimit in relayParameters");
// Estimate the gas costs of the delivery, and confirm the user sent the right amount of gas.
uint256 deliveryCostEstimate = estimateCost(targetChain, targetGasLimit);
uint256 deliveryCostEstimate = estimateEvmCost(targetChain, targetGasLimit);
require(
relayParams.nativePayment == deliveryCostEstimate && msg.value == deliveryCostEstimate,
@ -118,8 +110,7 @@ contract CoreRelayer is CoreRelayerGovernance {
* it locates the DeliveryInstructions VAA in the batch
* it checks to see if the batch has been delivered already
* it verifies that the delivery instructions were generated by a registered relayer contract
* it verifies that the delivered batch contains all VAAs specified in the deliveryList (if it's a partial batch)
* it forwards the array of VAAs in the batch to the target contract by calling the `wormholeReceiver` endpoint
* it forwards the array of VAAs in the batch to the target contract by calling the `receiveWormholeMessages` endpoint
* it records the specified relayer fees for the caller
* it emits a DeliveryStatus message containing the results of the delivery
*/
@ -127,28 +118,37 @@ contract CoreRelayer is CoreRelayerGovernance {
// cache wormhole instance
IWormhole wormhole = wormhole();
// build InternalDelivery struct to reduce local variable count
InternalDeliveryParams memory internalParams;
// parse the batch VAA
IWormhole.VM2 memory batchVM = wormhole.parseBatchVM(targetParams.encodedVM);
internalParams.batchVM = wormhole.parseBatchVM(targetParams.encodedVM);
// cache the deliveryVM
IWormhole.VM memory deliveryVM = batchVM.indexedObservations[targetParams.deliveryIndex].vm3;
IWormhole.VM memory deliveryVM =
parseWormholeObservation(internalParams.batchVM.observations[targetParams.deliveryIndex]);
require(verifyRelayerVM(deliveryVM), "invalid emitter");
// create the VAAId for the delivery VAA
VAAId memory deliveryId = VAAId({emitterAddress: deliveryVM.emitterAddress, sequence: deliveryVM.sequence});
// create the AllowedEmitterSequence for the delivery VAA
internalParams.deliveryId =
AllowedEmitterSequence({emitterAddress: deliveryVM.emitterAddress, sequence: deliveryVM.sequence});
// parse the deliveryVM payload into the DeliveryInstructions struct
DeliveryInstructions memory deliveryInstructions = decodeDeliveryInstructions(deliveryVM.payload);
internalParams.deliveryInstructions = decodeDeliveryInstructions(deliveryVM.payload);
// parse the relayParams
RelayParameters memory relayParams = decodeRelayParameters(deliveryInstructions.relayParameters);
internalParams.relayParams = decodeRelayParameters(internalParams.deliveryInstructions.relayParameters);
// Override the target gas if requested by the relayer
if (targetParams.targetCallGasOverride > relayParams.deliveryGasLimit) {
relayParams.deliveryGasLimit = targetParams.targetCallGasOverride;
// override the target gas if requested by the relayer
if (targetParams.targetCallGasOverride > internalParams.relayParams.deliveryGasLimit) {
internalParams.relayParams.deliveryGasLimit = targetParams.targetCallGasOverride;
}
return _deliver(wormhole, batchVM, deliveryInstructions, deliveryId, relayParams, 0);
// set the remaining values in the InternalDeliveryParams struct
internalParams.deliveryIndex = targetParams.deliveryIndex;
internalParams.deliveryAttempts = 0;
return _deliver(wormhole, internalParams);
}
// TODO: WIP
@ -160,53 +160,58 @@ contract CoreRelayer is CoreRelayerGovernance {
// cache wormhole instance
IWormhole wormhole = wormhole();
// build InternalDeliveryParams struct to reduce local variable count
InternalDeliveryParams memory internalParams;
// parse the batch
IWormhole.VM2 memory batchVM = wormhole.parseBatchVM(targetParams.encodedVM);
internalParams.batchVM = wormhole.parseBatchVM(targetParams.encodedVM);
// cache the deliveryVM
IWormhole.VM memory deliveryVM = batchVM.indexedObservations[targetParams.deliveryIndex].vm3;
IWormhole.VM memory deliveryVM =
parseWormholeObservation(internalParams.batchVM.observations[targetParams.deliveryIndex]);
require(verifyRelayerVM(deliveryVM), "invalid emitter");
// create the VAAId for the delivery VAA
VAAId memory deliveryId = VAAId({emitterAddress: deliveryVM.emitterAddress, sequence: deliveryVM.sequence});
// create the AllowedEmitterSequence for the delivery VAA
internalParams.deliveryId =
AllowedEmitterSequence({emitterAddress: deliveryVM.emitterAddress, sequence: deliveryVM.sequence});
// parse the deliveryVM payload into the DeliveryInstructions struct
DeliveryInstructions memory deliveryInstructions = decodeDeliveryInstructions(deliveryVM.payload);
internalParams.deliveryInstructions = decodeDeliveryInstructions(deliveryVM.payload);
// parse and verify the encoded redelivery message
(IWormhole.VM memory redeliveryVm, bool valid, string memory reason) =
wormhole.parseAndVerifyVM(encodedRedeliveryVm);
require(valid, reason);
require(verifyRelayerVM(redeliveryVm), "invalid emitter");
// parse the RedeliveryInstructions
RedeliveryInstructions memory redeliveryInstructions = parseRedeliveryInstructions(redeliveryVm.payload);
require(redeliveryInstructions.batchHash == batchVM.hash, "invalid batch");
require(redeliveryInstructions.emitterAddress == deliveryVM.emitterAddress, "invalid delivery");
require(redeliveryInstructions.sequence == deliveryVM.sequence, "invalid delivery");
require(redeliveryInstructions.batchHash == internalParams.batchVM.hash, "invalid batch");
require(
redeliveryInstructions.emitterAddress == internalParams.deliveryId.emitterAddress,
"invalid delivery emitter"
);
require(redeliveryInstructions.sequence == internalParams.deliveryId.sequence, "invalid delivery sequence");
// overwrite the DeliveryInstruction's relayParams
deliveryInstructions.relayParameters = redeliveryInstructions.relayParameters;
// override the DeliveryInstruction's relayParams
internalParams.deliveryInstructions.relayParameters = redeliveryInstructions.relayParameters;
// parse the new relayParams
RelayParameters memory relayParams = decodeRelayParameters(redeliveryInstructions.relayParameters);
internalParams.relayParams = decodeRelayParameters(redeliveryInstructions.relayParameters);
// Overwrite the target gas if requested by the relayer
if (targetParams.targetCallGasOverride > relayParams.deliveryGasLimit) {
relayParams.deliveryGasLimit = targetParams.targetCallGasOverride;
// override the target gas if requested by the relayer
if (targetParams.targetCallGasOverride > internalParams.relayParams.deliveryGasLimit) {
internalParams.relayParams.deliveryGasLimit = targetParams.targetCallGasOverride;
}
return _deliver(
wormhole, batchVM, deliveryInstructions, deliveryId, relayParams, redeliveryInstructions.deliveryCount
);
// set the remaining values in the InternalDeliveryParams struct
internalParams.deliveryIndex = targetParams.deliveryIndex;
internalParams.deliveryAttempts = redeliveryInstructions.deliveryCount;
return _deliver(wormhole, internalParams);
}
function _deliver(
IWormhole wormhole,
IWormhole.VM2 memory batchVM,
DeliveryInstructions memory deliveryInstructions,
VAAId memory deliveryId,
RelayParameters memory relayParams,
uint16 attempt
)
function _deliver(IWormhole wormhole, InternalDeliveryParams memory internalParams)
internal
returns (uint64 sequence)
{
@ -214,51 +219,50 @@ contract CoreRelayer is CoreRelayerGovernance {
// Compute the hash(batchHash, deliveryId) and check to see if the batch
// was successfully delivered already. Revert if it was.
bytes32 deliveryHash = keccak256(abi.encodePacked(batchVM.hash, deliveryId.emitterAddress, deliveryId.sequence));
bytes32 deliveryHash = keccak256(
abi.encodePacked(
internalParams.batchVM.hash,
internalParams.deliveryId.emitterAddress,
internalParams.deliveryId.sequence
)
);
require(!isDeliveryCompleted(deliveryHash), "batch already delivered");
// confirm this is the correct destination chain
require(chainId() == deliveryInstructions.targetChain, "targetChain is not this chain");
require(chainId() == internalParams.deliveryInstructions.targetChain, "targetChain is not this chain");
// confirm the correct delivery attempt sequence
uint256 attemptedDeliveryCount = attemptedDeliveryCount(deliveryHash);
require(attempt == attemptedDeliveryCount, "wrong delivery attempt index");
// Check to see if a deliveryList is specified. If not, confirm that all VAAs made it to this contract.
// If a deliveryList is specified, forward the list of VAAs to the receiving contract.
IWormhole.VM[] memory targetVMs;
{
// bypass stack-too-deep
uint256 deliveryListLength = deliveryInstructions.deliveryList.length;
if (deliveryListLength > 0) {
targetVMs =
preparePartialBatchForDelivery(batchVM.indexedObservations, deliveryInstructions.deliveryList);
} else {
targetVMs =
prepareBatchForDelivery(batchVM.indexedObservations, relayParams.maximumBatchSize, deliveryId);
}
}
require(internalParams.deliveryAttempts == attemptedDeliveryCount, "wrong delivery attempt index");
// verify the batchVM before calling the receiver
(bool valid, string memory reason) = wormhole.verifyBatchVM(batchVM, true);
(bool valid, string memory reason) = wormhole.verifyBatchVM(internalParams.batchVM, true);
require(valid, reason);
// remove the deliveryVM from the array of observations in the batch
uint256 numObservations = internalParams.batchVM.observations.length;
bytes[] memory targetObservations = new bytes[](numObservations - 1);
uint256 lastIndex = 0;
for (uint256 i = 0; i < numObservations;) {
if (i != internalParams.deliveryIndex) {
targetObservations[lastIndex] = internalParams.batchVM.observations[i];
unchecked {
lastIndex += 1;
}
}
unchecked {
i += 1;
}
}
// lock the contract to prevent reentrancy
require(!isContractLocked(), "reentrant call");
setContractLock(true);
// process the delivery by calling the wormholeReceiver endpoint on the target contract
(bool success,) = address(uint160(uint256(deliveryInstructions.targetAddress))).call{
gas: relayParams.deliveryGasLimit
}(
abi.encodeWithSignature(
"wormholeReceiver((uint8,uint32,uint32,uint16,bytes32,uint64,uint8,bytes,uint32,(bytes32,bytes32,uint8,uint8)[],bytes32)[],uint16,bytes32,bytes)",
targetVMs,
deliveryInstructions.fromChain,
deliveryInstructions.fromAddress,
deliveryInstructions.payload
)
);
// call the receiveWormholeMessages endpoint on the target contract
(bool success,) = address(uint160(uint256(internalParams.deliveryInstructions.targetAddress))).call{
gas: internalParams.relayParams.deliveryGasLimit
}(abi.encodeWithSignature("receiveWormholeMessages(bytes[])", targetObservations));
// unlock the contract
setContractLock(false);
@ -275,17 +279,19 @@ contract CoreRelayer is CoreRelayerGovernance {
}
// increment the relayer rewards
incrementRelayerRewards(msg.sender, deliveryInstructions.fromChain, relayParams.nativePayment);
incrementRelayerRewards(
msg.sender, internalParams.deliveryInstructions.fromChain, internalParams.relayParams.nativePayment
);
// clear the cache to reduce gas overhead
wormhole.clearBatchCache(batchVM.hashes);
wormhole.clearBatchCache(internalParams.batchVM.hashes);
// emit delivery status message
DeliveryStatus memory status = DeliveryStatus({
payloadID: 2,
batchHash: batchVM.hash,
emitterAddress: deliveryId.emitterAddress,
sequence: deliveryId.sequence,
batchHash: internalParams.batchVM.hash,
emitterAddress: internalParams.deliveryId.emitterAddress,
sequence: internalParams.deliveryId.sequence,
deliveryCount: uint16(attemptedDeliveryCount + 1),
deliverySuccess: success
});
@ -294,7 +300,7 @@ contract CoreRelayer is CoreRelayerGovernance {
}
// TODO: WIP
function rewardPayout(uint16 rewardChain, bytes32 receiver, uint32 nonce)
function collectRewards(uint16 rewardChain, bytes32 receiver, uint32 nonce)
public
payable
returns (uint64 sequence)
@ -321,7 +327,7 @@ contract CoreRelayer is CoreRelayerGovernance {
}
// TODO: WIP
function finaliseRewardPayout(bytes memory encodedVm) public {
function payRewards(bytes memory encodedVm) public {
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole().parseAndVerifyVM(encodedVm);
require(valid, reason);
@ -334,80 +340,6 @@ contract CoreRelayer is CoreRelayerGovernance {
payable(address(uint160(uint256(payout.receiver)))).transfer(payout.amount);
}
function prepareBatchForDelivery(
IWormhole.IndexedObservation[] memory indexedObservations,
uint8 maximumBatchSize,
VAAId memory deliveryId
)
internal
pure
returns (IWormhole.VM[] memory batch)
{
// array that will hold the resulting VAAs
batch = new IWormhole.VM[](maximumBatchSize);
uint8 observationCount = 0;
uint256 observationsLen = indexedObservations.length;
for (uint256 i = 0; i < observationsLen;) {
// parse the VM
IWormhole.VM memory vm = indexedObservations[i].vm3;
// make sure not to include any deliveryVMs
if (vm.emitterAddress != deliveryId.emitterAddress) {
batch[i] = vm;
observationCount += 1;
}
unchecked {
i += 1;
}
}
// confirm that the whole batch was sent
require(observationCount == maximumBatchSize, "invalid batch size");
}
function preparePartialBatchForDelivery(
IWormhole.IndexedObservation[] memory indexedObservations,
VAAId[] memory deliveryList
)
internal
pure
returns (IWormhole.VM[] memory partialBatch)
{
// cache deliveryList length
uint256 deliveryListLen = deliveryList.length;
// final array with the individual VMs
partialBatch = new IWormhole.VM[](deliveryListLen);
// cache observationsLen to save on gas
uint256 observationsLen = indexedObservations.length;
// loop through the delivery list and save VAAs if they are included in the batch
for (uint256 i = 0; i < deliveryListLen;) {
for (uint256 j = 0; j < observationsLen;) {
// parse the VM
IWormhole.VM memory vm = indexedObservations[j].vm3;
// save if there is a match
if (vm.emitterAddress == deliveryList[i].emitterAddress && vm.sequence == deliveryList[i].sequence) {
partialBatch[i] = vm;
break;
}
unchecked {
j += 1;
}
}
unchecked {
i += 1;
}
}
// confirm that the whole batch was sent
require(partialBatch.length == deliveryListLen, "invalid batch size");
}
function verifyRelayerVM(IWormhole.VM memory vm) internal view returns (bool) {
if (registeredRelayer(vm.emitterChainId) == vm.emitterAddress) {
return true;
@ -415,4 +347,8 @@ contract CoreRelayer is CoreRelayerGovernance {
return false;
}
function parseWormholeObservation(bytes memory observation) public view returns (IWormhole.VM memory) {
return wormhole().parseVM(observation);
}
}

View File

@ -11,16 +11,6 @@ import "./CoreRelayerStructs.sol";
contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
using BytesLib for bytes;
function encodeDeliveryList(VAAId[] memory deliveryList) internal pure returns (bytes memory encoded) {
uint256 len = deliveryList.length;
for (uint8 i = 0; i < len;) {
encoded = abi.encodePacked(encoded, deliveryList[i].emitterAddress, deliveryList[i].sequence);
unchecked {
i += 1;
}
}
}
function encodeDeliveryInstructions(DeliveryParameters memory instructions)
internal
view
@ -32,12 +22,6 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
chainId(),
instructions.targetAddress,
instructions.targetChain,
uint16(instructions.payload.length),
instructions.payload,
uint16(instructions.chainPayload.length),
instructions.chainPayload,
uint16(instructions.deliveryList.length),
encodeDeliveryList(instructions.deliveryList),
uint16(instructions.relayParameters.length),
instructions.relayParameters
);
@ -104,40 +88,6 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
instructions.targetChain = encoded.toUint16(index);
index += 2;
// length of payload
uint16 payloadLen = encoded.toUint16(index);
index += 2;
// payload
instructions.payload = encoded.slice(index, payloadLen);
index += payloadLen;
// length of chain payload
uint16 chainPayloadLen = encoded.toUint16(index);
index += 2;
// chain payload
instructions.chainPayload = encoded.slice(index, chainPayloadLen);
index += chainPayloadLen;
// length of the deliveryList
uint16 deliveryListLen = encoded.toUint16(index);
index += 2;
// list of VAAs to deliver
instructions.deliveryList = new VAAId[](deliveryListLen);
for (uint16 i = 0; i < deliveryListLen;) {
instructions.deliveryList[i].emitterAddress = encoded.toBytes32(index);
index += 32;
instructions.deliveryList[i].sequence = encoded.toUint64(index);
index += 8;
unchecked {
i += 1;
}
}
// length of relayParameters
uint16 relayParametersLen = encoded.toUint16(index);
index += 2;
@ -162,10 +112,6 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
relayParams.deliveryGasLimit = encoded.toUint32(index);
index += 4;
// maximum batch size
relayParams.maximumBatchSize = encoded.toUint8(index);
index += 1;
// payment made on the source chain
relayParams.nativePayment = encoded.toUint256(index);
index += 32;

View File

@ -3,11 +3,13 @@
pragma solidity ^0.8.0;
import "../interfaces/IWormhole.sol";
contract CoreRelayerStructs {
struct VAAId {
// VAA emitter address
struct AllowedEmitterSequence {
// wormhole emitter address
bytes32 emitterAddress;
// VAA sequence
// wormhole message sequence
uint64 sequence;
}
@ -23,10 +25,7 @@ contract CoreRelayerStructs {
struct DeliveryParameters {
uint16 targetChain;
bytes32 targetAddress;
bytes payload;
VAAId[] deliveryList;
bytes relayParameters;
bytes chainPayload;
uint32 nonce;
uint8 consistencyLevel;
}
@ -37,12 +36,18 @@ contract CoreRelayerStructs {
uint16 fromChain;
bytes32 targetAddress;
uint16 targetChain;
bytes payload;
bytes chainPayload;
VAAId[] deliveryList;
bytes relayParameters;
}
struct InternalDeliveryParams {
IWormhole.VM2 batchVM;
DeliveryInstructions deliveryInstructions;
AllowedEmitterSequence deliveryId;
RelayParameters relayParams;
uint8 deliveryIndex;
uint16 deliveryAttempts;
}
// TODO: WIP
struct RedeliveryInstructions {
uint8 payloadID; // payloadID = 3;
@ -51,7 +56,7 @@ contract CoreRelayerStructs {
// Point to the original delivery instruction
bytes32 emitterAddress;
uint64 sequence;
// Current deliverycount
// Current number of delivery attempts
uint16 deliveryCount;
// New Relayer-Specific Parameters
bytes relayParameters;
@ -71,8 +76,6 @@ contract CoreRelayerStructs {
uint8 version;
// gasLimit to call the receiving contract with
uint32 deliveryGasLimit;
// maximum batch size
uint8 maximumBatchSize;
// the payment made on the source chain, which is later paid to the relayer
uint256 nativePayment;
}

View File

@ -4,7 +4,7 @@
pragma solidity ^0.8.0;
interface ICoreRelayer {
struct VAAId {
struct AllowedEmitterSequence {
// VAA emitter address
bytes32 emitterAddress;
// VAA sequence
@ -23,10 +23,7 @@ interface ICoreRelayer {
struct DeliveryParameters {
uint16 targetChain;
bytes32 targetAddress;
bytes payload;
VAAId[] deliveryList;
bytes relayParameters;
bytes chainPayload;
uint32 nonce;
uint8 consistencyLevel;
}
@ -37,9 +34,6 @@ interface ICoreRelayer {
uint16 fromChain;
bytes32 targetAddress;
uint16 targetChain;
bytes payload;
bytes chainPayload;
VAAId[] deliveryList;
bytes relayParameters;
}
@ -59,7 +53,7 @@ interface ICoreRelayer {
bytes32 batchHash;
}
function estimateCost(uint16 chainId, uint256 gasLimit) external view returns (uint256 gasEstimate);
function estimateEvmCost(uint16 chainId, uint256 gasLimit) external view returns (uint256 gasEstimate);
function send(DeliveryParameters memory deliveryParams) external payable returns (uint64 sequence);

View File

@ -59,8 +59,8 @@ interface IWormhole {
bytes32[] hashes;
// Computed batch hash - hash(hash(Observation1), hash(Observation2), ...)
bytes32 hash;
// Array of IndexedObservations
IndexedObservation[] indexedObservations;
// Array of observations with prepended version 3
bytes[] observations;
}
event LogMessagePublished(

View File

@ -19,9 +19,6 @@ contract MockRelayerIntegration {
// deployer of this contract
address immutable owner;
// trusted mock integration contracts
mapping(uint16 => bytes32) trustedSenders;
// map that stores payloads from received VAAs
mapping(bytes32 => bytes) verifiedPayloads;
@ -32,7 +29,7 @@ contract MockRelayerIntegration {
}
function estimateRelayCosts(uint16 targetChainId, uint256 targetGasLimit) public view returns (uint256) {
return relayer.estimateCost(targetChainId, targetGasLimit);
return relayer.estimateEvmCost(targetChainId, targetGasLimit);
}
struct RelayerArgs {
@ -41,22 +38,56 @@ contract MockRelayerIntegration {
address targetAddress;
uint32 targetGasLimit;
uint8 consistencyLevel;
uint8[] deliveryListIndices;
}
function doStuff(uint32 batchNonce, bytes[] calldata payload, uint8[] calldata consistencyLevel)
public
payable
returns (uint64[] memory sequences)
{
// cache the payload count to save on gas
uint256 numInputPayloads = payload.length;
require(numInputPayloads == consistencyLevel.length, "invalid input parameters");
// Cache the wormhole fee to save on gas costs. Then make sure the user sent
// enough native asset to cover the cost of delivery (plus the cost of generating wormhole messages).
uint256 wormholeFee = wormhole.messageFee();
require(msg.value >= wormholeFee * (numInputPayloads + 1));
// Create an array to store the wormhole message sequences. Add
// a slot for the relay message sequence.
sequences = new uint64[](numInputPayloads + 1);
// send each wormhole message and save the message sequence
uint256 messageIdx = 0;
bytes memory verifyingPayload = abi.encodePacked(wormhole.chainId(), uint8(numInputPayloads));
for (; messageIdx < numInputPayloads;) {
sequences[messageIdx] = wormhole.publishMessage{value: wormholeFee}(
batchNonce, payload[messageIdx], consistencyLevel[messageIdx]
);
verifyingPayload = abi.encodePacked(verifyingPayload, emitterAddress(), sequences[messageIdx]);
unchecked {
messageIdx += 1;
}
}
// Encode app-relevant info regarding the input payloads.
// All we care about is source chain id and number of input payloads.
sequences[messageIdx] = wormhole.publishMessage{value: wormholeFee}(
batchNonce,
verifyingPayload,
1 // consistencyLevel
);
}
function sendBatchToTargetChain(
bytes[] calldata payload,
uint8[] calldata consistencyLevel,
RelayerArgs memory relayerArgs
)
public
payable
returns (uint64[] memory messageSequences)
{
// cache the payload count to save on gas
uint256 numPayloads = payload.length;
require(numPayloads == consistencyLevel.length, "invalid input parameters");
) public payable returns (uint64 relayerMessageSequence) {
uint64[] memory doStuffSequences = doStuff(relayerArgs.nonce, payload, consistencyLevel);
uint256 numMessageSequences = doStuffSequences.length;
// estimate the cost of sending the batch based on the user specified gas limit
uint256 gasEstimate = estimateRelayCosts(relayerArgs.targetChainId, relayerArgs.targetGasLimit);
@ -64,76 +95,78 @@ contract MockRelayerIntegration {
// Cache the wormhole fee to save on gas costs. Then make sure the user sent
// enough native asset to cover the cost of delivery (plus the cost of generating wormhole messages).
uint256 wormholeFee = wormhole.messageFee();
require(msg.value >= gasEstimate + wormholeFee * numPayloads);
// Create an array to store the wormhole message sequences. Add
// a slot for the relay message sequence.
messageSequences = new uint64[](numPayloads+1);
// create the deliveryList
uint256 deliveryListLength = relayerArgs.deliveryListIndices.length;
ICoreRelayer.VAAId[] memory deliveryList = new ICoreRelayer.VAAId[](deliveryListLength);
// send each wormhole message and save the message sequence
for (uint256 i = 0; i < numPayloads; i++) {
messageSequences[i] =
wormhole.publishMessage{value: wormholeFee}(relayerArgs.nonce, payload[i], consistencyLevel[i]);
// add to delivery list based on the index (if indices are specified)
for (uint256 j = 0; j < deliveryListLength; j++) {
if (i == relayerArgs.deliveryListIndices[j]) {
deliveryList[j] = ICoreRelayer.VAAId({
emitterAddress: bytes32(uint256(uint160(address(this)))),
sequence: messageSequences[i]
});
}
}
}
require(msg.value >= gasEstimate + wormholeFee * (numMessageSequences + 1));
// encode the relay parameters
bytes memory relayParameters =
abi.encodePacked(uint8(1), relayerArgs.targetGasLimit, uint8(numPayloads), gasEstimate);
bytes memory relayParameters = abi.encodePacked(uint8(1), relayerArgs.targetGasLimit, gasEstimate);
// create the relayer params to call the relayer with
ICoreRelayer.DeliveryParameters memory deliveryParams = ICoreRelayer.DeliveryParameters({
targetChain: relayerArgs.targetChainId,
targetAddress: bytes32(uint256(uint160(relayerArgs.targetAddress))),
payload: new bytes(0),
deliveryList: deliveryList,
relayParameters: relayParameters,
chainPayload: new bytes(0),
relayParameters: relayParameters, // REVIEW: rename to encodedRelayParameters?
nonce: relayerArgs.nonce,
consistencyLevel: relayerArgs.consistencyLevel
});
// call the relayer contract and save the sequence.
messageSequences[numPayloads] = relayer.send{value: gasEstimate}(deliveryParams);
return messageSequences;
relayerMessageSequence = relayer.send{value: gasEstimate}(deliveryParams);
}
function wormholeReceiver(
IWormhole.VM[] memory vmList,
uint16 sourceChain,
bytes32 sourceAddress,
bytes memory payload
)
struct EmitterSequence {
bytes32 emitter;
uint64 sequence;
}
function parseVerifyingMessage(bytes memory verifyingObservation, uint256 numObservations)
public
returns (EmitterSequence[] memory emitterSequences)
{
// make sure the caller is a trusted relayer contract
require(msg.sender == address(relayer), "caller not trusted");
(IWormhole.VM memory parsed, bool valid, string memory reason) = wormhole.parseAndVerifyVM(verifyingObservation);
require(valid, reason);
// make sure the sender of the batch is a trusted contract
require(sourceAddress == trustedSender(sourceChain), "batch sender not trusted");
bytes memory payload = parsed.payload;
require(payload.toUint16(0) == parsed.emitterChainId, "source chain != emitterChainId");
require(uint256(payload.toUint8(2)) == numObservations, "incorrect number of observations");
// loop through the array of VMs and store each payload
uint256 vmCount = vmList.length;
for (uint256 i = 0; i < vmCount;) {
(bool valid, string memory reason) = wormhole.verifyVM(vmList[i]);
verifiedPayloads[parsed.hash] = payload;
// TODO: instead of returning VM, return a struct that has info to verify observations
emitterSequences = new EmitterSequence[](numObservations);
uint256 index = 3;
for (uint256 i = 0; i < numObservations;) {
emitterSequences[i].emitter = payload.toBytes32(index);
unchecked {
index += 32;
}
emitterSequences[i].sequence = payload.toUint64(index);
unchecked {
index += 8;
}
unchecked {
i += 1;
}
}
require(payload.length == index, "payload.length != index");
}
function receiveWormholeMessages(bytes[] memory wormholeObservations) public {
// loop through the array of wormhole observations from the batch and store each payload
uint256 numObservations = wormholeObservations.length - 1;
EmitterSequence[] memory emitterSequences =
parseVerifyingMessage(wormholeObservations[numObservations], numObservations);
for (uint256 i = 0; i < numObservations;) {
(IWormhole.VM memory parsed, bool valid, string memory reason) =
wormhole.parseAndVerifyVM(wormholeObservations[i]);
require(valid, reason);
// save the payload from each VAA
verifiedPayloads[vmList[i].hash] = vmList[i].payload;
require(emitterSequences[i].emitter == parsed.emitterAddress, "verifying emitter != emitterAddress");
require(emitterSequences[i].sequence == parsed.sequence, "verifying sequence != sequence");
// save the payload from each wormhole message
verifiedPayloads[parsed.hash] = parsed.payload;
unchecked {
i += 1;
@ -141,17 +174,6 @@ contract MockRelayerIntegration {
}
}
// setters
function registerTrustedSender(uint16 chainId, bytes32 senderAddress) public {
require(msg.sender == owner, "caller must be the owner");
trustedSenders[chainId] = senderAddress;
}
// getters
function trustedSender(uint16 chainId) public view returns (bytes32) {
return trustedSenders[chainId];
}
function getPayload(bytes32 hash) public view returns (bytes memory) {
return verifiedPayloads[hash];
}
@ -160,11 +182,15 @@ contract MockRelayerIntegration {
delete verifiedPayloads[hash];
}
function parseBatchVM(bytes memory encoded) public view returns (IWormhole.VM2 memory) {
function parseWormholeBatch(bytes memory encoded) public view returns (IWormhole.VM2 memory) {
return wormhole.parseBatchVM(encoded);
}
function parseVM(bytes memory encoded) public view returns (IWormhole.VM memory) {
function parseWormholeObservation(bytes memory encoded) public view returns (IWormhole.VM memory) {
return wormhole.parseVM(encoded);
}
function emitterAddress() public view returns (bytes32) {
return bytes32(uint256(uint160(address(this))));
}
}

View File

@ -34,7 +34,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
struct VMParams {
uint32 nonce;
uint8 consistencyLevel;
uint8 deliveryListCount;
uint8 batchCount;
address VMEmitterAddress;
address targetAddress;
}
@ -83,9 +83,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
address wormhole_,
uint16 chainId_,
uint32 evmGasOverhead_
)
public
{
) public {
vm.assume(chainId_ > 0);
vm.assume(gasOracle_ != address(0));
vm.assume(wormhole_ != address(0));
@ -125,7 +123,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
gasOracle.updatePrice(SOURCE_CHAIN_ID, gasParams.sourceGasPrice, gasParams.sourceNativePrice);
// estimate the cost based on the intialized values
uint256 gasEstimate = estimateCost(TARGET_CHAIN_ID, gasParams.targetGasLimit);
uint256 gasEstimate = estimateEvmCost(TARGET_CHAIN_ID, gasParams.targetGasLimit);
// compute the expected output
uint256 expectedGasEstimate = (
@ -196,34 +194,6 @@ contract TestCoreRelayer is CoreRelayer, Test {
assertEq(TARGET_CHAIN_ID, payload.toUint16(index));
index += 2;
// payload length
assertEq(deliveryParams.payload.length, payload.toUint16(index));
index += 2;
// payload
assertEq(deliveryParams.payload, payload.slice(index, deliveryParams.payload.length));
index += deliveryParams.payload.length;
// chainPayload length
assertEq(deliveryParams.chainPayload.length, payload.toUint16(index));
index += 2;
// chainPayload
assertEq(deliveryParams.chainPayload, payload.slice(index, deliveryParams.chainPayload.length));
index += deliveryParams.chainPayload.length;
// deliveryList length
assertEq(deliveryParams.deliveryList.length, payload.toUint16(index));
index += 2;
for (uint16 i = 0; i < deliveryParams.deliveryList.length; i++) {
assertEq(deliveryParams.deliveryList[i].emitterAddress, payload.toBytes32(index));
index += 32;
assertEq(deliveryParams.deliveryList[i].sequence, payload.toUint64(index));
index += 8;
}
// relayParameters length
assertEq(deliveryParams.relayParameters.length, payload.toUint16(index));
index += 2;
@ -237,14 +207,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
// This test confirms that the `send` method generates the correct delivery Instructions payload
// to be delivered on the target chain.
function testSend(
GasParameters memory gasParams,
VMParams memory batchParams,
bytes memory relayPayload,
bytes memory chainPayload
)
public
{
function testSend(GasParameters memory gasParams, VMParams memory batchParams) public {
uint128 halfMaxUint128 = 2 ** (128 / 2) - 1;
vm.assume(gasParams.evmGasOverhead > 0);
vm.assume(gasParams.targetGasLimit > 0);
@ -255,8 +218,6 @@ contract TestCoreRelayer is CoreRelayer, Test {
vm.assume(batchParams.targetAddress != address(0));
vm.assume(batchParams.nonce > 0);
vm.assume(batchParams.consistencyLevel > 0);
vm.assume(relayPayload.length < MAX_UINT16_VALUE);
vm.assume(chainPayload.length < MAX_UINT16_VALUE);
vm.assume(batchParams.VMEmitterAddress != address(0));
// initialize all contracts
@ -267,24 +228,16 @@ contract TestCoreRelayer is CoreRelayer, Test {
gasOracle.updatePrice(SOURCE_CHAIN_ID, gasParams.sourceGasPrice, gasParams.sourceNativePrice);
// estimate the cost based on the intialized values
uint256 gasEstimate = estimateCost(TARGET_CHAIN_ID, gasParams.targetGasLimit);
uint256 gasEstimate = estimateEvmCost(TARGET_CHAIN_ID, gasParams.targetGasLimit);
uint256 wormholeFee = IWormhole(address(wormhole)).messageFee();
// the balance of this contract is the max Uint96
vm.assume(gasEstimate < MAX_UINT96_VALUE - wormholeFee);
// generate a random list of VAAIds for the batch
VAAId[] memory deliveryList = new VAAId[](batchParams.deliveryListCount);
for (uint8 i = 0; i < batchParams.deliveryListCount; i++) {
deliveryList[i] =
VAAId({emitterAddress: bytes32(uint256(uint160(batchParams.VMEmitterAddress))), sequence: uint64(i)});
}
// format the relayParameters
bytes memory relayParameters = abi.encodePacked(
uint8(1), // version
gasParams.targetGasLimit,
uint8(batchParams.deliveryListCount), // no other VAAs for this test
gasEstimate
);
@ -292,10 +245,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
DeliveryParameters memory deliveryParams = DeliveryParameters({
targetChain: TARGET_CHAIN_ID,
targetAddress: bytes32(uint256(uint160(batchParams.targetAddress))),
payload: relayPayload,
deliveryList: deliveryList,
relayParameters: relayParameters,
chainPayload: chainPayload,
nonce: batchParams.nonce,
consistencyLevel: batchParams.consistencyLevel
});
@ -323,12 +273,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
// This tests confirms that the DeliveryInstructions are deserialized correctly
// when calling `deliver` on the target chain.
function testDeliveryInstructionDeserialization(
GasParameters memory gasParams,
VMParams memory batchParams,
bytes memory chainPayload,
bytes memory relayPayload
)
function testDeliveryInstructionDeserialization(GasParameters memory gasParams, VMParams memory batchParams)
public
{
uint128 halfMaxUint128 = 2 ** (128 / 2) - 1;
@ -341,8 +286,6 @@ contract TestCoreRelayer is CoreRelayer, Test {
vm.assume(batchParams.targetAddress != address(0));
vm.assume(batchParams.nonce > 0);
vm.assume(batchParams.consistencyLevel > 0);
vm.assume(relayPayload.length < MAX_UINT16_VALUE);
vm.assume(chainPayload.length < MAX_UINT16_VALUE);
vm.assume(batchParams.VMEmitterAddress != address(0));
// initialize all contracts
@ -353,24 +296,16 @@ contract TestCoreRelayer is CoreRelayer, Test {
gasOracle.updatePrice(SOURCE_CHAIN_ID, gasParams.sourceGasPrice, gasParams.sourceNativePrice);
// estimate the cost based on the intialized values
uint256 gasEstimate = estimateCost(TARGET_CHAIN_ID, gasParams.targetGasLimit);
uint256 gasEstimate = estimateEvmCost(TARGET_CHAIN_ID, gasParams.targetGasLimit);
uint256 wormholeFee = IWormhole(address(wormhole)).messageFee();
// the balance of this contract is the max Uint96
vm.assume(gasEstimate < MAX_UINT96_VALUE - wormholeFee);
// generate a random list of VAAIds for the batch
VAAId[] memory deliveryList = new VAAId[](batchParams.deliveryListCount);
for (uint8 i = 0; i < batchParams.deliveryListCount; i++) {
deliveryList[i] =
VAAId({emitterAddress: bytes32(uint256(uint160(batchParams.VMEmitterAddress))), sequence: uint64(i)});
}
// format the relayParameters
bytes memory relayParameters = abi.encodePacked(
uint8(1), // version
gasParams.targetGasLimit,
uint8(batchParams.deliveryListCount), // no other VAAs for this test
gasEstimate
);
@ -378,10 +313,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
DeliveryParameters memory deliveryParams = DeliveryParameters({
targetChain: TARGET_CHAIN_ID,
targetAddress: bytes32(uint256(uint160(batchParams.targetAddress))),
payload: relayPayload,
deliveryList: deliveryList,
relayParameters: relayParameters,
chainPayload: chainPayload,
nonce: batchParams.nonce,
consistencyLevel: batchParams.consistencyLevel
});
@ -398,15 +330,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
assertEq(SOURCE_CHAIN_ID, instructions.fromChain);
assertEq(deliveryParams.targetAddress, instructions.targetAddress);
assertEq(TARGET_CHAIN_ID, instructions.targetChain);
assertEq(deliveryParams.payload, instructions.payload);
assertEq(deliveryParams.chainPayload, instructions.chainPayload);
assertEq(deliveryParams.relayParameters, instructions.relayParameters);
// check the values in the delivery list
for (uint8 i = 0; i < batchParams.deliveryListCount; i++) {
assertEq(deliveryParams.deliveryList[i].emitterAddress, instructions.deliveryList[i].emitterAddress);
assertEq(deliveryParams.deliveryList[i].sequence, instructions.deliveryList[i].sequence);
}
}
// This tests confirms that the DeliveryInstructions are deserialized correctly
@ -429,24 +353,16 @@ contract TestCoreRelayer is CoreRelayer, Test {
gasOracle.updatePrice(SOURCE_CHAIN_ID, gasParams.sourceGasPrice, gasParams.sourceNativePrice);
// estimate the cost based on the intialized values
uint256 gasEstimate = estimateCost(TARGET_CHAIN_ID, gasParams.targetGasLimit);
uint256 gasEstimate = estimateEvmCost(TARGET_CHAIN_ID, gasParams.targetGasLimit);
uint256 wormholeFee = IWormhole(address(wormhole)).messageFee();
// the balance of this contract is the max Uint96
vm.assume(gasEstimate < MAX_UINT96_VALUE - wormholeFee);
// generate a random list of VAAIds for the batch
VAAId[] memory deliveryList = new VAAId[](batchParams.deliveryListCount);
for (uint8 i = 0; i < batchParams.deliveryListCount; i++) {
deliveryList[i] =
VAAId({emitterAddress: bytes32(uint256(uint160(batchParams.VMEmitterAddress))), sequence: uint64(i)});
}
// format the relayParameters
bytes memory encodedRelayParameters = abi.encodePacked(
uint8(1), // version
gasParams.targetGasLimit,
uint8(batchParams.deliveryListCount), // no other VAAs for this test
gasEstimate
);
@ -456,7 +372,6 @@ contract TestCoreRelayer is CoreRelayer, Test {
// confirm the values were parsed correctly
assertEq(uint8(1), decodedRelayParams.version);
assertEq(gasParams.targetGasLimit, decodedRelayParams.deliveryGasLimit);
assertEq(uint8(batchParams.deliveryListCount), decodedRelayParams.maximumBatchSize);
assertEq(gasEstimate, decodedRelayParams.nativePayment);
}
@ -480,24 +395,16 @@ contract TestCoreRelayer is CoreRelayer, Test {
gasOracle.updatePrice(SOURCE_CHAIN_ID, gasParams.sourceGasPrice, gasParams.sourceNativePrice);
// estimate the cost based on the intialized values
uint256 gasEstimate = estimateCost(TARGET_CHAIN_ID, gasParams.targetGasLimit);
uint256 gasEstimate = estimateEvmCost(TARGET_CHAIN_ID, gasParams.targetGasLimit);
uint256 wormholeFee = IWormhole(address(wormhole)).messageFee();
// the balance of this contract is the max Uint96
vm.assume(gasEstimate < MAX_UINT96_VALUE - wormholeFee);
// generate a random list of VAAIds for the batch
VAAId[] memory deliveryList = new VAAId[](batchParams.deliveryListCount);
for (uint8 i = 0; i < batchParams.deliveryListCount; i++) {
deliveryList[i] =
VAAId({emitterAddress: bytes32(uint256(uint160(batchParams.VMEmitterAddress))), sequence: uint64(i)});
}
// format the relayParameters (add random bytes to the relayerParams)
bytes memory encodedRelayParameters = abi.encodePacked(
uint8(1), // version
gasParams.targetGasLimit,
uint8(batchParams.deliveryListCount), // no other VAAs for this test
gasEstimate,
gasEstimate
);

View File

@ -1,7 +1,7 @@
import { expect } from "chai";
import { ethers } from "ethers";
import { TargetDeliveryParameters, TestResults } from "./helpers/structs";
import { ChainId, tryNativeToHexString } from "@certusone/wormhole-sdk";
import {expect} from "chai";
import {ethers} from "ethers";
import {TargetDeliveryParameters, TestResults} from "./helpers/structs";
import {ChainId, tryNativeToHexString} from "@certusone/wormhole-sdk";
import {
CHAIN_ID_ETH,
CORE_RELAYER_ADDRESS,
@ -9,11 +9,10 @@ import {
RELAYER_DEPLOYER_PRIVATE_KEY,
MOCK_RELAYER_INTEGRATION_ADDRESS,
} from "./helpers/consts";
import { makeContract } from "./helpers/io";
import {makeContract} from "./helpers/io";
import {
getSignedBatchVaaFromReceiptOnEth,
getSignedVaaFromReceiptOnEth,
removeObservationFromBatch,
verifyDeliveryStatusPayload,
} from "./helpers/utils";
@ -72,20 +71,6 @@ describe("Core Relayer Integration Test", () => {
expect(actualRegisteredRelayer).to.equal(expectedRegisteredRelayer);
});
it("Register trusted senders in the mock integration contract", async () => {
// create hex address for the mock contract
const targetMockContractAddressBytes = "0x" + tryNativeToHexString(mockContract.address, TARGET_CHAIN_ID);
// register the trusted sender with the mock integration contract
await mockContract
.registerTrustedSender(TARGET_CHAIN_ID, targetMockContractAddressBytes)
.then((tx: ethers.ContractTransaction) => tx.wait());
// query the mock integration contract to confirm the trusted sender registration worked
const trustedSender = await mockContract.trustedSender(TARGET_CHAIN_ID);
expect(trustedSender).to.equal(targetMockContractAddressBytes);
});
it("Should update EVM deliver gas overhead", async () => {
// the new evmGasOverhead value
const newEvmGasOverhead = 500000;
@ -104,7 +89,7 @@ describe("Core Relayer Integration Test", () => {
it("Should create a batch VAA with a DeliveryInstructions VAA", async () => {
// estimate the cost of submitting the batch on the target chain
fullBatchTest.targetChainGasEstimate = await coreRelayer.estimateCost(TARGET_CHAIN_ID, TARGET_GAS_LIMIT);
fullBatchTest.targetChainGasEstimate = await coreRelayer.estimateEvmCost(TARGET_CHAIN_ID, TARGET_GAS_LIMIT);
// relayer args
fullBatchTest.relayerArgs = {
@ -113,7 +98,6 @@ describe("Core Relayer Integration Test", () => {
targetAddress: TARGET_CONTRACT_ADDRESS,
targetGasLimit: TARGET_GAS_LIMIT,
consistencyLevel: deliveryVAAConsistencyLevel,
deliveryListIndices: [] as number[],
};
// call the mock integration contract to create a batch
@ -133,19 +117,28 @@ describe("Core Relayer Integration Test", () => {
it("Should deserialize and validate the full batch DeliveryInstructions VAA values", async () => {
// parse the batchVM and verify the values
const parsedBatchVM = await mockContract.parseBatchVM(fullBatchTest.signedBatchVM);
const parsedBatchVM = await mockContract.parseWormholeBatch(fullBatchTest.signedBatchVM);
// validate the individual messages
const batchLen = parsedBatchVM.indexedObservations.length;
for (let i = 0; i < batchLen - 1; i++) {
const parsedVM = await parsedBatchVM.indexedObservations[i].vm3;
const observations = parsedBatchVM.observations;
const batchLen = parsedBatchVM.observations.length;
for (let i = 0; i < batchLen - 2; ++i) {
const parsedVM = await mockContract.parseWormholeObservation(observations[i]);
expect(parsedVM.nonce).to.equal(batchNonce);
expect(parsedVM.consistencyLevel).to.equal(batchVAAConsistencyLevels[i]);
expect(parsedVM.payload).to.equal(batchVAAPayloads[i]);
}
// validate the mock integration instructions
const integratorMessage = await mockContract.parseWormholeObservation(observations[batchLen - 2]);
expect(integratorMessage.nonce).to.equal(batchNonce);
expect(integratorMessage.consistencyLevel).to.equal(1);
const integratorMessagePayload = Buffer.from(ethers.utils.arrayify(integratorMessage.payload));
expect(integratorMessagePayload.readUInt16BE(0)).to.equal(2);
expect(integratorMessagePayload.readUInt8(2)).to.equal(batchLen - 2);
// validate the delivery instructions VAA
const deliveryVM = parsedBatchVM.indexedObservations[batchLen - 1].vm3;
const deliveryVM = await mockContract.parseWormholeObservation(observations[batchLen - 1]);
expect(deliveryVM.nonce).to.equal(batchNonce);
expect(deliveryVM.consistencyLevel).to.equal(fullBatchTest.relayerArgs.consistencyLevel);
@ -160,15 +153,11 @@ describe("Core Relayer Integration Test", () => {
"0x" + tryNativeToHexString(TARGET_CONTRACT_ADDRESS, CHAIN_ID_ETH)
);
expect(deliveryInstructions.targetChain).to.equal(TARGET_CHAIN_ID);
expect(deliveryInstructions.payload).to.equal("0x");
expect(deliveryInstructions.chainPayload).to.equal("0x");
expect(deliveryInstructions.deliveryList.length).to.equal(0);
// deserialize the deliveryParameters and confirm the values
const relayParameters = await coreRelayer.decodeRelayParameters(deliveryInstructions.relayParameters);
expect(relayParameters.version).to.equal(1);
expect(relayParameters.deliveryGasLimit).to.equal(TARGET_GAS_LIMIT);
expect(relayParameters.maximumBatchSize).to.equal(batchVAAPayloads.length);
expect(relayParameters.nativePayment.toString()).to.equal(fullBatchTest.targetChainGasEstimate.toString());
});
@ -176,7 +165,7 @@ describe("Core Relayer Integration Test", () => {
// create the TargetDeliveryParameters
const targetDeliveryParams: TargetDeliveryParameters = {
encodedVM: fullBatchTest.signedBatchVM,
deliveryIndex: batchVAAPayloads.length,
deliveryIndex: batchVAAPayloads.length + 1,
targetCallGasOverride: ethers.BigNumber.from(TARGET_GAS_LIMIT),
};
@ -186,10 +175,12 @@ describe("Core Relayer Integration Test", () => {
.then((tx: ethers.ContractTransaction) => tx.wait());
// confirm that the batch VAA payloads were stored in a map in the mock contract
const parsedBatchVM = await mockContract.parseBatchVM(fullBatchTest.signedBatchVM);
const batchLen = parsedBatchVM.indexedObservations.length;
for (let i = 0; i < batchLen - 1; i++) {
const parsedVM = parsedBatchVM.indexedObservations[i].vm3;
const parsedBatchVM = await mockContract.parseWormholeBatch(fullBatchTest.signedBatchVM);
const observations = parsedBatchVM.observations;
const batchLen = observations.length;
for (let i = 0; i < batchLen - 1; ++i) {
const parsedVM = await mockContract.parseWormholeObservation(observations[i]);
// query the contract for the saved payload
const verifiedPayload = await mockContract.getPayload(parsedVM.hash);
@ -213,15 +204,14 @@ describe("Core Relayer Integration Test", () => {
it("Should correctly emit a DeliveryStatus message upon full batch delivery", async () => {
// parse the delivery status VAA payload
const parsedDeliveryStatus = await mockContract.parseVM(fullBatchTest.deliveryStatusVM);
const parsedDeliveryStatus = await mockContract.parseWormholeObservation(fullBatchTest.deliveryStatusVM);
const deliveryStatusPayload = parsedDeliveryStatus.payload;
// parse the batch VAA (need to use the batch hash)
const parsedBatchVM = await mockContract.parseBatchVM(fullBatchTest.signedBatchVM);
const parsedBatchVM = await mockContract.parseWormholeBatch(fullBatchTest.signedBatchVM);
// grab the deliveryVM index, which is the last VM in the batch
const deliveryVMIndex = parsedBatchVM.indexedObservations.length - 1;
const deliveryVM = parsedBatchVM.indexedObservations[deliveryVMIndex].vm3;
const deliveryVM = await mockContract.parseWormholeObservation(parsedBatchVM.observations.at(-1)!);
// expected values in the DeliveryStatus payload
const expectedDeliveryAttempts = 1;
@ -243,198 +233,5 @@ describe("Core Relayer Integration Test", () => {
const queriedRelayerFees = await coreRelayer.relayerRewards(wallet.address, TARGET_CHAIN_ID);
expect(queriedRelayerFees.toString()).to.equal(fullBatchTest.targetChainGasEstimate.toString());
});
it("Should create a batch VAA with a DeliveryInstructions VAA (with a VAAId deliveryList)", async () => {
// estimate the gas of submitting the partial batch on the target chain
partialBatchTest.targetChainGasEstimate = await coreRelayer.estimateCost(TARGET_CHAIN_ID, TARGET_GAS_LIMIT);
// randomly select four indices to put in the delivery list (not including the delivery VAA)
let deliveryListIndices: number[] = [];
for (let i = 0; i < batchVAAPayloads.length; i++) {
deliveryListIndices.push(i);
}
deliveryListIndices = deliveryListIndices.sort(() => 0.5 - Math.random()).slice(4);
// relayer args
partialBatchTest.relayerArgs = {
nonce: batchNonce,
targetChainId: TARGET_CHAIN_ID,
targetAddress: TARGET_CONTRACT_ADDRESS,
targetGasLimit: TARGET_GAS_LIMIT,
consistencyLevel: deliveryVAAConsistencyLevel,
deliveryListIndices: deliveryListIndices,
};
// call the mock integration contract to create a batch
const sendReceipt: ethers.ContractReceipt = await mockContract
.sendBatchToTargetChain(batchVAAPayloads, batchVAAConsistencyLevels, partialBatchTest.relayerArgs, {
value: partialBatchTest.targetChainGasEstimate,
})
.then((tx: ethers.ContractTransaction) => tx.wait());
// fetch the signedBatchVAA
partialBatchTest.signedBatchVM = await getSignedBatchVaaFromReceiptOnEth(
sendReceipt,
SOURCE_CHAIN_ID as ChainId,
0 // guardianSetIndex
);
});
it("Should deserialize and validate the partial batch DeliveryInstructions VAA values", async () => {
// parse the batchVM and verify the values
const parsedBatchVM = await mockContract.parseBatchVM(partialBatchTest.signedBatchVM);
// validate the individual messages
const batchLen = parsedBatchVM.indexedObservations.length;
for (let i = 0; i < batchLen - 1; i++) {
const parsedVM = await parsedBatchVM.indexedObservations[i].vm3;
expect(parsedVM.nonce).to.equal(batchNonce);
expect(parsedVM.consistencyLevel).to.equal(batchVAAConsistencyLevels[i]);
expect(parsedVM.payload).to.equal(batchVAAPayloads[i]);
}
// validate the delivery instructions VAA
const deliveryVM = parsedBatchVM.indexedObservations[batchLen - 1].vm3;
expect(deliveryVM.nonce).to.equal(batchNonce);
expect(deliveryVM.consistencyLevel).to.equal(partialBatchTest.relayerArgs.consistencyLevel);
// deserialize the delivery instruction payload and validate the values
const deliveryInstructions = await coreRelayer.decodeDeliveryInstructions(deliveryVM.payload);
expect(deliveryInstructions.payloadID).to.equal(1);
expect(deliveryInstructions.fromAddress).to.equal(
"0x" + tryNativeToHexString(SOURCE_CONTRACT_ADDRESS, CHAIN_ID_ETH)
);
expect(deliveryInstructions.fromChain).to.equal(SOURCE_CHAIN_ID);
expect(deliveryInstructions.targetAddress).to.equal(
"0x" + tryNativeToHexString(TARGET_CONTRACT_ADDRESS, CHAIN_ID_ETH)
);
expect(deliveryInstructions.targetChain).to.equal(TARGET_CHAIN_ID);
expect(deliveryInstructions.payload).to.equal("0x");
expect(deliveryInstructions.chainPayload).to.equal("0x");
expect(deliveryInstructions.deliveryList.length).to.equal(4);
// deserialize the deliveryParameters and confirm the values
const relayParameters = await coreRelayer.decodeRelayParameters(deliveryInstructions.relayParameters);
expect(relayParameters.version).to.equal(1);
expect(relayParameters.deliveryGasLimit).to.equal(TARGET_GAS_LIMIT);
expect(relayParameters.maximumBatchSize).to.equal(batchVAAPayloads.length);
expect(relayParameters.nativePayment.toString()).to.equal(partialBatchTest.targetChainGasEstimate.toString());
});
it("Should create a partial batch based on the deliveryList in the DeliveryInstructions VAA", async () => {
// parse the batchVM and deserialize the DeliveryInstructions
const parsedBatchVM = await mockContract.parseBatchVM(partialBatchTest.signedBatchVM);
// Delivery VAA starting index (before pruning the batch). It should be the
// last VAA in the batch.
const deliveryVAAIndex: number = batchVAAPayloads.length;
// The delivery VAA is the last message in the batch. Relayers will not know this,
// and will have to iterate through the batch to find the VAAId.
const deliveryInstructionsPayload = await coreRelayer.decodeDeliveryInstructions(
parsedBatchVM.indexedObservations[deliveryVAAIndex].vm3.payload
);
// Loop through the deliveryList in the DeliveryInstructions and find the indices to deliver. Store
// the delivery index to make sure that it is not removed from the batch.
let indicesToKeep: number[] = [deliveryVAAIndex];
for (const deliveryId of deliveryInstructionsPayload.deliveryList) {
for (const indexedObservations of parsedBatchVM.indexedObservations) {
let vm3 = indexedObservations.vm3;
if (
vm3.emitterAddress == deliveryId.emitterAddress &&
vm3.sequence.toString() == deliveryId.sequence.toString()
) {
indicesToKeep.push(indexedObservations.index);
}
}
}
// prune the batch
for (const indexedObservations of parsedBatchVM.indexedObservations) {
const index = indexedObservations.index;
if (!indicesToKeep.includes(index)) {
// prune the batch
partialBatchTest.signedBatchVM = removeObservationFromBatch(index, partialBatchTest.signedBatchVM);
}
}
// confirm that the indices that we care about are still in the VAA
const prunedBatchVM = await mockContract.parseBatchVM(partialBatchTest.signedBatchVM);
for (const indexedObservations of prunedBatchVM.indexedObservations) {
expect(indicesToKeep.includes(indexedObservations.index)).to.be.true;
}
});
it("Should deliver the partial batch VAA and call the wormholeReceiver endpoint on the mock contract", async () => {
// The delivery VAA index has changed since the batch was pruned, find the new delivery VAA index.
// It should still be the last VAA in the batch.
const prunedBatchVM = await mockContract.parseBatchVM(partialBatchTest.signedBatchVM);
const deliveryVAAIndex = prunedBatchVM.indexedObservations.length - 1;
expect(prunedBatchVM.indexedObservations[deliveryVAAIndex].vm3.emitterAddress).to.equal(RELAYER_EMITTER_ADDRESS);
// create the TargetDeliveryParameters
const targetDeliveryParams: TargetDeliveryParameters = {
encodedVM: partialBatchTest.signedBatchVM,
deliveryIndex: deliveryVAAIndex,
targetCallGasOverride: ethers.BigNumber.from(TARGET_GAS_LIMIT),
};
// call the deliver method on the relayer contract
const deliveryReceipt: ethers.ContractReceipt = await coreRelayer
.deliver(targetDeliveryParams)
.then((tx: ethers.ContractTransaction) => tx.wait());
// confirm that the batch VM payloads were stored in a map in the mock contract
const batchLen = prunedBatchVM.indexedObservations.length;
for (let i = 0; i < batchLen - 1; i++) {
const parsedVM = prunedBatchVM.indexedObservations[i].vm3;
// query the contract for the saved payload
const verifiedPayload = await mockContract.getPayload(parsedVM.hash);
expect(verifiedPayload).to.equal(parsedVM.payload);
// clear the payload from storage for future tests
await mockContract.clearPayload(parsedVM.hash).then((tx: ethers.ContractTransaction) => tx.wait());
// confirm that the payload was cleared
const emptyPayload = await mockContract.getPayload(parsedVM.hash);
expect(emptyPayload).to.equal("0x");
}
// fetch and save the delivery status VAA
partialBatchTest.deliveryStatusVM = await getSignedVaaFromReceiptOnEth(
deliveryReceipt,
TARGET_CHAIN_ID,
0 // guardianSetIndex
);
});
it("Should correctly emit a DeliveryStatus message upon partial batch delivery", async () => {
// parse the VM payload
const parsedDeliveryStatus = await mockContract.parseVM(partialBatchTest.deliveryStatusVM);
const deliveryStatusPayload = parsedDeliveryStatus.payload;
// parse the batch VAA (need to use the batch hash)
const parsedBatchVM = await mockContract.parseBatchVM(partialBatchTest.signedBatchVM);
// grab the deliveryVM based, which is the last VM in the batch
const deliveryVMIndex = parsedBatchVM.indexedObservations.length - 1;
const deliveryVM = parsedBatchVM.indexedObservations[deliveryVMIndex].vm3;
// expected values in the DeliveryStatus payload
const expectedDeliveryAttempts = 1;
const expectedSuccessBoolean = 1;
const success = verifyDeliveryStatusPayload(
deliveryStatusPayload,
parsedBatchVM.hash,
RELAYER_EMITTER_ADDRESS,
deliveryVM.sequence,
expectedDeliveryAttempts,
expectedSuccessBoolean
);
expect(success).to.be.true;
});
});
});

View File

@ -6,7 +6,6 @@ export interface RelayerArgs {
targetAddress: string;
targetGasLimit: number;
consistencyLevel: number;
deliveryListIndices: number[];
}
export interface TargetDeliveryParameters {

View File

@ -1,6 +1,6 @@
import {ethers} from "ethers";
import {ChainId, tryNativeToHexString} from "@certusone/wormhole-sdk";
import {WORMHOLE_MESSAGE_EVENT_ABI, GUARDIAN_PRIVATE_KEY} from "./consts";
import { ethers } from "ethers";
import { ChainId, tryNativeToHexString } from "@certusone/wormhole-sdk";
import { WORMHOLE_MESSAGE_EVENT_ABI, GUARDIAN_PRIVATE_KEY } from "./consts";
const elliptic = require("elliptic");
export async function parseWormholeEventsFromReceipt(
@ -84,7 +84,7 @@ export async function getSignedBatchVaaFromReceiptOnEth(
// sign the batchHash
const ec = new elliptic.ec("secp256k1");
const key = ec.keyFromPrivate(GUARDIAN_PRIVATE_KEY);
const signature = key.sign(batchHash.substring(2), {canonical: true});
const signature = key.sign(batchHash.substring(2), { canonical: true });
// create the signature
const packSig = [
@ -153,7 +153,7 @@ export async function getSignedVaaFromReceiptOnEth(
// sign the batchHash
const ec = new elliptic.ec("secp256k1");
const key = ec.keyFromPrivate(GUARDIAN_PRIVATE_KEY);
const signature = key.sign(hash.substring(2), {canonical: true});
const signature = key.sign(hash.substring(2), { canonical: true });
// create the signature
const packSig = [
@ -275,10 +275,10 @@ export function verifyDeliveryStatusPayload(
console.log("Invalid batch hash");
return false;
} else if (emitterAddress != relayerAddress) {
console.log("Invalid emitter address in delivery VAAId");
console.log("Invalid emitter address in delivery AllowedEmitterSequenceedEmitterSequence");
return false;
} else if (sequence != deliverySequence) {
console.log("Invalid emitter address in delivery VAAId");
console.log("Invalid emitter address in delivery AllowedEmitterSequenceedEmitterSequence");
return false;
} else if (deliveryCount != deliveryAttempts) {
console.log("Invalid number of delivery attempts");

2
offchain-relayer/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.relayer.yaml
relay/ethereum/core_relayer/abi.go

View File

@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -exuo pipefail
cat relayer.tilt.yaml > .relayer.yaml
# function for updating or inserting a KEY: value pair in a file.
function upsert_env_file {

View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -exuo pipefail
cd $(dirname $0)/..
jq '.abi' ./ethereum/build/CoreRelayer.sol/CoreRelayer.json | docker run -i --rm -v $(pwd):/root -u $(id -u):$(id -g) ethereum/client-go:alltools-stable abigen --abi - --pkg core_relayer --type CoreRelayer --out /root/offchain-relayer/relay/ethereum/core_relayer/abi.go

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,10 @@
evmRPC: ws://localhost:8545
evmContract: 0x2f3efA6bbDC5fAf4dC1a600765c7B7829e47bE10
evmContract: 0xdeadbeef
evmWormholeChainID: 2
evmNetworkID: 1
evm2RPC: ws://localhost:8546
evm2Contract: 0x2f3efA6bbDC5fAf4dC1a600765c7B7829e47bE10
evm2Contract: 0xdeadbeef
evm2WormholeChainID: 4
evm2NetworkID: 56

View File

@ -10,19 +10,19 @@ import {
ZERO_ADDRESS_BYTES,
TARGET_GAS_LIMIT,
} from "./helpers/consts";
import {RelayerArgs } from "./helpers/structs";
import { RelayerArgs } from "./helpers/structs";
import {
makeCoreRelayerFromForgeBroadcast,
makeGasOracleFromForgeBroadcast,
makeMockRelayerIntegrationFromForgeBroadcast,
resolvePath
resolvePath,
} from "./helpers/utils";
import {
CHAIN_ID_BSC,
CHAIN_ID_ETH,
getSignedBatchVAAWithRetry,
tryNativeToUint8Array,
tryNativeToHexString
tryNativeToHexString,
} from "@certusone/wormhole-sdk";
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
@ -113,24 +113,6 @@ describe("ETH <> BSC Generic Relayer Integration Test", () => {
.registerChain(CHAIN_ID_BSC, tryNativeToUint8Array(bscCoreRelayer.address, CHAIN_ID_BSC))
.then((tx) => tx.wait());
}
// Query the mock relayer integration contracts to see if trusted mock relayer
// integration contracts have been registered.
const trustedSenderOnBsc = await bscRelayerIntegrator.trustedSender(CHAIN_ID_ETH);
const trustedSenderOnEth = await ethRelayerIntegrator.trustedSender(CHAIN_ID_BSC);
// register the trusted mock relayer integration contracts
if (trustedSenderOnBsc == ZERO_ADDRESS_BYTES) {
await bscRelayerIntegrator
.registerTrustedSender(CHAIN_ID_ETH, tryNativeToUint8Array(ethRelayerIntegrator.address, CHAIN_ID_ETH))
.then((tx) => tx.wait());
}
if (trustedSenderOnEth == ZERO_ADDRESS_BYTES) {
await ethRelayerIntegrator
.registerTrustedSender(CHAIN_ID_BSC, tryNativeToUint8Array(bscRelayerIntegrator.address, CHAIN_ID_BSC))
.then((tx) => tx.wait());
}
});
describe("Send from Ethereum and Deliver to BSC", () => {
@ -178,7 +160,6 @@ describe("ETH <> BSC Generic Relayer Integration Test", () => {
targetAddress: bscRelayerIntegrator.address,
targetGasLimit: TARGET_GAS_LIMIT,
consistencyLevel: batchVaaConsistencyLevels[0],
deliveryListIndices: [] as number[], // no indices specified for full batch delivery
};
// call the mock integration contract and send the batch VAA
@ -204,12 +185,12 @@ describe("ETH <> BSC Generic Relayer Integration Test", () => {
it("Wait for off-chain relayer to deliver the batch VAA to BSC", async () => {
// parse the batch VAA
const parsedBatch = await ethRelayerIntegrator.parseBatchVM(batchVaaFromEth);
const parsedBatch = await ethRelayerIntegrator.parseWormholeBatch(batchVaaFromEth);
// Check to see if the batch VAA was delivered by querying the contract
// for the first payload sent in the batch.
let isBatchDelivered: boolean = false;
const targetVm3 = parsedBatch.indexedObservations[0].vm3;
const targetVm3 = await ethRelayerIntegrator.parseWormholeObservation(parsedBatch.observations[0]);
while (!isBatchDelivered) {
// query the contract to see if the batch was delivered
const storedPayload = await bscRelayerIntegrator.getPayload(targetVm3.hash);
@ -219,11 +200,11 @@ describe("ETH <> BSC Generic Relayer Integration Test", () => {
}
// confirm that the remaining payloads are stored in the contract
for (const indexedObservation of parsedBatch.indexedObservations) {
const vm3 = indexedObservation.vm3;
for (const observation of parsedBatch.observations) {
const vm3 = await bscRelayerIntegrator.parseWormholeObservation(observation);
// skip delivery instructions VM
if (vm3.emitterAddress == "0x"+tryNativeToHexString(ethCoreRelayer.address, CHAIN_ID_ETH)) {
if (vm3.emitterAddress == "0x" + tryNativeToHexString(ethCoreRelayer.address, CHAIN_ID_ETH)) {
continue;
}

View File

@ -1,4 +1,4 @@
import {ethers} from "ethers";
import { ethers } from "ethers";
export interface RelayerArgs {
nonce: number;
@ -6,7 +6,6 @@ export interface RelayerArgs {
targetAddress: string;
targetGasLimit: number;
consistencyLevel: number;
deliveryListIndices: number[];
}
export interface TargetDeliveryParameters {