Replace strings with typed errors (#37)

* Replace strings with typed errors

Replace strings with typed errors in `ethereum/contracts/relayProvider/*.sol`.

* Rename misleading identifier

* Update forge assertions to use typed errors

Update RelayProvider unit tests to use typed errors.

* Replace strings with typed errors

Replace strings with typed errors in `ethereum/contracts/coreRelayer/*.sol`.

* Update forge assertions to use typed errors

Update CoreRelayer unit tests to use typed errors.

* `forge fmt`
This commit is contained in:
agodnic 2023-01-19 11:59:35 -03:00 committed by GitHub
parent 9d51fd54ae
commit 87cfc41bf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 318 additions and 117 deletions

View File

@ -11,6 +11,32 @@ import "./CoreRelayerStructs.sol";
contract CoreRelayer is CoreRelayerGovernance {
using BytesLib for bytes;
error InsufficientFunds(string reason);
error MsgValueTooLow(); // msg.value must cover the budget specified
error NonceIsZero();
error NoDeliveryInProcess();
error CantRequestMultipleForwards();
error RelayProviderDoesNotSupportTargetChain();
error RolloverChainNotIncluded(); // Rollover chain was not included in the forwarding request
error ChainNotFoundInDeliveryRequests(uint16 chainId); // Required chain not found in the delivery requests
error ReentrantCall();
error InvalidEmitterInOriginalDeliveryVM();
error InvalidRedeliveryVM(string reason);
error InvalidEmitterInRedeliveryVM();
error MismatchingRelayProvidersInRedelivery(); // The same relay provider must be specified when doing a single VAA redeliver
error ProviderAddressIsNotSender(); // msg.sender must be the provider
error RedeliveryRequestDoesNotTargetThisChain();
error OriginalDeliveryRequestDidNotTargetThisChain();
error InvalidVaa(uint256 deliveryIndex); // Invalid VAA at delivery index
error InvalidEmitter();
error DeliveryRequestNotSufficientlyFunded(); // This delivery request was not sufficiently funded, and must request redelivery
error UnexpectedRelayer(); // Specified relayer is not the relayer delivering the message
error InsufficientRelayerFunds(); // The relayer didn't pass sufficient funds (msg.value does not cover the necessary budget fees)
error AlreadyDelivered(); // The message was already delivered.
error TargetChainIsNotThisChain(uint16 targetChainId);
error SrcNativeCurrencyPriceIsZero();
error DstNativeCurrencyPriceIsZero();
event DeliverySuccess(bytes32 deliveryVaaHash, address recipientContract);
event DeliveryFailure(bytes32 deliveryVaaHash, address recipientContract);
event ForwardRequestFailure(bytes32 deliveryVaaHash, address recipientContract);
@ -61,11 +87,15 @@ contract CoreRelayer is CoreRelayerGovernance {
isDelivery: false
})
);
require(isSufficient, reason);
if (!isSufficient) {
revert InsufficientFunds(reason);
}
uint256 totalFee = requestFee + wormhole().messageFee();
//Make sure the msg.value covers the budget they specified
require(msg.value >= totalFee, "1"); //"Msg.value does not cover the specified budget");
if (msg.value < totalFee) {
revert MsgValueTooLow();
}
emitRedelivery(request, nonce, provider.getConsistencyLevel(), applicationBudgetTarget, maximumRefund, provider);
@ -108,8 +138,12 @@ contract CoreRelayer is CoreRelayerGovernance {
returns (uint64 sequence)
{
(uint256 totalCost, bool isSufficient, string memory cause) = sufficientFundsHelper(deliveryRequests, msg.value);
require(isSufficient, cause);
require(nonce > 0, "2"); //"nonce must be > 0");
if (!isSufficient) {
revert InsufficientFunds(cause);
}
if (nonce == 0) {
revert NonceIsZero();
}
// encode the DeliveryInstructions
bytes memory container = convertToEncodedDeliveryInstructions(deliveryRequests, true);
@ -140,8 +174,13 @@ contract CoreRelayer is CoreRelayerGovernance {
public
payable
{
require(isContractLocked(), "3"); //"Can only forward while a delivery is in process.");
require(getForwardingRequest().isValid != true, "4"); //"Cannot request multiple forwards.");
// Can only forward while a delivery is in process.
if (!isContractLocked()) {
revert NoDeliveryInProcess();
}
if (getForwardingRequest().isValid) {
revert CantRequestMultipleForwards();
}
//We want to catch malformed requests in this function, and only underfunded requests when emitting.
verifyForwardingRequest(deliveryRequests, rolloverChain, nonce);
@ -209,19 +248,25 @@ contract CoreRelayer is CoreRelayerGovernance {
internal
view
{
require(nonce > 0, "2"); //"nonce must be > 0");
if (nonce == 0) {
revert NonceIsZero();
}
bool foundRolloverChain = false;
IRelayProvider selectedProvider = IRelayProvider(container.relayProviderAddress);
for (uint16 i = 0; i < container.requests.length; i++) {
require(selectedProvider.getDeliveryAddress(container.requests[i].targetChain) != 0, "5"); //"Specified relay provider does not support the target chain" );
if (selectedProvider.getDeliveryAddress(container.requests[i].targetChain) == 0) {
revert RelayProviderDoesNotSupportTargetChain();
}
if (container.requests[i].targetChain == rolloverChain) {
foundRolloverChain = true;
}
}
require(foundRolloverChain, "6"); //"Rollover chain was not included in the forwarding request.");
if (!foundRolloverChain) {
revert RolloverChainNotIncluded();
}
}
function findDeliveryIndex(DeliveryRequestsContainer memory container, uint16 chainId)
@ -236,7 +281,7 @@ contract CoreRelayer is CoreRelayerGovernance {
}
}
revert("7"); //"Required chain not found in the delivery requests");
revert ChainNotFoundInDeliveryRequests(chainId);
}
/*
@ -343,7 +388,9 @@ contract CoreRelayer is CoreRelayerGovernance {
//REVISE Decide whether we want to remove the DeliveryInstructionContainer from encodedVMs.
// lock the contract to prevent reentrancy
require(!isContractLocked(), "8"); //"reentrant call");
if (isContractLocked()) {
revert ReentrantCall();
}
setContractLock(true);
// store gas budget pre target invocation to calculate unused gas budget
@ -475,14 +522,24 @@ contract CoreRelayer is CoreRelayerGovernance {
//validate the original delivery VM
(IWormhole.VM memory originalDeliveryVM, bool valid, string memory reason) =
wormhole.parseAndVerifyVM(targetParams.sourceEncodedVMs[targetParams.deliveryIndex]);
require(valid, "9"); //"Invalid VAA at delivery index");
require(verifyRelayerVM(originalDeliveryVM), "10"); //"Original Delivery VM has a invalid emitter");
if (!valid) {
revert InvalidVaa(targetParams.deliveryIndex);
}
if (!verifyRelayerVM(originalDeliveryVM)) {
// Original Delivery VM has a invalid emitter
revert InvalidEmitterInOriginalDeliveryVM();
}
//validate the redelivery VM
IWormhole.VM memory redeliveryVM;
(redeliveryVM, valid, reason) = wormhole.parseAndVerifyVM(targetParams.redeliveryVM);
require(valid, "11"); //"Redelivery VM is invalid");
require(verifyRelayerVM(redeliveryVM), "12"); //"Redelivery VM has an invalid emitter");
if (!valid) {
revert InvalidRedeliveryVM(reason);
}
if (!verifyRelayerVM(redeliveryVM)) {
// Redelivery VM has an invalid emitter
revert InvalidEmitterInRedeliveryVM();
}
DeliveryInstruction memory instruction = validateRedeliverySingle(
decodeRedeliveryByTxHashInstruction(redeliveryVM.payload),
@ -490,7 +547,9 @@ contract CoreRelayer is CoreRelayerGovernance {
);
//redelivery request cannot have already been attempted
require(!isDeliveryCompleted(redeliveryVM.hash));
if (isDeliveryCompleted(redeliveryVM.hash)) {
revert AlreadyDelivered();
}
//mark redelivery as attempted
markAsDelivered(redeliveryVM.hash);
@ -504,28 +563,35 @@ contract CoreRelayer is CoreRelayerGovernance {
) internal view returns (DeliveryInstruction memory deliveryInstruction) {
//All the same checks as delivery single, with a couple additional
//providers must match on both
// The same relay provider must be specified when doing a single VAA redeliver.
address providerAddress = fromWormholeFormat(redeliveryInstruction.executionParameters.providerDeliveryAddress);
require(
providerAddress == fromWormholeFormat(originalInstruction.executionParameters.providerDeliveryAddress), "13"
); //"The same relay provider must be specified when doing a single VAA redeliver");
if (providerAddress != fromWormholeFormat(originalInstruction.executionParameters.providerDeliveryAddress)) {
revert MismatchingRelayProvidersInRedelivery();
}
//msg.sender must be the provider
require(msg.sender == providerAddress, "14"); //"Relay provider differed from the specified address");
// msg.sender must be the provider
if (msg.sender != providerAddress) {
revert ProviderAddressIsNotSender();
}
//redelivery must target this chain
require(chainId() == redeliveryInstruction.targetChain, "15"); //"Redelivery request does not target this chain.");
if (chainId() != redeliveryInstruction.targetChain) {
revert RedeliveryRequestDoesNotTargetThisChain();
}
//original delivery must target this chain
require(chainId() == originalInstruction.targetChain, "16"); //"Original delivery request did not target this chain.");
//original delivery request must target this chain
if (chainId() != originalInstruction.targetChain) {
revert OriginalDeliveryRequestDidNotTargetThisChain();
}
//relayer must have covered the necessary funds
require(
if (
msg.value
>= redeliveryInstruction.newMaximumRefundTarget + redeliveryInstruction.newApplicationBudgetTarget
+ wormhole().messageFee(),
"17"
); //"Msg.value does not cover the necessary budget fees");
< redeliveryInstruction.newMaximumRefundTarget + redeliveryInstruction.newApplicationBudgetTarget
+ wormhole().messageFee()
) {
revert InsufficientRelayerFunds();
}
//Overwrite compute budget and application budget on the original request and proceed.
originalInstruction.maximumRefundTarget = redeliveryInstruction.newMaximumRefundTarget;
@ -545,35 +611,48 @@ contract CoreRelayer is CoreRelayerGovernance {
// validate the deliveryIndex
(IWormhole.VM memory deliveryVM, bool valid, string memory reason) =
wormhole.parseAndVerifyVM(targetParams.encodedVMs[targetParams.deliveryIndex]);
require(valid, "18"); //"Invalid VAA at delivery index");
require(verifyRelayerVM(deliveryVM), "19"); //"invalid emitter");
if (!valid) {
revert InvalidVaa(targetParams.deliveryIndex);
}
if (!verifyRelayerVM(deliveryVM)) {
revert InvalidEmitter();
}
DeliveryInstructionsContainer memory container = decodeDeliveryInstructionsContainer(deliveryVM.payload);
//ensure this is a funded delivery, not a failed forward.
require(container.sufficientlyFunded, "20"); //"This delivery request was not sufficiently funded, and must request redelivery.");
if (!container.sufficientlyFunded) {
revert DeliveryRequestNotSufficientlyFunded();
}
// parse the deliveryVM payload into the DeliveryInstructions struct
DeliveryInstruction memory deliveryInstruction = container.instructions[targetParams.multisendIndex];
//make sure the specified relayer is the relayer delivering this message
require(fromWormholeFormat(deliveryInstruction.executionParameters.providerDeliveryAddress) == msg.sender, "21"); //"Specified relayer is not the relayer delivering the message");
if (fromWormholeFormat(deliveryInstruction.executionParameters.providerDeliveryAddress) != msg.sender) {
revert UnexpectedRelayer();
}
//make sure relayer passed in sufficient funds
require(
if (
msg.value
>= deliveryInstruction.maximumRefundTarget + deliveryInstruction.applicationBudgetTarget
+ wormhole.messageFee(),
"22"
); //"Relayer did not pass in sufficient funds");
< deliveryInstruction.maximumRefundTarget + deliveryInstruction.applicationBudgetTarget
+ wormhole.messageFee()
) {
revert InsufficientRelayerFunds();
}
//make sure this has not already been delivered
require(!isDeliveryCompleted(deliveryVM.hash), "23"); //"delivery is already completed");
if (isDeliveryCompleted(deliveryVM.hash)) {
revert AlreadyDelivered();
}
//mark as delivered, so it can't be reattempted
markAsDelivered(deliveryVM.hash);
//make sure this delivery is intended for this chain
require(chainId() == deliveryInstruction.targetChain, "24"); //"targetChain is not this chain");
if (chainId() != deliveryInstruction.targetChain) {
revert TargetChainIsNotThisChain(deliveryInstruction.targetChain);
}
return _executeDelivery(wormhole, deliveryInstruction, targetParams.encodedVMs, deliveryVM.hash);
}
@ -701,10 +780,14 @@ contract CoreRelayer is CoreRelayerGovernance {
returns (uint256 targetAmount)
{
uint256 srcNativeCurrencyPrice = provider.quoteAssetPrice(sourceChain);
require(srcNativeCurrencyPrice > 0, "28");
if (srcNativeCurrencyPrice == 0) {
revert SrcNativeCurrencyPriceIsZero();
}
uint256 dstNativeCurrencyPrice = provider.quoteAssetPrice(targetChain);
require(dstNativeCurrencyPrice > 0, "29");
if (dstNativeCurrencyPrice == 0) {
revert DstNativeCurrencyPriceIsZero();
}
return sourceAmount * srcNativeCurrencyPrice / dstNativeCurrencyPrice;
}

View File

@ -23,49 +23,68 @@ abstract contract CoreRelayerGovernance is
{
using BytesLib for bytes;
error InvalidFork();
error InvalidGovernanceVM(string reason);
error WrongChainId(uint16 chainId);
error InvalidChainId(uint16 chainId);
error FailedToInitializeImplementation(string reason);
event ContractUpgraded(address indexed oldContract, address indexed newContract);
// "CoreRelayer" (left padded)
bytes32 constant module = 0x000000000000000000000000000000000000000000436f726552656c61796572;
function submitContractUpgrade(bytes memory _vm) public {
require(!isFork(), "invalid fork");
if (isFork()) {
revert InvalidFork();
}
(IWormhole.VM memory vm, bool valid, string memory reason) = verifyGovernanceVM(_vm);
require(valid, reason);
if (!valid) {
revert InvalidGovernanceVM(string(reason));
}
setConsumedGovernanceAction(vm.hash);
CoreRelayerLibrary.ContractUpgrade memory contractUpgrade = CoreRelayerLibrary.parseUpgrade(vm.payload, module);
require(contractUpgrade.chain == chainId(), "wrong chain id");
if (contractUpgrade.chain != chainId()) {
revert WrongChainId(contractUpgrade.chain);
}
upgradeImplementation(contractUpgrade.newContract);
}
function registerCoreRelayerContract(bytes memory vaa) public {
(IWormhole.VM memory vm, bool valid, string memory reason) = verifyGovernanceVM(vaa);
require(valid, reason);
if (!valid) {
revert InvalidGovernanceVM(string(reason));
}
setConsumedGovernanceAction(vm.hash);
CoreRelayerLibrary.RegisterChain memory rc = CoreRelayerLibrary.parseRegisterChain(vm.payload, module);
require((rc.chain == chainId() && !isFork()) || rc.chain == 0, "invalid chain id");
if ((rc.chain != chainId() || isFork()) && rc.chain != 0) {
revert InvalidChainId(rc.chain);
}
setRegisteredCoreRelayerContract(rc.emitterChain, rc.emitterAddress);
}
function setDefaultRelayProvider(bytes memory vaa) public {
(IWormhole.VM memory vm, bool valid, string memory reason) = verifyGovernanceVM(vaa);
require(valid, reason);
if (!valid) {
revert InvalidGovernanceVM(string(reason));
}
setConsumedGovernanceAction(vm.hash);
CoreRelayerLibrary.UpdateDefaultProvider memory provider =
CoreRelayerLibrary.parseUpdateDefaultProvider(vm.payload, module);
require((provider.chain == chainId() && !isFork()) || provider.chain == 0, "invalid chain id");
if ((provider.chain != chainId() || isFork()) && provider.chain != 0) {
revert InvalidChainId(provider.chain);
}
setRelayProvider(provider.newProvider);
}
@ -78,7 +97,9 @@ abstract contract CoreRelayerGovernance is
// Call initialize function of the new implementation
(bool success, bytes memory reason) = newImplementation.delegatecall(abi.encodeWithSignature("initialize()"));
require(success, string(reason));
if (!success) {
revert FailedToInitializeImplementation(string(reason));
}
emit ContractUpgraded(currentImplementation, newImplementation);
}

View File

@ -8,6 +8,8 @@ import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
import "./CoreRelayer.sol";
contract CoreRelayerImplementation is CoreRelayer {
error ImplementationAlreadyInitialized();
function initialize() public virtual initializer {
// this function needs to be exposed for an upgrade to pass
}
@ -15,7 +17,9 @@ contract CoreRelayerImplementation is CoreRelayer {
modifier initializer() {
address impl = ERC1967Upgrade._getImplementation();
require(!isInitialized(impl), "already initialized");
if (isInitialized(impl)) {
revert ImplementationAlreadyInitialized();
}
setInitialized(impl);

View File

@ -6,6 +6,14 @@ import "../libraries/external/BytesLib.sol";
library CoreRelayerLibrary {
using BytesLib for bytes;
error WrongModule(bytes32 module);
error InvalidContractUpgradeAction(uint8 action);
error InvalidContractUpgradeLength(uint256 length);
error InvalidRegisterChainAction(uint8);
error InvalidRegisterChainLength(uint256);
error InvalidDefaultProviderAction(uint8);
error InvalidDefaultProviderLength(uint256);
function parseUpgrade(bytes memory encodedUpgrade, bytes32 module)
public
pure
@ -16,12 +24,16 @@ library CoreRelayerLibrary {
cu.module = encodedUpgrade.toBytes32(index);
index += 32;
require(cu.module == module, "wrong module");
if (cu.module != module) {
revert WrongModule(cu.module);
}
cu.action = encodedUpgrade.toUint8(index);
index += 1;
require(cu.action == 1, "invalid ContractUpgrade");
if (cu.action != 1) {
revert InvalidContractUpgradeAction(cu.action);
}
cu.chain = encodedUpgrade.toUint16(index);
index += 2;
@ -29,7 +41,9 @@ library CoreRelayerLibrary {
cu.newContract = address(uint160(uint256(encodedUpgrade.toBytes32(index))));
index += 32;
require(encodedUpgrade.length == index, "invalid ContractUpgrade");
if (encodedUpgrade.length != index) {
revert InvalidContractUpgradeLength(encodedUpgrade.length);
}
}
function parseRegisterChain(bytes memory encodedRegistration, bytes32 module)
@ -42,7 +56,9 @@ library CoreRelayerLibrary {
registerChain.module = encodedRegistration.toBytes32(index);
index += 32;
require(registerChain.module == module, "wrong module");
if (registerChain.module != module) {
revert WrongModule(registerChain.module);
}
registerChain.action = encodedRegistration.toUint8(index);
index += 1;
@ -50,7 +66,9 @@ library CoreRelayerLibrary {
registerChain.chain = encodedRegistration.toUint16(index);
index += 2;
require(registerChain.action == 2, "invalid RegisterChain");
if (registerChain.action != 2) {
revert InvalidRegisterChainAction(registerChain.action);
}
registerChain.emitterChain = encodedRegistration.toUint16(index);
index += 2;
@ -58,7 +76,9 @@ library CoreRelayerLibrary {
registerChain.emitterAddress = encodedRegistration.toBytes32(index);
index += 32;
require(encodedRegistration.length == index, "invalid RegisterChain");
if (encodedRegistration.length != index) {
revert InvalidRegisterChainLength(encodedRegistration.length);
}
}
function parseUpdateDefaultProvider(bytes memory encodedDefaultProvider, bytes32 module)
@ -71,12 +91,16 @@ library CoreRelayerLibrary {
defaultProvider.module = encodedDefaultProvider.toBytes32(index);
index += 32;
require(defaultProvider.module == module, "wrong module");
if (defaultProvider.module != module) {
revert WrongModule(defaultProvider.module);
}
defaultProvider.action = encodedDefaultProvider.toUint8(index);
index += 1;
require(defaultProvider.action == 3, "invalid DefaultProvider");
if (defaultProvider.action != 3) {
revert InvalidDefaultProviderAction(defaultProvider.action);
}
defaultProvider.chain = encodedDefaultProvider.toUint16(index);
index += 2;
@ -84,7 +108,9 @@ library CoreRelayerLibrary {
defaultProvider.newProvider = address(uint160(uint256(encodedDefaultProvider.toBytes32(index))));
index += 32;
require(encodedDefaultProvider.length == index, "invalid DefaultProvider");
if (encodedDefaultProvider.length != index) {
revert InvalidDefaultProviderLength(encodedDefaultProvider.length);
}
}
struct ContractUpgrade {

View File

@ -11,6 +11,10 @@ import "./CoreRelayerStructs.sol";
contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
using BytesLib for bytes;
error InvalidPayloadId(uint8 payloadId);
error InvalidDeliveryInstructionsPayload(uint256 length);
error InvalidDeliveryRequestsPayload(uint256 length);
function decodeRedeliveryByTxHashInstruction(bytes memory encoded)
internal
pure
@ -57,7 +61,9 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
uint256 index = 0;
uint8 payloadId = encoded.toUint8(index);
require(payloadId == 1, "invalid payloadId");
if (payloadId != 1) {
revert InvalidPayloadId(payloadId);
}
index += 1;
bool sufficientlyFunded = encoded.toUint8(index) == 1;
index += 1;
@ -99,7 +105,9 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
instructionArray[i] = instruction;
}
require(index == encoded.length, "invalid delivery instructions payload");
if (index != encoded.length) {
revert InvalidDeliveryInstructionsPayload(encoded.length);
}
return DeliveryInstructionsContainer({
payloadId: payloadId,
@ -144,7 +152,9 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
uint256 index = 0;
uint8 payloadId = encoded.toUint8(index);
require(payloadId == 1, "invalid payloadId");
if (payloadId != 1) {
revert InvalidPayloadId(payloadId);
}
index += 1;
address relayProviderAddress = encoded.toAddress(index);
index += 20;
@ -185,7 +195,9 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
requestArray[i] = request;
}
require(index == encoded.length, "invalid delivery requests payload");
if (index != encoded.length) {
revert InvalidDeliveryRequestsPayload(encoded.length);
}
return DeliveryRequestsContainer({
payloadId: payloadId,

View File

@ -8,6 +8,8 @@ import "@openzeppelin/contracts/utils/Context.sol";
import "./CoreRelayerStructs.sol";
contract CoreRelayerSetters is CoreRelayerState, Context {
error InvalidEvmChainId();
function setInitialized(address implementation) internal {
_state.initializedImplementations[implementation] = true;
}
@ -57,7 +59,9 @@ contract CoreRelayerSetters is CoreRelayerState, Context {
}
function setEvmChainId(uint256 evmChainId) internal {
require(evmChainId == block.chainid, "invalid evmChainId ");
if (evmChainId != block.chainid) {
revert InvalidEvmChainId();
}
_state.evmChainId = evmChainId;
}
}

View File

@ -8,6 +8,11 @@ import "./CoreRelayerGovernance.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
contract CoreRelayerSetup is CoreRelayerSetters, ERC1967Upgrade {
error ImplementationAddressIsZero();
error WormholeAddressIsZero();
error DefaultRelayProviderAddressIsZero();
error FailedToInitializeImplementation(string reason);
function setup(
address implementation,
uint16 chainId,
@ -18,9 +23,15 @@ contract CoreRelayerSetup is CoreRelayerSetters, ERC1967Upgrade {
uint256 evmChainId
) public {
// sanity check initial values
require(implementation != address(0), "1"); //"implementation cannot be address(0)");
require(wormhole != address(0), "2"); //wormhole cannot be address(0)");
require(defaultRelayProvider != address(0), "3"); //default relay provider cannot be address(0)");
if (implementation == address(0)) {
revert ImplementationAddressIsZero();
}
if (wormhole == address(0)) {
revert WormholeAddressIsZero();
}
if (defaultRelayProvider == address(0)) {
revert DefaultRelayProviderAddressIsZero();
}
setChainId(chainId);
@ -38,6 +49,8 @@ contract CoreRelayerSetup is CoreRelayerSetters, ERC1967Upgrade {
// call initialize function of the new implementation
(bool success, bytes memory reason) = implementation.delegatecall(abi.encodeWithSignature("initialize()"));
require(success, string(reason));
if (!success) {
revert FailedToInitializeImplementation(string(reason));
}
}
}

View File

@ -9,8 +9,12 @@ import "../interfaces/IRelayProvider.sol";
import "../interfaces/ICoreRelayer.sol";
contract RelayProvider is RelayProviderGovernance, IRelayProvider {
error CallerNotApproved(address msgSender);
modifier onlyApprovedSender() {
require(approvedSender(_msgSender()), "_msgSender() not approved");
if (!approvedSender(_msgSender())) {
revert CallerNotApproved(_msgSender());
}
_;
}

View File

@ -12,6 +12,15 @@ import "./RelayProviderSetters.sol";
import "./RelayProviderStructs.sol";
abstract contract RelayProviderGovernance is RelayProviderGetters, RelayProviderSetters, ERC1967Upgrade {
error ChainIdIsZero();
error GasPriceIsZero();
error NativeCurrencyPriceIsZero();
error FailedToInitializeImplementation(string reason);
error WrongChainId();
error AddressIsZero();
error CallerMustBePendingOwner();
error CallerMustBeOwner();
event ContractUpgraded(address indexed oldContract, address indexed newContract);
event OwnershipTransfered(address indexed oldOwner, address indexed newOwner);
event RewardAddressUpdated(address indexed newAddress);
@ -55,9 +64,16 @@ abstract contract RelayProviderGovernance is RelayProviderGetters, RelayProvider
public
onlyOwner
{
require(updateChainId > 0, "updateChainId == 0");
require(updateGasPrice > 0, "updateGasPrice == 0");
require(updateNativeCurrencyPrice > 0, "updateNativeCurrencyPrice == 0");
if (updateChainId == 0) {
revert ChainIdIsZero();
}
if (updateGasPrice == 0) {
revert GasPriceIsZero();
}
if (updateNativeCurrencyPrice == 0) {
revert NativeCurrencyPriceIsZero();
}
setPriceInfo(updateChainId, updateGasPrice, updateNativeCurrencyPrice);
}
@ -85,7 +101,9 @@ abstract contract RelayProviderGovernance is RelayProviderGetters, RelayProvider
/// @dev upgrade serves to upgrade contract implementations
function upgrade(uint16 relayProviderChainId, address newImplementation) public onlyOwner {
require(relayProviderChainId == chainId(), "wrong chain id");
if (relayProviderChainId != chainId()) {
revert WrongChainId();
}
address currentImplementation = _getImplementation();
@ -94,7 +112,9 @@ abstract contract RelayProviderGovernance is RelayProviderGetters, RelayProvider
// call initialize function of the new implementation
(bool success, bytes memory reason) = newImplementation.delegatecall(abi.encodeWithSignature("initialize()"));
require(success, string(reason));
if (!success) {
revert FailedToInitializeImplementation(string(reason));
}
emit ContractUpgraded(currentImplementation, newImplementation);
}
@ -104,8 +124,12 @@ abstract contract RelayProviderGovernance is RelayProviderGetters, RelayProvider
* - it saves an address for the new owner in the pending state
*/
function submitOwnershipTransferRequest(uint16 thisRelayerChainId, address newOwner) public onlyOwner {
require(thisRelayerChainId == chainId(), "incorrect chainId");
require(newOwner != address(0), "new owner cannot be address(0)");
if (thisRelayerChainId != chainId()) {
revert WrongChainId();
}
if (newOwner == address(0)) {
revert AddressIsZero();
}
setPendingOwner(newOwner);
}
@ -119,7 +143,9 @@ abstract contract RelayProviderGovernance is RelayProviderGetters, RelayProvider
// cache the new owner address
address newOwner = pendingOwner();
require(msg.sender == newOwner, "caller must be pending owner");
if (msg.sender != newOwner) {
revert CallerMustBePendingOwner();
}
// cache currentOwner for Event
address currentOwner = owner();
@ -132,7 +158,9 @@ abstract contract RelayProviderGovernance is RelayProviderGetters, RelayProvider
}
modifier onlyOwner() {
require(owner() == _msgSender(), "owner() != _msgSender()");
if (owner() != _msgSender()) {
revert CallerMustBeOwner();
}
_;
}
}

View File

@ -8,6 +8,8 @@ import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
import "./RelayProvider.sol";
contract RelayProviderImplementation is RelayProvider {
error ImplementationAlreadyInitialized();
function initialize() public virtual initializer {
// this function needs to be exposed for an upgrade to pass
}
@ -15,7 +17,9 @@ contract RelayProviderImplementation is RelayProvider {
modifier initializer() {
address impl = ERC1967Upgrade._getImplementation();
require(!isInitialized(impl), "already initialized");
if (isInitialized(impl)) {
revert ImplementationAlreadyInitialized();
}
setInitialized(impl);

View File

@ -8,9 +8,14 @@ import "./RelayProviderGovernance.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
contract RelayProviderSetup is RelayProviderSetters, ERC1967Upgrade {
error ImplementationAddressIsZero();
error FailedToInitializeImplementation(string reason);
function setup(address implementation, uint16 chainId) public {
// sanity check initial values
require(implementation != address(0), "implementation cannot be address(0)");
if (implementation == address(0)) {
revert ImplementationAddressIsZero();
}
setOwner(_msgSender());
@ -20,6 +25,8 @@ contract RelayProviderSetup is RelayProviderSetters, ERC1967Upgrade {
// call initialize function of the new implementation
(bool success, bytes memory reason) = implementation.delegatecall(abi.encodeWithSignature("initialize()"));
require(success, string(reason));
if (!success) {
revert FailedToInitializeImplementation(string(reason));
}
}
}

View File

@ -517,7 +517,7 @@ contract TestCoreRelayer is Test {
uint256 computeBudget =
source.coreRelayer.quoteGasDeliveryFee(TARGET_CHAIN_ID, gasParams.targetGasLimit, source.relayProvider);
vm.expectRevert(bytes("2"));
vm.expectRevert(abi.encodeWithSignature("NonceIsZero()"));
source.integration.sendMessageGeneral{value: computeBudget + wormholeFee}(
abi.encodePacked(uint8(0), message),
TARGET_CHAIN_ID,
@ -560,10 +560,7 @@ contract TestCoreRelayer is Test {
}
}
function testRevertRedeliveryErrors_1_9_10_11_12_13_14_15_16_17(
GasParameters memory gasParams,
bytes memory message
) public {
function testRevertRedeliveryErrors(GasParameters memory gasParams, bytes memory message) public {
(uint16 SOURCE_CHAIN_ID, uint16 TARGET_CHAIN_ID, Contracts memory source, Contracts memory target) =
standardAssumeAndSetupTwoChains(gasParams, 1000000);
@ -598,7 +595,7 @@ contract TestCoreRelayer is Test {
newRelayParameters: source.coreRelayer.getDefaultRelayParams()
});
vm.expectRevert(bytes("1"));
vm.expectRevert(abi.encodeWithSignature("MsgValueTooLow()"));
source.coreRelayer.requestRedelivery{value: stack.payment - 1}(stack.redeliveryRequest, 1, source.relayProvider);
source.coreRelayer.requestRedelivery{value: stack.payment}(stack.redeliveryRequest, 1, source.relayProvider);
@ -630,7 +627,7 @@ contract TestCoreRelayer is Test {
+ target.wormhole.messageFee();
vm.prank(target.relayer);
vm.expectRevert(bytes("9"));
vm.expectRevert(abi.encodeWithSignature("InvalidVaa(uint256)", 1));
target.coreRelayer.redeliverSingle{value: stack.budget}(stack.package);
stack.originalDelivery.encodedVMs[1] = stack.originalDelivery.encodedVMs[0];
@ -643,7 +640,7 @@ contract TestCoreRelayer is Test {
});
vm.prank(target.relayer);
vm.expectRevert(bytes("10"));
vm.expectRevert(abi.encodeWithSignature("InvalidEmitterInOriginalDeliveryVM()"));
target.coreRelayer.redeliverSingle{value: stack.budget}(stack.package);
stack.originalDelivery.encodedVMs[1] = correctVM;
@ -667,7 +664,7 @@ contract TestCoreRelayer is Test {
});
vm.prank(target.relayer);
vm.expectRevert(bytes("11"));
vm.expectRevert(abi.encodeWithSignature("InvalidRedeliveryVM(string)", "VM signature invalid"));
target.coreRelayer.redeliverSingle{value: stack.budget}(stack.package);
fakeVM = relayerWormholeSimulator.fetchSignedMessageFromLogs(
@ -681,7 +678,7 @@ contract TestCoreRelayer is Test {
});
vm.prank(target.relayer);
vm.expectRevert(bytes("12"));
vm.expectRevert(abi.encodeWithSignature("InvalidEmitterInRedeliveryVM()"));
target.coreRelayer.redeliverSingle{value: stack.budget}(stack.package);
source.relayProvider.updateDeliveryAddress(TARGET_CHAIN_ID, bytes32(uint256(uint160(address(this)))));
@ -701,7 +698,7 @@ contract TestCoreRelayer is Test {
});
vm.prank(target.relayer);
vm.expectRevert(bytes("13"));
vm.expectRevert(abi.encodeWithSignature("MismatchingRelayProvidersInRedelivery()"));
target.coreRelayer.redeliverSingle{value: stack.budget}(stack.package);
stack.package = ICoreRelayer.TargetRedeliveryByTxHashParamsSingle({
@ -711,7 +708,7 @@ contract TestCoreRelayer is Test {
multisendIndex: stack.originalDelivery.multisendIndex
});
vm.expectRevert(bytes("14"));
vm.expectRevert(abi.encodeWithSignature("ProviderAddressIsNotSender()"));
target.coreRelayer.redeliverSingle{value: stack.budget}(stack.package);
uint16 differentChainId = 2;
@ -720,7 +717,7 @@ contract TestCoreRelayer is Test {
}
vm.deal(map[differentChainId].relayer, stack.budget);
vm.expectRevert(bytes("15"));
vm.expectRevert(abi.encodeWithSignature("RedeliveryRequestDoesNotTargetThisChain()"));
vm.prank(target.relayer);
map[differentChainId].coreRelayer.redeliverSingle{value: stack.budget}(stack.package);
@ -753,7 +750,7 @@ contract TestCoreRelayer is Test {
multisendIndex: stack.originalDelivery.multisendIndex
});
vm.expectRevert(bytes("16"));
vm.expectRevert(abi.encodeWithSignature("OriginalDeliveryRequestDidNotTargetThisChain()"));
vm.prank(target.relayer);
map[differentChainId].coreRelayer.redeliverSingle{value: stack.budget}(stack.package);
@ -764,7 +761,7 @@ contract TestCoreRelayer is Test {
multisendIndex: stack.originalDelivery.multisendIndex
});
vm.expectRevert(bytes("17"));
vm.expectRevert(abi.encodeWithSignature("InsufficientRelayerFunds()"));
vm.prank(target.relayer);
target.coreRelayer.redeliverSingle{value: stack.budget - 1}(stack.package);
@ -794,9 +791,7 @@ contract TestCoreRelayer is Test {
ICoreRelayer.DeliveryInstruction instruction;
}
function testRevertDeliveryErrors_18_19_20_21_22_23_24(GasParameters memory gasParams, bytes memory message)
public
{
function testRevertDeliveryErrors(GasParameters memory gasParams, bytes memory message) public {
(uint16 SOURCE_CHAIN_ID, uint16 TARGET_CHAIN_ID, Contracts memory source, Contracts memory target) =
standardAssumeAndSetupTwoChains(gasParams, 1000000);
@ -842,7 +837,7 @@ contract TestCoreRelayer is Test {
+ target.wormhole.messageFee();
vm.prank(source.relayer);
vm.expectRevert(bytes("20"));
vm.expectRevert(abi.encodeWithSignature("DeliveryRequestNotSufficientlyFunded()"));
source.coreRelayer.deliverSingle{value: stack.budget}(stack.package);
}
@ -890,7 +885,7 @@ contract TestCoreRelayer is Test {
+ target.wormhole.messageFee();
vm.prank(target.relayer);
vm.expectRevert(bytes("18"));
vm.expectRevert(abi.encodeWithSignature("InvalidVaa(uint256)", 1));
target.coreRelayer.deliverSingle{value: stack.budget}(stack.package);
stack.encodedVMs[1] = stack.encodedVMs[0];
@ -898,18 +893,18 @@ contract TestCoreRelayer is Test {
stack.package = ICoreRelayer.TargetDeliveryParametersSingle(stack.encodedVMs, 1, 0);
vm.prank(target.relayer);
vm.expectRevert(bytes("19"));
vm.expectRevert(abi.encodeWithSignature("InvalidEmitter()"));
target.coreRelayer.deliverSingle{value: stack.budget}(stack.package);
stack.encodedVMs[1] = stack.deliveryVM;
stack.package = ICoreRelayer.TargetDeliveryParametersSingle(stack.encodedVMs, 1, 0);
vm.expectRevert(bytes("21"));
vm.expectRevert(abi.encodeWithSignature("UnexpectedRelayer()"));
target.coreRelayer.deliverSingle{value: stack.budget}(stack.package);
vm.prank(target.relayer);
vm.expectRevert(bytes("22"));
vm.expectRevert(abi.encodeWithSignature("InsufficientRelayerFunds()"));
target.coreRelayer.deliverSingle{value: stack.budget - 1}(stack.package);
uint16 differentChainId = 2;
@ -918,14 +913,14 @@ contract TestCoreRelayer is Test {
}
vm.prank(target.relayer);
vm.expectRevert(bytes("24"));
vm.expectRevert(abi.encodeWithSignature("TargetChainIsNotThisChain(uint16)", 2));
map[differentChainId].coreRelayer.deliverSingle{value: stack.budget}(stack.package);
vm.prank(target.relayer);
target.coreRelayer.deliverSingle{value: stack.budget}(stack.package);
vm.prank(target.relayer);
vm.expectRevert(bytes("23"));
vm.expectRevert(abi.encodeWithSignature("AlreadyDelivered()"));
target.coreRelayer.deliverSingle{value: stack.budget}(stack.package);
}
@ -939,7 +934,7 @@ contract TestCoreRelayer is Test {
* Request delivery 25-27
*/
function testRevertRequestDeliveryErrors_25_26_27(GasParameters memory gasParams, bytes memory message) public {
function testRevertRequestDeliveryErrors(GasParameters memory gasParams, bytes memory message) public {
(uint16 SOURCE_CHAIN_ID, uint16 TARGET_CHAIN_ID, Contracts memory source, Contracts memory target) =
standardAssumeAndSetupTwoChains(gasParams, 1000000);
@ -963,7 +958,7 @@ contract TestCoreRelayer is Test {
source.coreRelayer.getDefaultRelayParams()
);
vm.expectRevert(bytes("25"));
vm.expectRevert(abi.encodeWithSignature("InsufficientFunds(string)", "25"));
source.coreRelayer.requestDelivery{value: stack.payment - 1}(stack.deliveryRequest, 1, source.relayProvider);
source.relayProvider.updateDeliverGasOverhead(TARGET_CHAIN_ID, gasParams.evmGasOverhead);
@ -980,7 +975,7 @@ contract TestCoreRelayer is Test {
source.coreRelayer.getDefaultRelayParams()
);
vm.expectRevert(bytes("26"));
vm.expectRevert(abi.encodeWithSignature("InsufficientFunds(string)", "26"));
source.coreRelayer.requestDelivery{value: stack.deliveryOverhead - 1}(
stack.badDeliveryRequest, 1, source.relayProvider
);
@ -991,7 +986,7 @@ contract TestCoreRelayer is Test {
TARGET_CHAIN_ID, uint256(gasParams.targetGasLimit - 1) * gasParams.targetGasPrice
);
vm.expectRevert(bytes("27"));
vm.expectRevert(abi.encodeWithSignature("InsufficientFunds(string)", "27"));
source.coreRelayer.requestDelivery{value: stack.payment}(stack.deliveryRequest, 1, source.relayProvider);
}

View File

@ -42,7 +42,7 @@ contract TestRelayProvider is Test {
initializeRelayProvider();
// you shall not pass
vm.expectRevert("updateChainId == 0");
vm.expectRevert(abi.encodeWithSignature("ChainIdIsZero()"));
relayProvider.updatePrice(
0, // updateChainId
updateGasPrice,
@ -57,7 +57,7 @@ contract TestRelayProvider is Test {
initializeRelayProvider();
// you shall not pass
vm.expectRevert("updateGasPrice == 0");
vm.expectRevert(abi.encodeWithSignature("GasPriceIsZero()"));
relayProvider.updatePrice(
updateChainId,
0, // updateGasPrice == 0
@ -72,7 +72,7 @@ contract TestRelayProvider is Test {
initializeRelayProvider();
// you shall not pass
vm.expectRevert("updateNativeCurrencyPrice == 0");
vm.expectRevert(abi.encodeWithSignature("NativeCurrencyPriceIsZero()"));
relayProvider.updatePrice(
updateChainId,
updateGasPrice,
@ -96,7 +96,7 @@ contract TestRelayProvider is Test {
// you shall not pass
vm.prank(oracleOwner);
vm.expectRevert("owner() != _msgSender()");
vm.expectRevert(abi.encodeWithSignature("CallerMustBeOwner()"));
relayProvider.updatePrice(updateChainId, updateGasPrice, updateNativeCurrencyPrice);
}