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; 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 { function redeliverSingle(IDelivery.TargetRedeliveryByTxHashParamsSingle memory targetParams) public payable {
//cache wormhole
IWormhole wormhole = wormhole(); IWormhole wormhole = wormhole();
//validate the redelivery VM
(IWormhole.VM memory redeliveryVM, bool valid, string memory reason) = (IWormhole.VM memory redeliveryVM, bool valid, string memory reason) =
wormhole.parseAndVerifyVM(targetParams.redeliveryVM); wormhole.parseAndVerifyVM(targetParams.redeliveryVM);
// Check that the redelivery VAA (targetParams.redeliveryVM) has a valid signature
if (!valid) { if (!valid) {
revert IDelivery.InvalidRedeliveryVM(reason); revert IDelivery.InvalidRedeliveryVM(reason);
} }
// Check that the redelivery VAA's emitter is one of these CoreRelayer contracts
if (!verifyRelayerVM(redeliveryVM)) { if (!verifyRelayerVM(redeliveryVM)) {
// Redelivery VM has an invalid emitter
revert IDelivery.InvalidEmitterInRedeliveryVM(); revert IDelivery.InvalidEmitterInRedeliveryVM();
} }
RedeliveryByTxHashInstruction memory redeliveryInstruction = decodeRedeliveryInstruction(redeliveryVM.payload); RedeliveryByTxHashInstruction memory redeliveryInstruction = decodeRedeliveryInstruction(redeliveryVM.payload);
//validate the original delivery VM // Obtain the original delivery VAA
IWormhole.VM memory originalDeliveryVM; IWormhole.VM memory originalDeliveryVM;
(originalDeliveryVM, valid, reason) = (originalDeliveryVM, valid, reason) =
wormhole.parseAndVerifyVM(targetParams.sourceEncodedVMs[redeliveryInstruction.deliveryIndex]); wormhole.parseAndVerifyVM(targetParams.sourceEncodedVMs[redeliveryInstruction.deliveryIndex]);
// Check that the original delivery VAA has a valid signature
if (!valid) { if (!valid) {
revert IDelivery.InvalidVaa(redeliveryInstruction.deliveryIndex, reason); revert IDelivery.InvalidVaa(redeliveryInstruction.deliveryIndex, reason);
} }
// Check that the original delivery VAA's emitter is one of these CoreRelayer contracts
if (!verifyRelayerVM(originalDeliveryVM)) { if (!verifyRelayerVM(originalDeliveryVM)) {
// Original Delivery VM has a invalid emitter
revert IDelivery.InvalidEmitterInOriginalDeliveryVM(redeliveryInstruction.deliveryIndex); revert IDelivery.InvalidEmitterInOriginalDeliveryVM(redeliveryInstruction.deliveryIndex);
} }
DeliveryInstruction memory instruction; // Obtain the specific old instruction that was originally executed (and is meant to be re-executed with new parameters)
(instruction, valid) = validateRedeliverySingle( // specifying the the target chain (must be this chain), target address, refund address, old maximum refund (in this chain's currency),
redeliveryInstruction, // old receiverValue (in this chain's currency), old upper bound on gas, and the permissioned address allowed to execute this instruction
decodeDeliveryInstructionsContainer(originalDeliveryVM.payload).instructions[redeliveryInstruction DeliveryInstruction memory originalInstruction = decodeDeliveryInstructionsContainer(originalDeliveryVM.payload).instructions[redeliveryInstruction
.multisendIndex] .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) { if (!valid) {
emit Delivery({ emit Delivery({
recipientContract: fromWormholeFormat(instruction.targetAddress), recipientContract: fromWormholeFormat(originalInstruction.targetAddress),
sourceChain: redeliveryVM.emitterChainId, sourceChain: originalDeliveryVM.emitterChainId,
sequence: redeliveryVM.sequence, sequence: originalDeliveryVM.sequence,
deliveryVaaHash: redeliveryVM.hash, deliveryVaaHash: originalDeliveryVM.hash,
status: DeliveryStatus.INVALID_REDELIVERY status: DeliveryStatus.INVALID_REDELIVERY
}); });
pay(targetParams.relayerRefundAddress, msg.value); pay(targetParams.relayerRefundAddress, msg.value);
return; return;
} }
// Replace maximumRefund, receiverValue, and the gasLimit on the original request
originalInstruction.maximumRefundTarget = redeliveryInstruction.newMaximumRefundTarget;
originalInstruction.receiverValueTarget = redeliveryInstruction.newReceiverValueTarget;
originalInstruction.executionParameters = redeliveryInstruction.executionParameters;
_executeDelivery( _executeDelivery(
instruction, originalInstruction,
targetParams.sourceEncodedVMs, targetParams.sourceEncodedVMs,
originalDeliveryVM.hash, originalDeliveryVM.hash,
targetParams.relayerRefundAddress, 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, RedeliveryByTxHashInstruction memory redeliveryInstruction,
DeliveryInstruction memory originalInstruction DeliveryInstruction memory originalInstruction
) internal view returns (DeliveryInstruction memory deliveryInstruction, bool isValid) { ) internal view returns (bool isValid) {
// All the same checks as delivery single, with a couple additional
// The same relay provider must be specified when doing a single VAA redeliver.
address providerAddress = fromWormholeFormat(redeliveryInstruction.executionParameters.providerDeliveryAddress); 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(); revert IDelivery.MismatchingRelayProvidersInRedelivery();
} }
uint256 wormholeMessageFee = wormhole().messageFee(); 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 ( if (
msg.value msg.value
< redeliveryInstruction.newMaximumRefundTarget + redeliveryInstruction.newReceiverValueTarget < redeliveryInstruction.newMaximumRefundTarget + redeliveryInstruction.newReceiverValueTarget
@ -238,57 +303,64 @@ contract CoreRelayerDelivery is CoreRelayerGovernance {
} }
uint16 whChainId = chainId(); 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 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 && 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 && 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 && 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; && 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 * @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), * 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) * (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 * 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) * @param targetParams struct containing the signed wormhole messages and encoded delivery instruction container (and other information)
*/ */
function deliverSingle(IDelivery.TargetDeliveryParametersSingle memory targetParams) public payable { function deliverSingle(IDelivery.TargetDeliveryParametersSingle memory targetParams) public payable {
IWormhole wormhole = wormhole(); 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) = (IWormhole.VM memory deliveryVM, bool valid, string memory reason) =
wormhole.parseAndVerifyVM(targetParams.encodedVMs[targetParams.deliveryIndex]); wormhole.parseAndVerifyVM(targetParams.encodedVMs[targetParams.deliveryIndex]);
// Check that the delivery VAA has a valid signature
if (!valid) { if (!valid) {
revert IDelivery.InvalidVaa(targetParams.deliveryIndex, reason); revert IDelivery.InvalidVaa(targetParams.deliveryIndex, reason);
} }
// Check that the delivery VAA's emitter is one of these CoreRelayer contracts
if (!verifyRelayerVM(deliveryVM)) { if (!verifyRelayerVM(deliveryVM)) {
revert IDelivery.InvalidEmitter(); revert IDelivery.InvalidEmitter();
} }
DeliveryInstructionsContainer memory container = decodeDeliveryInstructionsContainer(deliveryVM.payload); 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) { if (!container.sufficientlyFunded) {
revert IDelivery.SendNotSufficientlyFunded(); 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 // 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]; 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) { if (fromWormholeFormat(deliveryInstruction.executionParameters.providerDeliveryAddress) != msg.sender) {
revert IDelivery.UnexpectedRelayer(); revert IDelivery.UnexpectedRelayer();
} }
uint256 wormholeMessageFee = wormhole.messageFee(); 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 ( if (
msg.value msg.value
< deliveryInstruction.maximumRefundTarget + deliveryInstruction.receiverValueTarget + wormholeMessageFee < deliveryInstruction.maximumRefundTarget + deliveryInstruction.receiverValueTarget + wormholeMessageFee
@ -313,7 +385,7 @@ contract CoreRelayerDelivery is CoreRelayerGovernance {
revert IDelivery.InsufficientRelayerFunds(); revert IDelivery.InsufficientRelayerFunds();
} }
// make sure 'targetChain' is the current chain // Check that the instruction's target chain is this chain
if (chainId() != deliveryInstruction.targetChain) { if (chainId() != deliveryInstruction.targetChain) {
revert IDelivery.TargetChainIsNotThisChain(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 * @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), * 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) * (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 * 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) * @param targetParams struct containing the signed wormhole messages and encoded delivery instruction container (and other information)
*/ */
function deliverSingle(TargetDeliveryParametersSingle memory targetParams) external payable; 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) * (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) * 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) * @param targetParams struct containing the signed wormhole messages and encoded redelivery instruction (and other information)
*/ */
function redeliverSingle(TargetRedeliveryByTxHashParamsSingle memory targetParams) external payable; 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. * @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. * 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' * 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. * @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()' * @custom:member newRelayParameters This should be 'getDefaultRelayParameters()'
*/ */
struct ResendByTx { struct ResendByTx {