Skip to content

Commit ed0d055

Browse files
committed
receiver/offramp
1 parent 96e7860 commit ed0d055

12 files changed

Lines changed: 107 additions & 124 deletions

File tree

chains/evm/contracts/applications/CCIPClientExampleWithCCVs.sol

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,11 @@ contract CCIPClientExampleWithCCVs is CCIPClientExample {
104104
}
105105
}
106106

107-
/// @notice Provides the required and optional CCVs and min block confirmations for a source chain.
107+
/// @notice Provides the required and optional CCVs and allowed finality config for a source chain.
108108
/// @dev OffRamp will apply the defaults for the lane if no CCVs are defined for a source chain.
109-
/// @dev WARNING: minBlockConfirmations must be used carefully, when in doubt it's safer to require finality (minBlockConfirmations = 0) than
109+
/// @dev WARNING: allowedFinalityConfig must be used carefully, when in doubt it's safer to require finality (allowedFinalityConfig = bytes2(0)) than
110110
/// to allow FTF messages (any non-zero value) as FTF messages can be reorged.
111-
function getCCVsAndMinBlockConfirmations(
111+
function getCCVsAndFinalityConfig(
112112
uint64 sourceChainSelector,
113113
bytes calldata
114114
)
@@ -120,14 +120,14 @@ contract CCIPClientExampleWithCCVs is CCIPClientExample {
120120
address[] memory requiredCCVs,
121121
address[] memory optionalCCVs,
122122
uint8 optionalThreshold,
123-
uint16 minBlockConfirmations
123+
bytes2 allowedFinalityConfig
124124
)
125125
{
126126
CCVConfig memory config = s_ccvConfigs[sourceChainSelector];
127-
// If allowFasterThanFinality is true, minBlockConfirmations = 1 (allow any FTF level) - WARNING only use a finality of 1 when
127+
// If allowFasterThanFinality is true, allowedFinalityConfig = bytes2(uint16(1)) (allow any FTF level) - WARNING only use a finality of 1 when
128128
// you use a trusted sender on the source chain that manages the finality risk when sending messages.
129-
// If allowFasterThanFinality is false, minBlockConfirmations = 0 (require finality).
130-
minBlockConfirmations = config.allowFasterThanFinality ? 1 : 0;
131-
return (config.requiredCCVs, config.optionalCCVs, config.optionalThreshold, minBlockConfirmations);
129+
// If allowFasterThanFinality is false, allowedFinalityConfig = bytes2(0) (require finality).
130+
allowedFinalityConfig = config.allowFasterThanFinality ? bytes2(uint16(1)) : bytes2(0);
131+
return (config.requiredCCVs, config.optionalCCVs, config.optionalThreshold, allowedFinalityConfig);
132132
}
133133
}

chains/evm/contracts/applications/CCIPReceiver.sol

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ abstract contract CCIPReceiver is IAny2EVMMessageReceiverV2, IERC165 {
5555
return address(i_ccipRouter);
5656
}
5757

58-
/// @notice Return the CCVs required/optional and min block confirmations for a source chain.
58+
/// @notice Return the CCVs required/optional and allowed finality config for a source chain.
5959
/// @dev This can be overridden to specify different CCVs per source chain. The current implementation means the
60-
/// default CCV is used and finality is required (minBlockConfirmations = 0).
61-
function getCCVsAndMinBlockConfirmations(
60+
/// default CCV is used and finality is required (allowedFinalityConfig = bytes2(0)).
61+
function getCCVsAndFinalityConfig(
6262
uint64,
6363
bytes calldata
6464
)
@@ -69,12 +69,12 @@ abstract contract CCIPReceiver is IAny2EVMMessageReceiverV2, IERC165 {
6969
address[] memory requiredCCVs,
7070
address[] memory optionalCCVs,
7171
uint8 optionalThreshold,
72-
uint16 minBlockConfirmations
72+
bytes2 allowedFinalityConfig
7373
)
7474
{
7575
// By default no specific CCVs are required or optional. This means the default CCV is chosen.
76-
// minBlockConfirmations = 0 means finality is required.
77-
return (new address[](0), new address[](0), 0, 0);
76+
// allowedFinalityConfig = bytes2(0) means finality is required.
77+
return (new address[](0), new address[](0), 0, bytes2(0));
7878
}
7979

8080
error InvalidRouter(address router);

chains/evm/contracts/interfaces/IAny2EVMMessageReceiverV2.sol

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,22 @@ import {IAny2EVMMessageReceiver} from "./IAny2EVMMessageReceiver.sol";
55

66
interface IAny2EVMMessageReceiverV2 is IAny2EVMMessageReceiver {
77
/// @notice Get the CCV configuration & minimum accepted block confirmations for a source chain and sender.
8-
/// @dev Implementations must return an appropriate minBlockConfirmations value. Returning 0 signals that only fully finalized
9-
/// messages are accepted. Returning a non-zero value allows faster-than-finality (FTF) messages whose requested block
10-
/// depth is at least minBlockConfirmations. If a suitable minBlockConfirmations is not returned, anyone will be able to send messages
11-
/// with any level of finality to the receiver. In most cases, the receiver will want to require a certain level of
12-
/// finality. When a trusted sender is used (and verified by the receiver), this is less critical as the trusted sender
8+
/// @dev Implementations must return an appropriate allowedFinalityConfig value. Returning bytes2(0) signals that only fully finalized
9+
/// messages are accepted. Returning a non-zero value allows faster-than-finality (FTF) messages whose requested
10+
/// finality is permitted by the allowedFinalityConfig (see `FinalityCodec._ensureRequestedFinalityAllowed`).
11+
/// When a trusted sender is used (and verified by the receiver), this is less critical as the trusted sender
1312
/// will only send messages with a certain level of finality. The simplest way to implement this is to either allow FTF
1413
/// messages when sender-verification is used, or require finality for all messages. That means the config can be a
1514
/// simple boolean instead of n^2 config where for each source, some safe block confirmations must be chosen.
1615
///
1716
/// A few methods to check the block confirmations requirement are:
18-
/// - Only allow trusted senders, always return `1` to signal any level of FTF
19-
/// - Return a single threshold (e.g. 10 blocks) for all chains
20-
/// - Return a threshold specific to the source chain (e.g. 10 blocks for chain A, 20 blocks for chain B)
21-
/// - Do not allow FTF messages at all, always return `0`
17+
/// - Only allow trusted senders, allow FTF broadly as the senders will decide on the final value
18+
/// - Return a single config for all chains
19+
/// - Return a config specific to the source chain (e.g. 10 blocks for chain A, 20 blocks for chain B)
20+
/// - Do not allow FTF messages at all, always return the WAIT_FOR_FINALITY_FLAG.
2221
///
2322
/// @param sourceChainSelector The source chain selector of the incoming message. This can be used to specify
24-
/// different CCV requirements for different source chains, and provides context for the blockConfirmationsRequested parameter.
23+
/// different CCV requirements for different source chains, and provides context for the allowed finality config.
2524
/// @param sender The sender of the message on the source chain. This can be used to implement sender-specific
2625
/// security policies, such as allowing FTF only for trusted senders.
2726
/// @dev Messages are executable when either the required block confirmations has been reached, or the chain has marked the
@@ -33,10 +32,9 @@ interface IAny2EVMMessageReceiverV2 is IAny2EVMMessageReceiver {
3332
/// included, the optionalThreshold parameter must also be set to indicate how many of the optional CCVs must pass
3433
/// verification for a message to be accepted.
3534
/// @return optionalThreshold The number of optional CCVs that must pass verification for a message to be accepted.
36-
/// @return minBlockConfirmations The minimum block confirmations the receiver requires for FTF messages. A value of 0 means only
37-
/// finalized messages are accepted. A non-zero value allows FTF messages whose requested block confirmations meets or
38-
/// exceeds this threshold.
39-
function getCCVsAndMinBlockConfirmations(
35+
/// @return allowedFinalityConfig The allowed finality config, encoded according to the `FinalityCodec`. This allows
36+
/// the receiver to specify which finality configs are accepted for messages from this source chain.
37+
function getCCVsAndFinalityConfig(
4038
uint64 sourceChainSelector,
4139
bytes calldata sender
4240
)
@@ -46,6 +44,6 @@ interface IAny2EVMMessageReceiverV2 is IAny2EVMMessageReceiver {
4644
address[] memory requiredCCVs,
4745
address[] memory optionalCCVs,
4846
uint8 optionalThreshold,
49-
uint16 minBlockConfirmations
47+
bytes2 allowedFinalityConfig
5048
);
5149
}

chains/evm/contracts/offRamp/OffRamp.sol

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,25 @@ import {IAny2EVMMessageReceiver} from "../interfaces/IAny2EVMMessageReceiver.sol
55
import {IAny2EVMMessageReceiverV2} from "../interfaces/IAny2EVMMessageReceiverV2.sol";
66
import {ICrossChainVerifierResolver} from "../interfaces/ICrossChainVerifierResolver.sol";
77
import {ICrossChainVerifierV1} from "../interfaces/ICrossChainVerifierV1.sol";
8+
import {CCVConfigValidation} from "../libraries/CCVConfigValidation.sol";
9+
import {Client} from "../libraries/Client.sol";
10+
import {ERC165CheckerReverting} from "../libraries/ERC165CheckerReverting.sol";
11+
import "../libraries/FinalityCodec.sol";
12+
import {IERC20} from "@openzeppelin/contracts@5.3.0/token/ERC20/IERC20.sol";
13+
import {EnumerableSet} from "@openzeppelin/contracts@5.3.0/utils/structs/EnumerableSet.sol";
14+
815
import {IPoolV1} from "../interfaces/IPool.sol";
916
import {IPoolV2} from "../interfaces/IPoolV2.sol";
1017
import {IRMNRemote} from "../interfaces/IRMNRemote.sol";
1118
import {IRouter} from "../interfaces/IRouter.sol";
1219
import {ITokenAdminRegistry} from "../interfaces/ITokenAdminRegistry.sol";
20+
import {Internal} from "../libraries/Internal.sol";
1321
import {ITypeAndVersion} from "@chainlink/contracts/src/v0.8/shared/interfaces/ITypeAndVersion.sol";
1422

15-
import {CCVConfigValidation} from "../libraries/CCVConfigValidation.sol";
16-
import {Client} from "../libraries/Client.sol";
17-
import {ERC165CheckerReverting} from "../libraries/ERC165CheckerReverting.sol";
18-
import {Internal} from "../libraries/Internal.sol";
1923
import {MessageV1Codec} from "../libraries/MessageV1Codec.sol";
2024
import {Pool} from "../libraries/Pool.sol";
2125
import {Ownable2StepMsgSender} from "@chainlink/contracts/src/v0.8/shared/access/Ownable2StepMsgSender.sol";
2226

23-
import {IERC20} from "@openzeppelin/contracts@5.3.0/token/ERC20/IERC20.sol";
24-
import {EnumerableSet} from "@openzeppelin/contracts@5.3.0/utils/structs/EnumerableSet.sol";
25-
2627
contract OffRamp is ITypeAndVersion, Ownable2StepMsgSender {
2728
using ERC165CheckerReverting for address;
2829
using EnumerableSet for EnumerableSet.UintSet;
@@ -44,7 +45,6 @@ contract OffRamp is ITypeAndVersion, Ownable2StepMsgSender {
4445
error SkippedAlreadyExecutedMessage(bytes32 messageId, uint64 sourceChainSelector, uint64 messageNumber);
4546
error ReentrancyGuardReentrantCall();
4647
error RequiredCCVMissing(address requiredCCV);
47-
error InvalidFinalityForReceiver(address receiver, uint16 msgBlockDepth, uint16 requiredBlockDepth);
4848
error InvalidNumberOfTokens(uint256 numTokens);
4949
error InvalidOnRamp(bytes got);
5050
error InvalidOffRamp(address expected, bytes got);
@@ -693,16 +693,16 @@ contract OffRamp is ITypeAndVersion, Ownable2StepMsgSender {
693693
bytes memory sender,
694694
bytes2 messageRequestedFinality
695695
) internal view returns (address[] memory requiredCCV, address[] memory optionalCCVs, uint8 optionalThreshold) {
696-
// Default block confirmations requirement is 0, which means "wait for finality". A receiver implementing
697-
// IAny2EVMMessageReceiverV2 can return a different value.
696+
// Default finality config is 0, which means "wait for finality". A receiver implementing IAny2EVMMessageReceiverV2
697+
// can return a different value.
698698
// If the receiver does not support the V2 interface it cannot support FTF. This is to protect anyone not
699699
// explicitly opting in to support FTF from accidentally allowing messages with FTF finality to be executed.
700-
uint16 minBlockConfirmations;
700+
bytes2 receiverFinalityConfig = bytes2(0);
701701

702702
// Only query for custom CCVs if the receiver supports the interface.
703703
if (receiver._supportsInterfaceReverting(type(IAny2EVMMessageReceiverV2).interfaceId)) {
704-
(requiredCCV, optionalCCVs, optionalThreshold, minBlockConfirmations) =
705-
IAny2EVMMessageReceiverV2(receiver).getCCVsAndMinBlockConfirmations(sourceChainSelector, sender);
704+
(requiredCCV, optionalCCVs, optionalThreshold, receiverFinalityConfig) =
705+
IAny2EVMMessageReceiverV2(receiver).getCCVsAndFinalityConfig(sourceChainSelector, sender);
706706

707707
CCVConfigValidation._assertNoDuplicates(requiredCCV);
708708
CCVConfigValidation._assertNoDuplicates(optionalCCVs);
@@ -713,18 +713,8 @@ contract OffRamp is ITypeAndVersion, Ownable2StepMsgSender {
713713
}
714714
}
715715

716-
// If non-zero it means FTF is enabled. Ensure it follows the min requirements from the receiver.
717-
uint16 requestedWire = uint16(messageRequestedFinality);
718-
if (requestedWire != 0) {
719-
// If the receiver requires finality, but the msg is FTF, revert.
720-
if (minBlockConfirmations == 0) {
721-
revert InvalidFinalityForReceiver(receiver, requestedWire, minBlockConfirmations);
722-
}
723-
// If the receiver specified a minBlockConfirmations that is higher than the message requested block confirmations, revert.
724-
if (minBlockConfirmations > requestedWire) {
725-
revert InvalidFinalityForReceiver(receiver, requestedWire, minBlockConfirmations);
726-
}
727-
}
716+
// Check the finality requirement of the message against the allowed finality for the receiver.
717+
FinalityCodec._ensureRequestedFinalityAllowed(messageRequestedFinality, receiverFinalityConfig);
728718

729719
// If the receiver specified empty required and optional CCVs, we fall back to the default CCVs.
730720
// If they did specify something, we use what they specified.
@@ -742,6 +732,7 @@ contract OffRamp is ITypeAndVersion, Ownable2StepMsgSender {
742732
/// @param localToken The local token address.
743733
/// @param sourceChainSelector The source chain selector.
744734
/// @param amount The amount of the token to be released/minted.
735+
/// @param finality The finality requirement of the message (see `FinalityCodec`).
745736
/// @param extraData The extra data for the pool.
746737
/// @return requiredCCV The required CCVs.
747738
function _getCCVsFromPool(

chains/evm/contracts/onRamp/OnRamp.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ contract OnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, Ownable2StepMsgSender
4747
error ReentrancyGuardReentrantCall();
4848
error DestinationChainNotSupported(uint64 destChainSelector);
4949
error InvalidDestChainAddress(bytes destChainAddress);
50-
error CustomBlockConfirmationsNotSupportedOnPoolV1();
50+
error FTFNotSupportedOnPoolV1();
5151
error TokenArgsNotSupportedOnPoolV1();
5252
error InsufficientFeeTokenAmount();
5353
error TokenReceiverNotAllowed(uint64 destChainSelector);
@@ -756,7 +756,7 @@ contract OnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, Ownable2StepMsgSender
756756
// V1 pools don't understand `finalityConfig`/`tokenArgs`.
757757
// We enforce default finality and no `tokenArgs` to avoid silent mis-interpretation.
758758
if (finalityConfig != bytes2(0)) {
759-
revert CustomBlockConfirmationsNotSupportedOnPoolV1();
759+
revert FTFNotSupportedOnPoolV1();
760760
}
761761
if (tokenArgs.length != 0) {
762762
revert TokenArgsNotSupportedOnPoolV1();

chains/evm/contracts/test/applications/CCIPClientExampleWithCCVs/CCIPClientExampleWithCCVs.applyCCVConfigUpdates.t.sol

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ contract CCIPClientExampleWithCCVs_applyCCVConfigUpdates is RouterSetup {
4444
address[] memory retRequiredCCVs,
4545
address[] memory retOptionalCCVs,
4646
uint8 retOptionalThreshold,
47-
uint16 minBlockConfirmations
48-
) = s_client.getCCVsAndMinBlockConfirmations(SOURCE_CHAIN_SELECTOR, sender);
47+
bytes2 allowedFinalityConfig
48+
) = s_client.getCCVsAndFinalityConfig(SOURCE_CHAIN_SELECTOR, sender);
4949
assertEq(retRequiredCCVs.length, requiredCCVs.length);
5050
assertEq(retOptionalCCVs.length, optionalCCVs.length);
5151
assertEq(retOptionalThreshold, optionalThreshold);
52-
assertEq(minBlockConfirmations, 1);
52+
assertEq(allowedFinalityConfig, bytes2(uint16(1)));
5353
for (uint256 i = 0; i < requiredCCVs.length; ++i) {
5454
assertEq(retRequiredCCVs[i], requiredCCVs[i]);
5555
}
@@ -153,7 +153,7 @@ contract CCIPClientExampleWithCCVs_applyCCVConfigUpdates is RouterSetup {
153153
s_client.applyCCVConfigUpdates(args);
154154
}
155155

156-
function test_getCCVsAndMinBlockConfirmations_RequireFinality_ReturnsZeroMinBlockConfirmations() public {
156+
function test_getCCVsAndFinalityConfig_RequireFinality_ReturnsZeroAllowedFinalityConfig() public {
157157
address[] memory requiredCCVs = new address[](1);
158158
requiredCCVs[0] = address(0x1);
159159

@@ -170,14 +170,14 @@ contract CCIPClientExampleWithCCVs_applyCCVConfigUpdates is RouterSetup {
170170

171171
bytes memory sender = abi.encodePacked(makeAddr("sender"));
172172

173-
(address[] memory retRequired,,, uint16 minBlockConfirmations) =
174-
s_client.getCCVsAndMinBlockConfirmations(SOURCE_CHAIN_SELECTOR, sender);
173+
(address[] memory retRequired,,, bytes2 allowedFinalityConfig) =
174+
s_client.getCCVsAndFinalityConfig(SOURCE_CHAIN_SELECTOR, sender);
175175
assertEq(retRequired.length, 1);
176176
assertEq(retRequired[0], address(0x1));
177-
assertEq(minBlockConfirmations, 0);
177+
assertEq(allowedFinalityConfig, bytes2(0));
178178
}
179179

180-
function test_getCCVsAndMinBlockConfirmations_NoRequireFinality_ReturnsNonZeroMinBlockConfirmations() public {
180+
function test_getCCVsAndFinalityConfig_NoRequireFinality_ReturnsNonZeroAllowedFinalityConfig() public {
181181
address[] memory requiredCCVs = new address[](1);
182182
requiredCCVs[0] = address(0x1);
183183

@@ -194,10 +194,10 @@ contract CCIPClientExampleWithCCVs_applyCCVConfigUpdates is RouterSetup {
194194

195195
bytes memory sender = abi.encodePacked(makeAddr("sender"));
196196

197-
(address[] memory retRequired,,, uint16 minBlockConfirmations) =
198-
s_client.getCCVsAndMinBlockConfirmations(SOURCE_CHAIN_SELECTOR, sender);
197+
(address[] memory retRequired,,, bytes2 allowedFinalityConfig) =
198+
s_client.getCCVsAndFinalityConfig(SOURCE_CHAIN_SELECTOR, sender);
199199
assertEq(retRequired.length, 1);
200200
assertEq(retRequired[0], address(0x1));
201-
assertEq(minBlockConfirmations, 1);
201+
assertEq(allowedFinalityConfig, bytes2(uint16(1)));
202202
}
203203
}

chains/evm/contracts/test/mocks/MockReceiverV2.sol

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ contract MockReceiverV2 is IAny2EVMMessageReceiverV2, IERC165 {
1010
address[] internal s_required;
1111
address[] internal s_optional;
1212
uint8 internal s_threshold;
13-
uint16 internal s_minBlockConfirmations;
13+
bytes2 internal s_allowedFinalityConfig;
1414

1515
constructor(
1616
address[] memory required,
@@ -22,10 +22,10 @@ contract MockReceiverV2 is IAny2EVMMessageReceiverV2, IERC165 {
2222
s_threshold = threshold;
2323
}
2424

25-
function setMinBlockConfirmations(
26-
uint16 minBlockConfirmations
25+
function setAllowedFinalityConfig(
26+
bytes2 allowedFinalityConfig
2727
) external {
28-
s_minBlockConfirmations = minBlockConfirmations;
28+
s_allowedFinalityConfig = allowedFinalityConfig;
2929
}
3030

3131
// From IAny2EVMMessageReceiver
@@ -34,7 +34,7 @@ contract MockReceiverV2 is IAny2EVMMessageReceiverV2, IERC165 {
3434
) external {}
3535

3636
// From IAny2EVMMessageReceiverV2
37-
function getCCVsAndMinBlockConfirmations(
37+
function getCCVsAndFinalityConfig(
3838
uint64, // sourceChainSelector
3939
bytes calldata // sender
4040
)
@@ -44,10 +44,10 @@ contract MockReceiverV2 is IAny2EVMMessageReceiverV2, IERC165 {
4444
address[] memory requiredVerifier,
4545
address[] memory optionalVerifiers,
4646
uint8 threshold,
47-
uint16 minBlockDepth
47+
bytes2 allowedFinalityConfig
4848
)
4949
{
50-
return (s_required, s_optional, s_threshold, s_minBlockConfirmations);
50+
return (s_required, s_optional, s_threshold, s_allowedFinalityConfig);
5151
}
5252

5353
function supportsInterface(

0 commit comments

Comments
 (0)