Merge pull request #4 from wormhole-foundation/core-relayer/integration
Clean up send (#2)
This commit is contained in:
commit
684912a8ba
1
Makefile
1
Makefile
|
@ -6,6 +6,7 @@ all: build
|
||||||
build: sdk/node_modules
|
build: sdk/node_modules
|
||||||
cd ethereum && make build
|
cd ethereum && make build
|
||||||
cd sdk && npm run build
|
cd sdk && npm run build
|
||||||
|
cd offchain-relayer && bash regenerate-abi.sh
|
||||||
|
|
||||||
sdk/node_modules:
|
sdk/node_modules:
|
||||||
cd sdk && npm ci
|
cd sdk && npm ci
|
||||||
|
|
|
@ -61,20 +61,10 @@ struct DeliveryInstructions {
|
||||||
// Chain ID of the receiver
|
// Chain ID of the receiver
|
||||||
ToChain uint16
|
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
|
// Length of VAA whitelist
|
||||||
WhitelistLen uint16
|
WhitelistLen uint16
|
||||||
// VAA whitelist
|
// VAA whitelist
|
||||||
Whitelist []VAAId
|
Whitelist []AllowedEmitterSequence
|
||||||
|
|
||||||
// Length of Relayer Parameters
|
// Length of Relayer Parameters
|
||||||
RelayerParamsLen uint16
|
RelayerParamsLen uint16
|
||||||
|
@ -82,7 +72,7 @@ struct DeliveryInstructions {
|
||||||
RelayerParams bytes
|
RelayerParams bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VAAId {
|
struct AllowedEmitterSequence {
|
||||||
// VAA emitter
|
// VAA emitter
|
||||||
EmitterAddress bytes32
|
EmitterAddress bytes32
|
||||||
// VAA sequence
|
// VAA sequence
|
||||||
|
@ -96,7 +86,7 @@ struct DeliveryStatus {
|
||||||
// Hash of the relayed batch
|
// Hash of the relayed batch
|
||||||
BatchHash bytes32
|
BatchHash bytes32
|
||||||
// Delivery
|
// Delivery
|
||||||
Delivery VAAId
|
Delivery AllowedEmitterSequence
|
||||||
|
|
||||||
// Delivery try
|
// Delivery try
|
||||||
DeliveryCount uint16
|
DeliveryCount uint16
|
||||||
|
@ -112,7 +102,7 @@ struct ReDeliveryInstructions {
|
||||||
// Hash of the batch to re-deliver
|
// Hash of the batch to re-deliver
|
||||||
BatchHash bytes32
|
BatchHash bytes32
|
||||||
// Point to the original delivery instruction
|
// Point to the original delivery instruction
|
||||||
OriginalDelivery VAAId
|
OriginalDelivery AllowedEmitterSequence
|
||||||
|
|
||||||
// Current deliverycount
|
// Current deliverycount
|
||||||
DeliveryCount uint16
|
DeliveryCount uint16
|
||||||
|
@ -136,14 +126,14 @@ A relaying system that handles payment on the source chain could have minimal Re
|
||||||
struct MinimalRelayerParamsExample {
|
struct MinimalRelayerParamsExample {
|
||||||
// All payloads should be versioned
|
// All payloads should be versioned
|
||||||
Version uint8
|
Version uint8
|
||||||
// Limit the gas amount forwarded to wormholeReceiver()
|
// Limit the gas amount forwarded to receiveWormholeMessages()
|
||||||
DeliveryGasAmount uint32
|
DeliveryGasAmount uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RelayerParamsExample {
|
struct RelayerParamsExample {
|
||||||
// All payloads should be versioned
|
// All payloads should be versioned
|
||||||
Version uint8
|
Version uint8
|
||||||
// Limit the gas amount forwarded to wormholeReceiver()
|
// Limit the gas amount forwarded to receiveWormholeMessages()
|
||||||
DeliveryGasAmount uint32
|
DeliveryGasAmount uint32
|
||||||
// Limit the max batch size that was paid for -> fail delivery if batch is too big
|
// Limit the max batch size that was paid for -> fail delivery if batch is too big
|
||||||
MaxVAAsInBatch bytes32
|
MaxVAAsInBatch bytes32
|
||||||
|
@ -156,7 +146,7 @@ struct RelayerParamsExample {
|
||||||
|
|
||||||
### Relaying Contract Endpoints
|
### 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
|
- Optionally: handle payment
|
||||||
- Emit `DeliveryInstructions` VAA with specified `targetChain, targetAddress, payload, relayerParams, nonce, VAAWhitelist` and `msg.sender`, the entry-point’s `chainId`
|
- Emit `DeliveryInstructions` VAA with specified `targetChain, targetAddress, payload, relayerParams, nonce, VAAWhitelist` and `msg.sender`, the entry-point’s `chainId`
|
||||||
|
@ -166,19 +156,19 @@ struct RelayerParamsExample {
|
||||||
- Optionally: handle payment
|
- Optionally: handle payment
|
||||||
- Emit `RedeliveryInstructions` VAA with the batch hash & delivery reference and new `relayerParams`
|
- 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
|
- provide a quote for given relayerParams
|
||||||
- Gas Price, Limit should be encoded in `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
|
- 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 emitter of the `DeliveryInstructions` VAA is a known and trusted relaying contract
|
||||||
- Check if the `DeliveryInstructions.ToChain` is the right chain
|
- 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
|
- 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 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
|
- 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
|
- Emit `DeliveryStatus` depending on success or failure of the `receive` call
|
||||||
|
@ -195,7 +185,7 @@ struct RelayerParamsExample {
|
||||||
|
|
||||||
### Receiver Contract Endpoints
|
### 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 `msg.sender == relayer contract`
|
||||||
- verify `sourceAddress` and `sourceChain`
|
- verify `sourceAddress` and `sourceChain`
|
||||||
|
|
|
@ -12,10 +12,10 @@ contract CoreRelayer is CoreRelayerGovernance {
|
||||||
using BytesLib for bytes;
|
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
|
* 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());
|
return (gasOracle().computeGasCost(chainId, gasLimit + evmDeliverGasOverhead()) + wormhole().messageFee());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,16 +32,8 @@ contract CoreRelayer is CoreRelayerGovernance {
|
||||||
// decode the relay parameters
|
// decode the relay parameters
|
||||||
RelayParameters memory relayParams = decodeRelayParameters(deliveryParams.relayParameters);
|
RelayParameters memory relayParams = decodeRelayParameters(deliveryParams.relayParameters);
|
||||||
|
|
||||||
require(relayParams.deliveryGasLimit > 0, "invalid deliveryGasLimit in relayParameters");
|
// estimate relay cost and check to see if the user sent enough eth to cover the relay
|
||||||
|
collectRelayerParameterPayment(relayParams, deliveryParams.targetChain, relayParams.deliveryGasLimit);
|
||||||
// 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"
|
|
||||||
);
|
|
||||||
|
|
||||||
// sanity check a few of the values before composing the DeliveryInstructions
|
// sanity check a few of the values before composing the DeliveryInstructions
|
||||||
require(deliveryParams.targetAddress != bytes32(0), "invalid targetAddress");
|
require(deliveryParams.targetAddress != bytes32(0), "invalid targetAddress");
|
||||||
|
@ -76,6 +68,8 @@ contract CoreRelayer is CoreRelayerGovernance {
|
||||||
incrementRedeliveryAttempt(deliveryHash);
|
incrementRedeliveryAttempt(deliveryHash);
|
||||||
|
|
||||||
RelayParameters memory relayParams = decodeRelayParameters(newRelayerParams);
|
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);
|
collectRelayerParameterPayment(relayParams, vm.emitterChainId, relayParams.deliveryGasLimit);
|
||||||
|
|
||||||
RedeliveryInstructions memory redeliveryInstructions = RedeliveryInstructions({
|
RedeliveryInstructions memory redeliveryInstructions = RedeliveryInstructions({
|
||||||
|
@ -98,13 +92,11 @@ contract CoreRelayer is CoreRelayerGovernance {
|
||||||
RelayParameters memory relayParams,
|
RelayParameters memory relayParams,
|
||||||
uint16 targetChain,
|
uint16 targetChain,
|
||||||
uint32 targetGasLimit
|
uint32 targetGasLimit
|
||||||
)
|
) internal {
|
||||||
internal
|
|
||||||
{
|
|
||||||
require(relayParams.deliveryGasLimit > 0, "invalid deliveryGasLimit in 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.
|
// 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(
|
require(
|
||||||
relayParams.nativePayment == deliveryCostEstimate && msg.value == deliveryCostEstimate,
|
relayParams.nativePayment == deliveryCostEstimate && msg.value == deliveryCostEstimate,
|
||||||
|
@ -118,8 +110,7 @@ contract CoreRelayer is CoreRelayerGovernance {
|
||||||
* it locates the DeliveryInstructions VAA in the batch
|
* it locates the DeliveryInstructions VAA in the batch
|
||||||
* it checks to see if the batch has been delivered already
|
* 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 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 `receiveWormholeMessages` endpoint
|
||||||
* it forwards the array of VAAs in the batch to the target contract by calling the `wormholeReceiver` endpoint
|
|
||||||
* it records the specified relayer fees for the caller
|
* it records the specified relayer fees for the caller
|
||||||
* it emits a DeliveryStatus message containing the results of the delivery
|
* it emits a DeliveryStatus message containing the results of the delivery
|
||||||
*/
|
*/
|
||||||
|
@ -127,28 +118,37 @@ contract CoreRelayer is CoreRelayerGovernance {
|
||||||
// cache wormhole instance
|
// cache wormhole instance
|
||||||
IWormhole wormhole = wormhole();
|
IWormhole wormhole = wormhole();
|
||||||
|
|
||||||
|
// build InternalDelivery struct to reduce local variable count
|
||||||
|
InternalDeliveryParams memory internalParams;
|
||||||
|
|
||||||
// parse the batch VAA
|
// parse the batch VAA
|
||||||
IWormhole.VM2 memory batchVM = wormhole.parseBatchVM(targetParams.encodedVM);
|
internalParams.batchVM = wormhole.parseBatchVM(targetParams.encodedVM);
|
||||||
|
|
||||||
// cache the deliveryVM
|
// 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");
|
require(verifyRelayerVM(deliveryVM), "invalid emitter");
|
||||||
|
|
||||||
// create the VAAId for the delivery VAA
|
// create the AllowedEmitterSequence for the delivery VAA
|
||||||
VAAId memory deliveryId = VAAId({emitterAddress: deliveryVM.emitterAddress, sequence: deliveryVM.sequence});
|
internalParams.deliveryId =
|
||||||
|
AllowedEmitterSequence({emitterAddress: deliveryVM.emitterAddress, sequence: deliveryVM.sequence});
|
||||||
|
|
||||||
// parse the deliveryVM payload into the DeliveryInstructions struct
|
// parse the deliveryVM payload into the DeliveryInstructions struct
|
||||||
DeliveryInstructions memory deliveryInstructions = decodeDeliveryInstructions(deliveryVM.payload);
|
internalParams.deliveryInstructions = decodeDeliveryInstructions(deliveryVM.payload);
|
||||||
|
|
||||||
// parse the relayParams
|
// parse the relayParams
|
||||||
RelayParameters memory relayParams = decodeRelayParameters(deliveryInstructions.relayParameters);
|
internalParams.relayParams = decodeRelayParameters(internalParams.deliveryInstructions.relayParameters);
|
||||||
|
|
||||||
// Override the target gas if requested by the relayer
|
// override the target gas if requested by the relayer
|
||||||
if (targetParams.targetCallGasOverride > relayParams.deliveryGasLimit) {
|
if (targetParams.targetCallGasOverride > internalParams.relayParams.deliveryGasLimit) {
|
||||||
relayParams.deliveryGasLimit = targetParams.targetCallGasOverride;
|
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
|
// TODO: WIP
|
||||||
|
@ -160,53 +160,58 @@ contract CoreRelayer is CoreRelayerGovernance {
|
||||||
// cache wormhole instance
|
// cache wormhole instance
|
||||||
IWormhole wormhole = wormhole();
|
IWormhole wormhole = wormhole();
|
||||||
|
|
||||||
|
// build InternalDeliveryParams struct to reduce local variable count
|
||||||
|
InternalDeliveryParams memory internalParams;
|
||||||
|
|
||||||
// parse the batch
|
// parse the batch
|
||||||
IWormhole.VM2 memory batchVM = wormhole.parseBatchVM(targetParams.encodedVM);
|
internalParams.batchVM = wormhole.parseBatchVM(targetParams.encodedVM);
|
||||||
|
|
||||||
// cache the deliveryVM
|
// 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");
|
require(verifyRelayerVM(deliveryVM), "invalid emitter");
|
||||||
|
|
||||||
// create the VAAId for the delivery VAA
|
// create the AllowedEmitterSequence for the delivery VAA
|
||||||
VAAId memory deliveryId = VAAId({emitterAddress: deliveryVM.emitterAddress, sequence: deliveryVM.sequence});
|
internalParams.deliveryId =
|
||||||
|
AllowedEmitterSequence({emitterAddress: deliveryVM.emitterAddress, sequence: deliveryVM.sequence});
|
||||||
|
|
||||||
// parse the deliveryVM payload into the DeliveryInstructions struct
|
// 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) =
|
(IWormhole.VM memory redeliveryVm, bool valid, string memory reason) =
|
||||||
wormhole.parseAndVerifyVM(encodedRedeliveryVm);
|
wormhole.parseAndVerifyVM(encodedRedeliveryVm);
|
||||||
require(valid, reason);
|
require(valid, reason);
|
||||||
require(verifyRelayerVM(redeliveryVm), "invalid emitter");
|
require(verifyRelayerVM(redeliveryVm), "invalid emitter");
|
||||||
|
|
||||||
|
// parse the RedeliveryInstructions
|
||||||
RedeliveryInstructions memory redeliveryInstructions = parseRedeliveryInstructions(redeliveryVm.payload);
|
RedeliveryInstructions memory redeliveryInstructions = parseRedeliveryInstructions(redeliveryVm.payload);
|
||||||
require(redeliveryInstructions.batchHash == batchVM.hash, "invalid batch");
|
require(redeliveryInstructions.batchHash == internalParams.batchVM.hash, "invalid batch");
|
||||||
require(redeliveryInstructions.emitterAddress == deliveryVM.emitterAddress, "invalid delivery");
|
require(
|
||||||
require(redeliveryInstructions.sequence == deliveryVM.sequence, "invalid delivery");
|
redeliveryInstructions.emitterAddress == internalParams.deliveryId.emitterAddress,
|
||||||
|
"invalid delivery emitter"
|
||||||
|
);
|
||||||
|
require(redeliveryInstructions.sequence == internalParams.deliveryId.sequence, "invalid delivery sequence");
|
||||||
|
|
||||||
// overwrite the DeliveryInstruction's relayParams
|
// override the DeliveryInstruction's relayParams
|
||||||
deliveryInstructions.relayParameters = redeliveryInstructions.relayParameters;
|
internalParams.deliveryInstructions.relayParameters = redeliveryInstructions.relayParameters;
|
||||||
|
|
||||||
// parse the new relayParams
|
// parse the new relayParams
|
||||||
RelayParameters memory relayParams = decodeRelayParameters(redeliveryInstructions.relayParameters);
|
internalParams.relayParams = decodeRelayParameters(redeliveryInstructions.relayParameters);
|
||||||
|
|
||||||
// Overwrite the target gas if requested by the relayer
|
// override the target gas if requested by the relayer
|
||||||
if (targetParams.targetCallGasOverride > relayParams.deliveryGasLimit) {
|
if (targetParams.targetCallGasOverride > internalParams.relayParams.deliveryGasLimit) {
|
||||||
relayParams.deliveryGasLimit = targetParams.targetCallGasOverride;
|
internalParams.relayParams.deliveryGasLimit = targetParams.targetCallGasOverride;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _deliver(
|
// set the remaining values in the InternalDeliveryParams struct
|
||||||
wormhole, batchVM, deliveryInstructions, deliveryId, relayParams, redeliveryInstructions.deliveryCount
|
internalParams.deliveryIndex = targetParams.deliveryIndex;
|
||||||
);
|
internalParams.deliveryAttempts = redeliveryInstructions.deliveryCount;
|
||||||
|
|
||||||
|
return _deliver(wormhole, internalParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _deliver(
|
function _deliver(IWormhole wormhole, InternalDeliveryParams memory internalParams)
|
||||||
IWormhole wormhole,
|
|
||||||
IWormhole.VM2 memory batchVM,
|
|
||||||
DeliveryInstructions memory deliveryInstructions,
|
|
||||||
VAAId memory deliveryId,
|
|
||||||
RelayParameters memory relayParams,
|
|
||||||
uint16 attempt
|
|
||||||
)
|
|
||||||
internal
|
internal
|
||||||
returns (uint64 sequence)
|
returns (uint64 sequence)
|
||||||
{
|
{
|
||||||
|
@ -214,51 +219,50 @@ contract CoreRelayer is CoreRelayerGovernance {
|
||||||
|
|
||||||
// Compute the hash(batchHash, deliveryId) and check to see if the batch
|
// Compute the hash(batchHash, deliveryId) and check to see if the batch
|
||||||
// was successfully delivered already. Revert if it was.
|
// 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");
|
require(!isDeliveryCompleted(deliveryHash), "batch already delivered");
|
||||||
|
|
||||||
// confirm this is the correct destination chain
|
// 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
|
// confirm the correct delivery attempt sequence
|
||||||
uint256 attemptedDeliveryCount = attemptedDeliveryCount(deliveryHash);
|
uint256 attemptedDeliveryCount = attemptedDeliveryCount(deliveryHash);
|
||||||
require(attempt == attemptedDeliveryCount, "wrong delivery attempt index");
|
require(internalParams.deliveryAttempts == 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify the batchVM before calling the receiver
|
// 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);
|
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
|
// lock the contract to prevent reentrancy
|
||||||
require(!isContractLocked(), "reentrant call");
|
require(!isContractLocked(), "reentrant call");
|
||||||
setContractLock(true);
|
setContractLock(true);
|
||||||
|
|
||||||
// process the delivery by calling the wormholeReceiver endpoint on the target contract
|
// call the receiveWormholeMessages endpoint on the target contract
|
||||||
(bool success,) = address(uint160(uint256(deliveryInstructions.targetAddress))).call{
|
(bool success,) = address(uint160(uint256(internalParams.deliveryInstructions.targetAddress))).call{
|
||||||
gas: relayParams.deliveryGasLimit
|
gas: internalParams.relayParams.deliveryGasLimit
|
||||||
}(
|
}(abi.encodeWithSignature("receiveWormholeMessages(bytes[])", targetObservations));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// unlock the contract
|
// unlock the contract
|
||||||
setContractLock(false);
|
setContractLock(false);
|
||||||
|
@ -275,17 +279,19 @@ contract CoreRelayer is CoreRelayerGovernance {
|
||||||
}
|
}
|
||||||
|
|
||||||
// increment the relayer rewards
|
// 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
|
// clear the cache to reduce gas overhead
|
||||||
wormhole.clearBatchCache(batchVM.hashes);
|
wormhole.clearBatchCache(internalParams.batchVM.hashes);
|
||||||
|
|
||||||
// emit delivery status message
|
// emit delivery status message
|
||||||
DeliveryStatus memory status = DeliveryStatus({
|
DeliveryStatus memory status = DeliveryStatus({
|
||||||
payloadID: 2,
|
payloadID: 2,
|
||||||
batchHash: batchVM.hash,
|
batchHash: internalParams.batchVM.hash,
|
||||||
emitterAddress: deliveryId.emitterAddress,
|
emitterAddress: internalParams.deliveryId.emitterAddress,
|
||||||
sequence: deliveryId.sequence,
|
sequence: internalParams.deliveryId.sequence,
|
||||||
deliveryCount: uint16(attemptedDeliveryCount + 1),
|
deliveryCount: uint16(attemptedDeliveryCount + 1),
|
||||||
deliverySuccess: success
|
deliverySuccess: success
|
||||||
});
|
});
|
||||||
|
@ -294,7 +300,7 @@ contract CoreRelayer is CoreRelayerGovernance {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: WIP
|
// TODO: WIP
|
||||||
function rewardPayout(uint16 rewardChain, bytes32 receiver, uint32 nonce)
|
function collectRewards(uint16 rewardChain, bytes32 receiver, uint32 nonce)
|
||||||
public
|
public
|
||||||
payable
|
payable
|
||||||
returns (uint64 sequence)
|
returns (uint64 sequence)
|
||||||
|
@ -321,7 +327,7 @@ contract CoreRelayer is CoreRelayerGovernance {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: WIP
|
// 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);
|
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole().parseAndVerifyVM(encodedVm);
|
||||||
|
|
||||||
require(valid, reason);
|
require(valid, reason);
|
||||||
|
@ -334,80 +340,6 @@ contract CoreRelayer is CoreRelayerGovernance {
|
||||||
payable(address(uint160(uint256(payout.receiver)))).transfer(payout.amount);
|
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) {
|
function verifyRelayerVM(IWormhole.VM memory vm) internal view returns (bool) {
|
||||||
if (registeredRelayer(vm.emitterChainId) == vm.emitterAddress) {
|
if (registeredRelayer(vm.emitterChainId) == vm.emitterAddress) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -415,4 +347,8 @@ contract CoreRelayer is CoreRelayerGovernance {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseWormholeObservation(bytes memory observation) public view returns (IWormhole.VM memory) {
|
||||||
|
return wormhole().parseVM(observation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,16 +11,6 @@ import "./CoreRelayerStructs.sol";
|
||||||
contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
|
contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
|
||||||
using BytesLib for bytes;
|
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)
|
function encodeDeliveryInstructions(DeliveryParameters memory instructions)
|
||||||
internal
|
internal
|
||||||
view
|
view
|
||||||
|
@ -32,12 +22,6 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
|
||||||
chainId(),
|
chainId(),
|
||||||
instructions.targetAddress,
|
instructions.targetAddress,
|
||||||
instructions.targetChain,
|
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),
|
uint16(instructions.relayParameters.length),
|
||||||
instructions.relayParameters
|
instructions.relayParameters
|
||||||
);
|
);
|
||||||
|
@ -104,40 +88,6 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
|
||||||
instructions.targetChain = encoded.toUint16(index);
|
instructions.targetChain = encoded.toUint16(index);
|
||||||
index += 2;
|
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
|
// length of relayParameters
|
||||||
uint16 relayParametersLen = encoded.toUint16(index);
|
uint16 relayParametersLen = encoded.toUint16(index);
|
||||||
index += 2;
|
index += 2;
|
||||||
|
@ -162,10 +112,6 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
|
||||||
relayParams.deliveryGasLimit = encoded.toUint32(index);
|
relayParams.deliveryGasLimit = encoded.toUint32(index);
|
||||||
index += 4;
|
index += 4;
|
||||||
|
|
||||||
// maximum batch size
|
|
||||||
relayParams.maximumBatchSize = encoded.toUint8(index);
|
|
||||||
index += 1;
|
|
||||||
|
|
||||||
// payment made on the source chain
|
// payment made on the source chain
|
||||||
relayParams.nativePayment = encoded.toUint256(index);
|
relayParams.nativePayment = encoded.toUint256(index);
|
||||||
index += 32;
|
index += 32;
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
|
|
||||||
pragma solidity ^0.8.0;
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
import "../interfaces/IWormhole.sol";
|
||||||
|
|
||||||
contract CoreRelayerStructs {
|
contract CoreRelayerStructs {
|
||||||
struct VAAId {
|
struct AllowedEmitterSequence {
|
||||||
// VAA emitter address
|
// wormhole emitter address
|
||||||
bytes32 emitterAddress;
|
bytes32 emitterAddress;
|
||||||
// VAA sequence
|
// wormhole message sequence
|
||||||
uint64 sequence;
|
uint64 sequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,10 +25,7 @@ contract CoreRelayerStructs {
|
||||||
struct DeliveryParameters {
|
struct DeliveryParameters {
|
||||||
uint16 targetChain;
|
uint16 targetChain;
|
||||||
bytes32 targetAddress;
|
bytes32 targetAddress;
|
||||||
bytes payload;
|
|
||||||
VAAId[] deliveryList;
|
|
||||||
bytes relayParameters;
|
bytes relayParameters;
|
||||||
bytes chainPayload;
|
|
||||||
uint32 nonce;
|
uint32 nonce;
|
||||||
uint8 consistencyLevel;
|
uint8 consistencyLevel;
|
||||||
}
|
}
|
||||||
|
@ -37,12 +36,18 @@ contract CoreRelayerStructs {
|
||||||
uint16 fromChain;
|
uint16 fromChain;
|
||||||
bytes32 targetAddress;
|
bytes32 targetAddress;
|
||||||
uint16 targetChain;
|
uint16 targetChain;
|
||||||
bytes payload;
|
|
||||||
bytes chainPayload;
|
|
||||||
VAAId[] deliveryList;
|
|
||||||
bytes relayParameters;
|
bytes relayParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct InternalDeliveryParams {
|
||||||
|
IWormhole.VM2 batchVM;
|
||||||
|
DeliveryInstructions deliveryInstructions;
|
||||||
|
AllowedEmitterSequence deliveryId;
|
||||||
|
RelayParameters relayParams;
|
||||||
|
uint8 deliveryIndex;
|
||||||
|
uint16 deliveryAttempts;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: WIP
|
// TODO: WIP
|
||||||
struct RedeliveryInstructions {
|
struct RedeliveryInstructions {
|
||||||
uint8 payloadID; // payloadID = 3;
|
uint8 payloadID; // payloadID = 3;
|
||||||
|
@ -51,7 +56,7 @@ contract CoreRelayerStructs {
|
||||||
// Point to the original delivery instruction
|
// Point to the original delivery instruction
|
||||||
bytes32 emitterAddress;
|
bytes32 emitterAddress;
|
||||||
uint64 sequence;
|
uint64 sequence;
|
||||||
// Current deliverycount
|
// Current number of delivery attempts
|
||||||
uint16 deliveryCount;
|
uint16 deliveryCount;
|
||||||
// New Relayer-Specific Parameters
|
// New Relayer-Specific Parameters
|
||||||
bytes relayParameters;
|
bytes relayParameters;
|
||||||
|
@ -71,8 +76,6 @@ contract CoreRelayerStructs {
|
||||||
uint8 version;
|
uint8 version;
|
||||||
// gasLimit to call the receiving contract with
|
// gasLimit to call the receiving contract with
|
||||||
uint32 deliveryGasLimit;
|
uint32 deliveryGasLimit;
|
||||||
// maximum batch size
|
|
||||||
uint8 maximumBatchSize;
|
|
||||||
// the payment made on the source chain, which is later paid to the relayer
|
// the payment made on the source chain, which is later paid to the relayer
|
||||||
uint256 nativePayment;
|
uint256 nativePayment;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
pragma solidity ^0.8.0;
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
interface ICoreRelayer {
|
interface ICoreRelayer {
|
||||||
struct VAAId {
|
struct AllowedEmitterSequence {
|
||||||
// VAA emitter address
|
// VAA emitter address
|
||||||
bytes32 emitterAddress;
|
bytes32 emitterAddress;
|
||||||
// VAA sequence
|
// VAA sequence
|
||||||
|
@ -23,10 +23,7 @@ interface ICoreRelayer {
|
||||||
struct DeliveryParameters {
|
struct DeliveryParameters {
|
||||||
uint16 targetChain;
|
uint16 targetChain;
|
||||||
bytes32 targetAddress;
|
bytes32 targetAddress;
|
||||||
bytes payload;
|
|
||||||
VAAId[] deliveryList;
|
|
||||||
bytes relayParameters;
|
bytes relayParameters;
|
||||||
bytes chainPayload;
|
|
||||||
uint32 nonce;
|
uint32 nonce;
|
||||||
uint8 consistencyLevel;
|
uint8 consistencyLevel;
|
||||||
}
|
}
|
||||||
|
@ -37,9 +34,6 @@ interface ICoreRelayer {
|
||||||
uint16 fromChain;
|
uint16 fromChain;
|
||||||
bytes32 targetAddress;
|
bytes32 targetAddress;
|
||||||
uint16 targetChain;
|
uint16 targetChain;
|
||||||
bytes payload;
|
|
||||||
bytes chainPayload;
|
|
||||||
VAAId[] deliveryList;
|
|
||||||
bytes relayParameters;
|
bytes relayParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +53,7 @@ interface ICoreRelayer {
|
||||||
bytes32 batchHash;
|
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);
|
function send(DeliveryParameters memory deliveryParams) external payable returns (uint64 sequence);
|
||||||
|
|
||||||
|
|
|
@ -59,8 +59,8 @@ interface IWormhole {
|
||||||
bytes32[] hashes;
|
bytes32[] hashes;
|
||||||
// Computed batch hash - hash(hash(Observation1), hash(Observation2), ...)
|
// Computed batch hash - hash(hash(Observation1), hash(Observation2), ...)
|
||||||
bytes32 hash;
|
bytes32 hash;
|
||||||
// Array of IndexedObservations
|
// Array of observations with prepended version 3
|
||||||
IndexedObservation[] indexedObservations;
|
bytes[] observations;
|
||||||
}
|
}
|
||||||
|
|
||||||
event LogMessagePublished(
|
event LogMessagePublished(
|
||||||
|
|
|
@ -19,9 +19,6 @@ contract MockRelayerIntegration {
|
||||||
// deployer of this contract
|
// deployer of this contract
|
||||||
address immutable owner;
|
address immutable owner;
|
||||||
|
|
||||||
// trusted mock integration contracts
|
|
||||||
mapping(uint16 => bytes32) trustedSenders;
|
|
||||||
|
|
||||||
// map that stores payloads from received VAAs
|
// map that stores payloads from received VAAs
|
||||||
mapping(bytes32 => bytes) verifiedPayloads;
|
mapping(bytes32 => bytes) verifiedPayloads;
|
||||||
|
|
||||||
|
@ -32,7 +29,7 @@ contract MockRelayerIntegration {
|
||||||
}
|
}
|
||||||
|
|
||||||
function estimateRelayCosts(uint16 targetChainId, uint256 targetGasLimit) public view returns (uint256) {
|
function estimateRelayCosts(uint16 targetChainId, uint256 targetGasLimit) public view returns (uint256) {
|
||||||
return relayer.estimateCost(targetChainId, targetGasLimit);
|
return relayer.estimateEvmCost(targetChainId, targetGasLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RelayerArgs {
|
struct RelayerArgs {
|
||||||
|
@ -41,22 +38,56 @@ contract MockRelayerIntegration {
|
||||||
address targetAddress;
|
address targetAddress;
|
||||||
uint32 targetGasLimit;
|
uint32 targetGasLimit;
|
||||||
uint8 consistencyLevel;
|
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(
|
function sendBatchToTargetChain(
|
||||||
bytes[] calldata payload,
|
bytes[] calldata payload,
|
||||||
uint8[] calldata consistencyLevel,
|
uint8[] calldata consistencyLevel,
|
||||||
RelayerArgs memory relayerArgs
|
RelayerArgs memory relayerArgs
|
||||||
)
|
) public payable returns (uint64 relayerMessageSequence) {
|
||||||
public
|
uint64[] memory doStuffSequences = doStuff(relayerArgs.nonce, payload, consistencyLevel);
|
||||||
payable
|
uint256 numMessageSequences = doStuffSequences.length;
|
||||||
returns (uint64[] memory messageSequences)
|
|
||||||
{
|
|
||||||
// cache the payload count to save on gas
|
|
||||||
uint256 numPayloads = payload.length;
|
|
||||||
|
|
||||||
require(numPayloads == consistencyLevel.length, "invalid input parameters");
|
|
||||||
|
|
||||||
// estimate the cost of sending the batch based on the user specified gas limit
|
// estimate the cost of sending the batch based on the user specified gas limit
|
||||||
uint256 gasEstimate = estimateRelayCosts(relayerArgs.targetChainId, relayerArgs.targetGasLimit);
|
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
|
// 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).
|
// enough native asset to cover the cost of delivery (plus the cost of generating wormhole messages).
|
||||||
uint256 wormholeFee = wormhole.messageFee();
|
uint256 wormholeFee = wormhole.messageFee();
|
||||||
require(msg.value >= gasEstimate + wormholeFee * numPayloads);
|
require(msg.value >= gasEstimate + wormholeFee * (numMessageSequences + 1));
|
||||||
|
|
||||||
// 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]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode the relay parameters
|
// encode the relay parameters
|
||||||
bytes memory relayParameters =
|
bytes memory relayParameters = abi.encodePacked(uint8(1), relayerArgs.targetGasLimit, gasEstimate);
|
||||||
abi.encodePacked(uint8(1), relayerArgs.targetGasLimit, uint8(numPayloads), gasEstimate);
|
|
||||||
|
|
||||||
// create the relayer params to call the relayer with
|
// create the relayer params to call the relayer with
|
||||||
ICoreRelayer.DeliveryParameters memory deliveryParams = ICoreRelayer.DeliveryParameters({
|
ICoreRelayer.DeliveryParameters memory deliveryParams = ICoreRelayer.DeliveryParameters({
|
||||||
targetChain: relayerArgs.targetChainId,
|
targetChain: relayerArgs.targetChainId,
|
||||||
targetAddress: bytes32(uint256(uint160(relayerArgs.targetAddress))),
|
targetAddress: bytes32(uint256(uint160(relayerArgs.targetAddress))),
|
||||||
payload: new bytes(0),
|
relayParameters: relayParameters, // REVIEW: rename to encodedRelayParameters?
|
||||||
deliveryList: deliveryList,
|
|
||||||
relayParameters: relayParameters,
|
|
||||||
chainPayload: new bytes(0),
|
|
||||||
nonce: relayerArgs.nonce,
|
nonce: relayerArgs.nonce,
|
||||||
consistencyLevel: relayerArgs.consistencyLevel
|
consistencyLevel: relayerArgs.consistencyLevel
|
||||||
});
|
});
|
||||||
|
|
||||||
// call the relayer contract and save the sequence.
|
// call the relayer contract and save the sequence.
|
||||||
messageSequences[numPayloads] = relayer.send{value: gasEstimate}(deliveryParams);
|
relayerMessageSequence = relayer.send{value: gasEstimate}(deliveryParams);
|
||||||
|
|
||||||
return messageSequences;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function wormholeReceiver(
|
struct EmitterSequence {
|
||||||
IWormhole.VM[] memory vmList,
|
bytes32 emitter;
|
||||||
uint16 sourceChain,
|
uint64 sequence;
|
||||||
bytes32 sourceAddress,
|
}
|
||||||
bytes memory payload
|
|
||||||
)
|
function parseVerifyingMessage(bytes memory verifyingObservation, uint256 numObservations)
|
||||||
public
|
public
|
||||||
|
returns (EmitterSequence[] memory emitterSequences)
|
||||||
{
|
{
|
||||||
// make sure the caller is a trusted relayer contract
|
(IWormhole.VM memory parsed, bool valid, string memory reason) = wormhole.parseAndVerifyVM(verifyingObservation);
|
||||||
require(msg.sender == address(relayer), "caller not trusted");
|
require(valid, reason);
|
||||||
|
|
||||||
// make sure the sender of the batch is a trusted contract
|
bytes memory payload = parsed.payload;
|
||||||
require(sourceAddress == trustedSender(sourceChain), "batch sender not trusted");
|
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
|
verifiedPayloads[parsed.hash] = payload;
|
||||||
uint256 vmCount = vmList.length;
|
|
||||||
for (uint256 i = 0; i < vmCount;) {
|
// TODO: instead of returning VM, return a struct that has info to verify observations
|
||||||
(bool valid, string memory reason) = wormhole.verifyVM(vmList[i]);
|
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);
|
require(valid, reason);
|
||||||
|
|
||||||
// save the payload from each VAA
|
require(emitterSequences[i].emitter == parsed.emitterAddress, "verifying emitter != emitterAddress");
|
||||||
verifiedPayloads[vmList[i].hash] = vmList[i].payload;
|
require(emitterSequences[i].sequence == parsed.sequence, "verifying sequence != sequence");
|
||||||
|
|
||||||
|
// save the payload from each wormhole message
|
||||||
|
verifiedPayloads[parsed.hash] = parsed.payload;
|
||||||
|
|
||||||
unchecked {
|
unchecked {
|
||||||
i += 1;
|
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) {
|
function getPayload(bytes32 hash) public view returns (bytes memory) {
|
||||||
return verifiedPayloads[hash];
|
return verifiedPayloads[hash];
|
||||||
}
|
}
|
||||||
|
@ -160,11 +182,15 @@ contract MockRelayerIntegration {
|
||||||
delete verifiedPayloads[hash];
|
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);
|
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);
|
return wormhole.parseVM(encoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function emitterAddress() public view returns (bytes32) {
|
||||||
|
return bytes32(uint256(uint160(address(this))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
struct VMParams {
|
struct VMParams {
|
||||||
uint32 nonce;
|
uint32 nonce;
|
||||||
uint8 consistencyLevel;
|
uint8 consistencyLevel;
|
||||||
uint8 deliveryListCount;
|
uint8 batchCount;
|
||||||
address VMEmitterAddress;
|
address VMEmitterAddress;
|
||||||
address targetAddress;
|
address targetAddress;
|
||||||
}
|
}
|
||||||
|
@ -83,9 +83,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
address wormhole_,
|
address wormhole_,
|
||||||
uint16 chainId_,
|
uint16 chainId_,
|
||||||
uint32 evmGasOverhead_
|
uint32 evmGasOverhead_
|
||||||
)
|
) public {
|
||||||
public
|
|
||||||
{
|
|
||||||
vm.assume(chainId_ > 0);
|
vm.assume(chainId_ > 0);
|
||||||
vm.assume(gasOracle_ != address(0));
|
vm.assume(gasOracle_ != address(0));
|
||||||
vm.assume(wormhole_ != 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);
|
gasOracle.updatePrice(SOURCE_CHAIN_ID, gasParams.sourceGasPrice, gasParams.sourceNativePrice);
|
||||||
|
|
||||||
// estimate the cost based on the intialized values
|
// 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
|
// compute the expected output
|
||||||
uint256 expectedGasEstimate = (
|
uint256 expectedGasEstimate = (
|
||||||
|
@ -196,34 +194,6 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
assertEq(TARGET_CHAIN_ID, payload.toUint16(index));
|
assertEq(TARGET_CHAIN_ID, payload.toUint16(index));
|
||||||
index += 2;
|
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
|
// relayParameters length
|
||||||
assertEq(deliveryParams.relayParameters.length, payload.toUint16(index));
|
assertEq(deliveryParams.relayParameters.length, payload.toUint16(index));
|
||||||
index += 2;
|
index += 2;
|
||||||
|
@ -237,14 +207,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
|
|
||||||
// This test confirms that the `send` method generates the correct delivery Instructions payload
|
// This test confirms that the `send` method generates the correct delivery Instructions payload
|
||||||
// to be delivered on the target chain.
|
// to be delivered on the target chain.
|
||||||
function testSend(
|
function testSend(GasParameters memory gasParams, VMParams memory batchParams) public {
|
||||||
GasParameters memory gasParams,
|
|
||||||
VMParams memory batchParams,
|
|
||||||
bytes memory relayPayload,
|
|
||||||
bytes memory chainPayload
|
|
||||||
)
|
|
||||||
public
|
|
||||||
{
|
|
||||||
uint128 halfMaxUint128 = 2 ** (128 / 2) - 1;
|
uint128 halfMaxUint128 = 2 ** (128 / 2) - 1;
|
||||||
vm.assume(gasParams.evmGasOverhead > 0);
|
vm.assume(gasParams.evmGasOverhead > 0);
|
||||||
vm.assume(gasParams.targetGasLimit > 0);
|
vm.assume(gasParams.targetGasLimit > 0);
|
||||||
|
@ -255,8 +218,6 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
vm.assume(batchParams.targetAddress != address(0));
|
vm.assume(batchParams.targetAddress != address(0));
|
||||||
vm.assume(batchParams.nonce > 0);
|
vm.assume(batchParams.nonce > 0);
|
||||||
vm.assume(batchParams.consistencyLevel > 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));
|
vm.assume(batchParams.VMEmitterAddress != address(0));
|
||||||
|
|
||||||
// initialize all contracts
|
// initialize all contracts
|
||||||
|
@ -267,24 +228,16 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
gasOracle.updatePrice(SOURCE_CHAIN_ID, gasParams.sourceGasPrice, gasParams.sourceNativePrice);
|
gasOracle.updatePrice(SOURCE_CHAIN_ID, gasParams.sourceGasPrice, gasParams.sourceNativePrice);
|
||||||
|
|
||||||
// estimate the cost based on the intialized values
|
// 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();
|
uint256 wormholeFee = IWormhole(address(wormhole)).messageFee();
|
||||||
|
|
||||||
// the balance of this contract is the max Uint96
|
// the balance of this contract is the max Uint96
|
||||||
vm.assume(gasEstimate < MAX_UINT96_VALUE - wormholeFee);
|
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
|
// format the relayParameters
|
||||||
bytes memory relayParameters = abi.encodePacked(
|
bytes memory relayParameters = abi.encodePacked(
|
||||||
uint8(1), // version
|
uint8(1), // version
|
||||||
gasParams.targetGasLimit,
|
gasParams.targetGasLimit,
|
||||||
uint8(batchParams.deliveryListCount), // no other VAAs for this test
|
|
||||||
gasEstimate
|
gasEstimate
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -292,10 +245,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
DeliveryParameters memory deliveryParams = DeliveryParameters({
|
DeliveryParameters memory deliveryParams = DeliveryParameters({
|
||||||
targetChain: TARGET_CHAIN_ID,
|
targetChain: TARGET_CHAIN_ID,
|
||||||
targetAddress: bytes32(uint256(uint160(batchParams.targetAddress))),
|
targetAddress: bytes32(uint256(uint160(batchParams.targetAddress))),
|
||||||
payload: relayPayload,
|
|
||||||
deliveryList: deliveryList,
|
|
||||||
relayParameters: relayParameters,
|
relayParameters: relayParameters,
|
||||||
chainPayload: chainPayload,
|
|
||||||
nonce: batchParams.nonce,
|
nonce: batchParams.nonce,
|
||||||
consistencyLevel: batchParams.consistencyLevel
|
consistencyLevel: batchParams.consistencyLevel
|
||||||
});
|
});
|
||||||
|
@ -323,12 +273,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
|
|
||||||
// This tests confirms that the DeliveryInstructions are deserialized correctly
|
// This tests confirms that the DeliveryInstructions are deserialized correctly
|
||||||
// when calling `deliver` on the target chain.
|
// when calling `deliver` on the target chain.
|
||||||
function testDeliveryInstructionDeserialization(
|
function testDeliveryInstructionDeserialization(GasParameters memory gasParams, VMParams memory batchParams)
|
||||||
GasParameters memory gasParams,
|
|
||||||
VMParams memory batchParams,
|
|
||||||
bytes memory chainPayload,
|
|
||||||
bytes memory relayPayload
|
|
||||||
)
|
|
||||||
public
|
public
|
||||||
{
|
{
|
||||||
uint128 halfMaxUint128 = 2 ** (128 / 2) - 1;
|
uint128 halfMaxUint128 = 2 ** (128 / 2) - 1;
|
||||||
|
@ -341,8 +286,6 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
vm.assume(batchParams.targetAddress != address(0));
|
vm.assume(batchParams.targetAddress != address(0));
|
||||||
vm.assume(batchParams.nonce > 0);
|
vm.assume(batchParams.nonce > 0);
|
||||||
vm.assume(batchParams.consistencyLevel > 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));
|
vm.assume(batchParams.VMEmitterAddress != address(0));
|
||||||
|
|
||||||
// initialize all contracts
|
// initialize all contracts
|
||||||
|
@ -353,24 +296,16 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
gasOracle.updatePrice(SOURCE_CHAIN_ID, gasParams.sourceGasPrice, gasParams.sourceNativePrice);
|
gasOracle.updatePrice(SOURCE_CHAIN_ID, gasParams.sourceGasPrice, gasParams.sourceNativePrice);
|
||||||
|
|
||||||
// estimate the cost based on the intialized values
|
// 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();
|
uint256 wormholeFee = IWormhole(address(wormhole)).messageFee();
|
||||||
|
|
||||||
// the balance of this contract is the max Uint96
|
// the balance of this contract is the max Uint96
|
||||||
vm.assume(gasEstimate < MAX_UINT96_VALUE - wormholeFee);
|
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
|
// format the relayParameters
|
||||||
bytes memory relayParameters = abi.encodePacked(
|
bytes memory relayParameters = abi.encodePacked(
|
||||||
uint8(1), // version
|
uint8(1), // version
|
||||||
gasParams.targetGasLimit,
|
gasParams.targetGasLimit,
|
||||||
uint8(batchParams.deliveryListCount), // no other VAAs for this test
|
|
||||||
gasEstimate
|
gasEstimate
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -378,10 +313,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
DeliveryParameters memory deliveryParams = DeliveryParameters({
|
DeliveryParameters memory deliveryParams = DeliveryParameters({
|
||||||
targetChain: TARGET_CHAIN_ID,
|
targetChain: TARGET_CHAIN_ID,
|
||||||
targetAddress: bytes32(uint256(uint160(batchParams.targetAddress))),
|
targetAddress: bytes32(uint256(uint160(batchParams.targetAddress))),
|
||||||
payload: relayPayload,
|
|
||||||
deliveryList: deliveryList,
|
|
||||||
relayParameters: relayParameters,
|
relayParameters: relayParameters,
|
||||||
chainPayload: chainPayload,
|
|
||||||
nonce: batchParams.nonce,
|
nonce: batchParams.nonce,
|
||||||
consistencyLevel: batchParams.consistencyLevel
|
consistencyLevel: batchParams.consistencyLevel
|
||||||
});
|
});
|
||||||
|
@ -398,15 +330,7 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
assertEq(SOURCE_CHAIN_ID, instructions.fromChain);
|
assertEq(SOURCE_CHAIN_ID, instructions.fromChain);
|
||||||
assertEq(deliveryParams.targetAddress, instructions.targetAddress);
|
assertEq(deliveryParams.targetAddress, instructions.targetAddress);
|
||||||
assertEq(TARGET_CHAIN_ID, instructions.targetChain);
|
assertEq(TARGET_CHAIN_ID, instructions.targetChain);
|
||||||
assertEq(deliveryParams.payload, instructions.payload);
|
|
||||||
assertEq(deliveryParams.chainPayload, instructions.chainPayload);
|
|
||||||
assertEq(deliveryParams.relayParameters, instructions.relayParameters);
|
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
|
// 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);
|
gasOracle.updatePrice(SOURCE_CHAIN_ID, gasParams.sourceGasPrice, gasParams.sourceNativePrice);
|
||||||
|
|
||||||
// estimate the cost based on the intialized values
|
// 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();
|
uint256 wormholeFee = IWormhole(address(wormhole)).messageFee();
|
||||||
|
|
||||||
// the balance of this contract is the max Uint96
|
// the balance of this contract is the max Uint96
|
||||||
vm.assume(gasEstimate < MAX_UINT96_VALUE - wormholeFee);
|
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
|
// format the relayParameters
|
||||||
bytes memory encodedRelayParameters = abi.encodePacked(
|
bytes memory encodedRelayParameters = abi.encodePacked(
|
||||||
uint8(1), // version
|
uint8(1), // version
|
||||||
gasParams.targetGasLimit,
|
gasParams.targetGasLimit,
|
||||||
uint8(batchParams.deliveryListCount), // no other VAAs for this test
|
|
||||||
gasEstimate
|
gasEstimate
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -456,7 +372,6 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
// confirm the values were parsed correctly
|
// confirm the values were parsed correctly
|
||||||
assertEq(uint8(1), decodedRelayParams.version);
|
assertEq(uint8(1), decodedRelayParams.version);
|
||||||
assertEq(gasParams.targetGasLimit, decodedRelayParams.deliveryGasLimit);
|
assertEq(gasParams.targetGasLimit, decodedRelayParams.deliveryGasLimit);
|
||||||
assertEq(uint8(batchParams.deliveryListCount), decodedRelayParams.maximumBatchSize);
|
|
||||||
assertEq(gasEstimate, decodedRelayParams.nativePayment);
|
assertEq(gasEstimate, decodedRelayParams.nativePayment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,24 +395,16 @@ contract TestCoreRelayer is CoreRelayer, Test {
|
||||||
gasOracle.updatePrice(SOURCE_CHAIN_ID, gasParams.sourceGasPrice, gasParams.sourceNativePrice);
|
gasOracle.updatePrice(SOURCE_CHAIN_ID, gasParams.sourceGasPrice, gasParams.sourceNativePrice);
|
||||||
|
|
||||||
// estimate the cost based on the intialized values
|
// 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();
|
uint256 wormholeFee = IWormhole(address(wormhole)).messageFee();
|
||||||
|
|
||||||
// the balance of this contract is the max Uint96
|
// the balance of this contract is the max Uint96
|
||||||
vm.assume(gasEstimate < MAX_UINT96_VALUE - wormholeFee);
|
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)
|
// format the relayParameters (add random bytes to the relayerParams)
|
||||||
bytes memory encodedRelayParameters = abi.encodePacked(
|
bytes memory encodedRelayParameters = abi.encodePacked(
|
||||||
uint8(1), // version
|
uint8(1), // version
|
||||||
gasParams.targetGasLimit,
|
gasParams.targetGasLimit,
|
||||||
uint8(batchParams.deliveryListCount), // no other VAAs for this test
|
|
||||||
gasEstimate,
|
gasEstimate,
|
||||||
gasEstimate
|
gasEstimate
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { expect } from "chai";
|
import {expect} from "chai";
|
||||||
import { ethers } from "ethers";
|
import {ethers} from "ethers";
|
||||||
import { TargetDeliveryParameters, TestResults } from "./helpers/structs";
|
import {TargetDeliveryParameters, TestResults} from "./helpers/structs";
|
||||||
import { ChainId, tryNativeToHexString } from "@certusone/wormhole-sdk";
|
import {ChainId, tryNativeToHexString} from "@certusone/wormhole-sdk";
|
||||||
import {
|
import {
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CORE_RELAYER_ADDRESS,
|
CORE_RELAYER_ADDRESS,
|
||||||
|
@ -9,11 +9,10 @@ import {
|
||||||
RELAYER_DEPLOYER_PRIVATE_KEY,
|
RELAYER_DEPLOYER_PRIVATE_KEY,
|
||||||
MOCK_RELAYER_INTEGRATION_ADDRESS,
|
MOCK_RELAYER_INTEGRATION_ADDRESS,
|
||||||
} from "./helpers/consts";
|
} from "./helpers/consts";
|
||||||
import { makeContract } from "./helpers/io";
|
import {makeContract} from "./helpers/io";
|
||||||
import {
|
import {
|
||||||
getSignedBatchVaaFromReceiptOnEth,
|
getSignedBatchVaaFromReceiptOnEth,
|
||||||
getSignedVaaFromReceiptOnEth,
|
getSignedVaaFromReceiptOnEth,
|
||||||
removeObservationFromBatch,
|
|
||||||
verifyDeliveryStatusPayload,
|
verifyDeliveryStatusPayload,
|
||||||
} from "./helpers/utils";
|
} from "./helpers/utils";
|
||||||
|
|
||||||
|
@ -72,20 +71,6 @@ describe("Core Relayer Integration Test", () => {
|
||||||
expect(actualRegisteredRelayer).to.equal(expectedRegisteredRelayer);
|
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 () => {
|
it("Should update EVM deliver gas overhead", async () => {
|
||||||
// the new evmGasOverhead value
|
// the new evmGasOverhead value
|
||||||
const newEvmGasOverhead = 500000;
|
const newEvmGasOverhead = 500000;
|
||||||
|
@ -104,7 +89,7 @@ describe("Core Relayer Integration Test", () => {
|
||||||
|
|
||||||
it("Should create a batch VAA with a DeliveryInstructions VAA", async () => {
|
it("Should create a batch VAA with a DeliveryInstructions VAA", async () => {
|
||||||
// estimate the cost of submitting the batch on the target chain
|
// 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
|
// relayer args
|
||||||
fullBatchTest.relayerArgs = {
|
fullBatchTest.relayerArgs = {
|
||||||
|
@ -113,7 +98,6 @@ describe("Core Relayer Integration Test", () => {
|
||||||
targetAddress: TARGET_CONTRACT_ADDRESS,
|
targetAddress: TARGET_CONTRACT_ADDRESS,
|
||||||
targetGasLimit: TARGET_GAS_LIMIT,
|
targetGasLimit: TARGET_GAS_LIMIT,
|
||||||
consistencyLevel: deliveryVAAConsistencyLevel,
|
consistencyLevel: deliveryVAAConsistencyLevel,
|
||||||
deliveryListIndices: [] as number[],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// call the mock integration contract to create a batch
|
// 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 () => {
|
it("Should deserialize and validate the full batch DeliveryInstructions VAA values", async () => {
|
||||||
// parse the batchVM and verify the values
|
// 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
|
// validate the individual messages
|
||||||
const batchLen = parsedBatchVM.indexedObservations.length;
|
const observations = parsedBatchVM.observations;
|
||||||
for (let i = 0; i < batchLen - 1; i++) {
|
const batchLen = parsedBatchVM.observations.length;
|
||||||
const parsedVM = await parsedBatchVM.indexedObservations[i].vm3;
|
for (let i = 0; i < batchLen - 2; ++i) {
|
||||||
|
const parsedVM = await mockContract.parseWormholeObservation(observations[i]);
|
||||||
expect(parsedVM.nonce).to.equal(batchNonce);
|
expect(parsedVM.nonce).to.equal(batchNonce);
|
||||||
expect(parsedVM.consistencyLevel).to.equal(batchVAAConsistencyLevels[i]);
|
expect(parsedVM.consistencyLevel).to.equal(batchVAAConsistencyLevels[i]);
|
||||||
expect(parsedVM.payload).to.equal(batchVAAPayloads[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
|
// 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.nonce).to.equal(batchNonce);
|
||||||
expect(deliveryVM.consistencyLevel).to.equal(fullBatchTest.relayerArgs.consistencyLevel);
|
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)
|
"0x" + tryNativeToHexString(TARGET_CONTRACT_ADDRESS, CHAIN_ID_ETH)
|
||||||
);
|
);
|
||||||
expect(deliveryInstructions.targetChain).to.equal(TARGET_CHAIN_ID);
|
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
|
// deserialize the deliveryParameters and confirm the values
|
||||||
const relayParameters = await coreRelayer.decodeRelayParameters(deliveryInstructions.relayParameters);
|
const relayParameters = await coreRelayer.decodeRelayParameters(deliveryInstructions.relayParameters);
|
||||||
expect(relayParameters.version).to.equal(1);
|
expect(relayParameters.version).to.equal(1);
|
||||||
expect(relayParameters.deliveryGasLimit).to.equal(TARGET_GAS_LIMIT);
|
expect(relayParameters.deliveryGasLimit).to.equal(TARGET_GAS_LIMIT);
|
||||||
expect(relayParameters.maximumBatchSize).to.equal(batchVAAPayloads.length);
|
|
||||||
expect(relayParameters.nativePayment.toString()).to.equal(fullBatchTest.targetChainGasEstimate.toString());
|
expect(relayParameters.nativePayment.toString()).to.equal(fullBatchTest.targetChainGasEstimate.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -176,7 +165,7 @@ describe("Core Relayer Integration Test", () => {
|
||||||
// create the TargetDeliveryParameters
|
// create the TargetDeliveryParameters
|
||||||
const targetDeliveryParams: TargetDeliveryParameters = {
|
const targetDeliveryParams: TargetDeliveryParameters = {
|
||||||
encodedVM: fullBatchTest.signedBatchVM,
|
encodedVM: fullBatchTest.signedBatchVM,
|
||||||
deliveryIndex: batchVAAPayloads.length,
|
deliveryIndex: batchVAAPayloads.length + 1,
|
||||||
targetCallGasOverride: ethers.BigNumber.from(TARGET_GAS_LIMIT),
|
targetCallGasOverride: ethers.BigNumber.from(TARGET_GAS_LIMIT),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -186,10 +175,12 @@ describe("Core Relayer Integration Test", () => {
|
||||||
.then((tx: ethers.ContractTransaction) => tx.wait());
|
.then((tx: ethers.ContractTransaction) => tx.wait());
|
||||||
|
|
||||||
// confirm that the batch VAA payloads were stored in a map in the mock contract
|
// confirm that the batch VAA payloads were stored in a map in the mock contract
|
||||||
const parsedBatchVM = await mockContract.parseBatchVM(fullBatchTest.signedBatchVM);
|
const parsedBatchVM = await mockContract.parseWormholeBatch(fullBatchTest.signedBatchVM);
|
||||||
const batchLen = parsedBatchVM.indexedObservations.length;
|
|
||||||
for (let i = 0; i < batchLen - 1; i++) {
|
const observations = parsedBatchVM.observations;
|
||||||
const parsedVM = parsedBatchVM.indexedObservations[i].vm3;
|
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
|
// query the contract for the saved payload
|
||||||
const verifiedPayload = await mockContract.getPayload(parsedVM.hash);
|
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 () => {
|
it("Should correctly emit a DeliveryStatus message upon full batch delivery", async () => {
|
||||||
// parse the delivery status VAA payload
|
// parse the delivery status VAA payload
|
||||||
const parsedDeliveryStatus = await mockContract.parseVM(fullBatchTest.deliveryStatusVM);
|
const parsedDeliveryStatus = await mockContract.parseWormholeObservation(fullBatchTest.deliveryStatusVM);
|
||||||
const deliveryStatusPayload = parsedDeliveryStatus.payload;
|
const deliveryStatusPayload = parsedDeliveryStatus.payload;
|
||||||
|
|
||||||
// parse the batch VAA (need to use the batch hash)
|
// 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
|
// grab the deliveryVM index, which is the last VM in the batch
|
||||||
const deliveryVMIndex = parsedBatchVM.indexedObservations.length - 1;
|
const deliveryVM = await mockContract.parseWormholeObservation(parsedBatchVM.observations.at(-1)!);
|
||||||
const deliveryVM = parsedBatchVM.indexedObservations[deliveryVMIndex].vm3;
|
|
||||||
|
|
||||||
// expected values in the DeliveryStatus payload
|
// expected values in the DeliveryStatus payload
|
||||||
const expectedDeliveryAttempts = 1;
|
const expectedDeliveryAttempts = 1;
|
||||||
|
@ -243,198 +233,5 @@ describe("Core Relayer Integration Test", () => {
|
||||||
const queriedRelayerFees = await coreRelayer.relayerRewards(wallet.address, TARGET_CHAIN_ID);
|
const queriedRelayerFees = await coreRelayer.relayerRewards(wallet.address, TARGET_CHAIN_ID);
|
||||||
expect(queriedRelayerFees.toString()).to.equal(fullBatchTest.targetChainGasEstimate.toString());
|
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;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,7 +6,6 @@ export interface RelayerArgs {
|
||||||
targetAddress: string;
|
targetAddress: string;
|
||||||
targetGasLimit: number;
|
targetGasLimit: number;
|
||||||
consistencyLevel: number;
|
consistencyLevel: number;
|
||||||
deliveryListIndices: number[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TargetDeliveryParameters {
|
export interface TargetDeliveryParameters {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {ethers} from "ethers";
|
import { ethers } from "ethers";
|
||||||
import {ChainId, tryNativeToHexString} from "@certusone/wormhole-sdk";
|
import { ChainId, tryNativeToHexString } from "@certusone/wormhole-sdk";
|
||||||
import {WORMHOLE_MESSAGE_EVENT_ABI, GUARDIAN_PRIVATE_KEY} from "./consts";
|
import { WORMHOLE_MESSAGE_EVENT_ABI, GUARDIAN_PRIVATE_KEY } from "./consts";
|
||||||
const elliptic = require("elliptic");
|
const elliptic = require("elliptic");
|
||||||
|
|
||||||
export async function parseWormholeEventsFromReceipt(
|
export async function parseWormholeEventsFromReceipt(
|
||||||
|
@ -84,7 +84,7 @@ export async function getSignedBatchVaaFromReceiptOnEth(
|
||||||
// sign the batchHash
|
// sign the batchHash
|
||||||
const ec = new elliptic.ec("secp256k1");
|
const ec = new elliptic.ec("secp256k1");
|
||||||
const key = ec.keyFromPrivate(GUARDIAN_PRIVATE_KEY);
|
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
|
// create the signature
|
||||||
const packSig = [
|
const packSig = [
|
||||||
|
@ -153,7 +153,7 @@ export async function getSignedVaaFromReceiptOnEth(
|
||||||
// sign the batchHash
|
// sign the batchHash
|
||||||
const ec = new elliptic.ec("secp256k1");
|
const ec = new elliptic.ec("secp256k1");
|
||||||
const key = ec.keyFromPrivate(GUARDIAN_PRIVATE_KEY);
|
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
|
// create the signature
|
||||||
const packSig = [
|
const packSig = [
|
||||||
|
@ -275,10 +275,10 @@ export function verifyDeliveryStatusPayload(
|
||||||
console.log("Invalid batch hash");
|
console.log("Invalid batch hash");
|
||||||
return false;
|
return false;
|
||||||
} else if (emitterAddress != relayerAddress) {
|
} else if (emitterAddress != relayerAddress) {
|
||||||
console.log("Invalid emitter address in delivery VAAId");
|
console.log("Invalid emitter address in delivery AllowedEmitterSequenceedEmitterSequence");
|
||||||
return false;
|
return false;
|
||||||
} else if (sequence != deliverySequence) {
|
} else if (sequence != deliverySequence) {
|
||||||
console.log("Invalid emitter address in delivery VAAId");
|
console.log("Invalid emitter address in delivery AllowedEmitterSequenceedEmitterSequence");
|
||||||
return false;
|
return false;
|
||||||
} else if (deliveryCount != deliveryAttempts) {
|
} else if (deliveryCount != deliveryAttempts) {
|
||||||
console.log("Invalid number of delivery attempts");
|
console.log("Invalid number of delivery attempts");
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
.relayer.yaml
|
||||||
|
relay/ethereum/core_relayer/abi.go
|
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -exuo pipefail
|
set -exuo pipefail
|
||||||
|
|
||||||
|
cat relayer.tilt.yaml > .relayer.yaml
|
||||||
|
|
||||||
# function for updating or inserting a KEY: value pair in a file.
|
# function for updating or inserting a KEY: value pair in a file.
|
||||||
function upsert_env_file {
|
function upsert_env_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
|
@ -1,10 +1,10 @@
|
||||||
evmRPC: ws://localhost:8545
|
evmRPC: ws://localhost:8545
|
||||||
evmContract: 0x2f3efA6bbDC5fAf4dC1a600765c7B7829e47bE10
|
evmContract: 0xdeadbeef
|
||||||
evmWormholeChainID: 2
|
evmWormholeChainID: 2
|
||||||
evmNetworkID: 1
|
evmNetworkID: 1
|
||||||
|
|
||||||
evm2RPC: ws://localhost:8546
|
evm2RPC: ws://localhost:8546
|
||||||
evm2Contract: 0x2f3efA6bbDC5fAf4dC1a600765c7B7829e47bE10
|
evm2Contract: 0xdeadbeef
|
||||||
evm2WormholeChainID: 4
|
evm2WormholeChainID: 4
|
||||||
evm2NetworkID: 56
|
evm2NetworkID: 56
|
||||||
|
|
|
@ -10,19 +10,19 @@ import {
|
||||||
ZERO_ADDRESS_BYTES,
|
ZERO_ADDRESS_BYTES,
|
||||||
TARGET_GAS_LIMIT,
|
TARGET_GAS_LIMIT,
|
||||||
} from "./helpers/consts";
|
} from "./helpers/consts";
|
||||||
import {RelayerArgs } from "./helpers/structs";
|
import { RelayerArgs } from "./helpers/structs";
|
||||||
import {
|
import {
|
||||||
makeCoreRelayerFromForgeBroadcast,
|
makeCoreRelayerFromForgeBroadcast,
|
||||||
makeGasOracleFromForgeBroadcast,
|
makeGasOracleFromForgeBroadcast,
|
||||||
makeMockRelayerIntegrationFromForgeBroadcast,
|
makeMockRelayerIntegrationFromForgeBroadcast,
|
||||||
resolvePath
|
resolvePath,
|
||||||
} from "./helpers/utils";
|
} from "./helpers/utils";
|
||||||
import {
|
import {
|
||||||
CHAIN_ID_BSC,
|
CHAIN_ID_BSC,
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
getSignedBatchVAAWithRetry,
|
getSignedBatchVAAWithRetry,
|
||||||
tryNativeToUint8Array,
|
tryNativeToUint8Array,
|
||||||
tryNativeToHexString
|
tryNativeToHexString,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
|
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))
|
.registerChain(CHAIN_ID_BSC, tryNativeToUint8Array(bscCoreRelayer.address, CHAIN_ID_BSC))
|
||||||
.then((tx) => tx.wait());
|
.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", () => {
|
describe("Send from Ethereum and Deliver to BSC", () => {
|
||||||
|
@ -178,7 +160,6 @@ describe("ETH <> BSC Generic Relayer Integration Test", () => {
|
||||||
targetAddress: bscRelayerIntegrator.address,
|
targetAddress: bscRelayerIntegrator.address,
|
||||||
targetGasLimit: TARGET_GAS_LIMIT,
|
targetGasLimit: TARGET_GAS_LIMIT,
|
||||||
consistencyLevel: batchVaaConsistencyLevels[0],
|
consistencyLevel: batchVaaConsistencyLevels[0],
|
||||||
deliveryListIndices: [] as number[], // no indices specified for full batch delivery
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// call the mock integration contract and send the batch VAA
|
// 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 () => {
|
it("Wait for off-chain relayer to deliver the batch VAA to BSC", async () => {
|
||||||
// parse the batch VAA
|
// 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
|
// Check to see if the batch VAA was delivered by querying the contract
|
||||||
// for the first payload sent in the batch.
|
// for the first payload sent in the batch.
|
||||||
let isBatchDelivered: boolean = false;
|
let isBatchDelivered: boolean = false;
|
||||||
const targetVm3 = parsedBatch.indexedObservations[0].vm3;
|
const targetVm3 = await ethRelayerIntegrator.parseWormholeObservation(parsedBatch.observations[0]);
|
||||||
while (!isBatchDelivered) {
|
while (!isBatchDelivered) {
|
||||||
// query the contract to see if the batch was delivered
|
// query the contract to see if the batch was delivered
|
||||||
const storedPayload = await bscRelayerIntegrator.getPayload(targetVm3.hash);
|
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
|
// confirm that the remaining payloads are stored in the contract
|
||||||
for (const indexedObservation of parsedBatch.indexedObservations) {
|
for (const observation of parsedBatch.observations) {
|
||||||
const vm3 = indexedObservation.vm3;
|
const vm3 = await bscRelayerIntegrator.parseWormholeObservation(observation);
|
||||||
|
|
||||||
// skip delivery instructions VM
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {ethers} from "ethers";
|
import { ethers } from "ethers";
|
||||||
|
|
||||||
export interface RelayerArgs {
|
export interface RelayerArgs {
|
||||||
nonce: number;
|
nonce: number;
|
||||||
|
@ -6,7 +6,6 @@ export interface RelayerArgs {
|
||||||
targetAddress: string;
|
targetAddress: string;
|
||||||
targetGasLimit: number;
|
targetGasLimit: number;
|
||||||
consistencyLevel: number;
|
consistencyLevel: number;
|
||||||
deliveryListIndices: number[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TargetDeliveryParameters {
|
export interface TargetDeliveryParameters {
|
||||||
|
|
Loading…
Reference in New Issue