-
Notifications
You must be signed in to change notification settings - Fork 21
[CCIP-7723] Define & Implement ICrossChainVerifierResolver #1330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
4781b61
ed45056
3099172
b8e02cb
136af18
71d37db
f76ae67
b32893e
beb5518
69adf06
ca440f5
9e16385
895c788
eebe683
fa39409
1d835e4
8edcb5f
302b6d3
b80bbd2
7a3126b
e28e606
c2afcdc
2acc758
0413073
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| // SPDX-License-Identifier: BUSL-1.1 | ||
| pragma solidity ^0.8.24; | ||
|
|
||
| import {ICrossChainVerifierResolver} from "../interfaces/ICrossChainVerifierResolver.sol"; | ||
| import {Ownable2StepMsgSender} from "@chainlink/contracts/src/v0.8/shared/access/Ownable2StepMsgSender.sol"; | ||
|
|
||
| import {IERC165} from "@openzeppelin/[email protected]/utils/introspection/IERC165.sol"; | ||
|
|
||
| /// @notice Resolves and returns the appropriate verifier contract for the given outbound / inbound traffic. | ||
| contract VersionedVerifierResolver is ICrossChainVerifierResolver, Ownable2StepMsgSender { | ||
| error InvalidCCVDataLength(); | ||
| error InvalidDestChainSelector(uint64 destChainSelector); | ||
| error InvalidVersion(bytes4 version); | ||
| error InboundImplementationAlreadyExists(bytes4 version); | ||
| error InboundImplementationNotFound(bytes4 version); | ||
| error OutboundImplementationAlreadyExists(uint64 destChainSelector); | ||
| error OutboundImplementationNotFound(uint64 destChainSelector); | ||
| error ZeroAddressNotAllowed(); | ||
|
|
||
| event InboundImplementationAdded(bytes4 version, address verifier); | ||
| event InboundImplementationRemoved(bytes4 version); | ||
| event OutboundImplementationAdded(uint64 destChainSelector, address verifier); | ||
| event OutboundImplementationRemoved(uint64 destChainSelector); | ||
|
|
||
| struct InboundImplementationArgs { | ||
| bytes4 version; // ────╮ Verifier version | ||
| address verifier; // ──╯ Address of the verifier contract | ||
| } | ||
|
|
||
| struct OutboundImplementationArgs { | ||
| uint64 destChainSelector; // ──╮ Destination chain selector | ||
| address verifier; // ──────────╯ Address of the verifier contract | ||
| } | ||
|
|
||
| /// @notice maps verifier versions to their implementation addresses, applied to inbound traffic | ||
| mapping(bytes4 => address) private s_inboundImplementations; | ||
| /// @notice maps destination chain selectors to their implementation addresses, applied to outbound traffic | ||
| mapping(uint64 => address) private s_outboundImplementations; | ||
kylesmartin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| constructor() Ownable2StepMsgSender() {} | ||
kylesmartin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| /// @inheritdoc ICrossChainVerifierResolver | ||
| function getInboundImplementation( | ||
kylesmartin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| bytes calldata ccvData | ||
| ) external view returns (address) { | ||
| if (ccvData.length < 4) { | ||
| revert InvalidCCVDataLength(); | ||
| } | ||
| return _getInboundImplementationForVersion(bytes4(ccvData[:4])); | ||
| } | ||
|
|
||
| /// @notice Returns the verifier contract for a given version. | ||
| /// @param version The version of the verifier contract. | ||
| /// @return verifierAddress The address of the verifier contract. | ||
| function getInboundImplementationForVersion( | ||
| bytes4 version | ||
kylesmartin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ) external view returns (address) { | ||
| return _getInboundImplementationForVersion(version); | ||
| } | ||
|
|
||
| /// @dev Internal function that enables reuse between functions. | ||
kylesmartin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
kylesmartin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /// Validates that the inbound implementation exists before returning it. | ||
| /// @param version The version of the verifier contract. | ||
| /// @return verifierAddress The address of the verifier contract. | ||
| function _getInboundImplementationForVersion( | ||
| bytes4 version | ||
| ) internal view returns (address) { | ||
| if (s_inboundImplementations[version] == address(0)) { | ||
| revert InboundImplementationNotFound(version); | ||
| } | ||
| return s_inboundImplementations[version]; | ||
| } | ||
|
|
||
| /// @inheritdoc ICrossChainVerifierResolver | ||
| function getOutboundImplementation( | ||
| uint64 destChainSelector | ||
| ) external view returns (address) { | ||
| if (s_outboundImplementations[destChainSelector] == address(0)) { | ||
| revert OutboundImplementationNotFound(destChainSelector); | ||
| } | ||
| return s_outboundImplementations[destChainSelector]; | ||
| } | ||
|
|
||
| /// @notice Updates inbound implementations. | ||
| /// @param versionsToRemove Versions that must no longer be supported by the resolver. | ||
| /// @param implementationsToAdd New verifier contracts and their versions. | ||
| function applyInboundImplementationUpdates( | ||
| bytes4[] calldata versionsToRemove, | ||
| InboundImplementationArgs[] calldata implementationsToAdd | ||
| ) external onlyOwner { | ||
| for (uint256 i = 0; i < versionsToRemove.length; i++) { | ||
kylesmartin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| bytes4 version = versionsToRemove[i]; | ||
| if (s_inboundImplementations[version] == address(0)) { | ||
| revert InboundImplementationNotFound(version); | ||
| } | ||
| delete s_inboundImplementations[version]; | ||
| emit InboundImplementationRemoved(version); | ||
| } | ||
| for (uint256 i = 0; i < implementationsToAdd.length; i++) { | ||
| InboundImplementationArgs memory implementation = implementationsToAdd[i]; | ||
| if (implementation.verifier == address(0)) { | ||
| revert ZeroAddressNotAllowed(); | ||
| } | ||
| if (implementation.version == bytes4(0)) { | ||
| revert InvalidVersion(implementation.version); | ||
| } | ||
| if (s_inboundImplementations[implementation.version] != address(0)) { | ||
kylesmartin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| revert InboundImplementationAlreadyExists(implementation.version); | ||
| } | ||
| s_inboundImplementations[implementation.version] = implementation.verifier; | ||
| emit InboundImplementationAdded(implementation.version, implementation.verifier); | ||
| } | ||
| } | ||
|
|
||
| /// @notice Updates outbound implementations. | ||
| /// @param chainsToRemove Destinations that must no longer be supported by the resolver. | ||
| /// @param implementationsToAdd New verifier contracts and their destination chain selectors. | ||
| function applyOutboundImplementationUpdates( | ||
kylesmartin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| uint64[] calldata chainsToRemove, | ||
| OutboundImplementationArgs[] calldata implementationsToAdd | ||
| ) external onlyOwner { | ||
| for (uint256 i = 0; i < chainsToRemove.length; i++) { | ||
| uint64 destChainSelector = chainsToRemove[i]; | ||
| if (s_outboundImplementations[destChainSelector] == address(0)) { | ||
| revert OutboundImplementationNotFound(destChainSelector); | ||
| } | ||
| delete s_outboundImplementations[destChainSelector]; | ||
| emit OutboundImplementationRemoved(destChainSelector); | ||
| } | ||
| for (uint256 i = 0; i < implementationsToAdd.length; i++) { | ||
| OutboundImplementationArgs memory implementation = implementationsToAdd[i]; | ||
| if (implementation.verifier == address(0)) { | ||
| revert ZeroAddressNotAllowed(); | ||
| } | ||
| if (implementation.destChainSelector == 0) { | ||
| revert InvalidDestChainSelector(implementation.destChainSelector); | ||
| } | ||
| if (s_outboundImplementations[implementation.destChainSelector] != address(0)) { | ||
| revert OutboundImplementationAlreadyExists(implementation.destChainSelector); | ||
| } | ||
| s_outboundImplementations[implementation.destChainSelector] = implementation.verifier; | ||
| emit OutboundImplementationAdded(implementation.destChainSelector, implementation.verifier); | ||
| } | ||
| } | ||
|
|
||
| /// @inheritdoc IERC165 | ||
| function supportsInterface( | ||
kylesmartin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| bytes4 interfaceId | ||
| ) external pure returns (bool) { | ||
| return interfaceId == type(ICrossChainVerifierResolver).interfaceId || interfaceId == type(IERC165).interfaceId; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // SPDX-License-Identifier: BUSL-1.1 | ||
| pragma solidity ^0.8.24; | ||
|
|
||
| import {IERC165} from "@openzeppelin/[email protected]/utils/introspection/IERC165.sol"; | ||
|
|
||
| /// @notice Resolves and returns the appropriate verifier contract for the given outbound / inbound traffic. | ||
| interface ICrossChainVerifierResolver is IERC165 { | ||
| /// @notice Returns the appropriate verifier contract based on the given ccvData. | ||
| /// @dev The OffRamp is responsible for calling this function using the ccvData it receives from the executor. | ||
| /// If the verifier specified by the executor is actually a resolver, the OffRamp will call this function to get the actual verifier contract. | ||
| /// Verifiers can build resolvers that process the ccvData in accordance with how their verifier forms ccvData. For example, their verifier may | ||
| /// prefix the ccvData with a version identifier, which the resolver can parse to determine the correct verifier contract. | ||
| /// @param ccvData The ccvData formed by the verifier. | ||
| /// @return verifierAddress The address of the verifier contract. | ||
| function getInboundImplementation( | ||
| bytes calldata ccvData | ||
| ) external view returns (address); | ||
|
|
||
| /// @notice Returns the appropriate verifier contract based on the given destChainSelector. | ||
| /// @dev The OnRamp is responsible for calling this function using the destChainSelector specified by the sender. | ||
| /// If the verifier specified by the sender is actually a resolver, the OnRamp will call this function to get the actual verifier contract. | ||
| /// For example, resolvers can maintain a simple mapping of destChainSelector to verifier contract address. | ||
| /// @param destChainSelector The destChainSelector for a message. | ||
| /// @return verifierAddress The address of the verifier contract. | ||
| function getOutboundImplementation( | ||
| uint64 destChainSelector | ||
kylesmartin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ) external view returns (address); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,8 @@ pragma solidity ^0.8.24; | |
|
|
||
| import {IAny2EVMMessageReceiver} from "../interfaces/IAny2EVMMessageReceiver.sol"; | ||
| import {IAny2EVMMessageReceiverV2} from "../interfaces/IAny2EVMMessageReceiverV2.sol"; | ||
|
|
||
kylesmartin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| import {ICrossChainVerifierResolver} from "../interfaces/ICrossChainVerifierResolver.sol"; | ||
| import {ICrossChainVerifierV1} from "../interfaces/ICrossChainVerifierV1.sol"; | ||
| import {IPoolV1} from "../interfaces/IPool.sol"; | ||
| import {IPoolV2} from "../interfaces/IPoolV2.sol"; | ||
|
|
@@ -282,7 +284,11 @@ contract OffRamp is ITypeAndVersion, Ownable2StepMsgSender { | |
| _ensureCCVQuorumIsReached(message.sourceChainSelector, receiver, message.tokenTransfer, message.finality, ccvs); | ||
|
|
||
| for (uint256 i = 0; i < ccvsToQuery.length; ++i) { | ||
| ICrossChainVerifierV1(ccvsToQuery[i]).verifyMessage({ | ||
| address ccvAddress = ccvsToQuery[i]; | ||
|
||
| if (ccvAddress._supportsInterfaceReverting(type(ICrossChainVerifierResolver).interfaceId)) { | ||
kylesmartin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ccvAddress = ICrossChainVerifierResolver(ccvAddress).getInboundImplementation(ccvData[ccvDataIndex[i]]); | ||
| } | ||
| ICrossChainVerifierV1(ccvAddress).verifyMessage({ | ||
| message: message, | ||
| messageId: messageId, | ||
| ccvData: ccvData[ccvDataIndex[i]] | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.