This commit is contained in:
derpy-duck 2023-03-01 16:36:37 +00:00 committed by chase-45
parent c2117b6ef1
commit dab942c980
3 changed files with 139 additions and 47 deletions

View File

@ -157,56 +157,108 @@ contract CoreRelayerDelivery is CoreRelayerGovernance {
return registeredCoreRelayerContract(vm.emitterChainId) == vm.emitterAddress;
}
/**
* @notice The relay provider calls 'redeliverSingle' to relay messages as described by one redelivery instruction
*
* The instruction specifies, among other things, the target chain (must be this chain), refund address, new maximum refund (in this chain's currency),
* new receiverValue (in this chain's currency), new upper bound on gas
*
* The relay provider must pass in the original signed wormhole messages from the source chain of the same nonce
* (the wormhole message with the original delivery instructions (the delivery VAA) must be one of these messages)
* as well as the wormhole message with the new redelivery instruction (the redelivery VAA)
*
* The messages will be relayed to the target address (with the specified gas limit and receiver value) iff the following checks are met:
* - the redelivery VAA (targetParams.redeliveryVM) has a valid signature
* - the redelivery VAA's emitter is one of these CoreRelayer contracts
* - the original delivery VAA has a valid signature
* - the original delivery VAA's emitter is one of these CoreRelayer contracts
* - the new redelivery instruction's upper bound on gas >= the original instruction's upper bound on gas
* - the new redelivery instruction's 'receiver value' amount >= the original instruction's 'receiver value' amount
* - the redelivery instruction's target chain = this chain
* - the original instruction's target chain = this chain
* - for the redelivery instruction, the relay provider passed in at least [(one wormhole message fee) + instruction.newMaximumRefundTarget + instruction.newReceiverValueTarget] of this chain's currency as msg.value
* - msg.sender is the permissioned address allowed to execute this redelivery instruction
* - the permissioned address allowed to execute this redelivery instruction is the permissioned address allowed to execute the old instruction
*
* @param targetParams struct containing the signed wormhole messages and encoded redelivery instruction (and other information)
*/
function redeliverSingle(IDelivery.TargetRedeliveryByTxHashParamsSingle memory targetParams) public payable {
//cache wormhole
IWormhole wormhole = wormhole();
//validate the redelivery VM
(IWormhole.VM memory redeliveryVM, bool valid, string memory reason) =
wormhole.parseAndVerifyVM(targetParams.redeliveryVM);
// Check that the redelivery VAA (targetParams.redeliveryVM) has a valid signature
if (!valid) {
revert IDelivery.InvalidRedeliveryVM(reason);
}
// Check that the redelivery VAA's emitter is one of these CoreRelayer contracts
if (!verifyRelayerVM(redeliveryVM)) {
// Redelivery VM has an invalid emitter
revert IDelivery.InvalidEmitterInRedeliveryVM();
}
RedeliveryByTxHashInstruction memory redeliveryInstruction = decodeRedeliveryInstruction(redeliveryVM.payload);
//validate the original delivery VM
// Obtain the original delivery VAA
IWormhole.VM memory originalDeliveryVM;
(originalDeliveryVM, valid, reason) =
wormhole.parseAndVerifyVM(targetParams.sourceEncodedVMs[redeliveryInstruction.deliveryIndex]);
// Check that the original delivery VAA has a valid signature
if (!valid) {
revert IDelivery.InvalidVaa(redeliveryInstruction.deliveryIndex, reason);
}
// Check that the original delivery VAA's emitter is one of these CoreRelayer contracts
if (!verifyRelayerVM(originalDeliveryVM)) {
// Original Delivery VM has a invalid emitter
revert IDelivery.InvalidEmitterInOriginalDeliveryVM(redeliveryInstruction.deliveryIndex);
}
DeliveryInstruction memory instruction;
(instruction, valid) = validateRedeliverySingle(
redeliveryInstruction,
decodeDeliveryInstructionsContainer(originalDeliveryVM.payload).instructions[redeliveryInstruction
.multisendIndex]
// Obtain the specific old instruction that was originally executed (and is meant to be re-executed with new parameters)
// specifying the the target chain (must be this chain), target address, refund address, old maximum refund (in this chain's currency),
// old receiverValue (in this chain's currency), old upper bound on gas, and the permissioned address allowed to execute this instruction
DeliveryInstruction memory originalInstruction = decodeDeliveryInstructionsContainer(originalDeliveryVM.payload).instructions[redeliveryInstruction
.multisendIndex];
// Perform the following checks:
// - the new redelivery instruction's upper bound on gas >= the original instruction's upper bound on gas
// - the new redelivery instruction's 'receiver value' amount >= the original instruction's 'receiver value' amount
// - the redelivery instruction's target chain = this chain
// - the original instruction's target chain = this chain
// - for the redelivery instruction, the relay provider passed in at least [(one wormhole message fee) + instruction.newMaximumRefundTarget + instruction.newReceiverValueTarget] of this chain's currency as msg.value
// - msg.sender is the permissioned address allowed to execute this redelivery instruction
// - the permissioned address allowed to execute this redelivery instruction is the permissioned address allowed to execute the old instruction
valid = checkRedeliveryInstructionTarget(
redeliveryInstruction, originalInstruction
);
// Emit an 'Invalid Redelivery' event if one of the following five checks failed:
// - msg.sender is the permissioned address allowed to execute this redelivery instruction
// - the redelivery instruction's target chain = this chain
// - the original instruction's target chain = this chain
// - the new redelivery instruction's 'receiver value' amount >= the original instruction's 'receiver value' amount
// - the new redelivery instruction's upper bound on gas >= the original instruction's upper bound on gas
if (!valid) {
emit Delivery({
recipientContract: fromWormholeFormat(instruction.targetAddress),
sourceChain: redeliveryVM.emitterChainId,
sequence: redeliveryVM.sequence,
deliveryVaaHash: redeliveryVM.hash,
recipientContract: fromWormholeFormat(originalInstruction.targetAddress),
sourceChain: originalDeliveryVM.emitterChainId,
sequence: originalDeliveryVM.sequence,
deliveryVaaHash: originalDeliveryVM.hash,
status: DeliveryStatus.INVALID_REDELIVERY
});
pay(targetParams.relayerRefundAddress, msg.value);
return;
}
// Replace maximumRefund, receiverValue, and the gasLimit on the original request
originalInstruction.maximumRefundTarget = redeliveryInstruction.newMaximumRefundTarget;
originalInstruction.receiverValueTarget = redeliveryInstruction.newReceiverValueTarget;
originalInstruction.executionParameters = redeliveryInstruction.executionParameters;
_executeDelivery(
instruction,
originalInstruction,
targetParams.sourceEncodedVMs,
originalDeliveryVM.hash,
targetParams.relayerRefundAddress,
@ -215,20 +267,33 @@ contract CoreRelayerDelivery is CoreRelayerGovernance {
);
}
function validateRedeliverySingle(
/**
* Check that:
* - the new redelivery instruction's upper bound on gas >= the original instruction's upper bound on gas
* - the new redelivery instruction's 'receiver value' amount >= the original instruction's 'receiver value' amount
* - the redelivery instruction's target chain = this chain
* - the original instruction's target chain = this chain
* - for the redelivery instruction, the relay provider passed in at least [(one wormhole message fee) + instruction.newMaximumRefundTarget + instruction.newReceiverValueTarget] of this chain's currency as msg.value
* - msg.sender is the permissioned address allowed to execute this redelivery instruction
* - the permissioned address allowed to execute this redelivery instruction is the permissioned address allowed to execute the old instruction
* @param redeliveryInstruction redelivery instruction
* @param originalInstruction old instruction
*/
function checkRedeliveryInstructionTarget(
RedeliveryByTxHashInstruction memory redeliveryInstruction,
DeliveryInstruction memory originalInstruction
) internal view returns (DeliveryInstruction memory deliveryInstruction, bool isValid) {
// All the same checks as delivery single, with a couple additional
) internal view returns (bool isValid) {
// The same relay provider must be specified when doing a single VAA redeliver.
address providerAddress = fromWormholeFormat(redeliveryInstruction.executionParameters.providerDeliveryAddress);
if (providerAddress != fromWormholeFormat(originalInstruction.executionParameters.providerDeliveryAddress)) {
// Check that the permissioned address allowed to execute this redelivery instruction is the permissioned address allowed to execute the old instruction
if ((providerAddress != fromWormholeFormat(originalInstruction.executionParameters.providerDeliveryAddress))) {
revert IDelivery.MismatchingRelayProvidersInRedelivery();
}
uint256 wormholeMessageFee = wormhole().messageFee();
// relayer must have covered the necessary funds
// Check that for the redelivery instruction, the relay provider passed in at least [(one wormhole message fee) + instruction.newMaximumRefundTarget + instruction.newReceiverValueTarget] of this chain's currency as msg.value
if (
msg.value
< redeliveryInstruction.newMaximumRefundTarget + redeliveryInstruction.newReceiverValueTarget
@ -238,57 +303,64 @@ contract CoreRelayerDelivery is CoreRelayerGovernance {
}
uint16 whChainId = chainId();
// msg.sender must be the provider
// "Relay provider differed from the specified address");
// Check that msg.sender is the permissioned address allowed to execute this redelivery instruction
isValid = msg.sender == providerAddress
// redelivery must target this chain
// "Redelivery request does not target this chain.");
// Check that the redelivery instruction's target chain = this chain
&& whChainId == redeliveryInstruction.targetChain
// original delivery must target this chain
// "Original delivery request did not target this chain.");
// Check that the original instruction's target chain = this chain
&& whChainId == originalInstruction.targetChain
// gasLimit & receiverValue must be at least as large as the initial delivery
// "New receiver value is smaller than the original"
// Check that the new redelivery instruction's 'receiver value' amount >= the original instruction's 'receiver value' amount
&& originalInstruction.receiverValueTarget <= redeliveryInstruction.newReceiverValueTarget
// "New gasLimit is smaller than the original"
// Check that the new redelivery instruction's upper bound on gas >= the original instruction's upper bound on gas
&& originalInstruction.executionParameters.gasLimit <= redeliveryInstruction.executionParameters.gasLimit;
// Overwrite compute budget and application budget on the original request and proceed.
deliveryInstruction = originalInstruction;
deliveryInstruction.maximumRefundTarget = redeliveryInstruction.newMaximumRefundTarget;
deliveryInstruction.receiverValueTarget = redeliveryInstruction.newReceiverValueTarget;
deliveryInstruction.executionParameters = redeliveryInstruction.executionParameters;
}
/**
* @notice The relay provider calls 'deliverSingle' to relay messages as described by one delivery instruction
*
* The instruction specifies the target chain (must be this chain), target address, refund address, maximum refund (in this chain's currency),
* receiverValue (in this chain's currency), upper bound on gas, and the permissioned address allowed to execute this instruction
* receiver value (in this chain's currency), upper bound on gas, and the permissioned address allowed to execute this instruction
*
* The relay provider must pass in the signed wormhole messages from the source chain of the same nonce
* The relay provider must pass in the signed wormhole messages (VAAs) from the source chain of the same nonce
* (the wormhole message with the delivery instructions (the delivery VAA) must be one of these messages)
* as well as identify which of these messages is the delivery VAA and which of the many instructions in the multichainSend container is meant to be executed
*
* The messages will be relayed to the target address (with the specified gas limit and receiver value) iff the following checks are met:
* - the delivery VAA has a valid signature
* - the delivery VAA's emitter is one of these CoreRelayer contracts
* - the delivery instruction container in the delivery VAA was fully funded
* - the instruction's target chain is this chain
* - the relay provider passed in at least [(one wormhole message fee) + instruction.maximumRefundTarget + instruction.receiverValueTarget] of this chain's currency as msg.value
* - msg.sender is the permissioned address allowed to execute this instruction
*
* @param targetParams struct containing the signed wormhole messages and encoded delivery instruction container (and other information)
*/
function deliverSingle(IDelivery.TargetDeliveryParametersSingle memory targetParams) public payable {
IWormhole wormhole = wormhole();
// Verify the signature and emitter of the wormhole message containing the delivery instructions container
// Obtain the delivery VAA
(IWormhole.VM memory deliveryVM, bool valid, string memory reason) =
wormhole.parseAndVerifyVM(targetParams.encodedVMs[targetParams.deliveryIndex]);
// Check that the delivery VAA has a valid signature
if (!valid) {
revert IDelivery.InvalidVaa(targetParams.deliveryIndex, reason);
}
// Check that the delivery VAA's emitter is one of these CoreRelayer contracts
if (!verifyRelayerVM(deliveryVM)) {
revert IDelivery.InvalidEmitter();
}
DeliveryInstructionsContainer memory container = decodeDeliveryInstructionsContainer(deliveryVM.payload);
// Check that the delivery instructions container was fully funded
// Check that the delivery instruction container in the delivery VAA was fully funded
if (!container.sufficientlyFunded) {
revert IDelivery.SendNotSufficientlyFunded();
}
@ -298,14 +370,14 @@ contract CoreRelayerDelivery is CoreRelayerGovernance {
// receiverValue (in this chain's currency), upper bound on gas, and the permissioned address allowed to execute this instruction
DeliveryInstruction memory deliveryInstruction = container.instructions[targetParams.multisendIndex];
// make sure that msg.sender is the permissioned address allowed to execute this instruction
// Check that msg.sender is the permissioned address allowed to execute this instruction
if (fromWormholeFormat(deliveryInstruction.executionParameters.providerDeliveryAddress) != msg.sender) {
revert IDelivery.UnexpectedRelayer();
}
uint256 wormholeMessageFee = wormhole.messageFee();
// make sure relayer passed in (as msg.value) sufficient funds
// Check that the relay provider passed in at least [(one wormhole message fee) + instruction.maximumRefund + instruction.receiverValue] of this chain's currency as msg.value
if (
msg.value
< deliveryInstruction.maximumRefundTarget + deliveryInstruction.receiverValueTarget + wormholeMessageFee
@ -313,7 +385,7 @@ contract CoreRelayerDelivery is CoreRelayerGovernance {
revert IDelivery.InsufficientRelayerFunds();
}
// make sure 'targetChain' is the current chain
// Check that the instruction's target chain is this chain
if (chainId() != deliveryInstruction.targetChain) {
revert IDelivery.TargetChainIsNotThisChain(deliveryInstruction.targetChain);
}

View File

@ -26,12 +26,20 @@ interface IDelivery {
* @notice The relay provider calls 'deliverSingle' to relay messages as described by one delivery instruction
*
* The instruction specifies the target chain (must be this chain), target address, refund address, maximum refund (in this chain's currency),
* receiverValue (in this chain's currency), upper bound on gas, and the permissioned address allowed to execute this instruction
* receiver value (in this chain's currency), upper bound on gas, and the permissioned address allowed to execute this instruction
*
* The relay provider must pass in the signed wormhole messages from the source chain of the same nonce
* The relay provider must pass in the signed wormhole messages (VAAs) from the source chain of the same nonce
* (the wormhole message with the delivery instructions (the delivery VAA) must be one of these messages)
* as well as identify which of these messages is the delivery VAA and which of the many instructions in the multichainSend container is meant to be executed
*
* The messages will be relayed to the target address (with the specified gas limit and receiver value) iff the following checks are met:
* - the delivery VAA has a valid signature
* - the delivery VAA's emitter is one of these CoreRelayer contracts
* - the delivery instruction container in the delivery VAA was fully funded
* - the instruction's target chain is this chain
* - the relay provider passed in at least [(one wormhole message fee) + instruction.maximumRefundTarget + instruction.receiverValueTarget] of this chain's currency as msg.value
* - msg.sender is the permissioned address allowed to execute this instruction
*
* @param targetParams struct containing the signed wormhole messages and encoded delivery instruction container (and other information)
*/
function deliverSingle(TargetDeliveryParametersSingle memory targetParams) external payable;
@ -60,6 +68,18 @@ interface IDelivery {
* (the wormhole message with the original delivery instructions (the delivery VAA) must be one of these messages)
* as well as the wormhole message with the new redelivery instruction (the redelivery VAA)
*
* The messages will be relayed to the target address (with the specified gas limit and receiver value) iff the following checks are met:
* - the redelivery VAA (targetParams.redeliveryVM) has a valid signature
* - the redelivery VAA's emitter is one of these CoreRelayer contracts
* - the original delivery VAA has a valid signature
* - the original delivery VAA's emitter is one of these CoreRelayer contracts
* - the new redelivery instruction's upper bound on gas >= the original instruction's upper bound on gas
* - the new redelivery instruction's 'receiver value' amount >= the original instruction's 'receiver value' amount
* - the redelivery instruction's target chain = the original instruction's target chain = this chain
* - for the redelivery instruction, the relay provider passed in at least [(one wormhole message fee) + instruction.newMaximumRefundTarget + instruction.newReceiverValueTarget] of this chain's currency as msg.value
* - msg.sender is the permissioned address allowed to execute this redelivery instruction
* - msg.sender is the permissioned address allowed to execute the old instruction
*
* @param targetParams struct containing the signed wormhole messages and encoded redelivery instruction (and other information)
*/
function redeliverSingle(TargetRedeliveryByTxHashParamsSingle memory targetParams) external payable;

View File

@ -238,9 +238,9 @@ interface IWormholeRelayer {
* @custom:member newMaxTransactionFee The new maximum amount (denominated in source chain (this chain) currency) that you wish to spend on funding gas for the target chain.
* If more gas is needed on the target chain than is paid for, there will be a Receiver Failure.
* Any unused value out of this fee will be refunded to 'refundAddress'
* This must be greater than or equal to the original maxTransactionFee paid in the original request
* This must pay for at least as much gas as was paid for in the original request
* @custom:member receiverValue The amount (denominated in source chain currency) that will be converted to target chain currency and passed into the receiveWormholeMessage endpoint as value.
* This must be greater than or equal to the original receiverValue paid in the original request
* This must pay for at least as much receiver value as was paid for in the original request
* @custom:member newRelayParameters This should be 'getDefaultRelayParameters()'
*/
struct ResendByTx {