diff --git a/.changeset/curly-onions-tell.md b/.changeset/curly-onions-tell.md new file mode 100644 index 00000000000..249f616c012 --- /dev/null +++ b/.changeset/curly-onions-tell.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#changed Make Mantle use default OP stack l1 gas oracle in core diff --git a/.changeset/curvy-boxes-burn.md b/.changeset/curvy-boxes-burn.md new file mode 100644 index 00000000000..8a189c5f5d1 --- /dev/null +++ b/.changeset/curvy-boxes-burn.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +RMNCrypto evm implementation for CCIP - RMN Integration #added diff --git a/.changeset/rare-crabs-tell.md b/.changeset/rare-crabs-tell.md new file mode 100644 index 00000000000..9bd98213aec --- /dev/null +++ b/.changeset/rare-crabs-tell.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#added Adds the ability to use out of order execution transactions in CCIP E2E tests diff --git a/.changeset/red-icons-build.md b/.changeset/red-icons-build.md new file mode 100644 index 00000000000..feee928a876 --- /dev/null +++ b/.changeset/red-icons-build.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +Small fixes to multichain keyring adapter #internal diff --git a/.changeset/silly-boxes-sneeze.md b/.changeset/silly-boxes-sneeze.md new file mode 100644 index 00000000000..c6236b57f91 --- /dev/null +++ b/.changeset/silly-boxes-sneeze.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +bump chainlink-ccip #updated diff --git a/.changeset/stale-falcons-train.md b/.changeset/stale-falcons-train.md new file mode 100644 index 00000000000..ddaa0907f85 --- /dev/null +++ b/.changeset/stale-falcons-train.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#added [Keystone] Batch identical trigger events diff --git a/.changeset/wise-bears-end.md b/.changeset/wise-bears-end.md new file mode 100644 index 00000000000..30d10055dae --- /dev/null +++ b/.changeset/wise-bears-end.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#updated mercury plugin to consider PluginConfig as optional if EnableTriggerCapability relay config is true. Then if PluginConfig is nil, skip fetching latestPrice for linkFeedId and nativeFeedId. diff --git a/.github/E2E_TESTS_ON_GITHUB_CI.md b/.github/E2E_TESTS_ON_GITHUB_CI.md index f3a279338df..572e0070a05 100644 --- a/.github/E2E_TESTS_ON_GITHUB_CI.md +++ b/.github/E2E_TESTS_ON_GITHUB_CI.md @@ -49,9 +49,9 @@ To learn more about test configs see [CTF Test Config](https://github.com/smartc ## Test Secrets -For security reasons, test secrets and sensitive information are not stored directly within the test config TOML files. Instead, these secrets are securely injected into tests using environment variables. For a detailed explanation on managing test secrets, refer to our [Test Secrets documentation](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/config/README.md#test-secrets). +For security reasons, test secrets and sensitive information are not stored directly within the test config TOML files. Instead, these secrets are securely injected into tests using environment variables. For a detailed explanation on managing test secrets, refer to our [Test Secrets documentation](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/lib/config/README.md#test-secrets). -If you need to run a GitHub workflow using custom secrets, please refer to the [guide on running GitHub workflows with your test secrets](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/config/README.md#run-github-workflow-with-your-test-secrets). +If you need to run a GitHub workflow using custom secrets, please refer to the [guide on running GitHub workflows with your test secrets](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/lib/config/README.md#run-github-workflow-with-your-test-secrets). ## About the Reusable Workflow diff --git a/.github/e2e-tests.yml b/.github/e2e-tests.yml index 8bec26212e3..7a83ef096df 100644 --- a/.github/e2e-tests.yml +++ b/.github/e2e-tests.yml @@ -591,8 +591,13 @@ runner-test-matrix: test_env_vars: TEST_SUITE: chaos +<<<<<<< TT-1550-Provide-PoC-for-keeping-test-configs-in-git - id: benchmark/keeper_test.go:TestAutomationBenchmark path: integration-tests/benchmark/keeper_test.go +======= + - id: benchmark/automation_test.go:^TestAutomationBenchmark$ + path: integration-tests/benchmark/automation_test.go +>>>>>>> develop test_env_type: k8s-remote-runner remote_runner_memory: 4Gi runs_on: ubuntu-latest diff --git a/contracts/src/v0.8/keystone/OCR3Capability.sol b/contracts/src/v0.8/keystone/OCR3Capability.sol index 1ba934b1c40..c7ab8299b73 100644 --- a/contracts/src/v0.8/keystone/OCR3Capability.sol +++ b/contracts/src/v0.8/keystone/OCR3Capability.sol @@ -1,29 +1,186 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.24; -import {OCR2Base} from "./ocr/OCR2Base.sol"; +import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; +import {OCR2Abstract} from "./ocr/OCR2Abstract.sol"; // OCR2Base provides config management compatible with OCR3 -contract OCR3Capability is OCR2Base { +contract OCR3Capability is ConfirmedOwner, OCR2Abstract { + error InvalidConfig(string message); error ReportingUnsupported(); - constructor() OCR2Base() {} + constructor() ConfirmedOwner(msg.sender) {} + // incremented each time a new config is posted. This count is incorporated + // into the config digest, to prevent replay attacks. + uint32 internal s_configCount; + uint32 internal s_latestConfigBlockNumber; // makes it easier for offchain systems + // to extract config from logs. - function typeAndVersion() external pure override returns (string memory) { - return "Keystone 1.0.0"; + // Storing these fields used on the hot path in a ConfigInfo variable reduces the + // retrieval of all of them to a single SLOAD. If any further fields are + // added, make sure that storage of the struct still takes at most 32 bytes. + struct ConfigInfo { + bytes32 latestConfigDigest; + uint8 f; // TODO: could be optimized by squeezing into one slot + uint8 n; + } + ConfigInfo internal s_configInfo; + + /* + * Config logic + */ + + // Reverts transaction if config args are invalid + modifier checkConfigValid( + uint256 numSigners, + uint256 numTransmitters, + uint256 f + ) { + if (numSigners > MAX_NUM_ORACLES) revert InvalidConfig("too many signers"); + if (f == 0) revert InvalidConfig("f must be positive"); + if (numSigners != numTransmitters) revert InvalidConfig("oracle addresses out of registration"); + if (numSigners <= 3 * f) revert InvalidConfig("faulty-oracle f too high"); + _; } - function _beforeSetConfig(uint8 /* _f */, bytes memory /* _onchainConfig */) internal override { - // no-op + /// @inheritdoc OCR2Abstract + function latestConfigDigestAndEpoch() + external + view + virtual + override + returns (bool scanLogs, bytes32 configDigest, uint32 epoch) + { + return (true, bytes32(0), uint32(0)); + } + + // signer = [ 1 byte type | 2 byte len | n byte value ]... + + /** + * @notice sets offchain reporting protocol configuration incl. participating oracles + * @param _signers addresses with which oracles sign the reports + * @param _transmitters addresses oracles use to transmit the reports + * @param _f number of faulty oracles the system can tolerate + * @param _onchainConfig encoded on-chain contract configuration + * @param _offchainConfigVersion version number for offchainEncoding schema + * @param _offchainConfig encoded off-chain oracle configuration + */ + function setConfig( + bytes[] calldata _signers, + address[] calldata _transmitters, + uint8 _f, + bytes memory _onchainConfig, + uint64 _offchainConfigVersion, + bytes memory _offchainConfig + ) external override checkConfigValid(_signers.length, _transmitters.length, _f) onlyOwner { + // Bounded by MAX_NUM_ORACLES in OCR2Abstract.sol + for (uint256 i = 0; i < _signers.length; i++) { + if (_transmitters[i] == address(0)) revert InvalidConfig("transmitter must not be empty"); + // add new signers + bytes calldata publicKeys = _signers[i]; + uint16 offset = 0; + uint16 len = uint16(publicKeys.length); + // scan through public keys to validate encoded format + while (offset < len) { + // solhint-disable-next-line no-unused-vars + uint8 keyType = uint8(publicKeys[offset]); + uint16 keyLen = uint16(uint8(publicKeys[offset + 1])) + (uint16(uint8(publicKeys[offset + 2])) << 8); + // solhint-disable-next-line no-unused-vars + bytes calldata publicKey = publicKeys[offset + 3:offset + 3 + keyLen]; + offset += 3 + keyLen; + } + } + s_configInfo.f = _f; + uint32 previousConfigBlockNumber = s_latestConfigBlockNumber; + s_latestConfigBlockNumber = uint32(block.number); + s_configCount += 1; + { + s_configInfo.latestConfigDigest = _configDigestFromConfigData( + block.chainid, + address(this), + s_configCount, + _signers, + _transmitters, + _f, + _onchainConfig, + _offchainConfigVersion, + _offchainConfig + ); + } + s_configInfo.n = uint8(_signers.length); + + emit ConfigSet( + previousConfigBlockNumber, + s_configInfo.latestConfigDigest, + s_configCount, + _signers, + _transmitters, + _f, + _onchainConfig, + _offchainConfigVersion, + _offchainConfig + ); + } + + function _configDigestFromConfigData( + uint256 _chainId, + address _contractAddress, + uint64 _configCount, + bytes[] calldata _signers, + address[] calldata _transmitters, + uint8 _f, + bytes memory _onchainConfig, + uint64 _encodedConfigVersion, + bytes memory _encodedConfig + ) internal pure returns (bytes32) { + uint256 h = uint256( + keccak256( + abi.encode( + _chainId, + _contractAddress, + _configCount, + _signers, + _transmitters, + _f, + _onchainConfig, + _encodedConfigVersion, + _encodedConfig + ) + ) + ); + uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00 + uint256 prefix = 0x000e << (256 - 16); // 0x000e00..00 + return bytes32((prefix & prefixMask) | (h & ~prefixMask)); + } + + /** + * @notice information about current offchain reporting protocol configuration + * @return configCount ordinal number of current config, out of all configs applied to this contract so far + * @return blockNumber block at which this config was set + * @return configDigest domain-separation tag for current config (see __configDigestFromConfigData) + */ + function latestConfigDetails() + external + view + override + returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest) + { + return (s_configCount, s_latestConfigBlockNumber, s_configInfo.latestConfigDigest); + } + + function typeAndVersion() external pure override returns (string memory) { + return "Keystone 1.0.0"; } - function _report( - uint256 /* initialGas */, - address /* transmitter */, - uint8 /* signerCount */, - address[MAX_NUM_ORACLES] memory /* signers */, - bytes calldata /* report */ - ) internal virtual override { + function transmit( + // NOTE: If these parameters are changed, expectedMsgDataLength and/or + // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly + bytes32[3] calldata /* reportContext */, + bytes calldata /* report */, + bytes32[] calldata /* rs */, + bytes32[] calldata /* ss */, + bytes32 /* rawVs */ // signatures + ) external pure override { revert ReportingUnsupported(); } } diff --git a/contracts/src/v0.8/keystone/ocr/OCR2Abstract.sol b/contracts/src/v0.8/keystone/ocr/OCR2Abstract.sol index 3c1e304748f..4eb5cb069b7 100644 --- a/contracts/src/v0.8/keystone/ocr/OCR2Abstract.sol +++ b/contracts/src/v0.8/keystone/ocr/OCR2Abstract.sol @@ -23,7 +23,7 @@ abstract contract OCR2Abstract is ITypeAndVersion { uint32 previousConfigBlockNumber, bytes32 configDigest, uint64 configCount, - address[] signers, + bytes[] signers, address[] transmitters, uint8 f, bytes onchainConfig, @@ -41,7 +41,7 @@ abstract contract OCR2Abstract is ITypeAndVersion { * @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract */ function setConfig( - address[] memory signers, + bytes[] memory signers, address[] memory transmitters, uint8 f, bytes memory onchainConfig, diff --git a/contracts/src/v0.8/keystone/ocr/OCR2Base.sol b/contracts/src/v0.8/keystone/ocr/OCR2Base.sol deleted file mode 100644 index efc7992e90a..00000000000 --- a/contracts/src/v0.8/keystone/ocr/OCR2Base.sol +++ /dev/null @@ -1,352 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; -import {OCR2Abstract} from "./OCR2Abstract.sol"; - -/** - * @notice Onchain verification of reports from the offchain reporting protocol - * @dev For details on its operation, see the offchain reporting protocol design - * doc, which refers to this contract as simply the "contract". - */ -abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { - error ReportInvalid(string message); - error InvalidConfig(string message); - - constructor() ConfirmedOwner(msg.sender) {} - - // incremented each time a new config is posted. This count is incorporated - // into the config digest, to prevent replay attacks. - uint32 internal s_configCount; - uint32 internal s_latestConfigBlockNumber; // makes it easier for offchain systems - // to extract config from logs. - - // Storing these fields used on the hot path in a ConfigInfo variable reduces the - // retrieval of all of them to a single SLOAD. If any further fields are - // added, make sure that storage of the struct still takes at most 32 bytes. - struct ConfigInfo { - bytes32 latestConfigDigest; - uint8 f; // TODO: could be optimized by squeezing into one slot - uint8 n; - } - ConfigInfo internal s_configInfo; - - // Used for s_oracles[a].role, where a is an address, to track the purpose - // of the address, or to indicate that the address is unset. - enum Role { - // No oracle role has been set for address a - Unset, - // Signing address for the s_oracles[a].index'th oracle. I.e., report - // signatures from this oracle should ecrecover back to address a. - Signer, - // Transmission address for the s_oracles[a].index'th oracle. I.e., if a - // report is received by OCR2Aggregator.transmit in which msg.sender is - // a, it is attributed to the s_oracles[a].index'th oracle. - Transmitter - } - - struct Oracle { - uint8 index; // Index of oracle in s_signers/s_transmitters - Role role; // Role of the address which mapped to this struct - } - - mapping(address signerOrTransmitter => Oracle) internal s_oracles; - - // s_signers contains the signing address of each oracle - address[] internal s_signers; - - // s_transmitters contains the transmission address of each oracle, - // i.e. the address the oracle actually sends transactions to the contract from - address[] internal s_transmitters; - - /* - * Config logic - */ - - // Reverts transaction if config args are invalid - modifier checkConfigValid( - uint256 numSigners, - uint256 numTransmitters, - uint256 f - ) { - if (numSigners > MAX_NUM_ORACLES) revert InvalidConfig("too many signers"); - if (f == 0) revert InvalidConfig("f must be positive"); - if (numSigners != numTransmitters) revert InvalidConfig("oracle addresses out of registration"); - if (numSigners <= 3 * f) revert InvalidConfig("faulty-oracle f too high"); - _; - } - - // solhint-disable-next-line gas-struct-packing - struct SetConfigArgs { - address[] signers; - address[] transmitters; - uint8 f; - bytes onchainConfig; - uint64 offchainConfigVersion; - bytes offchainConfig; - } - - /// @inheritdoc OCR2Abstract - function latestConfigDigestAndEpoch() - external - view - virtual - override - returns (bool scanLogs, bytes32 configDigest, uint32 epoch) - { - return (true, bytes32(0), uint32(0)); - } - - /** - * @notice sets offchain reporting protocol configuration incl. participating oracles - * @param _signers addresses with which oracles sign the reports - * @param _transmitters addresses oracles use to transmit the reports - * @param _f number of faulty oracles the system can tolerate - * @param _onchainConfig encoded on-chain contract configuration - * @param _offchainConfigVersion version number for offchainEncoding schema - * @param _offchainConfig encoded off-chain oracle configuration - */ - function setConfig( - address[] memory _signers, - address[] memory _transmitters, - uint8 _f, - bytes memory _onchainConfig, - uint64 _offchainConfigVersion, - bytes memory _offchainConfig - ) external override checkConfigValid(_signers.length, _transmitters.length, _f) onlyOwner { - SetConfigArgs memory args = SetConfigArgs({ - signers: _signers, - transmitters: _transmitters, - f: _f, - onchainConfig: _onchainConfig, - offchainConfigVersion: _offchainConfigVersion, - offchainConfig: _offchainConfig - }); - - _beforeSetConfig(args.f, args.onchainConfig); - - while (s_signers.length != 0) { - // remove any old signer/transmitter addresses - uint256 lastIdx = s_signers.length - 1; - address signer = s_signers[lastIdx]; - address transmitter = s_transmitters[lastIdx]; - delete s_oracles[signer]; - delete s_oracles[transmitter]; - s_signers.pop(); - s_transmitters.pop(); - } - - // Bounded by MAX_NUM_ORACLES in OCR2Abstract.sol - for (uint256 i = 0; i < args.signers.length; i++) { - if (args.signers[i] == address(0)) revert InvalidConfig("signer must not be empty"); - if (args.transmitters[i] == address(0)) revert InvalidConfig("transmitter must not be empty"); - // add new signer/transmitter addresses - if (s_oracles[args.signers[i]].role != Role.Unset) revert InvalidConfig("repeated signer address"); - s_oracles[args.signers[i]] = Oracle(uint8(i), Role.Signer); - if (s_oracles[args.transmitters[i]].role != Role.Unset) revert InvalidConfig("repeated transmitter address"); - s_oracles[args.transmitters[i]] = Oracle(uint8(i), Role.Transmitter); - s_signers.push(args.signers[i]); - s_transmitters.push(args.transmitters[i]); - } - s_configInfo.f = args.f; - uint32 previousConfigBlockNumber = s_latestConfigBlockNumber; - s_latestConfigBlockNumber = uint32(block.number); - s_configCount += 1; - { - s_configInfo.latestConfigDigest = _configDigestFromConfigData( - block.chainid, - address(this), - s_configCount, - args.signers, - args.transmitters, - args.f, - args.onchainConfig, - args.offchainConfigVersion, - args.offchainConfig - ); - } - s_configInfo.n = uint8(args.signers.length); - - emit ConfigSet( - previousConfigBlockNumber, - s_configInfo.latestConfigDigest, - s_configCount, - args.signers, - args.transmitters, - args.f, - args.onchainConfig, - args.offchainConfigVersion, - args.offchainConfig - ); - } - - function _configDigestFromConfigData( - uint256 _chainId, - address _contractAddress, - uint64 _configCount, - address[] memory _signers, - address[] memory _transmitters, - uint8 _f, - bytes memory _onchainConfig, - uint64 _encodedConfigVersion, - bytes memory _encodedConfig - ) internal pure returns (bytes32) { - uint256 h = uint256( - keccak256( - abi.encode( - _chainId, - _contractAddress, - _configCount, - _signers, - _transmitters, - _f, - _onchainConfig, - _encodedConfigVersion, - _encodedConfig - ) - ) - ); - uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00 - uint256 prefix = 0x0001 << (256 - 16); // 0x000100..00 - return bytes32((prefix & prefixMask) | (h & ~prefixMask)); - } - - /** - * @notice information about current offchain reporting protocol configuration - * @return configCount ordinal number of current config, out of all configs applied to this contract so far - * @return blockNumber block at which this config was set - * @return configDigest domain-separation tag for current config (see __configDigestFromConfigData) - */ - function latestConfigDetails() - external - view - override - returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest) - { - return (s_configCount, s_latestConfigBlockNumber, s_configInfo.latestConfigDigest); - } - - /** - * @return list of addresses permitted to transmit reports to this contract - * @dev The list will match the order used to specify the transmitter during setConfig - */ - function transmitters() external view returns (address[] memory) { - return s_transmitters; - } - - function _beforeSetConfig(uint8 _f, bytes memory _onchainConfig) internal virtual; - - /** - * @dev hook called after the report has been fully validated - * for the extending contract to handle additional logic, such as oracle payment - * @param initialGas the amount of gas before validation - * @param transmitter the address of the account that submitted the report - * @param signers the addresses of all signing accounts - * @param report serialized report - */ - function _report( - uint256 initialGas, - address transmitter, - uint8 signerCount, - address[MAX_NUM_ORACLES] memory signers, - bytes calldata report - ) internal virtual; - - // The constant-length components of the msg.data sent to transmit. - // See the "If we wanted to call sam" example on for example reasoning - // https://solidity.readthedocs.io/en/v0.7.2/abi-spec.html - uint16 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT = - 4 + // function selector - 32 * - 3 + // 3 words containing reportContext - 32 + // word containing start location of abiencoded report value - 32 + // word containing location start of abiencoded rs value - 32 + // word containing start location of abiencoded ss value - 32 + // rawVs value - 32 + // word containing length of report - 32 + // word containing length rs - 32 + // word containing length of ss - 0; // placeholder - - function _requireExpectedMsgDataLength( - bytes calldata report, - bytes32[] calldata rs, - bytes32[] calldata ss - ) private pure { - // calldata will never be big enough to make this overflow - uint256 expected = uint256(TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT) + - report.length + // one byte pure entry in _report - rs.length * - 32 + // 32 bytes per entry in _rs - ss.length * - 32 + // 32 bytes per entry in _ss - 0; // placeholder - if (msg.data.length != expected) revert ReportInvalid("calldata length mismatch"); - } - - /** - * @notice transmit is called to post a new report to the contract - * @param report serialized report, which the signatures are signing. - * @param rs ith element is the R components of the ith signature on report. Must have at most maxNumOracles entries - * @param ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles entries - * @param rawVs ith element is the the V component of the ith signature - */ - function transmit( - // NOTE: If these parameters are changed, expectedMsgDataLength and/or - // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly - bytes32[3] calldata reportContext, - bytes calldata report, - bytes32[] calldata rs, - bytes32[] calldata ss, - bytes32 rawVs // signatures - ) external override { - uint256 initialGas = gasleft(); // This line must come first - - { - // reportContext consists of: - // reportContext[0]: ConfigDigest - // reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round - // reportContext[2]: ExtraHash - bytes32 configDigest = reportContext[0]; - uint32 epochAndRound = uint32(uint256(reportContext[1])); - - emit Transmitted(configDigest, uint32(epochAndRound >> 8)); - - // The following check is disabled to allow both current and proposed routes to submit reports using the same OCR config digest - // Chainlink Functions uses globally unique request IDs. Metadata about the request is stored and checked in the Coordinator and Router - // require(configInfo.latestConfigDigest == configDigest, "configDigest mismatch"); - - _requireExpectedMsgDataLength(report, rs, ss); - - uint256 expectedNumSignatures = (s_configInfo.n + s_configInfo.f) / 2 + 1; - - if (rs.length != expectedNumSignatures) revert ReportInvalid("wrong number of signatures"); - if (rs.length != ss.length) revert ReportInvalid("report rs and ss must be of equal length"); - - Oracle memory transmitter = s_oracles[msg.sender]; - if (transmitter.role != Role.Transmitter && msg.sender != s_transmitters[transmitter.index]) - revert ReportInvalid("unauthorized transmitter"); - } - - address[MAX_NUM_ORACLES] memory signed; - uint8 signerCount = 0; - - { - // Verify signatures attached to report - bytes32 h = keccak256(abi.encodePacked(keccak256(report), reportContext)); - - Oracle memory o; - // Bounded by MAX_NUM_ORACLES in OCR2Abstract.sol - for (uint256 i = 0; i < rs.length; ++i) { - address signer = ecrecover(h, uint8(rawVs[i]) + 27, rs[i], ss[i]); - o = s_oracles[signer]; - if (o.role != Role.Signer) revert ReportInvalid("address not authorized to sign"); - if (signed[o.index] != address(0)) revert ReportInvalid("non-unique signature"); - signed[o.index] = signer; - signerCount += 1; - } - } - - _report(initialGas, msg.sender, signerCount, signed, report); - } -} diff --git a/core/capabilities/ccip/ccipevm/helpers.go b/core/capabilities/ccip/ccipevm/helpers.go index ee83230a4ce..4cfd64b7c65 100644 --- a/core/capabilities/ccip/ccipevm/helpers.go +++ b/core/capabilities/ccip/ccipevm/helpers.go @@ -4,6 +4,8 @@ import ( "bytes" "fmt" "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" ) func decodeExtraArgsV1V2(extraArgs []byte) (gasLimit *big.Int, err error) { @@ -31,3 +33,13 @@ func decodeExtraArgsV1V2(extraArgs []byte) (gasLimit *big.Int, err error) { } return ifaces[0].(*big.Int), nil } + +// abiEncodeMethodInputs encodes the inputs for a method call. +// example abi: `[{ "name" : "method", "type": "function", "inputs": [{"name": "a", "type": "uint256"}]}]` +func abiEncodeMethodInputs(abiDef abi.ABI, inputs ...interface{}) ([]byte, error) { + packed, err := abiDef.Pack("method", inputs...) + if err != nil { + return nil, err + } + return packed[4:], nil // remove the method selector +} diff --git a/core/capabilities/ccip/ccipevm/msghasher.go b/core/capabilities/ccip/ccipevm/msghasher.go index e620d96a43a..cf37a28b003 100644 --- a/core/capabilities/ccip/ccipevm/msghasher.go +++ b/core/capabilities/ccip/ccipevm/msghasher.go @@ -61,12 +61,12 @@ func (h *MessageHasherV1) Hash(_ context.Context, msg cciptypes.Message) (ccipty Amount: rta.Amount.Int, }) } - encodedRampTokenAmounts, err := abiEncode("encodeTokenAmountsHashPreimage", rampTokenAmounts) + encodedRampTokenAmounts, err := h.abiEncode("encodeTokenAmountsHashPreimage", rampTokenAmounts) if err != nil { return [32]byte{}, fmt.Errorf("abi encode token amounts: %w", err) } - metaDataHashInput, err := abiEncode( + metaDataHashInput, err := h.abiEncode( "encodeMetadataHashPreimage", ANY_2_EVM_MESSAGE_HASH, uint64(msg.Header.SourceChainSelector), @@ -86,7 +86,7 @@ func (h *MessageHasherV1) Hash(_ context.Context, msg cciptypes.Message) (ccipty return [32]byte{}, fmt.Errorf("decode extra args: %w", err) } - fixedSizeFieldsEncoded, err := abiEncode( + fixedSizeFieldsEncoded, err := h.abiEncode( "encodeFixedSizeFieldsHashPreimage", msg.Header.MessageID, []byte(msg.Sender), @@ -99,7 +99,7 @@ func (h *MessageHasherV1) Hash(_ context.Context, msg cciptypes.Message) (ccipty return [32]byte{}, fmt.Errorf("abi encode fixed size values: %w", err) } - packedValues, err := abiEncode( + packedValues, err := h.abiEncode( "encodeFinalHashPreimage", leafDomainSeparator, utils.Keccak256Fixed(metaDataHashInput), @@ -114,7 +114,7 @@ func (h *MessageHasherV1) Hash(_ context.Context, msg cciptypes.Message) (ccipty return utils.Keccak256Fixed(packedValues), nil } -func abiEncode(method string, values ...interface{}) ([]byte, error) { +func (h *MessageHasherV1) abiEncode(method string, values ...interface{}) ([]byte, error) { res, err := messageHasherABI.Pack(method, values...) if err != nil { return nil, err diff --git a/core/capabilities/ccip/ccipevm/rmncrypto.go b/core/capabilities/ccip/ccipevm/rmncrypto.go new file mode 100644 index 00000000000..794a466524f --- /dev/null +++ b/core/capabilities/ccip/ccipevm/rmncrypto.go @@ -0,0 +1,184 @@ +package ccipevm + +import ( + "bytes" + "context" + "errors" + "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" +) + +// encodingUtilsAbi is the ABI for the EncodingUtils contract. +// Should be imported when gethwrappers are moved from ccip repo to core. +// nolint:lll +const encodingUtilsAbiRaw = `[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DoNotDeploy","type":"error"},{"inputs":[{"internalType":"bytes32","name":"rmnReportVersion","type":"bytes32"},{"components":[{"internalType":"uint256","name":"destChainId","type":"uint256"},{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"internalType":"address","name":"rmnRemoteContractAddress","type":"address"},{"internalType":"address","name":"offrampAddress","type":"address"},{"internalType":"bytes32","name":"rmnHomeContractConfigDigest","type":"bytes32"},{"components":[{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"},{"internalType":"bytes","name":"onRampAddress","type":"bytes"},{"internalType":"uint64","name":"minSeqNr","type":"uint64"},{"internalType":"uint64","name":"maxSeqNr","type":"uint64"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"internalType":"struct Internal.MerkleRoot[]","name":"destLaneUpdates","type":"tuple[]"}],"internalType":"struct RMNRemote.Report","name":"rmnReport","type":"tuple"}],"name":"_rmnReport","outputs":[],"stateMutability":"nonpayable","type":"function"}]` +const addressEncodeAbiRaw = `[{"name":"method","type":"function","inputs":[{"name": "", "type": "address"}]}]` + +var ( + encodingUtilsABI abi.ABI + addressEncodeABI abi.ABI +) + +func init() { + var err error + + encodingUtilsABI, err = abi.JSON(strings.NewReader(encodingUtilsAbiRaw)) + if err != nil { + panic(fmt.Errorf("failed to parse encoding utils ABI: %v", err)) + } + + addressEncodeABI, err = abi.JSON(strings.NewReader(addressEncodeAbiRaw)) + if err != nil { + panic(fmt.Errorf("failed to parse address encode ABI: %v", err)) + } +} + +const ( + // v is the recovery ID for ECDSA signatures. This implementation assumes that v is always 27. + v = 27 +) + +// EVMRMNCrypto is the RMNCrypto implementation for EVM chains. +type EVMRMNCrypto struct{} + +// Interface compliance check +var _ cciptypes.RMNCrypto = (*EVMRMNCrypto)(nil) + +func NewEVMRMNCrypto() *EVMRMNCrypto { + return &EVMRMNCrypto{} +} + +// Should be replaced by gethwrapper types when they're available +type evmRMNRemoteReport struct { + DestChainID *big.Int `abi:"destChainId"` + DestChainSelector uint64 + RmnRemoteContractAddress common.Address + OfframpAddress common.Address + RmnHomeContractConfigDigest [32]byte + DestLaneUpdates []evmInternalMerkleRoot +} + +type evmInternalMerkleRoot struct { + SourceChainSelector uint64 + OnRampAddress []byte + MinSeqNr uint64 + MaxSeqNr uint64 + MerkleRoot [32]byte +} + +func (r *EVMRMNCrypto) VerifyReportSignatures( + _ context.Context, + sigs []cciptypes.RMNECDSASignature, + report cciptypes.RMNReport, + signerAddresses []cciptypes.Bytes, +) error { + if sigs == nil { + return fmt.Errorf("no signatures provided") + } + if report.LaneUpdates == nil { + return fmt.Errorf("no lane updates provided") + } + + rmnVersionHash := crypto.Keccak256Hash([]byte(report.ReportVersion)) + + evmLaneUpdates := make([]evmInternalMerkleRoot, len(report.LaneUpdates)) + for i, lu := range report.LaneUpdates { + onRampAddress := common.BytesToAddress(lu.OnRampAddress) + onRampAddrAbi, err := abiEncodeMethodInputs(addressEncodeABI, onRampAddress) + if err != nil { + return fmt.Errorf("ΑΒΙ encode onRampAddress: %w", err) + } + evmLaneUpdates[i] = evmInternalMerkleRoot{ + SourceChainSelector: uint64(lu.SourceChainSelector), + OnRampAddress: onRampAddrAbi, + MinSeqNr: uint64(lu.MinSeqNr), + MaxSeqNr: uint64(lu.MaxSeqNr), + MerkleRoot: lu.MerkleRoot, + } + } + + evmReport := evmRMNRemoteReport{ + DestChainID: report.DestChainID.Int, + DestChainSelector: uint64(report.DestChainSelector), + RmnRemoteContractAddress: common.HexToAddress(report.RmnRemoteContractAddress.String()), + OfframpAddress: common.HexToAddress(report.OfframpAddress.String()), + RmnHomeContractConfigDigest: report.RmnHomeContractConfigDigest, + DestLaneUpdates: evmLaneUpdates, + } + + abiEnc, err := encodingUtilsABI.Methods["_rmnReport"].Inputs.Pack(rmnVersionHash, evmReport) + if err != nil { + return fmt.Errorf("failed to ABI encode args: %w", err) + } + + signedHash := crypto.Keccak256Hash(abiEnc) + + // keep track of the previous signer for validating signers ordering + prevSignerAddr := common.Address{} + + for _, sig := range sigs { + recoveredAddress, err := recoverAddressFromSig( + v, + sig.R, + sig.S, + signedHash[:], + ) + if err != nil { + return fmt.Errorf("failed to recover public key from signature: %w", err) + } + + // make sure that signers are ordered correctly (ASC addresses). + if bytes.Compare(prevSignerAddr.Bytes(), recoveredAddress.Bytes()) == 1 { + return fmt.Errorf("signers are not ordered correctly") + } + prevSignerAddr = recoveredAddress + + // Check if the public key is in the list of the provided RMN nodes + found := false + for _, signerAddr := range signerAddresses { + signerAddrEvm := common.BytesToAddress(signerAddr) + if signerAddrEvm == recoveredAddress { + found = true + break + } + } + if !found { + return fmt.Errorf("the recovered public key does not match any signer address, verification failed") + } + } + + return nil +} + +// recoverAddressFromSig Recovers a public address from an ECDSA signature using r, s, v, and the hash of the message. +func recoverAddressFromSig(v int, r, s [32]byte, hash []byte) (common.Address, error) { + // Ensure v is either 27 or 28 (as used in Ethereum) + if v != 27 && v != 28 { + return common.Address{}, errors.New("v must be 27 or 28") + } + + // Construct the signature by concatenating r, s, and the recovery ID (v - 27 to convert to 0/1) + sig := append(r[:], s[:]...) + sig = append(sig, byte(v-27)) + + // Recover the public key bytes from the signature and message hash + pubKeyBytes, err := crypto.Ecrecover(hash, sig) + if err != nil { + return common.Address{}, fmt.Errorf("failed to recover public key: %v", err) + } + + // Convert the recovered public key to an ECDSA public key + pubKey, err := crypto.UnmarshalPubkey(pubKeyBytes) + if err != nil { + return common.Address{}, fmt.Errorf("failed to unmarshal public key: %v", err) + } // or SigToPub + + return crypto.PubkeyToAddress(*pubKey), nil +} diff --git a/core/capabilities/ccip/ccipevm/rmncrypto_test.go b/core/capabilities/ccip/ccipevm/rmncrypto_test.go new file mode 100644 index 00000000000..e12b20ab304 --- /dev/null +++ b/core/capabilities/ccip/ccipevm/rmncrypto_test.go @@ -0,0 +1,68 @@ +package ccipevm + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + + cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_VerifyRmnReportSignatures(t *testing.T) { + // NOTE: The following test data (public keys, signatures, ...) are shared from the RMN team. + + onchainRmnRemoteAddr := common.HexToAddress("0x7821bcd6944457d17c631157efeb0c621baa76eb") + + rmnHomeContractConfigDigestHex := "0x785936570d1c7422ef30b7da5555ad2f175fa2dd97a2429a2e71d1e07c94e060" + rmnHomeContractConfigDigest := common.FromHex(rmnHomeContractConfigDigestHex) + require.Len(t, rmnHomeContractConfigDigest, 32) + var rmnHomeContractConfigDigest32 [32]byte + copy(rmnHomeContractConfigDigest32[:], rmnHomeContractConfigDigest) + + rootHex := "0x48e688aefc20a04fdec6b8ff19df358fd532455659dcf529797cda358e9e5205" + root := common.FromHex(rootHex) + require.Len(t, root, 32) + var root32 [32]byte + copy(root32[:], root) + + onRampAddr := common.HexToAddress("0x6662cb20464f4be557262693bea0409f068397ed") + + destChainEvmID := int64(4083663998511321420) + + reportData := cciptypes.RMNReport{ + ReportVersion: "RMN_V1_6_ANY2EVM_REPORT", + DestChainID: cciptypes.NewBigIntFromInt64(destChainEvmID), + DestChainSelector: 5266174733271469989, + RmnRemoteContractAddress: common.HexToAddress("0x3d015cec4411357eff4ea5f009a581cc519f75d3").Bytes(), + OfframpAddress: common.HexToAddress("0xc5cdb7711a478058023373b8ae9e7421925140f8").Bytes(), + RmnHomeContractConfigDigest: rmnHomeContractConfigDigest32, + LaneUpdates: []cciptypes.RMNLaneUpdate{ + { + SourceChainSelector: 8258882951688608272, + OnRampAddress: onRampAddr.Bytes(), + MinSeqNr: 9018980618932210108, + MaxSeqNr: 8239368306600774074, + MerkleRoot: root32, + }, + }, + } + + ctx := tests.Context(t) + + rmnCrypto := NewEVMRMNCrypto() + + r, _ := cciptypes.NewBytes32FromString("0x89546b4652d0377062a398e413344e4da6034ae877c437d0efe0e5246b70a9a1") + s, _ := cciptypes.NewBytes32FromString("0x95eef2d24d856ccac3886db8f4aebea60684ed73942392692908fed79a679b4e") + + err := rmnCrypto.VerifyReportSignatures( + ctx, + []cciptypes.RMNECDSASignature{{R: r, S: s}}, + reportData, + []cciptypes.Bytes{onchainRmnRemoteAddr.Bytes()}, + ) + assert.NoError(t, err) +} diff --git a/core/capabilities/launcher.go b/core/capabilities/launcher.go index 03a1dd54f02..f2bfd5e4b16 100644 --- a/core/capabilities/launcher.go +++ b/core/capabilities/launcher.go @@ -394,7 +394,7 @@ func (w *launcher) exposeCapabilities(ctx context.Context, myPeerID p2ptypes.Pee switch capability.CapabilityType { case capabilities.CapabilityTypeTrigger: - newTriggerPublisher := func(capability capabilities.BaseCapability, info capabilities.CapabilityInfo) (receiverService, error) { + newTriggerPublisher := func(capability capabilities.BaseCapability, info capabilities.CapabilityInfo) (remotetypes.ReceiverService, error) { publisher := remote.NewTriggerPublisher( capabilityConfig.RemoteTriggerConfig, capability.(capabilities.TriggerCapability), @@ -416,7 +416,7 @@ func (w *launcher) exposeCapabilities(ctx context.Context, myPeerID p2ptypes.Pee case capabilities.CapabilityTypeConsensus: w.lggr.Warn("no remote client configured for capability type consensus, skipping configuration") case capabilities.CapabilityTypeTarget: - newTargetServer := func(capability capabilities.BaseCapability, info capabilities.CapabilityInfo) (receiverService, error) { + newTargetServer := func(capability capabilities.BaseCapability, info capabilities.CapabilityInfo) (remotetypes.ReceiverService, error) { return target.NewServer( capabilityConfig.RemoteTargetConfig, myPeerID, @@ -441,12 +441,7 @@ func (w *launcher) exposeCapabilities(ctx context.Context, myPeerID p2ptypes.Pee return nil } -type receiverService interface { - services.Service - remotetypes.Receiver -} - -func (w *launcher) addReceiver(ctx context.Context, capability registrysyncer.Capability, don registrysyncer.DON, newReceiverFn func(capability capabilities.BaseCapability, info capabilities.CapabilityInfo) (receiverService, error)) error { +func (w *launcher) addReceiver(ctx context.Context, capability registrysyncer.Capability, don registrysyncer.DON, newReceiverFn func(capability capabilities.BaseCapability, info capabilities.CapabilityInfo) (remotetypes.ReceiverService, error)) error { capID := capability.ID info, err := capabilities.NewRemoteCapabilityInfo( capID, diff --git a/core/capabilities/remote/trigger_publisher.go b/core/capabilities/remote/trigger_publisher.go index c5b4e841858..e5a46c87914 100644 --- a/core/capabilities/remote/trigger_publisher.go +++ b/core/capabilities/remote/trigger_publisher.go @@ -2,10 +2,11 @@ package remote import ( "context" + "crypto/sha256" + "encoding/binary" "sync" "time" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/services" @@ -22,19 +23,22 @@ import ( // // TriggerPublisher communicates with corresponding TriggerSubscribers on remote nodes. type triggerPublisher struct { - config *capabilities.RemoteTriggerConfig - underlying commoncap.TriggerCapability - capInfo commoncap.CapabilityInfo - capDonInfo commoncap.DON - workflowDONs map[uint32]commoncap.DON - membersCache map[uint32]map[p2ptypes.PeerID]bool - dispatcher types.Dispatcher - messageCache *messageCache[registrationKey, p2ptypes.PeerID] - registrations map[registrationKey]*pubRegState - mu sync.RWMutex // protects messageCache and registrations - stopCh services.StopChan - wg sync.WaitGroup - lggr logger.Logger + config *commoncap.RemoteTriggerConfig + underlying commoncap.TriggerCapability + capInfo commoncap.CapabilityInfo + capDonInfo commoncap.DON + workflowDONs map[uint32]commoncap.DON + membersCache map[uint32]map[p2ptypes.PeerID]bool + dispatcher types.Dispatcher + messageCache *messageCache[registrationKey, p2ptypes.PeerID] + registrations map[registrationKey]*pubRegState + mu sync.RWMutex // protects messageCache and registrations + batchingQueue map[[32]byte]*batchedResponse + batchingEnabled bool + bqMu sync.Mutex // protects batchingQueue + stopCh services.StopChan + wg sync.WaitGroup + lggr logger.Logger } type registrationKey struct { @@ -47,13 +51,21 @@ type pubRegState struct { request commoncap.TriggerRegistrationRequest } -var _ types.Receiver = &triggerPublisher{} -var _ services.Service = &triggerPublisher{} +type batchedResponse struct { + rawResponse []byte + callerDonID uint32 + triggerEventID string + workflowIDs []string +} + +var _ types.ReceiverService = &triggerPublisher{} -func NewTriggerPublisher(config *capabilities.RemoteTriggerConfig, underlying commoncap.TriggerCapability, capInfo commoncap.CapabilityInfo, capDonInfo commoncap.DON, workflowDONs map[uint32]commoncap.DON, dispatcher types.Dispatcher, lggr logger.Logger) *triggerPublisher { +const minAllowedBatchCollectionPeriod = 10 * time.Millisecond + +func NewTriggerPublisher(config *commoncap.RemoteTriggerConfig, underlying commoncap.TriggerCapability, capInfo commoncap.CapabilityInfo, capDonInfo commoncap.DON, workflowDONs map[uint32]commoncap.DON, dispatcher types.Dispatcher, lggr logger.Logger) *triggerPublisher { if config == nil { lggr.Info("no config provided, using default values") - config = &capabilities.RemoteTriggerConfig{} + config = &commoncap.RemoteTriggerConfig{} } config.ApplyDefaults() membersCache := make(map[uint32]map[p2ptypes.PeerID]bool) @@ -65,23 +77,29 @@ func NewTriggerPublisher(config *capabilities.RemoteTriggerConfig, underlying co membersCache[id] = cache } return &triggerPublisher{ - config: config, - underlying: underlying, - capInfo: capInfo, - capDonInfo: capDonInfo, - workflowDONs: workflowDONs, - membersCache: membersCache, - dispatcher: dispatcher, - messageCache: NewMessageCache[registrationKey, p2ptypes.PeerID](), - registrations: make(map[registrationKey]*pubRegState), - stopCh: make(services.StopChan), - lggr: lggr.Named("TriggerPublisher"), + config: config, + underlying: underlying, + capInfo: capInfo, + capDonInfo: capDonInfo, + workflowDONs: workflowDONs, + membersCache: membersCache, + dispatcher: dispatcher, + messageCache: NewMessageCache[registrationKey, p2ptypes.PeerID](), + registrations: make(map[registrationKey]*pubRegState), + batchingQueue: make(map[[32]byte]*batchedResponse), + batchingEnabled: config.MaxBatchSize > 1 && config.BatchCollectionPeriod >= minAllowedBatchCollectionPeriod, + stopCh: make(services.StopChan), + lggr: lggr.Named("TriggerPublisher"), } } func (p *triggerPublisher) Start(ctx context.Context) error { p.wg.Add(1) go p.registrationCleanupLoop() + if p.batchingEnabled { + p.wg.Add(1) + go p.batchingLoop() + } p.lggr.Info("TriggerPublisher started") return nil } @@ -202,31 +220,98 @@ func (p *triggerPublisher) triggerEventLoop(callbackCh <-chan commoncap.TriggerR } triggerEvent := response.Event p.lggr.Debugw("received trigger event", "capabilityId", p.capInfo.ID, "workflowId", key.workflowId, "triggerEventID", triggerEvent.ID) - marshaled, err := pb.MarshalTriggerResponse(response) + marshaledResponse, err := pb.MarshalTriggerResponse(response) if err != nil { p.lggr.Debugw("can't marshal trigger event", "err", err) break } - msg := &types.MessageBody{ - CapabilityId: p.capInfo.ID, - CapabilityDonId: p.capDonInfo.ID, - CallerDonId: key.callerDonId, - Method: types.MethodTriggerEvent, - Payload: marshaled, - Metadata: &types.MessageBody_TriggerEventMetadata{ - TriggerEventMetadata: &types.TriggerEventMetadata{ - // NOTE: optionally introduce batching across workflows as an optimization - WorkflowIds: []string{key.workflowId}, - TriggerEventId: triggerEvent.ID, - }, + + if p.batchingEnabled { + p.enqueueForBatching(marshaledResponse, key, triggerEvent.ID) + } else { + // a single-element "batch" + p.sendBatch(&batchedResponse{ + rawResponse: marshaledResponse, + callerDonID: key.callerDonId, + triggerEventID: triggerEvent.ID, + workflowIDs: []string{key.workflowId}, + }) + } + } + } +} + +func (p *triggerPublisher) enqueueForBatching(rawResponse []byte, key registrationKey, triggerEventID string) { + // put in batching queue, group by hash(callerDonId, triggerEventID, response) + combined := make([]byte, 4) + binary.LittleEndian.PutUint32(combined, key.callerDonId) + combined = append(combined, []byte(triggerEventID)...) + combined = append(combined, rawResponse...) + sha := sha256.Sum256(combined) + p.bqMu.Lock() + elem, exists := p.batchingQueue[sha] + if !exists { + elem = &batchedResponse{ + rawResponse: rawResponse, + callerDonID: key.callerDonId, + triggerEventID: triggerEventID, + workflowIDs: []string{key.workflowId}, + } + p.batchingQueue[sha] = elem + } else { + elem.workflowIDs = append(elem.workflowIDs, key.workflowId) + } + p.bqMu.Unlock() +} + +func (p *triggerPublisher) sendBatch(resp *batchedResponse) { + for len(resp.workflowIDs) > 0 { + idBatch := resp.workflowIDs + if p.batchingEnabled && int64(len(idBatch)) > int64(p.config.MaxBatchSize) { + idBatch = idBatch[:p.config.MaxBatchSize] + resp.workflowIDs = resp.workflowIDs[p.config.MaxBatchSize:] + } else { + resp.workflowIDs = nil + } + msg := &types.MessageBody{ + CapabilityId: p.capInfo.ID, + CapabilityDonId: p.capDonInfo.ID, + CallerDonId: resp.callerDonID, + Method: types.MethodTriggerEvent, + Payload: resp.rawResponse, + Metadata: &types.MessageBody_TriggerEventMetadata{ + TriggerEventMetadata: &types.TriggerEventMetadata{ + WorkflowIds: idBatch, + TriggerEventId: resp.triggerEventID, }, + }, + } + // NOTE: send to all nodes by default, introduce different strategies later (KS-76) + for _, peerID := range p.workflowDONs[resp.callerDonID].Members { + err := p.dispatcher.Send(peerID, msg) + if err != nil { + p.lggr.Errorw("failed to send trigger event", "capabilityId", p.capInfo.ID, "peerID", peerID, "err", err) } - // NOTE: send to all nodes by default, introduce different strategies later (KS-76) - for _, peerID := range p.workflowDONs[key.callerDonId].Members { - err = p.dispatcher.Send(peerID, msg) - if err != nil { - p.lggr.Errorw("failed to send trigger event", "capabilityId", p.capInfo.ID, "peerID", peerID, "err", err) - } + } + } +} + +func (p *triggerPublisher) batchingLoop() { + defer p.wg.Done() + ticker := time.NewTicker(p.config.BatchCollectionPeriod) + defer ticker.Stop() + for { + select { + case <-p.stopCh: + return + case <-ticker.C: + p.bqMu.Lock() + queue := p.batchingQueue + p.batchingQueue = make(map[[32]byte]*batchedResponse) + p.bqMu.Unlock() + + for _, elem := range queue { + p.sendBatch(elem) } } } diff --git a/core/capabilities/remote/trigger_publisher_test.go b/core/capabilities/remote/trigger_publisher_test.go index bcc79b4fbb9..8d078dc1aad 100644 --- a/core/capabilities/remote/trigger_publisher_test.go +++ b/core/capabilities/remote/trigger_publisher_test.go @@ -5,47 +5,130 @@ import ( "testing" "time" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote" remotetypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" - remoteMocks "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types/mocks" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" ) +const capID = "cap_id@1" + func TestTriggerPublisher_Register(t *testing.T) { + ctx := testutils.Context(t) + capabilityDONID, workflowDONID := uint32(1), uint32(2) + + underlyingTriggerCap, publisher, _, peers := newServices(t, capabilityDONID, workflowDONID, 1) + + // invalid sender case - node 0 is not a member of the workflow DON, registration shoudn't happen + regEvent := newRegisterTriggerMessage(t, workflowDONID, peers[0]) + publisher.Receive(ctx, regEvent) + require.Empty(t, underlyingTriggerCap.registrationsCh) + + // valid registration + regEvent = newRegisterTriggerMessage(t, workflowDONID, peers[1]) + publisher.Receive(ctx, regEvent) + require.NotEmpty(t, underlyingTriggerCap.registrationsCh) + forwarded := <-underlyingTriggerCap.registrationsCh + require.Equal(t, workflowID1, forwarded.Metadata.WorkflowID) + + require.NoError(t, publisher.Close()) +} + +func TestTriggerPublisher_ReceiveTriggerEvents_NoBatching(t *testing.T) { + ctx := testutils.Context(t) + capabilityDONID, workflowDONID := uint32(1), uint32(2) + + underlyingTriggerCap, publisher, dispatcher, peers := newServices(t, capabilityDONID, workflowDONID, 1) + regEvent := newRegisterTriggerMessage(t, workflowDONID, peers[1]) + publisher.Receive(ctx, regEvent) + require.NotEmpty(t, underlyingTriggerCap.registrationsCh) + + // send a trigger event and expect that it gets delivered right away + underlyingTriggerCap.eventCh <- commoncap.TriggerResponse{} + awaitOutgoingMessageCh := make(chan struct{}) + dispatcher.On("Send", peers[1], mock.Anything).Run(func(args mock.Arguments) { + awaitOutgoingMessageCh <- struct{}{} + }).Return(nil) + <-awaitOutgoingMessageCh + + require.NoError(t, publisher.Close()) +} + +func TestTriggerPublisher_ReceiveTriggerEvents_BatchingEnabled(t *testing.T) { + ctx := testutils.Context(t) + capabilityDONID, workflowDONID := uint32(1), uint32(2) + + underlyingTriggerCap, publisher, dispatcher, peers := newServices(t, capabilityDONID, workflowDONID, 2) + regEvent := newRegisterTriggerMessage(t, workflowDONID, peers[1]) + publisher.Receive(ctx, regEvent) + require.NotEmpty(t, underlyingTriggerCap.registrationsCh) + + // send two trigger events and expect them to be delivered in a batch + underlyingTriggerCap.eventCh <- commoncap.TriggerResponse{} + underlyingTriggerCap.eventCh <- commoncap.TriggerResponse{} + awaitOutgoingMessageCh := make(chan struct{}) + dispatcher.On("Send", peers[1], mock.Anything).Run(func(args mock.Arguments) { + msg := args.Get(1).(*remotetypes.MessageBody) + require.Equal(t, capID, msg.CapabilityId) + require.Equal(t, remotetypes.MethodTriggerEvent, msg.Method) + require.NotEmpty(t, msg.Payload) + metadata := msg.Metadata.(*remotetypes.MessageBody_TriggerEventMetadata) + require.Len(t, metadata.TriggerEventMetadata.WorkflowIds, 2) + awaitOutgoingMessageCh <- struct{}{} + }).Return(nil).Once() + <-awaitOutgoingMessageCh + + // if there are fewer pending event than the batch size, + // the events should still be sent after the batch collection period + underlyingTriggerCap.eventCh <- commoncap.TriggerResponse{} + dispatcher.On("Send", peers[1], mock.Anything).Run(func(args mock.Arguments) { + msg := args.Get(1).(*remotetypes.MessageBody) + metadata := msg.Metadata.(*remotetypes.MessageBody_TriggerEventMetadata) + require.Len(t, metadata.TriggerEventMetadata.WorkflowIds, 1) + awaitOutgoingMessageCh <- struct{}{} + }).Return(nil).Once() + <-awaitOutgoingMessageCh + + require.NoError(t, publisher.Close()) +} + +func newServices(t *testing.T, capabilityDONID uint32, workflowDONID uint32, maxBatchSize uint32) (*testTrigger, remotetypes.ReceiverService, *mocks.Dispatcher, []p2ptypes.PeerID) { lggr := logger.TestLogger(t) ctx := testutils.Context(t) capInfo := commoncap.CapabilityInfo{ - ID: "cap_id@1", + ID: capID, CapabilityType: commoncap.CapabilityTypeTrigger, Description: "Remote Trigger", } - p1 := p2ptypes.PeerID{} - require.NoError(t, p1.UnmarshalText([]byte(peerID1))) - p2 := p2ptypes.PeerID{} - require.NoError(t, p2.UnmarshalText([]byte(peerID2))) + peers := make([]p2ptypes.PeerID, 2) + require.NoError(t, peers[0].UnmarshalText([]byte(peerID1))) + require.NoError(t, peers[1].UnmarshalText([]byte(peerID2))) capDonInfo := commoncap.DON{ - ID: 1, - Members: []p2ptypes.PeerID{p1}, + ID: capabilityDONID, + Members: []p2ptypes.PeerID{peers[0]}, // peer 0 is in the capability DON F: 0, } workflowDonInfo := commoncap.DON{ - ID: 2, - Members: []p2ptypes.PeerID{p2}, + ID: workflowDONID, + Members: []p2ptypes.PeerID{peers[1]}, // peer 1 is in the workflow DON F: 0, } - dispatcher := remoteMocks.NewDispatcher(t) + dispatcher := mocks.NewDispatcher(t) config := &commoncap.RemoteTriggerConfig{ RegistrationRefresh: 100 * time.Millisecond, RegistrationExpiry: 100 * time.Second, MinResponsesToAggregate: 1, MessageExpiry: 100 * time.Second, + MaxBatchSize: maxBatchSize, + BatchCollectionPeriod: time.Second, } workflowDONs := map[uint32]commoncap.DON{ workflowDonInfo.ID: workflowDonInfo, @@ -53,10 +136,14 @@ func TestTriggerPublisher_Register(t *testing.T) { underlying := &testTrigger{ info: capInfo, registrationsCh: make(chan commoncap.TriggerRegistrationRequest, 2), + eventCh: make(chan commoncap.TriggerResponse, 2), } publisher := remote.NewTriggerPublisher(config, underlying, capInfo, capDonInfo, workflowDONs, dispatcher, lggr) require.NoError(t, publisher.Start(ctx)) + return underlying, publisher, dispatcher, peers +} +func newRegisterTriggerMessage(t *testing.T, callerDonID uint32, sender p2ptypes.PeerID) *remotetypes.MessageBody { // trigger registration event triggerRequest := commoncap.TriggerRegistrationRequest{ Metadata: commoncap.RequestMetadata{ @@ -65,39 +152,29 @@ func TestTriggerPublisher_Register(t *testing.T) { } marshaled, err := pb.MarshalTriggerRegistrationRequest(triggerRequest) require.NoError(t, err) - regEvent := &remotetypes.MessageBody{ - Sender: p1[:], + return &remotetypes.MessageBody{ + Sender: sender[:], Method: remotetypes.MethodRegisterTrigger, - CallerDonId: workflowDonInfo.ID, + CallerDonId: callerDonID, Payload: marshaled, } - publisher.Receive(ctx, regEvent) - // node p1 is not a member of the workflow DON so registration shoudn't happen - require.Empty(t, underlying.registrationsCh) - - regEvent.Sender = p2[:] - publisher.Receive(ctx, regEvent) - require.NotEmpty(t, underlying.registrationsCh) - forwarded := <-underlying.registrationsCh - require.Equal(t, triggerRequest.Metadata.WorkflowID, forwarded.Metadata.WorkflowID) - - require.NoError(t, publisher.Close()) } type testTrigger struct { info commoncap.CapabilityInfo registrationsCh chan commoncap.TriggerRegistrationRequest + eventCh chan commoncap.TriggerResponse } -func (t *testTrigger) Info(_ context.Context) (commoncap.CapabilityInfo, error) { - return t.info, nil +func (tr *testTrigger) Info(_ context.Context) (commoncap.CapabilityInfo, error) { + return tr.info, nil } -func (t *testTrigger) RegisterTrigger(_ context.Context, request commoncap.TriggerRegistrationRequest) (<-chan commoncap.TriggerResponse, error) { - t.registrationsCh <- request - return nil, nil +func (tr *testTrigger) RegisterTrigger(_ context.Context, request commoncap.TriggerRegistrationRequest) (<-chan commoncap.TriggerResponse, error) { + tr.registrationsCh <- request + return tr.eventCh, nil } -func (t *testTrigger) UnregisterTrigger(_ context.Context, request commoncap.TriggerRegistrationRequest) error { +func (tr *testTrigger) UnregisterTrigger(_ context.Context, request commoncap.TriggerRegistrationRequest) error { return nil } diff --git a/core/capabilities/remote/types/types.go b/core/capabilities/remote/types/types.go index 7f3868486a4..54ec16f09f1 100644 --- a/core/capabilities/remote/types/types.go +++ b/core/capabilities/remote/types/types.go @@ -30,6 +30,11 @@ type Receiver interface { Receive(ctx context.Context, msg *MessageBody) } +type ReceiverService interface { + services.Service + Receiver +} + type Aggregator interface { Aggregate(eventID string, responses [][]byte) (commoncap.TriggerResponse, error) } diff --git a/core/chains/evm/gas/rollups/arbitrum_l1_oracle.go b/core/chains/evm/gas/rollups/arbitrum_l1_oracle.go index c01244db70c..d758dc711e9 100644 --- a/core/chains/evm/gas/rollups/arbitrum_l1_oracle.go +++ b/core/chains/evm/gas/rollups/arbitrum_l1_oracle.go @@ -112,6 +112,10 @@ func (o *arbitrumL1Oracle) Name() string { return o.logger.Name() } +func (o *arbitrumL1Oracle) ChainType(_ context.Context) chaintype.ChainType { + return o.chainType +} + func (o *arbitrumL1Oracle) Start(ctx context.Context) error { return o.StartOnce(o.Name(), func() error { go o.run() diff --git a/core/chains/evm/gas/rollups/l1_oracle.go b/core/chains/evm/gas/rollups/l1_oracle.go index e1249fdb7e9..41951755986 100644 --- a/core/chains/evm/gas/rollups/l1_oracle.go +++ b/core/chains/evm/gas/rollups/l1_oracle.go @@ -26,6 +26,7 @@ type L1Oracle interface { GasPrice(ctx context.Context) (*assets.Wei, error) GetGasCost(ctx context.Context, tx *types.Transaction, blockNum *big.Int) (*assets.Wei, error) + ChainType(ctx context.Context) chaintype.ChainType } type l1OracleClient interface { diff --git a/core/chains/evm/gas/rollups/l1_oracle_abi.go b/core/chains/evm/gas/rollups/l1_oracle_abi.go index 848957ce53a..fa5d9c85391 100644 --- a/core/chains/evm/gas/rollups/l1_oracle_abi.go +++ b/core/chains/evm/gas/rollups/l1_oracle_abi.go @@ -19,4 +19,3 @@ const OPBaseFeeScalarAbiString = `[{"inputs":[],"name":"baseFeeScalar","outputs" const OPBlobBaseFeeAbiString = `[{"inputs":[],"name":"blobBaseFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]` const OPBlobBaseFeeScalarAbiString = `[{"inputs":[],"name":"blobBaseFeeScalar","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"}]` const OPDecimalsAbiString = `[{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}]` -const MantleTokenRatioAbiString = `[{"inputs":[],"name":"tokenRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]` diff --git a/core/chains/evm/gas/rollups/mocks/l1_oracle.go b/core/chains/evm/gas/rollups/mocks/l1_oracle.go index e82cb4ee903..25bb3a2db59 100644 --- a/core/chains/evm/gas/rollups/mocks/l1_oracle.go +++ b/core/chains/evm/gas/rollups/mocks/l1_oracle.go @@ -7,6 +7,8 @@ import ( assets "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + chaintype "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" + context "context" mock "github.com/stretchr/testify/mock" @@ -27,6 +29,52 @@ func (_m *L1Oracle) EXPECT() *L1Oracle_Expecter { return &L1Oracle_Expecter{mock: &_m.Mock} } +// ChainType provides a mock function with given fields: ctx +func (_m *L1Oracle) ChainType(ctx context.Context) chaintype.ChainType { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for ChainType") + } + + var r0 chaintype.ChainType + if rf, ok := ret.Get(0).(func(context.Context) chaintype.ChainType); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(chaintype.ChainType) + } + + return r0 +} + +// L1Oracle_ChainType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainType' +type L1Oracle_ChainType_Call struct { + *mock.Call +} + +// ChainType is a helper method to define mock.On call +// - ctx context.Context +func (_e *L1Oracle_Expecter) ChainType(ctx interface{}) *L1Oracle_ChainType_Call { + return &L1Oracle_ChainType_Call{Call: _e.mock.On("ChainType", ctx)} +} + +func (_c *L1Oracle_ChainType_Call) Run(run func(ctx context.Context)) *L1Oracle_ChainType_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *L1Oracle_ChainType_Call) Return(_a0 chaintype.ChainType) *L1Oracle_ChainType_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *L1Oracle_ChainType_Call) RunAndReturn(run func(context.Context) chaintype.ChainType) *L1Oracle_ChainType_Call { + _c.Call.Return(run) + return _c +} + // Close provides a mock function with given fields: func (_m *L1Oracle) Close() error { ret := _m.Called() diff --git a/core/chains/evm/gas/rollups/op_l1_oracle.go b/core/chains/evm/gas/rollups/op_l1_oracle.go index 1b93f8fc3f9..11babc5ca5d 100644 --- a/core/chains/evm/gas/rollups/op_l1_oracle.go +++ b/core/chains/evm/gas/rollups/op_l1_oracle.go @@ -88,10 +88,6 @@ const ( // decimals is a hex encoded call to: // `function decimals() public pure returns (uint256);` decimalsMethod = "decimals" - // tokenRatio fetches the tokenRatio used for Mantle's gas price calculation - // tokenRatio is a hex encoded call to: - // `function tokenRatio() public pure returns (uint256);` - tokenRatioMethod = "tokenRatio" // OPGasOracleAddress is the address of the precompiled contract that exists on Optimism, Base and Mantle. OPGasOracleAddress = "0x420000000000000000000000000000000000000F" // KromaGasOracleAddress is the address of the precompiled contract that exists on Kroma. @@ -192,16 +188,6 @@ func newOpStackL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainTy return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", decimalsMethod, chainType, err) } - // Encode calldata for tokenRatio method - tokenRatioMethodAbi, err := abi.JSON(strings.NewReader(MantleTokenRatioAbiString)) - if err != nil { - return nil, fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", tokenRatioMethod, chainType, err) - } - tokenRatioCalldata, err := tokenRatioMethodAbi.Pack(tokenRatioMethod) - if err != nil { - return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", tokenRatioMethod, chainType, err) - } - return &optimismL1Oracle{ client: ethClient, pollPeriod: PollPeriod, @@ -223,7 +209,6 @@ func newOpStackL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainTy blobBaseFeeCalldata: blobBaseFeeCalldata, blobBaseFeeScalarCalldata: blobBaseFeeScalarCalldata, decimalsCalldata: decimalsCalldata, - tokenRatioCalldata: tokenRatioCalldata, isEcotoneCalldata: isEcotoneCalldata, isEcotoneMethodAbi: isEcotoneMethodAbi, isFjordCalldata: isFjordCalldata, @@ -235,6 +220,10 @@ func (o *optimismL1Oracle) Name() string { return o.logger.Name() } +func (o *optimismL1Oracle) ChainType(_ context.Context) chaintype.ChainType { + return o.chainType +} + func (o *optimismL1Oracle) Start(ctx context.Context) error { return o.StartOnce(o.Name(), func() error { go o.run() @@ -362,10 +351,6 @@ func (o *optimismL1Oracle) GetGasCost(ctx context.Context, tx *gethtypes.Transac } func (o *optimismL1Oracle) GetDAGasPrice(ctx context.Context) (*big.Int, error) { - if o.chainType == chaintype.ChainMantle { - return o.getMantleGasPrice(ctx) - } - err := o.checkForUpgrade(ctx) if err != nil { return nil, err @@ -463,69 +448,6 @@ func (o *optimismL1Oracle) getV1GasPrice(ctx context.Context) (*big.Int, error) return new(big.Int).SetBytes(b), nil } -// Returns the gas price for Mantle. The formula is the same as Optimism Bedrock (getV1GasPrice), but the tokenRatio parameter is multiplied -func (o *optimismL1Oracle) getMantleGasPrice(ctx context.Context) (*big.Int, error) { - // call oracle to get l1BaseFee and tokenRatio - rpcBatchCalls := []rpc.BatchElem{ - { - Method: "eth_call", - Args: []any{ - map[string]interface{}{ - "from": common.Address{}, - "to": o.l1OracleAddress, - "data": hexutil.Bytes(o.l1BaseFeeCalldata), - }, - "latest", - }, - Result: new(string), - }, - { - Method: "eth_call", - Args: []any{ - map[string]interface{}{ - "from": common.Address{}, - "to": o.l1OracleAddress, - "data": hexutil.Bytes(o.tokenRatioCalldata), - }, - "latest", - }, - Result: new(string), - }, - } - - err := o.client.BatchCallContext(ctx, rpcBatchCalls) - if err != nil { - return nil, fmt.Errorf("fetch gas price parameters batch call failed: %w", err) - } - if rpcBatchCalls[0].Error != nil { - return nil, fmt.Errorf("%s call failed in a batch: %w", l1BaseFeeMethod, err) - } - if rpcBatchCalls[1].Error != nil { - return nil, fmt.Errorf("%s call failed in a batch: %w", tokenRatioMethod, err) - } - - // Extract values from responses - l1BaseFeeResult := *(rpcBatchCalls[0].Result.(*string)) - tokenRatioResult := *(rpcBatchCalls[1].Result.(*string)) - - // Decode the responses into bytes - l1BaseFeeBytes, err := hexutil.Decode(l1BaseFeeResult) - if err != nil { - return nil, fmt.Errorf("failed to decode %s rpc result: %w", l1BaseFeeMethod, err) - } - tokenRatioBytes, err := hexutil.Decode(tokenRatioResult) - if err != nil { - return nil, fmt.Errorf("failed to decode %s rpc result: %w", tokenRatioMethod, err) - } - - // Convert bytes to big int for calculations - l1BaseFee := new(big.Int).SetBytes(l1BaseFeeBytes) - tokenRatio := new(big.Int).SetBytes(tokenRatioBytes) - - // multiply l1BaseFee and tokenRatio and return - return new(big.Int).Mul(l1BaseFee, tokenRatio), nil -} - // Returns the scaled gas price using baseFeeScalar, l1BaseFee, blobBaseFeeScalar, and blobBaseFee fields from the oracle // Confirmed the same calculation is used to determine gas price for both Ecotone and Fjord func (o *optimismL1Oracle) getEcotoneFjordGasPrice(ctx context.Context) (*big.Int, error) { diff --git a/core/chains/evm/gas/rollups/op_l1_oracle_test.go b/core/chains/evm/gas/rollups/op_l1_oracle_test.go index 88bb96534d3..f5f009f1ea6 100644 --- a/core/chains/evm/gas/rollups/op_l1_oracle_test.go +++ b/core/chains/evm/gas/rollups/op_l1_oracle_test.go @@ -111,94 +111,6 @@ func TestOPL1Oracle_ReadV1GasPrice(t *testing.T) { } } -func TestOPL1Oracle_ReadMantleGasPrice(t *testing.T) { - l1BaseFee := big.NewInt(100) - tokenRatio := big.NewInt(40) - oracleAddress := common.HexToAddress("0x1234").String() - - t.Parallel() - t.Run("correctly fetches gas price if chain is Mantle", func(t *testing.T) { - // Encode calldata for l1BaseFee method - l1BaseFeeMethodAbi, err := abi.JSON(strings.NewReader(L1BaseFeeAbiString)) - require.NoError(t, err) - l1BaseFeeCalldata, err := l1BaseFeeMethodAbi.Pack(l1BaseFeeMethod) - require.NoError(t, err) - - // Encode calldata for tokenRatio method - tokenRatioMethodAbi, err := abi.JSON(strings.NewReader(MantleTokenRatioAbiString)) - require.NoError(t, err) - tokenRatioCalldata, err := tokenRatioMethodAbi.Pack(tokenRatioMethod) - require.NoError(t, err) - - ethClient := mocks.NewL1OracleClient(t) - ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { - rpcElements := args.Get(1).([]rpc.BatchElem) - require.Equal(t, 2, len(rpcElements)) - for _, rE := range rpcElements { - require.Equal(t, "eth_call", rE.Method) - require.Equal(t, oracleAddress, rE.Args[0].(map[string]interface{})["to"]) - require.Equal(t, "latest", rE.Args[1]) - } - require.Equal(t, hexutil.Bytes(l1BaseFeeCalldata), rpcElements[0].Args[0].(map[string]interface{})["data"]) - require.Equal(t, hexutil.Bytes(tokenRatioCalldata), rpcElements[1].Args[0].(map[string]interface{})["data"]) - - res1 := common.BigToHash(l1BaseFee).Hex() - res2 := common.BigToHash(tokenRatio).Hex() - - rpcElements[0].Result = &res1 - rpcElements[1].Result = &res2 - }).Return(nil).Once() - - oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainMantle, oracleAddress) - require.NoError(t, err) - - gasPrice, err := oracle.GetDAGasPrice(tests.Context(t)) - require.NoError(t, err) - - assert.Equal(t, new(big.Int).Mul(l1BaseFee, tokenRatio), gasPrice) - }) - - t.Run("fetching Mantle price but rpc returns bad data", func(t *testing.T) { - ethClient := mocks.NewL1OracleClient(t) - ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { - rpcElements := args.Get(1).([]rpc.BatchElem) - var badData = "zzz" - rpcElements[0].Result = &badData - rpcElements[1].Result = &badData - }).Return(nil).Once() - - oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainMantle, oracleAddress) - require.NoError(t, err) - _, err = oracle.GetDAGasPrice(tests.Context(t)) - assert.Error(t, err) - }) - - t.Run("fetching Mantle price but rpc parent call errors", func(t *testing.T) { - ethClient := mocks.NewL1OracleClient(t) - ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Return(fmt.Errorf("revert")).Once() - - oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainMantle, oracleAddress) - require.NoError(t, err) - _, err = oracle.GetDAGasPrice(tests.Context(t)) - assert.Error(t, err) - }) - - t.Run("fetching Mantle price but one of the sub rpc call errors", func(t *testing.T) { - ethClient := mocks.NewL1OracleClient(t) - ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { - rpcElements := args.Get(1).([]rpc.BatchElem) - res := common.BigToHash(l1BaseFee).Hex() - rpcElements[0].Result = &res - rpcElements[1].Error = fmt.Errorf("revert") - }).Return(nil).Once() - - oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainMantle, oracleAddress) - require.NoError(t, err) - _, err = oracle.GetDAGasPrice(tests.Context(t)) - assert.Error(t, err) - }) -} - func setupUpgradeCheck(t *testing.T, oracleAddress string, isFjord, isEcotone bool) *mocks.L1OracleClient { trueHex := "0x0000000000000000000000000000000000000000000000000000000000000001" falseHex := "0x0000000000000000000000000000000000000000000000000000000000000000" diff --git a/core/chains/evm/gas/rollups/zkSync_l1_oracle.go b/core/chains/evm/gas/rollups/zkSync_l1_oracle.go index 31d93bc587d..c2941233545 100644 --- a/core/chains/evm/gas/rollups/zkSync_l1_oracle.go +++ b/core/chains/evm/gas/rollups/zkSync_l1_oracle.go @@ -83,6 +83,10 @@ func (o *zkSyncL1Oracle) Name() string { return o.logger.Name() } +func (o *zkSyncL1Oracle) ChainType(_ context.Context) chaintype.ChainType { + return o.chainType +} + func (o *zkSyncL1Oracle) Start(ctx context.Context) error { return o.StartOnce(o.Name(), func() error { go o.run() diff --git a/core/chains/evm/logpoller/orm.go b/core/chains/evm/logpoller/orm.go index 1c119c9f9ac..5ab7db4eb02 100644 --- a/core/chains/evm/logpoller/orm.go +++ b/core/chains/evm/logpoller/orm.go @@ -330,8 +330,9 @@ func (o *DSORM) DeleteExpiredLogs(ctx context.Context, limit int64) (int64, erro FROM evm.log_poller_filters WHERE evm_chain_id = $1 GROUP BY evm_chain_id, address, event - ) r ON l.evm_chain_id = $1 AND l.address = r.address AND l.event_sig = r.event - WHERE r.retention IS NULL OR (r.retention != 0 AND l.block_timestamp <= STATEMENT_TIMESTAMP() - (r.retention / 10^9 * interval '1 second')) %s)` + ) r ON l.address = r.address AND l.event_sig = r.event + WHERE l.evm_chain_id = $1 AND -- Must be WHERE rather than ON due to LEFT JOIN + r.retention IS NULL OR (r.retention != 0 AND l.block_timestamp <= STATEMENT_TIMESTAMP() - (r.retention / 10^9 * interval '1 second')) %s)` if limit > 0 { result, err = o.ds.ExecContext(ctx, fmt.Sprintf(query, "LIMIT $2"), ubig.New(o.chainID), limit) diff --git a/core/gethwrappers/keystone/generated/ocr3_capability/ocr3_capability.go b/core/gethwrappers/keystone/generated/ocr3_capability/ocr3_capability.go index e51f4762d88..21aeb4975c9 100644 --- a/core/gethwrappers/keystone/generated/ocr3_capability/ocr3_capability.go +++ b/core/gethwrappers/keystone/generated/ocr3_capability/ocr3_capability.go @@ -31,8 +31,8 @@ var ( ) var OCR3CapabilityMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"ReportInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReportingUnsupported\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"previousConfigBlockNumber\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"onchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"scanLogs\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"_f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"_onchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"_offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"_offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"transmitters\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", - Bin: "0x60806040523480156200001157600080fd5b503380600081620000695760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b03848116919091179091558116156200009c576200009c81620000a5565b50505062000150565b336001600160a01b03821603620000ff5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000060565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b611fe180620001606000396000f3fe608060405234801561001057600080fd5b50600436106100a35760003560e01c80638da5cb5b11610076578063b1dc65a41161005b578063b1dc65a4146101c4578063e3d0e712146101d7578063f2fde38b146101ea57600080fd5b80638da5cb5b1461017c578063afcb95d7146101a457600080fd5b8063181f5a77146100a857806379ba5097146100f057806381411834146100fa57806381ff70481461010f575b600080fd5b604080518082018252600e81527f4b657973746f6e6520312e302e30000000000000000000000000000000000000602082015290516100e79190611883565b60405180910390f35b6100f86101fd565b005b6101026102ff565b6040516100e791906118ef565b61015960015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff9485168152939092166020840152908201526060016100e7565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100e7565b6040805160018152600060208201819052918101919091526060016100e7565b6100f86101d236600461194e565b61036e565b6100f86101e5366004611c18565b610975565b6100f86101f8366004611ce5565b6114e0565b60015473ffffffffffffffffffffffffffffffffffffffff163314610283576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6060600680548060200260200160405190810160405280929190818152602001828054801561036457602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610339575b5050505050905090565b60005a604080518b3580825262ffffff6020808f0135600881901c929092169084015293945092917fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a16103cf8a8a8a8a8a8a6114f4565b6003546000906002906103ed9060ff80821691610100900416611d5e565b6103f79190611d7d565b610402906001611d5e565b60ff169050878114610470576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f77726f6e67206e756d626572206f66207369676e617475726573000000000000604482015260640161027a565b8786146104ff576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f7265706f727420727320616e64207373206d757374206265206f66206571756160448201527f6c206c656e677468000000000000000000000000000000000000000000000000606482015260840161027a565b3360009081526004602090815260408083208151808301909252805460ff8082168452929391929184019161010090910416600281111561054257610542611dc6565b600281111561055357610553611dc6565b905250905060028160200151600281111561057057610570611dc6565b141580156105b957506006816000015160ff168154811061059357610593611d00565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff163314155b15610620576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f756e617574686f72697a6564207472616e736d69747465720000000000000000604482015260640161027a565b5050505061062c611800565b6000808a8a60405161063f929190611df5565b604051908190038120610656918e90602001611e05565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120838301909252600080845290830152915060005b898110156109575760006001848984602081106106bf576106bf611d00565b6106cc91901a601b611d5e565b8e8e868181106106de576106de611d00565b905060200201358d8d878181106106f7576106f7611d00565b9050602002013560405160008152602001604052604051610734949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015610756573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff811660009081526004602090815290849020838501909452835460ff808216855292965092945084019161010090041660028111156107d6576107d6611dc6565b60028111156107e7576107e7611dc6565b905250925060018360200151600281111561080457610804611dc6565b1461086b576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e0000604482015260640161027a565b8251600090879060ff16601f811061088557610885611d00565b602002015173ffffffffffffffffffffffffffffffffffffffff1614610907576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6e6f6e2d756e69717565207369676e6174757265000000000000000000000000604482015260640161027a565b8086846000015160ff16601f811061092157610921611d00565b73ffffffffffffffffffffffffffffffffffffffff909216602092909202015261094c600186611d5e565b9450506001016106a0565b505050610968833383858e8e6115ab565b5050505050505050505050565b855185518560ff16601f8311156109e8576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e65727300000000000000000000000000000000604482015260640161027a565b80600003610a52576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f7369746976650000000000000000000000000000604482015260640161027a565b818314610ae0576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e00000000000000000000000000000000000000000000000000000000606482015260840161027a565b610aeb816003611e19565b8311610b53576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f20686967680000000000000000604482015260640161027a565b610b5b6115dd565b6040805160c0810182528a8152602081018a905260ff8916918101919091526060810187905267ffffffffffffffff8616608082015260a081018590525b60055415610d4e57600554600090610bb390600190611e30565b9050600060058281548110610bca57610bca611d00565b60009182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff90921693509084908110610c0457610c04611d00565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff85811684526004909252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090811690915592909116808452922080549091169055600580549192509080610c8457610c84611e43565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190556006805480610ced57610ced611e43565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550610b99915050565b60005b8151518110156112fd57815180516000919083908110610d7357610d73611d00565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603610df8576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f7369676e6572206d757374206e6f7420626520656d7074790000000000000000604482015260640161027a565b600073ffffffffffffffffffffffffffffffffffffffff1682602001518281518110610e2657610e26611d00565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603610eab576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f7472616e736d6974746572206d757374206e6f7420626520656d707479000000604482015260640161027a565b60006004600084600001518481518110610ec757610ec7611d00565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115610f1157610f11611dc6565b14610f78576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f7265706561746564207369676e65722061646472657373000000000000000000604482015260640161027a565b6040805180820190915260ff82168152600160208201528251805160049160009185908110610fa957610fa9611d00565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000161761010083600281111561104a5761104a611dc6565b02179055506000915061105a9050565b600460008460200151848151811061107457611074611d00565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff1660028111156110be576110be611dc6565b14611125576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7265706561746564207472616e736d6974746572206164647265737300000000604482015260640161027a565b6040805180820190915260ff82168152602081016002815250600460008460200151848151811061115857611158611d00565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016176101008360028111156111f9576111f9611dc6565b02179055505082518051600592508390811061121757611217611d00565b602090810291909101810151825460018101845560009384529282902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909316929092179091558201518051600691908390811061129357611293611d00565b60209081029190910181015182546001808201855560009485529290932090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9093169290921790915501610d51565b506040810151600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff43811682029290921780855592048116929182916014916113b591849174010000000000000000000000000000000000000000900416611e72565b92506101000a81548163ffffffff021916908363ffffffff1602179055506114144630600160149054906101000a900463ffffffff1663ffffffff16856000015186602001518760400151886060015189608001518a60a00151611660565b600281905582518051600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff9093169290920291909117905560015460208501516040808701516060880151608089015160a08a015193517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05986114cb988b9891977401000000000000000000000000000000000000000090920463ffffffff16969095919491939192611e96565b60405180910390a15050505050505050505050565b6114e86115dd565b6114f18161170b565b50565b6000611501826020611e19565b61150c856020611e19565b61151888610144611f2c565b6115229190611f2c565b61152c9190611f2c565b611537906000611f2c565b90503681146115a2576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f63616c6c64617461206c656e677468206d69736d617463680000000000000000604482015260640161027a565b50505050505050565b6040517f0750181900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005473ffffffffffffffffffffffffffffffffffffffff16331461165e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640161027a565b565b6000808a8a8a8a8a8a8a8a8a60405160200161168499989796959493929190611f3f565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff82160361178a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161027a565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b604051806103e00160405280601f906020820280368337509192915050565b6000815180845260005b8181101561184557602081850181015186830182015201611829565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000611896602083018461181f565b9392505050565b60008151808452602080850194506020840160005b838110156118e457815173ffffffffffffffffffffffffffffffffffffffff16875295820195908201906001016118b2565b509495945050505050565b602081526000611896602083018461189d565b60008083601f84011261191457600080fd5b50813567ffffffffffffffff81111561192c57600080fd5b6020830191508360208260051b850101111561194757600080fd5b9250929050565b60008060008060008060008060e0898b03121561196a57600080fd5b606089018a81111561197b57600080fd5b8998503567ffffffffffffffff8082111561199557600080fd5b818b0191508b601f8301126119a957600080fd5b8135818111156119b857600080fd5b8c60208285010111156119ca57600080fd5b6020830199508098505060808b01359150808211156119e857600080fd5b6119f48c838d01611902565b909750955060a08b0135915080821115611a0d57600080fd5b50611a1a8b828c01611902565b999c989b50969995989497949560c00135949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611aa957611aa9611a33565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611ad557600080fd5b919050565b600082601f830112611aeb57600080fd5b8135602067ffffffffffffffff821115611b0757611b07611a33565b8160051b611b16828201611a62565b9283528481018201928281019087851115611b3057600080fd5b83870192505b84831015611b5657611b4783611ab1565b82529183019190830190611b36565b979650505050505050565b803560ff81168114611ad557600080fd5b600082601f830112611b8357600080fd5b813567ffffffffffffffff811115611b9d57611b9d611a33565b611bce60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611a62565b818152846020838601011115611be357600080fd5b816020850160208301376000918101602001919091529392505050565b803567ffffffffffffffff81168114611ad557600080fd5b60008060008060008060c08789031215611c3157600080fd5b863567ffffffffffffffff80821115611c4957600080fd5b611c558a838b01611ada565b97506020890135915080821115611c6b57600080fd5b611c778a838b01611ada565b9650611c8560408a01611b61565b95506060890135915080821115611c9b57600080fd5b611ca78a838b01611b72565b9450611cb560808a01611c00565b935060a0890135915080821115611ccb57600080fd5b50611cd889828a01611b72565b9150509295509295509295565b600060208284031215611cf757600080fd5b61189682611ab1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff8181168382160190811115611d7757611d77611d2f565b92915050565b600060ff831680611db7577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8060ff84160491505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183823760009101908152919050565b828152606082602083013760800192915050565b8082028115828204841417611d7757611d77611d2f565b81810381811115611d7757611d77611d2f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b63ffffffff818116838216019080821115611e8f57611e8f611d2f565b5092915050565b600061012063ffffffff808d1684528b6020850152808b16604085015250806060840152611ec68184018a61189d565b90508281036080840152611eda818961189d565b905060ff871660a084015282810360c0840152611ef7818761181f565b905067ffffffffffffffff851660e0840152828103610100840152611f1c818561181f565b9c9b505050505050505050505050565b80820180821115611d7757611d77611d2f565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152611f868285018b61189d565b91508382036080850152611f9a828a61189d565b915060ff881660a085015283820360c0850152611fb7828861181f565b90861660e08501528381036101008501529050611f1c818561181f56fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReportingUnsupported\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"previousConfigBlockNumber\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes[]\",\"name\":\"signers\",\"type\":\"bytes[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"onchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"scanLogs\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"_signers\",\"type\":\"bytes[]\"},{\"internalType\":\"address[]\",\"name\":\"_transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"_f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"_onchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"_offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"_offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b611266806101576000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80638da5cb5b1161005b5780638da5cb5b1461015f578063afcb95d714610187578063b1dc65a4146101a7578063f2fde38b146101ba57600080fd5b8063181f5a771461008d57806379ba5097146100d55780637f3c87d3146100df57806381ff7048146100f2575b600080fd5b604080518082018252600e81527f4b657973746f6e6520312e302e30000000000000000000000000000000000000602082015290516100cc9190610b02565b60405180910390f35b6100dd6101cd565b005b6100dd6100ed366004610c70565b6102cf565b61013c60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff9485168152939092166020840152908201526060016100cc565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100cc565b6040805160018152600060208201819052918101919091526060016100cc565b6100dd6101b5366004610d4c565b61082f565b6100dd6101c8366004610e55565b610861565b60015473ffffffffffffffffffffffffffffffffffffffff163314610253576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b868560ff8616601f831115610340576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e65727300000000000000000000000000000000604482015260640161024a565b806000036103aa576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f7369746976650000000000000000000000000000604482015260640161024a565b818314610438576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e00000000000000000000000000000000000000000000000000000000606482015260840161024a565b610443816003610e9f565b83116104ab576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f20686967680000000000000000604482015260640161024a565b6104b3610875565b60005b8a81101561069d5760008a8a838181106104d2576104d2610ebc565b90506020020160208101906104e79190610e55565b73ffffffffffffffffffffffffffffffffffffffff1603610564576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f7472616e736d6974746572206d757374206e6f7420626520656d707479000000604482015260640161024a565b3660008d8d8481811061057957610579610ebc565b905060200281019061058b9190610eeb565b90925090506000815b8061ffff168261ffff16101561068d57600084848461ffff168181106105bc576105bc610ebc565b919091013560f81c915060009050600886866105d9876002610f50565b61ffff168181106105ec576105ec610ebc565b919091013560f81c90911b90508686610606876001610f50565b61ffff1681811061061957610619610ebc565b61062a9392013560f81c9050610f50565b9050366000878761063c886003610f50565b61ffff16908561064d8a6003610f50565b6106579190610f50565b61ffff169261066893929190610f72565b9092509050610678836003610f50565b6106829087610f50565b955050505050610594565b5050600190920191506104b69050565b50600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff8916179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff438116820292909217808555920481169291829160149161074c91849174010000000000000000000000000000000000000000900416610f9c565b92506101000a81548163ffffffff021916908363ffffffff1602179055506107954630600160149054906101000a900463ffffffff1663ffffffff168f8f8f8f8f8f8f8f6108f8565b6002600001819055508b8b9050600260010160016101000a81548160ff021916908360ff1602179055507f36257c6e8d535293ad661e377c0baac536289be6707b8a488ac175ddaa4055c881600260000154600160149054906101000a900463ffffffff168f8f8f8f8f8f8f8f6040516108199b9a99989796959493929190611128565b60405180910390a1505050505050505050505050565b6040517f0750181900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610869610875565b610872816109a9565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146108f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640161024a565b565b6000808c8c8c8c8c8c8c8c8c8c8c6040516020016109209b9a999897969594939291906111c2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0e000000000000000000000000000000000000000000000000000000000000179150509b9a5050505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603610a28576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161024a565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000815180845260005b81811015610ac457602081850181015186830182015201610aa8565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000610b156020830184610a9e565b9392505050565b60008083601f840112610b2e57600080fd5b50813567ffffffffffffffff811115610b4657600080fd5b6020830191508360208260051b8501011115610b6157600080fd5b9250929050565b803560ff81168114610b7957600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112610bbe57600080fd5b813567ffffffffffffffff80821115610bd957610bd9610b7e565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715610c1f57610c1f610b7e565b81604052838152866020858801011115610c3857600080fd5b836020870160208301376000602085830101528094505050505092915050565b803567ffffffffffffffff81168114610b7957600080fd5b60008060008060008060008060c0898b031215610c8c57600080fd5b883567ffffffffffffffff80821115610ca457600080fd5b610cb08c838d01610b1c565b909a50985060208b0135915080821115610cc957600080fd5b610cd58c838d01610b1c565b9098509650869150610ce960408c01610b68565b955060608b0135915080821115610cff57600080fd5b610d0b8c838d01610bad565b9450610d1960808c01610c58565b935060a08b0135915080821115610d2f57600080fd5b50610d3c8b828c01610bad565b9150509295985092959890939650565b60008060008060008060008060e0898b031215610d6857600080fd5b606089018a811115610d7957600080fd5b8998503567ffffffffffffffff80821115610d9357600080fd5b818b0191508b601f830112610da757600080fd5b813581811115610db657600080fd5b8c6020828501011115610dc857600080fd5b6020830199508098505060808b0135915080821115610de657600080fd5b610df28c838d01610b1c565b909750955060a08b0135915080821115610e0b57600080fd5b50610e188b828c01610b1c565b999c989b50969995989497949560c00135949350505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610b7957600080fd5b600060208284031215610e6757600080fd5b610b1582610e31565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610eb657610eb6610e70565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610f2057600080fd5b83018035915067ffffffffffffffff821115610f3b57600080fd5b602001915036819003821315610b6157600080fd5b61ffff818116838216019080821115610f6b57610f6b610e70565b5092915050565b60008085851115610f8257600080fd5b83861115610f8f57600080fd5b5050820193919092039150565b63ffffffff818116838216019080821115610f6b57610f6b610e70565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b6000838385526020808601955060208560051b8301018460005b878110156110c7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085840301895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe188360301811261107d57600080fd5b8701848101903567ffffffffffffffff81111561109957600080fd5b8036038213156110a857600080fd5b6110b3858284610fb9565b9a86019a945050509083019060010161101c565b5090979650505050505050565b8183526000602080850194508260005b8581101561111d5773ffffffffffffffffffffffffffffffffffffffff61110a83610e31565b16875295820195908201906001016110e4565b509495945050505050565b600061012063ffffffff808f1684528d6020850152808d166040850152508060608401526111598184018b8d611002565b9050828103608084015261116e81898b6110d4565b905060ff871660a084015282810360c084015261118b8187610a9e565b905067ffffffffffffffff851660e08401528281036101008401526111b08185610a9e565b9e9d5050505050505050505050505050565b60006101208d835273ffffffffffffffffffffffffffffffffffffffff8d16602084015267ffffffffffffffff808d16604085015281606085015261120a8285018c8e611002565b9150838203608085015261121f828a8c6110d4565b915060ff881660a085015283820360c085015261123c8288610a9e565b90861660e085015283810361010085015290506111b08185610a9e56fea164736f6c6343000818000a", } var OCR3CapabilityABI = OCR3CapabilityMetaData.ABI @@ -255,26 +255,24 @@ func (_OCR3Capability *OCR3CapabilityCallerSession) Owner() (common.Address, err return _OCR3Capability.Contract.Owner(&_OCR3Capability.CallOpts) } -func (_OCR3Capability *OCR3CapabilityCaller) Transmitters(opts *bind.CallOpts) ([]common.Address, error) { +func (_OCR3Capability *OCR3CapabilityCaller) Transmit(opts *bind.CallOpts, arg0 [3][32]byte, arg1 []byte, arg2 [][32]byte, arg3 [][32]byte, arg4 [32]byte) error { var out []interface{} - err := _OCR3Capability.contract.Call(opts, &out, "transmitters") + err := _OCR3Capability.contract.Call(opts, &out, "transmit", arg0, arg1, arg2, arg3, arg4) if err != nil { - return *new([]common.Address), err + return err } - out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) - - return out0, err + return err } -func (_OCR3Capability *OCR3CapabilitySession) Transmitters() ([]common.Address, error) { - return _OCR3Capability.Contract.Transmitters(&_OCR3Capability.CallOpts) +func (_OCR3Capability *OCR3CapabilitySession) Transmit(arg0 [3][32]byte, arg1 []byte, arg2 [][32]byte, arg3 [][32]byte, arg4 [32]byte) error { + return _OCR3Capability.Contract.Transmit(&_OCR3Capability.CallOpts, arg0, arg1, arg2, arg3, arg4) } -func (_OCR3Capability *OCR3CapabilityCallerSession) Transmitters() ([]common.Address, error) { - return _OCR3Capability.Contract.Transmitters(&_OCR3Capability.CallOpts) +func (_OCR3Capability *OCR3CapabilityCallerSession) Transmit(arg0 [3][32]byte, arg1 []byte, arg2 [][32]byte, arg3 [][32]byte, arg4 [32]byte) error { + return _OCR3Capability.Contract.Transmit(&_OCR3Capability.CallOpts, arg0, arg1, arg2, arg3, arg4) } func (_OCR3Capability *OCR3CapabilityCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) { @@ -311,15 +309,15 @@ func (_OCR3Capability *OCR3CapabilityTransactorSession) AcceptOwnership() (*type return _OCR3Capability.Contract.AcceptOwnership(&_OCR3Capability.TransactOpts) } -func (_OCR3Capability *OCR3CapabilityTransactor) SetConfig(opts *bind.TransactOpts, _signers []common.Address, _transmitters []common.Address, _f uint8, _onchainConfig []byte, _offchainConfigVersion uint64, _offchainConfig []byte) (*types.Transaction, error) { +func (_OCR3Capability *OCR3CapabilityTransactor) SetConfig(opts *bind.TransactOpts, _signers [][]byte, _transmitters []common.Address, _f uint8, _onchainConfig []byte, _offchainConfigVersion uint64, _offchainConfig []byte) (*types.Transaction, error) { return _OCR3Capability.contract.Transact(opts, "setConfig", _signers, _transmitters, _f, _onchainConfig, _offchainConfigVersion, _offchainConfig) } -func (_OCR3Capability *OCR3CapabilitySession) SetConfig(_signers []common.Address, _transmitters []common.Address, _f uint8, _onchainConfig []byte, _offchainConfigVersion uint64, _offchainConfig []byte) (*types.Transaction, error) { +func (_OCR3Capability *OCR3CapabilitySession) SetConfig(_signers [][]byte, _transmitters []common.Address, _f uint8, _onchainConfig []byte, _offchainConfigVersion uint64, _offchainConfig []byte) (*types.Transaction, error) { return _OCR3Capability.Contract.SetConfig(&_OCR3Capability.TransactOpts, _signers, _transmitters, _f, _onchainConfig, _offchainConfigVersion, _offchainConfig) } -func (_OCR3Capability *OCR3CapabilityTransactorSession) SetConfig(_signers []common.Address, _transmitters []common.Address, _f uint8, _onchainConfig []byte, _offchainConfigVersion uint64, _offchainConfig []byte) (*types.Transaction, error) { +func (_OCR3Capability *OCR3CapabilityTransactorSession) SetConfig(_signers [][]byte, _transmitters []common.Address, _f uint8, _onchainConfig []byte, _offchainConfigVersion uint64, _offchainConfig []byte) (*types.Transaction, error) { return _OCR3Capability.Contract.SetConfig(&_OCR3Capability.TransactOpts, _signers, _transmitters, _f, _onchainConfig, _offchainConfigVersion, _offchainConfig) } @@ -335,18 +333,6 @@ func (_OCR3Capability *OCR3CapabilityTransactorSession) TransferOwnership(to com return _OCR3Capability.Contract.TransferOwnership(&_OCR3Capability.TransactOpts, to) } -func (_OCR3Capability *OCR3CapabilityTransactor) Transmit(opts *bind.TransactOpts, reportContext [3][32]byte, report []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) { - return _OCR3Capability.contract.Transact(opts, "transmit", reportContext, report, rs, ss, rawVs) -} - -func (_OCR3Capability *OCR3CapabilitySession) Transmit(reportContext [3][32]byte, report []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) { - return _OCR3Capability.Contract.Transmit(&_OCR3Capability.TransactOpts, reportContext, report, rs, ss, rawVs) -} - -func (_OCR3Capability *OCR3CapabilityTransactorSession) Transmit(reportContext [3][32]byte, report []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) { - return _OCR3Capability.Contract.Transmit(&_OCR3Capability.TransactOpts, reportContext, report, rs, ss, rawVs) -} - type OCR3CapabilityConfigSetIterator struct { Event *OCR3CapabilityConfigSet @@ -411,7 +397,7 @@ type OCR3CapabilityConfigSet struct { PreviousConfigBlockNumber uint32 ConfigDigest [32]byte ConfigCount uint64 - Signers []common.Address + Signers [][]byte Transmitters []common.Address F uint8 OnchainConfig []byte @@ -890,7 +876,7 @@ func (_OCR3Capability *OCR3Capability) ParseLog(log types.Log) (generated.Abigen } func (OCR3CapabilityConfigSet) Topic() common.Hash { - return common.HexToHash("0x1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05") + return common.HexToHash("0x36257c6e8d535293ad661e377c0baac536289be6707b8a488ac175ddaa4055c8") } func (OCR3CapabilityOwnershipTransferRequested) Topic() common.Hash { @@ -920,18 +906,16 @@ type OCR3CapabilityInterface interface { Owner(opts *bind.CallOpts) (common.Address, error) - Transmitters(opts *bind.CallOpts) ([]common.Address, error) + Transmit(opts *bind.CallOpts, arg0 [3][32]byte, arg1 []byte, arg2 [][32]byte, arg3 [][32]byte, arg4 [32]byte) error TypeAndVersion(opts *bind.CallOpts) (string, error) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) - SetConfig(opts *bind.TransactOpts, _signers []common.Address, _transmitters []common.Address, _f uint8, _onchainConfig []byte, _offchainConfigVersion uint64, _offchainConfig []byte) (*types.Transaction, error) + SetConfig(opts *bind.TransactOpts, _signers [][]byte, _transmitters []common.Address, _f uint8, _onchainConfig []byte, _offchainConfigVersion uint64, _offchainConfig []byte) (*types.Transaction, error) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) - Transmit(opts *bind.TransactOpts, reportContext [3][32]byte, report []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) - FilterConfigSet(opts *bind.FilterOpts) (*OCR3CapabilityConfigSetIterator, error) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *OCR3CapabilityConfigSet) (event.Subscription, error) diff --git a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt index d8b3b030423..6f9d3f2b3a2 100644 --- a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -2,4 +2,4 @@ GETH_VERSION: 1.13.8 capabilities_registry: ../../../contracts/solc/v0.8.24/CapabilitiesRegistry/CapabilitiesRegistry.abi ../../../contracts/solc/v0.8.24/CapabilitiesRegistry/CapabilitiesRegistry.bin cb3e79280a928979bc37de154b12b876996bdbe10f1827e683ee2bfa7a429a6c feeds_consumer: ../../../contracts/solc/v0.8.24/KeystoneFeedsConsumer/KeystoneFeedsConsumer.abi ../../../contracts/solc/v0.8.24/KeystoneFeedsConsumer/KeystoneFeedsConsumer.bin 6ac5b12eff3b022a35c3c40d5ed0285bf9bfec0e3669a4b12307332a216048ca forwarder: ../../../contracts/solc/v0.8.24/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.24/KeystoneForwarder/KeystoneForwarder.bin 0da2ce239c9d4521005428f2d42a67dbee2ae6dd7160fd9e4f4322fb51d4f6ba -ocr3_capability: ../../../contracts/solc/v0.8.24/OCR3Capability/OCR3Capability.abi ../../../contracts/solc/v0.8.24/OCR3Capability/OCR3Capability.bin 8bf0f53f222efce7143dea6134552eb26ea1eef845407b4475a0d79b7d7ba9f8 +ocr3_capability: ../../../contracts/solc/v0.8.24/OCR3Capability/OCR3Capability.abi ../../../contracts/solc/v0.8.24/OCR3Capability/OCR3Capability.bin 2a6bfae30ccf38327fc7e78605a226839dfa0ce5a1a22e0414b91d24c35b3a53 diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 4b11f323a57..59e032565f5 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -22,7 +22,7 @@ require ( github.com/prometheus/client_golang v1.20.0 github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chainlink-automation v1.0.4 - github.com/smartcontractkit/chainlink-common v0.2.2-0.20240913161926-ce5d667907ce + github.com/smartcontractkit/chainlink-common v0.2.2-0.20240916150342-36cb47701edf github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240717100443-f6226e09bee7 github.com/spf13/cobra v1.8.1 @@ -271,9 +271,9 @@ require ( github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shirou/gopsutil/v3 v3.24.3 // indirect github.com/smartcontractkit/chain-selectors v1.0.23 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20240916150615-85b8aa5fa7e6 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20240917180332-5a68498d1612 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240911175228-daf2600bb7b7 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240906125718-9f0a98d32fbc // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240916152957-433914114bd2 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240910155501-42f20443189f // indirect github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240911182932-3c609a6ac664 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240911194142-506bc469d8ae // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 5a945009635..8cd12f5e426 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1081,14 +1081,14 @@ github.com/smartcontractkit/chain-selectors v1.0.23 h1:D2Eaex4Cw/O7Lg3tX6WklOqnj github.com/smartcontractkit/chain-selectors v1.0.23/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20240916150615-85b8aa5fa7e6 h1:cbHlV2CSphQ+ghDye21M8ym0aAO/Y649H2Mg60M2AuE= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20240916150615-85b8aa5fa7e6/go.mod h1:Lv77O13ZxOdmlvnu2vaUC0Lg+t3JAL+N+9K8dRsgmDI= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240913161926-ce5d667907ce h1:qXS0aWiDFDoLRCB+kSGnzp77iYT2luflUyzE5BnNmpY= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240913161926-ce5d667907ce/go.mod h1:sjiiPwd4KsYOCf68MwL86EKphdXeT66EY7j53WH5DCc= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240917180332-5a68498d1612 h1:xPEM9XbfZmv8N3NjZ7AX5salonll/LdXrbb8JCbA4FE= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240917180332-5a68498d1612/go.mod h1:Lv77O13ZxOdmlvnu2vaUC0Lg+t3JAL+N+9K8dRsgmDI= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240916150342-36cb47701edf h1:1AlTUkT5D8HmvU9bwDoIN54/EFyOnRBl7gnXZVrYXEA= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240916150342-36cb47701edf/go.mod h1:l8NTByXUdGGJX+vyKYI6yX1/HIpM14F8Wm9BkU3Q4Qo= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240911175228-daf2600bb7b7 h1:lTGIOQYLk1Ufn++X/AvZnt6VOcuhste5yp+C157No/Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240911175228-daf2600bb7b7/go.mod h1:BMYE1vC/pGmdFSsOJdPrAA0/4gZ0Xo0SxTMdGspBtRo= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240906125718-9f0a98d32fbc h1:tRmTlaoAt+7FakMXXgeCuRPmzzBo5jsGpeCVvcU6KMc= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240906125718-9f0a98d32fbc/go.mod h1:PwPcmQNAzVmU8r8JWKrDRgvXesDwxnqbMD6DvYt/Z7M= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240916152957-433914114bd2 h1:yRk4ektpx/UxwarqAfgxUXLrsYXlaNeP1NOwzHGrK2Q= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240916152957-433914114bd2/go.mod h1:rNhNSrrRMvkgAm5SA6bNTdh2340bTQQZdUVNtZ2o2bk= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240910155501-42f20443189f h1:p4p3jBT91EQyLuAMvHD+zNJsuAYI/QjJbzuGUJ7wIgg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240910155501-42f20443189f/go.mod h1:FLlWBt2hwiMVgt9AcSo6wBJYIRd/nsc8ENbV1Wir1bw= github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240911182932-3c609a6ac664 h1:JPs35oSO07PK3Qv7Kyv0GJHVLacIE1IkrvefaPyBjKs= diff --git a/core/scripts/keystone/src/88_gen_ocr3_config.go b/core/scripts/keystone/src/88_gen_ocr3_config.go index 1107df57ca1..d9280ffe243 100644 --- a/core/scripts/keystone/src/88_gen_ocr3_config.go +++ b/core/scripts/keystone/src/88_gen_ocr3_config.go @@ -4,6 +4,7 @@ import ( "crypto/ed25519" "encoding/hex" "encoding/json" + "fmt" "time" "github.com/ethereum/go-ethereum/common" @@ -13,6 +14,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" + "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) @@ -56,7 +58,7 @@ type NodeKeys struct { } type orc2drOracleConfig struct { - Signers []common.Address + Signers [][]byte Transmitters []common.Address F uint8 OnchainConfig []byte @@ -82,7 +84,7 @@ func (c orc2drOracleConfig) MarshalJSON() ([]byte, error) { } for i, signer := range c.Signers { - alias.Signers[i] = signer.Hex() + alias.Signers[i] = hex.EncodeToString(signer) } for i, transmitter := range c.Transmitters { @@ -101,9 +103,27 @@ func generateOCR3Config(nodeList string, configFile string, chainID int64, pubKe cfg := topLevelCfg.OracleConfig nca := downloadNodePubKeys(nodeList, chainID, pubKeysPath) - onchainPubKeys := []common.Address{} + onchainPubKeys := [][]byte{} + allPubKeys := map[string]any{} for _, n := range nca { - onchainPubKeys = append(onchainPubKeys, common.HexToAddress(n.OCR2OnchainPublicKey)) + ethPubKey := common.HexToAddress(n.OCR2OnchainPublicKey) + pubKeys := map[string]types.OnchainPublicKey{ + "evm": ethPubKey[:], + } + // validate uniqueness of each individual key + for _, key := range pubKeys { + raw := hex.EncodeToString(key) + _, exists := allPubKeys[raw] + if exists { + panic(fmt.Sprintf("Duplicate onchain public key: %v", raw)) + } + allPubKeys[raw] = struct{}{} + } + pubKey, err := ocrcommon.MarshalMultichainPublicKey(pubKeys) + if err != nil { + panic(err) + } + onchainPubKeys = append(onchainPubKeys, pubKey) } offchainPubKeysBytes := []types.OffchainPublicKey{} @@ -170,13 +190,16 @@ func generateOCR3Config(nodeList string, configFile string, chainID int64, pubKe ) helpers.PanicErr(err) - signerAddresses, err := evm.OnchainPublicKeyToAddress(signers) - PanicErr(err) + var configSigners [][]byte + for _, signer := range signers { + configSigners = append(configSigners, signer) + } + transmitterAddresses, err := evm.AccountToAddress(transmitters) PanicErr(err) config := orc2drOracleConfig{ - Signers: signerAddresses, + Signers: configSigners, Transmitters: transmitterAddresses, F: f, OnchainConfig: onchainConfig, diff --git a/core/scripts/keystone/src/__snapshots__/88_gen_jobspecs_test.snap b/core/scripts/keystone/src/__snapshots__/88_gen_jobspecs_test.snap index a4b4e6e3021..b21c1549db4 100755 --- a/core/scripts/keystone/src/__snapshots__/88_gen_jobspecs_test.snap +++ b/core/scripts/keystone/src/__snapshots__/88_gen_jobspecs_test.snap @@ -10,6 +10,7 @@ relay = "evm" [relayConfig] chainID = "11155111" +providerType = "ocr3-capability" Oracles: Oracle 0: @@ -37,8 +38,9 @@ providerType = "ocr3-capability" telemetryType = "plugin" [onchainSigningStrategy] -strategyName = 'single-chain' +strategyName = 'multi-chain' [onchainSigningStrategy.config] +evm = "b3df4d8748b67731a1112e8b45a764941974f5590c93672eebbc4f3504dd10ed" -------------------------------- Oracle 1: @@ -66,8 +68,9 @@ providerType = "ocr3-capability" telemetryType = "plugin" [onchainSigningStrategy] -strategyName = 'single-chain' +strategyName = 'multi-chain' [onchainSigningStrategy.config] +evm = "38459ae37f29f2c1fde0f25972a973322be8cada82acf43f464756836725be97" -------------------------------- Oracle 2: @@ -95,8 +98,9 @@ providerType = "ocr3-capability" telemetryType = "plugin" [onchainSigningStrategy] -strategyName = 'single-chain' +strategyName = 'multi-chain' [onchainSigningStrategy.config] +evm = "b5dbc4c9da983cddde2e3226b85807eb7beaf818694a22576af4d80f352702ed" -------------------------------- Oracle 3: @@ -124,8 +128,9 @@ providerType = "ocr3-capability" telemetryType = "plugin" [onchainSigningStrategy] -strategyName = 'single-chain' +strategyName = 'multi-chain' [onchainSigningStrategy.config] +evm = "260d5c1a618cdf5324509d7db95f5a117511864ebb9e1f709e8969339eb225af" --- diff --git a/core/scripts/keystone/src/__snapshots__/88_gen_ocr3_config_test.snap b/core/scripts/keystone/src/__snapshots__/88_gen_ocr3_config_test.snap index a2ecd236f74..b9581afb880 100755 --- a/core/scripts/keystone/src/__snapshots__/88_gen_ocr3_config_test.snap +++ b/core/scripts/keystone/src/__snapshots__/88_gen_ocr3_config_test.snap @@ -6,11 +6,11 @@ "OffchainConfigVersion": 30, "OnchainConfig": "0x", "Signers": [ - "0xA2402db8E549f094EA31e1C0EDd77623F4cA5b12", - "0x4af19c802B244D1d085492C3946391C965E10519", - "0x61925685d2B80b121537341d063c4E57B2F9323c", - "0xFd97eFD53FC20acc098Fcd746C04d8d7540D97E0", - "0xA0b67Dc5345a71D02B396147AE2cb75dDA63CbE9" + "011400a2402db8e549f094ea31e1c0edd77623f4ca5b12", + "0114004af19c802b244d1d085492c3946391c965e10519", + "01140061925685d2b80b121537341d063c4e57b2f9323c", + "011400fd97efd53fc20acc098fcd746c04d8d7540d97e0", + "011400a0b67dc5345a71d02b396147ae2cb75dda63cbe9" ], "Transmitters": [ "0xF4e7e516146c8567F8E8be0ED1f1A92798628d35", diff --git a/core/scripts/keystone/templates/bootstrap.toml b/core/scripts/keystone/templates/bootstrap.toml index 1baa43101b6..cdd9065caba 100644 --- a/core/scripts/keystone/templates/bootstrap.toml +++ b/core/scripts/keystone/templates/bootstrap.toml @@ -6,3 +6,4 @@ relay = "evm" [relayConfig] chainID = "{{ chain_id }}" +providerType = "ocr3-capability" diff --git a/core/scripts/keystone/templates/oracle.toml b/core/scripts/keystone/templates/oracle.toml index 6049ad925d4..c45e0fcb608 100644 --- a/core/scripts/keystone/templates/oracle.toml +++ b/core/scripts/keystone/templates/oracle.toml @@ -21,5 +21,6 @@ providerType = "ocr3-capability" telemetryType = "plugin" [onchainSigningStrategy] -strategyName = 'single-chain' +strategyName = 'multi-chain' [onchainSigningStrategy.config] +evm = "{{ ocr_key_bundle_id }}" diff --git a/core/services/feeds/service.go b/core/services/feeds/service.go index 87f0ae60f6b..d5c8c1ba22e 100644 --- a/core/services/feeds/service.go +++ b/core/services/feeds/service.go @@ -1210,7 +1210,7 @@ func (s *service) generateJob(ctx context.Context, spec string) (*job.Job, error case job.FluxMonitor: js, err = fluxmonitorv2.ValidatedFluxMonitorSpec(s.jobCfg, spec) case job.Workflow: - js, err = workflows.ValidatedWorkflowJobSpec(spec) + js, err = workflows.ValidatedWorkflowJobSpec(ctx, spec) case job.CCIP: js, err = ccip.ValidatedCCIPSpec(spec) default: diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go index b6af86df335..e65e7fa5e12 100644 --- a/core/services/job/job_orm_test.go +++ b/core/services/job/job_orm_test.go @@ -1861,6 +1861,7 @@ func Test_ORM_FindJobByWorkflow(t *testing.T) { spec: &job.WorkflowSpec{ ID: 1, Workflow: pkgworkflows.WFYamlSpec(t, "workflow01", addr1), + SpecType: job.YamlSpec, }, before: mustInsertWFJob, }, @@ -1881,6 +1882,7 @@ func Test_ORM_FindJobByWorkflow(t *testing.T) { var c job.WorkflowSpec c.ID = s.ID c.Workflow = pkgworkflows.WFYamlSpec(t, "workflow99", addr1) // insert with mismatched name + c.SpecType = job.YamlSpec return mustInsertWFJob(t, o, &c) }, }, @@ -1948,18 +1950,21 @@ func Test_ORM_FindJobByWorkflow_Multiple(t *testing.T) { wfYaml1 := pkgworkflows.WFYamlSpec(t, "workflow00", addr1) s1 := job.WorkflowSpec{ Workflow: wfYaml1, + SpecType: job.YamlSpec, } wantJobID1 := mustInsertWFJob(t, o, &s1) wfYaml2 := pkgworkflows.WFYamlSpec(t, "workflow01", addr1) s2 := job.WorkflowSpec{ Workflow: wfYaml2, + SpecType: job.YamlSpec, } wantJobID2 := mustInsertWFJob(t, o, &s2) wfYaml3 := pkgworkflows.WFYamlSpec(t, "workflow00", addr2) s3 := job.WorkflowSpec{ Workflow: wfYaml3, + SpecType: job.YamlSpec, } wantJobID3 := mustInsertWFJob(t, o, &s3) @@ -1976,13 +1981,14 @@ func Test_ORM_FindJobByWorkflow_Multiple(t *testing.T) { assert.EqualValues(t, j.WorkflowSpec.WorkflowID, s.WorkflowID) assert.EqualValues(t, j.WorkflowSpec.WorkflowOwner, s.WorkflowOwner) assert.EqualValues(t, j.WorkflowSpec.WorkflowName, s.WorkflowName) + assert.Equal(t, j.WorkflowSpec.SpecType, job.YamlSpec) } }) } func mustInsertWFJob(t *testing.T, orm job.ORM, s *job.WorkflowSpec) int32 { t.Helper() - err := s.Validate() + err := s.Validate(testutils.Context(t)) require.NoError(t, err, "failed to validate spec %v", s) ctx := testutils.Context(t) _, err = toml.Marshal(s.Workflow) diff --git a/core/services/job/models.go b/core/services/job/models.go index 1c46d08c59c..b9ec8e726f4 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -1,6 +1,7 @@ package job import ( + "context" "database/sql/driver" "encoding/json" "fmt" @@ -14,9 +15,12 @@ import ( "github.com/pkg/errors" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/workflows/sdk" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/types" pkgworkflows "github.com/smartcontractkit/chainlink-common/pkg/workflows" + "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/bridges" @@ -857,16 +861,26 @@ type LiquidityBalancerSpec struct { LiquidityBalancerConfig string `toml:"liquidityBalancerConfig" db:"liquidity_balancer_config"` } +type WorkflowSpecType string + +const ( + YamlSpec WorkflowSpecType = "yaml" + DefaultSpecType = YamlSpec +) + type WorkflowSpec struct { ID int32 `toml:"-"` - Workflow string `toml:"workflow"` // the yaml representation of the workflow + Workflow string `toml:"workflow"` // the raw representation of the workflow + Config string `toml:"config"` // the raw representation of the config // fields derived from the yaml spec, used for indexing the database // note: i tried to make these private, but translating them to the database seems to require them to be public - WorkflowID string `toml:"-" db:"workflow_id"` // Derived. Do not modify. the CID of the workflow. - WorkflowOwner string `toml:"-" db:"workflow_owner"` // Derived. Do not modify. the owner of the workflow. - WorkflowName string `toml:"-" db:"workflow_name"` // Derived. Do not modify. the name of the workflow. - CreatedAt time.Time `toml:"-"` - UpdatedAt time.Time `toml:"-"` + WorkflowID string `toml:"-" db:"workflow_id"` // Derived. Do not modify. the CID of the workflow. + WorkflowOwner string `toml:"-" db:"workflow_owner"` // Derived. Do not modify. the owner of the workflow. + WorkflowName string `toml:"-" db:"workflow_name"` // Derived. Do not modify. the name of the workflow. + CreatedAt time.Time `toml:"-"` + UpdatedAt time.Time `toml:"-"` + SpecType WorkflowSpecType `db:"spec_type"` + sdkWorkflow *sdk.WorkflowSpec } var ( @@ -879,14 +893,18 @@ const ( ) // Validate checks the workflow spec for correctness -func (w *WorkflowSpec) Validate() error { +func (w *WorkflowSpec) Validate(ctx context.Context) error { s, err := pkgworkflows.ParseWorkflowSpecYaml(w.Workflow) if err != nil { return fmt.Errorf("%w: failed to parse workflow spec %s: %w", ErrInvalidWorkflowYAMLSpec, w.Workflow, err) } + + if _, err = w.SDKSpec(ctx); err != nil { + return err + } + w.WorkflowOwner = strings.TrimPrefix(s.Owner, "0x") // the json schema validation ensures it is a hex string with 0x prefix, but the database does not store the prefix w.WorkflowName = s.Name - w.WorkflowID = s.CID() if len(w.WorkflowID) != workflowIDLen { return fmt.Errorf("%w: incorrect length for id %s: expected %d, got %d", ErrInvalidWorkflowID, w.WorkflowID, workflowIDLen, len(w.WorkflowID)) @@ -895,6 +913,20 @@ func (w *WorkflowSpec) Validate() error { return nil } +func (w *WorkflowSpec) SDKSpec(ctx context.Context) (sdk.WorkflowSpec, error) { + if w.sdkWorkflow != nil { + return *w.sdkWorkflow, nil + } + + spec, cid, err := workflowSpecFactory.Spec(ctx, w.Workflow, []byte(w.Config), w.SpecType) + if err != nil { + return sdk.WorkflowSpec{}, err + } + w.sdkWorkflow = &spec + w.WorkflowID = cid + return spec, nil +} + type StandardCapabilitiesSpec struct { ID int32 CreatedAt time.Time `toml:"-"` diff --git a/core/services/job/models_test.go b/core/services/job/models_test.go index 1f88bb6d38f..49bd29c9959 100644 --- a/core/services/job/models_test.go +++ b/core/services/job/models_test.go @@ -11,6 +11,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/codec" "github.com/smartcontractkit/chainlink-common/pkg/types" pkgworkflows "github.com/smartcontractkit/chainlink-common/pkg/workflows" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/stretchr/testify/assert" @@ -322,7 +324,7 @@ func TestWorkflowSpec_Validate(t *testing.T) { w := &WorkflowSpec{ Workflow: tt.fields.Workflow, } - err := w.Validate() + err := w.Validate(testutils.Context(t)) require.Equal(t, tt.wantError, err != nil) if !tt.wantError { assert.NotEmpty(t, w.WorkflowID) diff --git a/core/services/job/orm.go b/core/services/job/orm.go index 5e7328b87d7..d02e0b29200 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -409,8 +409,8 @@ func (o *orm) CreateJob(ctx context.Context, jb *Job) error { case Stream: // 'stream' type has no associated spec, nothing to do here case Workflow: - sql := `INSERT INTO workflow_specs (workflow, workflow_id, workflow_owner, workflow_name, created_at, updated_at) - VALUES (:workflow, :workflow_id, :workflow_owner, :workflow_name, NOW(), NOW()) + sql := `INSERT INTO workflow_specs (workflow, workflow_id, workflow_owner, workflow_name, created_at, updated_at, spec_type) + VALUES (:workflow, :workflow_id, :workflow_owner, :workflow_name, NOW(), NOW(), :spec_type) RETURNING id;` specID, err := tx.prepareQuerySpecID(ctx, sql, jb.WorkflowSpec) if err != nil { @@ -961,7 +961,7 @@ func (o *orm) FindJob(ctx context.Context, id int32) (jb Job, err error) { return } -// FindJobWithoutSpecErrors returns a job by ID, without loading Spec Errors preloaded +// FindJobWithoutSpecErrors returns a job by ID, without loading SpecVal Errors preloaded func (o *orm) FindJobWithoutSpecErrors(ctx context.Context, id int32) (jb Job, err error) { err = o.transact(ctx, true, func(tx *orm) error { stmt := "SELECT jobs.*, job_pipeline_specs.pipeline_spec_id as pipeline_spec_id FROM jobs JOIN job_pipeline_specs ON (jobs.id = job_pipeline_specs.job_id) WHERE jobs.id = $1 LIMIT 1" diff --git a/core/services/job/validate_test.go b/core/services/job/validate_test.go old mode 100644 new mode 100755 diff --git a/core/services/job/workflow_spec_factory.go b/core/services/job/workflow_spec_factory.go new file mode 100644 index 00000000000..565e6a9fce1 --- /dev/null +++ b/core/services/job/workflow_spec_factory.go @@ -0,0 +1,51 @@ +package job + +import ( + "context" + "crypto/sha256" + "errors" + "fmt" + + "github.com/smartcontractkit/chainlink-common/pkg/workflows/sdk" +) + +var ErrInvalidWorkflowType = errors.New("invalid workflow type") + +type SDKWorkflowSpecFactory interface { + Spec(ctx context.Context, rawSpec, config []byte) (sdk.WorkflowSpec, error) + RawSpec(ctx context.Context, wf string) ([]byte, error) +} + +type WorkflowSpecFactory map[WorkflowSpecType]SDKWorkflowSpecFactory + +func (wsf WorkflowSpecFactory) Spec( + ctx context.Context, workflow string, config []byte, tpe WorkflowSpecType) (sdk.WorkflowSpec, string, error) { + if tpe == "" { + tpe = DefaultSpecType + } + + factory, ok := wsf[tpe] + if !ok { + return sdk.WorkflowSpec{}, "", ErrInvalidWorkflowType + } + + rawSpec, err := factory.RawSpec(ctx, workflow) + if err != nil { + return sdk.WorkflowSpec{}, "", err + } + + spec, err := factory.Spec(ctx, rawSpec, config) + if err != nil { + return sdk.WorkflowSpec{}, "", err + } + + sum := sha256.New() + sum.Write(rawSpec) + sum.Write(config) + + return spec, fmt.Sprintf("%x", sum.Sum(nil)), nil +} + +var workflowSpecFactory = WorkflowSpecFactory{ + YamlSpec: YAMLSpecFactory{}, +} diff --git a/core/services/job/workflow_spec_factory_test.go b/core/services/job/workflow_spec_factory_test.go new file mode 100644 index 00000000000..dc2ff3bac9b --- /dev/null +++ b/core/services/job/workflow_spec_factory_test.go @@ -0,0 +1,106 @@ +package job_test + +import ( + "context" + "crypto/sha256" + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/workflows/sdk" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/services/job" +) + +func TestWorkflowSpecFactory_ToSpec(t *testing.T) { + t.Parallel() + + anyData := "any data" + anyConfig := []byte("any config") + anySpec := sdk.WorkflowSpec{Name: "name", Owner: "owner"} + + t.Run("delegates to factory and calculates CID", func(t *testing.T) { + runYamlSpecTest(t, anySpec, anyData, anyConfig, job.YamlSpec) + }) + + t.Run("delegates default", func(t *testing.T) { + runYamlSpecTest(t, anySpec, anyData, anyConfig, "") + }) + + t.Run("CID without config matches", func(t *testing.T) { + factory := job.WorkflowSpecFactory{ + job.YamlSpec: mockSdkSpecFactory{t: t, noConfig: true, SpecVal: anySpec}, + } + results, cid, err := factory.Spec(testutils.Context(t), anyData, nil, job.YamlSpec) + require.NoError(t, err) + + assert.Equal(t, anySpec, results) + + sha256Hash := sha256.New() + sha256Hash.Write([]byte(anyData)) + expectedCid := fmt.Sprintf("%x", sha256Hash.Sum(nil)) + assert.Equal(t, expectedCid, cid) + }) + + t.Run("returns errors from sdk factory", func(t *testing.T) { + anyErr := errors.New("nope") + factory := job.WorkflowSpecFactory{ + job.YamlSpec: mockSdkSpecFactory{t: t, Err: anyErr}, + } + + _, _, err := factory.Spec(testutils.Context(t), anyData, anyConfig, job.YamlSpec) + assert.Equal(t, anyErr, err) + }) + + t.Run("returns an error if the type is not supported", func(t *testing.T) { + factory := job.WorkflowSpecFactory{ + job.YamlSpec: mockSdkSpecFactory{t: t, SpecVal: anySpec}, + } + + _, _, err := factory.Spec(testutils.Context(t), anyData, anyConfig, "unsupported") + assert.Error(t, err) + }) +} + +func runYamlSpecTest(t *testing.T, anySpec sdk.WorkflowSpec, anyData string, anyConfig []byte, specType job.WorkflowSpecType) { + factory := job.WorkflowSpecFactory{ + job.YamlSpec: mockSdkSpecFactory{t: t, SpecVal: anySpec}, + } + + results, cid, err := factory.Spec(testutils.Context(t), anyData, anyConfig, specType) + + require.NoError(t, err) + assert.Equal(t, anySpec, results) + + sha256Hash := sha256.New() + sha256Hash.Write([]byte(anyData)) + sha256Hash.Write(anyConfig) + expectedCid := fmt.Sprintf("%x", sha256Hash.Sum(nil)) + assert.Equal(t, expectedCid, cid) +} + +type mockSdkSpecFactory struct { + t *testing.T + noConfig bool + SpecVal sdk.WorkflowSpec + Err error +} + +func (f mockSdkSpecFactory) RawSpec(_ context.Context, wf string) ([]byte, error) { + return []byte(wf), nil +} + +func (f mockSdkSpecFactory) Spec(_ context.Context, rawSpec, config []byte) (sdk.WorkflowSpec, error) { + assert.ElementsMatch(f.t, rawSpec, []byte("any data")) + if f.noConfig { + assert.Nil(f.t, config) + } else { + assert.ElementsMatch(f.t, config, []byte("any config")) + } + + return f.SpecVal, f.Err +} diff --git a/core/services/job/yaml_spec_factory.go b/core/services/job/yaml_spec_factory.go new file mode 100644 index 00000000000..ea344a3ffc4 --- /dev/null +++ b/core/services/job/yaml_spec_factory.go @@ -0,0 +1,20 @@ +package job + +import ( + "context" + + "github.com/smartcontractkit/chainlink-common/pkg/workflows" + "github.com/smartcontractkit/chainlink-common/pkg/workflows/sdk" +) + +type YAMLSpecFactory struct{} + +var _ SDKWorkflowSpecFactory = (*YAMLSpecFactory)(nil) + +func (y YAMLSpecFactory) Spec(_ context.Context, rawSpec, _ []byte) (sdk.WorkflowSpec, error) { + return workflows.ParseWorkflowSpecYaml(string(rawSpec)) +} + +func (y YAMLSpecFactory) RawSpec(_ context.Context, wf string) ([]byte, error) { + return []byte(wf), nil +} diff --git a/core/services/job/yaml_spec_factory_test.go b/core/services/job/yaml_spec_factory_test.go new file mode 100644 index 00000000000..4d075fe6e20 --- /dev/null +++ b/core/services/job/yaml_spec_factory_test.go @@ -0,0 +1,82 @@ +package job_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + commonworkflows "github.com/smartcontractkit/chainlink-common/pkg/workflows" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/services/job" +) + +const anyYamlSpec = ` +name: "wf-name" +owner: "0x00000000000000000000000000000000000000aa" +triggers: + - id: "mercury-trigger@1.0.0" + config: + feedIds: + - "0x1111111111111111111100000000000000000000000000000000000000000000" + - "0x2222222222222222222200000000000000000000000000000000000000000000" + - "0x3333333333333333333300000000000000000000000000000000000000000000" + +consensus: + - id: "offchain_reporting@2.0.0" + ref: "evm_median" + inputs: + observations: + - "$(trigger.outputs)" + config: + aggregation_method: "data_feeds_2_0" + aggregation_config: + "0x1111111111111111111100000000000000000000000000000000000000000000": + deviation: "0.001" + heartbeat: 3600 + "0x2222222222222222222200000000000000000000000000000000000000000000": + deviation: "0.001" + heartbeat: 3600 + "0x3333333333333333333300000000000000000000000000000000000000000000": + deviation: "0.001" + heartbeat: 3600 + encoder: "EVM" + encoder_config: + abi: "mercury_reports bytes[]" + +targets: + - id: "write_polygon-testnet-mumbai@3.0.0" + inputs: + report: "$(evm_median.outputs.report)" + config: + address: "0x3F3554832c636721F1fD1822Ccca0354576741Ef" + params: ["$(report)"] + abi: "receive(report bytes)" + - id: "write_ethereum-testnet-sepolia@4.0.0" + inputs: + report: "$(evm_median.outputs.report)" + config: + address: "0x54e220867af6683aE6DcBF535B4f952cB5116510" + params: ["$(report)"] + abi: "receive(report bytes)" +` + +func TestYamlSpecFactory_GetSpec(t *testing.T) { + t.Parallel() + + actual, err := job.YAMLSpecFactory{}.Spec(testutils.Context(t), []byte(anyYamlSpec), []byte{}) + require.NoError(t, err) + + expected, err := commonworkflows.ParseWorkflowSpecYaml(anyYamlSpec) + require.NoError(t, err) + + require.Equal(t, expected, actual) +} + +func TestYamlSpecFactory_GetRawSpec(t *testing.T) { + t.Parallel() + + actual, err := job.YAMLSpecFactory{}.RawSpec(testutils.Context(t), anyYamlSpec) + require.NoError(t, err) + require.Equal(t, []byte(anyYamlSpec), actual) +} diff --git a/core/services/keystore/chaintype/chaintype.go b/core/services/keystore/chaintype/chaintype.go index 8a12322ebfe..419dfa2d073 100644 --- a/core/services/keystore/chaintype/chaintype.go +++ b/core/services/keystore/chaintype/chaintype.go @@ -36,6 +36,40 @@ func (c ChainTypes) String() (out string) { return sb.String() } +func NewChainType(typ uint8) (ChainType, error) { + switch typ { + case 1: + return EVM, nil + case 2: + return Solana, nil + case 3: + return Cosmos, nil + case 4: + return StarkNet, nil + case 5: + return Aptos, nil + default: + return "", fmt.Errorf("unexpected chaintype.ChainType: %#v", typ) + } +} + +func (c ChainType) Type() (uint8, error) { + switch c { + case EVM: + return 1, nil + case Solana: + return 2, nil + case Cosmos: + return 3, nil + case StarkNet: + return 4, nil + case Aptos: + return 5, nil + default: + return 0, fmt.Errorf("unexpected chaintype.ChainType: %#v", c) + } +} + // SupportedChainTypes contain all chains that are supported var SupportedChainTypes = ChainTypes{EVM, Cosmos, Solana, StarkNet, Aptos} diff --git a/core/services/llo/data_source.go b/core/services/llo/data_source.go index 3d265de2618..bad1bf0896a 100644 --- a/core/services/llo/data_source.go +++ b/core/services/llo/data_source.go @@ -71,10 +71,16 @@ var _ llo.DataSource = &dataSource{} type dataSource struct { lggr logger.Logger registry Registry + + t Telemeter +} + +func NewDataSource(lggr logger.Logger, registry Registry, t Telemeter) llo.DataSource { + return newDataSource(lggr, registry, t) } -func newDataSource(lggr logger.Logger, registry Registry) llo.DataSource { - return &dataSource{lggr.Named("DataSource"), registry} +func newDataSource(lggr logger.Logger, registry Registry, t Telemeter) *dataSource { + return &dataSource{lggr.Named("DataSource"), registry, t} } // Observe looks up all streams in the registry and populates a map of stream ID => value @@ -82,7 +88,7 @@ func (d *dataSource) Observe(ctx context.Context, streamValues llo.StreamValues, var wg sync.WaitGroup wg.Add(len(streamValues)) var svmu sync.Mutex - var errors []ErrObservationFailed + var errs []ErrObservationFailed var errmu sync.Mutex if opts.VerboseLogging() { @@ -91,7 +97,7 @@ func (d *dataSource) Observe(ctx context.Context, streamValues llo.StreamValues, streamIDs = append(streamIDs, streamID) } sort.Slice(streamIDs, func(i, j int) bool { return streamIDs[i] < streamIDs[j] }) - d.lggr.Debugw("Observing streams", "streamIDs", streamIDs, "seqNr", opts.SeqNr()) + d.lggr.Debugw("Observing streams", "streamIDs", streamIDs, "configDigest", opts.ConfigDigest(), "seqNr", opts.OutCtx().SeqNr) } for _, streamID := range maps.Keys(streamValues) { @@ -103,7 +109,7 @@ func (d *dataSource) Observe(ctx context.Context, streamValues llo.StreamValues, stream, exists := d.registry.Get(streamID) if !exists { errmu.Lock() - errors = append(errors, ErrObservationFailed{streamID: streamID, reason: fmt.Sprintf("missing stream: %d", streamID)}) + errs = append(errs, ErrObservationFailed{streamID: streamID, reason: fmt.Sprintf("missing stream: %d", streamID)}) errmu.Unlock() promMissingStreamCount.WithLabelValues(fmt.Sprintf("%d", streamID)).Inc() return @@ -111,19 +117,26 @@ func (d *dataSource) Observe(ctx context.Context, streamValues llo.StreamValues, run, trrs, err := stream.Run(ctx) if err != nil { errmu.Lock() - errors = append(errors, ErrObservationFailed{inner: err, run: run, streamID: streamID, reason: "pipeline run failed"}) + errs = append(errs, ErrObservationFailed{inner: err, run: run, streamID: streamID, reason: "pipeline run failed"}) errmu.Unlock() promObservationErrorCount.WithLabelValues(fmt.Sprintf("%d", streamID)).Inc() + // TODO: Consolidate/reduce telemetry. We should send all observation results in a single packet + // https://smartcontract-it.atlassian.net/browse/MERC-6290 + d.t.EnqueueV3PremiumLegacy(run, trrs, streamID, opts, nil, err) return } + // TODO: Consolidate/reduce telemetry. We should send all observation results in a single packet + // https://smartcontract-it.atlassian.net/browse/MERC-6290 val, err = ExtractStreamValue(trrs) if err != nil { errmu.Lock() - errors = append(errors, ErrObservationFailed{inner: err, run: run, streamID: streamID, reason: "failed to extract big.Int"}) + errs = append(errs, ErrObservationFailed{inner: err, run: run, streamID: streamID, reason: "failed to extract big.Int"}) errmu.Unlock() return } + d.t.EnqueueV3PremiumLegacy(run, trrs, streamID, opts, val, nil) + if val != nil { svmu.Lock() defer svmu.Unlock() @@ -136,15 +149,15 @@ func (d *dataSource) Observe(ctx context.Context, streamValues llo.StreamValues, // Failed observations are always logged at warn level var failedStreamIDs []streams.StreamID - if len(errors) > 0 { - sort.Slice(errors, func(i, j int) bool { return errors[i].streamID < errors[j].streamID }) - failedStreamIDs = make([]streams.StreamID, len(errors)) - errStrs := make([]string, len(errors)) - for i, e := range errors { + if len(errs) > 0 { + sort.Slice(errs, func(i, j int) bool { return errs[i].streamID < errs[j].streamID }) + failedStreamIDs = make([]streams.StreamID, len(errs)) + errStrs := make([]string, len(errs)) + for i, e := range errs { errStrs[i] = e.String() failedStreamIDs[i] = e.streamID } - d.lggr.Warnw("Observation failed for streams", "failedStreamIDs", failedStreamIDs, "errors", errStrs, "seqNr", opts.SeqNr()) + d.lggr.Warnw("Observation failed for streams", "failedStreamIDs", failedStreamIDs, "errs", errStrs, "configDigest", opts.ConfigDigest(), "seqNr", opts.OutCtx().SeqNr) } if opts.VerboseLogging() { @@ -153,7 +166,7 @@ func (d *dataSource) Observe(ctx context.Context, streamValues llo.StreamValues, successes = append(successes, strmID) } sort.Slice(successes, func(i, j int) bool { return successes[i] < successes[j] }) - d.lggr.Debugw("Observation complete", "successfulStreamIDs", successes, "failedStreamIDs", failedStreamIDs, "values", streamValues, "seqNr", opts.SeqNr()) + d.lggr.Debugw("Observation complete", "successfulStreamIDs", successes, "failedStreamIDs", failedStreamIDs, "configDigest", opts.ConfigDigest(), "values", streamValues, "seqNr", opts.OutCtx().SeqNr) } return nil diff --git a/core/services/llo/data_source_test.go b/core/services/llo/data_source_test.go index 59cd870bc91..932c4c0c73a 100644 --- a/core/services/llo/data_source_test.go +++ b/core/services/llo/data_source_test.go @@ -4,10 +4,15 @@ import ( "context" "errors" "math/big" + "sync" "testing" "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-data-streams/llo" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -35,8 +40,9 @@ func (m *mockRegistry) Get(streamID streams.StreamID) (strm streams.Stream, exis return } -func makeStreamWithSingleResult[T any](res T, err error) *mockStream { +func makeStreamWithSingleResult[T any](runID int64, res T, err error) *mockStream { return &mockStream{ + run: &pipeline.Run{ID: runID}, trrs: []pipeline.TaskRunResult{pipeline.TaskRunResult{Task: &pipeline.MemoTask{}, Result: pipeline.Result{Value: res}}}, err: err, } @@ -52,30 +58,59 @@ func makeStreamValues() llo.StreamValues { type mockOpts struct{} -func (m mockOpts) VerboseLogging() bool { return true } -func (m mockOpts) SeqNr() uint64 { return 42 } +func (m *mockOpts) VerboseLogging() bool { return true } +func (m *mockOpts) SeqNr() uint64 { return 1042 } +func (m *mockOpts) OutCtx() ocr3types.OutcomeContext { + return ocr3types.OutcomeContext{SeqNr: 1042, PreviousOutcome: ocr3types.Outcome([]byte("foo"))} +} +func (m *mockOpts) ConfigDigest() ocr2types.ConfigDigest { + return ocr2types.ConfigDigest{6, 5, 4} +} + +type mockTelemeter struct { + mu sync.Mutex + v3PremiumLegacyPackets []v3PremiumLegacyPacket +} + +type v3PremiumLegacyPacket struct { + run *pipeline.Run + trrs pipeline.TaskRunResults + streamID uint32 + opts llo.DSOpts + val llo.StreamValue + err error +} + +var _ Telemeter = &mockTelemeter{} + +func (m *mockTelemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts llo.DSOpts, val llo.StreamValue, err error) { + m.mu.Lock() + defer m.mu.Unlock() + m.v3PremiumLegacyPackets = append(m.v3PremiumLegacyPackets, v3PremiumLegacyPacket{run, trrs, streamID, opts, val, err}) +} func Test_DataSource(t *testing.T) { lggr := logger.TestLogger(t) reg := &mockRegistry{make(map[streams.StreamID]*mockStream)} - ds := newDataSource(lggr, reg) + ds := newDataSource(lggr, reg, NullTelemeter) ctx := testutils.Context(t) + opts := &mockOpts{} t.Run("Observe", func(t *testing.T) { t.Run("doesn't set any values if no streams are defined", func(t *testing.T) { vals := makeStreamValues() - err := ds.Observe(ctx, vals, mockOpts{}) + err := ds.Observe(ctx, vals, opts) assert.NoError(t, err) assert.Equal(t, makeStreamValues(), vals) }) t.Run("observes each stream with success and returns values matching map argument", func(t *testing.T) { - reg.streams[1] = makeStreamWithSingleResult[*big.Int](big.NewInt(2181), nil) - reg.streams[2] = makeStreamWithSingleResult[*big.Int](big.NewInt(40602), nil) - reg.streams[3] = makeStreamWithSingleResult[*big.Int](big.NewInt(15), nil) + reg.streams[1] = makeStreamWithSingleResult[*big.Int](1, big.NewInt(2181), nil) + reg.streams[2] = makeStreamWithSingleResult[*big.Int](2, big.NewInt(40602), nil) + reg.streams[3] = makeStreamWithSingleResult[*big.Int](3, big.NewInt(15), nil) vals := makeStreamValues() - err := ds.Observe(ctx, vals, mockOpts{}) + err := ds.Observe(ctx, vals, opts) assert.NoError(t, err) assert.Equal(t, llo.StreamValues{ @@ -85,12 +120,12 @@ func Test_DataSource(t *testing.T) { }, vals) }) t.Run("observes each stream and returns success/errors", func(t *testing.T) { - reg.streams[1] = makeStreamWithSingleResult[*big.Int](big.NewInt(2181), errors.New("something exploded")) - reg.streams[2] = makeStreamWithSingleResult[*big.Int](big.NewInt(40602), nil) - reg.streams[3] = makeStreamWithSingleResult[*big.Int](nil, errors.New("something exploded 2")) + reg.streams[1] = makeStreamWithSingleResult[*big.Int](1, big.NewInt(2181), errors.New("something exploded")) + reg.streams[2] = makeStreamWithSingleResult[*big.Int](2, big.NewInt(40602), nil) + reg.streams[3] = makeStreamWithSingleResult[*big.Int](3, nil, errors.New("something exploded 2")) vals := makeStreamValues() - err := ds.Observe(ctx, vals, mockOpts{}) + err := ds.Observe(ctx, vals, opts) assert.NoError(t, err) assert.Equal(t, llo.StreamValues{ @@ -99,5 +134,37 @@ func Test_DataSource(t *testing.T) { 3: nil, }, vals) }) + + t.Run("records telemetry", func(t *testing.T) { + tm := &mockTelemeter{} + ds.t = tm + + reg.streams[1] = makeStreamWithSingleResult[*big.Int](100, big.NewInt(2181), nil) + reg.streams[2] = makeStreamWithSingleResult[*big.Int](101, big.NewInt(40602), nil) + reg.streams[3] = makeStreamWithSingleResult[*big.Int](102, big.NewInt(15), nil) + + vals := makeStreamValues() + err := ds.Observe(ctx, vals, opts) + assert.NoError(t, err) + + assert.Equal(t, llo.StreamValues{ + 2: llo.ToDecimal(decimal.NewFromInt(40602)), + 1: llo.ToDecimal(decimal.NewFromInt(2181)), + 3: llo.ToDecimal(decimal.NewFromInt(15)), + }, vals) + + require.Len(t, tm.v3PremiumLegacyPackets, 3) + m := make(map[int]v3PremiumLegacyPacket) + for _, pkt := range tm.v3PremiumLegacyPackets { + m[int(pkt.run.ID)] = pkt + } + pkt := m[100] + assert.Equal(t, 100, int(pkt.run.ID)) + assert.Len(t, pkt.trrs, 1) + assert.Equal(t, 1, int(pkt.streamID)) + assert.Equal(t, opts, pkt.opts) + assert.Equal(t, "2181", pkt.val.(*llo.Decimal).String()) + assert.Nil(t, pkt.err) + }) }) } diff --git a/core/services/llo/delegate.go b/core/services/llo/delegate.go index aff670e807d..ab55c6b2a95 100644 --- a/core/services/llo/delegate.go +++ b/core/services/llo/delegate.go @@ -37,16 +37,18 @@ type delegate struct { prrc llo.PredecessorRetirementReportCache src llo.ShouldRetireCache ds llo.DataSource + t services.Service oracle Closer } type DelegateConfig struct { - Logger logger.Logger - DataSource sqlutil.DataSource - Runner streams.Runner - Registry Registry - JobName null.String + Logger logger.Logger + DataSource sqlutil.DataSource + Runner streams.Runner + Registry Registry + JobName null.String + CaptureEATelemetry bool // LLO ChannelDefinitionCache llotypes.ChannelDefinitionCache @@ -67,6 +69,7 @@ type DelegateConfig struct { } func NewDelegate(cfg DelegateConfig) (job.ServiceCtx, error) { + lggr := cfg.Logger.With("jobName", cfg.JobName.ValueOrZero()) if cfg.DataSource == nil { return nil, errors.New("DataSource must not be nil") } @@ -82,9 +85,15 @@ func NewDelegate(cfg DelegateConfig) (job.ServiceCtx, error) { // https://smartcontract-it.atlassian.net/browse/MERC-3386 prrc := llo.NewPredecessorRetirementReportCache() src := llo.NewShouldRetireCache() - ds := newDataSource(cfg.Logger.Named("DataSource"), cfg.Registry) + var t TelemeterService + if cfg.CaptureEATelemetry { + t = NewTelemeterService(lggr, cfg.MonitoringEndpoint) + } else { + t = NullTelemeter + } + ds := newDataSource(lggr.Named("DataSource"), cfg.Registry, t) - return &delegate{services.StateMachine{}, cfg, codecs, prrc, src, ds, nil}, nil + return &delegate{services.StateMachine{}, cfg, codecs, prrc, src, ds, t, nil}, nil } func (d *delegate) Start(ctx context.Context) error { diff --git a/core/services/llo/evm/report_codec_premium_legacy.go b/core/services/llo/evm/report_codec_premium_legacy.go index da14aceab69..f8d72619ea1 100644 --- a/core/services/llo/evm/report_codec_premium_legacy.go +++ b/core/services/llo/evm/report_codec_premium_legacy.go @@ -155,10 +155,15 @@ func ExtractReportValues(report llo.Report) (nativePrice, linkPrice *llo.Decimal // MERC-3524 var LLOExtraHash = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001") -func LegacyReportContext(cd ocr2types.ConfigDigest, seqNr uint64) ocr2types.ReportContext { +func SeqNrToEpochAndRound(seqNr uint64) (epoch uint32, round uint8) { // Simulate 256 rounds/epoch - epoch := seqNr / 256 - round := seqNr % 256 + epoch = uint32(seqNr / 256) // nolint + round = uint8(seqNr % 256) // nolint + return +} + +func LegacyReportContext(cd ocr2types.ConfigDigest, seqNr uint64) ocr2types.ReportContext { + epoch, round := SeqNrToEpochAndRound(seqNr) return ocr2types.ReportContext{ ReportTimestamp: ocr2types.ReportTimestamp{ ConfigDigest: cd, diff --git a/core/services/llo/telemetry.go b/core/services/llo/telemetry.go new file mode 100644 index 00000000000..62b586f5cc8 --- /dev/null +++ b/core/services/llo/telemetry.go @@ -0,0 +1,184 @@ +package llo + +import ( + "context" + "errors" + "fmt" + + "github.com/smartcontractkit/libocr/commontypes" + "google.golang.org/protobuf/proto" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-data-streams/llo" + + "github.com/smartcontractkit/chainlink/v2/core/services/llo/evm" + "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/eautils" + mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" + "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" +) + +const adapterLWBAErrorName = "AdapterLWBAError" + +type Telemeter interface { + EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts llo.DSOpts, val llo.StreamValue, err error) +} + +type TelemeterService interface { + Telemeter + services.Service +} + +func NewTelemeterService(lggr logger.Logger, monitoringEndpoint commontypes.MonitoringEndpoint) TelemeterService { + if monitoringEndpoint == nil { + return NullTelemeter + } + return newTelemeter(lggr, monitoringEndpoint) +} + +func newTelemeter(lggr logger.Logger, monitoringEndpoint commontypes.MonitoringEndpoint) *telemeter { + chTelemetryObservation := make(chan TelemetryObservation, 100) + t := &telemeter{ + chTelemetryObservation: chTelemetryObservation, + monitoringEndpoint: monitoringEndpoint, + } + t.Service, t.eng = services.Config{ + Name: "LLOTelemeterService", + Start: t.start, + }.NewServiceEngine(lggr) + + return t +} + +type telemeter struct { + services.Service + eng *services.Engine + + monitoringEndpoint commontypes.MonitoringEndpoint + chTelemetryObservation chan TelemetryObservation +} + +func (t *telemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts llo.DSOpts, val llo.StreamValue, err error) { + var adapterError *eautils.AdapterError + var dpInvariantViolationDetected bool + if errors.As(err, &adapterError) && adapterError.Name == adapterLWBAErrorName { + dpInvariantViolationDetected = true + } else if err != nil { + // ignore errors + return + } + tObs := TelemetryObservation{run, trrs, streamID, opts, val, dpInvariantViolationDetected} + select { + case t.chTelemetryObservation <- tObs: + default: + } +} + +func (t *telemeter) start(_ context.Context) error { + t.eng.Go(func(ctx context.Context) { + for { + select { + case tObs := <-t.chTelemetryObservation: + t.collectV3PremiumLegacyTelemetry(tObs) + case <-ctx.Done(): + return + } + } + }) + return nil +} + +func (t *telemeter) collectV3PremiumLegacyTelemetry(d TelemetryObservation) { + eaTelemetryValues := ocrcommon.ParseMercuryEATelemetry(t.eng.SugaredLogger, d.trrs, mercuryutils.REPORT_V3) + for _, eaTelem := range eaTelemetryValues { + var benchmarkPrice, bidPrice, askPrice int64 + var bp, bid, ask string + switch v := d.val.(type) { + case *llo.Decimal: + benchmarkPrice = v.Decimal().IntPart() + bp = v.Decimal().String() + case *llo.Quote: + benchmarkPrice = v.Benchmark.IntPart() + bp = v.Benchmark.String() + bidPrice = v.Bid.IntPart() + bid = v.Bid.String() + askPrice = v.Ask.IntPart() + ask = v.Ask.String() + } + epoch, round := evm.SeqNrToEpochAndRound(d.opts.OutCtx().SeqNr) + tea := &telem.EnhancedEAMercury{ + DataSource: eaTelem.DataSource, + DpBenchmarkPrice: eaTelem.DpBenchmarkPrice, + DpBid: eaTelem.DpBid, + DpAsk: eaTelem.DpAsk, + DpInvariantViolationDetected: d.dpInvariantViolationDetected, + BridgeTaskRunStartedTimestamp: eaTelem.BridgeTaskRunStartedTimestamp, + BridgeTaskRunEndedTimestamp: eaTelem.BridgeTaskRunEndedTimestamp, + ProviderRequestedTimestamp: eaTelem.ProviderRequestedTimestamp, + ProviderReceivedTimestamp: eaTelem.ProviderReceivedTimestamp, + ProviderDataStreamEstablished: eaTelem.ProviderDataStreamEstablished, + ProviderIndicatedTime: eaTelem.ProviderIndicatedTime, + Feed: fmt.Sprintf("streamID:%d", d.streamID), + ObservationBenchmarkPrice: benchmarkPrice, + ObservationBid: bidPrice, + ObservationAsk: askPrice, + ObservationBenchmarkPriceString: bp, + ObservationBidString: bid, + ObservationAskString: ask, + IsLinkFeed: false, + IsNativeFeed: false, + ConfigDigest: d.opts.ConfigDigest().Hex(), + Round: int64(round), + Epoch: int64(epoch), + AssetSymbol: eaTelem.AssetSymbol, + Version: uint32(1000 + mercuryutils.REPORT_V3), // add 1000 to distinguish between legacy feeds, this can be changed if necessary + } + + bytes, err := proto.Marshal(tea) + if err != nil { + t.eng.SugaredLogger.Warnf("protobuf marshal failed %v", err.Error()) + continue + } + + t.monitoringEndpoint.SendLog(bytes) + } +} + +type TelemetryObservation struct { + run *pipeline.Run + trrs pipeline.TaskRunResults + streamID uint32 + opts llo.DSOpts + val llo.StreamValue + dpInvariantViolationDetected bool +} + +var NullTelemeter TelemeterService = &nullTelemeter{} + +type nullTelemeter struct{} + +func (t *nullTelemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts llo.DSOpts, val llo.StreamValue, err error) { +} +func (t *nullTelemeter) Start(context.Context) error { + return nil +} +func (t *nullTelemeter) Close() error { + return nil +} +func (t *nullTelemeter) Healthy() error { + return nil +} +func (t *nullTelemeter) Unhealthy() error { + return nil +} +func (t *nullTelemeter) HealthReport() map[string]error { + return nil +} +func (t *nullTelemeter) Name() string { + return "NullTelemeter" +} +func (t *nullTelemeter) Ready() error { + return nil +} diff --git a/core/services/llo/telemetry_test.go b/core/services/llo/telemetry_test.go new file mode 100644 index 00000000000..ec77e959d24 --- /dev/null +++ b/core/services/llo/telemetry_test.go @@ -0,0 +1,216 @@ +package llo + +import ( + "errors" + "testing" + "time" + + "github.com/shopspring/decimal" + "github.com/smartcontractkit/libocr/commontypes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "gopkg.in/guregu/null.v4" + + "github.com/smartcontractkit/chainlink-data-streams/llo" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/eautils" + "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" +) + +var _ commontypes.MonitoringEndpoint = &mockMonitoringEndpoint{} + +type mockMonitoringEndpoint struct { + chLogs chan []byte +} + +func (m *mockMonitoringEndpoint) SendLog(log []byte) { + m.chLogs <- log +} + +const bridgeResponse = `{ + "meta":{ + "adapterName":"data-source-name" + }, + "timestamps":{ + "providerDataRequestedUnixMs":92233720368547760, + "providerDataReceivedUnixMs":-92233720368547760, + "providerDataStreamEstablishedUnixMs":1, + "providerIndicatedTimeUnixMs":-123456789 + } + }` + +var trrs = pipeline.TaskRunResults{ + pipeline.TaskRunResult{ + Task: &pipeline.BridgeTask{ + Name: "test-bridge-1", + BaseTask: pipeline.NewBaseTask(0, "ds1", nil, nil, 0), + RequestData: `{"data":{"from":"eth", "to":"usd"}}`, + }, + Result: pipeline.Result{ + Value: bridgeResponse, + }, + CreatedAt: time.Unix(0, 0), + FinishedAt: null.TimeFrom(time.Unix(0, 0)), + }, + pipeline.TaskRunResult{ + Task: &pipeline.JSONParseTask{ + BaseTask: pipeline.NewBaseTask(1, "ds1_parse", nil, nil, 1), + }, + Result: pipeline.Result{ + Value: "123456.123456789", + }, + }, + pipeline.TaskRunResult{ + Task: &pipeline.BridgeTask{ + Name: "test-bridge-2", + BaseTask: pipeline.NewBaseTask(0, "ds2", nil, nil, 0), + RequestData: `{"data":{"from":"eth", "to":"usd"}}`, + }, + Result: pipeline.Result{ + Value: bridgeResponse, + }, + CreatedAt: time.Unix(1, 0), + FinishedAt: null.TimeFrom(time.Unix(10, 0)), + }, + pipeline.TaskRunResult{ + Task: &pipeline.JSONParseTask{ + BaseTask: pipeline.NewBaseTask(1, "ds2_parse", nil, nil, 1), + }, + Result: pipeline.Result{ + Value: "12345678", + }, + }, + pipeline.TaskRunResult{ + Task: &pipeline.BridgeTask{ + Name: "test-bridge-3", + BaseTask: pipeline.NewBaseTask(0, "ds3", nil, nil, 0), + RequestData: `{"data":{"from":"eth", "to":"usd"}}`, + }, + Result: pipeline.Result{ + Value: bridgeResponse, + }, + CreatedAt: time.Unix(2, 0), + FinishedAt: null.TimeFrom(time.Unix(20, 0)), + }, + pipeline.TaskRunResult{ + Task: &pipeline.JSONParseTask{ + BaseTask: pipeline.NewBaseTask(1, "ds3_parse", nil, nil, 1), + }, + Result: pipeline.Result{ + Value: "1234567890", + }, + }, +} + +func Test_Telemeter(t *testing.T) { + lggr := logger.Test(t) + m := &mockMonitoringEndpoint{} + + run := &pipeline.Run{ID: 42} + streamID := uint32(135) + opts := &mockOpts{} + + t.Run("with error", func(t *testing.T) { + tm := newTelemeter(lggr, m) + servicetest.Run(t, tm) + + t.Run("if error is some random failure returns immediately", func(t *testing.T) { + // should return immediately and not even send on the channel + m.chLogs = nil + tm.EnqueueV3PremiumLegacy(run, trrs, streamID, opts, nil, errors.New("test error")) + }) + t.Run("if error is dp invariant violation, sets this flag", func(t *testing.T) { + m.chLogs = make(chan []byte, 100) + adapterError := new(eautils.AdapterError) + adapterError.Name = adapterLWBAErrorName + tm.EnqueueV3PremiumLegacy(run, trrs, streamID, opts, nil, adapterError) + + var i int + for log := range m.chLogs { + decoded := &telem.EnhancedEAMercury{} + require.NoError(t, proto.Unmarshal(log, decoded)) + assert.True(t, decoded.DpInvariantViolationDetected) + if i == 2 { + return + } + i++ + } + }) + }) + t.Run("with decimal value, sets all values correctly", func(t *testing.T) { + tm := newTelemeter(lggr, m) + val := llo.ToDecimal(decimal.NewFromFloat32(102.12)) + servicetest.Run(t, tm) + tm.EnqueueV3PremiumLegacy(run, trrs, streamID, opts, val, nil) + + var i int + for log := range m.chLogs { + decoded := &telem.EnhancedEAMercury{} + require.NoError(t, proto.Unmarshal(log, decoded)) + assert.Equal(t, int(1003), int(decoded.Version)) + assert.Equal(t, float64(123456.123456789), decoded.DpBenchmarkPrice) + assert.Zero(t, decoded.DpBid) + assert.Zero(t, decoded.DpAsk) + assert.False(t, decoded.DpInvariantViolationDetected) + assert.Zero(t, decoded.CurrentBlockNumber) + assert.Zero(t, decoded.CurrentBlockHash) + assert.Zero(t, decoded.CurrentBlockTimestamp) + assert.Zero(t, decoded.FetchMaxFinalizedTimestamp) + assert.Zero(t, decoded.MaxFinalizedTimestamp) + assert.Zero(t, decoded.ObservationTimestamp) + assert.False(t, decoded.IsLinkFeed) + assert.Zero(t, decoded.LinkPrice) + assert.False(t, decoded.IsNativeFeed) + assert.Zero(t, decoded.NativePrice) + assert.Equal(t, int64(i*1000), decoded.BridgeTaskRunStartedTimestamp) + assert.Equal(t, int64(i*10000), decoded.BridgeTaskRunEndedTimestamp) + assert.Equal(t, int64(92233720368547760), decoded.ProviderRequestedTimestamp) + assert.Equal(t, int64(-92233720368547760), decoded.ProviderReceivedTimestamp) + assert.Equal(t, int64(1), decoded.ProviderDataStreamEstablished) + assert.Equal(t, int64(-123456789), decoded.ProviderIndicatedTime) + assert.Equal(t, "streamID:135", decoded.Feed) + assert.Equal(t, int64(102), decoded.ObservationBenchmarkPrice) + assert.Equal(t, "102.12", decoded.ObservationBenchmarkPriceString) + assert.Zero(t, decoded.ObservationBid) + assert.Zero(t, decoded.ObservationBidString) + assert.Zero(t, decoded.ObservationAsk) + assert.Zero(t, decoded.ObservationAskString) + assert.Zero(t, decoded.ObservationMarketStatus) + assert.Equal(t, "0605040000000000000000000000000000000000000000000000000000000000", decoded.ConfigDigest) + assert.Equal(t, int64(18), decoded.Round) + assert.Equal(t, int64(4), decoded.Epoch) + assert.Equal(t, "eth/usd", decoded.AssetSymbol) + if i == 2 { + return + } + i++ + } + }) + t.Run("with quote value", func(t *testing.T) { + tm := newTelemeter(lggr, m) + val := &llo.Quote{Bid: decimal.NewFromFloat32(102.12), Benchmark: decimal.NewFromFloat32(103.32), Ask: decimal.NewFromFloat32(104.25)} + servicetest.Run(t, tm) + tm.EnqueueV3PremiumLegacy(run, trrs, streamID, opts, val, nil) + + var i int + for log := range m.chLogs { + decoded := &telem.EnhancedEAMercury{} + require.NoError(t, proto.Unmarshal(log, decoded)) + assert.Equal(t, int64(103), decoded.ObservationBenchmarkPrice) + assert.Equal(t, "103.32", decoded.ObservationBenchmarkPriceString) + assert.Equal(t, int64(102), decoded.ObservationBid) + assert.Equal(t, "102.12", decoded.ObservationBidString) + assert.Equal(t, int64(104), decoded.ObservationAsk) + assert.Equal(t, "104.25", decoded.ObservationAskString) + assert.Zero(t, decoded.ObservationMarketStatus) + if i == 2 { + return + } + i++ + } + }) +} diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 7bc3f473098..18f4d6224e7 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -783,7 +783,7 @@ func (d *Delegate) newServicesGenericPlugin( } keyBundles[name] = os } - onchainKeyringAdapter, err = ocrcommon.NewOCR3OnchainKeyringMultiChainAdapter(keyBundles, kb.PublicKey(), lggr) + onchainKeyringAdapter, err = ocrcommon.NewOCR3OnchainKeyringMultiChainAdapter(keyBundles, lggr) if err != nil { return nil, err } @@ -1015,7 +1015,8 @@ func (d *Delegate) newServicesLLO( Runner: d.pipelineRunner, Registry: d.streamRegistry, - JobName: jb.Name, + JobName: jb.Name, + CaptureEATelemetry: jb.OCR2OracleSpec.CaptureEATelemetry, ChannelDefinitionCache: provider.ChannelDefinitionCache(), @@ -1025,13 +1026,11 @@ func (d *Delegate) newServicesLLO( ContractConfigTracker: provider.ContractConfigTracker(), Database: ocrDB, LocalConfig: lc, - // TODO: Telemetry for llo - // https://smartcontract-it.atlassian.net/browse/MERC-3603 - MonitoringEndpoint: nil, - OffchainConfigDigester: provider.OffchainConfigDigester(), - OffchainKeyring: kb, - OnchainKeyring: kr, - OCRLogger: ocrLogger, + MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, fmt.Sprintf("%d", pluginCfg.DonID), synchronization.EnhancedEAMercury), + OffchainConfigDigester: provider.OffchainConfigDigester(), + OffchainKeyring: kb, + OnchainKeyring: kr, + OCRLogger: ocrLogger, // Enable verbose logging if either Mercury.VerboseLogging is on or OCR2.TraceLogging is on ReportingPluginConfig: datastreamsllo.Config{VerboseLogging: d.cfg.Mercury().VerboseLogging() || d.cfg.OCR2().TraceLogging()}, diff --git a/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go b/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go index 7b3351ce062..08020282c39 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go @@ -150,6 +150,7 @@ func NewExecOffchainConfig( RelativeBoostPerWaitHour float64, InflightCacheExpiry config.Duration, RootSnoozeTime config.Duration, + BatchingStrategyID uint32, ) ExecOffchainConfig { return ExecOffchainConfig{v1_2_0.JSONExecOffchainConfig{ DestOptimisticConfirmations: DestOptimisticConfirmations, @@ -157,6 +158,7 @@ func NewExecOffchainConfig( RelativeBoostPerWaitHour: RelativeBoostPerWaitHour, InflightCacheExpiry: InflightCacheExpiry, RootSnoozeTime: RootSnoozeTime, + BatchingStrategyID: BatchingStrategyID, }} } diff --git a/core/services/ocr2/plugins/ccip/testhelpers/config.go b/core/services/ocr2/plugins/ccip/testhelpers/config.go index c9d1ca5a126..4dcb627347f 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/config.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/config.go @@ -70,6 +70,7 @@ func (c *CCIPContracts) createExecOffchainConfig(t *testing.T, inflightCacheExpi 0.07, *config.MustNewDuration(inflightCacheExpiry), *config.MustNewDuration(rootSnoozeTime), + uint32(0), ).Encode() require.NoError(t, err) return config diff --git a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/ccip_contracts_1_4_0.go b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/ccip_contracts_1_4_0.go index b8db2dfff7f..9906a7b365a 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/ccip_contracts_1_4_0.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/ccip_contracts_1_4_0.go @@ -157,6 +157,7 @@ func NewExecOffchainConfig( RelativeBoostPerWaitHour float64, InflightCacheExpiry config.Duration, RootSnoozeTime config.Duration, + BatchingStrategyID uint32, ) ExecOffchainConfig { return ExecOffchainConfig{v1_2_0.JSONExecOffchainConfig{ DestOptimisticConfirmations: DestOptimisticConfirmations, @@ -164,6 +165,7 @@ func NewExecOffchainConfig( RelativeBoostPerWaitHour: RelativeBoostPerWaitHour, InflightCacheExpiry: InflightCacheExpiry, RootSnoozeTime: RootSnoozeTime, + BatchingStrategyID: BatchingStrategyID, }} } diff --git a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/config_1_4_0.go b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/config_1_4_0.go index 666ad79e59f..087c21e9333 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/config_1_4_0.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/config_1_4_0.go @@ -68,6 +68,7 @@ func (c *CCIPContracts) createExecOffchainConfig(t *testing.T, inflightCacheExpi 0.07, *config.MustNewDuration(inflightCacheExpiry), *config.MustNewDuration(rootSnoozeTime), + uint32(0), ).Encode() require.NoError(t, err) return config diff --git a/core/services/ocr2/plugins/llo/integration_test.go b/core/services/ocr2/plugins/llo/integration_test.go index 62c43eaf4da..2e49e989462 100644 --- a/core/services/ocr2/plugins/llo/integration_test.go +++ b/core/services/ocr2/plugins/llo/integration_test.go @@ -210,7 +210,6 @@ func TestIntegration_LLO(t *testing.T) { } serverURL := startMercuryServer(t, srv, clientPubKeys) - // TODO: all args? steve, backend, configurator, configuratorAddress, verifier, _, verifierProxy, _, configStore, configStoreAddress := setupBlockchain(t) fromBlock := 1 diff --git a/core/services/ocr2/plugins/mercury/plugin.go b/core/services/ocr2/plugins/mercury/plugin.go index 0898c1821ec..13793570dec 100644 --- a/core/services/ocr2/plugins/mercury/plugin.go +++ b/core/services/ocr2/plugins/mercury/plugin.go @@ -31,6 +31,7 @@ import ( mercuryv2 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v2" mercuryv3 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3" mercuryv4 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v4" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" "github.com/smartcontractkit/chainlink/v2/plugins" ) @@ -78,15 +79,28 @@ func NewServices( return nil, errors.New("expected job to have a non-nil PipelineSpec") } - var pluginConfig config.PluginConfig - err := json.Unmarshal(jb.OCR2OracleSpec.PluginConfig.Bytes(), &pluginConfig) + var relayConfig evmtypes.RelayConfig + err := json.Unmarshal(jb.OCR2OracleSpec.RelayConfig.Bytes(), &relayConfig) if err != nil { - return nil, errors.WithStack(err) + return nil, fmt.Errorf("error while unmarshalling relay config: %w", err) } - err = config.ValidatePluginConfig(pluginConfig, feedID) - if err != nil { - return nil, err + + var pluginConfig config.PluginConfig + if jb.OCR2OracleSpec.PluginConfig == nil { + if !relayConfig.EnableTriggerCapability { + return nil, fmt.Errorf("at least one transmission option must be configured") + } + } else { + err = json.Unmarshal(jb.OCR2OracleSpec.PluginConfig.Bytes(), &pluginConfig) + if err != nil { + return nil, errors.WithStack(err) + } + err = config.ValidatePluginConfig(pluginConfig, feedID) + if err != nil { + return nil, err + } } + lggr = lggr.Named("MercuryPlugin").With("jobID", jb.ID, "jobName", jb.Name.ValueOrZero()) // encapsulate all the subservices and ensure we close them all if any fail to start diff --git a/core/services/ocr2/validate/validate.go b/core/services/ocr2/validate/validate.go index a224249e1e8..09b974d4e30 100644 --- a/core/services/ocr2/validate/validate.go +++ b/core/services/ocr2/validate/validate.go @@ -26,6 +26,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/relay" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" "github.com/smartcontractkit/chainlink/v2/plugins" ) @@ -84,7 +85,6 @@ var ( "relay": {}, "relayConfig": {}, "pluginType": {}, - "pluginConfig": {}, } notExpectedParams = map[string]struct{}{ "isBootstrapPeer": {}, @@ -117,7 +117,7 @@ func validateSpec(ctx context.Context, tree *toml.Tree, spec job.Job, rc plugins // TODO validator for DR-OCR spec: https://smartcontract-it.atlassian.net/browse/FUN-112 return nil case types.Mercury: - return validateOCR2MercurySpec(spec.OCR2OracleSpec.PluginConfig, *spec.OCR2OracleSpec.FeedID) + return validateOCR2MercurySpec(spec.OCR2OracleSpec, *spec.OCR2OracleSpec.FeedID) case types.CCIPExecution: return validateOCR2CCIPExecutionSpec(spec.OCR2OracleSpec.PluginConfig) case types.CCIPCommit: @@ -297,13 +297,26 @@ func validateOCR2KeeperSpec(jsonConfig job.JSONConfig) error { return nil } -func validateOCR2MercurySpec(jsonConfig job.JSONConfig, feedId [32]byte) error { +func validateOCR2MercurySpec(spec *job.OCR2OracleSpec, feedID [32]byte) error { + var relayConfig evmtypes.RelayConfig + err := json.Unmarshal(spec.RelayConfig.Bytes(), &relayConfig) + if err != nil { + return pkgerrors.Wrap(err, "error while unmarshalling relay config") + } + + if spec.PluginConfig == nil { + if !relayConfig.EnableTriggerCapability { + return pkgerrors.Wrap(err, "at least one transmission option must be configured") + } + return nil + } + var pluginConfig mercuryconfig.PluginConfig - err := json.Unmarshal(jsonConfig.Bytes(), &pluginConfig) + err = json.Unmarshal(spec.PluginConfig.Bytes(), &pluginConfig) if err != nil { return pkgerrors.Wrap(err, "error while unmarshalling plugin config") } - return pkgerrors.Wrap(mercuryconfig.ValidatePluginConfig(pluginConfig, feedId), "Mercury PluginConfig is invalid") + return pkgerrors.Wrap(mercuryconfig.ValidatePluginConfig(pluginConfig, feedID), "Mercury PluginConfig is invalid") } func validateOCR2CCIPExecutionSpec(jsonConfig job.JSONConfig) error { diff --git a/core/services/ocrcommon/adapters.go b/core/services/ocrcommon/adapters.go index 53e62be9a07..b75a8a003c3 100644 --- a/core/services/ocrcommon/adapters.go +++ b/core/services/ocrcommon/adapters.go @@ -1,8 +1,14 @@ package ocrcommon import ( + "bytes" + "cmp" "context" + "encoding/binary" "fmt" + "io" + "math" + "slices" "github.com/pkg/errors" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" @@ -11,6 +17,7 @@ import ( "google.golang.org/protobuf/types/known/structpb" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocr2key" ) @@ -81,16 +88,96 @@ func (c *OCR3ContractTransmitterAdapter) FromAccount() (ocrtypes.Account, error) var _ ocr3types.OnchainKeyring[[]byte] = (*OCR3OnchainKeyringMultiChainAdapter)(nil) +func MarshalMultichainKeyBundle(ost map[string]ocr2key.KeyBundle) (ocrtypes.OnchainPublicKey, error) { + pubKeys := map[string]ocrtypes.OnchainPublicKey{} + for k, b := range ost { + pubKeys[k] = []byte(b.PublicKey()) + } + return MarshalMultichainPublicKey(pubKeys) +} + +func MarshalMultichainPublicKey(ost map[string]ocrtypes.OnchainPublicKey) (ocrtypes.OnchainPublicKey, error) { + var pubKeys [][]byte + for k, pubKey := range ost { + typ, err := chaintype.ChainType(k).Type() + if err != nil { + // skipping unknown key type + continue + } + buf := new(bytes.Buffer) + if err = binary.Write(buf, binary.LittleEndian, typ); err != nil { + return nil, err + } + length := len(pubKey) + if length < 0 || length > math.MaxUint16 { + return nil, fmt.Errorf("pubKey doesn't fit into uint16") + } + if err = binary.Write(buf, binary.LittleEndian, uint16(length)); err != nil { //nolint:gosec + return nil, err + } + _, _ = buf.Write(pubKey) + pubKeys = append(pubKeys, buf.Bytes()) + } + // sort keys based on encoded type to make encoding deterministic + slices.SortFunc(pubKeys, func(a, b []byte) int { return cmp.Compare(a[0], b[0]) }) + return bytes.Join(pubKeys, nil), nil +} + +func UnmarshalMultichainPublicKey(d []byte) (map[string]ocrtypes.OnchainPublicKey, error) { + m := map[string]ocrtypes.OnchainPublicKey{} + buf := bytes.NewReader(d) + + for { + // type + typ, err := buf.ReadByte() + if err != nil { + return nil, err + } + // length + var length uint16 + err = binary.Read(buf, binary.LittleEndian, &length) + if err != nil { + return nil, err + } + // value + pubKey := make([]byte, length) + n, err := buf.Read(pubKey) + if err != nil { + return nil, err + } + if n != int(length) { + return nil, io.EOF + } + + k, err := chaintype.NewChainType(typ) + if err != nil { + // skipping unknown key type + continue + } + m[string(k)] = pubKey + + if buf.Len() == 0 { + break + } + } + + return m, nil +} + type OCR3OnchainKeyringMultiChainAdapter struct { keyBundles map[string]ocr2key.KeyBundle publicKey ocrtypes.OnchainPublicKey lggr logger.Logger } -func NewOCR3OnchainKeyringMultiChainAdapter(ost map[string]ocr2key.KeyBundle, publicKey ocrtypes.OnchainPublicKey, lggr logger.Logger) (*OCR3OnchainKeyringMultiChainAdapter, error) { +func NewOCR3OnchainKeyringMultiChainAdapter(ost map[string]ocr2key.KeyBundle, lggr logger.Logger) (*OCR3OnchainKeyringMultiChainAdapter, error) { if len(ost) == 0 { return nil, errors.New("no key bundles provided") } + publicKey, err := MarshalMultichainKeyBundle(ost) + if err != nil { + return nil, err + } return &OCR3OnchainKeyringMultiChainAdapter{ost, publicKey, lggr}, nil } @@ -98,30 +185,30 @@ func (a *OCR3OnchainKeyringMultiChainAdapter) PublicKey() ocrtypes.OnchainPublic return a.publicKey } -func (a *OCR3OnchainKeyringMultiChainAdapter) getKeyBundleFromInfo(info []byte) (ocr2key.KeyBundle, error) { +func (a *OCR3OnchainKeyringMultiChainAdapter) getKeyBundleFromInfo(info []byte) (string, ocr2key.KeyBundle, error) { unmarshalledInfo := new(structpb.Struct) err := proto.Unmarshal(info, unmarshalledInfo) if err != nil { - return nil, fmt.Errorf("failed to unmarshal report info: %v", err) + return "", nil, fmt.Errorf("failed to unmarshal report info: %v", err) } infoMap := unmarshalledInfo.AsMap() keyBundleName, ok := infoMap["keyBundleName"] if !ok { - return nil, errors.New("keyBundleName not found in report info") + return "", nil, errors.New("keyBundleName not found in report info") } name, ok := keyBundleName.(string) if !ok { - return nil, errors.New("keyBundleName is not a string") + return "", nil, errors.New("keyBundleName is not a string") } kb, ok := a.keyBundles[name] if !ok { - return nil, fmt.Errorf("keyBundle not found: %s", name) + return "", nil, fmt.Errorf("keyBundle not found: %s", name) } - return kb, nil + return name, kb, nil } func (a *OCR3OnchainKeyringMultiChainAdapter) Sign(digest ocrtypes.ConfigDigest, seqNr uint64, r ocr3types.ReportWithInfo[[]byte]) (signature []byte, err error) { - kb, err := a.getKeyBundleFromInfo(r.Info) + _, kb, err := a.getKeyBundleFromInfo(r.Info) if err != nil { return nil, fmt.Errorf("sign: failed to get key bundle from report info: %v", err) } @@ -136,12 +223,22 @@ func (a *OCR3OnchainKeyringMultiChainAdapter) Sign(digest ocrtypes.ConfigDigest, } func (a *OCR3OnchainKeyringMultiChainAdapter) Verify(opk ocrtypes.OnchainPublicKey, digest ocrtypes.ConfigDigest, seqNr uint64, ri ocr3types.ReportWithInfo[[]byte], signature []byte) bool { - kb, err := a.getKeyBundleFromInfo(ri.Info) + kbName, kb, err := a.getKeyBundleFromInfo(ri.Info) if err != nil { a.lggr.Warnf("verify: failed to get key bundle from report info: %v", err) return false } - return kb.Verify(opk, ocrtypes.ReportContext{ + keys, err := UnmarshalMultichainPublicKey(opk) + if err != nil { + a.lggr.Warnf("verify: failed to unmarshal public keys: %v", err) + return false + } + publicKey, ok := keys[kbName] + if !ok { + a.lggr.Warnf("verify: publicKey not found: %v", kbName) + return false + } + return kb.Verify(publicKey, ocrtypes.ReportContext{ ReportTimestamp: ocrtypes.ReportTimestamp{ ConfigDigest: digest, Epoch: uint32(seqNr), diff --git a/core/services/ocrcommon/adapters_test.go b/core/services/ocrcommon/adapters_test.go index e7d45627299..fddfb297ecc 100644 --- a/core/services/ocrcommon/adapters_test.go +++ b/core/services/ocrcommon/adapters_test.go @@ -2,25 +2,23 @@ package ocrcommon_test import ( "context" - "encoding/json" "fmt" + "math" "reflect" "testing" - "github.com/pelletier/go-toml" "github.com/smartcontractkit/libocr/offchainreporting2/types" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/keystest" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocr2key" - keystoreMocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" ) @@ -115,63 +113,116 @@ func TestOCR3OnchainKeyringAdapter(t *testing.T) { require.Equal(t, maxSignatureLength, kr.MaxSignatureLength()) } -type envelope struct { - OnchainSigningStrategy *validate.OCR2OnchainSigningStrategy -} - func TestNewOCR3OnchainKeyringMultiChainAdapter(t *testing.T) { - payload := ` -[onchainSigningStrategy] -strategyName = "single-chain" -[onchainSigningStrategy.config] -evm = "08d14c6eed757414d72055d28de6caf06535806c6a14e450f3a2f1c854420e17" -publicKey = "pub-key" -` - oss := &envelope{} - tree, err := toml.Load(payload) - require.NoError(t, err) - o := map[string]any{} - err = tree.Unmarshal(&o) + evmBundle, err := ocr2key.New(chaintype.EVM) require.NoError(t, err) - b, err := json.Marshal(o) - require.NoError(t, err) - err = json.Unmarshal(b, oss) + + aptosBundle, err := ocr2key.New(chaintype.Aptos) require.NoError(t, err) - reportInfo := ocr3types.ReportWithInfo[[]byte]{ - Report: []byte("multi-chain-report"), + + bundles := map[string]ocr2key.KeyBundle{ + "evm": evmBundle, + "aptos": aptosBundle, } - info, err := structpb.NewStruct(map[string]interface{}{ + adapter, err := ocrcommon.NewOCR3OnchainKeyringMultiChainAdapter(bundles, logger.TestLogger(t)) + require.NoError(t, err) + + maxLength := math.Max(float64(evmBundle.MaxSignatureLength()), float64(aptosBundle.MaxSignatureLength())) + assert.Equal(t, int(maxLength), adapter.MaxSignatureLength()) + + // evm signature + info, err := structpb.NewStruct(map[string]any{ "keyBundleName": "evm", }) require.NoError(t, err) - infoB, err := proto.Marshal(info) + + infob, err := proto.Marshal(info) require.NoError(t, err) - reportInfo.Info = infoB + r := ocr3types.ReportWithInfo[[]byte]{ + Report: []byte("report"), + Info: infob, + } - ks := keystoreMocks.NewOCR2(t) - fakeKey := ocr2key.MustNewInsecure(keystest.NewRandReaderFromSeed(1), "evm") - pk := fakeKey.PublicKey() - ks.On("Get", "pub-key").Return(fakeKey, nil) - ks.On("Get", "08d14c6eed757414d72055d28de6caf06535806c6a14e450f3a2f1c854420e17").Return(fakeKey, nil) - keyBundles := map[string]ocr2key.KeyBundle{} - for name := range oss.OnchainSigningStrategy.ConfigCopy() { - kbID, ostErr := oss.OnchainSigningStrategy.KeyBundleID(name) - require.NoError(t, ostErr) - os, ostErr := ks.Get(kbID) - require.NoError(t, ostErr) - keyBundles[name] = os + sig, err := adapter.Sign(configDigest, seqNr, r) + require.NoError(t, err) + assert.True(t, adapter.Verify(adapter.PublicKey(), configDigest, seqNr, r, sig)) + + // aptos signature + info, err = structpb.NewStruct(map[string]any{ + "keyBundleName": "aptos", + }) + require.NoError(t, err) + + infob, err = proto.Marshal(info) + require.NoError(t, err) + r = ocr3types.ReportWithInfo[[]byte]{ + Report: []byte("report"), + Info: infob, } - adapter, err := ocrcommon.NewOCR3OnchainKeyringMultiChainAdapter(keyBundles, pk, logger.TestLogger(t)) + sig, err = adapter.Sign(configDigest, seqNr, r) require.NoError(t, err) - _, err = ocrcommon.NewOCR3OnchainKeyringMultiChainAdapter(map[string]ocr2key.KeyBundle{}, pk, logger.TestLogger(t)) + assert.True(t, adapter.Verify(adapter.PublicKey(), configDigest, seqNr, r, sig)) + + // no bundles + _, err = ocrcommon.NewOCR3OnchainKeyringMultiChainAdapter(map[string]ocr2key.KeyBundle{}, logger.TestLogger(t)) require.Error(t, err, "no key bundles provided") +} + +func newMultichainAdapter(t *testing.T) *ocrcommon.OCR3OnchainKeyringMultiChainAdapter { + evmBundle, err := ocr2key.New(chaintype.EVM) + require.NoError(t, err) + + aptosBundle, err := ocr2key.New(chaintype.Aptos) + require.NoError(t, err) + + bundles := map[string]ocr2key.KeyBundle{ + "evm": evmBundle, + "aptos": aptosBundle, + } + adapter, err := ocrcommon.NewOCR3OnchainKeyringMultiChainAdapter(bundles, logger.TestLogger(t)) + require.NoError(t, err) + + return adapter +} + +func TestNewOCR3OnchainKeyringMultiChainAdapter_VerifyFromDifferentNodesPublicKeys(t *testing.T) { + firstNodeAdapter := newMultichainAdapter(t) + secondNodeAdapter := newMultichainAdapter(t) - sig, err := adapter.Sign(configDigest, seqNr, reportInfo) - assert.NoError(t, err) - assert.True(t, adapter.Verify(pk, configDigest, seqNr, reportInfo, sig)) - assert.Equal(t, pk, adapter.PublicKey()) - assert.Equal(t, fakeKey.MaxSignatureLength(), adapter.MaxSignatureLength()) + // evm signature + info, err := structpb.NewStruct(map[string]any{ + "keyBundleName": "evm", + }) + require.NoError(t, err) + + infob, err := proto.Marshal(info) + require.NoError(t, err) + r := ocr3types.ReportWithInfo[[]byte]{ + Report: []byte("report"), + Info: infob, + } + + sig, err := firstNodeAdapter.Sign(configDigest, seqNr, r) + require.NoError(t, err) + assert.True(t, secondNodeAdapter.Verify(firstNodeAdapter.PublicKey(), configDigest, seqNr, r, sig)) + + // aptos signature + info, err = structpb.NewStruct(map[string]any{ + "keyBundleName": "aptos", + }) + require.NoError(t, err) + + infob, err = proto.Marshal(info) + require.NoError(t, err) + r = ocr3types.ReportWithInfo[[]byte]{ + Report: []byte("report"), + Info: infob, + } + + sig, err = secondNodeAdapter.Sign(configDigest, seqNr, r) + require.NoError(t, err) + assert.True(t, firstNodeAdapter.Verify(secondNodeAdapter.PublicKey(), configDigest, seqNr, r, sig)) } var _ ocrtypes.ContractTransmitter = (*fakeContractTransmitter)(nil) diff --git a/core/services/ocrcommon/telemetry.go b/core/services/ocrcommon/telemetry.go index 7db29aaacba..2be3e2228c4 100644 --- a/core/services/ocrcommon/telemetry.go +++ b/core/services/ocrcommon/telemetry.go @@ -11,13 +11,13 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "google.golang.org/protobuf/proto" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" v1types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" v2types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" v3types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" v4types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v4" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" @@ -25,12 +25,19 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) -type eaTelemetry struct { +type EATelemetry struct { DataSource string ProviderRequestedTimestamp int64 ProviderReceivedTimestamp int64 ProviderDataStreamEstablished int64 ProviderIndicatedTime int64 + + DpBenchmarkPrice float64 + DpBid float64 + DpAsk float64 + BridgeTaskRunStartedTimestamp int64 + BridgeTaskRunEndedTimestamp int64 + AssetSymbol string } type EnhancedTelemetryData struct { @@ -144,8 +151,37 @@ func (e *EnhancedTelemetryService[T]) getChainID() string { } } +func ParseMercuryEATelemetry(lggr logger.Logger, trrs pipeline.TaskRunResults, feedVersion mercuryutils.FeedVersion) (eaTelemetryValues []EATelemetry) { + for _, trr := range trrs { + if trr.Task.Type() != pipeline.TaskTypeBridge { + continue + } + bridgeTask := trr.Task.(*pipeline.BridgeTask) + bridgeName := bridgeTask.Name + + bridgeRawResponse, ok := trr.Result.Value.(string) + if !ok { + lggr.Warnw(fmt.Sprintf("cannot get bridge response from bridge task, id=%s, name=%q, expected string got %T", trr.Task.DotID(), bridgeName, trr.Result.Value), "dotID", trr.Task.DotID(), "bridgeName", bridgeName) + continue + } + eaTelem, err := parseEATelemetry([]byte(bridgeRawResponse)) + if err != nil { + lggr.Warnw(fmt.Sprintf("cannot parse EA telemetry, id=%s, name=%q", trr.Task.DotID(), bridgeName), "err", err, "dotID", trr.Task.DotID(), "bridgeName", bridgeName) + } + + eaTelem.DpBenchmarkPrice, eaTelem.DpBid, eaTelem.DpAsk = getPricesFromResults(lggr, trr, trrs, feedVersion) + + eaTelem.BridgeTaskRunStartedTimestamp = trr.CreatedAt.UnixMilli() + eaTelem.BridgeTaskRunEndedTimestamp = trr.FinishedAt.Time.UnixMilli() + eaTelem.AssetSymbol = getAssetSymbolFromRequestData(bridgeTask.RequestData) + + eaTelemetryValues = append(eaTelemetryValues, eaTelem) + } + return +} + // parseEATelemetry attempts to parse the bridge telemetry -func parseEATelemetry(b []byte) (eaTelemetry, error) { +func parseEATelemetry(b []byte) (EATelemetry, error) { type eaTimestamps struct { ProviderRequestedTimestamp int64 `json:"providerDataRequestedUnixMs"` ProviderReceivedTimestamp int64 `json:"providerDataReceivedUnixMs"` @@ -163,10 +199,10 @@ func parseEATelemetry(b []byte) (eaTelemetry, error) { t := eaTelem{} if err := json.Unmarshal(b, &t); err != nil { - return eaTelemetry{}, err + return EATelemetry{}, err } - return eaTelemetry{ + return EATelemetry{ DataSource: t.TelemMeta.AdapterName, ProviderRequestedTimestamp: t.TelemTimestamps.ProviderRequestedTimestamp, ProviderReceivedTimestamp: t.TelemTimestamps.ProviderReceivedTimestamp, @@ -378,40 +414,21 @@ func (e *EnhancedTelemetryService[T]) collectMercuryEnhancedTelemetry(d Enhanced } } - for _, trr := range d.TaskRunResults { - if trr.Task.Type() != pipeline.TaskTypeBridge { - continue - } - bridgeTask := trr.Task.(*pipeline.BridgeTask) - bridgeName := bridgeTask.Name - - bridgeRawResponse, ok := trr.Result.Value.(string) - if !ok { - e.lggr.Warnw(fmt.Sprintf("cannot get bridge response from bridge task, job=%d, id=%s, name=%q, expected string got %T", e.job.ID, trr.Task.DotID(), bridgeName, trr.Result.Value), "jobID", e.job.ID, "dotID", trr.Task.DotID(), "bridgeName", bridgeName) - continue - } - eaTelem, err := parseEATelemetry([]byte(bridgeRawResponse)) - if err != nil { - e.lggr.Warnw(fmt.Sprintf("cannot parse EA telemetry, job=%d, id=%s, name=%q", e.job.ID, trr.Task.DotID(), bridgeName), "err", err, "jobID", e.job.ID, "dotID", trr.Task.DotID(), "bridgeName", bridgeName) - } - - assetSymbol := e.getAssetSymbolFromRequestData(bridgeTask.RequestData) - - benchmarkPrice, bidPrice, askPrice := e.getPricesFromResults(trr, d.TaskRunResults, d.FeedVersion) - + eaTelemetryValues := ParseMercuryEATelemetry(logger.Sugared(e.lggr).With("jobID", e.job.ID), d.TaskRunResults, d.FeedVersion) + for _, eaTelem := range eaTelemetryValues { t := &telem.EnhancedEAMercury{ DataSource: eaTelem.DataSource, - DpBenchmarkPrice: benchmarkPrice, - DpBid: bidPrice, - DpAsk: askPrice, + DpBenchmarkPrice: eaTelem.DpBenchmarkPrice, + DpBid: eaTelem.DpBid, + DpAsk: eaTelem.DpAsk, DpInvariantViolationDetected: d.DpInvariantViolationDetected, CurrentBlockNumber: bn, CurrentBlockHash: bh, CurrentBlockTimestamp: bt, FetchMaxFinalizedTimestamp: d.FetchMaxFinalizedTimestamp, MaxFinalizedTimestamp: mfts, - BridgeTaskRunStartedTimestamp: trr.CreatedAt.UnixMilli(), - BridgeTaskRunEndedTimestamp: trr.FinishedAt.Time.UnixMilli(), + BridgeTaskRunStartedTimestamp: eaTelem.BridgeTaskRunStartedTimestamp, + BridgeTaskRunEndedTimestamp: eaTelem.BridgeTaskRunEndedTimestamp, ProviderRequestedTimestamp: eaTelem.ProviderRequestedTimestamp, ProviderReceivedTimestamp: eaTelem.ProviderReceivedTimestamp, ProviderDataStreamEstablished: eaTelem.ProviderDataStreamEstablished, @@ -431,7 +448,7 @@ func (e *EnhancedTelemetryService[T]) collectMercuryEnhancedTelemetry(d Enhanced ConfigDigest: d.RepTimestamp.ConfigDigest.Hex(), Round: int64(d.RepTimestamp.Round), Epoch: int64(d.RepTimestamp.Epoch), - AssetSymbol: assetSymbol, + AssetSymbol: eaTelem.AssetSymbol, Version: uint32(d.FeedVersion), } @@ -446,7 +463,7 @@ func (e *EnhancedTelemetryService[T]) collectMercuryEnhancedTelemetry(d Enhanced } // getAssetSymbolFromRequestData parses the requestData of the bridge to generate an asset symbol pair -func (e *EnhancedTelemetryService[T]) getAssetSymbolFromRequestData(requestData string) string { +func getAssetSymbolFromRequestData(requestData string) string { type reqDataPayload struct { To string `json:"to"` From string `json:"from"` @@ -474,22 +491,22 @@ func ShouldCollectEnhancedTelemetryMercury(jb job.Job) bool { // getPricesFromResults parses the pipeline.TaskRunResults for pipeline.TaskTypeJSONParse and gets the benchmarkPrice, // bid and ask. This functions expects the pipeline.TaskRunResults to be correctly ordered -func (e *EnhancedTelemetryService[T]) getPricesFromResults(startTask pipeline.TaskRunResult, allTasks pipeline.TaskRunResults, mercuryVersion mercuryutils.FeedVersion) (float64, float64, float64) { +func getPricesFromResults(lggr logger.Logger, startTask pipeline.TaskRunResult, allTasks pipeline.TaskRunResults, mercuryVersion mercuryutils.FeedVersion) (float64, float64, float64) { var benchmarkPrice, askPrice, bidPrice float64 var err error // We rely on task results to be sorted in the correct order benchmarkPriceTask := allTasks.GetNextTaskOf(startTask) if benchmarkPriceTask == nil { - e.lggr.Warnf("cannot parse enhanced EA telemetry benchmark price, task is nil, job %d", e.job.ID) + lggr.Warn("cannot parse enhanced EA telemetry benchmark price, task is nil") return 0, 0, 0 } if benchmarkPriceTask.Task.Type() == pipeline.TaskTypeJSONParse { if benchmarkPriceTask.Result.Error != nil { - e.lggr.Warnw(fmt.Sprintf("got error for enhanced EA telemetry benchmark price, job %d, id %s: %s", e.job.ID, benchmarkPriceTask.Task.DotID(), benchmarkPriceTask.Result.Error), "err", benchmarkPriceTask.Result.Error) + lggr.Warnw(fmt.Sprintf("got error for enhanced EA telemetry benchmark price, id %s: %s", benchmarkPriceTask.Task.DotID(), benchmarkPriceTask.Result.Error), "err", benchmarkPriceTask.Result.Error) } else { benchmarkPrice, err = getResultFloat64(benchmarkPriceTask) if err != nil { - e.lggr.Warnw(fmt.Sprintf("cannot parse enhanced EA telemetry benchmark price, job %d, id %s", e.job.ID, benchmarkPriceTask.Task.DotID()), "err", err) + lggr.Warnw(fmt.Sprintf("cannot parse enhanced EA telemetry benchmark price, id %s", benchmarkPriceTask.Task.DotID()), "err", err) } } } @@ -501,33 +518,33 @@ func (e *EnhancedTelemetryService[T]) getPricesFromResults(startTask pipeline.Ta bidTask := allTasks.GetNextTaskOf(*benchmarkPriceTask) if bidTask == nil { - e.lggr.Warnf("cannot parse enhanced EA telemetry bid price, task is nil, job %d, id %s", e.job.ID, benchmarkPriceTask.Task.DotID()) + lggr.Warnf("cannot parse enhanced EA telemetry bid price, task is nil, id %s", benchmarkPriceTask.Task.DotID()) return benchmarkPrice, 0, 0 } if bidTask != nil && bidTask.Task.Type() == pipeline.TaskTypeJSONParse { if bidTask.Result.Error != nil { - e.lggr.Warnw(fmt.Sprintf("got error for enhanced EA telemetry bid price, job %d, id %s: %s", e.job.ID, bidTask.Task.DotID(), bidTask.Result.Error), "err", bidTask.Result.Error) + lggr.Warnw(fmt.Sprintf("got error for enhanced EA telemetry bid price, id %s: %s", bidTask.Task.DotID(), bidTask.Result.Error), "err", bidTask.Result.Error) } else { bidPrice, err = getResultFloat64(bidTask) if err != nil { - e.lggr.Warnw(fmt.Sprintf("cannot parse enhanced EA telemetry bid price, job %d, id %s", e.job.ID, bidTask.Task.DotID()), "err", err) + lggr.Warnw(fmt.Sprintf("cannot parse enhanced EA telemetry bid price, id %s", bidTask.Task.DotID()), "err", err) } } } askTask := allTasks.GetNextTaskOf(*bidTask) if askTask == nil { - e.lggr.Warnf("cannot parse enhanced EA telemetry ask price, task is nil, job %d, id %s", e.job.ID, benchmarkPriceTask.Task.DotID()) + lggr.Warnf("cannot parse enhanced EA telemetry ask price, task is nil, id %s", benchmarkPriceTask.Task.DotID()) return benchmarkPrice, bidPrice, 0 } if askTask != nil && askTask.Task.Type() == pipeline.TaskTypeJSONParse { if bidTask.Result.Error != nil { - e.lggr.Warnw(fmt.Sprintf("got error for enhanced EA telemetry ask price, job %d, id %s: %s", e.job.ID, askTask.Task.DotID(), askTask.Result.Error), "err", askTask.Result.Error) + lggr.Warnw(fmt.Sprintf("got error for enhanced EA telemetry ask price, id %s: %s", askTask.Task.DotID(), askTask.Result.Error), "err", askTask.Result.Error) } else { askPrice, err = getResultFloat64(askTask) if err != nil { - e.lggr.Warnw(fmt.Sprintf("cannot parse enhanced EA telemetry ask price, job %d, id %s", e.job.ID, askTask.Task.DotID()), "err", err) + lggr.Warnw(fmt.Sprintf("cannot parse enhanced EA telemetry ask price, id %s", askTask.Task.DotID()), "err", err) } } } diff --git a/core/services/ocrcommon/telemetry_test.go b/core/services/ocrcommon/telemetry_test.go index f764e7380f8..e257554803c 100644 --- a/core/services/ocrcommon/telemetry_test.go +++ b/core/services/ocrcommon/telemetry_test.go @@ -447,19 +447,13 @@ var trrsMercuryV2 = pipeline.TaskRunResults{ func TestGetPricesFromResults(t *testing.T) { lggr, logs := logger.TestLoggerObserved(t, zap.WarnLevel) - e := EnhancedTelemetryService[EnhancedTelemetryMercuryData]{ - lggr: lggr, - job: &job.Job{ - ID: 0, - }, - } - benchmarkPrice, bid, ask := e.getPricesFromResults(trrsMercuryV1[0], trrsMercuryV1, 1) + benchmarkPrice, bid, ask := getPricesFromResults(lggr, trrsMercuryV1[0], trrsMercuryV1, 1) require.Equal(t, 123456.123456, benchmarkPrice) require.Equal(t, 1234567.1234567, bid) require.Equal(t, float64(321123), ask) - benchmarkPrice, bid, ask = e.getPricesFromResults(trrsMercuryV1[0], pipeline.TaskRunResults{}, 1) + benchmarkPrice, bid, ask = getPricesFromResults(lggr, trrsMercuryV1[0], pipeline.TaskRunResults{}, 1) require.Equal(t, float64(0), benchmarkPrice) require.Equal(t, float64(0), bid) require.Equal(t, float64(0), ask) @@ -467,12 +461,12 @@ func TestGetPricesFromResults(t *testing.T) { require.Contains(t, logs.All()[0].Message, "cannot parse enhanced EA telemetry") tt := trrsMercuryV1[:2] - e.getPricesFromResults(trrsMercuryV1[0], tt, 1) + getPricesFromResults(lggr, trrsMercuryV1[0], tt, 1) require.Equal(t, 2, logs.Len()) require.Contains(t, logs.All()[1].Message, "cannot parse enhanced EA telemetry bid price, task is nil") tt = trrsMercuryV1[:3] - e.getPricesFromResults(trrsMercuryV1[0], tt, 1) + getPricesFromResults(lggr, trrsMercuryV1[0], tt, 1) require.Equal(t, 3, logs.Len()) require.Contains(t, logs.All()[2].Message, "cannot parse enhanced EA telemetry ask price, task is nil") @@ -510,7 +504,7 @@ func TestGetPricesFromResults(t *testing.T) { Value: nil, }, }} - benchmarkPrice, bid, ask = e.getPricesFromResults(trrsMercuryV1[0], trrs2, 3) + benchmarkPrice, bid, ask = getPricesFromResults(lggr, trrsMercuryV1[0], trrs2, 3) require.Equal(t, benchmarkPrice, float64(0)) require.Equal(t, bid, float64(0)) require.Equal(t, ask, float64(0)) @@ -519,7 +513,7 @@ func TestGetPricesFromResults(t *testing.T) { require.Contains(t, logs.All()[4].Message, "cannot parse enhanced EA telemetry bid price") require.Contains(t, logs.All()[5].Message, "cannot parse enhanced EA telemetry ask price") - benchmarkPrice, bid, ask = e.getPricesFromResults(trrsMercuryV1[0], trrsMercuryV2, 2) + benchmarkPrice, bid, ask = getPricesFromResults(lggr, trrsMercuryV1[0], trrsMercuryV2, 2) require.Equal(t, 123456.123456, benchmarkPrice) require.Equal(t, float64(0), bid) require.Equal(t, float64(0), ask) @@ -542,10 +536,9 @@ func TestShouldCollectEnhancedTelemetryMercury(t *testing.T) { } func TestGetAssetSymbolFromRequestData(t *testing.T) { - e := EnhancedTelemetryService[EnhancedTelemetryMercuryData]{} - require.Equal(t, e.getAssetSymbolFromRequestData(""), "") + require.Equal(t, getAssetSymbolFromRequestData(""), "") reqData := `{"data":{"to":"LINK","from":"USD"}}` - require.Equal(t, e.getAssetSymbolFromRequestData(reqData), "USD/LINK") + require.Equal(t, getAssetSymbolFromRequestData(reqData), "USD/LINK") } func TestCollectMercuryEnhancedTelemetryV1(t *testing.T) { @@ -660,7 +653,7 @@ func TestCollectMercuryEnhancedTelemetryV1(t *testing.T) { wg.Wait() require.Equal(t, 2, logs.Len()) - require.Contains(t, logs.All()[0].Message, `cannot get bridge response from bridge task, job=0, id=ds1, name="test-mercury-bridge-1"`) + require.Contains(t, logs.All()[0].Message, `cannot get bridge response from bridge task, id=ds1, name="test-mercury-bridge-1"`) require.Contains(t, logs.All()[1].Message, "cannot parse EA telemetry") chDone <- struct{}{} } diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 579e8b67881..880ad73ad21 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -65,6 +65,7 @@ import ( var ( OCR2AggregatorTransmissionContractABI abi.ABI OCR2AggregatorLogDecoder LogDecoder + OCR3CapabilityLogDecoder LogDecoder ) func init() { @@ -77,6 +78,10 @@ func init() { if err != nil { panic(err) } + OCR3CapabilityLogDecoder, err = newOCR3CapabilityLogDecoder() + if err != nil { + panic(err) + } } var _ commontypes.Relayer = &Relayer{} //nolint:staticcheck @@ -256,12 +261,57 @@ func (r *Relayer) HealthReport() (report map[string]error) { return } +func newOCR3CapabilityConfigProvider(ctx context.Context, lggr logger.Logger, chain legacyevm.Chain, opts *types.RelayOpts) (*configWatcher, error) { + if !common.IsHexAddress(opts.ContractID) { + return nil, errors.New("invalid contractID, expected hex address") + } + + aggregatorAddress := common.HexToAddress(opts.ContractID) + offchainConfigDigester := OCR3CapabilityOffchainConfigDigester{ + ChainID: chain.Config().EVM().ChainID().Uint64(), + ContractAddress: aggregatorAddress, + } + return newContractConfigProvider(ctx, lggr, chain, opts, aggregatorAddress, OCR3CapabilityLogDecoder, offchainConfigDigester) +} + +// NewPluginProvider, but customized to use a different config provider func (r *Relayer) NewOCR3CapabilityProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.OCR3CapabilityProvider, error) { - pp, err := r.NewPluginProvider(rargs, pargs) + // TODO https://smartcontract-it.atlassian.net/browse/BCF-2887 + ctx := context.Background() + lggr := logger.Sugared(r.lggr).Named("PluginProvider").Named(rargs.ExternalJobID.String()) + relayOpts := types.NewRelayOpts(rargs) + relayConfig, err := relayOpts.RelayConfig() + if err != nil { + return nil, fmt.Errorf("failed to get relay config: %w", err) + } + + configWatcher, err := newOCR3CapabilityConfigProvider(ctx, r.lggr, r.chain, relayOpts) + if err != nil { + return nil, err + } + + transmitter, err := newOnChainContractTransmitter(ctx, r.lggr, rargs, r.ks.Eth(), configWatcher, configTransmitterOpts{}, OCR2AggregatorTransmissionContractABI) if err != nil { return nil, err } + var chainReaderService ChainReaderService + if relayConfig.ChainReader != nil { + if chainReaderService, err = NewChainReaderService(ctx, lggr, r.chain.LogPoller(), r.chain.HeadTracker(), r.chain.Client(), *relayConfig.ChainReader); err != nil { + return nil, err + } + } else { + lggr.Info("ChainReader missing from RelayConfig") + } + + pp := NewPluginProvider( + chainReaderService, + r.codec, + transmitter, + configWatcher, + lggr, + ) + fromAccount, err := pp.ContractTransmitter().FromAccount() if err != nil { return nil, err @@ -520,6 +570,8 @@ func (r *Relayer) NewConfigProvider(args commontypes.RelayArgs) (configProvider configProvider, err = newMercuryConfigProvider(ctx, lggr, r.chain, relayOpts) case "llo": configProvider, err = newLLOConfigProvider(ctx, lggr, r.chain, relayOpts) + case "ocr3-capability": + configProvider, err = newOCR3CapabilityConfigProvider(ctx, r.lggr, r.chain, relayOpts) default: return nil, fmt.Errorf("unrecognized provider type: %q", args.ProviderType) } diff --git a/core/services/relay/evm/mercury/v2/data_source.go b/core/services/relay/evm/mercury/v2/data_source.go index 28487ec714e..d05bd00e25a 100644 --- a/core/services/relay/evm/mercury/v2/data_source.go +++ b/core/services/relay/evm/mercury/v2/data_source.go @@ -2,6 +2,7 @@ package v2 import ( "context" + "encoding/json" "fmt" "math/big" "sync" @@ -22,6 +23,7 @@ import ( mercurytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/types" mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v2/reportcodec" + relayTypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -61,6 +63,12 @@ func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedTimestamp bool) (obs v2types.Observation, pipelineExecutionErr error) { var wg sync.WaitGroup + var relayConfig relayTypes.RelayConfig + err := json.Unmarshal(ds.jb.OCR2OracleSpec.RelayConfig.Bytes(), &relayConfig) + if err != nil { + pipelineExecutionErr = fmt.Errorf("failed to deserialize relay config: %w", err) + return + } ctx, cancel := context.WithCancel(ctx) if fetchMaxFinalizedTimestamp { @@ -108,7 +116,9 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam }() var isLink, isNative bool - if ds.feedID == ds.linkFeedID { + if ds.jb.OCR2OracleSpec.PluginConfig == nil { + obs.LinkPrice.Val = v2.MissingPrice + } else if ds.feedID == ds.linkFeedID { isLink = true } else { wg.Add(1) @@ -126,7 +136,9 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam }() } - if ds.feedID == ds.nativeFeedID { + if ds.jb.OCR2OracleSpec.PluginConfig == nil { + obs.NativePrice.Val = v2.MissingPrice + } else if ds.feedID == ds.nativeFeedID { isNative = true } else { wg.Add(1) diff --git a/core/services/relay/evm/mercury/v2/data_source_test.go b/core/services/relay/evm/mercury/v2/data_source_test.go index 19af909c8e9..7392ceb9866 100644 --- a/core/services/relay/evm/mercury/v2/data_source_test.go +++ b/core/services/relay/evm/mercury/v2/data_source_test.go @@ -15,6 +15,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" mercurymocks "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" @@ -72,7 +73,16 @@ func (ms *mockSaver) Save(r *pipeline.Run) { func Test_Datasource(t *testing.T) { orm := &mockORM{} - ds := &datasource{orm: orm, lggr: logger.TestLogger(t)} + jb := job.Job{ + Type: job.Type(pipeline.OffchainReporting2JobType), + OCR2OracleSpec: &job.OCR2OracleSpec{ + CaptureEATelemetry: true, + PluginConfig: map[string]interface{}{ + "serverURL": "a", + }, + }, + } + ds := &datasource{orm: orm, lggr: logger.TestLogger(t), jb: jb} ctx := testutils.Context(t) repts := ocrtypes.ReportTimestamp{} @@ -274,6 +284,25 @@ func Test_Datasource(t *testing.T) { assert.EqualError(t, obs.NativePrice.Err, "some error fetching native price") }) + t.Run("when PluginConfig=nil skips fetching link and native prices", func(t *testing.T) { + t.Cleanup(func() { + ds.jb = jb + }) + + fetcher.linkPriceErr = errors.New("some error fetching link price") + fetcher.nativePriceErr = errors.New("some error fetching native price") + + ds.jb.OCR2OracleSpec.PluginConfig = nil + + obs, err := ds.Observe(ctx, repts, false) + assert.NoError(t, err) + assert.Nil(t, obs.LinkPrice.Err) + assert.Equal(t, obs.LinkPrice.Val, v2.MissingPrice) + assert.Nil(t, obs.NativePrice.Err) + assert.Equal(t, obs.NativePrice.Val, v2.MissingPrice) + assert.Equal(t, big.NewInt(122), obs.BenchmarkPrice.Val) + }) + t.Run("when succeeds to fetch linkPrice or nativePrice but got nil (new feed)", func(t *testing.T) { obs, err := ds.Observe(ctx, repts, false) assert.NoError(t, err) diff --git a/core/services/relay/evm/mercury/v3/data_source.go b/core/services/relay/evm/mercury/v3/data_source.go index 644f4e775ec..48db946517f 100644 --- a/core/services/relay/evm/mercury/v3/data_source.go +++ b/core/services/relay/evm/mercury/v3/data_source.go @@ -2,6 +2,7 @@ package v3 import ( "context" + "encoding/json" "errors" "fmt" "math/big" @@ -22,6 +23,7 @@ import ( mercurytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/types" mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3/reportcodec" + relayTypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -63,6 +65,12 @@ func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedTimestamp bool) (obs v3types.Observation, pipelineExecutionErr error) { var wg sync.WaitGroup + var relayConfig relayTypes.RelayConfig + err := json.Unmarshal(ds.jb.OCR2OracleSpec.RelayConfig.Bytes(), &relayConfig) + if err != nil { + pipelineExecutionErr = fmt.Errorf("failed to deserialize relay config: %w", err) + return + } ctx, cancel := context.WithCancel(ctx) if fetchMaxFinalizedTimestamp { @@ -112,7 +120,9 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam }() var isLink, isNative bool - if ds.feedID == ds.linkFeedID { + if ds.jb.OCR2OracleSpec.PluginConfig == nil { + obs.LinkPrice.Val = v3.MissingPrice + } else if ds.feedID == ds.linkFeedID { isLink = true } else { wg.Add(1) @@ -130,7 +140,9 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam }() } - if ds.feedID == ds.nativeFeedID { + if ds.jb.OCR2OracleSpec.PluginConfig == nil { + obs.NativePrice.Val = v3.MissingPrice + } else if ds.feedID == ds.nativeFeedID { isNative = true } else { wg.Add(1) diff --git a/core/services/relay/evm/mercury/v3/data_source_test.go b/core/services/relay/evm/mercury/v3/data_source_test.go index a0f624c78d8..3d01d7472e5 100644 --- a/core/services/relay/evm/mercury/v3/data_source_test.go +++ b/core/services/relay/evm/mercury/v3/data_source_test.go @@ -5,18 +5,19 @@ import ( "math/big" "testing" + relaymercuryv3 "github.com/smartcontractkit/chainlink-data-streams/mercury/v3" + "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/eautils" + "github.com/pkg/errors" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" mercurytypes "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" - relaymercuryv3 "github.com/smartcontractkit/chainlink-data-streams/mercury/v3" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/eautils" mercurymocks "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" reportcodecv3 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3/reportcodec" @@ -77,6 +78,9 @@ func Test_Datasource(t *testing.T) { Type: job.Type(pipeline.OffchainReporting2JobType), OCR2OracleSpec: &job.OCR2OracleSpec{ CaptureEATelemetry: true, + PluginConfig: map[string]interface{}{ + "serverURL": "a", + }, }, } ds := &datasource{orm: orm, lggr: logger.TestLogger(t), jb: jb} @@ -360,6 +364,25 @@ func Test_Datasource(t *testing.T) { assert.EqualError(t, obs.NativePrice.Err, "some error fetching native price") }) + t.Run("when PluginConfig=nil skips fetching link and native prices", func(t *testing.T) { + t.Cleanup(func() { + ds.jb = jb + }) + + fetcher.linkPriceErr = errors.New("some error fetching link price") + fetcher.nativePriceErr = errors.New("some error fetching native price") + + ds.jb.OCR2OracleSpec.PluginConfig = nil + + obs, err := ds.Observe(ctx, repts, false) + assert.NoError(t, err) + assert.Nil(t, obs.LinkPrice.Err) + assert.Equal(t, obs.LinkPrice.Val, relaymercuryv3.MissingPrice) + assert.Nil(t, obs.NativePrice.Err) + assert.Equal(t, obs.NativePrice.Val, relaymercuryv3.MissingPrice) + assert.Equal(t, big.NewInt(122), obs.BenchmarkPrice.Val) + }) + t.Run("when succeeds to fetch linkPrice or nativePrice but got nil (new feed)", func(t *testing.T) { obs, err := ds.Observe(ctx, repts, false) assert.NoError(t, err) diff --git a/core/services/relay/evm/ocr3_capability_provider.go b/core/services/relay/evm/ocr3_capability_provider.go index 2080e1df873..ca41466570c 100644 --- a/core/services/relay/evm/ocr3_capability_provider.go +++ b/core/services/relay/evm/ocr3_capability_provider.go @@ -1,9 +1,23 @@ package evm import ( + "encoding/hex" + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-common/pkg/types" + + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/crypto" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/ocr3_capability" + "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" ) type ocr3CapabilityProvider struct { @@ -14,3 +28,181 @@ type ocr3CapabilityProvider struct { func (o *ocr3CapabilityProvider) OCR3ContractTransmitter() ocr3types.ContractTransmitter[[]byte] { return o.transmitter } + +var _ LogDecoder = &ocr3CapabilityLogDecoder{} + +type ocr3CapabilityLogDecoder struct { + eventName string + eventSig common.Hash + abi *abi.ABI +} + +// Modified newOCR2AggregatorLogDecoder to use OCR3Capability ABI +func newOCR3CapabilityLogDecoder() (*ocr3CapabilityLogDecoder, error) { + const eventName = "ConfigSet" + abi, err := ocr3_capability.OCR3CapabilityMetaData.GetAbi() + if err != nil { + return nil, err + } + return &ocr3CapabilityLogDecoder{ + eventName: eventName, + eventSig: abi.Events[eventName].ID, + abi: abi, + }, nil +} + +func (d *ocr3CapabilityLogDecoder) Decode(rawLog []byte) (ocrtypes.ContractConfig, error) { + unpacked := new(ocr3_capability.OCR3CapabilityConfigSet) + err := d.abi.UnpackIntoInterface(unpacked, d.eventName, rawLog) + if err != nil { + return ocrtypes.ContractConfig{}, fmt.Errorf("failed to unpack log data: %w", err) + } + + var transmitAccounts []ocrtypes.Account + for _, addr := range unpacked.Transmitters { + transmitAccounts = append(transmitAccounts, ocrtypes.Account(addr.Hex())) + } + var signers []ocrtypes.OnchainPublicKey + allPubKeys := map[string]any{} + for _, pubKey := range unpacked.Signers { + pubKey := pubKey + + // validate uniqueness of each individual key + pubKeys, err := ocrcommon.UnmarshalMultichainPublicKey(pubKey) + if err != nil { + return ocrtypes.ContractConfig{}, err + } + for _, key := range pubKeys { + raw := hex.EncodeToString(key) + _, exists := allPubKeys[raw] + if exists { + return ocrtypes.ContractConfig{}, fmt.Errorf("Duplicate onchain public key: %v", raw) + } + allPubKeys[raw] = struct{}{} + } + + signers = append(signers, pubKey[:]) + } + + return ocrtypes.ContractConfig{ + ConfigDigest: unpacked.ConfigDigest, + ConfigCount: unpacked.ConfigCount, + Signers: signers, + Transmitters: transmitAccounts, + F: unpacked.F, + OnchainConfig: unpacked.OnchainConfig, + OffchainConfigVersion: unpacked.OffchainConfigVersion, + OffchainConfig: unpacked.OffchainConfig, + }, nil +} + +func (d *ocr3CapabilityLogDecoder) EventSig() common.Hash { + return d.eventSig +} + +var _ ocrtypes.OffchainConfigDigester = OCR3CapabilityOffchainConfigDigester{} + +// EVMOffchainConfigDigester forked to not assume signers are 20 byte addresses +type OCR3CapabilityOffchainConfigDigester struct { + ChainID uint64 + ContractAddress common.Address +} + +func (d OCR3CapabilityOffchainConfigDigester) ConfigDigest(cc ocrtypes.ContractConfig) (ocrtypes.ConfigDigest, error) { + signers := [][]byte{} + for _, signer := range cc.Signers { + signers = append(signers, signer) + } + transmitters := []common.Address{} + for i, transmitter := range cc.Transmitters { + if !strings.HasPrefix(string(transmitter), "0x") || len(transmitter) != 42 || !common.IsHexAddress(string(transmitter)) { + return ocrtypes.ConfigDigest{}, fmt.Errorf("%v-th evm transmitter should be a 42 character Ethereum address string, but got '%v'", i, transmitter) + } + a := common.HexToAddress(string(transmitter)) + transmitters = append(transmitters, a) + } + + return ocr3CapabilityConfigDigest( + d.ChainID, + d.ContractAddress, + cc.ConfigCount, + signers, + transmitters, + cc.F, + cc.OnchainConfig, + cc.OffchainConfigVersion, + cc.OffchainConfig, + ), nil +} + +const ConfigDigestPrefixKeystoneOCR3Capability ocrtypes.ConfigDigestPrefix = 0x000e + +func (d OCR3CapabilityOffchainConfigDigester) ConfigDigestPrefix() (ocrtypes.ConfigDigestPrefix, error) { + return ConfigDigestPrefixKeystoneOCR3Capability, nil +} + +func makeOCR3CapabilityConfigDigestArgs() abi.Arguments { + mustNewType := func(t string) abi.Type { + result, err := abi.NewType(t, "", []abi.ArgumentMarshaling{}) + if err != nil { + panic(fmt.Sprintf("Unexpected error during abi.NewType: %s", err)) + } + return result + } + return abi.Arguments([]abi.Argument{ + {Name: "chainId", Type: mustNewType("uint256")}, + {Name: "contractAddress", Type: mustNewType("address")}, + {Name: "configCount", Type: mustNewType("uint64")}, + {Name: "signers", Type: mustNewType("bytes[]")}, + {Name: "transmitters", Type: mustNewType("address[]")}, + {Name: "f", Type: mustNewType("uint8")}, + {Name: "onchainConfig", Type: mustNewType("bytes")}, + {Name: "encodedConfigVersion", Type: mustNewType("uint64")}, + {Name: "encodedConfig", Type: mustNewType("bytes")}, + }) +} + +var ocr3CapabilityConfigDigestArgs = makeOCR3CapabilityConfigDigestArgs() + +func ocr3CapabilityConfigDigest( + chainID uint64, + contractAddress common.Address, + configCount uint64, + oracles [][]byte, + transmitters []common.Address, + f uint8, + onchainConfig []byte, + offchainConfigVersion uint64, + offchainConfig []byte, +) ocrtypes.ConfigDigest { + chainIDBig := new(big.Int) + chainIDBig.SetUint64(chainID) + msg, err := ocr3CapabilityConfigDigestArgs.Pack( + chainIDBig, + contractAddress, + configCount, + oracles, + transmitters, + f, + onchainConfig, + offchainConfigVersion, + offchainConfig, + ) + if err != nil { + // assertion + panic(err) + } + rawHash := crypto.Keccak256(msg) + configDigest := ocrtypes.ConfigDigest{} + if n := copy(configDigest[:], rawHash); n != len(configDigest) { + // assertion + panic("copy too little data") + } + if ConfigDigestPrefixKeystoneOCR3Capability != 0x000e { + // assertion + panic("wrong ConfigDigestPrefix") + } + configDigest[0] = 0 + configDigest[1] = 0x0e + return configDigest +} diff --git a/core/services/workflows/delegate.go b/core/services/workflows/delegate.go index acd006940f3..2aac9ba97b5 100644 --- a/core/services/workflows/delegate.go +++ b/core/services/workflows/delegate.go @@ -6,8 +6,8 @@ import ( "github.com/google/uuid" "github.com/pelletier/go-toml" - "github.com/smartcontractkit/chainlink-common/pkg/types/core" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/workflows/store" @@ -34,15 +34,21 @@ func (d *Delegate) BeforeJobDeleted(spec job.Job) {} func (d *Delegate) OnDeleteJob(context.Context, job.Job) error { return nil } // ServicesForSpec satisfies the job.Delegate interface. -func (d *Delegate) ServicesForSpec(_ context.Context, spec job.Job) ([]job.ServiceCtx, error) { +func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) ([]job.ServiceCtx, error) { + sdkSpec, err := spec.WorkflowSpec.SDKSpec(ctx) + if err != nil { + return nil, err + } + cfg := Config{ Lggr: d.logger, - Spec: spec.WorkflowSpec.Workflow, + Workflow: sdkSpec, WorkflowID: spec.WorkflowSpec.WorkflowID, WorkflowOwner: spec.WorkflowSpec.WorkflowOwner, WorkflowName: spec.WorkflowSpec.WorkflowName, Registry: d.registry, Store: d.store, + Config: []byte(spec.WorkflowSpec.Config), } engine, err := NewEngine(cfg) if err != nil { @@ -59,7 +65,7 @@ func NewDelegate( return &Delegate{logger: logger, registry: registry, store: store} } -func ValidatedWorkflowJobSpec(tomlString string) (job.Job, error) { +func ValidatedWorkflowJobSpec(ctx context.Context, tomlString string) (job.Job, error) { var jb = job.Job{ExternalJobID: uuid.New()} tree, err := toml.Load(tomlString) @@ -81,16 +87,21 @@ func ValidatedWorkflowJobSpec(tomlString string) (job.Job, error) { return jb, fmt.Errorf("toml unmarshal error on workflow spec: %w", err) } - err = spec.Validate() + sdkSpec, err := spec.SDKSpec(ctx) if err != nil { - return jb, fmt.Errorf("invalid WorkflowSpec: %w", err) + return jb, fmt.Errorf("failed to convert to sdk workflow spec: %w", err) } // ensure the embedded workflow graph is valid - _, err = Parse(spec.Workflow) - if err != nil { + if _, err = Parse(sdkSpec); err != nil { return jb, fmt.Errorf("failed to parse workflow graph: %w", err) } + + err = spec.Validate(ctx) + if err != nil { + return jb, fmt.Errorf("invalid WorkflowSpec: %w", err) + } + jb.WorkflowSpec = &spec jb.WorkflowSpecID = &spec.ID diff --git a/core/services/workflows/delegate_test.go b/core/services/workflows/delegate_test.go index a12eac80bbf..d27a1012e6b 100644 --- a/core/services/workflows/delegate_test.go +++ b/core/services/workflows/delegate_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/workflows" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" ) @@ -99,7 +100,7 @@ schemaVersion = 1 for _, tc := range tt { tc := tc t.Run(tc.name, func(t *testing.T) { - _, err := workflows.ValidatedWorkflowJobSpec(tc.workflowTomlFn()) + _, err := workflows.ValidatedWorkflowJobSpec(testutils.Context(t), tc.workflowTomlFn()) if tc.valid { require.NoError(t, err) } else { diff --git a/core/services/workflows/engine.go b/core/services/workflows/engine.go index 88fb198d154..26bc2d4cd68 100644 --- a/core/services/workflows/engine.go +++ b/core/services/workflows/engine.go @@ -10,6 +10,7 @@ import ( "time" "github.com/jonboulle/clockwork" + "github.com/smartcontractkit/chainlink-common/pkg/workflows/sdk" "github.com/smartcontractkit/chainlink-common/pkg/workflows/exec" @@ -853,7 +854,7 @@ func (e *Engine) Close() error { } type Config struct { - Spec string + Workflow sdk.WorkflowSpec WorkflowID string WorkflowOwner string WorkflowName string @@ -864,6 +865,7 @@ type Config struct { NewWorkerTimeout time.Duration MaxExecutionDuration time.Duration Store store.Store + Config []byte // For testing purposes only maxRetries int @@ -929,7 +931,7 @@ func NewEngine(cfg Config) (engine *Engine, err error) { // - that the resulting graph is strongly connected (i.e. no disjointed subgraphs exist) // - etc. - workflow, err := Parse(cfg.Spec) + workflow, err := Parse(cfg.Workflow) if err != nil { return nil, err } diff --git a/core/services/workflows/engine_test.go b/core/services/workflows/engine_test.go index e7f83504e9e..9bd1b285377 100644 --- a/core/services/workflows/engine_test.go +++ b/core/services/workflows/engine_test.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/values" "github.com/smartcontractkit/chainlink-common/pkg/workflows" + coreCap "github.com/smartcontractkit/chainlink/v2/core/capabilities" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" @@ -136,12 +137,18 @@ func newTestEngine(t *testing.T, reg *coreCap.Registry, spec string, opts ...fun executionFinished := make(chan string, 100) clock := clockwork.NewFakeClock() + sdkSpec, err := (&job.WorkflowSpec{ + Workflow: spec, + SpecType: job.YamlSpec, + }).SDKSpec(testutils.Context(t)) + require.NoError(t, err) + reg.SetLocalRegistry(&testConfigProvider{}) cfg := Config{ WorkflowID: testWorkflowId, Lggr: logger.TestLogger(t), Registry: reg, - Spec: spec, + Workflow: sdkSpec, maxRetries: 1, retryMs: 100, afterInit: func(success bool) { diff --git a/core/services/workflows/models.go b/core/services/workflows/models.go index 461569e8c69..cf1fffa9a4c 100644 --- a/core/services/workflows/models.go +++ b/core/services/workflows/models.go @@ -6,6 +6,7 @@ import ( "sync/atomic" "github.com/dominikbraun/graph" + "github.com/smartcontractkit/chainlink-common/pkg/workflows/sdk" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/values" @@ -25,7 +26,7 @@ type workflow struct { triggers []*triggerCapability - spec *workflows.WorkflowSpec + spec *sdk.WorkflowSpec } func (w *workflow) walkDo(start string, do func(s *step) error) error { @@ -85,18 +86,20 @@ type step struct { } type triggerCapability struct { - workflows.StepDefinition + sdk.StepDefinition trigger capabilities.TriggerCapability config atomic.Pointer[values.Map] } -func Parse(yamlWorkflow string) (*workflow, error) { - wf2, err := workflows.ParseDependencyGraph(yamlWorkflow) +func Parse(sdkSpec sdk.WorkflowSpec) (*workflow, error) { + wf2, err := workflows.BuildDependencyGraph(sdkSpec) if err != nil { return nil, err } - return createWorkflow(wf2) + + wfs, err := createWorkflow(wf2) + return wfs, err } // createWorkflow converts a StaticWorkflow to an executable workflow diff --git a/core/services/workflows/models_test.go b/core/services/workflows/models_test.go index a28aeb9df01..68944e64bad 100644 --- a/core/services/workflows/models_test.go +++ b/core/services/workflows/models_test.go @@ -7,6 +7,9 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/workflows" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/services/job" ) func TestParse_Graph(t *testing.T) { @@ -290,7 +293,11 @@ targets: for _, tc := range testCases { t.Run(tc.name, func(st *testing.T) { - wf, err := Parse(tc.yaml) + + spec, err := job.YAMLSpecFactory{}.Spec(testutils.Context(t), []byte(tc.yaml), nil) + require.NoError(t, err) + + wf, err := Parse(spec) if tc.errMsg != "" { assert.ErrorContains(st, err, tc.errMsg) } else { @@ -316,7 +323,10 @@ targets: } func TestParsesIntsCorrectly(t *testing.T) { - wf, err := Parse(hardcodedWorkflow) + spec, err := job.YAMLSpecFactory{}.Spec(testutils.Context(t), []byte(hardcodedWorkflow), nil) + require.NoError(t, err) + + wf, err := Parse(spec) require.NoError(t, err) n, err := wf.Vertex("evm_median") diff --git a/core/store/migrate/migrations/0253_add_spec_type_to_workflow_spec.sql b/core/store/migrate/migrations/0253_add_spec_type_to_workflow_spec.sql new file mode 100644 index 00000000000..3c62c460976 --- /dev/null +++ b/core/store/migrate/migrations/0253_add_spec_type_to_workflow_spec.sql @@ -0,0 +1,13 @@ +-- +goose Up +-- +goose StatementBegin + +ALTER TABLE workflow_specs ADD COLUMN spec_type varchar(255) DEFAULT 'yaml'; + +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin + +ALTER TABLE workflow_specs DROP COLUMN spec_type; + +-- +goose StatementEnd \ No newline at end of file diff --git a/core/testdata/testspecs/v2_specs.go b/core/testdata/testspecs/v2_specs.go index 3a1798ab5ad..554b18ae818 100644 --- a/core/testdata/testspecs/v2_specs.go +++ b/core/testdata/testspecs/v2_specs.go @@ -1,6 +1,7 @@ package testspecs import ( + "crypto/sha256" "fmt" "strconv" "strings" @@ -10,9 +11,9 @@ import ( "github.com/google/uuid" "github.com/test-go/testify/require" - pkgworkflows "github.com/smartcontractkit/chainlink-common/pkg/workflows" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" @@ -899,9 +900,8 @@ func (w WorkflowJobSpec) Job() job.Job { // GenerateWorkflowJobSpec creates a WorkflowJobSpec from the given workflow yaml spec string func GenerateWorkflowJobSpec(t *testing.T, spec string) WorkflowJobSpec { t.Helper() - s, err := pkgworkflows.ParseWorkflowSpecYaml(spec) - require.NoError(t, err, "failed to parse YAML workflow spec %s", spec) - id := s.CID + sum := sha256.Sum256([]byte(spec)) + id := fmt.Sprintf("%x", sum) template := ` type = "workflow" schemaVersion = 1 @@ -913,7 +913,7 @@ workflow = """ ` toml := fmt.Sprintf(template, id, spec) - j, err := workflows.ValidatedWorkflowJobSpec(toml) + j, err := workflows.ValidatedWorkflowJobSpec(testutils.Context(t), toml) require.NoError(t, err, "failed to validate TOML job spec for workflow %s", toml) return WorkflowJobSpec{toml: toml, j: j} } diff --git a/core/web/jobs_controller.go b/core/web/jobs_controller.go index 1a80ca9693b..b11f6e61094 100644 --- a/core/web/jobs_controller.go +++ b/core/web/jobs_controller.go @@ -256,7 +256,7 @@ func (jc *JobsController) validateJobSpec(ctx context.Context, tomlString string case job.Stream: jb, err = streams.ValidatedStreamSpec(tomlString) case job.Workflow: - jb, err = workflows.ValidatedWorkflowJobSpec(tomlString) + jb, err = workflows.ValidatedWorkflowJobSpec(ctx, tomlString) case job.StandardCapabilities: jb, err = standardcapabilities.ValidatedStandardCapabilitiesSpec(tomlString) case job.CCIP: diff --git a/core/web/resolver/mutation.go b/core/web/resolver/mutation.go index 4c9e409cbbf..4388bd5a701 100644 --- a/core/web/resolver/mutation.go +++ b/core/web/resolver/mutation.go @@ -1063,7 +1063,7 @@ func (r *Resolver) CreateJob(ctx context.Context, args struct { case job.Gateway: jb, err = gateway.ValidatedGatewaySpec(args.Input.TOML) case job.Workflow: - jb, err = workflows.ValidatedWorkflowJobSpec(args.Input.TOML) + jb, err = workflows.ValidatedWorkflowJobSpec(ctx, args.Input.TOML) case job.StandardCapabilities: jb, err = standardcapabilities.ValidatedStandardCapabilitiesSpec(args.Input.TOML) case job.Stream: diff --git a/dashboard-lib/go.mod b/dashboard-lib/go.mod index 8e68d3c6203..0ab2b696dbc 100644 --- a/dashboard-lib/go.mod +++ b/dashboard-lib/go.mod @@ -17,11 +17,11 @@ require ( github.com/gosimple/slug v1.13.1 // indirect github.com/gosimple/unidecode v1.0.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/common v0.45.0 // indirect github.com/stretchr/testify v1.9.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/sys v0.24.0 // indirect ) replace github.com/sourcegraph/sourcegraph/lib => github.com/sourcegraph/sourcegraph-public-snapshot/lib v0.0.0-20240822153003-c864f15af264 diff --git a/dashboard-lib/go.sum b/dashboard-lib/go.sum index 7eb74088f13..fd6985df0cb 100644 --- a/dashboard-lib/go.sum +++ b/dashboard-lib/go.sum @@ -15,8 +15,9 @@ github.com/grafana/grafana-foundation-sdk/go v0.0.0-20240314112857-a7c9c6d0044c/ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -31,7 +32,7 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go.mod b/go.mod index e900c0ebcc7..f5b4216e29a 100644 --- a/go.mod +++ b/go.mod @@ -74,10 +74,10 @@ require ( github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chain-selectors v1.0.23 github.com/smartcontractkit/chainlink-automation v1.0.4 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20240916150615-85b8aa5fa7e6 - github.com/smartcontractkit/chainlink-common v0.2.2-0.20240913161926-ce5d667907ce + github.com/smartcontractkit/chainlink-ccip v0.0.0-20240917180332-5a68498d1612 + github.com/smartcontractkit/chainlink-common v0.2.2-0.20240916150342-36cb47701edf github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240911175228-daf2600bb7b7 - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240906125718-9f0a98d32fbc + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240916152957-433914114bd2 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240910155501-42f20443189f github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240911182932-3c609a6ac664 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240911194142-506bc469d8ae diff --git a/go.sum b/go.sum index 1414f564a8b..4c05468ad67 100644 --- a/go.sum +++ b/go.sum @@ -1042,14 +1042,14 @@ github.com/smartcontractkit/chain-selectors v1.0.23 h1:D2Eaex4Cw/O7Lg3tX6WklOqnj github.com/smartcontractkit/chain-selectors v1.0.23/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20240916150615-85b8aa5fa7e6 h1:cbHlV2CSphQ+ghDye21M8ym0aAO/Y649H2Mg60M2AuE= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20240916150615-85b8aa5fa7e6/go.mod h1:Lv77O13ZxOdmlvnu2vaUC0Lg+t3JAL+N+9K8dRsgmDI= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240913161926-ce5d667907ce h1:qXS0aWiDFDoLRCB+kSGnzp77iYT2luflUyzE5BnNmpY= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240913161926-ce5d667907ce/go.mod h1:sjiiPwd4KsYOCf68MwL86EKphdXeT66EY7j53WH5DCc= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240917180332-5a68498d1612 h1:xPEM9XbfZmv8N3NjZ7AX5salonll/LdXrbb8JCbA4FE= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240917180332-5a68498d1612/go.mod h1:Lv77O13ZxOdmlvnu2vaUC0Lg+t3JAL+N+9K8dRsgmDI= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240916150342-36cb47701edf h1:1AlTUkT5D8HmvU9bwDoIN54/EFyOnRBl7gnXZVrYXEA= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240916150342-36cb47701edf/go.mod h1:l8NTByXUdGGJX+vyKYI6yX1/HIpM14F8Wm9BkU3Q4Qo= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240911175228-daf2600bb7b7 h1:lTGIOQYLk1Ufn++X/AvZnt6VOcuhste5yp+C157No/Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240911175228-daf2600bb7b7/go.mod h1:BMYE1vC/pGmdFSsOJdPrAA0/4gZ0Xo0SxTMdGspBtRo= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240906125718-9f0a98d32fbc h1:tRmTlaoAt+7FakMXXgeCuRPmzzBo5jsGpeCVvcU6KMc= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240906125718-9f0a98d32fbc/go.mod h1:PwPcmQNAzVmU8r8JWKrDRgvXesDwxnqbMD6DvYt/Z7M= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240916152957-433914114bd2 h1:yRk4ektpx/UxwarqAfgxUXLrsYXlaNeP1NOwzHGrK2Q= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240916152957-433914114bd2/go.mod h1:rNhNSrrRMvkgAm5SA6bNTdh2340bTQQZdUVNtZ2o2bk= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240910155501-42f20443189f h1:p4p3jBT91EQyLuAMvHD+zNJsuAYI/QjJbzuGUJ7wIgg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240910155501-42f20443189f/go.mod h1:FLlWBt2hwiMVgt9AcSo6wBJYIRd/nsc8ENbV1Wir1bw= github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240911182932-3c609a6ac664 h1:JPs35oSO07PK3Qv7Kyv0GJHVLacIE1IkrvefaPyBjKs= diff --git a/integration-tests/actions/automation_ocr_helpers.go b/integration-tests/actions/automation_ocr_helpers.go index df9168e25de..f1845804677 100644 --- a/integration-tests/actions/automation_ocr_helpers.go +++ b/integration-tests/actions/automation_ocr_helpers.go @@ -2,297 +2,20 @@ package actions //revive:disable:dot-imports import ( - "encoding/json" - "fmt" "math" "math/big" "testing" - "time" "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-testing-framework/seth" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_automation_registry_master_wrapper_2_3" - - "github.com/ethereum/go-ethereum/common" - "github.com/lib/pq" "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v4" - - ocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" - ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - ocr2keepers20config "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" - ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" - "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" - "github.com/smartcontractkit/chainlink/v2/core/store/models" ) -func BuildAutoOCR2ConfigVars( - t *testing.T, - chainlinkNodes []*client.ChainlinkK8sClient, - registryConfig contracts.KeeperRegistrySettings, - registrar string, - deltaStage time.Duration, - chainModuleAddress common.Address, - reorgProtectionEnabled bool, - linkToken contracts.LinkToken, - wethToken contracts.WETHToken, - ethUSDFeed contracts.MockETHUSDFeed, - -) (contracts.OCRv2Config, error) { - return BuildAutoOCR2ConfigVarsWithKeyIndex(t, chainlinkNodes, registryConfig, registrar, deltaStage, 0, common.Address{}, chainModuleAddress, reorgProtectionEnabled, linkToken, wethToken, ethUSDFeed) -} - -func BuildAutoOCR2ConfigVarsWithKeyIndex( - t *testing.T, - chainlinkNodes []*client.ChainlinkK8sClient, - registryConfig contracts.KeeperRegistrySettings, - registrar string, - deltaStage time.Duration, - keyIndex int, - registryOwnerAddress common.Address, - chainModuleAddress common.Address, - reorgProtectionEnabled bool, - linkToken contracts.LinkToken, - wethToken contracts.WETHToken, - ethUSDFeed contracts.MockETHUSDFeed, -) (contracts.OCRv2Config, error) { - l := logging.GetTestLogger(t) - S, oracleIdentities, err := GetOracleIdentitiesWithKeyIndex(chainlinkNodes, keyIndex) - if err != nil { - return contracts.OCRv2Config{}, err - } - - var offC []byte - var signerOnchainPublicKeys []types.OnchainPublicKey - var transmitterAccounts []types.Account - var f uint8 - var offchainConfigVersion uint64 - var offchainConfig []byte - - if registryConfig.RegistryVersion == ethereum.RegistryVersion_2_1 || registryConfig.RegistryVersion == ethereum.RegistryVersion_2_2 || registryConfig.RegistryVersion == ethereum.RegistryVersion_2_3 { - offC, err = json.Marshal(ocr2keepers30config.OffchainConfig{ - TargetProbability: "0.999", - TargetInRounds: 1, - PerformLockoutWindow: 3600000, // Intentionally set to be higher than in prod for testing purpose - GasLimitPerReport: 5_300_000, - GasOverheadPerUpkeep: 300_000, - MinConfirmations: 0, - MaxUpkeepBatchSize: 10, - }) - if err != nil { - return contracts.OCRv2Config{}, err - } - - signerOnchainPublicKeys, transmitterAccounts, f, _, offchainConfigVersion, offchainConfig, err = ocr3.ContractSetConfigArgsForTests( - 10*time.Second, // deltaProgress time.Duration, - 15*time.Second, // deltaResend time.Duration, - 500*time.Millisecond, // deltaInitial time.Duration, - 1000*time.Millisecond, // deltaRound time.Duration, - 200*time.Millisecond, // deltaGrace time.Duration, - 300*time.Millisecond, // deltaCertifiedCommitRequest time.Duration - deltaStage, // deltaStage time.Duration, - 24, // rMax uint64, - S, // s []int, - oracleIdentities, // oracles []OracleIdentityExtra, - offC, // reportingPluginConfig []byte, - 20*time.Millisecond, // maxDurationQuery time.Duration, - 20*time.Millisecond, // maxDurationObservation time.Duration, // good to here - 1200*time.Millisecond, // maxDurationShouldAcceptAttestedReport time.Duration, - 20*time.Millisecond, // maxDurationShouldTransmitAcceptedReport time.Duration, - 1, // f int, - nil, // onchainConfig []byte, - ) - if err != nil { - return contracts.OCRv2Config{}, err - } - } else { - offC, err = json.Marshal(ocr2keepers20config.OffchainConfig{ - TargetProbability: "0.999", - TargetInRounds: 1, - PerformLockoutWindow: 3600000, // Intentionally set to be higher than in prod for testing purpose - GasLimitPerReport: 5_300_000, - GasOverheadPerUpkeep: 300_000, - SamplingJobDuration: 3000, - MinConfirmations: 0, - MaxUpkeepBatchSize: 1, - }) - if err != nil { - return contracts.OCRv2Config{}, err - } - - signerOnchainPublicKeys, transmitterAccounts, f, _, offchainConfigVersion, offchainConfig, err = ocr2.ContractSetConfigArgsForTests( - 10*time.Second, // deltaProgress time.Duration, - 15*time.Second, // deltaResend time.Duration, - 3000*time.Millisecond, // deltaRound time.Duration, - 200*time.Millisecond, // deltaGrace time.Duration, - deltaStage, // deltaStage time.Duration, - 24, // rMax uint8, - S, // s []int, - oracleIdentities, // oracles []OracleIdentityExtra, - offC, // reportingPluginConfig []byte, - 20*time.Millisecond, // maxDurationQuery time.Duration, - 20*time.Millisecond, // maxDurationObservation time.Duration, - 1200*time.Millisecond, // maxDurationReport time.Duration, - 20*time.Millisecond, // maxDurationShouldAcceptFinalizedReport time.Duration, - 20*time.Millisecond, // maxDurationShouldTransmitAcceptedReport time.Duration, - 1, // f int, - nil, // onchainConfig []byte, - ) - if err != nil { - return contracts.OCRv2Config{}, err - } - } - - var signers []common.Address - for _, signer := range signerOnchainPublicKeys { - require.Equal(t, 20, len(signer), "OnChainPublicKey '%v' has wrong length for address", signer) - signers = append(signers, common.BytesToAddress(signer)) - } - - var transmitters []common.Address - for _, transmitter := range transmitterAccounts { - require.True(t, common.IsHexAddress(string(transmitter)), "TransmitAccount '%s' is not a valid Ethereum address", string(transmitter)) - transmitters = append(transmitters, common.HexToAddress(string(transmitter))) - } - - ocrConfig := contracts.OCRv2Config{ - Signers: signers, - Transmitters: transmitters, - F: f, - OffchainConfigVersion: offchainConfigVersion, - OffchainConfig: offchainConfig, - } - - if registryConfig.RegistryVersion == ethereum.RegistryVersion_2_0 { - ocrConfig.OnchainConfig = registryConfig.Encode20OnchainConfig(registrar) - } else if registryConfig.RegistryVersion == ethereum.RegistryVersion_2_1 { - ocrConfig.TypedOnchainConfig21 = registryConfig.Create21OnchainConfig(registrar, registryOwnerAddress) - } else if registryConfig.RegistryVersion == ethereum.RegistryVersion_2_2 { - ocrConfig.TypedOnchainConfig22 = registryConfig.Create22OnchainConfig(registrar, registryOwnerAddress, chainModuleAddress, reorgProtectionEnabled) - } else if registryConfig.RegistryVersion == ethereum.RegistryVersion_2_3 { - ocrConfig.TypedOnchainConfig23 = registryConfig.Create23OnchainConfig(registrar, registryOwnerAddress, chainModuleAddress, reorgProtectionEnabled) - ocrConfig.BillingTokens = []common.Address{ - common.HexToAddress(linkToken.Address()), - common.HexToAddress(wethToken.Address()), - } - - ocrConfig.BillingConfigs = []i_automation_registry_master_wrapper_2_3.AutomationRegistryBase23BillingConfig{ - { - GasFeePPB: 100, - FlatFeeMilliCents: big.NewInt(500), - PriceFeed: common.HexToAddress(ethUSDFeed.Address()), // ETH/USD feed and LINK/USD feed are the same - Decimals: 18, - FallbackPrice: big.NewInt(1000), - MinSpend: big.NewInt(200), - }, - { - GasFeePPB: 100, - FlatFeeMilliCents: big.NewInt(500), - PriceFeed: common.HexToAddress(ethUSDFeed.Address()), // ETH/USD feed and LINK/USD feed are the same - Decimals: 18, - FallbackPrice: big.NewInt(1000), - MinSpend: big.NewInt(200), - }, - } - } - - l.Info().Msg("Done building OCR config") - return ocrConfig, nil -} - -// CreateOCRKeeperJobs bootstraps the first node and to the other nodes sends ocr jobs -func CreateOCRKeeperJobs( - t *testing.T, - chainlinkNodes []*client.ChainlinkK8sClient, - registryAddr string, - chainID int64, - keyIndex int, - registryVersion ethereum.KeeperRegistryVersion, -) { - l := logging.GetTestLogger(t) - bootstrapNode := chainlinkNodes[0] - bootstrapP2PIds, err := bootstrapNode.MustReadP2PKeys() - require.NoError(t, err, "Shouldn't fail reading P2P keys from bootstrap node") - bootstrapP2PId := bootstrapP2PIds.Data[0].Attributes.PeerID - - var contractVersion string - if registryVersion == ethereum.RegistryVersion_2_2 || registryVersion == ethereum.RegistryVersion_2_3 { - contractVersion = "v2.1+" - } else if registryVersion == ethereum.RegistryVersion_2_1 { - contractVersion = "v2.1" - } else if registryVersion == ethereum.RegistryVersion_2_0 { - contractVersion = "v2.0" - } else { - require.FailNow(t, fmt.Sprintf("v2.0, v2.1, v2.2 and v2.3 are the only supported versions, but got something else: %v (iota)", registryVersion)) - } - - bootstrapSpec := &client.OCR2TaskJobSpec{ - Name: "ocr2 bootstrap node " + registryAddr, - JobType: "bootstrap", - OCR2OracleSpec: job.OCR2OracleSpec{ - ContractID: registryAddr, - Relay: "evm", - RelayConfig: map[string]interface{}{ - "chainID": int(chainID), - }, - ContractConfigTrackerPollInterval: *models.NewInterval(time.Second * 15), - }, - } - _, err = bootstrapNode.MustCreateJob(bootstrapSpec) - require.NoError(t, err, "Shouldn't fail creating bootstrap job on bootstrap node") - // TODO: Use service name returned by chainlink-env once that is available - P2Pv2Bootstrapper := fmt.Sprintf("%s@%s-node-1:%d", bootstrapP2PId, bootstrapNode.Name(), 6690) - - for nodeIndex := 1; nodeIndex < len(chainlinkNodes); nodeIndex++ { - nodeTransmitterAddress, err := chainlinkNodes[nodeIndex].EthAddresses() - require.NoError(t, err, "Shouldn't fail getting primary ETH address from OCR node %d", nodeIndex+1) - nodeOCRKeys, err := chainlinkNodes[nodeIndex].MustReadOCR2Keys() - require.NoError(t, err, "Shouldn't fail getting OCR keys from OCR node %d", nodeIndex+1) - var nodeOCRKeyId []string - for _, key := range nodeOCRKeys.Data { - if key.Attributes.ChainType == string(chaintype.EVM) { - nodeOCRKeyId = append(nodeOCRKeyId, key.ID) - break - } - } - - autoOCR2JobSpec := client.OCR2TaskJobSpec{ - Name: "ocr2 " + registryAddr, - JobType: "offchainreporting2", - OCR2OracleSpec: job.OCR2OracleSpec{ - PluginType: "ocr2automation", - Relay: "evm", - RelayConfig: map[string]interface{}{ - "chainID": int(chainID), - }, - PluginConfig: map[string]interface{}{ - "mercuryCredentialName": "\"cred1\"", - "contractVersion": "\"" + contractVersion + "\"", - }, - ContractConfigTrackerPollInterval: *models.NewInterval(time.Second * 15), - ContractID: registryAddr, // registryAddr - OCRKeyBundleID: null.StringFrom(nodeOCRKeyId[0]), // get node ocr2config.ID - TransmitterID: null.StringFrom(nodeTransmitterAddress[keyIndex]), // node addr - P2PV2Bootstrappers: pq.StringArray{P2Pv2Bootstrapper}, // bootstrap node key and address @bootstrap:8000 - }, - } - - _, err = chainlinkNodes[nodeIndex].MustCreateJob(&autoOCR2JobSpec) - require.NoError(t, err, "Shouldn't fail creating OCR Task job on OCR node %d err: %+v", nodeIndex+1, err) - } - l.Info().Msg("Done creating OCR automation jobs") -} - // DeployAutoOCRRegistryAndRegistrar registry and registrar func DeployAutoOCRRegistryAndRegistrar( t *testing.T, diff --git a/integration-tests/actions/automation_ocr_helpers_local.go b/integration-tests/actions/automation_ocr_helpers_local.go index d513f1875a4..e4d5ad70cac 100644 --- a/integration-tests/actions/automation_ocr_helpers_local.go +++ b/integration-tests/actions/automation_ocr_helpers_local.go @@ -28,7 +28,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/store/models" ) -func AutomationDefaultRegistryConfig(c tc.AutomationTestConfig) contracts.KeeperRegistrySettings { +func ReadRegistryConfig(c tc.AutomationTestConfig) contracts.KeeperRegistrySettings { registrySettings := c.GetAutomationConfig().AutomationConfig.RegistrySettings return contracts.KeeperRegistrySettings{ PaymentPremiumPPB: *registrySettings.PaymentPremiumPPB, @@ -40,12 +40,49 @@ func AutomationDefaultRegistryConfig(c tc.AutomationTestConfig) contracts.Keeper MaxPerformGas: *registrySettings.MaxPerformGas, FallbackGasPrice: registrySettings.FallbackGasPrice, FallbackLinkPrice: registrySettings.FallbackLinkPrice, + FallbackNativePrice: registrySettings.FallbackNativePrice, MaxCheckDataSize: *registrySettings.MaxCheckDataSize, MaxPerformDataSize: *registrySettings.MaxPerformDataSize, MaxRevertDataSize: *registrySettings.MaxRevertDataSize, } } +func ReadPluginConfig(c tc.AutomationTestConfig) ocr2keepers30config.OffchainConfig { + plCfg := c.GetAutomationConfig().AutomationConfig.PluginConfig + return ocr2keepers30config.OffchainConfig{ + TargetProbability: *plCfg.TargetProbability, + TargetInRounds: *plCfg.TargetInRounds, + PerformLockoutWindow: *plCfg.PerformLockoutWindow, + GasLimitPerReport: *plCfg.GasLimitPerReport, + GasOverheadPerUpkeep: *plCfg.GasOverheadPerUpkeep, + MinConfirmations: *plCfg.MinConfirmations, + MaxUpkeepBatchSize: *plCfg.MaxUpkeepBatchSize, + LogProviderConfig: ocr2keepers30config.LogProviderConfig{ + BlockRate: *plCfg.LogProviderConfig.BlockRate, + LogLimit: *plCfg.LogProviderConfig.LogLimit, + }, + } +} + +func ReadPublicConfig(c tc.AutomationTestConfig) ocr3.PublicConfig { + pubCfg := c.GetAutomationConfig().AutomationConfig.PublicConfig + return ocr3.PublicConfig{ + DeltaProgress: *pubCfg.DeltaProgress, + DeltaResend: *pubCfg.DeltaResend, + DeltaInitial: *pubCfg.DeltaInitial, + DeltaRound: *pubCfg.DeltaRound, + DeltaGrace: *pubCfg.DeltaGrace, + DeltaCertifiedCommitRequest: *pubCfg.DeltaCertifiedCommitRequest, + DeltaStage: *pubCfg.DeltaStage, + RMax: *pubCfg.RMax, + MaxDurationQuery: *pubCfg.MaxDurationQuery, + MaxDurationObservation: *pubCfg.MaxDurationObservation, + MaxDurationShouldAcceptAttestedReport: *pubCfg.MaxDurationShouldAcceptAttestedReport, + MaxDurationShouldTransmitAcceptedReport: *pubCfg.MaxDurationShouldTransmitAcceptedReport, + F: *pubCfg.F, + } +} + func BuildAutoOCR2ConfigVarsLocal( l zerolog.Logger, chainlinkNodes []*client.ChainlinkClient, diff --git a/integration-tests/actions/automationv2/actions.go b/integration-tests/actions/automationv2/actions.go index 0aebec546db..6ce35873d88 100644 --- a/integration-tests/actions/automationv2/actions.go +++ b/integration-tests/actions/automationv2/actions.go @@ -860,6 +860,14 @@ func (a *AutomationTest) SetupMercuryMock(t *testing.T, imposters []ctfTestEnv.K } func (a *AutomationTest) SetupAutomationDeployment(t *testing.T) { + a.setupDeployment(t, true) +} + +func (a *AutomationTest) SetupAutomationDeploymentWithoutJobs(t *testing.T) { + a.setupDeployment(t, false) +} + +func (a *AutomationTest) setupDeployment(t *testing.T, addJobs bool) { l := logging.GetTestLogger(t) err := a.CollectNodeDetails() require.NoError(t, err, "Error collecting node details") @@ -891,7 +899,9 @@ func (a *AutomationTest) SetupAutomationDeployment(t *testing.T) { err = a.DeployRegistrar() require.NoError(t, err, "Error deploying registrar contract") - a.AddJobsAndSetConfig(t) + if addJobs { + a.AddJobsAndSetConfig(t) + } } func (a *AutomationTest) LoadAutomationDeployment(t *testing.T, linkTokenAddress, diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/automation_test.go similarity index 80% rename from integration-tests/benchmark/keeper_test.go rename to integration-tests/benchmark/automation_test.go index 5c7ce23761e..534fca2f513 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/automation_test.go @@ -21,7 +21,6 @@ import ( sethutils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" ethcontracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" @@ -75,7 +74,7 @@ func TestAutomationBenchmark(t *testing.T) { testType, err := tc.GetConfigurationNameFromEnv() require.NoError(t, err, "Error getting test type") - config, err := tc.GetConfig([]string{testType}, tc.Keeper) + config, err := tc.GetConfig([]string{testType}, tc.Automation) require.NoError(t, err, "Error getting test config") testEnvironment, benchmarkNetwork := SetupAutomationBenchmarkEnv(t, &config) @@ -83,7 +82,7 @@ func TestAutomationBenchmark(t *testing.T) { return } networkName := strings.ReplaceAll(benchmarkNetwork.Name, " ", "") - testName := fmt.Sprintf("%s%s", networkName, *config.Keeper.Common.RegistryToTest) + testName := fmt.Sprintf("%s%s", networkName, *config.Automation.Benchmark.RegistryToTest) l.Info().Str("Test Name", testName).Msg("Running Benchmark Test") benchmarkTestNetwork := getNetworkConfig(&config) @@ -94,47 +93,27 @@ func TestAutomationBenchmark(t *testing.T) { require.NoError(t, err, "Error getting Seth client") registryVersions := addRegistry(&config) + registrySettings := actions.ReadRegistryConfig(config) keeperBenchmarkTest := testsetups.NewKeeperBenchmarkTest(t, testsetups.KeeperBenchmarkTestInputs{ - BlockchainClient: chainClient, - RegistryVersions: registryVersions, - KeeperRegistrySettings: &contracts.KeeperRegistrySettings{ - PaymentPremiumPPB: uint32(0), - FlatFeeMicroLINK: uint32(40000), - BlockCountPerTurn: big.NewInt(100), - CheckGasLimit: uint32(45_000_000), //45M - StalenessSeconds: big.NewInt(90_000), - GasCeilingMultiplier: uint16(2), - MaxPerformGas: uint32(*config.Keeper.Common.MaxPerformGas), - MinUpkeepSpend: big.NewInt(0), - FallbackGasPrice: big.NewInt(2e11), - FallbackLinkPrice: big.NewInt(2e18), - MaxCheckDataSize: uint32(5_000), - MaxPerformDataSize: uint32(5_000), - MaxRevertDataSize: uint32(5_000), - }, + BlockchainClient: chainClient, + RegistryVersions: registryVersions, + KeeperRegistrySettings: ®istrySettings, Upkeeps: &testsetups.UpkeepConfig{ - NumberOfUpkeeps: *config.Keeper.Common.NumberOfUpkeeps, - CheckGasToBurn: *config.Keeper.Common.CheckGasToBurn, - PerformGasToBurn: *config.Keeper.Common.PerformGasToBurn, - BlockRange: *config.Keeper.Common.BlockRange, - BlockInterval: *config.Keeper.Common.BlockInterval, - UpkeepGasLimit: *config.Keeper.Common.UpkeepGasLimit, + NumberOfUpkeeps: *config.Automation.Benchmark.NumberOfUpkeeps, + CheckGasToBurn: *config.Automation.Benchmark.CheckGasToBurn, + PerformGasToBurn: *config.Automation.Benchmark.PerformGasToBurn, + BlockRange: *config.Automation.Benchmark.BlockRange, + BlockInterval: *config.Automation.Benchmark.BlockInterval, + UpkeepGasLimit: *config.Automation.Benchmark.UpkeepGasLimit, FirstEligibleBuffer: 1, }, - Contracts: &testsetups.PreDeployedContracts{ - RegistrarAddress: *config.Keeper.Common.RegistrarAddress, - RegistryAddress: *config.Keeper.Common.RegistryAddress, - LinkTokenAddress: *config.Keeper.Common.LinkTokenAddress, - EthFeedAddress: *config.Keeper.Common.EthFeedAddress, - GasFeedAddress: *config.Keeper.Common.GasFeedAddress, - }, ChainlinkNodeFunding: benchmarkTestNetwork.funding, UpkeepSLA: benchmarkTestNetwork.upkeepSLA, BlockTime: benchmarkTestNetwork.blockTime, DeltaStage: benchmarkTestNetwork.deltaStage, - ForceSingleTxnKey: *config.Keeper.Common.ForceSingleTxKey, - DeleteJobsOnEnd: *config.Keeper.Common.DeleteJobsOnEnd, + ForceSingleTxnKey: *config.Automation.Benchmark.ForceSingleTxKey, + DeleteJobsOnEnd: *config.Automation.Benchmark.DeleteJobsOnEnd, }, ) t.Cleanup(func() { @@ -152,13 +131,7 @@ func TestAutomationBenchmark(t *testing.T) { } func addRegistry(config *tc.TestConfig) []ethcontracts.KeeperRegistryVersion { - switch *config.Keeper.Common.RegistryToTest { - case "1_1": - return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_1_1} - case "1_2": - return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_1_2} - case "1_3": - return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_1_3} + switch *config.Automation.Benchmark.RegistryToTest { case "2_0": return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_0} case "2_1": @@ -167,19 +140,14 @@ func addRegistry(config *tc.TestConfig) []ethcontracts.KeeperRegistryVersion { return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_2} case "2_3": return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_3} - case "2_0-1_3": - return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_0, ethcontracts.RegistryVersion_1_3} - case "2_1-2_0-1_3": - return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_1, - ethcontracts.RegistryVersion_2_0, ethcontracts.RegistryVersion_1_3} case "2_2-2_1": return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_2, ethcontracts.RegistryVersion_2_1} case "2_0-Multiple": - return repeatRegistries(ethcontracts.RegistryVersion_2_0, *config.Keeper.Common.NumberOfRegistries) + return repeatRegistries(ethcontracts.RegistryVersion_2_0, *config.Automation.Benchmark.NumberOfRegistries) case "2_1-Multiple": - return repeatRegistries(ethcontracts.RegistryVersion_2_1, *config.Keeper.Common.NumberOfRegistries) + return repeatRegistries(ethcontracts.RegistryVersion_2_1, *config.Automation.Benchmark.NumberOfRegistries) case "2_2-Multiple": - return repeatRegistries(ethcontracts.RegistryVersion_2_2, *config.Keeper.Common.NumberOfRegistries) + return repeatRegistries(ethcontracts.RegistryVersion_2_2, *config.Automation.Benchmark.NumberOfRegistries) default: return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_0} } @@ -275,13 +243,13 @@ var networkConfig = map[string]NetworkConfig{ }, } -func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.KeeperBenchmarkTestConfig) (*environment.Environment, blockchain.EVMNetwork) { +func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.AutomationBenchmarkTestConfig) (*environment.Environment, blockchain.EVMNetwork) { l := logging.GetTestLogger(t) testNetwork := networks.MustGetSelectedNetworkConfig(keeperTestConfig.GetNetworkConfig())[0] // Environment currently being used to run benchmark test on blockTime := "1" - numberOfNodes := *keeperTestConfig.GetKeeperConfig().Common.NumberOfNodes + numberOfNodes := *keeperTestConfig.GetAutomationConfig().General.NumberOfNodes - if strings.Contains(*keeperTestConfig.GetKeeperConfig().Common.RegistryToTest, "2_") { + if strings.Contains(*keeperTestConfig.GetAutomationConfig().Benchmark.RegistryToTest, "2_") { numberOfNodes++ } @@ -295,7 +263,7 @@ func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.KeeperBenc "automation-%s-%s-%s", strings.ToLower(strings.Join(keeperTestConfig.GetConfigurationNames(), "")), strings.ReplaceAll(strings.ToLower(testNetwork.Name), " ", "-"), - strings.ReplaceAll(strings.ToLower(*keeperTestConfig.GetKeeperConfig().Common.RegistryToTest), "_", "-"), + strings.ReplaceAll(strings.ToLower(*keeperTestConfig.GetAutomationConfig().Benchmark.RegistryToTest), "_", "-"), ), Test: t, PreventPodEviction: true, diff --git a/integration-tests/ccip-tests/README.md b/integration-tests/ccip-tests/README.md index 1abca8552b0..a4255928949 100644 --- a/integration-tests/ccip-tests/README.md +++ b/integration-tests/ccip-tests/README.md @@ -119,3 +119,9 @@ flowchart ### Using Remote Kubernetes Cluster For running more complex and intensive tests (like load and chaos tests) you need to connect the test to a Kubernetes cluster. These tests have more complex setup and running instructions. We endeavor to make these easier to run and configure, but for the time being please seek a member of the QA/Test Tooling team if you want to run these. + +### Live environment testing + +To run against live environments, use the configs available under [beta-testnet](./testconfig/tomls/beta-testnet) +or [prod-testnet](./testconfig/tomls/prod-testnet) directory. Prod testnet has configs for smoke, load and +soak test separately and beta-testnet has smoke and soak combined. \ No newline at end of file diff --git a/integration-tests/ccip-tests/actions/ccip_helpers.go b/integration-tests/ccip-tests/actions/ccip_helpers.go index 429751e169e..06334560e05 100644 --- a/integration-tests/ccip-tests/actions/ccip_helpers.go +++ b/integration-tests/ccip-tests/actions/ccip_helpers.go @@ -185,6 +185,7 @@ type CCIPCommon struct { gasUpdateWatcherMu *sync.Mutex gasUpdateWatcher map[uint64]*big.Int // key - destchain id; value - timestamp of update GasUpdateEvents []contracts.GasUpdateEvent + AllowOutOfOrder bool } // FreeUpUnusedSpace sets nil to various elements of ccipModule which are only used @@ -273,6 +274,9 @@ func (ccipModule *CCIPCommon) CurseARM() (*types.Transaction, error) { func (ccipModule *CCIPCommon) LoadContractAddresses(conf *laneconfig.LaneConfig, noOfTokens *int) { if conf != nil { + if conf.AllowOutOfOrder { + ccipModule.AllowOutOfOrder = true + } if common.IsHexAddress(conf.FeeToken) { ccipModule.FeeToken = &contracts.LinkToken{ EthAddress: common.HexToAddress(conf.FeeToken), @@ -1626,7 +1630,7 @@ func (sourceCCIP *SourceCCIPModule) IsRequestTriggeredWithinTimeframe(timeframe if sendRequestedEvents, exists := value.([]*evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested); exists { for _, sendRequestedEvent := range sendRequestedEvents { raw := sendRequestedEvent.Raw - hdr, err := sourceCCIP.Common.ChainClient.HeaderByNumber(context.Background(), big.NewInt(int64(raw.BlockNumber))) + hdr, err := sourceCCIP.Common.ChainClient.HeaderByNumber(context.Background(), new(big.Int).SetUint64(raw.BlockNumber)) if err == nil { if hdr.Timestamp.After(lastSeenTimestamp) { foundAt = pointer.ToTime(hdr.Timestamp) @@ -1709,6 +1713,7 @@ func (sourceCCIP *SourceCCIPModule) AssertEventCCIPSendRequested( // CCIPMsg constructs the message for a CCIP request func (sourceCCIP *SourceCCIPModule) CCIPMsg( receiver common.Address, + allowOutOfOrder bool, gasLimit *big.Int, ) (router.ClientEVM2AnyMessage, error) { length := sourceCCIP.MsgDataLength @@ -1750,9 +1755,17 @@ func (sourceCCIP *SourceCCIPModule) CCIPMsg( return router.ClientEVM2AnyMessage{}, fmt.Errorf("failed encoding the receiver address: %w", err) } - extraArgsV1, err := testhelpers.GetEVMExtraArgsV1(gasLimit, false) + var extraArgs []byte + matchErr := contracts.MatchContractVersionsOrAbove(map[contracts.Name]contracts.Version{ + contracts.OnRampContract: contracts.V1_5_0, + }) + if matchErr != nil { + extraArgs, err = testhelpers.GetEVMExtraArgsV1(gasLimit, false) + } else { + extraArgs, err = testhelpers.GetEVMExtraArgsV2(gasLimit, allowOutOfOrder) + } if err != nil { - return router.ClientEVM2AnyMessage{}, fmt.Errorf("failed encoding the options field: %w", err) + return router.ClientEVM2AnyMessage{}, fmt.Errorf("failed getting extra args: %w", err) } // form the message for transfer return router.ClientEVM2AnyMessage{ @@ -1760,7 +1773,7 @@ func (sourceCCIP *SourceCCIPModule) CCIPMsg( Data: []byte(data), TokenAmounts: tokenAndAmounts, FeeToken: common.HexToAddress(sourceCCIP.Common.FeeToken.Address()), - ExtraArgs: extraArgsV1, + ExtraArgs: extraArgs, }, nil } @@ -1775,7 +1788,7 @@ func (sourceCCIP *SourceCCIPModule) SendRequest( return common.Hash{}, d, nil, fmt.Errorf("failed getting the chain selector: %w", err) } // form the message for transfer - msg, err := sourceCCIP.CCIPMsg(receiver, gasLimit) + msg, err := sourceCCIP.CCIPMsg(receiver, sourceCCIP.Common.AllowOutOfOrder, gasLimit) if err != nil { return common.Hash{}, d, nil, fmt.Errorf("failed forming the ccip msg: %w", err) } @@ -2218,7 +2231,7 @@ func (destCCIP *DestCCIPModule) AssertNoReportAcceptedEventReceived(lggr *zerolo e, exists := value.(*evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged) if exists { vLogs := e.Raw - hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(ctx, big.NewInt(int64(vLogs.BlockNumber))) + hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(ctx, new(big.Int).SetUint64(vLogs.BlockNumber)) if err != nil { return true } @@ -2259,7 +2272,7 @@ func (destCCIP *DestCCIPModule) AssertNoExecutionStateChangedEventReceived( e, exists := value.(*contracts.EVM2EVMOffRampExecutionStateChanged) if exists { vLogs := e.LogInfo - hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(ctx, big.NewInt(int64(vLogs.BlockNumber))) + hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(ctx, new(big.Int).SetUint64(vLogs.BlockNumber)) if err != nil { return true } @@ -2288,7 +2301,7 @@ func (destCCIP *DestCCIPModule) AssertEventExecutionStateChanged( reqStat *testreporters.RequestStat, execState testhelpers.MessageExecutionState, ) (uint8, error) { - lggr.Info().Int64("seqNum", int64(seqNum)).Str("Timeout", timeout.String()).Msg("Waiting for ExecutionStateChanged event") + lggr.Info().Uint64("seqNum", seqNum).Str("Timeout", timeout.String()).Msg("Waiting for ExecutionStateChanged event") timer := time.NewTimer(timeout) defer timer.Stop() ticker := time.NewTicker(time.Second) @@ -2306,7 +2319,7 @@ func (destCCIP *DestCCIPModule) AssertEventExecutionStateChanged( destCCIP.ExecStateChangedWatcher.Delete(seqNum) vLogs := e.LogInfo receivedAt := time.Now().UTC() - hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(context.Background(), big.NewInt(int64(vLogs.BlockNumber))) + hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(context.Background(), new(big.Int).SetUint64(vLogs.BlockNumber)) if err == nil { receivedAt = hdr.Timestamp } @@ -2319,7 +2332,7 @@ func (destCCIP *DestCCIPModule) AssertEventExecutionStateChanged( gasUsed = receipt.GasUsed } if testhelpers.MessageExecutionState(e.State) == execState { - lggr.Info().Int64("seqNum", int64(seqNum)).Uint8("ExecutionState", e.State).Msg("ExecutionStateChanged event received") + lggr.Info().Uint64("seqNum", seqNum).Uint8("ExecutionState", e.State).Msg("ExecutionStateChanged event received") reqStat.UpdateState(lggr, seqNum, testreporters.ExecStateChanged, receivedAt.Sub(timeNow), testreporters.Success, &testreporters.TransactionStats{ @@ -2363,7 +2376,7 @@ func (destCCIP *DestCCIPModule) AssertEventReportAccepted( prevEventAt time.Time, reqStat *testreporters.RequestStat, ) (*contracts.CommitStoreReportAccepted, time.Time, error) { - lggr.Info().Int64("seqNum", int64(seqNum)).Str("Timeout", timeout.String()).Msg("Waiting for ReportAccepted event") + lggr.Info().Uint64("seqNum", seqNum).Str("Timeout", timeout.String()).Msg("Waiting for ReportAccepted event") timer := time.NewTimer(timeout) defer timer.Stop() resetTimerCount := 0 @@ -2379,7 +2392,7 @@ func (destCCIP *DestCCIPModule) AssertEventReportAccepted( // if the value is processed, delete it from the map destCCIP.ReportAcceptedWatcher.Delete(seqNum) receivedAt := time.Now().UTC() - hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(context.Background(), big.NewInt(int64(reportAccepted.LogInfo.BlockNumber))) + hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(context.Background(), new(big.Int).SetUint64(reportAccepted.LogInfo.BlockNumber)) if err == nil { receivedAt = hdr.Timestamp } @@ -2488,7 +2501,7 @@ func (destCCIP *DestCCIPModule) AssertReportBlessed( // if the value is processed, delete it from the map destCCIP.ReportBlessedBySeqNum.Delete(seqNum) } - hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(context.Background(), big.NewInt(int64(vLogs.BlockNumber))) + hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(context.Background(), new(big.Int).SetUint64(vLogs.BlockNumber)) if err == nil { receivedAt = hdr.Timestamp } @@ -2536,7 +2549,7 @@ func (destCCIP *DestCCIPModule) AssertSeqNumberExecuted( timeNow time.Time, reqStat *testreporters.RequestStat, ) error { - lggr.Info().Int64("seqNum", int64(seqNumberBefore)).Str("Timeout", timeout.String()).Msg("Waiting to be processed by commit store") + lggr.Info().Uint64("seqNum", seqNumberBefore).Str("Timeout", timeout.String()).Msg("Waiting to be processed by commit store") timer := time.NewTimer(timeout) defer timer.Stop() resetTimerCount := 0 @@ -2793,7 +2806,11 @@ func (lane *CCIPLane) AddToSentReqs(txHash common.Hash, reqStats []*testreporter func (lane *CCIPLane) Multicall(noOfRequests int, multiSendAddr common.Address) error { var ccipMultipleMsg []contracts.CCIPMsgData feeToken := common.HexToAddress(lane.Source.Common.FeeToken.Address()) - genericMsg, err := lane.Source.CCIPMsg(lane.Dest.ReceiverDapp.EthAddress, big.NewInt(DefaultDestinationGasLimit)) + genericMsg, err := lane.Source.CCIPMsg( + lane.Dest.ReceiverDapp.EthAddress, + lane.Source.Common.AllowOutOfOrder, + big.NewInt(DefaultDestinationGasLimit), + ) if err != nil { return fmt.Errorf("failed to form the ccip message: %w", err) } @@ -2885,10 +2902,7 @@ func (lane *CCIPLane) Multicall(noOfRequests int, multiSendAddr common.Address) func (lane *CCIPLane) SendRequests(noOfRequests int, gasLimit *big.Int) error { for i := 1; i <= noOfRequests; i++ { stat := testreporters.NewCCIPRequestStats(int64(lane.NumberOfReq+i), lane.SourceNetworkName, lane.DestNetworkName) - txHash, txConfirmationDur, fee, err := lane.Source.SendRequest( - lane.Dest.ReceiverDapp.EthAddress, - gasLimit, - ) + txHash, txConfirmationDur, fee, err := lane.Source.SendRequest(lane.Dest.ReceiverDapp.EthAddress, gasLimit) if err != nil { stat.UpdateState(lane.Logger, 0, testreporters.TX, txConfirmationDur, testreporters.Failure, nil) return fmt.Errorf("could not send request: %w", err) @@ -3515,6 +3529,7 @@ func (lane *CCIPLane) DeployNewCCIPLane( srcConf = lane.SrcNetworkLaneCfg destConf = lane.DstNetworkLaneCfg commitAndExecOnSameDON = pointer.GetBool(testConf.CommitAndExecuteOnSameDON) + allowOutOfOrder = pointer.GetBool(testConf.AllowOutOfOrder) withPipeline = pointer.GetBool(testConf.TokenConfig.WithPipeline) configureCLNodes = !pointer.GetBool(testConf.ExistingDeployment) ) @@ -3529,6 +3544,13 @@ func (lane *CCIPLane) DeployNewCCIPLane( if err != nil { return fmt.Errorf("failed to create source module: %w", err) } + + // If AllowOutOfOrder is set globally in test config, the assumption is to set it for every lane. + // However, if this is set as false, and set as true for specific chain in lane_configs, then apply it + // only for a lane where source network is of that chain. + if allowOutOfOrder { + lane.Source.Common.AllowOutOfOrder = true + } lane.Dest, err = DefaultDestinationCCIPModule( lane.Logger, testConf, destChainClient, sourceChainClient.GetChainID().Uint64(), @@ -3781,12 +3803,18 @@ func SetOCR2Config( nodes = execNodes } if destCCIP.OffRamp != nil { + // Use out of order batching strategy if we expect to be sending out of order messages + batchingStrategyID := uint32(0) + if pointer.GetBool(testConf.AllowOutOfOrder) { + batchingStrategyID = uint32(1) + } execOffchainCfg, err := contracts.NewExecOffchainConfig( 1, BatchGasLimit, 0.7, *inflightExpiryExec, *commonconfig.MustNewDuration(RootSnoozeTime), + batchingStrategyID, ) if err != nil { return fmt.Errorf("failed to create exec offchain config: %w", err) diff --git a/integration-tests/ccip-tests/contracts/contract_deployer.go b/integration-tests/ccip-tests/contracts/contract_deployer.go index 211bbc1ebb4..579d16da4fc 100644 --- a/integration-tests/ccip-tests/contracts/contract_deployer.go +++ b/integration-tests/ccip-tests/contracts/contract_deployer.go @@ -77,7 +77,7 @@ func MatchContractVersionsOrAbove(requiredContractVersions map[Name]Version) err // if the version is less than 1.5.0, then token admin registry is not needed func NeedTokenAdminRegistry() bool { return MatchContractVersionsOrAbove(map[Name]Version{ - TokenPoolContract: V1_5_0_dev, + TokenPoolContract: V1_5_0, }) == nil } @@ -1044,6 +1044,7 @@ func (e *CCIPContractsDeployer) DeployOnRamp( MaxPerMsgGasLimit: 4_000_000, DefaultTokenFeeUSDCents: 50, DefaultTokenDestGasOverhead: 125_000, + EnforceOutOfOrder: false, }, evm_2_evm_onramp.RateLimiterConfig{ Capacity: opts.Capacity, @@ -1465,12 +1466,14 @@ func NewExecOnchainConfig( } } +// NewExecOffchainConfig creates a config for the OffChain portion of how CCIP operates func NewExecOffchainConfig( destOptimisticConfirmations uint32, batchGasLimit uint32, relativeBoostPerWaitHour float64, inflightCacheExpiry config.Duration, rootSnoozeTime config.Duration, + batchingStrategyID uint32, // See ccipexec package ) (ccipconfig.OffchainConfig, error) { switch VersionMap[OffRampContract] { case Latest: @@ -1480,6 +1483,7 @@ func NewExecOffchainConfig( relativeBoostPerWaitHour, inflightCacheExpiry, rootSnoozeTime, + batchingStrategyID, ), nil case V1_2_0: return testhelpers_1_4_0.NewExecOffchainConfig( @@ -1488,6 +1492,7 @@ func NewExecOffchainConfig( relativeBoostPerWaitHour, inflightCacheExpiry, rootSnoozeTime, + batchingStrategyID, ), nil default: return nil, fmt.Errorf("version not supported: %s", VersionMap[OffRampContract]) diff --git a/integration-tests/ccip-tests/contracts/contract_models.go b/integration-tests/ccip-tests/contracts/contract_models.go index e3f6e45fad2..83fe12a60a6 100644 --- a/integration-tests/ccip-tests/contracts/contract_models.go +++ b/integration-tests/ccip-tests/contracts/contract_models.go @@ -112,9 +112,9 @@ const ( var ( V1_2_0 = MustVersion("1.2.0") V1_4_0 = MustVersion("1.4.0") - V1_5_0_dev = MustVersion("1.5.0") - LatestPoolVersion = V1_5_0_dev - Latest = V1_5_0_dev + V1_5_0 = MustVersion("1.5.0") + LatestPoolVersion = V1_5_0 + Latest = V1_5_0 VersionMap = map[Name]Version{ PriceRegistryContract: V1_2_0, OffRampContract: Latest, @@ -1616,22 +1616,58 @@ func (w OnRampWrapper) ParseCCIPSendRequested(l types.Log) (uint64, error) { return 0, fmt.Errorf("no instance found to parse CCIPSendRequested") } -func (w OnRampWrapper) GetDynamicConfig(opts *bind.CallOpts) (uint32, error) { +// GetDynamicConfig retrieves the dynamic config for the onramp +func (w OnRampWrapper) GetDynamicConfig(opts *bind.CallOpts) (evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig, error) { if w.Latest != nil { cfg, err := w.Latest.GetDynamicConfig(opts) if err != nil { - return 0, err + return evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig{}, err } - return cfg.MaxDataBytes, nil + return cfg, nil } if w.V1_2_0 != nil { cfg, err := w.V1_2_0.GetDynamicConfig(opts) if err != nil { - return 0, err + return evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig{}, err } - return cfg.MaxDataBytes, nil + return evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig{ + Router: cfg.Router, + MaxNumberOfTokensPerMsg: cfg.MaxNumberOfTokensPerMsg, + DestGasOverhead: cfg.DestGasOverhead, + DestGasPerPayloadByte: cfg.DestGasPerPayloadByte, + DestDataAvailabilityOverheadGas: cfg.DestDataAvailabilityOverheadGas, + DestGasPerDataAvailabilityByte: cfg.DestGasPerDataAvailabilityByte, + DestDataAvailabilityMultiplierBps: cfg.DestDataAvailabilityMultiplierBps, + PriceRegistry: cfg.PriceRegistry, + MaxDataBytes: cfg.MaxDataBytes, + MaxPerMsgGasLimit: cfg.MaxPerMsgGasLimit, + }, nil + } + return evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig{}, fmt.Errorf("no instance found to get dynamic config") +} + +// SetDynamicConfig sets the dynamic config for the onramp +// Note that you cannot set only a single field, you must set all fields or they will be reset to zero values +// You can use GetDynamicConfig to get the current config and modify it as needed +func (w OnRampWrapper) SetDynamicConfig(opts *bind.TransactOpts, dynamicConfig evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig) (*types.Transaction, error) { + if w.Latest != nil { + return w.Latest.SetDynamicConfig(opts, dynamicConfig) + } + if w.V1_2_0 != nil { + return w.V1_2_0.SetDynamicConfig(opts, evm_2_evm_onramp_1_2_0.EVM2EVMOnRampDynamicConfig{ + Router: dynamicConfig.Router, + MaxNumberOfTokensPerMsg: dynamicConfig.MaxNumberOfTokensPerMsg, + DestGasOverhead: dynamicConfig.DestGasOverhead, + DestGasPerPayloadByte: dynamicConfig.DestGasPerPayloadByte, + DestDataAvailabilityOverheadGas: dynamicConfig.DestDataAvailabilityOverheadGas, + DestGasPerDataAvailabilityByte: dynamicConfig.DestGasPerDataAvailabilityByte, + DestDataAvailabilityMultiplierBps: dynamicConfig.DestDataAvailabilityMultiplierBps, + PriceRegistry: dynamicConfig.PriceRegistry, + MaxDataBytes: dynamicConfig.MaxDataBytes, + MaxPerMsgGasLimit: dynamicConfig.MaxPerMsgGasLimit, + }) } - return 0, fmt.Errorf("no instance found to get dynamic config") + return nil, fmt.Errorf("no instance found to set dynamic config") } func (w OnRampWrapper) ApplyPoolUpdates(opts *bind.TransactOpts, tokens []common.Address, pools []common.Address) (*types.Transaction, error) { diff --git a/integration-tests/ccip-tests/contracts/laneconfig/parse_contracts.go b/integration-tests/ccip-tests/contracts/laneconfig/parse_contracts.go index 332bd48ab31..c96cb3dfdc6 100644 --- a/integration-tests/ccip-tests/contracts/laneconfig/parse_contracts.go +++ b/integration-tests/ccip-tests/contracts/laneconfig/parse_contracts.go @@ -21,6 +21,7 @@ var ( type CommonContracts struct { IsNativeFeeToken bool `json:"is_native_fee_token,omitempty"` + AllowOutOfOrder bool `json:"allow_out_of_order,omitempty"` // expected to set this value as True for ZK chain source networks IsMockARM bool `json:"is_mock_arm,omitempty"` FeeToken string `json:"fee_token"` BridgeTokens []string `json:"bridge_tokens,omitempty"` diff --git a/integration-tests/ccip-tests/load/ccip_loadgen.go b/integration-tests/ccip-tests/load/ccip_loadgen.go index 9dc8ce16e5c..bc7627b4e08 100644 --- a/integration-tests/ccip-tests/load/ccip_loadgen.go +++ b/integration-tests/ccip-tests/load/ccip_loadgen.go @@ -22,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testconfig" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" @@ -132,8 +133,9 @@ func (c *CCIPE2ELoad) BeforeAllCall() { c.EOAReceiver = c.msg.Receiver } if c.SendMaxDataIntermittentlyInMsgCount > 0 { - c.MaxDataBytes, err = sourceCCIP.OnRamp.Instance.GetDynamicConfig(nil) + cfg, err := sourceCCIP.OnRamp.Instance.GetDynamicConfig(nil) require.NoError(c.t, err, "failed to fetch dynamic config") + c.MaxDataBytes = cfg.MaxDataBytes } // if the msg is sent via multicall, transfer the token transfer amount to multicall contract if sourceCCIP.Common.MulticallEnabled && @@ -189,11 +191,27 @@ func (c *CCIPE2ELoad) CCIPMsg() (router.ClientEVM2AnyMessage, *testreporters.Req if !msgDetails.IsTokenTransfer() { msg.TokenAmounts = []router.ClientEVMTokenAmount{} } - extraArgsV1, err := testhelpers.GetEVMExtraArgsV1(big.NewInt(gasLimit), false) + + var ( + extraArgs []byte + err error + ) + matchErr := contracts.MatchContractVersionsOrAbove(map[contracts.Name]contracts.Version{ + contracts.OnRampContract: contracts.V1_5_0, + }) + // TODO: The CCIP Offchain upgrade tests make the AllowOutOfOrder flag tricky in this case. + // It runs with out of date contract versions at first, then upgrades them. So transactions will assume that the new contracts are there + // before being deployed. So setting v2 args will break the test. This is a bit of a hack to get around that. + // The test will soon be deprecated, so a temporary solution is fine. + if matchErr != nil || !c.Lane.Source.Common.AllowOutOfOrder { + extraArgs, err = testhelpers.GetEVMExtraArgsV1(big.NewInt(gasLimit), false) + } else { + extraArgs, err = testhelpers.GetEVMExtraArgsV2(big.NewInt(gasLimit), c.Lane.Source.Common.AllowOutOfOrder) + } if err != nil { return router.ClientEVM2AnyMessage{}, stats, err } - msg.ExtraArgs = extraArgsV1 + msg.ExtraArgs = extraArgs // if gaslimit is 0, set the receiver to EOA if gasLimit == 0 { msg.Receiver = c.EOAReceiver diff --git a/integration-tests/ccip-tests/load/helper.go b/integration-tests/ccip-tests/load/helper.go index e4ee4573b2a..34860c8871c 100644 --- a/integration-tests/ccip-tests/load/helper.go +++ b/integration-tests/ccip-tests/load/helper.go @@ -199,10 +199,7 @@ func (l *LoadArgs) ValidateCurseFollowedByUncurse() { // try to send requests on lanes on which curse is applied on source RMN and the request should revert // data-only transfer is sufficient lane.Source.TransferAmount = []*big.Int{} - failedTx, _, _, err := lane.Source.SendRequest( - lane.Dest.ReceiverDapp.EthAddress, - big.NewInt(actions.DefaultDestinationGasLimit), // gas limit - ) + failedTx, _, _, err := lane.Source.SendRequest(lane.Dest.ReceiverDapp.EthAddress, big.NewInt(actions.DefaultDestinationGasLimit)) if lane.Source.Common.ChainClient.GetNetworkConfig().MinimumConfirmations > 0 { require.Error(l.t, err) } else { diff --git a/integration-tests/ccip-tests/smoke/ccip_test.go b/integration-tests/ccip-tests/smoke/ccip_test.go index 0ed7f60dd3f..08054459481 100644 --- a/integration-tests/ccip-tests/smoke/ccip_test.go +++ b/integration-tests/ccip-tests/smoke/ccip_test.go @@ -244,10 +244,7 @@ func TestSmokeCCIPRateLimit(t *testing.T) { tc.lane.Source.Common.ChainClient.GetDefaultWallet(), src.Common.Router.Address(), src.TransferAmount[0]), ) require.NoError(t, tc.lane.Source.Common.ChainClient.WaitForEvents()) - failedTx, _, _, err := tc.lane.Source.SendRequest( - tc.lane.Dest.ReceiverDapp.EthAddress, - big.NewInt(actions.DefaultDestinationGasLimit), // gas limit - ) + failedTx, _, _, err := tc.lane.Source.SendRequest(tc.lane.Dest.ReceiverDapp.EthAddress, big.NewInt(actions.DefaultDestinationGasLimit)) require.NoError(t, err) require.Error(t, tc.lane.Source.Common.ChainClient.WaitForEvents()) errReason, v, err := tc.lane.Source.Common.ChainClient.RevertReasonFromTx(failedTx, evm_2_evm_onramp.EVM2EVMOnRampABI) @@ -272,10 +269,7 @@ func TestSmokeCCIPRateLimit(t *testing.T) { // try to send again with amount more than the amount refilled by rate and // this should fail, as the refill rate is not enough to refill the capacity src.TransferAmount[0] = new(big.Int).Mul(AggregatedRateLimitRate, big.NewInt(10)) - failedTx, _, _, err = tc.lane.Source.SendRequest( - tc.lane.Dest.ReceiverDapp.EthAddress, - big.NewInt(actions.DefaultDestinationGasLimit), // gas limit - ) + failedTx, _, _, err = tc.lane.Source.SendRequest(tc.lane.Dest.ReceiverDapp.EthAddress, big.NewInt(actions.DefaultDestinationGasLimit)) tc.lane.Logger.Info().Str("tokensToSend", src.TransferAmount[0].String()).Msg("More than Aggregated Rate") require.NoError(t, err) require.Error(t, tc.lane.Source.Common.ChainClient.WaitForEvents()) @@ -339,10 +333,7 @@ func TestSmokeCCIPRateLimit(t *testing.T) { src.TransferAmount[0] = tokensToSend tc.lane.Logger.Info().Str("tokensToSend", tokensToSend.String()).Msg("More than Token Pool Capacity") - failedTx, _, _, err = tc.lane.Source.SendRequest( - tc.lane.Dest.ReceiverDapp.EthAddress, - big.NewInt(actions.DefaultDestinationGasLimit), // gas limit - ) + failedTx, _, _, err = tc.lane.Source.SendRequest(tc.lane.Dest.ReceiverDapp.EthAddress, big.NewInt(actions.DefaultDestinationGasLimit)) require.NoError(t, err) require.Error(t, tc.lane.Source.Common.ChainClient.WaitForEvents()) errReason, v, err = tc.lane.Source.Common.ChainClient.RevertReasonFromTx(failedTx, lock_release_token_pool.LockReleaseTokenPoolABI) @@ -374,10 +365,7 @@ func TestSmokeCCIPRateLimit(t *testing.T) { src.Common.ChainClient.GetDefaultWallet(), src.Common.Router.Address(), src.TransferAmount[0]), ) require.NoError(t, tc.lane.Source.Common.ChainClient.WaitForEvents()) - failedTx, _, _, err = tc.lane.Source.SendRequest( - tc.lane.Dest.ReceiverDapp.EthAddress, - big.NewInt(actions.DefaultDestinationGasLimit), - ) + failedTx, _, _, err = tc.lane.Source.SendRequest(tc.lane.Dest.ReceiverDapp.EthAddress, big.NewInt(actions.DefaultDestinationGasLimit)) require.NoError(t, err) require.Error(t, tc.lane.Source.Common.ChainClient.WaitForEvents()) errReason, v, err = tc.lane.Source.Common.ChainClient.RevertReasonFromTx(failedTx, lock_release_token_pool.LockReleaseTokenPoolABI) @@ -406,8 +394,8 @@ func TestSmokeCCIPOnRampLimits(t *testing.T) { "This test modifies contract state. Before running it, ensure you are willing and able to do so.", ) err := contracts.MatchContractVersionsOrAbove(map[contracts.Name]contracts.Version{ - contracts.OffRampContract: contracts.V1_5_0_dev, - contracts.OnRampContract: contracts.V1_5_0_dev, + contracts.OffRampContract: contracts.V1_5_0, + contracts.OnRampContract: contracts.V1_5_0, }) require.NoError(t, err, "Required contract versions not met") @@ -627,8 +615,8 @@ func TestSmokeCCIPTokenPoolRateLimits(t *testing.T) { "This test modifies contract state. Before running it, ensure you are willing and able to do so.", ) err := contracts.MatchContractVersionsOrAbove(map[contracts.Name]contracts.Version{ - contracts.OffRampContract: contracts.V1_5_0_dev, - contracts.OnRampContract: contracts.V1_5_0_dev, + contracts.OffRampContract: contracts.V1_5_0, + contracts.OnRampContract: contracts.V1_5_0, }) require.NoError(t, err, "Required contract versions not met") @@ -1006,7 +994,7 @@ func testOffRampRateLimits(t *testing.T, rateLimiterConfig contracts.RateLimiter "This test modifies contract state. Before running it, ensure you are willing and able to do so.", ) err := contracts.MatchContractVersionsOrAbove(map[contracts.Name]contracts.Version{ - contracts.OffRampContract: contracts.V1_5_0_dev, + contracts.OffRampContract: contracts.V1_5_0, }) require.NoError(t, err, "Required contract versions not met") require.False(t, pointer.GetBool(TestCfg.TestGroupInput.ExistingDeployment), "This test modifies contract state and cannot be run on existing deployments") diff --git a/integration-tests/ccip-tests/testconfig/README.md b/integration-tests/ccip-tests/testconfig/README.md index d464ae247ff..ff57ecaa220 100644 --- a/integration-tests/ccip-tests/testconfig/README.md +++ b/integration-tests/ccip-tests/testconfig/README.md @@ -480,6 +480,10 @@ Specifies whether to set up bi-directional lanes between networks. Specifies whether commit and execution jobs are to be run on the same Chainlink node. +### CCIP.Groups.[testgroup].AllowOutOfOrder + +Specifies whether out of order execution is allowed globally for all the chains. + ### CCIP.Groups.[testgroup].NoOfCommitNodes Specifies the number of nodes on which commit jobs are to be run. This needs to be lesser than the total number of nodes mentioned in [CCIP.Env.NewCLCluster.NoOfNodes](#ccipenvnewclclusternoofnodes) or [CCIP.Env.ExistingCLCluster.NoOfNodes](#ccipenvexistingclclusternoofnodes). diff --git a/integration-tests/ccip-tests/testconfig/ccip.go b/integration-tests/ccip-tests/testconfig/ccip.go index 19ebd304ceb..76e00ecf496 100644 --- a/integration-tests/ccip-tests/testconfig/ccip.go +++ b/integration-tests/ccip-tests/testconfig/ccip.go @@ -266,6 +266,7 @@ type CCIPTestGroupConfig struct { KeepEnvAlive *bool `toml:",omitempty"` BiDirectionalLane *bool `toml:",omitempty"` CommitAndExecuteOnSameDON *bool `toml:",omitempty"` + AllowOutOfOrder *bool `toml:",omitempty"` // To set out of order execution globally NoOfCommitNodes int `toml:",omitempty"` MsgDetails *MsgDetails `toml:",omitempty"` TokenConfig *TokenConfig `toml:",omitempty"` diff --git a/integration-tests/ccip-tests/testconfig/tomls/beta-testnet/testnet-beta-workinglane.toml b/integration-tests/ccip-tests/testconfig/tomls/beta-testnet/testnet-beta-workinglane.toml new file mode 100644 index 00000000000..592cb046ad3 --- /dev/null +++ b/integration-tests/ccip-tests/testconfig/tomls/beta-testnet/testnet-beta-workinglane.toml @@ -0,0 +1,283 @@ +[CCIP] +[CCIP.ContractVersions] +PriceRegistry = 'latest' +OffRamp = 'latest' +OnRamp = 'latest' +TokenPool = 'latest' +CommitStore = 'latest' + +[CCIP.Deployments] +Data = """ +{ + "lane_configs": { + "Arbitrum Sepolia": { + "is_mock_arm": true, + "fee_token": "0xb1D4538B4571d411F07960EF2838Ce337FE1E80E", + "bridge_tokens": null, + "bridge_tokens_pools": null, + "price_aggregators": null, + "arm": "0x2aE6d5495fc20226F433be50e37D59c05D186AaA", + "router": "0x0fF6b6F3Ad10D66600Fd5CC25b98542A05Aa7Bc2", + "price_registry": "0x25d997d8618e1299418b3D905E40bC353ec89F61", + "wrapped_native": "0xE591bf0A0CF924A0674d7792db046B23CEbF5f34", + "src_contracts": { + "Base Sepolia": { + "on_ramp": "0x6BD0f1efA261Ea84DB219c1284b538A65E530ea1", + "deployed_at": 29428386 + }, + "Optimism Sepolia": { + "on_ramp": "0x94cd0d171eF08924F0008305e5Bb90b0fC1b61AB", + "deployed_at": 13945916 + }, + "Sepolia Testnet": { + "on_ramp": "0x44225eb3B73B1b52Dd2ecD258F9b63418eC6Bf79", + "deployed_at": 13730868 + } + }, + "dest_contracts": { + "Base Sepolia": { + "off_ramp": "0x21560B4ACAEdb8AA2Dd935618F15da43197bdc12", + "commit_store": "0x27B882c393151ADD910F3557849AF0bb09c7d5A6", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xAE32FD8Ae148BD88E3da6FaE8Cd7561Eed3ec5Cc", + "commit_store": "0x1f1160Ac7828B647A85c9a6b3A58A232C59D67Ab", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xc136114F379b812345bb7e467ECDdb6D0c87De8b", + "commit_store": "0x42b3EbEA14F6CB803e3C7df84392Efb85CE90168", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Avalanche Fuji": { + "is_mock_arm": true, + "fee_token": "0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846", + "bridge_tokens": null, + "bridge_tokens_pools": null, + "price_aggregators": null, + "arm": "0xD4A51dC0F5C680A8A18eA4Ec3A2f25C6db9424B7", + "router": "0xa62e685aDFF45f38eC94378513D128F168964E99", + "price_registry": "0xdbeA1a10AC6a2B729bF128aE9281Ed420dbE7113", + "wrapped_native": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + "src_contracts": { + "BSC Testnet": { + "on_ramp": "0xe4f1F7750352f1c37C15C4A314554d6A79d7d146", + "deployed_at": 31437550 + } + }, + "dest_contracts": { + "BSC Testnet": { + "off_ramp": "0x796D720ea9D4326ff356eadE13b123B267C03C80", + "commit_store": "0xaDb37cFd91fa9b6Df1DaAcbAfB4cDFF41e06c956", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "BSC Testnet": { + "is_mock_arm": true, + "fee_token": "0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06", + "bridge_tokens": null, + "bridge_tokens_pools": null, + "price_aggregators": null, + "arm": "0xbBF534D89d9640e3886db25FE1ffE603Fe160D75", + "router": "0x9CdA5b77eA23459eBaf2e3092c570a6B5605850A", + "price_registry": "0x9213967a47FC3F15A16A0b813208e8Ccb63Dbba6", + "wrapped_native": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0x0B4F541a7fcE5c251993Bc19D5A40B661e0463f5", + "deployed_at": 39097639 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0x41E59DCdDec18d7f79DA5F76Ce567d2c5e301E6B", + "commit_store": "0x9487C01D4b3Ae1c9Ac8740A07f3862D646548A14", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Base Sepolia": { + "is_mock_arm": true, + "fee_token": "0xE4aB69C077896252FAFBD49EFD26B5D171A32410", + "bridge_tokens": null, + "bridge_tokens_pools": null, + "price_aggregators": null, + "arm": "0x866faB92E04bAE5EDa238A9cbFf1e56E09508Ade", + "router": "0x2aE6d5495fc20226F433be50e37D59c05D186AaA", + "price_registry": "0xD886E2286Fd1073df82462ea1822119600Af80b6", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0xAE32FD8Ae148BD88E3da6FaE8Cd7561Eed3ec5Cc", + "deployed_at": 8133125 + }, + "Optimism Sepolia": { + "on_ramp": "0x9213967a47FC3F15A16A0b813208e8Ccb63Dbba6", + "deployed_at": 11607777 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x34433469A4d6c8b1B0a1a7B91a5C5C2Dd74c67Fb", + "commit_store": "0xFEE7c8E229F538a98437b9A7D0Dd8fCd8A1Ab569", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0x0f30449bcCaCCaA7221B3f7C3304c4AaD68068E8", + "commit_store": "0x17a5746c9cf7eAf23533F060F395B2E38eb976ea", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Optimism Sepolia": { + "is_mock_arm": true, + "fee_token": "0xE4aB69C077896252FAFBD49EFD26B5D171A32410", + "bridge_tokens": null, + "bridge_tokens_pools": null, + "price_aggregators": null, + "arm": "0x2aE6d5495fc20226F433be50e37D59c05D186AaA", + "router": "0x0fF6b6F3Ad10D66600Fd5CC25b98542A05Aa7Bc2", + "price_registry": "0x3B80b7Ef5c00Eb892CBe72800C028C47AD6380EF", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x622CB640F52bFfA68b78b2BD12c1940Ca4899621", + "deployed_at": 8020540 + }, + "Base Sepolia": { + "on_ramp": "0x12c164d0778E215873A062cEE2814507417339cB", + "deployed_at": 13590651 + }, + "Sepolia Testnet": { + "on_ramp": "0x0c2c8D4266C98f1b9333D5E1a42f3f775A0005d4", + "deployed_at": 8020948 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x37004c1245a2D5541377e87cA29699492a4114D5", + "commit_store": "0x51158Ca439feA9E809Bc063CfA6701747b05254e", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0xB3F3f362FbeD49fA0086B434051C822B55BaADbD", + "commit_store": "0xD4995B99c484CCABc868b26c0B2C2Ef10ecde3d7", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x80C2aa80F202FeFdFEEF80f516cFd89768c54057", + "commit_store": "0xc1fE981A040D679511ccb9139ca107aCA67520ef", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Sepolia Testnet": { + "is_mock_arm": true, + "fee_token": "0x779877A7B0D9E8603169DdbD7836e478b4624789", + "bridge_tokens": null, + "bridge_tokens_pools": null, + "price_aggregators": null, + "arm": "0x9912a7389382ff55f85A29C9378B38F7B992c4aE", + "router": "0x1E1F3d8Ac7Df65fCcFcc52dbF03929cEE95430ac", + "price_registry": "0x4358e81f88bB27222779c1BC85003A11A1c66f6F", + "wrapped_native": "0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x420a7B5ABB8CF27A70E1906F797e24509B11093D", + "deployed_at": 5275652 + }, + "Optimism Sepolia": { + "on_ramp": "0xEb4EBC1930bA81416A48a59142D89722163D85ae", + "deployed_at": 5281150 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x224D1eB3aB2b7F23b66f093F9cBBC68dA77a1986", + "commit_store": "0x35c54cF12FF9B29dBa60dc23EdD1de0F13CC7fc5", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xF21d01D6Ef822FBC56FC6c8F23f74fE3A0cb39aa", + "commit_store": "0x7F6AF440Bcc54f70Fd8AC2E534d37196c0bA1A38", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + } + } +} +""" + +[CCIP.Env] +TTL = '8h' + +[CCIP.Env.Network] +selected_networks = [ + 'ARBITRUM_SEPOLIA', + 'AVALANCHE_FUJI', + 'BASE_SEPOLIA', + 'BSC_TESTNET', + 'OPTIMISM_SEPOLIA', + 'SEPOLIA' +] + + + +[CCIP.Groups.smoke] +# these are all the valid network pairs +NetworkPairs = [ + 'BSC_TESTNET,AVALANCHE_FUJI', + 'SEPOLIA,ARBITRUM_SEPOLIA', + 'BASE_SEPOLIA,OPTIMISM_SEPOLIA' +] + +BiDirectionalLane = true +PhaseTimeout = '40m' +LocalCluster = false +ExistingDeployment = true +ReuseContracts = true + + +[CCIP.Groups.smoke.TokenConfig] +NoOfTokensPerChain = 1 +CCIPOwnerTokens = true + +[CCIP.Groups.smoke.MsgDetails] +MsgType = 'Data' +DestGasLimit = 0 +DataLength = 100 +NoOfTokens = 1 +AmountPerToken = 1 + +[CCIP.Groups.load] +NetworkPairs = [ + 'AVALANCHE_FUJI,BSC_TESTNET', + 'SEPOLIA,ARBITRUM_SEPOLIA', + 'BASE_SEPOLIA,OPTIMISM_SEPOLIA' +] + +BiDirectionalLane = true +PhaseTimeout = '40m' +ExistingDeployment = true + +[CCIP.Groups.load.TokenConfig] +NoOfTokensPerChain = 1 + +# Soak Test profile +[CCIP.Groups.load.LoadProfile] +RequestPerUnitTime = [1] +TimeUnit = '6m' +TestDuration = '3h' +TestRunName = 'BetaSoakTest_CCIPV1dot5' +FailOnFirstErrorInLoad = true + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'Data' +DestGasLimit = 0 +DataLength = 100 +NoOfTokens = 1 +AmountPerToken = 1 \ No newline at end of file diff --git a/integration-tests/ccip-tests/testconfig/tomls/beta-testnet/testnet-beta-workinglane_native.toml b/integration-tests/ccip-tests/testconfig/tomls/beta-testnet/testnet-beta-workinglane_native.toml new file mode 100644 index 00000000000..fcc063c7659 --- /dev/null +++ b/integration-tests/ccip-tests/testconfig/tomls/beta-testnet/testnet-beta-workinglane_native.toml @@ -0,0 +1,295 @@ +[CCIP] +[CCIP.ContractVersions] +PriceRegistry = 'latest' +OffRamp = 'latest' +OnRamp = 'latest' +TokenPool = 'latest' +CommitStore = 'latest' + +[CCIP.Deployments] +Data = """ +{ + "lane_configs": { + "Arbitrum Sepolia": { + "is_native_fee_token": true, + "is_mock_arm": true, + "fee_token": "0xE591bf0A0CF924A0674d7792db046B23CEbF5f34", + "bridge_tokens": null, + "bridge_tokens_pools": null, + "price_aggregators": null, + "arm": "0x2aE6d5495fc20226F433be50e37D59c05D186AaA", + "router": "0x0fF6b6F3Ad10D66600Fd5CC25b98542A05Aa7Bc2", + "price_registry": "0x25d997d8618e1299418b3D905E40bC353ec89F61", + "wrapped_native": "0xE591bf0A0CF924A0674d7792db046B23CEbF5f34", + "src_contracts": { + "Base Sepolia": { + "on_ramp": "0x6BD0f1efA261Ea84DB219c1284b538A65E530ea1", + "deployed_at": 29428386 + }, + "Optimism Sepolia": { + "on_ramp": "0x94cd0d171eF08924F0008305e5Bb90b0fC1b61AB", + "deployed_at": 13945916 + }, + "Sepolia Testnet": { + "on_ramp": "0x44225eb3B73B1b52Dd2ecD258F9b63418eC6Bf79", + "deployed_at": 13730868 + } + }, + "dest_contracts": { + "Base Sepolia": { + "off_ramp": "0x21560B4ACAEdb8AA2Dd935618F15da43197bdc12", + "commit_store": "0x27B882c393151ADD910F3557849AF0bb09c7d5A6", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xAE32FD8Ae148BD88E3da6FaE8Cd7561Eed3ec5Cc", + "commit_store": "0x1f1160Ac7828B647A85c9a6b3A58A232C59D67Ab", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xc136114F379b812345bb7e467ECDdb6D0c87De8b", + "commit_store": "0x42b3EbEA14F6CB803e3C7df84392Efb85CE90168", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Avalanche Fuji": { + "is_native_fee_token": true, + "is_mock_arm": true, + "fee_token": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + "bridge_tokens": [ + "0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846" + ], + "bridge_tokens_pools": [ + "0x156943ae87AaF63eA9272902Cb05407ec7bc9464" + ], + "price_aggregators": null, + "arm": "0xD4A51dC0F5C680A8A18eA4Ec3A2f25C6db9424B7", + "router": "0xa62e685aDFF45f38eC94378513D128F168964E99", + "price_registry": "0xdbeA1a10AC6a2B729bF128aE9281Ed420dbE7113", + "wrapped_native": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + "src_contracts": { + "BSC Testnet": { + "on_ramp": "0xe4f1F7750352f1c37C15C4A314554d6A79d7d146", + "deployed_at": 31437550 + } + }, + "dest_contracts": { + "BSC Testnet": { + "off_ramp": "0x796D720ea9D4326ff356eadE13b123B267C03C80", + "commit_store": "0xaDb37cFd91fa9b6Df1DaAcbAfB4cDFF41e06c956", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "BSC Testnet": { + "is_native_fee_token": true, + "is_mock_arm": true, + "fee_token": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", + "bridge_tokens": null, + "bridge_tokens_pools": null, + "price_aggregators": null, + "arm": "0xbBF534D89d9640e3886db25FE1ffE603Fe160D75", + "router": "0x9CdA5b77eA23459eBaf2e3092c570a6B5605850A", + "price_registry": "0x9213967a47FC3F15A16A0b813208e8Ccb63Dbba6", + "wrapped_native": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0x0B4F541a7fcE5c251993Bc19D5A40B661e0463f5", + "deployed_at": 39097639 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0x41E59DCdDec18d7f79DA5F76Ce567d2c5e301E6B", + "commit_store": "0x9487C01D4b3Ae1c9Ac8740A07f3862D646548A14", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Base Sepolia": { + "is_native_fee_token": true, + "is_mock_arm": true, + "fee_token": "0x4200000000000000000000000000000000000006", + "bridge_tokens": null, + "bridge_tokens_pools": null, + "price_aggregators": null, + "arm": "0x866faB92E04bAE5EDa238A9cbFf1e56E09508Ade", + "router": "0x2aE6d5495fc20226F433be50e37D59c05D186AaA", + "price_registry": "0xD886E2286Fd1073df82462ea1822119600Af80b6", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0xAE32FD8Ae148BD88E3da6FaE8Cd7561Eed3ec5Cc", + "deployed_at": 8133125 + }, + "Optimism Sepolia": { + "on_ramp": "0x9213967a47FC3F15A16A0b813208e8Ccb63Dbba6", + "deployed_at": 11607777 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x34433469A4d6c8b1B0a1a7B91a5C5C2Dd74c67Fb", + "commit_store": "0xFEE7c8E229F538a98437b9A7D0Dd8fCd8A1Ab569", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0x0f30449bcCaCCaA7221B3f7C3304c4AaD68068E8", + "commit_store": "0x17a5746c9cf7eAf23533F060F395B2E38eb976ea", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Optimism Sepolia": { + "is_native_fee_token": true, + "is_mock_arm": true, + "fee_token": "0x4200000000000000000000000000000000000006", + "bridge_tokens": null, + "bridge_tokens_pools": null, + "price_aggregators": null, + "arm": "0x2aE6d5495fc20226F433be50e37D59c05D186AaA", + "router": "0x0fF6b6F3Ad10D66600Fd5CC25b98542A05Aa7Bc2", + "price_registry": "0x3B80b7Ef5c00Eb892CBe72800C028C47AD6380EF", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x622CB640F52bFfA68b78b2BD12c1940Ca4899621", + "deployed_at": 8020540 + }, + "Base Sepolia": { + "on_ramp": "0x12c164d0778E215873A062cEE2814507417339cB", + "deployed_at": 13590651 + }, + "Sepolia Testnet": { + "on_ramp": "0x0c2c8D4266C98f1b9333D5E1a42f3f775A0005d4", + "deployed_at": 8020948 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x37004c1245a2D5541377e87cA29699492a4114D5", + "commit_store": "0x51158Ca439feA9E809Bc063CfA6701747b05254e", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0xB3F3f362FbeD49fA0086B434051C822B55BaADbD", + "commit_store": "0xD4995B99c484CCABc868b26c0B2C2Ef10ecde3d7", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x80C2aa80F202FeFdFEEF80f516cFd89768c54057", + "commit_store": "0xc1fE981A040D679511ccb9139ca107aCA67520ef", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Sepolia Testnet": { + "is_native_fee_token": true, + "is_mock_arm": true, + "fee_token": "0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534", + "bridge_tokens": null, + "bridge_tokens_pools": null, + "price_aggregators": null, + "arm": "0x9912a7389382ff55f85A29C9378B38F7B992c4aE", + "router": "0x1E1F3d8Ac7Df65fCcFcc52dbF03929cEE95430ac", + "price_registry": "0x4358e81f88bB27222779c1BC85003A11A1c66f6F", + "wrapped_native": "0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x420a7B5ABB8CF27A70E1906F797e24509B11093D", + "deployed_at": 5275652 + }, + "Optimism Sepolia": { + "on_ramp": "0xEb4EBC1930bA81416A48a59142D89722163D85ae", + "deployed_at": 5281150 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x224D1eB3aB2b7F23b66f093F9cBBC68dA77a1986", + "commit_store": "0x35c54cF12FF9B29dBa60dc23EdD1de0F13CC7fc5", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xF21d01D6Ef822FBC56FC6c8F23f74fE3A0cb39aa", + "commit_store": "0x7F6AF440Bcc54f70Fd8AC2E534d37196c0bA1A38", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + } + } +} +""" + +[CCIP.Env] +TTL = '8h' + +[CCIP.Env.Network] +selected_networks = [ + 'ARBITRUM_SEPOLIA', + 'AVALANCHE_FUJI', + 'BASE_SEPOLIA', + 'BSC_TESTNET', + 'OPTIMISM_SEPOLIA', + 'SEPOLIA', +] + + +[CCIP.Env.NewCLCluster.Common.ChainlinkImage] +version = "sha-17ce920" + +[CCIP.Groups.smoke] +# these are all the valid network pairs +NetworkPairs = [ + 'AVALANCHE_FUJI,BSC_TESTNET', + 'SEPOLIA,ARBITRUM_SEPOLIA', + 'BASE_SEPOLIA,OPTIMISM_SEPOLIA', +] + +BiDirectionalLane = true +PhaseTimeout = '30m' +LocalCluster = false +ExistingDeployment = true +ReuseContracts = true + + +[CCIP.Groups.smoke.TokenConfig] +NoOfTokensPerChain = 1 +CCIPOwnerTokens = true + +[CCIP.Groups.smoke.MsgDetails] +MsgType = 'Data' +DestGasLimit = 0 +DataLength = 100 +NoOfTokens = 1 +AmountPerToken = 1 + +[CCIP.Groups.load] +NetworkPairs = [ + 'AVALANCHE_FUJI,BSC_TESTNET', + 'SEPOLIA,ARBITRUM_SEPOLIA', + 'BASE_SEPOLIA,OPTIMISM_SEPOLIA' +] + +BiDirectionalLane = true +PhaseTimeout = '40m' +ExistingDeployment = true + +[CCIP.Groups.load.TokenConfig] +NoOfTokensPerChain = 1 + +# Soak Test profile +[CCIP.Groups.load.LoadProfile] +RequestPerUnitTime = [1] +TimeUnit = '6m' +TestDuration = '3h' +TestRunName = 'BetaSoakTest_V2.14.0-1.5.1' +FailOnFirstErrorInLoad = true + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'Data' +DestGasLimit = 0 +DataLength = 100 +NoOfTokens = 1 +AmountPerToken = 1 \ No newline at end of file diff --git a/integration-tests/ccip-tests/testconfig/tomls/ccip-default.toml b/integration-tests/ccip-tests/testconfig/tomls/ccip-default.toml index 0157ac24fb4..b03f03a6dab 100644 --- a/integration-tests/ccip-tests/testconfig/tomls/ccip-default.toml +++ b/integration-tests/ccip-tests/testconfig/tomls/ccip-default.toml @@ -27,7 +27,7 @@ ethereum_version = "eth1" # geth, besu, erigon or nethermind execution_layer = "geth" # eth2-only, if set to true environment startup will wait until at least 1 epoch has been finalised -wait_for_finalization=false +wait_for_finalization = false [CCIP.Env.PrivateEthereumNetworks.SIMULATED_1.EthereumChainConfig] # eth2-only, the lower the value the faster the block production (3 is minimum) @@ -239,6 +239,8 @@ EIP1559FeeCapBufferBlocks = 0 KeepEnvAlive = false # if true, the test will not tear down the test environment after the test is finished CommitAndExecuteOnSameDON = true # if true, and the test is building the env from scratch, same chainlink nodes will be used for Commit and Execution jobs. +AllowOutOfOrder = false # if true, all the lanes will allow out of order execution and it +# overrides settings from lane_config. To allow out of order execution per lane, then send "allow_out_of_order":true, similar to is_native_fee_token variable. # Otherwise Commit and execution jobs will be set up in different nodes based on the number of nodes specified in NoOfCommitNodes and CCIP.Env.NewCLCluster.NoOfNodes BiDirectionalLane = true # True uses both the lanes. If bidirectional is false only one way lane is set up. NoOfCommitNodes = 5 # no of chainlink nodes with Commit job @@ -271,7 +273,7 @@ TimeoutForPriceUpdate = '15m' # Duration to wait for the price update to time-ou # Now testing only with dynamic price getter (no pipeline). # Could be removed once the pipeline is completely removed. WithPipeline = false -NoOfTokensPerChain = 2 # number of bridge tokens to be deployed per network; if MsgType = 'Token'/'DataWithToken' +NoOfTokensPerChain = 2 # number of bridge tokens to be deployed per network; if MsgType = 'Token'/'DataWithToken' CCIPOwnerTokens = false # if true, the test will use deploy the tokens by the CCIPOwner, otherwise the tokens will be deployed by a non-owner account, only applicable for 1.5 pools and onwards #NoOfTokensWithDynamicPrice = 15 # number of tokens with dynamic price to be deployed @@ -293,6 +295,7 @@ CCIPOwnerTokens = false # if true, the test will use deploy the tokens by the CC KeepEnvAlive = false # same as above CommitAndExecuteOnSameDON = true # same as above +AllowOutOfOrder = false # same as above BiDirectionalLane = true # same as above NoOfCommitNodes = 5 # same as above PhaseTimeout = '10m' # same as above @@ -351,7 +354,7 @@ TimeoutForPriceUpdate = '15m' # Duration to wait for the price update to time-ou # Now testing only with dynamic price getter (no pipeline). # Could be removed once the pipeline is completely removed. WithPipeline = false -NoOfTokensPerChain = 2 # number of bridge tokens to be deployed per network; if MsgType = 'Token'/'DataWithToken' +NoOfTokensPerChain = 2 # number of bridge tokens to be deployed per network; if MsgType = 'Token'/'DataWithToken' CCIPOwnerTokens = false # if true, the test will use deploy the tokens by the CCIPOwner, otherwise the tokens and pools will be deployed by a non-owner account, # only applicable for 1.5 pools and onwards, if you are running with pre-1.5 pools, then set this to true to deploy token pools by CCIPOwner, otherwise # the test will fail @@ -403,6 +406,7 @@ CCIPOwnerTokens = false # if true, the test will use deploy the tokens by the CC #NetworkPairs = ['SEPOLIA,OPTIMISM_GOERLI','SEPOLIA,POLYGON_MUMBAI','AVALANCHE_FUJI,SEPOLIA','SEPOLIA,BASE_GOERLI','SEPOLIA,BSC_TESTNET','SEPOLIA,WEMIX_TESTNET','AVALANCHE_FUJI,OPTIMISM_GOERLI','AVALANCHE_FUJI,POLYGON_MUMBAI','AVALANCHE_FUJI,BSC_TESTNET','AVALANCHE_FUJI,BASE_GOERLI','OPTIMISM_GOERLI,BASE_GOERLI','OPTIMISM_GOERLI,POLYGON_MUMBAI','BSC_TESTNET,POLYGON_MUMBAI','BSC_TESTNET,BASE_GOERLI','WEMIX_TESTNET,KROMA_SEPOLIA'] KeepEnvAlive = false CommitAndExecuteOnSameDON = false +AllowOutOfOrder = false BiDirectionalLane = true NoOfCommitNodes = 5 PhaseTimeout = '50m' diff --git a/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress/prod-testnet.toml b/integration-tests/ccip-tests/testconfig/tomls/prod-testnet/load-prod-testnet.toml similarity index 99% rename from integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress/prod-testnet.toml rename to integration-tests/ccip-tests/testconfig/tomls/prod-testnet/load-prod-testnet.toml index f8321584c84..f13965d5e67 100644 --- a/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress/prod-testnet.toml +++ b/integration-tests/ccip-tests/testconfig/tomls/prod-testnet/load-prod-testnet.toml @@ -933,7 +933,7 @@ NoOfTokensPerChain = 1 RequestPerUnitTime = [1] TimeUnit = '5s' TestDuration = '1h' -TestRunName = 'v2.12.0-ccip1.4.16-load' +TestRunName = 'ccip-prod-testnet-stress' # to represent 20%, 60%, 15%, 5% of the total messages [CCIP.Groups.load.LoadProfile.MsgProfile] diff --git a/integration-tests/ccip-tests/testconfig/tomls/prod-testnet/smoke-release-testing_native.toml b/integration-tests/ccip-tests/testconfig/tomls/prod-testnet/smoke-release-testing_native.toml new file mode 100644 index 00000000000..24e7cbc9c88 --- /dev/null +++ b/integration-tests/ccip-tests/testconfig/tomls/prod-testnet/smoke-release-testing_native.toml @@ -0,0 +1,1035 @@ +[CCIP] +[CCIP.ContractVersions] +PriceRegistry = 'latest' +OffRamp = 'latest' +OnRamp = 'latest' +CommitStore = 'latest' +TokenPool = 'latest' + + +[CCIP.Deployments] +Data = """ +{ + "lane_configs": { + "Arbitrum Sepolia": { + "is_native_fee_token": true, + "fee_token": "0xE591bf0A0CF924A0674d7792db046B23CEbF5f34", + "bridge_tokens": [ + "0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D" + ], + "bridge_tokens_pools": [ + "0x99685281Ec520a003F1A726A5a8078c2124c1477" + ], + "arm": "0xbcBDf0aDEDC9a33ED5338Bdb4B6F7CE664DC2e8B", + "router": "0x2a9C5afB0d0e4BAb2BCdaE109EC4b0c4Be15a165", + "price_registry": "0x89D5b13908b9063abCC6791dc724bF7B7c93634C", + "wrapped_native": "0xE591bf0A0CF924A0674d7792db046B23CEbF5f34", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0x1Cb56374296ED19E86F68fA437ee679FD7798DaA", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x7854E73C73e7F9bb5b0D5B4861E997f4C6E8dcC6", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x973CbE752258D32AE82b60CD1CB656Eebb588dF0", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0x701Fe16916dd21EFE2f535CA59611D818B017877", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x4205E1Ca0202A248A5D42F5975A8FE56F3E302e9", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0xBD4106fBE4699FE212A34Cc21b10BFf22b02d959", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0xcab0EF91Bee323d1A617c0a027eE753aFd6997E4", + "commit_store": "0x0d90b9b96cBFa0D01635ce12982ccE1b70827c7a", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0xc1982985720B959E66c19b64F783361Eb9B60F26", + "commit_store": "0x28F66bB336f6db713d6ad2a3bd1B7a531282A159", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x935C26F9a9122E5F9a27f2d3803e74c75B94f5a3", + "commit_store": "0xEdb963Ec5c2E5AbdFdCF137eF44A445a7fa4787A", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xfD404A89e1d195F0c65be1A9042C77745197659e", + "commit_store": "0x84B7B012c95f8A152B44Ab3e952f2dEE424fA8e1", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x1c71f141b4630EBE52d6aF4894812960abE207eB", + "commit_store": "0xaB0c8Ba51E7Fa3E5693a4Fbb39473520FD85d173", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0x262e16C8D42aa07bE13e58F81e7D9F62F6DE2830", + "commit_store": "0xc132eFAf929299E5ee704Fa6D9796CFa23Bb8b2C", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Avalanche Fuji": { + "is_native_fee_token": true, + "fee_token": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + "bridge_tokens": [ + "0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4" + ], + "bridge_tokens_pools": [ + "0xEC1062cbDf4fBf31B3A6Aac62B6F6F123bb70E12" + ], + "arm": "0x7e28DD790214139798446A121cFe950B51304684", + "router": "0xF694E193200268f9a4868e4Aa017A0118C9a8177", + "price_registry": "0x19e157E5fb1DAec1aE4BaB113fdf077F980704AA", + "wrapped_native": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x8bB16BEDbFd62D1f905ACe8DBBF2954c8EEB4f66", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xF25ECF1Aad9B2E43EDc2960cF66f325783245535", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x1A674645f3EB4147543FCA7d40C5719cbd997362", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x1532e5b204ee2b2244170c78E743CB9c168F4DF9", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0xC334DE5b020e056d0fE766dE46e8d9f306Ffa1E2", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0x610F76A35E17DA4542518D85FfEa12645eF111Fc", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x5724B4Cc39a9690135F7273b44Dfd3BA6c0c69aD", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0x677B5ab5C8522d929166c064d5700F147b15fa33", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x90A74072e7B0c2d59e13aB4d8f93c8198c413194", + "commit_store": "0xf3458CFd2fdf4a6CF0Ce296d520DD21eB194828b", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0x10b28009E5D776F1f5AAA73941CE8953B8f42d26", + "commit_store": "0xacDD582F271eCF22FAd6764cCDe1c4a534b732A8", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0xdBdE8510226d1E060A3bf982b67705C67f5697e2", + "commit_store": "0x8Ee73BC9492b4182D289E5C1e66e40CD876CC00F", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x56dF55aF5F0A4689f3364230587a68eD6A314fAd", + "commit_store": "0xabA7ff98094c4cc7A075812EefF2CD21f6400235", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0x3d7CbC95DCC33257F14D6Eb780c88Bd56C6335BB", + "commit_store": "0x1fcDC02edDfb405f378ba53cF9E6104feBcB7542", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x3e33290B90fD0FF30a3FA138934DF028E4eCA348", + "commit_store": "0xCFe3556Aa42d40be09BD23aa80448a19443BE5B1", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x9e5e4324F8608D54A50a317832d456a392E4F8C2", + "commit_store": "0x92A51eD3F041B39EbD1e464C1f7cb1e8f8A8c63f", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0xD0D338318bC6837b091FC7AB5F2a94B7783507d5", + "commit_store": "0xd9D479208235c7355848ff4aF26eB5aacfDC30c6", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "BSC Testnet": { + "is_native_fee_token": true, + "fee_token": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", + "bridge_tokens": [ + "0xbFA2ACd33ED6EEc0ed3Cc06bF1ac38d22b36B9e9" + ], + "bridge_tokens_pools": [ + "0x31eDe84776DA37e2404eE88d71c234e92cB672e5" + ], + "arm": "0x7D899D26F2E94fFcd4b440C3008B0C6BEfcD3cca", + "router": "0xE1053aE1857476f36A3C62580FF9b016E8EE8F6f", + "price_registry": "0xCCDf022c9d31DC26Ebab4FB92432724a5b79809a", + "wrapped_native": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0xa2515683E99F50ADbE177519A46bb20FfdBaA5de", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x3E807220Ca84b997c0d1928162227b46C618e0c5", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x8735f991d41eA9cA9D2CC75cD201e4B7C866E63e", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0xf37CcbfC04adc1B56a46B36F811D52C744a1AF78", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0xB1DE44B04C00eaFe9915a3C07a0CaeA4410537dF", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0x89268Afc1BEA0782a27ba84124E3F42b196af927", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0x6e6fFCb6B4BED91ff0CC8C2e57EC029dA7DB80C2", + "commit_store": "0x38Bc38Bd824b6eE87571f9D3CFbe6D6E28E3Dc62", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0x2C61FD7E93Dc79422861282145c59B56dFbc3a8c", + "commit_store": "0x42fAe5B3605804CF6d08632d7A25864e24F792Ae", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x71a44a60832B0F8B63232C9516e7E6aEc3A373Dc", + "commit_store": "0xAC24299a91b72d1Cb5B31147e3CF54964D896974", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x63440C7747d37bc6154b5538AE32b54FE0965AfA", + "commit_store": "0xAD22fA198CECfC534927aE1D480c460d5bB3460F", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xf1c128Fe52Ea78CcAAB407509292E61ce38C1523", + "commit_store": "0x59dFD870dC4bd76A7B879A4f705Fdcd2595f85f9", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0xfd9B19c3725da5B517aA705B848ff3f21F98280e", + "commit_store": "0x3c1F1412563188aBc8FE3fd53E8F1Cb601CaB4f9", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Base Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000006", + "bridge_tokens": [ + "0x88A2d74F47a237a62e7A51cdDa67270CE381555e" + ], + "bridge_tokens_pools": [ + "0x875207858c691F192C606068f417dCf666b2EC6B" + ], + "arm": "0x7827dD0481EE18DB646bD250d20A8eA43da52146", + "router": "0xD3b06cEbF099CE7DA4AcCf578aaebFDBd6e88a93", + "price_registry": "0x4D20536e60832bE579Cd38E89Dc03d11E1741FbA", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x58622a80c6DdDc072F2b527a99BE1D0934eb2b50", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0xAbA09a1b7b9f13E05A6241292a66793Ec7d43357", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xD806966beAB5A3C75E5B90CDA4a6922C6A9F0c9d", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x2Eff2d1BF5C557d6289D208a7a43608f5E3FeCc2", + "deployed_at": 0 + }, + "Mode Sepolia": { + "on_ramp": "0x3d0115386C01436870a2c47e6297962284E70BA6", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0x3b39Cd9599137f892Ad57A4f54158198D445D147", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x6486906bB2d85A6c0cCEf2A2831C11A2059ebfea", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xd364C06ac99a82a00d3eFF9F2F78E4Abe4b9baAA", + "commit_store": "0xdE8d0f47a71eA3fDFBD3162271652f2847939097", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0xAd91214efFee446500940c764DF77AF18427294F", + "commit_store": "0x1242b6c5e0e349b8d4BCf0938f961C4B4f7EA3Fa", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0xd5E9508921434e8758f4540D55c1c066b7cc1598", + "commit_store": "0x1a86b29364D1B3fA3386329A361aA98A104b2742", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x9Bb7e398ef9Acfe9cA584C39B1E233Cba62BB9f7", + "commit_store": "0x1F4B82cDebaC5e3a0Dd53183D47e51808B4a64cB", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Mode Sepolia": { + "off_ramp": "0xB26647A23e8b4284375e5C74b77c9557aE709D03", + "commit_store": "0x4b4fEB401d3E613e1D6242E155C83A80BF9ac2C9", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0x86a3910908eCaAA31Fcd9F0fC8841D8E98f1511d", + "commit_store": "0xE99a87C9b5ed4D2b6060195DEea5106ffF655736", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x189F61D9B886Dd2975D5Abc893c8Cf5f5effda71", + "commit_store": "0xEE7e27346DCD1e711348D0F7f7ECB53a9a3a08a7", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Blast Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000023", + "bridge_tokens": [ + "0x8D122C3e8ce9C8B62b87d3551bDfD8C259Bb0771" + ], + "bridge_tokens_pools": [ + "0xFb04129aD1EEDB741CC705ebC1978a7aB63e51f6" + ], + "arm": "0x09c1Ed4b112Fb33e594F2aACfEF407e2F14d7F9b", + "router": "0xfb2f2A207dC428da81fbAFfDDe121761f8Be1194", + "price_registry": "0xc8acE9dF450FaD007755C6C9AB4f0e9c8626E29C", + "wrapped_native": "0x4200000000000000000000000000000000000023", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x85Ef19FC4C63c70744995DC38CAAEC185E0c619f", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0x92cD24C278D34C726f377703E50875d8f9535dC2", + "commit_store": "0xcE1b4D50CeD56850182Bd58Ace91171cB249B873", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Celo Alfajores": { + "is_native_fee_token": true, + "fee_token": "0x99604d0e2EfE7ABFb58BdE565b5330Bb46Ab3Dca", + "bridge_tokens": [ + "0x7e503dd1dAF90117A1b79953321043d9E6815C72" + ], + "bridge_tokens_pools": [ + "0xC6683ac4a0F62803Bec89a5355B36495ddF2C38b" + ], + "arm": "0xEbe35aA4F5e707485484c992AF2069a457b9bBB1", + "router": "0xb00E95b773528E2Ea724DB06B75113F239D15Dca", + "price_registry": "0x8F048206D11B2c69b8963E2EBd5968D141e022f4", + "wrapped_native": "0x99604d0e2EfE7ABFb58BdE565b5330Bb46Ab3Dca", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x16a020c4bbdE363FaB8481262D30516AdbcfcFc8", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0xa1b97F92D806BA040daf419AFC2765DC723683a4", + "commit_store": "0xcd92C0599Ac515e7588865cC45Eee21A74816aFc", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Gnosis Chiado": { + "is_native_fee_token": true, + "fee_token": "0x18c8a7ec7897177E4529065a7E7B0878358B3BfF", + "bridge_tokens": [ + "0xA189971a2c5AcA0DFC5Ee7a2C44a2Ae27b3CF389" + ], + "bridge_tokens_pools": [ + "0xF9a21B587111e7E8745Fb8b13750014f19DB0014" + ], + "arm": "0xfE4fB161D870D0F672Ed9C5A898569603f77983F", + "router": "0x19b1bac554111517831ACadc0FD119D23Bb14391", + "price_registry": "0x2F4ACd1f8986c6B1788159C4c9a5fC3fceCCE363", + "wrapped_native": "0x18c8a7ec7897177E4529065a7E7B0878358B3BfF", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x473b49fb592B54a4BfCD55d40E048431982879C9", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0x610F76A35E17DA4542518D85FfEa12645eF111Fc", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xE48E6AA1fc7D0411acEA95F8C6CaD972A37721D4", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x41b4A51cAfb699D9504E89d19D71F92E886028a8", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0xAae733212981e06D9C978Eb5148F8af03F54b6EF", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0x01800fCDd892e37f7829937271840A6F041bE62E", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x4ac7FBEc2A7298AbDf0E0F4fDC45015836C4bAFe", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x9aA82DBB53bf02096B771D40e9432A323a78fB26", + "commit_store": "0x5CdbA91aBC0cD81FC56bc10Ad1835C9E5fB38e5F", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0x3e33290B90fD0FF30a3FA138934DF028E4eCA348", + "commit_store": "0xCFe3556Aa42d40be09BD23aa80448a19443BE5B1", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0xbc4AD54e91b213D4279af92c0C5518c0b96cf62D", + "commit_store": "0xff84e8Dd4Fd17eaBb23b6AeA6e1981830e54389C", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0x4117953A5ceeF12f5B8C1E973b470ab83a8CebA6", + "commit_store": "0x94ad41296186E81f31e1ed0B1BcF5fa9e1721C27", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0x33d2898F8fb7714FD1661791766f40754982a343", + "commit_store": "0x55d6Df194472f02CD481e506A277c4A29D0D1bCc", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x450543b1d85ca79885851D7b74dc982981b78229", + "commit_store": "0x23B79d940A769FE31b4C867A8BAE80117f24Ca81", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xbf9036529123DE264bFA0FC7362fE25B650D4B16", + "commit_store": "0x5f7F1abD5c5EdaF2636D58B980e85355AF0Ef80d", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Kroma Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000001", + "bridge_tokens": [ + "0x6AC3e353D1DDda24d5A5416024d6E436b8817A4e" + ], + "bridge_tokens_pools": [ + "0x0eE8add19554C7bb1920A183Ed47b4FAB9Eb7601" + ], + "arm": "0x08f9Af992368FAc58C936A2c5eBc9092894CEa9b", + "router": "0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D", + "price_registry": "0xa1ed3A3aA29166C9c8448654A8cA6b7916BC8379", + "wrapped_native": "0x4200000000000000000000000000000000000001", + "src_contracts": { + "WeMix Testnet": { + "on_ramp": "0x6ea155Fc77566D9dcE01B8aa5D7968665dc4f0C5", + "deployed_at": 0 + } + }, + "dest_contracts": { + "WeMix Testnet": { + "off_ramp": "0xB602B6E5Caf08ac0C920EAE585aed100a8cF6f3B", + "commit_store": "0x89D5b13908b9063abCC6791dc724bF7B7c93634C", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Metis Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x5c48e07062aC4E2Cf4b9A768a711Aef18e8fbdA0", + "bridge_tokens": [ + "0x20Aa09AAb761e2E600d65c6929A9fd1E59821D3f" + ], + "bridge_tokens_pools": [ + "0xdE8451E952Eb43350614839cCAA84f7C8701a09C" + ], + "arm": "0xf0607A9BDdB5F54dB59ACaA0837aFec2D1c95df6", + "router": "0xaCdaBa07ECad81dc634458b98673931DD9d3Bc14", + "price_registry": "0x5DCE866b3ae6E0Ed153f0e149D7203A1B266cdF5", + "wrapped_native": "0x5c48e07062aC4E2Cf4b9A768a711Aef18e8fbdA0", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x2Eff2d1BF5C557d6289D208a7a43608f5E3FeCc2", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0x9Bb7e398ef9Acfe9cA584C39B1E233Cba62BB9f7", + "commit_store": "0x1F4B82cDebaC5e3a0Dd53183D47e51808B4a64cB", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Mode Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000006", + "bridge_tokens": [ + "0xB9d4e1141E67ECFedC8A8139b5229b7FF2BF16F5" + ], + "bridge_tokens_pools": [ + "0x20bBc874bE3Cd94C3E4689EDD5D89dD1cE8Cb7C4" + ], + "arm": "0x9eC8a0AbC75ce08978FAf67958482461bCd93B18", + "router": "0xc49ec0eB4beb48B8Da4cceC51AA9A5bD0D0A4c43", + "price_registry": "0xa733ce82a84335b2E9D864312225B0F3D5d80600", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Base Sepolia": { + "on_ramp": "0x73f7E074bd7291706a0C5412f51DB46441B1aDCB", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0xfFdE9E8c34A27BEBeaCcAcB7b3044A0A364455C9", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Base Sepolia": { + "off_ramp": "0x137a38c6b1Ad20101F93516aB2159Df525309168", + "commit_store": "0x8F43d867969F14619895d71E0A5b89E0bb20bF70", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xcD44cec849B6a8eBd5551D6DFeEcA452257Dfe4d", + "commit_store": "0xbA66f08733E6715D33edDfb5a5947676bb45d0e0", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Optimism Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000006", + "bridge_tokens": [ + "0x8aF4204e30565DF93352fE8E1De78925F6664dA7" + ], + "bridge_tokens_pools": [ + "0x3Cc9364260D80F09ccAC1eE6B07366dB598900E6" + ], + "arm": "0xF51366F72184E22cF4a7a8362508DB0d3370392d", + "router": "0x114A20A10b43D4115e5aeef7345a1A71d2a60C57", + "price_registry": "0x782a7Ba95215f2F7c3dD4C153cbB2Ae3Ec2d3215", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x1a86b29364D1B3fA3386329A361aA98A104b2742", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0x6b38CC6Fa938D5AB09Bdf0CFe580E226fDD793cE", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0xe284D2315a28c4d62C419e8474dC457b219DB969", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x835a5b8e6CA17c2bB5A336c93a4E22478E6F1C8A", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0x2Cf26fb01E9ccDb831414B766287c0A9e4551089", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0xC8b93b46BF682c39B3F65Aa1c135bC8A95A5E43a", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0xc7E53f6aB982af7A7C3e470c8cCa283d3399BDAd", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xDc2c7A3d8068C6F09F0F3648d24C84e372F6014d", + "commit_store": "0xb1aFb5cbE3c29b5Db71F21442BA9EfD450BC23C3", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0x1F350718e015EB20E5065C09F4A7a3f66888aEeD", + "commit_store": "0x98650A8EB59f75D93563aB34FcF603b1A30e4CBF", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0x0a750ca77369e03613d7640548F4b2b1c695c3Bb", + "commit_store": "0x8fEBC74C26129C8d7E60288C6dCCc75eb494aA3C", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0xCE2CE7F940B7c839384e5D7e079A6aE80e8AD6dB", + "commit_store": "0x1b9D78Ec1CEEC439F0b7eA6C428A1a607D9FA7e4", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0xD667b5706592D0b040C78fEe5EE17D243b7dCB41", + "commit_store": "0x96101BA5250EE9295c193693C1e08A55bC593664", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x260AF9b83e0d2Bb6C9015fC9f0BfF8858A0CCE68", + "commit_store": "0x7a0bB92Bc8663abe6296d0162A9b41a2Cb2E0358", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0x9C08B7712af0344188aa5087D9e6aD0f47191037", + "commit_store": "0x4BE6DB0B884169a6A207fe5cad01eB4C025a13dB", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Polygon Amoy": { + "is_native_fee_token": true, + "fee_token": "0x360ad4f9a9A8EFe9A8DCB5f461c4Cc1047E1Dcf9", + "bridge_tokens": [ + "0xcab0EF91Bee323d1A617c0a027eE753aFd6997E4" + ], + "bridge_tokens_pools": [ + "0x3064fB3EA546EE09A63AB3bD93E83D8B8525C636" + ], + "arm": "0x8b88C39D2875157aB4CE4AD3814409523d539ee1", + "router": "0x9C32fCB86BF0f4a1A8921a9Fe46de3198bb884B2", + "price_registry": "0xfb2f2A207dC428da81fbAFfDDe121761f8Be1194", + "wrapped_native": "0x360ad4f9a9A8EFe9A8DCB5f461c4Cc1047E1Dcf9", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0x8Fb98b3837578aceEA32b454f3221FE18D7Ce903", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xC6683ac4a0F62803Bec89a5355B36495ddF2C38b", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x2331F6D614C9Fd613Ff59a1aB727f1EDf6c37A68", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0xA52cDAeb43803A80B3c0C2296f5cFe57e695BE11", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x35347A2fC1f2a4c5Eae03339040d0b83b09e6FDA", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0x26546096F64B5eF9A1DcDAe70Df6F4f8c2E10C61", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0xa733ce82a84335b2E9D864312225B0F3D5d80600", + "commit_store": "0x09B0F93fC2111aE439e853884173AC5b2F809885", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0x948dfaa4842fc23e0e362Fe8D4396AaE4E6DF7EA", + "commit_store": "0x7F4e739D40E58BBd59dAD388171d18e37B26326f", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x17c542a28e08AEF5697251601C7b2B621d153D42", + "commit_store": "0x811250c20fAB9a1b7ca245453aC214ba637fBEB5", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xfFdE9E8c34A27BEBeaCcAcB7b3044A0A364455C9", + "commit_store": "0x74ED442ad211050e9C05Dc9A267E037E3d74A03B", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xFb04129aD1EEDB741CC705ebC1978a7aB63e51f6", + "commit_store": "0x63f875240149d29136053C954Ca164a9BfA81F77", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0xdE8451E952Eb43350614839cCAA84f7C8701a09C", + "commit_store": "0xaCdaBa07ECad81dc634458b98673931DD9d3Bc14", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Sepolia Testnet": { + "is_native_fee_token": true, + "fee_token": "0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534", + "bridge_tokens": [ + "0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05" + ], + "bridge_tokens_pools": [ + "0x38d1ef9619Cd40cf5482C045660Ae7C82Ada062c" + ], + "arm": "0x27Da8735d8d1402cEc072C234759fbbB4dABBC4A", + "router": "0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59", + "price_registry": "0x9EF7D57a4ea30b9e37794E55b0C75F2A70275dCc", + "wrapped_native": "0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0xe4Dd3B16E09c016402585a8aDFdB4A18f772a07e", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0x0477cA0a35eE05D3f9f424d88bC0977ceCf339D4", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xD990f8aFA5BCB02f95eEd88ecB7C68f5998bD618", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x2B70a05320cB069e0fB55084D402343F832556E7", + "deployed_at": 0 + }, + "Blast Sepolia": { + "on_ramp": "0xDB75E9D9ca7577CcBd7232741be954cf26194a66", + "deployed_at": 0 + }, + "Celo Alfajores": { + "on_ramp": "0x3C86d16F52C10B2ff6696a0e1b8E0BcfCC085948", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x3E842E3A79A00AFdd03B52390B1caC6306Ea257E", + "deployed_at": 0 + }, + "Metis Sepolia": { + "on_ramp": "0x1C4640914cd57c5f02a68048A0fbb0E12d904223", + "deployed_at": 0 + }, + "Mode Sepolia": { + "on_ramp": "0xc630fbD4D0F6AEB00aD0793FB827b54fBB78e981", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0x69CaB5A0a08a12BaFD8f5B195989D709E396Ed4d", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0x9f656e0361Fb5Df2ac446102c8aB31855B591692", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0xedFc22336Eb0B9B11Ff37C07777db27BCcDe3C65", + "deployed_at": 0 + }, + "ZKSync Sepolia": { + "on_ramp": "0x1Acb3A885feA37bdA30AB99b99327b14391f500F", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xF18896AB20a09A29e64fdEbA99FDb8EC328f43b1", + "commit_store": "0x93Ff9Dd39Dc01eac1fc4d2c9211D95Ee458CAB94", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0x000b26f604eAadC3D874a4404bde6D64a97d95ca", + "commit_store": "0x2dD9273F8208B8393350508131270A6574A69784", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0xdE2d8E126e08d675fCD7fFa5a6CE49925f3Dc692", + "commit_store": "0x0050ac355a82caB31194507f94174297bf0655A7", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0x31c0B81832B333419f0DfD36A69F502cF9094aed", + "commit_store": "0xDFcde9d698a2B32DB2537DC9B752Cadd1D846a52", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Blast Sepolia": { + "off_ramp": "0x4e897e5cF3aC307F0541B2151A88bCD781c153a3", + "commit_store": "0xB656652841F347178e193951C4663652aCe36B74", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Celo Alfajores": { + "off_ramp": "0xB435E0f73c18C5a12C324CA1d02F81F2C3e6e761", + "commit_store": "0xbc5d74957F171e75F92c8F0E1C317A25a56a416D", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x7db0115A0b3AAb01d30bf81123c5DD7B0C41Add5", + "commit_store": "0x6640723Ea801178c4383FA016b9781e7ef1016EF", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Metis Sepolia": { + "off_ramp": "0x4DB693A93E9d5196ECD42EC56CDEAe99dFC652ED", + "commit_store": "0xBfACd78F1412B6f93Ac23409bf456aFec1ABd845", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Mode Sepolia": { + "off_ramp": "0xbEfd8D65F6643De54F0b1268A3bf4618ff85dcB4", + "commit_store": "0x0C161D3470b45Cc677661654C30ce4AdE6aCD288", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xD50590D4438411EDe47029b0FD7901A7145E5Df6", + "commit_store": "0xe85EEE9Fd434A7b8a586Ee086E828abF41839479", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x5032cbC0C4aEeD25bb6E45D8B3fAF05DB0688C5d", + "commit_store": "0xe6201C9996Cc7B6E828E10CbE937E693d577D318", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0x46b639a3C1a4CBfD326b94a2dB7415c27157282f", + "commit_store": "0x7b74554678816b045c1e7409327E086bD436aa46", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "ZKSync Sepolia": { + "off_ramp": "0xBaABd4166C892a1081a26535875A8fA3f22937b2", + "commit_store": "0xfda2e83F4D3f42B7629134ecD6E4b29FB8A7A07B", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "WeMix Testnet": { + "is_native_fee_token": true, + "fee_token": "0xbE3686643c05f00eC46e73da594c78098F7a9Ae7", + "bridge_tokens": [ + "0xF4E4057FbBc86915F4b2d63EEFFe641C03294ffc" + ], + "bridge_tokens_pools": [ + "0x82A92B2863F93Be70D20660088Ec060720bA2fdb" + ], + "arm": "0x8f6cb63eD5e379722580DFF0A051C140C64F9619", + "router": "0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D", + "price_registry": "0x89D17571DB7C9540eeB36760E3c749C8fb984569", + "wrapped_native": "0xbE3686643c05f00eC46e73da594c78098F7a9Ae7", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0xA9DE3F7A617D67bC50c56baaCb9E0373C15EbfC6", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0xC4aC84da458ba8e40210D2dF94C76E9a41f70069", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0x5AD6eed6Be0ffaDCA4105050CF0E584D87E0c2F1", + "deployed_at": 0 + }, + "Kroma Sepolia": { + "on_ramp": "0x428C4dc89b6Bf908B82d77C9CBceA786ea8cc7D0", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0x1961a7De751451F410391c251D4D4F98D71B767D", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0xd55148e841e76265B484d399eC71b7076ecB1216", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x4d57C6d8037C65fa66D6231844785a428310a735", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xeB1dFaB2464Bf0574D43e764E0c758f92e7ecAFb", + "commit_store": "0xcEaCa2B7890065c485f3E58657358a185Ad33791", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0x98e811Df9D2512f1aaf58D534607F583D6c54A4F", + "commit_store": "0x8e538351F6E5B2daF3c90C565C3738bca69a2716", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0xB0e7f0fCcD3c961C473E7c44D939C1cDb4Cec1cB", + "commit_store": "0x4B56D8d53f1A6e0117B09700067De99581aA5542", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Kroma Sepolia": { + "off_ramp": "0xD685D2d224dd6D0Db2D56497db6270D77D9a7966", + "commit_store": "0x7e062D6880779a0347e7742058C1b1Ee4AA0B137", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xA5f97Bc69Bf06e7C37B93265c5457420A92c5F4b", + "commit_store": "0xd48b9213583074f518D8f4336FDf35370D450132", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x6c8f5999B06FDE17B11E4e3C1062b761766F960f", + "commit_store": "0x957c3c2056192e58A8485eF31165fC490d474239", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x8AB103843ED9D28D2C5DAf5FdB9c3e1CE2B6c876", + "commit_store": "0x7d5297c5506ee2A7Ef121Da9bE02b6a6AD30b392", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "ZKSync Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4317b2eCD41851173175005783322D29E9bAee9E", + "arm": "0x926b4f90610Aa448f27c8e0Fd0afa4A17B7305F9", + "router": "0xA1fdA8aa9A8C4b945C45aD30647b01f07D7A0B16", + "price_registry": "0x648B6BB09bE1C5766C8AC578B9B4aC8497eA671F", + "wrapped_native": "0x4317b2eCD41851173175005783322D29E9bAee9E", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x79a9a5e9e318e8e109776569574814bbf935125a", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0x18FF69051479796175852578725Fc74F58E963F8", + "commit_store": "0xF694b4FD7889480dca3CD41244e8e3395a9EFBa0", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + } + } +} +""" + +[CCIP.Env] +TTL = '8h' + +[CCIP.Env.Network] +selected_networks = [ + 'ARBITRUM_SEPOLIA', + 'AVALANCHE_FUJI', + 'BASE_SEPOLIA', + 'BLAST_SEPOLIA', + 'BSC_TESTNET', + 'CELO_ALFAJORES', + 'GNOSIS_CHIADO', + 'KROMA_SEPOLIA', + 'MODE_SEPOLIA', + 'OPTIMISM_SEPOLIA', + 'POLYGON_AMOY', + 'SEPOLIA', + 'METIS_SEPOLIA', + 'WEMIX_TESTNET', + 'ZKSYNC_SEPOLIA' +] + +[CCIP.Groups.smoke] +# these are all the valid network pairs +NetworkPairs = [ + 'SEPOLIA,AVALANCHE_FUJI', + 'SEPOLIA,BSC_TESTNET', + 'SEPOLIA,CELO_ALFAJORES', + 'SEPOLIA,ARBITRUM_SEPOLIA', + 'SEPOLIA,BASE_SEPOLIA', + 'SEPOLIA,BLAST_SEPOLIA', + 'SEPOLIA,MODE_SEPOLIA', + 'SEPOLIA,OPTIMISM_SEPOLIA', + 'SEPOLIA,POLYGON_AMOY', + 'SEPOLIA,WEMIX_TESTNET', + 'SEPOLIA,GNOSIS_CHIADO', + 'SEPOLIA,METIS_SEPOLIA', + 'SEPOLIA,ZKSYNC_SEPOLIA', + + 'AVALANCHE_FUJI,BSC_TESTNET', + 'AVALANCHE_FUJI,ARBITRUM_SEPOLIA', + 'AVALANCHE_FUJI,BASE_SEPOLIA', + 'AVALANCHE_FUJI,OPTIMISM_SEPOLIA', + 'AVALANCHE_FUJI,POLYGON_AMOY', + 'AVALANCHE_FUJI,WEMIX_TESTNET', + 'AVALANCHE_FUJI,GNOSIS_CHIADO', + + 'BSC_TESTNET,BASE_SEPOLIA', + 'BSC_TESTNET,POLYGON_AMOY', + 'BSC_TESTNET,WEMIX_TESTNET', + 'BSC_TESTNET,GNOSIS_CHIADO', + + 'ARBITRUM_SEPOLIA,BASE_SEPOLIA', + 'ARBITRUM_SEPOLIA,OPTIMISM_SEPOLIA', + 'ARBITRUM_SEPOLIA,WEMIX_TESTNET', + 'ARBITRUM_SEPOLIA,GNOSIS_CHIADO', + + 'BASE_SEPOLIA,MODE_SEPOLIA', + 'BASE_SEPOLIA,OPTIMISM_SEPOLIA', + 'BASE_SEPOLIA,GNOSIS_CHIADO', + + 'KROMA_SEPOLIA,WEMIX_TESTNET', + + 'OPTIMISM_SEPOLIA,POLYGON_AMOY', + 'OPTIMISM_SEPOLIA,WEMIX_TESTNET', + 'OPTIMISM_SEPOLIA,GNOSIS_CHIADO', + + 'POLYGON_AMOY,WEMIX_TESTNET', + 'POLYGON_AMOY,GNOSIS_CHIADO', +] + +BiDirectionalLane = true +PhaseTimeout = '30m' +LocalCluster = false +ExistingDeployment = true +ReuseContracts = true + + +[CCIP.Groups.smoke.TokenConfig] +NoOfTokensPerChain = 1 +CCIPOwnerTokens = true + +[CCIP.Groups.smoke.MsgDetails] +MsgType = 'Data' +DestGasLimit = 0 +DataLength = 100 +NoOfTokens = 1 +AmountPerToken = 1 \ No newline at end of file diff --git a/integration-tests/ccip-tests/testconfig/tomls/prod-testnet/smoke-release-testing_token_transfer_native.toml b/integration-tests/ccip-tests/testconfig/tomls/prod-testnet/smoke-release-testing_token_transfer_native.toml new file mode 100644 index 00000000000..b1549377fda --- /dev/null +++ b/integration-tests/ccip-tests/testconfig/tomls/prod-testnet/smoke-release-testing_token_transfer_native.toml @@ -0,0 +1,1035 @@ +[CCIP] +[CCIP.ContractVersions] +PriceRegistry = 'latest' +OffRamp = 'latest' +OnRamp = 'latest' +CommitStore = 'latest' +TokenPool = 'latest' + + +[CCIP.Deployments] +Data = """ +{ + "lane_configs": { + "Arbitrum Sepolia": { + "is_native_fee_token": true, + "fee_token": "0xE591bf0A0CF924A0674d7792db046B23CEbF5f34", + "bridge_tokens": [ + "0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D" + ], + "bridge_tokens_pools": [ + "0x99685281Ec520a003F1A726A5a8078c2124c1477" + ], + "arm": "0xbcBDf0aDEDC9a33ED5338Bdb4B6F7CE664DC2e8B", + "router": "0x2a9C5afB0d0e4BAb2BCdaE109EC4b0c4Be15a165", + "price_registry": "0x89D5b13908b9063abCC6791dc724bF7B7c93634C", + "wrapped_native": "0xE591bf0A0CF924A0674d7792db046B23CEbF5f34", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0x1Cb56374296ED19E86F68fA437ee679FD7798DaA", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x7854E73C73e7F9bb5b0D5B4861E997f4C6E8dcC6", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x973CbE752258D32AE82b60CD1CB656Eebb588dF0", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0x701Fe16916dd21EFE2f535CA59611D818B017877", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x4205E1Ca0202A248A5D42F5975A8FE56F3E302e9", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0xBD4106fBE4699FE212A34Cc21b10BFf22b02d959", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0xcab0EF91Bee323d1A617c0a027eE753aFd6997E4", + "commit_store": "0x0d90b9b96cBFa0D01635ce12982ccE1b70827c7a", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0xc1982985720B959E66c19b64F783361Eb9B60F26", + "commit_store": "0x28F66bB336f6db713d6ad2a3bd1B7a531282A159", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x935C26F9a9122E5F9a27f2d3803e74c75B94f5a3", + "commit_store": "0xEdb963Ec5c2E5AbdFdCF137eF44A445a7fa4787A", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xfD404A89e1d195F0c65be1A9042C77745197659e", + "commit_store": "0x84B7B012c95f8A152B44Ab3e952f2dEE424fA8e1", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x1c71f141b4630EBE52d6aF4894812960abE207eB", + "commit_store": "0xaB0c8Ba51E7Fa3E5693a4Fbb39473520FD85d173", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0x262e16C8D42aa07bE13e58F81e7D9F62F6DE2830", + "commit_store": "0xc132eFAf929299E5ee704Fa6D9796CFa23Bb8b2C", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Avalanche Fuji": { + "is_native_fee_token": true, + "fee_token": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + "bridge_tokens": [ + "0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4" + ], + "bridge_tokens_pools": [ + "0xEC1062cbDf4fBf31B3A6Aac62B6F6F123bb70E12" + ], + "arm": "0x7e28DD790214139798446A121cFe950B51304684", + "router": "0xF694E193200268f9a4868e4Aa017A0118C9a8177", + "price_registry": "0x19e157E5fb1DAec1aE4BaB113fdf077F980704AA", + "wrapped_native": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x8bB16BEDbFd62D1f905ACe8DBBF2954c8EEB4f66", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xF25ECF1Aad9B2E43EDc2960cF66f325783245535", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x1A674645f3EB4147543FCA7d40C5719cbd997362", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x1532e5b204ee2b2244170c78E743CB9c168F4DF9", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0xC334DE5b020e056d0fE766dE46e8d9f306Ffa1E2", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0x610F76A35E17DA4542518D85FfEa12645eF111Fc", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x5724B4Cc39a9690135F7273b44Dfd3BA6c0c69aD", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0x677B5ab5C8522d929166c064d5700F147b15fa33", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x90A74072e7B0c2d59e13aB4d8f93c8198c413194", + "commit_store": "0xf3458CFd2fdf4a6CF0Ce296d520DD21eB194828b", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0x10b28009E5D776F1f5AAA73941CE8953B8f42d26", + "commit_store": "0xacDD582F271eCF22FAd6764cCDe1c4a534b732A8", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0xdBdE8510226d1E060A3bf982b67705C67f5697e2", + "commit_store": "0x8Ee73BC9492b4182D289E5C1e66e40CD876CC00F", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x56dF55aF5F0A4689f3364230587a68eD6A314fAd", + "commit_store": "0xabA7ff98094c4cc7A075812EefF2CD21f6400235", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0x3d7CbC95DCC33257F14D6Eb780c88Bd56C6335BB", + "commit_store": "0x1fcDC02edDfb405f378ba53cF9E6104feBcB7542", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x3e33290B90fD0FF30a3FA138934DF028E4eCA348", + "commit_store": "0xCFe3556Aa42d40be09BD23aa80448a19443BE5B1", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x9e5e4324F8608D54A50a317832d456a392E4F8C2", + "commit_store": "0x92A51eD3F041B39EbD1e464C1f7cb1e8f8A8c63f", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0xD0D338318bC6837b091FC7AB5F2a94B7783507d5", + "commit_store": "0xd9D479208235c7355848ff4aF26eB5aacfDC30c6", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "BSC Testnet": { + "is_native_fee_token": true, + "fee_token": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", + "bridge_tokens": [ + "0xbFA2ACd33ED6EEc0ed3Cc06bF1ac38d22b36B9e9" + ], + "bridge_tokens_pools": [ + "0x31eDe84776DA37e2404eE88d71c234e92cB672e5" + ], + "arm": "0x7D899D26F2E94fFcd4b440C3008B0C6BEfcD3cca", + "router": "0xE1053aE1857476f36A3C62580FF9b016E8EE8F6f", + "price_registry": "0xCCDf022c9d31DC26Ebab4FB92432724a5b79809a", + "wrapped_native": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0xa2515683E99F50ADbE177519A46bb20FfdBaA5de", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x3E807220Ca84b997c0d1928162227b46C618e0c5", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x8735f991d41eA9cA9D2CC75cD201e4B7C866E63e", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0xf37CcbfC04adc1B56a46B36F811D52C744a1AF78", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0xB1DE44B04C00eaFe9915a3C07a0CaeA4410537dF", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0x89268Afc1BEA0782a27ba84124E3F42b196af927", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0x6e6fFCb6B4BED91ff0CC8C2e57EC029dA7DB80C2", + "commit_store": "0x38Bc38Bd824b6eE87571f9D3CFbe6D6E28E3Dc62", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0x2C61FD7E93Dc79422861282145c59B56dFbc3a8c", + "commit_store": "0x42fAe5B3605804CF6d08632d7A25864e24F792Ae", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x71a44a60832B0F8B63232C9516e7E6aEc3A373Dc", + "commit_store": "0xAC24299a91b72d1Cb5B31147e3CF54964D896974", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x63440C7747d37bc6154b5538AE32b54FE0965AfA", + "commit_store": "0xAD22fA198CECfC534927aE1D480c460d5bB3460F", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xf1c128Fe52Ea78CcAAB407509292E61ce38C1523", + "commit_store": "0x59dFD870dC4bd76A7B879A4f705Fdcd2595f85f9", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0xfd9B19c3725da5B517aA705B848ff3f21F98280e", + "commit_store": "0x3c1F1412563188aBc8FE3fd53E8F1Cb601CaB4f9", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Base Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000006", + "bridge_tokens": [ + "0x88A2d74F47a237a62e7A51cdDa67270CE381555e" + ], + "bridge_tokens_pools": [ + "0x875207858c691F192C606068f417dCf666b2EC6B" + ], + "arm": "0x7827dD0481EE18DB646bD250d20A8eA43da52146", + "router": "0xD3b06cEbF099CE7DA4AcCf578aaebFDBd6e88a93", + "price_registry": "0x4D20536e60832bE579Cd38E89Dc03d11E1741FbA", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x58622a80c6DdDc072F2b527a99BE1D0934eb2b50", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0xAbA09a1b7b9f13E05A6241292a66793Ec7d43357", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xD806966beAB5A3C75E5B90CDA4a6922C6A9F0c9d", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x2Eff2d1BF5C557d6289D208a7a43608f5E3FeCc2", + "deployed_at": 0 + }, + "Mode Sepolia": { + "on_ramp": "0x3d0115386C01436870a2c47e6297962284E70BA6", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0x3b39Cd9599137f892Ad57A4f54158198D445D147", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x6486906bB2d85A6c0cCEf2A2831C11A2059ebfea", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xd364C06ac99a82a00d3eFF9F2F78E4Abe4b9baAA", + "commit_store": "0xdE8d0f47a71eA3fDFBD3162271652f2847939097", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0xAd91214efFee446500940c764DF77AF18427294F", + "commit_store": "0x1242b6c5e0e349b8d4BCf0938f961C4B4f7EA3Fa", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0xd5E9508921434e8758f4540D55c1c066b7cc1598", + "commit_store": "0x1a86b29364D1B3fA3386329A361aA98A104b2742", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x9Bb7e398ef9Acfe9cA584C39B1E233Cba62BB9f7", + "commit_store": "0x1F4B82cDebaC5e3a0Dd53183D47e51808B4a64cB", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Mode Sepolia": { + "off_ramp": "0xB26647A23e8b4284375e5C74b77c9557aE709D03", + "commit_store": "0x4b4fEB401d3E613e1D6242E155C83A80BF9ac2C9", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0x86a3910908eCaAA31Fcd9F0fC8841D8E98f1511d", + "commit_store": "0xE99a87C9b5ed4D2b6060195DEea5106ffF655736", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x189F61D9B886Dd2975D5Abc893c8Cf5f5effda71", + "commit_store": "0xEE7e27346DCD1e711348D0F7f7ECB53a9a3a08a7", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Blast Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000023", + "bridge_tokens": [ + "0x8D122C3e8ce9C8B62b87d3551bDfD8C259Bb0771" + ], + "bridge_tokens_pools": [ + "0xFb04129aD1EEDB741CC705ebC1978a7aB63e51f6" + ], + "arm": "0x09c1Ed4b112Fb33e594F2aACfEF407e2F14d7F9b", + "router": "0xfb2f2A207dC428da81fbAFfDDe121761f8Be1194", + "price_registry": "0xc8acE9dF450FaD007755C6C9AB4f0e9c8626E29C", + "wrapped_native": "0x4200000000000000000000000000000000000023", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x85Ef19FC4C63c70744995DC38CAAEC185E0c619f", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0x92cD24C278D34C726f377703E50875d8f9535dC2", + "commit_store": "0xcE1b4D50CeD56850182Bd58Ace91171cB249B873", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Celo Alfajores": { + "is_native_fee_token": true, + "fee_token": "0x99604d0e2EfE7ABFb58BdE565b5330Bb46Ab3Dca", + "bridge_tokens": [ + "0x7e503dd1dAF90117A1b79953321043d9E6815C72" + ], + "bridge_tokens_pools": [ + "0xC6683ac4a0F62803Bec89a5355B36495ddF2C38b" + ], + "arm": "0xEbe35aA4F5e707485484c992AF2069a457b9bBB1", + "router": "0xb00E95b773528E2Ea724DB06B75113F239D15Dca", + "price_registry": "0x8F048206D11B2c69b8963E2EBd5968D141e022f4", + "wrapped_native": "0x99604d0e2EfE7ABFb58BdE565b5330Bb46Ab3Dca", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x16a020c4bbdE363FaB8481262D30516AdbcfcFc8", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0xa1b97F92D806BA040daf419AFC2765DC723683a4", + "commit_store": "0xcd92C0599Ac515e7588865cC45Eee21A74816aFc", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Gnosis Chiado": { + "is_native_fee_token": true, + "fee_token": "0x18c8a7ec7897177E4529065a7E7B0878358B3BfF", + "bridge_tokens": [ + "0xA189971a2c5AcA0DFC5Ee7a2C44a2Ae27b3CF389" + ], + "bridge_tokens_pools": [ + "0xF9a21B587111e7E8745Fb8b13750014f19DB0014" + ], + "arm": "0xfE4fB161D870D0F672Ed9C5A898569603f77983F", + "router": "0x19b1bac554111517831ACadc0FD119D23Bb14391", + "price_registry": "0x2F4ACd1f8986c6B1788159C4c9a5fC3fceCCE363", + "wrapped_native": "0x18c8a7ec7897177E4529065a7E7B0878358B3BfF", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x473b49fb592B54a4BfCD55d40E048431982879C9", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0x610F76A35E17DA4542518D85FfEa12645eF111Fc", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xE48E6AA1fc7D0411acEA95F8C6CaD972A37721D4", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x41b4A51cAfb699D9504E89d19D71F92E886028a8", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0xAae733212981e06D9C978Eb5148F8af03F54b6EF", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0x01800fCDd892e37f7829937271840A6F041bE62E", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x4ac7FBEc2A7298AbDf0E0F4fDC45015836C4bAFe", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x9aA82DBB53bf02096B771D40e9432A323a78fB26", + "commit_store": "0x5CdbA91aBC0cD81FC56bc10Ad1835C9E5fB38e5F", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0x3e33290B90fD0FF30a3FA138934DF028E4eCA348", + "commit_store": "0xCFe3556Aa42d40be09BD23aa80448a19443BE5B1", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0xbc4AD54e91b213D4279af92c0C5518c0b96cf62D", + "commit_store": "0xff84e8Dd4Fd17eaBb23b6AeA6e1981830e54389C", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0x4117953A5ceeF12f5B8C1E973b470ab83a8CebA6", + "commit_store": "0x94ad41296186E81f31e1ed0B1BcF5fa9e1721C27", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0x33d2898F8fb7714FD1661791766f40754982a343", + "commit_store": "0x55d6Df194472f02CD481e506A277c4A29D0D1bCc", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x450543b1d85ca79885851D7b74dc982981b78229", + "commit_store": "0x23B79d940A769FE31b4C867A8BAE80117f24Ca81", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xbf9036529123DE264bFA0FC7362fE25B650D4B16", + "commit_store": "0x5f7F1abD5c5EdaF2636D58B980e85355AF0Ef80d", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Kroma Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000001", + "bridge_tokens": [ + "0x6AC3e353D1DDda24d5A5416024d6E436b8817A4e" + ], + "bridge_tokens_pools": [ + "0x0eE8add19554C7bb1920A183Ed47b4FAB9Eb7601" + ], + "arm": "0x08f9Af992368FAc58C936A2c5eBc9092894CEa9b", + "router": "0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D", + "price_registry": "0xa1ed3A3aA29166C9c8448654A8cA6b7916BC8379", + "wrapped_native": "0x4200000000000000000000000000000000000001", + "src_contracts": { + "WeMix Testnet": { + "on_ramp": "0x6ea155Fc77566D9dcE01B8aa5D7968665dc4f0C5", + "deployed_at": 0 + } + }, + "dest_contracts": { + "WeMix Testnet": { + "off_ramp": "0xB602B6E5Caf08ac0C920EAE585aed100a8cF6f3B", + "commit_store": "0x89D5b13908b9063abCC6791dc724bF7B7c93634C", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Metis Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x5c48e07062aC4E2Cf4b9A768a711Aef18e8fbdA0", + "bridge_tokens": [ + "0x20Aa09AAb761e2E600d65c6929A9fd1E59821D3f" + ], + "bridge_tokens_pools": [ + "0xdE8451E952Eb43350614839cCAA84f7C8701a09C" + ], + "arm": "0xf0607A9BDdB5F54dB59ACaA0837aFec2D1c95df6", + "router": "0xaCdaBa07ECad81dc634458b98673931DD9d3Bc14", + "price_registry": "0x5DCE866b3ae6E0Ed153f0e149D7203A1B266cdF5", + "wrapped_native": "0x5c48e07062aC4E2Cf4b9A768a711Aef18e8fbdA0", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x2Eff2d1BF5C557d6289D208a7a43608f5E3FeCc2", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0x9Bb7e398ef9Acfe9cA584C39B1E233Cba62BB9f7", + "commit_store": "0x1F4B82cDebaC5e3a0Dd53183D47e51808B4a64cB", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Mode Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000006", + "bridge_tokens": [ + "0xB9d4e1141E67ECFedC8A8139b5229b7FF2BF16F5" + ], + "bridge_tokens_pools": [ + "0x20bBc874bE3Cd94C3E4689EDD5D89dD1cE8Cb7C4" + ], + "arm": "0x9eC8a0AbC75ce08978FAf67958482461bCd93B18", + "router": "0xc49ec0eB4beb48B8Da4cceC51AA9A5bD0D0A4c43", + "price_registry": "0xa733ce82a84335b2E9D864312225B0F3D5d80600", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Base Sepolia": { + "on_ramp": "0x73f7E074bd7291706a0C5412f51DB46441B1aDCB", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0xfFdE9E8c34A27BEBeaCcAcB7b3044A0A364455C9", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Base Sepolia": { + "off_ramp": "0x137a38c6b1Ad20101F93516aB2159Df525309168", + "commit_store": "0x8F43d867969F14619895d71E0A5b89E0bb20bF70", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xcD44cec849B6a8eBd5551D6DFeEcA452257Dfe4d", + "commit_store": "0xbA66f08733E6715D33edDfb5a5947676bb45d0e0", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Optimism Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000006", + "bridge_tokens": [ + "0x8aF4204e30565DF93352fE8E1De78925F6664dA7" + ], + "bridge_tokens_pools": [ + "0x3Cc9364260D80F09ccAC1eE6B07366dB598900E6" + ], + "arm": "0xF51366F72184E22cF4a7a8362508DB0d3370392d", + "router": "0x114A20A10b43D4115e5aeef7345a1A71d2a60C57", + "price_registry": "0x782a7Ba95215f2F7c3dD4C153cbB2Ae3Ec2d3215", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x1a86b29364D1B3fA3386329A361aA98A104b2742", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0x6b38CC6Fa938D5AB09Bdf0CFe580E226fDD793cE", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0xe284D2315a28c4d62C419e8474dC457b219DB969", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x835a5b8e6CA17c2bB5A336c93a4E22478E6F1C8A", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0x2Cf26fb01E9ccDb831414B766287c0A9e4551089", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0xC8b93b46BF682c39B3F65Aa1c135bC8A95A5E43a", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0xc7E53f6aB982af7A7C3e470c8cCa283d3399BDAd", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xDc2c7A3d8068C6F09F0F3648d24C84e372F6014d", + "commit_store": "0xb1aFb5cbE3c29b5Db71F21442BA9EfD450BC23C3", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0x1F350718e015EB20E5065C09F4A7a3f66888aEeD", + "commit_store": "0x98650A8EB59f75D93563aB34FcF603b1A30e4CBF", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0x0a750ca77369e03613d7640548F4b2b1c695c3Bb", + "commit_store": "0x8fEBC74C26129C8d7E60288C6dCCc75eb494aA3C", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0xCE2CE7F940B7c839384e5D7e079A6aE80e8AD6dB", + "commit_store": "0x1b9D78Ec1CEEC439F0b7eA6C428A1a607D9FA7e4", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0xD667b5706592D0b040C78fEe5EE17D243b7dCB41", + "commit_store": "0x96101BA5250EE9295c193693C1e08A55bC593664", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x260AF9b83e0d2Bb6C9015fC9f0BfF8858A0CCE68", + "commit_store": "0x7a0bB92Bc8663abe6296d0162A9b41a2Cb2E0358", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0x9C08B7712af0344188aa5087D9e6aD0f47191037", + "commit_store": "0x4BE6DB0B884169a6A207fe5cad01eB4C025a13dB", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Polygon Amoy": { + "is_native_fee_token": true, + "fee_token": "0x360ad4f9a9A8EFe9A8DCB5f461c4Cc1047E1Dcf9", + "bridge_tokens": [ + "0xcab0EF91Bee323d1A617c0a027eE753aFd6997E4" + ], + "bridge_tokens_pools": [ + "0x3064fB3EA546EE09A63AB3bD93E83D8B8525C636" + ], + "arm": "0x8b88C39D2875157aB4CE4AD3814409523d539ee1", + "router": "0x9C32fCB86BF0f4a1A8921a9Fe46de3198bb884B2", + "price_registry": "0xfb2f2A207dC428da81fbAFfDDe121761f8Be1194", + "wrapped_native": "0x360ad4f9a9A8EFe9A8DCB5f461c4Cc1047E1Dcf9", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0x8Fb98b3837578aceEA32b454f3221FE18D7Ce903", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xC6683ac4a0F62803Bec89a5355B36495ddF2C38b", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x2331F6D614C9Fd613Ff59a1aB727f1EDf6c37A68", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0xA52cDAeb43803A80B3c0C2296f5cFe57e695BE11", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x35347A2fC1f2a4c5Eae03339040d0b83b09e6FDA", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0x26546096F64B5eF9A1DcDAe70Df6F4f8c2E10C61", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0xa733ce82a84335b2E9D864312225B0F3D5d80600", + "commit_store": "0x09B0F93fC2111aE439e853884173AC5b2F809885", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0x948dfaa4842fc23e0e362Fe8D4396AaE4E6DF7EA", + "commit_store": "0x7F4e739D40E58BBd59dAD388171d18e37B26326f", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x17c542a28e08AEF5697251601C7b2B621d153D42", + "commit_store": "0x811250c20fAB9a1b7ca245453aC214ba637fBEB5", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xfFdE9E8c34A27BEBeaCcAcB7b3044A0A364455C9", + "commit_store": "0x74ED442ad211050e9C05Dc9A267E037E3d74A03B", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xFb04129aD1EEDB741CC705ebC1978a7aB63e51f6", + "commit_store": "0x63f875240149d29136053C954Ca164a9BfA81F77", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0xdE8451E952Eb43350614839cCAA84f7C8701a09C", + "commit_store": "0xaCdaBa07ECad81dc634458b98673931DD9d3Bc14", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Sepolia Testnet": { + "is_native_fee_token": true, + "fee_token": "0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534", + "bridge_tokens": [ + "0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05" + ], + "bridge_tokens_pools": [ + "0x38d1ef9619Cd40cf5482C045660Ae7C82Ada062c" + ], + "arm": "0x27Da8735d8d1402cEc072C234759fbbB4dABBC4A", + "router": "0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59", + "price_registry": "0x9EF7D57a4ea30b9e37794E55b0C75F2A70275dCc", + "wrapped_native": "0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0xe4Dd3B16E09c016402585a8aDFdB4A18f772a07e", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0x0477cA0a35eE05D3f9f424d88bC0977ceCf339D4", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xD990f8aFA5BCB02f95eEd88ecB7C68f5998bD618", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x2B70a05320cB069e0fB55084D402343F832556E7", + "deployed_at": 0 + }, + "Blast Sepolia": { + "on_ramp": "0xDB75E9D9ca7577CcBd7232741be954cf26194a66", + "deployed_at": 0 + }, + "Celo Alfajores": { + "on_ramp": "0x3C86d16F52C10B2ff6696a0e1b8E0BcfCC085948", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x3E842E3A79A00AFdd03B52390B1caC6306Ea257E", + "deployed_at": 0 + }, + "Metis Sepolia": { + "on_ramp": "0x1C4640914cd57c5f02a68048A0fbb0E12d904223", + "deployed_at": 0 + }, + "Mode Sepolia": { + "on_ramp": "0xc630fbD4D0F6AEB00aD0793FB827b54fBB78e981", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0x69CaB5A0a08a12BaFD8f5B195989D709E396Ed4d", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0x9f656e0361Fb5Df2ac446102c8aB31855B591692", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0xedFc22336Eb0B9B11Ff37C07777db27BCcDe3C65", + "deployed_at": 0 + }, + "ZKSync Sepolia": { + "on_ramp": "0x1Acb3A885feA37bdA30AB99b99327b14391f500F", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xF18896AB20a09A29e64fdEbA99FDb8EC328f43b1", + "commit_store": "0x93Ff9Dd39Dc01eac1fc4d2c9211D95Ee458CAB94", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0x000b26f604eAadC3D874a4404bde6D64a97d95ca", + "commit_store": "0x2dD9273F8208B8393350508131270A6574A69784", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0xdE2d8E126e08d675fCD7fFa5a6CE49925f3Dc692", + "commit_store": "0x0050ac355a82caB31194507f94174297bf0655A7", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0x31c0B81832B333419f0DfD36A69F502cF9094aed", + "commit_store": "0xDFcde9d698a2B32DB2537DC9B752Cadd1D846a52", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Blast Sepolia": { + "off_ramp": "0x4e897e5cF3aC307F0541B2151A88bCD781c153a3", + "commit_store": "0xB656652841F347178e193951C4663652aCe36B74", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Celo Alfajores": { + "off_ramp": "0xB435E0f73c18C5a12C324CA1d02F81F2C3e6e761", + "commit_store": "0xbc5d74957F171e75F92c8F0E1C317A25a56a416D", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x7db0115A0b3AAb01d30bf81123c5DD7B0C41Add5", + "commit_store": "0x6640723Ea801178c4383FA016b9781e7ef1016EF", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Metis Sepolia": { + "off_ramp": "0x4DB693A93E9d5196ECD42EC56CDEAe99dFC652ED", + "commit_store": "0xBfACd78F1412B6f93Ac23409bf456aFec1ABd845", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Mode Sepolia": { + "off_ramp": "0xbEfd8D65F6643De54F0b1268A3bf4618ff85dcB4", + "commit_store": "0x0C161D3470b45Cc677661654C30ce4AdE6aCD288", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xD50590D4438411EDe47029b0FD7901A7145E5Df6", + "commit_store": "0xe85EEE9Fd434A7b8a586Ee086E828abF41839479", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x5032cbC0C4aEeD25bb6E45D8B3fAF05DB0688C5d", + "commit_store": "0xe6201C9996Cc7B6E828E10CbE937E693d577D318", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0x46b639a3C1a4CBfD326b94a2dB7415c27157282f", + "commit_store": "0x7b74554678816b045c1e7409327E086bD436aa46", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "ZKSync Sepolia": { + "off_ramp": "0xBaABd4166C892a1081a26535875A8fA3f22937b2", + "commit_store": "0xfda2e83F4D3f42B7629134ecD6E4b29FB8A7A07B", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "WeMix Testnet": { + "is_native_fee_token": true, + "fee_token": "0xbE3686643c05f00eC46e73da594c78098F7a9Ae7", + "bridge_tokens": [ + "0xF4E4057FbBc86915F4b2d63EEFFe641C03294ffc" + ], + "bridge_tokens_pools": [ + "0x82A92B2863F93Be70D20660088Ec060720bA2fdb" + ], + "arm": "0x8f6cb63eD5e379722580DFF0A051C140C64F9619", + "router": "0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D", + "price_registry": "0x89D17571DB7C9540eeB36760E3c749C8fb984569", + "wrapped_native": "0xbE3686643c05f00eC46e73da594c78098F7a9Ae7", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0xA9DE3F7A617D67bC50c56baaCb9E0373C15EbfC6", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0xC4aC84da458ba8e40210D2dF94C76E9a41f70069", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0x5AD6eed6Be0ffaDCA4105050CF0E584D87E0c2F1", + "deployed_at": 0 + }, + "Kroma Sepolia": { + "on_ramp": "0x428C4dc89b6Bf908B82d77C9CBceA786ea8cc7D0", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0x1961a7De751451F410391c251D4D4F98D71B767D", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0xd55148e841e76265B484d399eC71b7076ecB1216", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x4d57C6d8037C65fa66D6231844785a428310a735", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xeB1dFaB2464Bf0574D43e764E0c758f92e7ecAFb", + "commit_store": "0xcEaCa2B7890065c485f3E58657358a185Ad33791", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0x98e811Df9D2512f1aaf58D534607F583D6c54A4F", + "commit_store": "0x8e538351F6E5B2daF3c90C565C3738bca69a2716", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0xB0e7f0fCcD3c961C473E7c44D939C1cDb4Cec1cB", + "commit_store": "0x4B56D8d53f1A6e0117B09700067De99581aA5542", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Kroma Sepolia": { + "off_ramp": "0xD685D2d224dd6D0Db2D56497db6270D77D9a7966", + "commit_store": "0x7e062D6880779a0347e7742058C1b1Ee4AA0B137", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xA5f97Bc69Bf06e7C37B93265c5457420A92c5F4b", + "commit_store": "0xd48b9213583074f518D8f4336FDf35370D450132", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x6c8f5999B06FDE17B11E4e3C1062b761766F960f", + "commit_store": "0x957c3c2056192e58A8485eF31165fC490d474239", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x8AB103843ED9D28D2C5DAf5FdB9c3e1CE2B6c876", + "commit_store": "0x7d5297c5506ee2A7Ef121Da9bE02b6a6AD30b392", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "ZKSync Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4317b2eCD41851173175005783322D29E9bAee9E", + "arm": "0x926b4f90610Aa448f27c8e0Fd0afa4A17B7305F9", + "router": "0xA1fdA8aa9A8C4b945C45aD30647b01f07D7A0B16", + "price_registry": "0x648B6BB09bE1C5766C8AC578B9B4aC8497eA671F", + "wrapped_native": "0x4317b2eCD41851173175005783322D29E9bAee9E", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x79a9a5e9e318e8e109776569574814bbf935125a", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0x18FF69051479796175852578725Fc74F58E963F8", + "commit_store": "0xF694b4FD7889480dca3CD41244e8e3395a9EFBa0", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + } + } +} +""" + +[CCIP.Env] +TTL = '8h' + +[CCIP.Env.Network] +selected_networks = [ + 'ARBITRUM_SEPOLIA', + 'AVALANCHE_FUJI', + 'BASE_SEPOLIA', + 'BLAST_SEPOLIA', + 'BSC_TESTNET', + 'CELO_ALFAJORES', + 'GNOSIS_CHIADO', + 'KROMA_SEPOLIA', + 'MODE_SEPOLIA', + 'OPTIMISM_SEPOLIA', + 'POLYGON_AMOY', + 'SEPOLIA', + 'METIS_SEPOLIA', + 'WEMIX_TESTNET', + 'ZKSYNC_SEPOLIA' +] + +[CCIP.Groups.smoke] +# these are all the valid network pairs +NetworkPairs = [ + 'SEPOLIA,AVALANCHE_FUJI', + 'SEPOLIA,BSC_TESTNET', + 'SEPOLIA,CELO_ALFAJORES', + 'SEPOLIA,ARBITRUM_SEPOLIA', + 'SEPOLIA,BASE_SEPOLIA', + 'SEPOLIA,BLAST_SEPOLIA', + 'SEPOLIA,MODE_SEPOLIA', + 'SEPOLIA,OPTIMISM_SEPOLIA', + 'SEPOLIA,POLYGON_AMOY', + 'SEPOLIA,WEMIX_TESTNET', + 'SEPOLIA,GNOSIS_CHIADO', + 'SEPOLIA,METIS_SEPOLIA', + 'SEPOLIA,ZKSYNC_SEPOLIA', + + 'AVALANCHE_FUJI,BSC_TESTNET', + 'AVALANCHE_FUJI,ARBITRUM_SEPOLIA', + 'AVALANCHE_FUJI,BASE_SEPOLIA', + 'AVALANCHE_FUJI,OPTIMISM_SEPOLIA', + 'AVALANCHE_FUJI,POLYGON_AMOY', + 'AVALANCHE_FUJI,WEMIX_TESTNET', + 'AVALANCHE_FUJI,GNOSIS_CHIADO', + + 'BSC_TESTNET,BASE_SEPOLIA', + 'BSC_TESTNET,POLYGON_AMOY', + 'BSC_TESTNET,WEMIX_TESTNET', + 'BSC_TESTNET,GNOSIS_CHIADO', + + 'ARBITRUM_SEPOLIA,BASE_SEPOLIA', + 'ARBITRUM_SEPOLIA,OPTIMISM_SEPOLIA', + 'ARBITRUM_SEPOLIA,WEMIX_TESTNET', + 'ARBITRUM_SEPOLIA,GNOSIS_CHIADO', + + 'BASE_SEPOLIA,MODE_SEPOLIA', + 'BASE_SEPOLIA,OPTIMISM_SEPOLIA', + 'BASE_SEPOLIA,GNOSIS_CHIADO', + + 'KROMA_SEPOLIA,WEMIX_TESTNET', + + 'OPTIMISM_SEPOLIA,POLYGON_AMOY', + 'OPTIMISM_SEPOLIA,WEMIX_TESTNET', + 'OPTIMISM_SEPOLIA,GNOSIS_CHIADO', + + 'POLYGON_AMOY,WEMIX_TESTNET', + 'POLYGON_AMOY,GNOSIS_CHIADO', +] + +BiDirectionalLane = true +PhaseTimeout = '30m' +LocalCluster = false +ExistingDeployment = true +ReuseContracts = true + + +[CCIP.Groups.smoke.TokenConfig] +NoOfTokensPerChain = 1 +CCIPOwnerTokens = true + +[CCIP.Groups.smoke.MsgDetails] +MsgType = 'DataWithToken' +DestGasLimit = 0 +DataLength = 100 +NoOfTokens = 1 +AmountPerToken = 1 \ No newline at end of file diff --git a/integration-tests/ccip-tests/testconfig/tomls/prod-testnet/soak-release-testing_token_transfer_with_native_feetoken.toml b/integration-tests/ccip-tests/testconfig/tomls/prod-testnet/soak-release-testing_token_transfer_with_native_feetoken.toml new file mode 100644 index 00000000000..6a6aa205fce --- /dev/null +++ b/integration-tests/ccip-tests/testconfig/tomls/prod-testnet/soak-release-testing_token_transfer_with_native_feetoken.toml @@ -0,0 +1,1013 @@ +[CCIP] +[CCIP.ContractVersions] +PriceRegistry = '1.2.0' +OffRamp = '1.2.0' +OnRamp = '1.2.0' +TokenPool = '1.4.0' +CommitStore = '1.2.0' + + +[CCIP.Deployments] +Data = """ +{ + "lane_configs": { + "Arbitrum Sepolia": { + "is_native_fee_token": true, + "fee_token": "0xE591bf0A0CF924A0674d7792db046B23CEbF5f34", + "bridge_tokens": [ + "0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D" + ], + "bridge_tokens_pools": [ + "0x99685281Ec520a003F1A726A5a8078c2124c1477" + ], + "arm": "0xbcBDf0aDEDC9a33ED5338Bdb4B6F7CE664DC2e8B", + "router": "0x2a9C5afB0d0e4BAb2BCdaE109EC4b0c4Be15a165", + "price_registry": "0x89D5b13908b9063abCC6791dc724bF7B7c93634C", + "wrapped_native": "0xE591bf0A0CF924A0674d7792db046B23CEbF5f34", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0x1Cb56374296ED19E86F68fA437ee679FD7798DaA", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x7854E73C73e7F9bb5b0D5B4861E997f4C6E8dcC6", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x973CbE752258D32AE82b60CD1CB656Eebb588dF0", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0x701Fe16916dd21EFE2f535CA59611D818B017877", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x4205E1Ca0202A248A5D42F5975A8FE56F3E302e9", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0xBD4106fBE4699FE212A34Cc21b10BFf22b02d959", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0xcab0EF91Bee323d1A617c0a027eE753aFd6997E4", + "commit_store": "0x0d90b9b96cBFa0D01635ce12982ccE1b70827c7a", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0xc1982985720B959E66c19b64F783361Eb9B60F26", + "commit_store": "0x28F66bB336f6db713d6ad2a3bd1B7a531282A159", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x935C26F9a9122E5F9a27f2d3803e74c75B94f5a3", + "commit_store": "0xEdb963Ec5c2E5AbdFdCF137eF44A445a7fa4787A", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xfD404A89e1d195F0c65be1A9042C77745197659e", + "commit_store": "0x84B7B012c95f8A152B44Ab3e952f2dEE424fA8e1", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x1c71f141b4630EBE52d6aF4894812960abE207eB", + "commit_store": "0xaB0c8Ba51E7Fa3E5693a4Fbb39473520FD85d173", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0x262e16C8D42aa07bE13e58F81e7D9F62F6DE2830", + "commit_store": "0xc132eFAf929299E5ee704Fa6D9796CFa23Bb8b2C", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Avalanche Fuji": { + "is_native_fee_token": true, + "fee_token": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + "bridge_tokens": [ + "0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4" + ], + "bridge_tokens_pools": [ + "0xEC1062cbDf4fBf31B3A6Aac62B6F6F123bb70E12" + ], + "arm": "0x7e28DD790214139798446A121cFe950B51304684", + "router": "0xF694E193200268f9a4868e4Aa017A0118C9a8177", + "price_registry": "0x19e157E5fb1DAec1aE4BaB113fdf077F980704AA", + "wrapped_native": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x8bB16BEDbFd62D1f905ACe8DBBF2954c8EEB4f66", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xF25ECF1Aad9B2E43EDc2960cF66f325783245535", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x1A674645f3EB4147543FCA7d40C5719cbd997362", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x1532e5b204ee2b2244170c78E743CB9c168F4DF9", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0xC334DE5b020e056d0fE766dE46e8d9f306Ffa1E2", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0x610F76A35E17DA4542518D85FfEa12645eF111Fc", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x5724B4Cc39a9690135F7273b44Dfd3BA6c0c69aD", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0x677B5ab5C8522d929166c064d5700F147b15fa33", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x90A74072e7B0c2d59e13aB4d8f93c8198c413194", + "commit_store": "0xf3458CFd2fdf4a6CF0Ce296d520DD21eB194828b", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0x10b28009E5D776F1f5AAA73941CE8953B8f42d26", + "commit_store": "0xacDD582F271eCF22FAd6764cCDe1c4a534b732A8", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0xdBdE8510226d1E060A3bf982b67705C67f5697e2", + "commit_store": "0x8Ee73BC9492b4182D289E5C1e66e40CD876CC00F", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x56dF55aF5F0A4689f3364230587a68eD6A314fAd", + "commit_store": "0xabA7ff98094c4cc7A075812EefF2CD21f6400235", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0x3d7CbC95DCC33257F14D6Eb780c88Bd56C6335BB", + "commit_store": "0x1fcDC02edDfb405f378ba53cF9E6104feBcB7542", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x3e33290B90fD0FF30a3FA138934DF028E4eCA348", + "commit_store": "0xCFe3556Aa42d40be09BD23aa80448a19443BE5B1", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x9e5e4324F8608D54A50a317832d456a392E4F8C2", + "commit_store": "0x92A51eD3F041B39EbD1e464C1f7cb1e8f8A8c63f", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0xD0D338318bC6837b091FC7AB5F2a94B7783507d5", + "commit_store": "0xd9D479208235c7355848ff4aF26eB5aacfDC30c6", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "BSC Testnet": { + "is_native_fee_token": true, + "fee_token": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", + "bridge_tokens": [ + "0xbFA2ACd33ED6EEc0ed3Cc06bF1ac38d22b36B9e9" + ], + "bridge_tokens_pools": [ + "0x31eDe84776DA37e2404eE88d71c234e92cB672e5" + ], + "arm": "0x7D899D26F2E94fFcd4b440C3008B0C6BEfcD3cca", + "router": "0xE1053aE1857476f36A3C62580FF9b016E8EE8F6f", + "price_registry": "0xCCDf022c9d31DC26Ebab4FB92432724a5b79809a", + "wrapped_native": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0xa2515683E99F50ADbE177519A46bb20FfdBaA5de", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x3E807220Ca84b997c0d1928162227b46C618e0c5", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x8735f991d41eA9cA9D2CC75cD201e4B7C866E63e", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0xf37CcbfC04adc1B56a46B36F811D52C744a1AF78", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0xB1DE44B04C00eaFe9915a3C07a0CaeA4410537dF", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0x89268Afc1BEA0782a27ba84124E3F42b196af927", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0x6e6fFCb6B4BED91ff0CC8C2e57EC029dA7DB80C2", + "commit_store": "0x38Bc38Bd824b6eE87571f9D3CFbe6D6E28E3Dc62", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0x2C61FD7E93Dc79422861282145c59B56dFbc3a8c", + "commit_store": "0x42fAe5B3605804CF6d08632d7A25864e24F792Ae", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x71a44a60832B0F8B63232C9516e7E6aEc3A373Dc", + "commit_store": "0xAC24299a91b72d1Cb5B31147e3CF54964D896974", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x63440C7747d37bc6154b5538AE32b54FE0965AfA", + "commit_store": "0xAD22fA198CECfC534927aE1D480c460d5bB3460F", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xf1c128Fe52Ea78CcAAB407509292E61ce38C1523", + "commit_store": "0x59dFD870dC4bd76A7B879A4f705Fdcd2595f85f9", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0xfd9B19c3725da5B517aA705B848ff3f21F98280e", + "commit_store": "0x3c1F1412563188aBc8FE3fd53E8F1Cb601CaB4f9", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Base Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000006", + "bridge_tokens": [ + "0x88A2d74F47a237a62e7A51cdDa67270CE381555e" + ], + "bridge_tokens_pools": [ + "0x875207858c691F192C606068f417dCf666b2EC6B" + ], + "arm": "0x7827dD0481EE18DB646bD250d20A8eA43da52146", + "router": "0xD3b06cEbF099CE7DA4AcCf578aaebFDBd6e88a93", + "price_registry": "0x4D20536e60832bE579Cd38E89Dc03d11E1741FbA", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x58622a80c6DdDc072F2b527a99BE1D0934eb2b50", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0xAbA09a1b7b9f13E05A6241292a66793Ec7d43357", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xD806966beAB5A3C75E5B90CDA4a6922C6A9F0c9d", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x2Eff2d1BF5C557d6289D208a7a43608f5E3FeCc2", + "deployed_at": 0 + }, + "Mode Sepolia": { + "on_ramp": "0x3d0115386C01436870a2c47e6297962284E70BA6", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0x3b39Cd9599137f892Ad57A4f54158198D445D147", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x6486906bB2d85A6c0cCEf2A2831C11A2059ebfea", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xd364C06ac99a82a00d3eFF9F2F78E4Abe4b9baAA", + "commit_store": "0xdE8d0f47a71eA3fDFBD3162271652f2847939097", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0xAd91214efFee446500940c764DF77AF18427294F", + "commit_store": "0x1242b6c5e0e349b8d4BCf0938f961C4B4f7EA3Fa", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0xd5E9508921434e8758f4540D55c1c066b7cc1598", + "commit_store": "0x1a86b29364D1B3fA3386329A361aA98A104b2742", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x9Bb7e398ef9Acfe9cA584C39B1E233Cba62BB9f7", + "commit_store": "0x1F4B82cDebaC5e3a0Dd53183D47e51808B4a64cB", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Mode Sepolia": { + "off_ramp": "0xB26647A23e8b4284375e5C74b77c9557aE709D03", + "commit_store": "0x4b4fEB401d3E613e1D6242E155C83A80BF9ac2C9", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0x86a3910908eCaAA31Fcd9F0fC8841D8E98f1511d", + "commit_store": "0xE99a87C9b5ed4D2b6060195DEea5106ffF655736", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x189F61D9B886Dd2975D5Abc893c8Cf5f5effda71", + "commit_store": "0xEE7e27346DCD1e711348D0F7f7ECB53a9a3a08a7", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Blast Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000023", + "bridge_tokens": [ + "0x8D122C3e8ce9C8B62b87d3551bDfD8C259Bb0771" + ], + "bridge_tokens_pools": [ + "0xFb04129aD1EEDB741CC705ebC1978a7aB63e51f6" + ], + "arm": "0x09c1Ed4b112Fb33e594F2aACfEF407e2F14d7F9b", + "router": "0xfb2f2A207dC428da81fbAFfDDe121761f8Be1194", + "price_registry": "0xc8acE9dF450FaD007755C6C9AB4f0e9c8626E29C", + "wrapped_native": "0x4200000000000000000000000000000000000023", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x85Ef19FC4C63c70744995DC38CAAEC185E0c619f", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0x92cD24C278D34C726f377703E50875d8f9535dC2", + "commit_store": "0xcE1b4D50CeD56850182Bd58Ace91171cB249B873", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Celo Alfajores": { + "is_native_fee_token": true, + "fee_token": "0x99604d0e2EfE7ABFb58BdE565b5330Bb46Ab3Dca", + "bridge_tokens": [ + "0x7e503dd1dAF90117A1b79953321043d9E6815C72" + ], + "bridge_tokens_pools": [ + "0xC6683ac4a0F62803Bec89a5355B36495ddF2C38b" + ], + "arm": "0xEbe35aA4F5e707485484c992AF2069a457b9bBB1", + "router": "0xb00E95b773528E2Ea724DB06B75113F239D15Dca", + "price_registry": "0x8F048206D11B2c69b8963E2EBd5968D141e022f4", + "wrapped_native": "0x99604d0e2EfE7ABFb58BdE565b5330Bb46Ab3Dca", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x16a020c4bbdE363FaB8481262D30516AdbcfcFc8", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0xa1b97F92D806BA040daf419AFC2765DC723683a4", + "commit_store": "0xcd92C0599Ac515e7588865cC45Eee21A74816aFc", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Gnosis Chiado": { + "is_native_fee_token": true, + "fee_token": "0x18c8a7ec7897177E4529065a7E7B0878358B3BfF", + "bridge_tokens": [ + "0xA189971a2c5AcA0DFC5Ee7a2C44a2Ae27b3CF389" + ], + "bridge_tokens_pools": [ + "0xF9a21B587111e7E8745Fb8b13750014f19DB0014" + ], + "arm": "0xfE4fB161D870D0F672Ed9C5A898569603f77983F", + "router": "0x19b1bac554111517831ACadc0FD119D23Bb14391", + "price_registry": "0x2F4ACd1f8986c6B1788159C4c9a5fC3fceCCE363", + "wrapped_native": "0x18c8a7ec7897177E4529065a7E7B0878358B3BfF", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x473b49fb592B54a4BfCD55d40E048431982879C9", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0x610F76A35E17DA4542518D85FfEa12645eF111Fc", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xE48E6AA1fc7D0411acEA95F8C6CaD972A37721D4", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x41b4A51cAfb699D9504E89d19D71F92E886028a8", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0xAae733212981e06D9C978Eb5148F8af03F54b6EF", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0x01800fCDd892e37f7829937271840A6F041bE62E", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x4ac7FBEc2A7298AbDf0E0F4fDC45015836C4bAFe", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x9aA82DBB53bf02096B771D40e9432A323a78fB26", + "commit_store": "0x5CdbA91aBC0cD81FC56bc10Ad1835C9E5fB38e5F", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0x3e33290B90fD0FF30a3FA138934DF028E4eCA348", + "commit_store": "0xCFe3556Aa42d40be09BD23aa80448a19443BE5B1", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0xbc4AD54e91b213D4279af92c0C5518c0b96cf62D", + "commit_store": "0xff84e8Dd4Fd17eaBb23b6AeA6e1981830e54389C", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0x4117953A5ceeF12f5B8C1E973b470ab83a8CebA6", + "commit_store": "0x94ad41296186E81f31e1ed0B1BcF5fa9e1721C27", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0x33d2898F8fb7714FD1661791766f40754982a343", + "commit_store": "0x55d6Df194472f02CD481e506A277c4A29D0D1bCc", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x450543b1d85ca79885851D7b74dc982981b78229", + "commit_store": "0x23B79d940A769FE31b4C867A8BAE80117f24Ca81", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xbf9036529123DE264bFA0FC7362fE25B650D4B16", + "commit_store": "0x5f7F1abD5c5EdaF2636D58B980e85355AF0Ef80d", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Kroma Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000001", + "bridge_tokens": [ + "0x6AC3e353D1DDda24d5A5416024d6E436b8817A4e" + ], + "bridge_tokens_pools": [ + "0x0eE8add19554C7bb1920A183Ed47b4FAB9Eb7601" + ], + "arm": "0x08f9Af992368FAc58C936A2c5eBc9092894CEa9b", + "router": "0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D", + "price_registry": "0xa1ed3A3aA29166C9c8448654A8cA6b7916BC8379", + "wrapped_native": "0x4200000000000000000000000000000000000001", + "src_contracts": { + "WeMix Testnet": { + "on_ramp": "0x6ea155Fc77566D9dcE01B8aa5D7968665dc4f0C5", + "deployed_at": 0 + } + }, + "dest_contracts": { + "WeMix Testnet": { + "off_ramp": "0xB602B6E5Caf08ac0C920EAE585aed100a8cF6f3B", + "commit_store": "0x89D5b13908b9063abCC6791dc724bF7B7c93634C", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Metis Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x5c48e07062aC4E2Cf4b9A768a711Aef18e8fbdA0", + "bridge_tokens": [ + "0x20Aa09AAb761e2E600d65c6929A9fd1E59821D3f" + ], + "bridge_tokens_pools": [ + "0xdE8451E952Eb43350614839cCAA84f7C8701a09C" + ], + "arm": "0xf0607A9BDdB5F54dB59ACaA0837aFec2D1c95df6", + "router": "0xaCdaBa07ECad81dc634458b98673931DD9d3Bc14", + "price_registry": "0x5DCE866b3ae6E0Ed153f0e149D7203A1B266cdF5", + "wrapped_native": "0x5c48e07062aC4E2Cf4b9A768a711Aef18e8fbdA0", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x2Eff2d1BF5C557d6289D208a7a43608f5E3FeCc2", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0x9Bb7e398ef9Acfe9cA584C39B1E233Cba62BB9f7", + "commit_store": "0x1F4B82cDebaC5e3a0Dd53183D47e51808B4a64cB", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Mode Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000006", + "bridge_tokens": [ + "0xB9d4e1141E67ECFedC8A8139b5229b7FF2BF16F5" + ], + "bridge_tokens_pools": [ + "0x20bBc874bE3Cd94C3E4689EDD5D89dD1cE8Cb7C4" + ], + "arm": "0x9eC8a0AbC75ce08978FAf67958482461bCd93B18", + "router": "0xc49ec0eB4beb48B8Da4cceC51AA9A5bD0D0A4c43", + "price_registry": "0xa733ce82a84335b2E9D864312225B0F3D5d80600", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Base Sepolia": { + "on_ramp": "0x73f7E074bd7291706a0C5412f51DB46441B1aDCB", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0xfFdE9E8c34A27BEBeaCcAcB7b3044A0A364455C9", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Base Sepolia": { + "off_ramp": "0x137a38c6b1Ad20101F93516aB2159Df525309168", + "commit_store": "0x8F43d867969F14619895d71E0A5b89E0bb20bF70", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xcD44cec849B6a8eBd5551D6DFeEcA452257Dfe4d", + "commit_store": "0xbA66f08733E6715D33edDfb5a5947676bb45d0e0", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Optimism Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4200000000000000000000000000000000000006", + "bridge_tokens": [ + "0x8aF4204e30565DF93352fE8E1De78925F6664dA7" + ], + "bridge_tokens_pools": [ + "0x3Cc9364260D80F09ccAC1eE6B07366dB598900E6" + ], + "arm": "0xF51366F72184E22cF4a7a8362508DB0d3370392d", + "router": "0x114A20A10b43D4115e5aeef7345a1A71d2a60C57", + "price_registry": "0x782a7Ba95215f2F7c3dD4C153cbB2Ae3Ec2d3215", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x1a86b29364D1B3fA3386329A361aA98A104b2742", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0x6b38CC6Fa938D5AB09Bdf0CFe580E226fDD793cE", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0xe284D2315a28c4d62C419e8474dC457b219DB969", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x835a5b8e6CA17c2bB5A336c93a4E22478E6F1C8A", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0x2Cf26fb01E9ccDb831414B766287c0A9e4551089", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0xC8b93b46BF682c39B3F65Aa1c135bC8A95A5E43a", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0xc7E53f6aB982af7A7C3e470c8cCa283d3399BDAd", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xDc2c7A3d8068C6F09F0F3648d24C84e372F6014d", + "commit_store": "0xb1aFb5cbE3c29b5Db71F21442BA9EfD450BC23C3", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0x1F350718e015EB20E5065C09F4A7a3f66888aEeD", + "commit_store": "0x98650A8EB59f75D93563aB34FcF603b1A30e4CBF", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0x0a750ca77369e03613d7640548F4b2b1c695c3Bb", + "commit_store": "0x8fEBC74C26129C8d7E60288C6dCCc75eb494aA3C", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0xCE2CE7F940B7c839384e5D7e079A6aE80e8AD6dB", + "commit_store": "0x1b9D78Ec1CEEC439F0b7eA6C428A1a607D9FA7e4", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0xD667b5706592D0b040C78fEe5EE17D243b7dCB41", + "commit_store": "0x96101BA5250EE9295c193693C1e08A55bC593664", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x260AF9b83e0d2Bb6C9015fC9f0BfF8858A0CCE68", + "commit_store": "0x7a0bB92Bc8663abe6296d0162A9b41a2Cb2E0358", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0x9C08B7712af0344188aa5087D9e6aD0f47191037", + "commit_store": "0x4BE6DB0B884169a6A207fe5cad01eB4C025a13dB", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Polygon Amoy": { + "is_native_fee_token": true, + "fee_token": "0x360ad4f9a9A8EFe9A8DCB5f461c4Cc1047E1Dcf9", + "bridge_tokens": [ + "0xcab0EF91Bee323d1A617c0a027eE753aFd6997E4" + ], + "bridge_tokens_pools": [ + "0x3064fB3EA546EE09A63AB3bD93E83D8B8525C636" + ], + "arm": "0x8b88C39D2875157aB4CE4AD3814409523d539ee1", + "router": "0x9C32fCB86BF0f4a1A8921a9Fe46de3198bb884B2", + "price_registry": "0xfb2f2A207dC428da81fbAFfDDe121761f8Be1194", + "wrapped_native": "0x360ad4f9a9A8EFe9A8DCB5f461c4Cc1047E1Dcf9", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0x8Fb98b3837578aceEA32b454f3221FE18D7Ce903", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xC6683ac4a0F62803Bec89a5355B36495ddF2C38b", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x2331F6D614C9Fd613Ff59a1aB727f1EDf6c37A68", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0xA52cDAeb43803A80B3c0C2296f5cFe57e695BE11", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x35347A2fC1f2a4c5Eae03339040d0b83b09e6FDA", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0x26546096F64B5eF9A1DcDAe70Df6F4f8c2E10C61", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0xa733ce82a84335b2E9D864312225B0F3D5d80600", + "commit_store": "0x09B0F93fC2111aE439e853884173AC5b2F809885", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0x948dfaa4842fc23e0e362Fe8D4396AaE4E6DF7EA", + "commit_store": "0x7F4e739D40E58BBd59dAD388171d18e37B26326f", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x17c542a28e08AEF5697251601C7b2B621d153D42", + "commit_store": "0x811250c20fAB9a1b7ca245453aC214ba637fBEB5", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xfFdE9E8c34A27BEBeaCcAcB7b3044A0A364455C9", + "commit_store": "0x74ED442ad211050e9C05Dc9A267E037E3d74A03B", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0xFb04129aD1EEDB741CC705ebC1978a7aB63e51f6", + "commit_store": "0x63f875240149d29136053C954Ca164a9BfA81F77", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0xdE8451E952Eb43350614839cCAA84f7C8701a09C", + "commit_store": "0xaCdaBa07ECad81dc634458b98673931DD9d3Bc14", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "Sepolia Testnet": { + "is_native_fee_token": true, + "fee_token": "0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534", + "bridge_tokens": [ + "0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05" + ], + "bridge_tokens_pools": [ + "0x38d1ef9619Cd40cf5482C045660Ae7C82Ada062c" + ], + "arm": "0x27Da8735d8d1402cEc072C234759fbbB4dABBC4A", + "router": "0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59", + "price_registry": "0x9EF7D57a4ea30b9e37794E55b0C75F2A70275dCc", + "wrapped_native": "0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0xe4Dd3B16E09c016402585a8aDFdB4A18f772a07e", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0x0477cA0a35eE05D3f9f424d88bC0977ceCf339D4", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0xD990f8aFA5BCB02f95eEd88ecB7C68f5998bD618", + "deployed_at": 0 + }, + "Base Sepolia": { + "on_ramp": "0x2B70a05320cB069e0fB55084D402343F832556E7", + "deployed_at": 0 + }, + "Blast Sepolia": { + "on_ramp": "0xDB75E9D9ca7577CcBd7232741be954cf26194a66", + "deployed_at": 0 + }, + "Celo Alfajores": { + "on_ramp": "0x3C86d16F52C10B2ff6696a0e1b8E0BcfCC085948", + "deployed_at": 0 + }, + "Gnosis Chiado": { + "on_ramp": "0x3E842E3A79A00AFdd03B52390B1caC6306Ea257E", + "deployed_at": 0 + }, + "Metis Sepolia": { + "on_ramp": "0x1C4640914cd57c5f02a68048A0fbb0E12d904223", + "deployed_at": 0 + }, + "Mode Sepolia": { + "on_ramp": "0xc630fbD4D0F6AEB00aD0793FB827b54fBB78e981", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0x69CaB5A0a08a12BaFD8f5B195989D709E396Ed4d", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0x9f656e0361Fb5Df2ac446102c8aB31855B591692", + "deployed_at": 0 + }, + "WeMix Testnet": { + "on_ramp": "0xedFc22336Eb0B9B11Ff37C07777db27BCcDe3C65", + "deployed_at": 0 + }, + "ZKSync Sepolia": { + "on_ramp": "0x1Acb3A885feA37bdA30AB99b99327b14391f500F", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xF18896AB20a09A29e64fdEbA99FDb8EC328f43b1", + "commit_store": "0x93Ff9Dd39Dc01eac1fc4d2c9211D95Ee458CAB94", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0x000b26f604eAadC3D874a4404bde6D64a97d95ca", + "commit_store": "0x2dD9273F8208B8393350508131270A6574A69784", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0xdE2d8E126e08d675fCD7fFa5a6CE49925f3Dc692", + "commit_store": "0x0050ac355a82caB31194507f94174297bf0655A7", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Base Sepolia": { + "off_ramp": "0x31c0B81832B333419f0DfD36A69F502cF9094aed", + "commit_store": "0xDFcde9d698a2B32DB2537DC9B752Cadd1D846a52", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Blast Sepolia": { + "off_ramp": "0x4e897e5cF3aC307F0541B2151A88bCD781c153a3", + "commit_store": "0xB656652841F347178e193951C4663652aCe36B74", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Celo Alfajores": { + "off_ramp": "0xB435E0f73c18C5a12C324CA1d02F81F2C3e6e761", + "commit_store": "0xbc5d74957F171e75F92c8F0E1C317A25a56a416D", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Gnosis Chiado": { + "off_ramp": "0x7db0115A0b3AAb01d30bf81123c5DD7B0C41Add5", + "commit_store": "0x6640723Ea801178c4383FA016b9781e7ef1016EF", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Metis Sepolia": { + "off_ramp": "0x4DB693A93E9d5196ECD42EC56CDEAe99dFC652ED", + "commit_store": "0xBfACd78F1412B6f93Ac23409bf456aFec1ABd845", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Mode Sepolia": { + "off_ramp": "0xbEfd8D65F6643De54F0b1268A3bf4618ff85dcB4", + "commit_store": "0x0C161D3470b45Cc677661654C30ce4AdE6aCD288", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xD50590D4438411EDe47029b0FD7901A7145E5Df6", + "commit_store": "0xe85EEE9Fd434A7b8a586Ee086E828abF41839479", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x5032cbC0C4aEeD25bb6E45D8B3fAF05DB0688C5d", + "commit_store": "0xe6201C9996Cc7B6E828E10CbE937E693d577D318", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "WeMix Testnet": { + "off_ramp": "0x46b639a3C1a4CBfD326b94a2dB7415c27157282f", + "commit_store": "0x7b74554678816b045c1e7409327E086bD436aa46", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "ZKSync Sepolia": { + "off_ramp": "0xBaABd4166C892a1081a26535875A8fA3f22937b2", + "commit_store": "0xfda2e83F4D3f42B7629134ecD6E4b29FB8A7A07B", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "WeMix Testnet": { + "is_native_fee_token": true, + "fee_token": "0xbE3686643c05f00eC46e73da594c78098F7a9Ae7", + "bridge_tokens": [ + "0xF4E4057FbBc86915F4b2d63EEFFe641C03294ffc" + ], + "bridge_tokens_pools": [ + "0x82A92B2863F93Be70D20660088Ec060720bA2fdb" + ], + "arm": "0x8f6cb63eD5e379722580DFF0A051C140C64F9619", + "router": "0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D", + "price_registry": "0x89D17571DB7C9540eeB36760E3c749C8fb984569", + "wrapped_native": "0xbE3686643c05f00eC46e73da594c78098F7a9Ae7", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0xA9DE3F7A617D67bC50c56baaCb9E0373C15EbfC6", + "deployed_at": 0 + }, + "Avalanche Fuji": { + "on_ramp": "0xC4aC84da458ba8e40210D2dF94C76E9a41f70069", + "deployed_at": 0 + }, + "BSC Testnet": { + "on_ramp": "0x5AD6eed6Be0ffaDCA4105050CF0E584D87E0c2F1", + "deployed_at": 0 + }, + "Kroma Sepolia": { + "on_ramp": "0x428C4dc89b6Bf908B82d77C9CBceA786ea8cc7D0", + "deployed_at": 0 + }, + "Optimism Sepolia": { + "on_ramp": "0x1961a7De751451F410391c251D4D4F98D71B767D", + "deployed_at": 0 + }, + "Polygon Amoy": { + "on_ramp": "0xd55148e841e76265B484d399eC71b7076ecB1216", + "deployed_at": 0 + }, + "Sepolia Testnet": { + "on_ramp": "0x4d57C6d8037C65fa66D6231844785a428310a735", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xeB1dFaB2464Bf0574D43e764E0c758f92e7ecAFb", + "commit_store": "0xcEaCa2B7890065c485f3E58657358a185Ad33791", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Avalanche Fuji": { + "off_ramp": "0x98e811Df9D2512f1aaf58D534607F583D6c54A4F", + "commit_store": "0x8e538351F6E5B2daF3c90C565C3738bca69a2716", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "BSC Testnet": { + "off_ramp": "0xB0e7f0fCcD3c961C473E7c44D939C1cDb4Cec1cB", + "commit_store": "0x4B56D8d53f1A6e0117B09700067De99581aA5542", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Kroma Sepolia": { + "off_ramp": "0xD685D2d224dd6D0Db2D56497db6270D77D9a7966", + "commit_store": "0x7e062D6880779a0347e7742058C1b1Ee4AA0B137", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Optimism Sepolia": { + "off_ramp": "0xA5f97Bc69Bf06e7C37B93265c5457420A92c5F4b", + "commit_store": "0xd48b9213583074f518D8f4336FDf35370D450132", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Polygon Amoy": { + "off_ramp": "0x6c8f5999B06FDE17B11E4e3C1062b761766F960f", + "commit_store": "0x957c3c2056192e58A8485eF31165fC490d474239", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + }, + "Sepolia Testnet": { + "off_ramp": "0x8AB103843ED9D28D2C5DAf5FdB9c3e1CE2B6c876", + "commit_store": "0x7d5297c5506ee2A7Ef121Da9bE02b6a6AD30b392", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + }, + "ZKSync Sepolia": { + "is_native_fee_token": true, + "fee_token": "0x4317b2eCD41851173175005783322D29E9bAee9E", + "arm": "0x926b4f90610Aa448f27c8e0Fd0afa4A17B7305F9", + "router": "0xA1fdA8aa9A8C4b945C45aD30647b01f07D7A0B16", + "price_registry": "0x648B6BB09bE1C5766C8AC578B9B4aC8497eA671F", + "wrapped_native": "0x4317b2eCD41851173175005783322D29E9bAee9E", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x79a9a5e9e318e8e109776569574814bbf935125a", + "deployed_at": 0 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0x18FF69051479796175852578725Fc74F58E963F8", + "commit_store": "0xF694b4FD7889480dca3CD41244e8e3395a9EFBa0", + "receiver_dapp": "0xea387241d834D04CC408f4C2FE7ef2c477E4B3E7" + } + } + } + } +} +""" + +[CCIP.Env] +TTL = '8h' + +[CCIP.Env.Network] +selected_networks = [ + 'ARBITRUM_SEPOLIA', + 'AVALANCHE_FUJI', + 'BASE_SEPOLIA', + 'BLAST_SEPOLIA', + 'BSC_TESTNET', + 'CELO_ALFAJORES', + 'GNOSIS_CHIADO', + 'KROMA_SEPOLIA', + 'MODE_SEPOLIA', + 'OPTIMISM_SEPOLIA', + 'POLYGON_AMOY', + 'SEPOLIA', + 'METIS_SEPOLIA', + 'WEMIX_TESTNET', + 'ZKSYNC_SEPOLIA' +] + +[CCIP.Groups.load] +NetworkPairs = [ + 'AVALANCHE_FUJI,ARBITRUM_SEPOLIA', + 'AVALANCHE_FUJI,BASE_SEPOLIA', + 'AVALANCHE_FUJI,OPTIMISM_SEPOLIA', + 'AVALANCHE_FUJI,POLYGON_AMOY', + 'AVALANCHE_FUJI,GNOSIS_CHIADO', + 'ARBITRUM_SEPOLIA,BASE_SEPOLIA', + 'ARBITRUM_SEPOLIA,OPTIMISM_SEPOLIA', + 'ARBITRUM_SEPOLIA,SEPOLIA', + 'ARBITRUM_SEPOLIA,GNOSIS_CHIADO', + 'BASE_SEPOLIA,MODE_SEPOLIA', + 'BASE_SEPOLIA,OPTIMISM_SEPOLIA', + 'BASE_SEPOLIA,GNOSIS_CHIADO', + 'SEPOLIA,MODE_SEPOLIA', + 'SEPOLIA,CELO_ALFAJORES', + 'SEPOLIA,METIS_SEPOLIA', + 'SEPOLIA,BLAST_SEPOLIA', + 'SEPOLIA,ZKSYNC_SEPOLIA', + 'KROMA_SEPOLIA,WEMIX_TESTNET', + 'OPTIMISM_SEPOLIA,GNOSIS_CHIADO', + 'BSC_TESTNET,GNOSIS_CHIADO', +] + +BiDirectionalLane = true +PhaseTimeout = '20m' +ExistingDeployment = true + +[CCIP.Groups.load.TokenConfig] +NoOfTokensPerChain = 1 + +[CCIP.Groups.load.LoadProfile] +RequestPerUnitTime = [1] +TimeUnit = '6m' +TestDuration = '24h' +TestRunName = 'SoakTest_prod_testnet' +FailOnFirstErrorInLoad = true + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'Data' +DestGasLimit = 0 +DataLength = 100 +NoOfTokens = 1 +AmountPerToken = 1 \ No newline at end of file diff --git a/integration-tests/chaos/automation_chaos_test.go b/integration-tests/chaos/automation_chaos_test.go index 59b52f8e0f2..cde5962390a 100644 --- a/integration-tests/chaos/automation_chaos_test.go +++ b/integration-tests/chaos/automation_chaos_test.go @@ -6,9 +6,6 @@ import ( "testing" "time" - ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" - - ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" "github.com/smartcontractkit/chainlink/integration-tests/actions/automationv2" seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" @@ -245,53 +242,15 @@ func TestAutomationChaos(t *testing.T) { a := automationv2.NewAutomationTestK8s(l, chainClient, chainlinkNodes) a.SetMercuryCredentialName("cred1") - conf := config.Automation.AutomationConfig - a.RegistrySettings = contracts.KeeperRegistrySettings{ - PaymentPremiumPPB: *conf.RegistrySettings.PaymentPremiumPPB, - FlatFeeMicroLINK: *conf.RegistrySettings.FlatFeeMicroLINK, - CheckGasLimit: *conf.RegistrySettings.CheckGasLimit, - StalenessSeconds: conf.RegistrySettings.StalenessSeconds, - GasCeilingMultiplier: *conf.RegistrySettings.GasCeilingMultiplier, - MaxPerformGas: *conf.RegistrySettings.MaxPerformGas, - MinUpkeepSpend: conf.RegistrySettings.MinUpkeepSpend, - FallbackGasPrice: conf.RegistrySettings.FallbackGasPrice, - FallbackLinkPrice: conf.RegistrySettings.FallbackLinkPrice, - MaxCheckDataSize: *conf.RegistrySettings.MaxCheckDataSize, - MaxPerformDataSize: *conf.RegistrySettings.MaxPerformDataSize, - MaxRevertDataSize: *conf.RegistrySettings.MaxRevertDataSize, - RegistryVersion: rv, - } + a.RegistrySettings = actions.ReadRegistryConfig(config) + a.RegistrySettings.RegistryVersion = rv + a.PluginConfig = actions.ReadPluginConfig(config) + a.PublicConfig = actions.ReadPublicConfig(config) a.RegistrarSettings = contracts.KeeperRegistrarSettings{ AutoApproveConfigType: uint8(2), AutoApproveMaxAllowed: 1000, MinLinkJuels: big.NewInt(0), } - plCfg := config.GetAutomationConfig().AutomationConfig.PluginConfig - a.PluginConfig = ocr2keepers30config.OffchainConfig{ - TargetProbability: *plCfg.TargetProbability, - TargetInRounds: *plCfg.TargetInRounds, - PerformLockoutWindow: *plCfg.PerformLockoutWindow, - GasLimitPerReport: *plCfg.GasLimitPerReport, - GasOverheadPerUpkeep: *plCfg.GasOverheadPerUpkeep, - MinConfirmations: *plCfg.MinConfirmations, - MaxUpkeepBatchSize: *plCfg.MaxUpkeepBatchSize, - } - pubCfg := config.GetAutomationConfig().AutomationConfig.PublicConfig - a.PublicConfig = ocr3.PublicConfig{ - DeltaProgress: *pubCfg.DeltaProgress, - DeltaResend: *pubCfg.DeltaResend, - DeltaInitial: *pubCfg.DeltaInitial, - DeltaRound: *pubCfg.DeltaRound, - DeltaGrace: *pubCfg.DeltaGrace, - DeltaCertifiedCommitRequest: *pubCfg.DeltaCertifiedCommitRequest, - DeltaStage: *pubCfg.DeltaStage, - RMax: *pubCfg.RMax, - MaxDurationQuery: *pubCfg.MaxDurationQuery, - MaxDurationObservation: *pubCfg.MaxDurationObservation, - MaxDurationShouldAcceptAttestedReport: *pubCfg.MaxDurationShouldAcceptAttestedReport, - MaxDurationShouldTransmitAcceptedReport: *pubCfg.MaxDurationShouldTransmitAcceptedReport, - F: *pubCfg.F, - } a.SetupAutomationDeployment(t) diff --git a/integration-tests/contracts/ethereum_keeper_contracts.go b/integration-tests/contracts/ethereum_keeper_contracts.go index 38aa5c58f0f..90f1af0319b 100644 --- a/integration-tests/contracts/ethereum_keeper_contracts.go +++ b/integration-tests/contracts/ethereum_keeper_contracts.go @@ -174,7 +174,7 @@ func (rcs *KeeperRegistrySettings) Create23OnchainConfig(registrar string, regis ChainModule: chainModuleAddress, ReorgProtectionEnabled: reorgProtectionEnabled, FinanceAdmin: registryOwnerAddress, - FallbackNativePrice: rcs.FallbackLinkPrice, // Just use the LINK price + FallbackNativePrice: rcs.FallbackNativePrice, } } diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 160f682a23d..4a1be879b9d 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -38,8 +38,8 @@ require ( github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240910151738-3f318badcfb5 github.com/smartcontractkit/chain-selectors v1.0.23 github.com/smartcontractkit/chainlink-automation v1.0.4 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20240916150615-85b8aa5fa7e6 - github.com/smartcontractkit/chainlink-common v0.2.2-0.20240913161926-ce5d667907ce + github.com/smartcontractkit/chainlink-ccip v0.0.0-20240917180332-5a68498d1612 + github.com/smartcontractkit/chainlink-common v0.2.2-0.20240916150342-36cb47701edf github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.0 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.6-0.20240918133844-005e8bc2da09 github.com/smartcontractkit/chainlink-testing-framework/lib/grafana v1.50.0 @@ -406,7 +406,7 @@ require ( github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240911175228-daf2600bb7b7 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240906125718-9f0a98d32fbc // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240916152957-433914114bd2 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240910155501-42f20443189f // indirect github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240911182932-3c609a6ac664 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240911194142-506bc469d8ae // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index af7432cf895..805cd37a636 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1423,14 +1423,14 @@ github.com/smartcontractkit/chain-selectors v1.0.23 h1:D2Eaex4Cw/O7Lg3tX6WklOqnj github.com/smartcontractkit/chain-selectors v1.0.23/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20240916150615-85b8aa5fa7e6 h1:cbHlV2CSphQ+ghDye21M8ym0aAO/Y649H2Mg60M2AuE= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20240916150615-85b8aa5fa7e6/go.mod h1:Lv77O13ZxOdmlvnu2vaUC0Lg+t3JAL+N+9K8dRsgmDI= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240913161926-ce5d667907ce h1:qXS0aWiDFDoLRCB+kSGnzp77iYT2luflUyzE5BnNmpY= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240913161926-ce5d667907ce/go.mod h1:sjiiPwd4KsYOCf68MwL86EKphdXeT66EY7j53WH5DCc= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240917180332-5a68498d1612 h1:xPEM9XbfZmv8N3NjZ7AX5salonll/LdXrbb8JCbA4FE= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240917180332-5a68498d1612/go.mod h1:Lv77O13ZxOdmlvnu2vaUC0Lg+t3JAL+N+9K8dRsgmDI= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240916150342-36cb47701edf h1:1AlTUkT5D8HmvU9bwDoIN54/EFyOnRBl7gnXZVrYXEA= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240916150342-36cb47701edf/go.mod h1:l8NTByXUdGGJX+vyKYI6yX1/HIpM14F8Wm9BkU3Q4Qo= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240911175228-daf2600bb7b7 h1:lTGIOQYLk1Ufn++X/AvZnt6VOcuhste5yp+C157No/Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240911175228-daf2600bb7b7/go.mod h1:BMYE1vC/pGmdFSsOJdPrAA0/4gZ0Xo0SxTMdGspBtRo= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240906125718-9f0a98d32fbc h1:tRmTlaoAt+7FakMXXgeCuRPmzzBo5jsGpeCVvcU6KMc= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240906125718-9f0a98d32fbc/go.mod h1:PwPcmQNAzVmU8r8JWKrDRgvXesDwxnqbMD6DvYt/Z7M= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240916152957-433914114bd2 h1:yRk4ektpx/UxwarqAfgxUXLrsYXlaNeP1NOwzHGrK2Q= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240916152957-433914114bd2/go.mod h1:rNhNSrrRMvkgAm5SA6bNTdh2340bTQQZdUVNtZ2o2bk= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240910155501-42f20443189f h1:p4p3jBT91EQyLuAMvHD+zNJsuAYI/QjJbzuGUJ7wIgg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240910155501-42f20443189f/go.mod h1:FLlWBt2hwiMVgt9AcSo6wBJYIRd/nsc8ENbV1Wir1bw= github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240911182932-3c609a6ac664 h1:JPs35oSO07PK3Qv7Kyv0GJHVLacIE1IkrvefaPyBjKs= diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go index cb2606f00be..6c49d1be44b 100644 --- a/integration-tests/load/automationv2_1/automationv2_1_test.go +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -22,12 +22,8 @@ import ( "github.com/slack-go/slack" "github.com/stretchr/testify/require" - ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" - "github.com/smartcontractkit/chainlink-testing-framework/wasp" - ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/pkg/helm/chainlink" @@ -154,6 +150,7 @@ func setUpDataStreamsWireMock(url string) error { func TestLogTrigger(t *testing.T) { ctx := tests.Context(t) l := logging.GetTestLogger(t) + registryVersion := contractseth.RegistryVersion_2_1 loadedTestConfig, err := tc.GetConfig([]string{"Load"}, tc.Automation) if err != nil { @@ -315,55 +312,15 @@ Load Config: require.NoError(t, err, "Error deploying multicall contract") a := automationv2.NewAutomationTestK8s(l, chainClient, chainlinkNodes) - conf := loadedTestConfig.Automation.AutomationConfig - a.RegistrySettings = contracts.KeeperRegistrySettings{ - PaymentPremiumPPB: *conf.RegistrySettings.PaymentPremiumPPB, - FlatFeeMicroLINK: *conf.RegistrySettings.FlatFeeMicroLINK, - CheckGasLimit: *conf.RegistrySettings.CheckGasLimit, - StalenessSeconds: conf.RegistrySettings.StalenessSeconds, - GasCeilingMultiplier: *conf.RegistrySettings.GasCeilingMultiplier, - MaxPerformGas: *conf.RegistrySettings.MaxPerformGas, - MinUpkeepSpend: conf.RegistrySettings.MinUpkeepSpend, - FallbackGasPrice: conf.RegistrySettings.FallbackGasPrice, - FallbackLinkPrice: conf.RegistrySettings.FallbackLinkPrice, - MaxCheckDataSize: *conf.RegistrySettings.MaxCheckDataSize, - MaxPerformDataSize: *conf.RegistrySettings.MaxPerformDataSize, - MaxRevertDataSize: *conf.RegistrySettings.MaxRevertDataSize, - RegistryVersion: contractseth.RegistryVersion_2_1, - } + a.RegistrySettings = actions.ReadRegistryConfig(loadedTestConfig) + a.RegistrySettings.RegistryVersion = registryVersion + a.PluginConfig = actions.ReadPluginConfig(loadedTestConfig) + a.PublicConfig = actions.ReadPublicConfig(loadedTestConfig) a.RegistrarSettings = contracts.KeeperRegistrarSettings{ AutoApproveConfigType: uint8(2), - AutoApproveMaxAllowed: math.MaxUint16, + AutoApproveMaxAllowed: 1000, MinLinkJuels: big.NewInt(0), } - a.PluginConfig = ocr2keepers30config.OffchainConfig{ - TargetProbability: *conf.PluginConfig.TargetProbability, - TargetInRounds: *conf.PluginConfig.TargetInRounds, - PerformLockoutWindow: *conf.PluginConfig.PerformLockoutWindow, - GasLimitPerReport: *conf.PluginConfig.GasLimitPerReport, - GasOverheadPerUpkeep: *conf.PluginConfig.GasOverheadPerUpkeep, - MinConfirmations: *conf.PluginConfig.MinConfirmations, - MaxUpkeepBatchSize: *conf.PluginConfig.MaxUpkeepBatchSize, - LogProviderConfig: ocr2keepers30config.LogProviderConfig{ - BlockRate: *conf.PluginConfig.LogProviderConfig.BlockRate, - LogLimit: *conf.PluginConfig.LogProviderConfig.LogLimit, - }, - } - a.PublicConfig = ocr3.PublicConfig{ - DeltaProgress: *conf.PublicConfig.DeltaProgress, - DeltaResend: *conf.PublicConfig.DeltaResend, - DeltaInitial: *conf.PublicConfig.DeltaInitial, - DeltaRound: *conf.PublicConfig.DeltaRound, - DeltaGrace: *conf.PublicConfig.DeltaGrace, - DeltaCertifiedCommitRequest: *conf.PublicConfig.DeltaCertifiedCommitRequest, - DeltaStage: *conf.PublicConfig.DeltaStage, - RMax: *conf.PublicConfig.RMax, - MaxDurationQuery: *conf.PublicConfig.MaxDurationQuery, - MaxDurationObservation: *conf.PublicConfig.MaxDurationObservation, - MaxDurationShouldAcceptAttestedReport: *conf.PublicConfig.MaxDurationShouldAcceptAttestedReport, - MaxDurationShouldTransmitAcceptedReport: *conf.PublicConfig.MaxDurationShouldTransmitAcceptedReport, - F: *conf.PublicConfig.F, - } if *loadedTestConfig.Automation.DataStreams.Enabled { a.SetMercuryCredentialName("cred1") diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index f800ed65b6d..17c1466a901 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -15,21 +15,29 @@ require ( github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.33.0 github.com/slack-go/slack v0.12.2 +<<<<<<< TT-1550-Provide-PoC-for-keeping-test-configs-in-git github.com/smartcontractkit/chainlink-automation v1.0.4 github.com/smartcontractkit/chainlink-common v0.2.2-0.20240913161926-ce5d667907ce github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.6-0.20240918133844-005e8bc2da09 +======= + github.com/smartcontractkit/chainlink-common v0.2.2-0.20240916150342-36cb47701edf + github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.5 +>>>>>>> develop github.com/smartcontractkit/chainlink-testing-framework/seth v1.50.1 github.com/smartcontractkit/chainlink-testing-framework/wasp v1.50.0 github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20240214231432-4ad5eb95178c github.com/smartcontractkit/chainlink/v2 v2.9.0-beta0.0.20240216210048-da02459ddad8 - github.com/smartcontractkit/libocr v0.0.0-20240717100443-f6226e09bee7 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/stretchr/testify v1.9.0 github.com/wiremock/go-wiremock v1.9.0 go.uber.org/ratelimit v0.3.0 ) -require github.com/AlekSi/pointer v1.1.0 // indirect +require ( + github.com/AlekSi/pointer v1.1.0 // indirect + github.com/smartcontractkit/chainlink-automation v1.0.4 // indirect + github.com/smartcontractkit/libocr v0.0.0-20240717100443-f6226e09bee7 // indirect +) require ( contrib.go.opencensus.io/exporter/stackdriver v0.13.5 // indirect @@ -384,9 +392,9 @@ require ( github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/chain-selectors v1.0.23 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20240916150615-85b8aa5fa7e6 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20240917180332-5a68498d1612 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240911175228-daf2600bb7b7 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240906125718-9f0a98d32fbc // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240916152957-433914114bd2 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240910155501-42f20443189f // indirect github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240911182932-3c609a6ac664 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240911194142-506bc469d8ae // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 3d477ba5cb1..a3245180e53 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1397,14 +1397,14 @@ github.com/smartcontractkit/chain-selectors v1.0.23 h1:D2Eaex4Cw/O7Lg3tX6WklOqnj github.com/smartcontractkit/chain-selectors v1.0.23/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20240916150615-85b8aa5fa7e6 h1:cbHlV2CSphQ+ghDye21M8ym0aAO/Y649H2Mg60M2AuE= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20240916150615-85b8aa5fa7e6/go.mod h1:Lv77O13ZxOdmlvnu2vaUC0Lg+t3JAL+N+9K8dRsgmDI= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240913161926-ce5d667907ce h1:qXS0aWiDFDoLRCB+kSGnzp77iYT2luflUyzE5BnNmpY= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240913161926-ce5d667907ce/go.mod h1:sjiiPwd4KsYOCf68MwL86EKphdXeT66EY7j53WH5DCc= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240917180332-5a68498d1612 h1:xPEM9XbfZmv8N3NjZ7AX5salonll/LdXrbb8JCbA4FE= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240917180332-5a68498d1612/go.mod h1:Lv77O13ZxOdmlvnu2vaUC0Lg+t3JAL+N+9K8dRsgmDI= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240916150342-36cb47701edf h1:1AlTUkT5D8HmvU9bwDoIN54/EFyOnRBl7gnXZVrYXEA= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240916150342-36cb47701edf/go.mod h1:l8NTByXUdGGJX+vyKYI6yX1/HIpM14F8Wm9BkU3Q4Qo= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240911175228-daf2600bb7b7 h1:lTGIOQYLk1Ufn++X/AvZnt6VOcuhste5yp+C157No/Q= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240911175228-daf2600bb7b7/go.mod h1:BMYE1vC/pGmdFSsOJdPrAA0/4gZ0Xo0SxTMdGspBtRo= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240906125718-9f0a98d32fbc h1:tRmTlaoAt+7FakMXXgeCuRPmzzBo5jsGpeCVvcU6KMc= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240906125718-9f0a98d32fbc/go.mod h1:PwPcmQNAzVmU8r8JWKrDRgvXesDwxnqbMD6DvYt/Z7M= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240916152957-433914114bd2 h1:yRk4ektpx/UxwarqAfgxUXLrsYXlaNeP1NOwzHGrK2Q= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240916152957-433914114bd2/go.mod h1:rNhNSrrRMvkgAm5SA6bNTdh2340bTQQZdUVNtZ2o2bk= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240910155501-42f20443189f h1:p4p3jBT91EQyLuAMvHD+zNJsuAYI/QjJbzuGUJ7wIgg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240910155501-42f20443189f/go.mod h1:FLlWBt2hwiMVgt9AcSo6wBJYIRd/nsc8ENbV1Wir1bw= github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240911182932-3c609a6ac664 h1:JPs35oSO07PK3Qv7Kyv0GJHVLacIE1IkrvefaPyBjKs= diff --git a/integration-tests/reorg/automation_reorg_test.go b/integration-tests/reorg/automation_reorg_test.go index 15b3b745827..85ca4b4264a 100644 --- a/integration-tests/reorg/automation_reorg_test.go +++ b/integration-tests/reorg/automation_reorg_test.go @@ -10,10 +10,8 @@ import ( "strings" "testing" - ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" "go.uber.org/zap/zapcore" - ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" "github.com/smartcontractkit/chainlink-testing-framework/lib/testreporters" sethUtils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" "github.com/smartcontractkit/chainlink/integration-tests/actions/automationv2" @@ -134,43 +132,17 @@ func TestAutomationReorg(t *testing.T) { gethRPCClient := ctfClient.NewRPCClient(evmNetwork.HTTPURLs[0], nil) - registryConfig := actions.AutomationDefaultRegistryConfig(config) - registryConfig.RegistryVersion = registryVersion - a := automationv2.NewAutomationTestDocker(l, sethClient, nodeClients) a.SetMercuryCredentialName("cred1") - a.RegistrySettings = registryConfig + a.RegistrySettings = actions.ReadRegistryConfig(config) + a.RegistrySettings.RegistryVersion = registryVersion + a.PluginConfig = actions.ReadPluginConfig(config) + a.PublicConfig = actions.ReadPublicConfig(config) a.RegistrarSettings = contracts.KeeperRegistrarSettings{ AutoApproveConfigType: uint8(2), AutoApproveMaxAllowed: 1000, MinLinkJuels: big.NewInt(0), } - plCfg := config.GetAutomationConfig().AutomationConfig.PluginConfig - a.PluginConfig = ocr2keepers30config.OffchainConfig{ - TargetProbability: *plCfg.TargetProbability, - TargetInRounds: *plCfg.TargetInRounds, - PerformLockoutWindow: *plCfg.PerformLockoutWindow, - GasLimitPerReport: *plCfg.GasLimitPerReport, - GasOverheadPerUpkeep: *plCfg.GasOverheadPerUpkeep, - MinConfirmations: *plCfg.MinConfirmations, - MaxUpkeepBatchSize: *plCfg.MaxUpkeepBatchSize, - } - pubCfg := config.GetAutomationConfig().AutomationConfig.PublicConfig - a.PublicConfig = ocr3.PublicConfig{ - DeltaProgress: *pubCfg.DeltaProgress, - DeltaResend: *pubCfg.DeltaResend, - DeltaInitial: *pubCfg.DeltaInitial, - DeltaRound: *pubCfg.DeltaRound, - DeltaGrace: *pubCfg.DeltaGrace, - DeltaCertifiedCommitRequest: *pubCfg.DeltaCertifiedCommitRequest, - DeltaStage: *pubCfg.DeltaStage, - RMax: *pubCfg.RMax, - MaxDurationQuery: *pubCfg.MaxDurationQuery, - MaxDurationObservation: *pubCfg.MaxDurationObservation, - MaxDurationShouldAcceptAttestedReport: *pubCfg.MaxDurationShouldAcceptAttestedReport, - MaxDurationShouldTransmitAcceptedReport: *pubCfg.MaxDurationShouldTransmitAcceptedReport, - F: *pubCfg.F, - } a.SetupAutomationDeployment(t) a.SetDockerEnv(env) diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 7844435760d..f5a329a1a29 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -20,9 +20,6 @@ import ( "github.com/onsi/gomega" "github.com/stretchr/testify/require" - ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" - - ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" ctfTestEnv "github.com/smartcontractkit/chainlink-testing-framework/lib/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" @@ -126,7 +123,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { isMercury := isMercuryV02 || isMercuryV03 a := setupAutomationTestDocker( - t, registryVersion, actions.AutomationDefaultRegistryConfig(cfg), isMercuryV02, isMercuryV03, &cfg, + t, registryVersion, actions.ReadRegistryConfig(cfg), isMercuryV02, isMercuryV03, &cfg, ) sb, err := a.ChainClient.Client.BlockNumber(context.Background()) @@ -265,7 +262,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( - t, registryVersion, actions.AutomationDefaultRegistryConfig(config), false, false, &config, + t, registryVersion, actions.ReadRegistryConfig(config), false, false, &config, ) sb, err := a.ChainClient.Client.BlockNumber(context.Background()) @@ -448,7 +445,7 @@ func TestAutomationAddFunds(t *testing.T) { config, err := tc.GetConfig([]string{"Smoke"}, tc.Automation) require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( - t, registryVersion, actions.AutomationDefaultRegistryConfig(config), false, false, &config, + t, registryVersion, actions.ReadRegistryConfig(config), false, false, &config, ) sb, err := a.ChainClient.Client.BlockNumber(context.Background()) @@ -528,7 +525,7 @@ func TestAutomationPauseUnPause(t *testing.T) { require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( - t, registryVersion, actions.AutomationDefaultRegistryConfig(config), false, false, &config, + t, registryVersion, actions.ReadRegistryConfig(config), false, false, &config, ) sb, err := a.ChainClient.Client.BlockNumber(context.Background()) @@ -629,7 +626,7 @@ func TestAutomationRegisterUpkeep(t *testing.T) { require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( - t, registryVersion, actions.AutomationDefaultRegistryConfig(config), false, false, &config, + t, registryVersion, actions.ReadRegistryConfig(config), false, false, &config, ) sb, err := a.ChainClient.Client.BlockNumber(context.Background()) @@ -725,7 +722,7 @@ func TestAutomationPauseRegistry(t *testing.T) { require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( - t, registryVersion, actions.AutomationDefaultRegistryConfig(config), false, false, &config, + t, registryVersion, actions.ReadRegistryConfig(config), false, false, &config, ) sb, err := a.ChainClient.Client.BlockNumber(context.Background()) @@ -805,7 +802,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( - t, registryVersion, actions.AutomationDefaultRegistryConfig(config), false, false, &config, + t, registryVersion, actions.ReadRegistryConfig(config), false, false, &config, ) sb, err := a.ChainClient.Client.BlockNumber(context.Background()) @@ -913,7 +910,7 @@ func TestAutomationPerformSimulation(t *testing.T) { require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( - t, registryVersion, actions.AutomationDefaultRegistryConfig(config), false, false, &config, + t, registryVersion, actions.ReadRegistryConfig(config), false, false, &config, ) sb, err := a.ChainClient.Client.BlockNumber(context.Background()) @@ -984,7 +981,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { config, err := tc.GetConfig([]string{"Smoke"}, tc.Automation) require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( - t, registryVersion, actions.AutomationDefaultRegistryConfig(config), false, false, &config, + t, registryVersion, actions.ReadRegistryConfig(config), false, false, &config, ) sb, err := a.ChainClient.Client.BlockNumber(context.Background()) @@ -1091,7 +1088,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { } // Now increase checkGasLimit on registry - highCheckGasLimit := actions.AutomationDefaultRegistryConfig(config) + highCheckGasLimit := actions.ReadRegistryConfig(config) highCheckGasLimit.CheckGasLimit = uint32(5000000) highCheckGasLimit.RegistryVersion = registryVersion @@ -1139,7 +1136,7 @@ func TestUpdateCheckData(t *testing.T) { require.NoError(t, err, "Failed to get config") a := setupAutomationTestDocker( - t, registryVersion, actions.AutomationDefaultRegistryConfig(config), false, false, &config, + t, registryVersion, actions.ReadRegistryConfig(config), false, false, &config, ) sb, err := a.ChainClient.Client.BlockNumber(context.Background()) @@ -1220,7 +1217,7 @@ func TestSetOffchainConfigWithMaxGasPrice(t *testing.T) { t.Fatal(err) } a := setupAutomationTestDocker( - t, registryVersion, actions.AutomationDefaultRegistryConfig(config), false, false, &config, + t, registryVersion, actions.ReadRegistryConfig(config), false, false, &config, ) sb, err := a.ChainClient.Client.BlockNumber(context.Background()) @@ -1446,32 +1443,8 @@ func setupAutomationTestDocker( AutoApproveMaxAllowed: 1000, MinLinkJuels: big.NewInt(0), } - plCfg := automationTestConfig.GetAutomationConfig().AutomationConfig.PluginConfig - a.PluginConfig = ocr2keepers30config.OffchainConfig{ - TargetProbability: *plCfg.TargetProbability, - TargetInRounds: *plCfg.TargetInRounds, - PerformLockoutWindow: *plCfg.PerformLockoutWindow, - GasLimitPerReport: *plCfg.GasLimitPerReport, - GasOverheadPerUpkeep: *plCfg.GasOverheadPerUpkeep, - MinConfirmations: *plCfg.MinConfirmations, - MaxUpkeepBatchSize: *plCfg.MaxUpkeepBatchSize, - } - pubCfg := automationTestConfig.GetAutomationConfig().AutomationConfig.PublicConfig - a.PublicConfig = ocr3.PublicConfig{ - DeltaProgress: *pubCfg.DeltaProgress, - DeltaResend: *pubCfg.DeltaResend, - DeltaInitial: *pubCfg.DeltaInitial, - DeltaRound: *pubCfg.DeltaRound, - DeltaGrace: *pubCfg.DeltaGrace, - DeltaCertifiedCommitRequest: *pubCfg.DeltaCertifiedCommitRequest, - DeltaStage: *pubCfg.DeltaStage, - RMax: *pubCfg.RMax, - MaxDurationQuery: *pubCfg.MaxDurationQuery, - MaxDurationObservation: *pubCfg.MaxDurationObservation, - MaxDurationShouldAcceptAttestedReport: *pubCfg.MaxDurationShouldAcceptAttestedReport, - MaxDurationShouldTransmitAcceptedReport: *pubCfg.MaxDurationShouldTransmitAcceptedReport, - F: *pubCfg.F, - } + a.PluginConfig = actions.ReadPluginConfig(automationTestConfig) + a.PublicConfig = actions.ReadPublicConfig(automationTestConfig) a.SetupAutomationDeployment(t) a.SetDockerEnv(env) diff --git a/integration-tests/testconfig/automation/automation.toml b/integration-tests/testconfig/automation/automation.toml index a6b8ee5ad9d..61662a3dab3 100644 --- a/integration-tests/testconfig/automation/automation.toml +++ b/integration-tests/testconfig/automation/automation.toml @@ -82,6 +82,7 @@ max_perform_gas=5_000_000 min_upkeep_spend=0 fallback_gas_price=200_000_000_000 fallback_link_price=2_000_000_000_000_000_000 +fallback_native_price=2_000_000_000_000_000_000 max_check_data_size=5_000 max_perform_data_size=5_000 max_revert_data_size=5_000 @@ -148,6 +149,7 @@ max_perform_gas=5_000_000 min_upkeep_spend=0 fallback_gas_price=200_000_000_000 fallback_link_price=2_000_000_000_000_000_000 +fallback_native_price=2_000_000_000_000_000_000 max_check_data_size=5_000 max_perform_data_size=5_000 max_revert_data_size=5_000 @@ -199,6 +201,7 @@ max_perform_gas=5_000_000 min_upkeep_spend=0 fallback_gas_price=200_000_000_000 fallback_link_price=2_000_000_000_000_000_000 +fallback_native_price=2_000_000_000_000_000_000 max_check_data_size=5_000 max_perform_data_size=5_000 max_revert_data_size=5_000 @@ -293,6 +296,7 @@ max_perform_gas=5_000_000 min_upkeep_spend=0 fallback_gas_price=200_000_000_000 fallback_link_price=2_000_000_000_000_000_000 +fallback_native_price=2_000_000_000_000_000_000 max_check_data_size=5_000 max_perform_data_size=5_000 max_revert_data_size=5_000 @@ -300,10 +304,246 @@ max_revert_data_size=5_000 [Load.Pyroscope] enabled=false +# automation benchmark test specific overrides [Benchmark.Logging.Grafana] base_url="https://grafana.ops.prod.cldev.sh" dashboard_url="/d/Q8n6m1unz/chainlink-automation-benchmark-test" +# will retry roughly for 1h before giving up (900 * 4s) +[Benchmark.Automation.Resiliency] +# number of retries before giving up +contract_call_limit = 900 +# static interval between retries +contract_call_interval = "4s" + +[Benchmark.Seth] +# keeper benchmark running on simulated network requires 100k per node +root_key_funds_buffer = 1_000_000 + +[Benchmark.Automation] +[Benchmark.Automation.General] +number_of_nodes=6 +duration=3600 +block_time=1 +spec_type="minimum" +chainlink_node_log_level="info" +use_prometheus=false +remove_namespace = true + +[Benchmark.Automation.Benchmark] +registry_to_test = "2_1" +number_of_registries = 1 +number_of_nodes = 6 +number_of_upkeeps = 1000 +upkeep_gas_limit = 1500000 +check_gas_to_burn = 10000 +perform_gas_to_burn = 1000 +block_range = 3600 +block_interval = 60 +forces_single_tx_key = false +delete_jobs_on_end = true + +[Benchmark.NodeConfig] +BaseConfigTOML = """ +[Feature] +LogPoller = true + +[OCR2] +Enabled = true + +[P2P] +[P2P.V2] +Enabled = true +AnnounceAddresses = ["0.0.0.0:6690"] +ListenAddresses = ["0.0.0.0:6690"] +[Keeper] +TurnLookBack = 0 +[WebServer] +HTTPWriteTimeout = '1h' +""" + +CommonChainConfigTOML = """ +""" + +[Benchmark.NodeConfig.ChainConfigTOMLByChainID] +# applicable for simulated chain +1337 = """ +FinalityDepth = 50 +LogPollInterval = '1s' +MinIncomingConfirmations = 1 + +[HeadTracker] +HistoryDepth = 100 + +[GasEstimator] +Mode = 'FixedPrice' +LimitDefault = 5_000_000 +""" + +[Benchmark.Automation.AutomationConfig] +use_log_buffer_v1=false + +[Benchmark.Automation.AutomationConfig.PublicConfig] +delta_progress=10_000_000_000 +delta_resend=15_000_000_000 +delta_initial=500_000_000 +delta_round=1_000_000_000 +delta_grace=200_000_000 +delta_certified_commit_request=300_000_000 +delta_stage=30_000_000_000 +r_max=24 +f=1 +max_duration_query=20_000_000 +max_duration_observation=20_000_000 +max_duration_should_accept_attested_report=1_200_000_000 +max_duration_should_transmit_accepted_report=20_000_000 + +[Benchmark.Automation.AutomationConfig.PluginConfig] +perform_lockout_window=3_600_000 +target_probability="0.999" +target_in_rounds=1 +min_confirmations=0 +gas_limit_per_report=10_300_000 +gas_overhead_per_upkeep=300_000 +max_upkeep_batch_size=10 + +[Benchmark.Automation.AutomationConfig.PluginConfig.LogProviderConfig] +block_rate=1 +log_limit=2 + +[Benchmark.Automation.AutomationConfig.RegistrySettings] +payment_premium_ppb=0 +flat_fee_micro_link=40000 +check_gas_limit=45_000_000 +staleness_seconds=90_000 +gas_ceiling_multiplier=2 +max_perform_gas=5_000_000 +min_upkeep_spend=0 +fallback_gas_price=200_000_000_000 +fallback_link_price=2_000_000_000_000_000_000 +fallback_native_price=2_000_000_000_000_000_000 +max_check_data_size=5_000 +max_perform_data_size=5_000 +max_revert_data_size=5_000 + +# automation soak test specific overrides [Soak.Logging.Grafana] base_url="https://grafana.ops.prod.cldev.sh" -dashboard_url="/d/Q8n6m1unz/chainlink-automation-benchmark-test" \ No newline at end of file +dashboard_url="/d/Q8n6m1unz/chainlink-automation-benchmark-test" + +# will retry roughly for 1h before giving up (900 * 4s) +[Soak.Automation.Resiliency] +# number of retries before giving up +contract_call_limit = 900 +# static interval between retries +contract_call_interval = "4s" + +[Soak.Seth] +# keeper benchmark running on simulated network requires 100k per node +root_key_funds_buffer = 1_000_000 + +[Soak.Automation] +[Soak.Automation.General] +number_of_nodes=6 +duration=3600 +block_time=1 +spec_type="minimum" +chainlink_node_log_level="info" +use_prometheus=false +remove_namespace = true + +[Soak.Automation.Benchmark] +registry_to_test = "2_1" +number_of_registries = 1 +number_of_nodes = 6 +number_of_upkeeps = 50 +upkeep_gas_limit = 1500000 +check_gas_to_burn = 10000 +perform_gas_to_burn = 1000 +block_range = 28800 +block_interval = 300 +forces_single_tx_key = false +delete_jobs_on_end = true + +[Soak.NodeConfig] +BaseConfigTOML = """ +[Feature] +LogPoller = true + +[OCR2] +Enabled = true + +[P2P] +[P2P.V2] +Enabled = true +AnnounceAddresses = ["0.0.0.0:6690"] +ListenAddresses = ["0.0.0.0:6690"] +[Keeper] +TurnLookBack = 0 +[WebServer] +HTTPWriteTimeout = '1h' +""" + +CommonChainConfigTOML = """ +""" + +[Soak.NodeConfig.ChainConfigTOMLByChainID] +# applicable for simulated chain +1337 = """ +FinalityDepth = 50 +LogPollInterval = '1s' +MinIncomingConfirmations = 1 + +[HeadTracker] +HistoryDepth = 100 + +[GasEstimator] +Mode = 'FixedPrice' +LimitDefault = 5_000_000 +""" + +[Soak.Automation.AutomationConfig] +use_log_buffer_v1=false + +[Soak.Automation.AutomationConfig.PublicConfig] +delta_progress=10_000_000_000 +delta_resend=15_000_000_000 +delta_initial=500_000_000 +delta_round=1_000_000_000 +delta_grace=200_000_000 +delta_certified_commit_request=300_000_000 +delta_stage=30_000_000_000 +r_max=24 +f=1 +max_duration_query=20_000_000 +max_duration_observation=20_000_000 +max_duration_should_accept_attested_report=1_200_000_000 +max_duration_should_transmit_accepted_report=20_000_000 + +[Soak.Automation.AutomationConfig.PluginConfig] +perform_lockout_window=3_600_000 +target_probability="0.999" +target_in_rounds=1 +min_confirmations=0 +gas_limit_per_report=10_300_000 +gas_overhead_per_upkeep=300_000 +max_upkeep_batch_size=10 + +[Soak.Automation.AutomationConfig.PluginConfig.LogProviderConfig] +block_rate=1 +log_limit=2 + +[Soak.Automation.AutomationConfig.RegistrySettings] +payment_premium_ppb=200_000_000 +flat_fee_micro_link=0 +check_gas_limit=2_500_000 +staleness_seconds=90000 +gas_ceiling_multiplier=1 +max_perform_gas=5_000_000 +min_upkeep_spend=0 +fallback_gas_price=200_000_000_000 +fallback_link_price=2_000_000_000_000_000_000 +fallback_native_price=2_000_000_000_000_000_000 +max_check_data_size=5_000 +max_perform_data_size=5_000 +max_revert_data_size=5_000 diff --git a/integration-tests/testconfig/automation/config.go b/integration-tests/testconfig/automation/config.go index bb38b213eca..2dc68ebf8f8 100644 --- a/integration-tests/testconfig/automation/config.go +++ b/integration-tests/testconfig/automation/config.go @@ -4,6 +4,8 @@ import ( "errors" "math/big" "time" + + "github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain" ) type Config struct { @@ -11,6 +13,8 @@ type Config struct { Load []Load `toml:"Load"` DataStreams *DataStreams `toml:"DataStreams"` AutomationConfig *AutomationConfig `toml:"AutomationConfig"` + Resiliency *ResiliencyConfig `toml:"Resiliency"` + Benchmark *Benchmark `toml:"Benchmark"` } func (c *Config) Validate() error { @@ -37,6 +41,57 @@ func (c *Config) Validate() error { return err } } + if c.Resiliency != nil { + if err := c.Resiliency.Validate(); err != nil { + return err + } + } + if c.Benchmark != nil { + if err := c.Benchmark.Validate(); err != nil { + return err + } + } + return nil +} + +type Benchmark struct { + RegistryToTest *string `toml:"registry_to_test"` + NumberOfRegistries *int `toml:"number_of_registries"` + NumberOfUpkeeps *int `toml:"number_of_upkeeps"` + UpkeepGasLimit *int64 `toml:"upkeep_gas_limit"` + CheckGasToBurn *int64 `toml:"check_gas_to_burn"` + PerformGasToBurn *int64 `toml:"perform_gas_to_burn"` + BlockRange *int64 `toml:"block_range"` + BlockInterval *int64 `toml:"block_interval"` + ForceSingleTxKey *bool `toml:"forces_single_tx_key"` + DeleteJobsOnEnd *bool `toml:"delete_jobs_on_end"` +} + +func (c *Benchmark) Validate() error { + if c.RegistryToTest == nil || *c.RegistryToTest == "" { + return errors.New("registry_to_test must be set") + } + if c.NumberOfRegistries == nil || *c.NumberOfRegistries <= 0 { + return errors.New("number_of_registries must be a positive integer") + } + if c.NumberOfUpkeeps == nil || *c.NumberOfUpkeeps <= 0 { + return errors.New("number_of_upkeeps must be a positive integer") + } + if c.UpkeepGasLimit == nil || *c.UpkeepGasLimit <= 0 { + return errors.New("upkeep_gas_limit must be a positive integer") + } + if c.CheckGasToBurn == nil || *c.CheckGasToBurn <= 0 { + return errors.New("check_gas_to_burn must be a positive integer") + } + if c.PerformGasToBurn == nil || *c.PerformGasToBurn <= 0 { + return errors.New("perform_gas_to_burn must be a positive integer") + } + if c.BlockRange == nil || *c.BlockRange <= 0 { + return errors.New("block_range must be a positive integer") + } + if c.BlockInterval == nil || *c.BlockInterval <= 0 { + return errors.New("block_interval must be a positive integer") + } return nil } @@ -298,6 +353,7 @@ type RegistrySettings struct { MinUpkeepSpend *big.Int `toml:"min_upkeep_spend"` FallbackGasPrice *big.Int `toml:"fallback_gas_price"` FallbackLinkPrice *big.Int `toml:"fallback_link_price"` + FallbackNativePrice *big.Int `toml:"fallback_native_price"` MaxCheckDataSize *uint32 `toml:"max_check_data_size"` MaxPerformDataSize *uint32 `toml:"max_perform_data_size"` MaxRevertDataSize *uint32 `toml:"max_revert_data_size"` @@ -331,6 +387,9 @@ func (c *RegistrySettings) Validate() error { if c.FallbackLinkPrice == nil || c.FallbackLinkPrice.Cmp(big.NewInt(0)) < 0 { return errors.New("fallback_link_price must be set to a non-negative integer") } + if c.FallbackNativePrice == nil || c.FallbackNativePrice.Cmp(big.NewInt(0)) < 0 { + return errors.New("fallback_native_price must be set to a non-negative integer") + } if c.MaxCheckDataSize == nil || *c.MaxCheckDataSize < 1 { return errors.New("max_check_data_size must be set to a positive integer") } @@ -342,3 +401,19 @@ func (c *RegistrySettings) Validate() error { } return nil } + +type ResiliencyConfig struct { + ContractCallLimit *uint `toml:"contract_call_limit"` + ContractCallInterval *blockchain.StrDuration `toml:"contract_call_interval"` +} + +func (c *ResiliencyConfig) Validate() error { + if c.ContractCallLimit == nil { + return errors.New("contract_call_limit must be set") + } + if c.ContractCallInterval == nil { + return errors.New("contract_call_interval must be set") + } + + return nil +} diff --git a/integration-tests/testconfig/keeper/config.go b/integration-tests/testconfig/keeper/config.go index 60bdfd86974..81a88ef67eb 100644 --- a/integration-tests/testconfig/keeper/config.go +++ b/integration-tests/testconfig/keeper/config.go @@ -1,110 +1,8 @@ package keeper -import ( - "errors" - - "github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain" -) - type Config struct { - Common *Common `toml:"Common"` - Resiliency *ResiliencyConfig `toml:"Resiliency"` } func (c *Config) Validate() error { - if c.Common == nil { - return nil - } - if err := c.Common.Validate(); err != nil { - return err - } - if c.Resiliency == nil { - return nil - } - return c.Resiliency.Validate() -} - -type Common struct { - RegistryToTest *string `toml:"registry_to_test"` - NumberOfRegistries *int `toml:"number_of_registries"` - NumberOfNodes *int `toml:"number_of_nodes"` - NumberOfUpkeeps *int `toml:"number_of_upkeeps"` - UpkeepGasLimit *int64 `toml:"upkeep_gas_limit"` - CheckGasToBurn *int64 `toml:"check_gas_to_burn"` - PerformGasToBurn *int64 `toml:"perform_gas_to_burn"` - MaxPerformGas *int64 `toml:"max_perform_gas"` - BlockRange *int64 `toml:"block_range"` - BlockInterval *int64 `toml:"block_interval"` - ForceSingleTxKey *bool `toml:"forces_single_tx_key"` - DeleteJobsOnEnd *bool `toml:"delete_jobs_on_end"` - RegistryAddress *string `toml:"registry_address"` - RegistrarAddress *string `toml:"registrar_address"` - LinkTokenAddress *string `toml:"link_token_address"` - EthFeedAddress *string `toml:"eth_feed_address"` - GasFeedAddress *string `toml:"gas_feed_address"` -} - -func (c *Common) Validate() error { - if c.RegistryToTest == nil || *c.RegistryToTest == "" { - return errors.New("registry_to_test must be set") - } - if c.NumberOfRegistries == nil || *c.NumberOfRegistries <= 0 { - return errors.New("number_of_registries must be a positive integer") - } - if c.NumberOfNodes == nil || *c.NumberOfNodes <= 0 { - return errors.New("number_of_nodes must be a positive integer") - } - if c.NumberOfUpkeeps == nil || *c.NumberOfUpkeeps <= 0 { - return errors.New("number_of_upkeeps must be a positive integer") - } - if c.UpkeepGasLimit == nil || *c.UpkeepGasLimit <= 0 { - return errors.New("upkeep_gas_limit must be a positive integer") - } - if c.CheckGasToBurn == nil || *c.CheckGasToBurn <= 0 { - return errors.New("check_gas_to_burn must be a positive integer") - } - if c.PerformGasToBurn == nil || *c.PerformGasToBurn <= 0 { - return errors.New("perform_gas_to_burn must be a positive integer") - } - if c.MaxPerformGas == nil || *c.MaxPerformGas <= 0 { - return errors.New("max_perform_gas must be a positive integer") - } - if c.BlockRange == nil || *c.BlockRange <= 0 { - return errors.New("block_range must be a positive integer") - } - if c.BlockInterval == nil || *c.BlockInterval <= 0 { - return errors.New("block_interval must be a positive integer") - } - if c.RegistryAddress == nil { - c.RegistryAddress = new(string) - } - if c.RegistrarAddress == nil { - c.RegistrarAddress = new(string) - } - if c.LinkTokenAddress == nil { - c.LinkTokenAddress = new(string) - } - if c.EthFeedAddress == nil { - c.EthFeedAddress = new(string) - } - if c.GasFeedAddress == nil { - c.GasFeedAddress = new(string) - } - return nil -} - -type ResiliencyConfig struct { - ContractCallLimit *uint `toml:"contract_call_limit"` - ContractCallInterval *blockchain.StrDuration `toml:"contract_call_interval"` -} - -func (c *ResiliencyConfig) Validate() error { - if c.ContractCallLimit == nil { - return errors.New("contract_call_limit must be set") - } - if c.ContractCallInterval == nil { - return errors.New("contract_call_interval must be set") - } - return nil } diff --git a/integration-tests/testconfig/keeper/keeper.toml b/integration-tests/testconfig/keeper/keeper.toml index 39eae1ea53c..b4f544165a9 100644 --- a/integration-tests/testconfig/keeper/keeper.toml +++ b/integration-tests/testconfig/keeper/keeper.toml @@ -33,130 +33,3 @@ HTTPSPort = 0 [Keeper] TurnLookBack = 0 """ - -[Keeper.Common] -registry_to_test = "2_1" -number_of_registries = 1 -number_of_nodes = 6 -number_of_upkeeps = 500 -upkeep_gas_limit = 1500000 -check_gas_to_burn = 100000 -perform_gas_to_burn = 50000 -max_perform_gas = 5000000 -block_range = 100 -block_interval = 20 -forces_single_tx_key = false -delete_jobs_on_end = true - -# will retry roughly for 1h before giving up (900 * 4s) -[Keeper.Resiliency] -# number of retries before giving up -contract_call_limit = 900 -# static interval between retries -contract_call_interval = "4s" - -[Seth] -# keeper benchmark running on simulated network requires 100k per node -root_key_funds_buffer = 1_000_000 - -[Benchmark.Keeper.Common] -registry_to_test = "2_1" -number_of_registries = 1 -number_of_nodes = 6 -number_of_upkeeps = 1000 -upkeep_gas_limit = 1500000 -check_gas_to_burn = 10000 -perform_gas_to_burn = 1000 -max_perform_gas = 5000000 -block_range = 3600 -block_interval = 60 -forces_single_tx_key = false -delete_jobs_on_end = true - -[Benchmark.NodeConfig] -BaseConfigTOML = """ -[Feature] -LogPoller = true - -[OCR2] -Enabled = true - -[P2P] -[P2P.V2] -Enabled = true -AnnounceAddresses = ["0.0.0.0:6690"] -ListenAddresses = ["0.0.0.0:6690"] -[Keeper] -TurnLookBack = 0 -[WebServer] -HTTPWriteTimeout = '1h' -""" - -CommonChainConfigTOML = """ -""" - -[Benchmark.NodeConfig.ChainConfigTOMLByChainID] -# applicable for simulated chain -1337 = """ -FinalityDepth = 50 -LogPollInterval = '1s' -MinIncomingConfirmations = 1 - -[HeadTracker] -HistoryDepth = 100 - -[GasEstimator] -Mode = 'FixedPrice' -LimitDefault = 5_000_000 -""" - -[Soak.Keeper.Common] -registry_to_test = "2_1" -number_of_registries = 1 -number_of_nodes = 6 -number_of_upkeeps = 50 -upkeep_gas_limit = 1500000 -check_gas_to_burn = 10000 -perform_gas_to_burn = 1000 -max_perform_gas = 5000000 -block_range = 28800 -block_interval = 300 -forces_single_tx_key = false -delete_jobs_on_end = true - -[Soak.NodeConfig] -BaseConfigTOML = """ -[Feature] -LogPoller = true - -[OCR2] -Enabled = true - -[P2P] -[P2P.V2] -Enabled = true -AnnounceAddresses = ["0.0.0.0:6690"] -ListenAddresses = ["0.0.0.0:6690"] -[Keeper] -TurnLookBack = 0 -[WebServer] -HTTPWriteTimeout = '1h' -""" - -CommonChainConfigTOML = """ -""" - -[Soak.NodeConfig.ChainConfigTOMLByChainID] -# applicable for simulated chain -1337 = """ -FinalityDepth = 50 -LogPollInterval = '1s' -MinIncomingConfirmations = 1 - -[HeadTracker] -HistoryDepth = 100 - -[GasEstimator] -Mode = 'FixedPrice' -LimitDefault = 5_000_000 -""" diff --git a/integration-tests/testsetups/keeper_benchmark.go b/integration-tests/testsetups/keeper_benchmark.go index 63a5938b170..40a56ba73c8 100644 --- a/integration-tests/testsetups/keeper_benchmark.go +++ b/integration-tests/testsetups/keeper_benchmark.go @@ -12,6 +12,8 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink/integration-tests/actions/automationv2" + geth "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" @@ -37,7 +39,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" - keepertestconfig "github.com/smartcontractkit/chainlink/integration-tests/testconfig/keeper" + autotestconfig "github.com/smartcontractkit/chainlink/integration-tests/testconfig/automation" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" tt "github.com/smartcontractkit/chainlink/integration-tests/types" ) @@ -52,6 +54,7 @@ type KeeperBenchmarkTest struct { log zerolog.Logger startingBlock *big.Int + automationTests []automationv2.AutomationTest keeperRegistries []contracts.KeeperRegistry keeperRegistrars []contracts.KeeperRegistrar keeperConsumerContracts []contracts.AutomationConsumerBenchmark @@ -61,13 +64,9 @@ type KeeperBenchmarkTest struct { namespace string chainlinkNodes []*client.ChainlinkK8sClient chainClient *seth.Client - testConfig tt.KeeperBenchmarkTestConfig + testConfig tt.AutomationBenchmarkTestConfig - linkToken contracts.LinkToken - linkethFeed contracts.MockLINKETHFeed - gasFeed contracts.MockGasFeed - ethusdFeed contracts.MockETHUSDFeed - wrappedNative contracts.WETHToken + linkToken contracts.LinkToken } // UpkeepConfig dictates details of how the test's upkeep contracts should be called and configured @@ -81,22 +80,11 @@ type UpkeepConfig struct { FirstEligibleBuffer int64 // How many blocks to add to randomised first eligible block, set to 0 to disable randomised first eligible block } -// PreDeployedContracts are contracts that are already deployed on a (usually) live testnet chain, so re-deployment -// in unnecessary -type PreDeployedContracts struct { - RegistryAddress string - RegistrarAddress string - LinkTokenAddress string - EthFeedAddress string - GasFeedAddress string -} - // KeeperBenchmarkTestInputs are all the required inputs for a Keeper Benchmark Test type KeeperBenchmarkTestInputs struct { BlockchainClient *seth.Client // Client for the test to connect to the blockchain with KeeperRegistrySettings *contracts.KeeperRegistrySettings // Settings of each keeper contract Upkeeps *UpkeepConfig - Contracts *PreDeployedContracts Timeout time.Duration // Timeout for the test ChainlinkNodeFunding *big.Float // Amount of ETH to fund each chainlink node with UpkeepSLA int64 // SLA in number of blocks for an upkeep to be performed once it becomes eligible @@ -117,7 +105,7 @@ func NewKeeperBenchmarkTest(t *testing.T, inputs KeeperBenchmarkTestInputs) *Kee } // Setup prepares contracts for the test -func (k *KeeperBenchmarkTest) Setup(env *environment.Environment, config tt.KeeperBenchmarkTestConfig) { +func (k *KeeperBenchmarkTest) Setup(env *environment.Environment, config tt.AutomationBenchmarkTestConfig) { startTime := time.Now() k.TestReporter.Summary.StartTime = startTime.UnixMilli() k.ensureInputValues() @@ -126,6 +114,7 @@ func (k *KeeperBenchmarkTest) Setup(env *environment.Environment, config tt.Keep inputs := k.Inputs k.testConfig = config + k.automationTests = make([]automationv2.AutomationTest, len(inputs.RegistryVersions)) k.keeperRegistries = make([]contracts.KeeperRegistry, len(inputs.RegistryVersions)) k.keeperRegistrars = make([]contracts.KeeperRegistrar, len(inputs.RegistryVersions)) k.keeperConsumerContracts = make([]contracts.AutomationConsumerBenchmark, len(inputs.RegistryVersions)) @@ -133,8 +122,8 @@ func (k *KeeperBenchmarkTest) Setup(env *environment.Environment, config tt.Keep k.log.Debug().Interface("TestInputs", inputs).Msg("Setting up benchmark test") // if not present disable it - if k.testConfig.GetKeeperConfig().Resiliency == nil { - k.testConfig.GetKeeperConfig().Resiliency = &keepertestconfig.ResiliencyConfig{ + if k.testConfig.GetAutomationConfig().Resiliency == nil { + k.testConfig.GetAutomationConfig().Resiliency = &autotestconfig.ResiliencyConfig{ ContractCallLimit: ptr.Ptr(uint(0)), ContractCallInterval: ptr.Ptr(blockchain.StrDuration{Duration: 0 * time.Second}), } @@ -155,40 +144,22 @@ func (k *KeeperBenchmarkTest) Setup(env *environment.Environment, config tt.Keep } } - c := inputs.Contracts - - if common.IsHexAddress(c.LinkTokenAddress) { - _, err = contracts.LoadLinkTokenContract(k.log, k.chainClient, common.HexToAddress(c.LinkTokenAddress)) - require.NoError(k.t, err, "Loading Link Token Contract shouldn't fail") - } else { - k.linkToken, err = contracts.DeployLinkTokenContract(k.log, k.chainClient) - require.NoError(k.t, err, "Deploying mock Link Token Contract feed shouldn't fail") - } - - if common.IsHexAddress(c.EthFeedAddress) { - _, err = contracts.LoadMockLINKETHFeed(k.chainClient, common.HexToAddress(c.EthFeedAddress)) - require.NoError(k.t, err, "Loading ETH-Link feed Contract shouldn't fail") - } else { - k.linkethFeed, err = contracts.DeployMockLINKETHFeed(k.chainClient, big.NewInt(2e18)) - require.NoError(k.t, err, "Deploying mock ETH-Link feed shouldn't fail") - } - - if common.IsHexAddress(c.GasFeedAddress) { - k.gasFeed, err = contracts.LoadMockGASFeed(k.chainClient, common.HexToAddress(c.GasFeedAddress)) - require.NoError(k.t, err, "Loading Gas feed Contract shouldn't fail") - } else { - k.gasFeed, err = contracts.DeployMockGASFeed(k.chainClient, big.NewInt(2e11)) - require.NoError(k.t, err, "Deploying mock gas feed shouldn't fail") - } - - k.ethusdFeed, err = contracts.DeployMockETHUSDFeed(k.chainClient, big.NewInt(200000000000)) - require.NoError(k.t, err, "Deploying mock ETH-USD feed shouldn't fail") - k.wrappedNative, err = contracts.DeployWETHTokenContract(k.log, k.chainClient) - require.NoError(k.t, err, "Deploying WETH Token Contract shouldn't fail") - for index := range inputs.RegistryVersions { k.log.Info().Int("Index", index).Msg("Starting Test Setup") - k.DeployBenchmarkKeeperContracts(index) + a := automationv2.NewAutomationTestK8s(k.log, k.chainClient, k.chainlinkNodes) + a.RegistrySettings = *k.Inputs.KeeperRegistrySettings + a.RegistrySettings.RegistryVersion = inputs.RegistryVersions[index] + a.RegistrarSettings = contracts.KeeperRegistrarSettings{ + AutoApproveConfigType: uint8(2), + AutoApproveMaxAllowed: math.MaxUint16, + MinLinkJuels: big.NewInt(0), + } + a.PluginConfig = actions.ReadPluginConfig(config) + a.PublicConfig = actions.ReadPublicConfig(config) + a.SetupAutomationDeploymentWithoutJobs(k.t) + err = a.SetConfigOnRegistry() + require.NoError(k.t, err, "Setting initial config on registry shouldn't fail") + k.DeployBenchmarkKeeperContracts(index, a) } var keysToFund = inputs.RegistryVersions @@ -223,7 +194,6 @@ func (k *KeeperBenchmarkTest) Run() { float64(u.BlockInterval) k.TestReporter.Summary.TestInputs = map[string]interface{}{ "NumberOfUpkeeps": u.NumberOfUpkeeps, - "BlockCountPerTurn": k.Inputs.KeeperRegistrySettings.BlockCountPerTurn, "CheckGasLimit": k.Inputs.KeeperRegistrySettings.CheckGasLimit, "MaxPerformGas": k.Inputs.KeeperRegistrySettings.MaxPerformGas, "CheckGasToBurn": u.CheckGasToBurn, @@ -240,34 +210,15 @@ func (k *KeeperBenchmarkTest) Run() { k.startingBlock = big.NewInt(0).SetUint64(startingBlock) startTime := time.Now() - nodesWithoutBootstrap := k.chainlinkNodes[1:] - for rIndex := range k.keeperRegistries { var txKeyId = rIndex if inputs.ForceSingleTxnKey { txKeyId = 0 } - kr := k.keeperRegistries[rIndex] - ocrConfig, err := actions.BuildAutoOCR2ConfigVarsWithKeyIndex( - k.t, nodesWithoutBootstrap, *inputs.KeeperRegistrySettings, kr.Address(), k.Inputs.DeltaStage, txKeyId, common.Address{}, kr.ChainModuleAddress(), kr.ReorgProtectionEnabled(), k.linkToken, k.wrappedNative, k.ethusdFeed, - ) - require.NoError(k.t, err, "Building OCR config shouldn't fail") - - rv := inputs.RegistryVersions[rIndex] - // Send keeper jobs to registry and chainlink nodes - if rv >= ethereum.RegistryVersion_2_0 { - actions.CreateOCRKeeperJobs(k.t, k.chainlinkNodes, kr.Address(), k.chainClient.ChainID, txKeyId, rv) - if rv == ethereum.RegistryVersion_2_0 { - err = kr.SetConfig(*inputs.KeeperRegistrySettings, ocrConfig) - } else { - err = kr.SetConfigTypeSafe(ocrConfig) - } - require.NoError(k.t, err, "Registry config should be be set successfully") - // Give time for OCR nodes to bootstrap - time.Sleep(1 * time.Minute) - } else { - actions.CreateKeeperJobsWithKeyIndex(k.t, k.chainlinkNodes, kr, txKeyId, ocrConfig, fmt.Sprint(k.chainClient.ChainID)) - } + k.automationTests[rIndex].SetTransmitterKeyIndex(txKeyId) + k.automationTests[rIndex].AddJobsAndSetConfig(k.t) + // Give time for OCR nodes to bootstrap + time.Sleep(1 * time.Minute) } k.log.Info().Msgf("Waiting for %d blocks for all upkeeps to be performed", inputs.Upkeeps.BlockRange+inputs.UpkeepSLA) @@ -665,7 +616,7 @@ func (k *KeeperBenchmarkTest) ensureInputValues() { } } -func (k *KeeperBenchmarkTest) SendSlackNotification(slackClient *slack.Client, config tt.KeeperBenchmarkTestConfig) error { +func (k *KeeperBenchmarkTest) SendSlackNotification(slackClient *slack.Client, config tt.AutomationBenchmarkTestConfig) error { if slackClient == nil { slackClient = slack.New(reportModel.SlackAPIKey) } @@ -700,62 +651,14 @@ func (k *KeeperBenchmarkTest) SendSlackNotification(slackClient *slack.Client, c } // DeployBenchmarkKeeperContracts deploys a set amount of keeper Benchmark contracts registered to a single registry -func (k *KeeperBenchmarkTest) DeployBenchmarkKeeperContracts(index int) { +func (k *KeeperBenchmarkTest) DeployBenchmarkKeeperContracts(index int, a *automationv2.AutomationTest) { registryVersion := k.Inputs.RegistryVersions[index] k.Inputs.KeeperRegistrySettings.RegistryVersion = registryVersion upkeep := k.Inputs.Upkeeps var ( - registry contracts.KeeperRegistry - registrar contracts.KeeperRegistrar - err error + err error ) - // Contract deployment is different for legacy keepers and OCR automation - if registryVersion <= ethereum.RegistryVersion_1_3 { // Legacy keeper - v1.X - registry, err = contracts.DeployKeeperRegistry(k.chainClient, &contracts.KeeperRegistryOpts{ - RegistryVersion: registryVersion, - LinkAddr: k.linkToken.Address(), - ETHFeedAddr: k.linkethFeed.Address(), - GasFeedAddr: k.gasFeed.Address(), - TranscoderAddr: actions.ZeroAddress.Hex(), - RegistrarAddr: actions.ZeroAddress.Hex(), - Settings: *k.Inputs.KeeperRegistrySettings, - }) - require.NoError(k.t, err, "Deploying registry contract shouldn't fail") - - // Fund the registry with 1 LINK * amount of AutomationConsumerBenchmark contracts - err := k.linkToken.Transfer(registry.Address(), big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(int64(k.Inputs.Upkeeps.NumberOfUpkeeps)))) - require.NoError(k.t, err, "Funding keeper registry contract shouldn't fail") - - registrarSettings := contracts.KeeperRegistrarSettings{ - AutoApproveConfigType: 2, - AutoApproveMaxAllowed: math.MaxUint16, - RegistryAddr: registry.Address(), - MinLinkJuels: big.NewInt(0), - } - - registrar, err = contracts.DeployKeeperRegistrar(k.chainClient, registryVersion, k.linkToken.Address(), registrarSettings) - require.NoError(k.t, err, "Funding keeper registrar contract shouldn't fail") - } else { // OCR automation - v2.X - registry, registrar = actions.DeployAutoOCRRegistryAndRegistrar( - k.t, k.chainClient, registryVersion, *k.Inputs.KeeperRegistrySettings, k.linkToken, k.wrappedNative, k.ethusdFeed, - ) - - // Fund the registry with LINK - err := k.linkToken.Transfer(registry.Address(), big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(int64(k.Inputs.Upkeeps.NumberOfUpkeeps)))) - require.NoError(k.t, err, "Funding keeper registry contract shouldn't fail") - ocrConfig, err := actions.BuildAutoOCR2ConfigVars(k.t, k.chainlinkNodes[1:], *k.Inputs.KeeperRegistrySettings, registrar.Address(), k.Inputs.DeltaStage, registry.ChainModuleAddress(), registry.ReorgProtectionEnabled(), k.linkToken, k.wrappedNative, k.ethusdFeed) - require.NoError(k.t, err, "Building OCR config shouldn't fail") - k.log.Debug().Interface("KeeperRegistrySettings", *k.Inputs.KeeperRegistrySettings).Interface("OCRConfig", ocrConfig).Msg("Config") - require.NoError(k.t, err, "Error building OCR config vars") - if registryVersion == ethereum.RegistryVersion_2_0 { - err = registry.SetConfig(*k.Inputs.KeeperRegistrySettings, ocrConfig) - } else { - err = registry.SetConfigTypeSafe(ocrConfig) - } - require.NoError(k.t, err, "Registry config should be be set successfully") - } - consumer := k.DeployKeeperConsumersBenchmark() var upkeepAddresses []string @@ -805,14 +708,16 @@ func (k *KeeperBenchmarkTest) DeployBenchmarkKeeperContracts(index int) { big.NewInt(0)) linkFunds = big.NewInt(0).Add(linkFunds, minLinkBalance) + k.linkToken = a.LinkToken err = actions.DeployMultiCallAndFundDeploymentAddresses(k.chainClient, k.linkToken, upkeep.NumberOfUpkeeps, linkFunds) require.NoError(k.t, err, "Sending link funds to deployment addresses shouldn't fail") - upkeepIds := actions.RegisterUpkeepContractsWithCheckData(k.t, k.chainClient, k.linkToken, linkFunds, uint32(upkeep.UpkeepGasLimit), registry, registrar, upkeep.NumberOfUpkeeps, upkeepAddresses, checkData, false, false, false, nil) + upkeepIds := actions.RegisterUpkeepContractsWithCheckData(k.t, k.chainClient, k.linkToken, linkFunds, uint32(upkeep.UpkeepGasLimit), a.Registry, a.Registrar, upkeep.NumberOfUpkeeps, upkeepAddresses, checkData, false, false, false, nil) - k.keeperRegistries[index] = registry - k.keeperRegistrars[index] = registrar + k.automationTests[index] = *a + k.keeperRegistries[index] = a.Registry + k.keeperRegistrars[index] = a.Registrar k.upkeepIDs[index] = upkeepIds k.keeperConsumerContracts[index] = consumer } @@ -821,9 +726,9 @@ func (k *KeeperBenchmarkTest) DeployKeeperConsumersBenchmark() contracts.Automat // Deploy consumer var err error var keeperConsumerInstance contracts.AutomationConsumerBenchmark - if *k.testConfig.GetKeeperConfig().Resiliency.ContractCallLimit != 0 && k.testConfig.GetKeeperConfig().Resiliency.ContractCallInterval.Duration != 0 { - maxRetryAttempts := *k.testConfig.GetKeeperConfig().Resiliency.ContractCallLimit - callRetryDelay := k.testConfig.GetKeeperConfig().Resiliency.ContractCallInterval.Duration + if *k.testConfig.GetAutomationConfig().Resiliency.ContractCallLimit != 0 && k.testConfig.GetAutomationConfig().Resiliency.ContractCallInterval.Duration != 0 { + maxRetryAttempts := *k.testConfig.GetAutomationConfig().Resiliency.ContractCallLimit + callRetryDelay := k.testConfig.GetAutomationConfig().Resiliency.ContractCallInterval.Duration keeperConsumerInstance, err = contracts.DeployKeeperConsumerBenchmarkWithRetry(k.chainClient, k.log, maxRetryAttempts, callRetryDelay) if err != nil { k.log.Error().Err(err).Msg("Deploying AutomationConsumerBenchmark instance shouldn't fail") diff --git a/integration-tests/types/testconfigs.go b/integration-tests/types/testconfigs.go index e0b1f7cc03e..7a72e9a1dfa 100644 --- a/integration-tests/types/testconfigs.go +++ b/integration-tests/types/testconfigs.go @@ -32,10 +32,10 @@ type AutomationTestConfig interface { tc.AutomationTestConfig } -type KeeperBenchmarkTestConfig interface { +type AutomationBenchmarkTestConfig interface { ctf_config.GlobalTestConfig tc.CommonTestConfig - tc.KeeperTestConfig + tc.AutomationTestConfig ctf_config.NamedConfigurations testreporters.GrafanaURLProvider }