Skip to content

Commit

Permalink
Merge branch 'ccip-develop' into feature/selfServeFactory
Browse files Browse the repository at this point in the history
  • Loading branch information
jhweintraub committed Sep 18, 2024
2 parents 65395df + f1adcc2 commit 58bdf08
Show file tree
Hide file tree
Showing 190 changed files with 8,495 additions and 3,238 deletions.
2 changes: 1 addition & 1 deletion .github/actions/build-test-image/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ runs:
uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/mod-version@fc3e0df622521019f50d772726d6bf8dc919dd38 # v2.3.19
with:
go-project-path: ./integration-tests
module-name: github.com/smartcontractkit/chainlink-testing-framework
module-name: github.com/smartcontractkit/chainlink-testing-framework/lib
enforce-semantic-tag: false
- name: Get CTF sha
if: steps.version.outputs.is_semantic == 'false'
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/ccip-load-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ on:
description: 'Key to run tests with custom test secrets'
required: false
type: string
pull_request: # DEBUG: Checking run conditions

# Only run 1 of this workflow at a time per PR
concurrency:
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/mod-version@fc3e0df622521019f50d772726d6bf8dc919dd38 # v2.3.19
with:
go-project-path: ./integration-tests
module-name: github.com/smartcontractkit/chainlink-testing-framework
module-name: github.com/smartcontractkit/chainlink-testing-framework/lib
enforce-semantic-tag: "true"

changes:
Expand Down Expand Up @@ -373,21 +373,21 @@ jobs:
file: ccip
run: -run ^TestSmokeCCIPForBidirectionalLane$
config_path: ./integration-tests/ccip-tests/testconfig/tomls/leader-lane.toml
- name: ccip-smoke-reorg
- name: ccip-smoke-reorg-bidirectional
nodes: 1
dir: ccip-tests/smoke
os: ubuntu-latest
file: ccip
run: -run ^TestSmokeCCIPReorgBelowFinality$ -v
config_path: ./integration-tests/ccip-tests/testconfig/tomls/ccip-reorg.toml
- name: ccip-smoke-reorg
- name: ccip-smoke-reorg-below-finality
nodes: 1
dir: ccip-tests/smoke
os: ubuntu-latest
file: ccip
run: -run ^TestSmokeCCIPReorgAboveFinalityAtDestination$ -v
config_path: ./integration-tests/ccip-tests/testconfig/tomls/ccip-reorg.toml
- name: ccip-smoke-reorg
- name: ccip-smoke-reorg-above-finality
nodes: 1
dir: ccip-tests/smoke
os: ubuntu-latest
Expand Down
7 changes: 5 additions & 2 deletions contracts/.solhintignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,8 @@
./src/v0.8/vendor
./node_modules/

# Ignore RMN contracts temporarily
./src/v0.8/ccip/rmn
# Ignore RMNHome contract temporarily
./src/v0.8/ccip/rmn/RMNHome.sol

# Ignore tweaked vendored contracts
./src/v0.8/shared/enumerable/EnumerableSetWithBytes16.sol
890 changes: 459 additions & 431 deletions contracts/gas-snapshots/ccip.gas-snapshot

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions contracts/src/v0.8/ccip/FeeQuoter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -862,13 +862,13 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver,
}

/// @inheritdoc IFeeQuoter
/// @dev precondition - rampTokenAmounts and sourceTokenAmounts lengths must be equal
/// @dev precondition - onRampTokenTransfers and sourceTokenAmounts lengths must be equal
function processMessageArgs(
uint64 destChainSelector,
address feeToken,
uint256 feeTokenAmount,
bytes calldata extraArgs,
Internal.RampTokenAmount[] calldata rampTokenAmounts,
Internal.EVM2AnyTokenTransfer[] calldata onRampTokenTransfers,
Client.EVMTokenAmount[] calldata sourceTokenAmounts
)
external
Expand All @@ -894,37 +894,37 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver,
// We can parse unvalidated args since this message is called after getFee (which will already validate the params)
Client.EVMExtraArgsV2 memory parsedExtraArgs = _parseUnvalidatedEVMExtraArgsFromBytes(extraArgs, defaultTxGasLimit);
isOutOfOrderExecution = parsedExtraArgs.allowOutOfOrderExecution;
destExecDataPerToken = _processPoolReturnData(destChainSelector, rampTokenAmounts, sourceTokenAmounts);
destExecDataPerToken = _processPoolReturnData(destChainSelector, onRampTokenTransfers, sourceTokenAmounts);

return (msgFeeJuels, isOutOfOrderExecution, Client._argsToBytes(parsedExtraArgs), destExecDataPerToken);
}

/// @notice Validates pool return data
/// @param destChainSelector Destination chain selector to which the token amounts are sent to
/// @param rampTokenAmounts Token amounts with populated pool return data
/// @param onRampTokenTransfers Token amounts with populated pool return data
/// @param sourceTokenAmounts Token amounts originally sent in a Client.EVM2AnyMessage message
/// @return destExecDataPerToken Destination chain execution data
function _processPoolReturnData(
uint64 destChainSelector,
Internal.RampTokenAmount[] calldata rampTokenAmounts,
Internal.EVM2AnyTokenTransfer[] calldata onRampTokenTransfers,
Client.EVMTokenAmount[] calldata sourceTokenAmounts
) internal view returns (bytes[] memory destExecDataPerToken) {
bytes4 chainFamilySelector = s_destChainConfigs[destChainSelector].chainFamilySelector;
destExecDataPerToken = new bytes[](rampTokenAmounts.length);
for (uint256 i = 0; i < rampTokenAmounts.length; ++i) {
destExecDataPerToken = new bytes[](onRampTokenTransfers.length);
for (uint256 i = 0; i < onRampTokenTransfers.length; ++i) {
address sourceToken = sourceTokenAmounts[i].token;

// Since the DON has to pay for the extraData to be included on the destination chain, we cap the length of the
// extraData. This prevents gas bomb attacks on the NOPs. As destBytesOverhead accounts for both
// extraData and offchainData, this caps the worst case abuse to the number of bytes reserved for offchainData.
uint256 destPoolDataLength = rampTokenAmounts[i].extraData.length;
uint256 destPoolDataLength = onRampTokenTransfers[i].extraData.length;
if (destPoolDataLength > Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) {
if (destPoolDataLength > s_tokenTransferFeeConfig[destChainSelector][sourceToken].destBytesOverhead) {
revert SourceTokenDataTooLarge(sourceToken);
}
}

_validateDestFamilyAddress(chainFamilySelector, rampTokenAmounts[i].destTokenAddress);
_validateDestFamilyAddress(chainFamilySelector, onRampTokenTransfers[i].destTokenAddress);
FeeQuoter.TokenTransferFeeConfig memory tokenTransferFeeConfig =
s_tokenTransferFeeConfig[destChainSelector][sourceToken];
uint32 defaultGasOverhead = s_destChainConfigs[destChainSelector].defaultTokenDestGasOverhead;
Expand Down
89 changes: 71 additions & 18 deletions contracts/src/v0.8/ccip/capability/CCIPConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator
error ChainSelectorNotSet();
error TooManyOCR3Configs();
error TooManySigners();
error P2PIdsLengthNotMatching(uint256 p2pIdsLength, uint256 signersLength, uint256 transmittersLength);
error InvalidNode(CCIPConfigTypes.OCR3Node node);
error NotEnoughTransmitters(uint256 got, uint256 minimum);
error FMustBePositive();
error FChainMustBePositive();
error FTooHigh();
error FChainTooHigh(uint256 fChain, uint256 FRoleDON);
error InvalidPluginType();
error OfframpAddressCannotBeZero();
error InvalidConfigLength(uint256 length);
Expand Down Expand Up @@ -167,6 +167,7 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator
}

/// @notice Called by the registry prior to the config being set for a particular DON.
/// @dev precondition Requires destination chain config to be set
function beforeCapabilityConfigSet(
bytes32[] calldata, /* nodes */
bytes calldata config,
Expand Down Expand Up @@ -213,7 +214,32 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator
// We won't run out of gas from this delete since the array is at most 2 elements long.
delete s_ocr3Configs[donId][pluginType];
for (uint256 i = 0; i < newConfigWithMeta.length; ++i) {
s_ocr3Configs[donId][pluginType].push(newConfigWithMeta[i]);
// Struct has to be manually copied since there is a nested OCR3Node array. Direct assignment
// will result in Unimplemented Feature issue.
CCIPConfigTypes.OCR3ConfigWithMeta storage ocr3ConfigWithMeta = s_ocr3Configs[donId][pluginType].push();
ocr3ConfigWithMeta.configDigest = newConfigWithMeta[i].configDigest;
ocr3ConfigWithMeta.configCount = newConfigWithMeta[i].configCount;

CCIPConfigTypes.OCR3Config storage ocr3Config = ocr3ConfigWithMeta.config;
CCIPConfigTypes.OCR3Config memory newOcr3Config = newConfigWithMeta[i].config;
ocr3Config.pluginType = newOcr3Config.pluginType;
ocr3Config.chainSelector = newOcr3Config.chainSelector;
ocr3Config.FRoleDON = newOcr3Config.FRoleDON;
ocr3Config.offchainConfigVersion = newOcr3Config.offchainConfigVersion;
ocr3Config.offrampAddress = newOcr3Config.offrampAddress;
ocr3Config.offchainConfig = newOcr3Config.offchainConfig;

// Remove all excess nodes
while (ocr3Config.nodes.length > newOcr3Config.nodes.length) {
ocr3Config.nodes.pop();
}

// Assign nodes
for (uint256 j = 0; j < newOcr3Config.nodes.length; ++j) {
if (j >= ocr3Config.nodes.length) {
ocr3Config.nodes.push(newOcr3Config.nodes[j]);
}
}
}

emit ConfigSet(donId, uint8(pluginType), newConfigWithMeta);
Expand Down Expand Up @@ -402,21 +428,49 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator
}
if (!s_remoteChainSelectors.contains(cfg.chainSelector)) revert ChainSelectorNotFound(cfg.chainSelector);

// We check for chain config presence above, so fChain here must be non-zero.
uint256 minTransmittersLength = 3 * s_chainConfigurations[cfg.chainSelector].fChain + 1;
if (cfg.transmitters.length < minTransmittersLength) {
revert NotEnoughTransmitters(cfg.transmitters.length, minTransmittersLength);
// fChain cannot exceed FRoleDON, since it is a subcommittee in the larger DON
uint256 FRoleDON = cfg.FRoleDON;
uint256 fChain = s_chainConfigurations[cfg.chainSelector].fChain;
// fChain > 0 is enforced in applyChainConfigUpdates, and the presence of a chain config is checked above
// FRoleDON != 0 because FRoleDON >= fChain is enforced here
if (fChain > FRoleDON) {
revert FChainTooHigh(fChain, FRoleDON);
}

// len(nodes) >= 3 * FRoleDON + 1
// len(nodes) == numberOfSigners
uint256 numberOfNodes = cfg.nodes.length;
if (numberOfNodes > MAX_NUM_ORACLES) revert TooManySigners();
if (numberOfNodes <= 3 * FRoleDON) revert FTooHigh();

uint256 nonZeroTransmitters = 0;
bytes32[] memory p2pIds = new bytes32[](numberOfNodes);
for (uint256 i = 0; i < numberOfNodes; ++i) {
CCIPConfigTypes.OCR3Node memory node = cfg.nodes[i];

// 3 * fChain + 1 <= nonZeroTransmitters <= 3 * FRoleDON + 1
// Transmitters can be set to 0 since there can be more signers than transmitters,
if (node.transmitterKey.length != 0) {
nonZeroTransmitters++;
}

// Signer key and p2pIds must always be present
if (node.signerKey.length == 0 || node.p2pId == bytes32(0)) {
revert InvalidNode(node);
}

p2pIds[i] = node.p2pId;
}
uint256 numberOfSigners = cfg.signers.length;
if (numberOfSigners > MAX_NUM_ORACLES) revert TooManySigners();
if (numberOfSigners != cfg.p2pIds.length || numberOfSigners != cfg.transmitters.length) {
revert P2PIdsLengthNotMatching(cfg.p2pIds.length, cfg.signers.length, cfg.transmitters.length);

// We check for chain config presence above, so fChain here must be non-zero. fChain <= FRoleDON due to the checks above.
// There can be less transmitters than signers - so they can be set to zero (which indicates that a node is a signer, but not a transmitter).
uint256 minTransmittersLength = 3 * fChain + 1;
if (nonZeroTransmitters < minTransmittersLength) {
revert NotEnoughTransmitters(nonZeroTransmitters, minTransmittersLength);
}
if (cfg.F == 0) revert FMustBePositive();
if (numberOfSigners <= 3 * cfg.F) revert FTooHigh();

// Check that the readers are in the capabilities registry.
_ensureInRegistry(cfg.p2pIds);
_ensureInRegistry(p2pIds);
}

/// @notice Computes the digest of the provided configuration.
Expand All @@ -441,10 +495,8 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator
ocr3Config.pluginType,
ocr3Config.offrampAddress,
configCount,
ocr3Config.p2pIds,
ocr3Config.signers,
ocr3Config.transmitters,
ocr3Config.F,
ocr3Config.nodes,
ocr3Config.FRoleDON,
ocr3Config.offchainConfigVersion,
ocr3Config.offchainConfig
)
Expand All @@ -459,6 +511,7 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator
// ================================================================

/// @notice Sets and/or removes chain configurations.
/// Does not validate that fChain <= FRoleDON and relies on OCR3Configs to be changed in case fChain becomes larger than the FRoleDON value.
/// @param chainSelectorRemoves The chain configurations to remove.
/// @param chainConfigAdds The chain configurations to add.
function applyChainConfigUpdates(
Expand Down
20 changes: 13 additions & 7 deletions contracts/src/v0.8/ccip/capability/libraries/CCIPConfigTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,25 @@ library CCIPConfigTypes {
ChainConfig chainConfig;
}

/// @notice Represents an oracle node in OCR3 configs part of the role DON.
/// Every configured node should be a signer, but does not have to be a transmitter.
struct OCR3Node {
bytes32 p2pId; // Peer2Peer connection ID of the oracle
bytes signerKey; // On-chain signer public key
bytes transmitterKey; // On-chain transmitter public key. Can be set to empty bytes to represent that the node is a signer but not a transmitter.
}

/// @notice OCR3 configuration.
/// Note that FRoleDON >= fChain, since FRoleDON represents the role DON, and fChain represents sub-committees.
/// FRoleDON values are typically identical across multiple OCR3 configs since the chains pertain to one role DON,
/// but FRoleDON values can change across OCR3 configs to indicate role DON splits.
struct OCR3Config {
Internal.OCRPluginType pluginType; // ────────╮ The plugin that the configuration is for.
uint64 chainSelector; // | The (remote) chain that the configuration is for.
uint8 F; // | The "big F" parameter for the role DON.
uint8 FRoleDON; // | The "big F" parameter for the role DON.
uint64 offchainConfigVersion; // ─────────────╯ The version of the offchain configuration.
bytes offrampAddress; // The remote chain offramp address.
// len(p2pIds) == len(signers) == len(transmitters) == 3 * F + 1
// NOTE: indexes matter here! The p2p ID at index i corresponds to the signer at index i and the transmitter at index i.
// This is crucial in order to build the oracle ID <-> peer ID mapping offchain.
bytes32[] p2pIds; // The P2P IDs of the oracles that are part of the role DON.
bytes[] signers; // The onchain signing keys of nodes in the don.
bytes[] transmitters; // The onchain transmitter keys of nodes in the don.
OCR3Node[] nodes; // Keys & IDs of nodes part of the role DON
bytes offchainConfig; // The offchain configuration for the OCR3 protocol. Protobuf encoded.
}

Expand Down
4 changes: 2 additions & 2 deletions contracts/src/v0.8/ccip/interfaces/IFeeQuoter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface IFeeQuoter is IPriceRegistry {
/// @param feeToken Fee token address used to pay for message fees
/// @param feeTokenAmount Fee token amount
/// @param extraArgs Message extra args that were passed in by the client
/// @param rampTokenAmounts Token amounts with populated pool return data
/// @param onRampTokenTransfers Token amounts with populated pool return data
/// @param sourceTokenAmounts Token amounts originally sent in a Client.EVM2AnyMessage message
/// @return msgFeeJuels message fee in juels
/// @return isOutOfOrderExecution true if the message should be executed out of order
Expand All @@ -32,7 +32,7 @@ interface IFeeQuoter is IPriceRegistry {
address feeToken,
uint256 feeTokenAmount,
bytes memory extraArgs,
Internal.RampTokenAmount[] calldata rampTokenAmounts,
Internal.EVM2AnyTokenTransfer[] calldata onRampTokenTransfers,
Client.EVMTokenAmount[] calldata sourceTokenAmounts
)
external
Expand Down
19 changes: 18 additions & 1 deletion contracts/src/v0.8/ccip/interfaces/IRMNV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,29 @@ interface IRMNV2 {
bytes32 s;
}

function verify(Internal.MerkleRoot[] memory merkleRoots, Signature[] memory signatures) external view;
/// @notice Verifies signatures of RMN nodes, on dest lane updates as provided in the CommitReport
/// @param offRampAddress is not inferred by msg.sender, in case the call is made through ARMProxy
/// @param merkleRoots must be well formed, and is a representation of the CommitReport received from the oracles
/// @param signatures rmnNodes ECDSA sigs, only r & s, must be sorted in ascending order by signer address
/// @param rawVs rmnNodes ECDSA sigs, part v bitmap
/// @dev Will revert if verification fails
function verify(
address offRampAddress,
Internal.MerkleRoot[] memory merkleRoots,
Signature[] memory signatures,
uint256 rawVs
) external view;

/// @notice gets the current set of cursed subjects
/// @return subjects the list of cursed subjects
function getCursedSubjects() external view returns (bytes16[] memory subjects);

/// @notice If there is an active global or legacy curse, this function returns true.
/// @return bool true if there is an active global curse
function isCursed() external view returns (bool);

/// @notice If there is an active global curse, or an active curse for `subject`, this function returns true.
/// @param subject To check whether a particular chain is cursed, set to bytes16(uint128(chainSelector)).
/// @return bool true if the provided subject is cured *or* if there is an active global curse
function isCursed(bytes16 subject) external view returns (bool);
}
Loading

0 comments on commit 58bdf08

Please sign in to comment.