diff --git a/.changeset/nervous-humans-march.md b/.changeset/nervous-humans-march.md new file mode 100644 index 0000000000..43c7a08683 --- /dev/null +++ b/.changeset/nervous-humans-march.md @@ -0,0 +1,5 @@ +--- +"ccip": patch +--- + +Added tests and cleanup of liquidity graph #changed diff --git a/.github/workflows/build-publish-develop.yml b/.github/workflows/build-publish-develop.yml index 53507a29e8..6e8e5ba3f5 100644 --- a/.github/workflows/build-publish-develop.yml +++ b/.github/workflows/build-publish-develop.yml @@ -52,8 +52,8 @@ jobs: ecr-image-name: ccip-develop ecr-tag-suffix: ${{ matrix.image.tag-suffix }} dockerfile: ${{ matrix.image.dockerfile }} - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + dockerhub_username: ${{ secrets.DOCKER_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKER_READONLY_PASSWORD }} git-commit-sha: ${{ steps.git-ref.outputs.checked-out || github.sha }} - name: Collect Metrics diff --git a/.github/workflows/build-publish-pr.yml b/.github/workflows/build-publish-pr.yml index 7553b74fba..1a426cdeac 100644 --- a/.github/workflows/build-publish-pr.yml +++ b/.github/workflows/build-publish-pr.yml @@ -50,8 +50,8 @@ jobs: sign-images: false ecr-hostname: ${{ secrets.AWS_SDLC_ECR_HOSTNAME }} ecr-image-name: ${{ env.ECR_IMAGE_NAME }} - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + dockerhub_username: ${{ secrets.DOCKER_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKER_READONLY_PASSWORD }} - name: Collect Metrics if: always() diff --git a/.github/workflows/build-publish.yml b/.github/workflows/build-publish.yml index ef0f641db1..865bdaf730 100644 --- a/.github/workflows/build-publish.yml +++ b/.github/workflows/build-publish.yml @@ -46,8 +46,8 @@ jobs: cosign-private-key: ${{ secrets.COSIGN_PRIVATE_KEY }} cosign-public-key: ${{ secrets.COSIGN_PUBLIC_KEY }} cosign-password: ${{ secrets.COSIGN_PASSWORD }} - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + dockerhub_username: ${{ secrets.DOCKER_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKER_READONLY_PASSWORD }} verify-signature: true - name: Collect Metrics if: always() diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6e3272204e..b82b5a8203 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,8 +34,8 @@ jobs: if: ${{ steps.change.outputs.changelog-only == 'false' }} uses: ./.github/actions/build-sign-publish-chainlink with: - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + dockerhub_username: ${{ secrets.DOCKER_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKER_READONLY_PASSWORD }} publish: false sign-images: false diff --git a/.github/workflows/ccip-ocr3-build-lint-test.yml b/.github/workflows/ccip-ocr3-build-lint-test.yml deleted file mode 100644 index 554995dc65..0000000000 --- a/.github/workflows/ccip-ocr3-build-lint-test.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: "Build lint and test CCIP-OCR3" - -on: - pull_request: - paths: - - core/services/ocr3/plugins/ccip/** - -jobs: - build-lint-test: - runs-on: ubuntu-20.04 - strategy: - matrix: - go-version: ['1.21'] - defaults: - run: - working-directory: ./core/services/ocr3/plugins/ccip - steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - name: Setup Go ${{ matrix.go-version }} - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 - with: - go-version: ${{ matrix.go-version }} - - name: Display Go version - run: go version - - name: Build - run: go build -v ./... - - name: Install linter - run: | - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.59.0 - - name: Run linter - run: golangci-lint run -c .golangci.yml - - name: Run tests - run: go test -race -fullpath -shuffle on -count 20 ./... diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index a5ea517456..79053bd072 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -303,12 +303,13 @@ jobs: fail-fast: false matrix: product: - - name: ccip-lm-smoke - nodes: 1 - os: ubuntu-latest - file: lm - dir: ccip-tests/smoke - run: -run ^TestLmBasic$ +# LM Smoke Test is disabled since project is paused +# - name: ccip-lm-smoke +# nodes: 1 +# os: ubuntu-latest +# file: lm +# dir: ccip-tests/smoke +# run: -run ^TestLmBasic$ - name: ccip-smoke nodes: 1 os: ubuntu-latest @@ -342,6 +343,12 @@ jobs: os: ubuntu-latest file: ccip run: -run ^TestSmokeCCIPRateLimit$ + - name: ccip-smoke-rate-limit + nodes: 1 + dir: ccip-tests/smoke + os: ubuntu-latest + file: ccip + run: -run ^TestSmokeCCIPTokenPoolRateLimits$ - name: ccip-smoke-multicall nodes: 1 dir: ccip-tests/smoke diff --git a/.github/workflows/live-vrf-tests.yml b/.github/workflows/live-vrf-tests.yml index 89c62c104f..80eb14a589 100644 --- a/.github/workflows/live-vrf-tests.yml +++ b/.github/workflows/live-vrf-tests.yml @@ -30,8 +30,7 @@ env: TEST_LOG_LEVEL: debug jobs: - - # Build Test Dependencies + # Build Test Dependencies build-chainlink: environment: integration @@ -107,7 +106,6 @@ jobs: cache_restore_only: "true" binary_name: tests - # End Build Test Dependencies live-smoke-tests: @@ -120,7 +118,7 @@ jobs: needs: [build-chainlink, build-tests] strategy: fail-fast: false - matrix: + matrix: network: ${{fromJson(needs.build-tests.outputs.matrix)}} name: Smoke Tests on ${{ matrix.network }} runs-on: ubuntu-latest @@ -177,8 +175,8 @@ jobs: cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + dockerhub_username: ${{ secrets.DOCKER_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKER_READONLY_PASSWORD }} artifacts_location: ./logs token: ${{ secrets.GITHUB_TOKEN }} cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} @@ -190,4 +188,4 @@ jobs: if: always() uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@fc3e0df622521019f50d772726d6bf8dc919dd38 # v2.3.19 with: - test_directory: "./" \ No newline at end of file + test_directory: "./" diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index f2a590cf67..54443aa4e2 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -116,148 +116,135 @@ CommitStore_verify:test_TooManyLeaves_Revert() (gas: 36785) DefensiveExampleTest:test_HappyPath_Success() (gas: 200018) DefensiveExampleTest:test_Recovery() (gas: 424253) E2E:test_E2E_3MessagesSuccess_gas() (gas: 1104821) -EVM2EVMMultiOffRamp__releaseOrMintSingleToken:test__releaseOrMintSingleToken_NotACompatiblePool_Revert() (gas: 38297) -EVM2EVMMultiOffRamp__releaseOrMintSingleToken:test__releaseOrMintSingleToken_Success() (gas: 108423) -EVM2EVMMultiOffRamp__releaseOrMintSingleToken:test__releaseOrMintSingleToken_TokenHandlingError_revert_Revert() (gas: 116875) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_AddMultipleChains_Success() (gas: 262922) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 93606) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 12376) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRampAndPrevOffRamp_Revert() (gas: 87783) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRamp_Revert() (gas: 87494) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainPrevOffRamp_Revert() (gas: 87642) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChain_Success() (gas: 102139) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ZeroOnRampAddress_Revert() (gas: 12465) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ZeroSourceChainSelector_Revert() (gas: 12440) -EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsDifferentChains_Success() (gas: 278602) -EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsSameChain_Success() (gas: 223952) -EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsSkipDuplicate_Success() (gas: 149563) -EVM2EVMMultiOffRamp_batchExecute:test_OutOfBoundsGasLimitsAccess_Revert() (gas: 178860) -EVM2EVMMultiOffRamp_batchExecute:test_SingleReport_Success() (gas: 136014) -EVM2EVMMultiOffRamp_batchExecute:test_Unhealthy_Revert() (gas: 518511) -EVM2EVMMultiOffRamp_batchExecute:test_ZeroReports_Revert() (gas: 10505) -EVM2EVMMultiOffRamp_ccipReceive:test_Reverts() (gas: 17210) -EVM2EVMMultiOffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 65962) -EVM2EVMMultiOffRamp_commit:test_InvalidInterval_Revert() (gas: 59612) -EVM2EVMMultiOffRamp_commit:test_InvalidRootRevert() (gas: 58692) -EVM2EVMMultiOffRamp_commit:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6568439) -EVM2EVMMultiOffRamp_commit:test_NoConfig_Revert() (gas: 6151690) -EVM2EVMMultiOffRamp_commit:test_OnlyGasPriceUpdates_Success() (gas: 108339) -EVM2EVMMultiOffRamp_commit:test_OnlyPriceUpdateStaleReport_Revert() (gas: 118247) -EVM2EVMMultiOffRamp_commit:test_OnlyTokenPriceUpdates_Success() (gas: 108382) -EVM2EVMMultiOffRamp_commit:test_PriceSequenceNumberCleared_Success() (gas: 353651) -EVM2EVMMultiOffRamp_commit:test_ReportAndPriceUpdate_Success() (gas: 160080) -EVM2EVMMultiOffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 134987) -EVM2EVMMultiOffRamp_commit:test_RootAlreadyCommitted_Revert() (gas: 136659) -EVM2EVMMultiOffRamp_commit:test_SourceChainNotEnabled_Revert() (gas: 58960) -EVM2EVMMultiOffRamp_commit:test_StaleReportWithRoot_Success() (gas: 225241) -EVM2EVMMultiOffRamp_commit:test_UnauthorizedTransmitter_Revert() (gas: 119596) -EVM2EVMMultiOffRamp_commit:test_Unhealthy_Revert() (gas: 77519) -EVM2EVMMultiOffRamp_commit:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 207875) -EVM2EVMMultiOffRamp_commit:test_WrongConfigWithoutSigners_Revert() (gas: 6562831) +EVM2EVMMultiOffRamp__releaseOrMintSingleToken:test__releaseOrMintSingleToken_NotACompatiblePool_Revert() (gas: 38361) +EVM2EVMMultiOffRamp__releaseOrMintSingleToken:test__releaseOrMintSingleToken_Success() (gas: 108455) +EVM2EVMMultiOffRamp__releaseOrMintSingleToken:test__releaseOrMintSingleToken_TokenHandlingError_revert_Revert() (gas: 116907) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_AddMultipleChains_Success() (gas: 460360) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 95427) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 12395) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRamp_Revert() (gas: 90249) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChain_Success() (gas: 105403) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ZeroOnRampAddress_Revert() (gas: 15651) +EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ZeroSourceChainSelector_Revert() (gas: 12989) +EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsDifferentChains_Success() (gas: 305737) +EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsSameChain_Success() (gas: 247076) +EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsSkipDuplicate_Success() (gas: 162376) +EVM2EVMMultiOffRamp_batchExecute:test_OutOfBoundsGasLimitsAccess_Revert() (gas: 195244) +EVM2EVMMultiOffRamp_batchExecute:test_SingleReport_Success() (gas: 150011) +EVM2EVMMultiOffRamp_batchExecute:test_Unhealthy_Revert() (gas: 535435) +EVM2EVMMultiOffRamp_batchExecute:test_ZeroReports_Revert() (gas: 10525) +EVM2EVMMultiOffRamp_ccipReceive:test_Reverts() (gas: 16096) +EVM2EVMMultiOffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 67080) +EVM2EVMMultiOffRamp_commit:test_InvalidInterval_Revert() (gas: 59630) +EVM2EVMMultiOffRamp_commit:test_InvalidRootRevert() (gas: 58710) +EVM2EVMMultiOffRamp_commit:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6574764) +EVM2EVMMultiOffRamp_commit:test_NoConfig_Revert() (gas: 6157992) +EVM2EVMMultiOffRamp_commit:test_OnlyGasPriceUpdates_Success() (gas: 108329) +EVM2EVMMultiOffRamp_commit:test_OnlyPriceUpdateStaleReport_Revert() (gas: 118244) +EVM2EVMMultiOffRamp_commit:test_OnlyTokenPriceUpdates_Success() (gas: 108372) +EVM2EVMMultiOffRamp_commit:test_PriceSequenceNumberCleared_Success() (gas: 353646) +EVM2EVMMultiOffRamp_commit:test_ReportAndPriceUpdate_Success() (gas: 161185) +EVM2EVMMultiOffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 136112) +EVM2EVMMultiOffRamp_commit:test_RootAlreadyCommitted_Revert() (gas: 136695) +EVM2EVMMultiOffRamp_commit:test_SourceChainNotEnabled_Revert() (gas: 58978) +EVM2EVMMultiOffRamp_commit:test_StaleReportWithRoot_Success() (gas: 227569) +EVM2EVMMultiOffRamp_commit:test_UnauthorizedTransmitter_Revert() (gas: 119611) +EVM2EVMMultiOffRamp_commit:test_Unhealthy_Revert() (gas: 77537) +EVM2EVMMultiOffRamp_commit:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 209029) +EVM2EVMMultiOffRamp_commit:test_WrongConfigWithoutSigners_Revert() (gas: 6569153) EVM2EVMMultiOffRamp_commit:test_ZeroEpochAndRound_Revert() (gas: 47717) -EVM2EVMMultiOffRamp_constructor:test_Constructor_Success() (gas: 6221393) -EVM2EVMMultiOffRamp_constructor:test_SourceChainSelector_Revert() (gas: 100992) -EVM2EVMMultiOffRamp_constructor:test_ZeroChainSelector_Revert() (gas: 97936) -EVM2EVMMultiOffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 101045) -EVM2EVMMultiOffRamp_constructor:test_ZeroRMNProxy_Revert() (gas: 95693) -EVM2EVMMultiOffRamp_constructor:test_ZeroTokenAdminRegistry_Revert() (gas: 95760) +EVM2EVMMultiOffRamp_constructor:test_Constructor_Success() (gas: 6158331) +EVM2EVMMultiOffRamp_constructor:test_SourceChainSelector_Revert() (gas: 104384) +EVM2EVMMultiOffRamp_constructor:test_ZeroChainSelector_Revert() (gas: 100309) +EVM2EVMMultiOffRamp_constructor:test_ZeroNonceManager_Revert() (gas: 98180) +EVM2EVMMultiOffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 106890) +EVM2EVMMultiOffRamp_constructor:test_ZeroRMNProxy_Revert() (gas: 98079) +EVM2EVMMultiOffRamp_constructor:test_ZeroTokenAdminRegistry_Revert() (gas: 98146) EVM2EVMMultiOffRamp_execute:test_IncorrectArrayType_Revert() (gas: 17299) -EVM2EVMMultiOffRamp_execute:test_LargeBatch_Success() (gas: 1492338) -EVM2EVMMultiOffRamp_execute:test_MultipleReportsWithPartialValidationFailures_Success() (gas: 330226) -EVM2EVMMultiOffRamp_execute:test_MultipleReports_Success() (gas: 247344) -EVM2EVMMultiOffRamp_execute:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6618873) -EVM2EVMMultiOffRamp_execute:test_NoConfig_Revert() (gas: 6201840) -EVM2EVMMultiOffRamp_execute:test_NonArray_Revert() (gas: 30077) -EVM2EVMMultiOffRamp_execute:test_SingleReport_Success() (gas: 156624) -EVM2EVMMultiOffRamp_execute:test_UnauthorizedTransmitter_Revert() (gas: 140572) -EVM2EVMMultiOffRamp_execute:test_WrongConfigWithSigners_Revert() (gas: 6980966) +EVM2EVMMultiOffRamp_execute:test_LargeBatch_Success() (gas: 1640074) +EVM2EVMMultiOffRamp_execute:test_MultipleReportsWithPartialValidationFailures_Success() (gas: 350683) +EVM2EVMMultiOffRamp_execute:test_MultipleReports_Success() (gas: 267647) +EVM2EVMMultiOffRamp_execute:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6627625) +EVM2EVMMultiOffRamp_execute:test_NoConfig_Revert() (gas: 6210572) +EVM2EVMMultiOffRamp_execute:test_NonArray_Revert() (gas: 28435) +EVM2EVMMultiOffRamp_execute:test_SingleReport_Success() (gas: 167667) +EVM2EVMMultiOffRamp_execute:test_UnauthorizedTransmitter_Revert() (gas: 151623) +EVM2EVMMultiOffRamp_execute:test_WrongConfigWithSigners_Revert() (gas: 6989700) EVM2EVMMultiOffRamp_execute:test_ZeroReports_Revert() (gas: 17174) -EVM2EVMMultiOffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 20742) -EVM2EVMMultiOffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 255656) -EVM2EVMMultiOffRamp_executeSingleMessage:test_NonContract_Success() (gas: 22893) -EVM2EVMMultiOffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 208045) -EVM2EVMMultiOffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 50975) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 50485) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() (gas: 235475) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidation_Revert() (gas: 91297) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 287890) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithValidation_Success() (gas: 95401) -EVM2EVMMultiOffRamp_executeSingleReport:test_DisabledSourceChain_Revert() (gas: 37347) -EVM2EVMMultiOffRamp_executeSingleReport:test_EmptyReport_Revert() (gas: 23929) -EVM2EVMMultiOffRamp_executeSingleReport:test_InvalidMessageId_Revert() (gas: 41823) -EVM2EVMMultiOffRamp_executeSingleReport:test_InvalidSourcePoolAddress_Success() (gas: 448428) -EVM2EVMMultiOffRamp_executeSingleReport:test_ManualExecutionNotYetEnabled_Revert() (gas: 53593) -EVM2EVMMultiOffRamp_executeSingleReport:test_MismatchingOnRampAddress_Revert() (gas: 44660) -EVM2EVMMultiOffRamp_executeSingleReport:test_MismatchingSourceChainSelector_Revert() (gas: 41715) -EVM2EVMMultiOffRamp_executeSingleReport:test_NonExistingSourceChain_Revert() (gas: 37577) -EVM2EVMMultiOffRamp_executeSingleReport:test_ReceiverError_Success() (gas: 170344) -EVM2EVMMultiOffRamp_executeSingleReport:test_RetryFailedMessageWithoutManualExecution_Revert() (gas: 182073) -EVM2EVMMultiOffRamp_executeSingleReport:test_RootNotCommitted_Revert() (gas: 47177) -EVM2EVMMultiOffRamp_executeSingleReport:test_RouterYULCall_Revert() (gas: 405859) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain_Success() (gas: 232987) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered_Success() (gas: 166040) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokens_Success() (gas: 180585) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageToNonCCIPReceiver_Success() (gas: 251817) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessagesNoTokensSuccess_gas() (gas: 119003) -EVM2EVMMultiOffRamp_executeSingleReport:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 383559) -EVM2EVMMultiOffRamp_executeSingleReport:test_SkippedIncorrectNonce_Success() (gas: 56005) -EVM2EVMMultiOffRamp_executeSingleReport:test_TokenDataMismatch_Revert() (gas: 51317) -EVM2EVMMultiOffRamp_executeSingleReport:test_TwoMessagesWithTokensAndGE_Success() (gas: 528276) -EVM2EVMMultiOffRamp_executeSingleReport:test_TwoMessagesWithTokensSuccess_gas() (gas: 465888) -EVM2EVMMultiOffRamp_executeSingleReport:test_UnexpectedTokenData_Revert() (gas: 38052) -EVM2EVMMultiOffRamp_executeSingleReport:test_UnhealthySingleChainCurse_Revert() (gas: 516921) -EVM2EVMMultiOffRamp_executeSingleReport:test_Unhealthy_Revert() (gas: 514300) -EVM2EVMMultiOffRamp_executeSingleReport:test_WithCurseOnAnotherSourceChain_Success() (gas: 483010) -EVM2EVMMultiOffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 127346) -EVM2EVMMultiOffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 147640) -EVM2EVMMultiOffRamp_execute_upgrade:test_NoPrevOffRampForChain_Success() (gas: 239300) -EVM2EVMMultiOffRamp_execute_upgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 239181) -EVM2EVMMultiOffRamp_execute_upgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 289556) -EVM2EVMMultiOffRamp_execute_upgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 270238) -EVM2EVMMultiOffRamp_execute_upgrade:test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() (gas: 247338) -EVM2EVMMultiOffRamp_execute_upgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 235313) -EVM2EVMMultiOffRamp_execute_upgrade:test_UpgradedWithMultiRamp_Revert() (gas: 7153761) -EVM2EVMMultiOffRamp_execute_upgrade:test_Upgraded_Success() (gas: 136447) -EVM2EVMMultiOffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 3652910) -EVM2EVMMultiOffRamp_getExecutionState:test_GetDifferentChainExecutionState_Success() (gas: 118102) -EVM2EVMMultiOffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 87240) -EVM2EVMMultiOffRamp_manuallyExecute:test_ManualExecGasLimitMismatchSingleReport_Revert() (gas: 80095) -EVM2EVMMultiOffRamp_manuallyExecute:test_ManualExecInvalidGasLimit_Revert() (gas: 28717) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 152265) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_FailedTx_Revert() (gas: 199773) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_ForkedChain_Revert() (gas: 28246) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() (gas: 160817) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit_Success() (gas: 497730) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails() (gas: 2371243) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_Success() (gas: 201928) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 202502) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success() (gas: 651738) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 287352) -EVM2EVMMultiOffRamp_metadataHash:test_MetadataHashChangesOnOnRampAddress_Success() (gas: 10983) -EVM2EVMMultiOffRamp_metadataHash:test_MetadataHashChangesOnSourceChain_Success() (gas: 11029) -EVM2EVMMultiOffRamp_metadataHash:test_MetadataHash_Success() (gas: 9135) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 165131) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 26919) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 63524) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidEVMAddress_Revert() (gas: 44641) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() (gas: 80548) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 185328) -EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 283920) +EVM2EVMMultiOffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 19548) +EVM2EVMMultiOffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 254458) +EVM2EVMMultiOffRamp_executeSingleMessage:test_NonContract_Success() (gas: 21806) +EVM2EVMMultiOffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 206750) +EVM2EVMMultiOffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 50035) +EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 49556) +EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() (gas: 234116) +EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidation_Revert() (gas: 90376) +EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 287302) +EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithValidation_Success() (gas: 94638) +EVM2EVMMultiOffRamp_executeSingleReport:test_DisabledSourceChain_Revert() (gas: 36249) +EVM2EVMMultiOffRamp_executeSingleReport:test_EmptyReport_Revert() (gas: 23921) +EVM2EVMMultiOffRamp_executeSingleReport:test_InvalidSourcePoolAddress_Success() (gas: 462794) +EVM2EVMMultiOffRamp_executeSingleReport:test_ManualExecutionNotYetEnabled_Revert() (gas: 55975) +EVM2EVMMultiOffRamp_executeSingleReport:test_MismatchingDestChainSelector_Revert() (gas: 37072) +EVM2EVMMultiOffRamp_executeSingleReport:test_MismatchingOnRampRoot_Revert() (gas: 156143) +EVM2EVMMultiOffRamp_executeSingleReport:test_NonExistingSourceChain_Revert() (gas: 36483) +EVM2EVMMultiOffRamp_executeSingleReport:test_ReceiverError_Success() (gas: 184243) +EVM2EVMMultiOffRamp_executeSingleReport:test_RetryFailedMessageWithoutManualExecution_Revert() (gas: 194605) +EVM2EVMMultiOffRamp_executeSingleReport:test_RootNotCommitted_Revert() (gas: 49553) +EVM2EVMMultiOffRamp_executeSingleReport:test_RouterYULCall_Revert() (gas: 439123) +EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain_Success() (gas: 256572) +EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered_Success() (gas: 179039) +EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokens_Success() (gas: 198372) +EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageToNonCCIPReceiver_Success() (gas: 262344) +EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessagesNoTokensSuccess_gas() (gas: 131587) +EVM2EVMMultiOffRamp_executeSingleReport:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 399804) +EVM2EVMMultiOffRamp_executeSingleReport:test_SkippedIncorrectNonce_Success() (gas: 67753) +EVM2EVMMultiOffRamp_executeSingleReport:test_TokenDataMismatch_Revert() (gas: 82478) +EVM2EVMMultiOffRamp_executeSingleReport:test_TwoMessagesWithTokensAndGE_Success() (gas: 546583) +EVM2EVMMultiOffRamp_executeSingleReport:test_TwoMessagesWithTokensSuccess_gas() (gas: 486844) +EVM2EVMMultiOffRamp_executeSingleReport:test_UnexpectedTokenData_Revert() (gas: 36976) +EVM2EVMMultiOffRamp_executeSingleReport:test_UnhealthySingleChainCurse_Revert() (gas: 534156) +EVM2EVMMultiOffRamp_executeSingleReport:test_Unhealthy_Revert() (gas: 531524) +EVM2EVMMultiOffRamp_executeSingleReport:test_WithCurseOnAnotherSourceChain_Success() (gas: 498454) +EVM2EVMMultiOffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 131751) +EVM2EVMMultiOffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 160626) +EVM2EVMMultiOffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 3633818) +EVM2EVMMultiOffRamp_getExecutionState:test_GetDifferentChainExecutionState_Success() (gas: 118144) +EVM2EVMMultiOffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 87253) +EVM2EVMMultiOffRamp_manuallyExecute:test_ManualExecGasLimitMismatchSingleReport_Revert() (gas: 81957) +EVM2EVMMultiOffRamp_manuallyExecute:test_ManualExecInvalidGasLimit_Revert() (gas: 27558) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 165891) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_FailedTx_Revert() (gas: 212381) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_ForkedChain_Revert() (gas: 27086) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() (gas: 166703) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit_Success() (gas: 512307) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails() (gas: 2397801) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_Success() (gas: 214474) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 215037) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success() (gas: 692864) +EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 309081) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 165102) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 26890) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 63495) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidEVMAddress_Revert() (gas: 44612) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() (gas: 80519) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 185299) +EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 283897) EVM2EVMMultiOffRamp_resetUnblessedRoots:test_OnlyOwner_Revert() (gas: 11420) -EVM2EVMMultiOffRamp_resetUnblessedRoots:test_ResetUnblessedRoots_Success() (gas: 215133) +EVM2EVMMultiOffRamp_resetUnblessedRoots:test_ResetUnblessedRoots_Success() (gas: 215247) EVM2EVMMultiOffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 14223) EVM2EVMMultiOffRamp_setDynamicConfig:test_PriceRegistryZeroAddress_Revert() (gas: 11729) EVM2EVMMultiOffRamp_setDynamicConfig:test_RouterZeroAddress_Revert() (gas: 13885) -EVM2EVMMultiOffRamp_setDynamicConfig:test_SetDynamicConfigWithValidator_Success() (gas: 55589) -EVM2EVMMultiOffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 33599) -EVM2EVMMultiOffRamp_trialExecute:test_RateLimitError_Success() (gas: 243734) -EVM2EVMMultiOffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 252391) -EVM2EVMMultiOffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 306560) -EVM2EVMMultiOffRamp_trialExecute:test_trialExecute_Success() (gas: 285807) -EVM2EVMMultiOffRamp_verify:test_Blessed_Success() (gas: 176503) -EVM2EVMMultiOffRamp_verify:test_NotBlessedWrongChainSelector_Success() (gas: 178574) -EVM2EVMMultiOffRamp_verify:test_NotBlessed_Success() (gas: 141432) -EVM2EVMMultiOffRamp_verify:test_TooManyLeaves_Revert() (gas: 51501) +EVM2EVMMultiOffRamp_setDynamicConfig:test_SetDynamicConfigWithValidator_Success() (gas: 55608) +EVM2EVMMultiOffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 33618) +EVM2EVMMultiOffRamp_trialExecute:test_RateLimitError_Success() (gas: 242740) +EVM2EVMMultiOffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 251403) +EVM2EVMMultiOffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 308206) +EVM2EVMMultiOffRamp_trialExecute:test_trialExecute_Success() (gas: 285105) +EVM2EVMMultiOffRamp_verify:test_Blessed_Success() (gas: 176538) +EVM2EVMMultiOffRamp_verify:test_NotBlessedWrongChainSelector_Success() (gas: 178609) +EVM2EVMMultiOffRamp_verify:test_NotBlessed_Success() (gas: 141470) +EVM2EVMMultiOffRamp_verify:test_TooManyLeaves_Revert() (gas: 51507) EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_InvalidDestBytesOverhead_Revert() (gas: 33833) EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_InvalidDestChainConfigDestChainSelectorEqZero_Revert() (gas: 16646) EVM2EVMMultiOnRamp_applyDestChainConfigUpdates:test_InvalidDestChainConfigNewPrevOnRampOnExistingChain_Revert() (gas: 30796) @@ -273,15 +260,15 @@ EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigLinkTokenEqAddressZ EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigNonceManagerEqAddressZero_Revert() (gas: 140293) EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigRMNProxyEqAddressZero_Revert() (gas: 145314) EVM2EVMMultiOnRamp_constructor:test_Constructor_InvalidConfigTokenAdminRegistryEqAddressZero_Revert() (gas: 140360) -EVM2EVMMultiOnRamp_constructor:test_Constructor_Success() (gas: 4895426) -EVM2EVMMultiOnRamp_forwardFromRouter:test_CannotSendZeroTokens_Revert() (gas: 96383) +EVM2EVMMultiOnRamp_constructor:test_Constructor_Success() (gas: 4895404) +EVM2EVMMultiOnRamp_forwardFromRouter:test_CannotSendZeroTokens_Revert() (gas: 96382) EVM2EVMMultiOnRamp_forwardFromRouter:test_EnforceOutOfOrder_Revert() (gas: 102566) EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2AllowOutOfOrderTrue_Success() (gas: 125845) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2_Success() (gas: 156592) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessCustomExtraArgs() (gas: 156103) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessEmptyExtraArgs() (gas: 156333) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessLegacyExtraArgs() (gas: 156358) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouter_Success() (gas: 155803) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2_Success() (gas: 156591) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessCustomExtraArgs() (gas: 156102) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessEmptyExtraArgs() (gas: 156332) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessLegacyExtraArgs() (gas: 156357) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ForwardFromRouter_Success() (gas: 155802) EVM2EVMMultiOnRamp_forwardFromRouter:test_InvalidAddressEncodePacked_Revert() (gas: 35086) EVM2EVMMultiOnRamp_forwardFromRouter:test_InvalidAddress_Revert() (gas: 35347) EVM2EVMMultiOnRamp_forwardFromRouter:test_InvalidChainSelector_Revert() (gas: 27934) @@ -293,17 +280,17 @@ EVM2EVMMultiOnRamp_forwardFromRouter:test_MesssageFeeTooHigh_Revert() (gas: 3412 EVM2EVMMultiOnRamp_forwardFromRouter:test_OriginalSender_Revert() (gas: 23037) EVM2EVMMultiOnRamp_forwardFromRouter:test_Paused_Revert() (gas: 41465) EVM2EVMMultiOnRamp_forwardFromRouter:test_Permissions_Revert() (gas: 25882) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldIncrementNonceOnlyOnOrdered_Success() (gas: 204830) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldIncrementSeqNumAndNonce_Success() (gas: 231106) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldStoreLinkFees() (gas: 142549) -EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldStoreNonLinkFees() (gas: 169490) -EVM2EVMMultiOnRamp_forwardFromRouter:test_SourceTokenDataTooLarge_Revert() (gas: 3778770) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldIncrementNonceOnlyOnOrdered_Success() (gas: 204962) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldIncrementSeqNumAndNonce_Success() (gas: 231235) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldStoreLinkFees() (gas: 142548) +EVM2EVMMultiOnRamp_forwardFromRouter:test_ShouldStoreNonLinkFees() (gas: 169489) +EVM2EVMMultiOnRamp_forwardFromRouter:test_SourceTokenDataTooLarge_Revert() (gas: 3778765) EVM2EVMMultiOnRamp_forwardFromRouter:test_TooManyTokens_Revert() (gas: 32857) EVM2EVMMultiOnRamp_forwardFromRouter:test_Unhealthy_Revert() (gas: 44213) -EVM2EVMMultiOnRamp_forwardFromRouter:test_UnsupportedToken_Revert() (gas: 135416) +EVM2EVMMultiOnRamp_forwardFromRouter:test_UnsupportedToken_Revert() (gas: 135415) EVM2EVMMultiOnRamp_forwardFromRouter:test_ZeroAddressReceiver_Revert() (gas: 281847) -EVM2EVMMultiOnRamp_forwardFromRouter:test_forwardFromRouter_UnsupportedToken_Revert() (gas: 98477) -EVM2EVMMultiOnRamp_forwardFromRouter:test_forwardFromRouter_WithValidation_Success() (gas: 274980) +EVM2EVMMultiOnRamp_forwardFromRouter:test_forwardFromRouter_UnsupportedToken_Revert() (gas: 98476) +EVM2EVMMultiOnRamp_forwardFromRouter:test_forwardFromRouter_WithValidation_Success() (gas: 274979) EVM2EVMMultiOnRamp_getDataAvailabilityCost:test_EmptyMessageCalculatesDataAvailabilityCost_Success() (gas: 124845) EVM2EVMMultiOnRamp_getDataAvailabilityCost:test_SimpleMessageCalculatesDataAvailabilityCostUnsupportedDestChainSelector_Success() (gas: 11969) EVM2EVMMultiOnRamp_getDataAvailabilityCost:test_SimpleMessageCalculatesDataAvailabilityCost_Success() (gas: 24662) @@ -650,16 +637,28 @@ MultiOCR3Base_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 24191) MultiOCR3Base_transmit:test_UnauthorizedSigner_Revert() (gas: 61409) MultiOCR3Base_transmit:test_UnconfiguredPlugin_Revert() (gas: 39890) MultiOCR3Base_transmit:test_ZeroSignatures_Revert() (gas: 32973) -MultiRampsE2E:test_E2E_3MessagesSuccess_gas() (gas: 1411191) -NonceManagerTest_getIncrementedOutboundNonce:test_getIncrementedOutboundNonce_Success() (gas: 40392) -NonceManager_applyPreviousRampsUpdates:test_MultipleRampsUpdates() (gas: 68922) -NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRamp_Revert() (gas: 38712) -NonceManager_applyPreviousRampsUpdates:test_SingleRampUpdate() (gas: 39539) -NonceManager_applyPreviousRampsUpdates:test_ZeroInput() (gas: 12007) -NonceManager_onRampUpgrade:test_UpgradeNonceNewSenderStartsAtZero_Success() (gas: 173338) -NonceManager_onRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 217286) -NonceManager_onRampUpgrade:test_UpgradeSenderNoncesReadsPreviousRamp_Success() (gas: 125619) -NonceManager_onRampUpgrade:test_Upgrade_Success() (gas: 120819) +MultiRampsE2E:test_E2E_3MessagesSuccess_gas() (gas: 1435700) +NonceManager_NonceIncrementation:test_getIncrementedOutboundNonce_Success() (gas: 37907) +NonceManager_NonceIncrementation:test_incrementInboundNonce_Skip() (gas: 23694) +NonceManager_NonceIncrementation:test_incrementInboundNonce_Success() (gas: 38763) +NonceManager_NonceIncrementation:test_incrementNoncesInboundAndOutbound_Success() (gas: 71847) +NonceManager_OffRampUpgrade:test_NoPrevOffRampForChain_Success() (gas: 255244) +NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 257899) +NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 313263) +NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 295481) +NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() (gas: 248932) +NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 236918) +NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 147192) +NonceManager_OnRampUpgrade:test_UpgradeNonceNewSenderStartsAtZero_Success() (gas: 173337) +NonceManager_OnRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 217372) +NonceManager_OnRampUpgrade:test_UpgradeSenderNoncesReadsPreviousRamp_Success() (gas: 125707) +NonceManager_OnRampUpgrade:test_Upgrade_Success() (gas: 120818) +NonceManager_applyPreviousRampsUpdates:test_MultipleRampsUpdates() (gas: 122899) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOffRamp_Revert() (gas: 42959) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRampAndOffRamp_Revert() (gas: 64282) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRamp_Revert() (gas: 42823) +NonceManager_applyPreviousRampsUpdates:test_SingleRampUpdate() (gas: 66548) +NonceManager_applyPreviousRampsUpdates:test_ZeroInput() (gas: 12025) OCR2BaseNoChecks_setOCR2Config:test_FMustBePositive_Revert() (gas: 12171) OCR2BaseNoChecks_setOCR2Config:test_RepeatAddress_Revert() (gas: 42233) OCR2BaseNoChecks_setOCR2Config:test_SetConfigSuccess_gas() (gas: 84124) diff --git a/contracts/scripts/native_solc_compile_all_ccip b/contracts/scripts/native_solc_compile_all_ccip index 63ffdca7f6..3cfecbc056 100755 --- a/contracts/scripts/native_solc_compile_all_ccip +++ b/contracts/scripts/native_solc_compile_all_ccip @@ -10,7 +10,7 @@ SOLC_VERSION="0.8.24" OPTIMIZE_RUNS=26000 OPTIMIZE_RUNS_OFFRAMP=18000 OPTIMIZE_RUNS_ONRAMP=3600 -OPTIMIZE_RUNS_MULTI_OFFRAMP=1800 +OPTIMIZE_RUNS_MULTI_OFFRAMP=2400 SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" @@ -76,6 +76,7 @@ compileContract ccip/ARMProxy.sol compileContract ccip/tokenAdminRegistry/TokenAdminRegistry.sol compileContract ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol compileContract ccip/capability/CCIPConfig.sol +compileContract ccip/capability/interfaces/IOCR3ConfigEncoder.sol # Test helpers compileContract ccip/test/helpers/BurnMintERC677Helper.sol diff --git a/contracts/src/v0.8/ccip/NonceManager.sol b/contracts/src/v0.8/ccip/NonceManager.sol index 96e5fd03e4..2cfcbbe9e2 100644 --- a/contracts/src/v0.8/ccip/NonceManager.sol +++ b/contracts/src/v0.8/ccip/NonceManager.sol @@ -11,12 +11,13 @@ import {AuthorizedCallers} from "../shared/access/AuthorizedCallers.sol"; contract NonceManager is INonceManager, AuthorizedCallers { error PreviousRampAlreadySet(); - event PreviousOnRampUpdated(uint64 indexed destChainSelector, address prevOnRamp); + event PreviousRampsUpdated(uint64 indexed remoteChainSelector, PreviousRamps prevRamp); + event SkippedIncorrectNonce(uint64 sourceChainSelector, uint64 nonce, bytes sender); /// @dev Struct that contains the previous on/off ramp addresses - // TODO: add prevOffRamp struct PreviousRamps { address prevOnRamp; // Previous onRamp + address prevOffRamp; // Previous offRamp } /// @dev Struct that contains the chain selector and the previous on/off ramps, same as PreviousRamps but with the chain selector @@ -30,6 +31,10 @@ contract NonceManager is INonceManager, AuthorizedCallers { mapping(uint64 chainSelector => PreviousRamps previousRamps) private s_previousRamps; /// @dev The current outbound nonce per sender used on the onramp mapping(uint64 destChainSelector => mapping(address sender => uint64 outboundNonce)) private s_outboundNonces; + /// @dev The current inbound nonce per sender used on the offramp + /// Eventually in sync with the outbound nonce in the remote source chain NonceManager, used to enforce that messages are + /// executed in the same order they are sent (assuming they are DON) + mapping(uint64 sourceChainSelector => mapping(bytes sender => uint64 inboundNonce)) private s_inboundNonces; constructor(address[] memory authorizedCallers) AuthorizedCallers(authorizedCallers) {} @@ -44,9 +49,7 @@ contract NonceManager is INonceManager, AuthorizedCallers { return outboundNonce; } - /// TODO: add incrementInboundNonce - - /// @notice Returns the outbound nonce for the given sender on the given destination chain + /// @notice Returns the outbound nonce for a given sender on a given destination chain /// @param destChainSelector The destination chain selector /// @param sender The sender address /// @return The outbound nonce @@ -57,6 +60,8 @@ contract NonceManager is INonceManager, AuthorizedCallers { function _getOutboundNonce(uint64 destChainSelector, address sender) private view returns (uint64) { uint64 outboundNonce = s_outboundNonces[destChainSelector][sender]; + // When introducing the NonceManager with existing lanes, we still want to have sequential nonces. + // Referencing the old onRamp preserves sequencing between updates. if (outboundNonce == 0) { address prevOnRamp = s_previousRamps[destChainSelector].prevOnRamp; if (prevOnRamp != address(0)) { @@ -67,7 +72,51 @@ contract NonceManager is INonceManager, AuthorizedCallers { return outboundNonce; } - /// TODO: add getInboundNonce + /// @inheritdoc INonceManager + function incrementInboundNonce( + uint64 sourceChainSelector, + uint64 expectedNonce, + bytes calldata sender + ) external onlyAuthorizedCallers returns (bool) { + uint64 inboundNonce = _getInboundNonce(sourceChainSelector, sender) + 1; + + if (inboundNonce != expectedNonce) { + // If the nonce is not the expected one, this means that there are still messages in flight so we skip + // the nonce increment + emit SkippedIncorrectNonce(sourceChainSelector, expectedNonce, sender); + return false; + } + + s_inboundNonces[sourceChainSelector][sender] = inboundNonce; + + return true; + } + + /// @notice Returns the inbound nonce for a given sender on a given source chain + /// @param sourceChainSelector The source chain selector + /// @param sender The encoded sender address + /// @return The inbound nonce + function getInboundNonce(uint64 sourceChainSelector, bytes calldata sender) external view returns (uint64) { + return _getInboundNonce(sourceChainSelector, sender); + } + + function _getInboundNonce(uint64 sourceChainSelector, bytes calldata sender) private view returns (uint64) { + uint64 inboundNonce = s_inboundNonces[sourceChainSelector][sender]; + + // When introducing the NonceManager with existing lanes, we still want to have sequential nonces. + // Referencing the old offRamp to check the expected nonce if none is set for a + // given sender allows us to skip the current message in the current offRamp if it would not be the next according + // to the old offRamp. This preserves sequencing between updates. + if (inboundNonce == 0) { + address prevOffRamp = s_previousRamps[sourceChainSelector].prevOffRamp; + if (prevOffRamp != address(0)) { + // We only expect EVM previous offRamps here so we can safely decode the sender + return IEVM2AnyOnRamp(prevOffRamp).getSenderNonce(abi.decode(sender, (address))); + } + } + + return inboundNonce; + } /// @notice Updates the previous ramps addresses /// @param previousRampsArgs The previous on/off ramps addresses @@ -77,13 +126,15 @@ contract NonceManager is INonceManager, AuthorizedCallers { PreviousRamps storage prevRamps = s_previousRamps[previousRampsArg.remoteChainSelector]; - // If the previous onRamp is already set then it should not be updated - if (prevRamps.prevOnRamp != address(0)) { + // If the previous ramps are already set then they should not be updated + if (prevRamps.prevOnRamp != address(0) || prevRamps.prevOffRamp != address(0)) { revert PreviousRampAlreadySet(); } prevRamps.prevOnRamp = previousRampsArg.prevRamps.prevOnRamp; - emit PreviousOnRampUpdated(previousRampsArg.remoteChainSelector, prevRamps.prevOnRamp); + prevRamps.prevOffRamp = previousRampsArg.prevRamps.prevOffRamp; + + emit PreviousRampsUpdated(previousRampsArg.remoteChainSelector, previousRampsArg.prevRamps); } } diff --git a/contracts/src/v0.8/ccip/capability/CCIPConfig.sol b/contracts/src/v0.8/ccip/capability/CCIPConfig.sol index 5ae82fe900..795db0c3e4 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPConfig.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPConfig.sol @@ -7,6 +7,9 @@ import {ICapabilitiesRegistry} from "./interfaces/ICapabilitiesRegistry.sol"; import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; +import {Internal} from "../libraries/Internal.sol"; +import {CCIPConfigTypes} from "./libraries/CCIPConfigTypes.sol"; + import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; @@ -21,7 +24,7 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator /// @notice Emitted when a chain's configuration is set. /// @param chainSelector The chain selector. /// @param chainConfig The chain configuration. - event ChainConfigSet(uint64 chainSelector, ChainConfig chainConfig); + event ChainConfigSet(uint64 chainSelector, CCIPConfigTypes.ChainConfig chainConfig); /// @notice Emitted when a chain's configuration is removed. /// @param chainSelector The chain selector. @@ -44,70 +47,14 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator error InvalidPluginType(); error OfframpAddressCannotBeZero(); error InvalidConfigLength(uint256 length); - error InvalidConfigStateTransition(ConfigState currentState, ConfigState proposedState); + error InvalidConfigStateTransition( + CCIPConfigTypes.ConfigState currentState, CCIPConfigTypes.ConfigState proposedState + ); error NonExistentConfigTransition(); error WrongConfigCount(uint64 got, uint64 expected); error WrongConfigDigest(bytes32 got, bytes32 expected); error WrongConfigDigestBlueGreen(bytes32 got, bytes32 expected); - /// @notice PluginType indicates the type of plugin that the configuration is for. - /// @param Commit The configuration is for the commit plugin. - /// @param Execution The configuration is for the execution plugin. - enum PluginType { - Commit, - Execution - } - - /// @notice ConfigState indicates the state of the configuration. - /// A DON's configuration always starts out in the "Init" state - this is the starting state. - /// The only valid transition from "Init" is to the "Running" state - this is the first ever configuration. - /// The only valid transition from "Running" is to the "Staging" state - this is a blue/green proposal. - /// The only valid transition from "Staging" is back to the "Running" state - this is a promotion. - /// TODO: explain rollbacks? - enum ConfigState { - Init, - Running, - Staging - } - - /// @notice Chain configuration. - /// Changes to chain configuration are detected out-of-band in plugins and decoded offchain. - struct ChainConfig { - bytes32[] readers; // The P2P IDs of the readers for the chain. These IDs must be registered in the capabilities registry. - uint8 fChain; // The fault tolerance parameter of the chain. - bytes config; // The chain configuration. This is kept intentionally opaque so as to add fields in the future if needed. - } - - /// @notice Chain configuration information struct used in applyChainConfigUpdates and getAllChainConfigs. - struct ChainConfigInfo { - uint64 chainSelector; - ChainConfig chainConfig; - } - - /// @notice OCR3 configuration. - struct OCR3Config { - PluginType pluginType; // ────────╮ The plugin that the configuration is for. - uint64 chainSelector; // | The (remote) chain that the configuration is for. - uint8 F; // | The "big F" parameter for the role DON. - uint64 offchainConfigVersion; // ─╯ The version of the offchain configuration. - bytes offrampAddress; // The remote chain offramp address. - bytes32[] bootstrapP2PIds; // The bootstrap P2P IDs of the oracles that are part of the role DON. - // len(p2pIds) == len(signers) == len(transmitters) == 3 * F + 1 - // NOTE: indexes matter here! The p2p ID at index i corresponds to the signer at index i and the transmitter at index i. - // This is crucial in order to build the oracle ID <-> peer ID mapping offchain. - bytes32[] p2pIds; // The P2P IDs of the oracles that are part of the role DON. - bytes[] signers; // The onchain signing keys of nodes in the don. - bytes[] transmitters; // The onchain transmitter keys of nodes in the don. - bytes offchainConfig; // The offchain configuration for the OCR3 protocol. Protobuf encoded. - } - - /// @notice OCR3 configuration with metadata, specifically the config count and the config digest. - struct OCR3ConfigWithMeta { - OCR3Config config; // The OCR3 configuration. - uint64 configCount; // The config count used to compute the config digest. - bytes32 configDigest; // The config digest of the OCR3 configuration. - } - /// @notice Type and version override. string public constant override typeAndVersion = "CCIPConfig 1.6.0-dev"; @@ -115,7 +62,7 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator address internal immutable i_capabilitiesRegistry; /// @notice chain configuration for each chain that CCIP is deployed on. - mapping(uint64 chainSelector => ChainConfig chainConfig) internal s_chainConfigurations; + mapping(uint64 chainSelector => CCIPConfigTypes.ChainConfig chainConfig) internal s_chainConfigurations; /// @notice All chains that are configured. EnumerableSet.UintSet internal s_remoteChainSelectors; @@ -123,7 +70,9 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator /// @notice OCR3 configurations for each DON. /// Each CR DON will have a commit and execution configuration. /// This means that a DON can have up to 4 configurations, since we are implementing blue/green deployments. - mapping(uint32 donId => mapping(PluginType pluginType => OCR3ConfigWithMeta[] ocr3Configs)) internal s_ocr3Configs; + mapping( + uint32 donId => mapping(Internal.OCRPluginType pluginType => CCIPConfigTypes.OCR3ConfigWithMeta[] ocr3Configs) + ) internal s_ocr3Configs; /// @notice The DONs that have been configured. EnumerableSet.UintSet internal s_donIds; @@ -149,13 +98,16 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator /// @notice Returns all the chain configurations. /// @return The chain configurations. // TODO: will this eventually hit the RPC max response size limit? - function getAllChainConfigs() external view returns (ChainConfigInfo[] memory) { + function getAllChainConfigs() external view returns (CCIPConfigTypes.ChainConfigInfo[] memory) { uint256[] memory chainSelectors = s_remoteChainSelectors.values(); - ChainConfigInfo[] memory chainConfigs = new ChainConfigInfo[](s_remoteChainSelectors.length()); + CCIPConfigTypes.ChainConfigInfo[] memory chainConfigs = + new CCIPConfigTypes.ChainConfigInfo[](s_remoteChainSelectors.length()); for (uint256 i = 0; i < chainSelectors.length; ++i) { uint64 chainSelector = uint64(chainSelectors[i]); - chainConfigs[i] = - ChainConfigInfo({chainSelector: chainSelector, chainConfig: s_chainConfigurations[chainSelector]}); + chainConfigs[i] = CCIPConfigTypes.ChainConfigInfo({ + chainSelector: chainSelector, + chainConfig: s_chainConfigurations[chainSelector] + }); } return chainConfigs; } @@ -164,7 +116,10 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator /// @param donId The DON ID. /// @param pluginType The plugin type. /// @return The OCR3 configurations, up to 2 (blue and green). - function getOCRConfig(uint32 donId, PluginType pluginType) external view returns (OCR3ConfigWithMeta[] memory) { + function getOCRConfig( + uint32 donId, + Internal.OCRPluginType pluginType + ) external view returns (CCIPConfigTypes.OCR3ConfigWithMeta[] memory) { return s_ocr3Configs[donId][pluginType]; } @@ -190,27 +145,32 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator revert OnlyCapabilitiesRegistryCanCall(); } - OCR3Config[] memory ocr3Configs = abi.decode(config, (OCR3Config[])); - (OCR3Config[] memory commitConfigs, OCR3Config[] memory execConfigs) = _groupByPluginType(ocr3Configs); + CCIPConfigTypes.OCR3Config[] memory ocr3Configs = abi.decode(config, (CCIPConfigTypes.OCR3Config[])); + (CCIPConfigTypes.OCR3Config[] memory commitConfigs, CCIPConfigTypes.OCR3Config[] memory execConfigs) = + _groupByPluginType(ocr3Configs); if (commitConfigs.length > 0) { - _updatePluginConfig(donId, PluginType.Commit, commitConfigs); + _updatePluginConfig(donId, Internal.OCRPluginType.Commit, commitConfigs); } if (execConfigs.length > 0) { - _updatePluginConfig(donId, PluginType.Execution, execConfigs); + _updatePluginConfig(donId, Internal.OCRPluginType.Execution, execConfigs); } } - function _updatePluginConfig(uint32 donId, PluginType pluginType, OCR3Config[] memory newConfig) internal { - OCR3ConfigWithMeta[] memory currentConfig = s_ocr3Configs[donId][pluginType]; + function _updatePluginConfig( + uint32 donId, + Internal.OCRPluginType pluginType, + CCIPConfigTypes.OCR3Config[] memory newConfig + ) internal { + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = s_ocr3Configs[donId][pluginType]; // Validate the state transition being proposed, which is implicitly defined by the combination // of lengths of the current and new configurations. - ConfigState currentState = _stateFromConfigLength(currentConfig.length); - ConfigState proposedState = _stateFromConfigLength(newConfig.length); + CCIPConfigTypes.ConfigState currentState = _stateFromConfigLength(currentConfig.length); + CCIPConfigTypes.ConfigState proposedState = _stateFromConfigLength(newConfig.length); _validateConfigStateTransition(currentState, proposedState); // Build the new configuration with metadata and validate that the transition is valid. - OCR3ConfigWithMeta[] memory newConfigWithMeta = + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta = _computeNewConfigWithMeta(donId, currentConfig, newConfig, currentState, proposedState); _validateConfigTransition(currentConfig, newConfigWithMeta); @@ -229,11 +189,11 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator /// @notice Determine the config state of the configuration from the length of the config. /// @param configLen The length of the configuration. /// @return The config state. - function _stateFromConfigLength(uint256 configLen) internal pure returns (ConfigState) { + function _stateFromConfigLength(uint256 configLen) internal pure returns (CCIPConfigTypes.ConfigState) { if (configLen > 2) { revert InvalidConfigLength(configLen); } - return ConfigState(configLen); + return CCIPConfigTypes.ConfigState(configLen); } // the only valid state transitions are the following: @@ -241,7 +201,10 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator // running -> staging (blue/green proposal) // staging -> running (promotion) // everything else is invalid and should revert. - function _validateConfigStateTransition(ConfigState currentState, ConfigState newState) internal pure { + function _validateConfigStateTransition( + CCIPConfigTypes.ConfigState currentState, + CCIPConfigTypes.ConfigState newState + ) internal pure { // Calculate the difference between the new state and the current state int256 stateDiff = int256(uint256(newState)) - int256(uint256(currentState)); @@ -250,15 +213,15 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator // 1. currentState -> newState (where stateDiff == 1) // e.g., init -> running or running -> staging // 2. staging -> running (where stateDiff == -1) - if (stateDiff == 1 || (stateDiff == -1 && currentState == ConfigState.Staging)) { + if (stateDiff == 1 || (stateDiff == -1 && currentState == CCIPConfigTypes.ConfigState.Staging)) { return; } revert InvalidConfigStateTransition(currentState, newState); } function _validateConfigTransition( - OCR3ConfigWithMeta[] memory currentConfig, - OCR3ConfigWithMeta[] memory newConfigWithMeta + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig, + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta ) internal pure { uint256 currentConfigLen = currentConfig.length; uint256 newConfigLen = newConfigWithMeta.length; @@ -303,35 +266,36 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator /// @return The new configuration with metadata. function _computeNewConfigWithMeta( uint32 donId, - OCR3ConfigWithMeta[] memory currentConfig, - OCR3Config[] memory newConfig, - ConfigState currentState, - ConfigState newState - ) internal view returns (OCR3ConfigWithMeta[] memory) { + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig, + CCIPConfigTypes.OCR3Config[] memory newConfig, + CCIPConfigTypes.ConfigState currentState, + CCIPConfigTypes.ConfigState newState + ) internal view returns (CCIPConfigTypes.OCR3ConfigWithMeta[] memory) { uint64[] memory configCounts = new uint64[](newConfig.length); // Set config counts based on the only valid state transitions. // Init -> Running (first ever config) // Running -> Staging (blue/green proposal) // Staging -> Running (promotion) - if (currentState == ConfigState.Init && newState == ConfigState.Running) { + if (currentState == CCIPConfigTypes.ConfigState.Init && newState == CCIPConfigTypes.ConfigState.Running) { // First ever config starts with config count == 1. configCounts[0] = 1; - } else if (currentState == ConfigState.Running && newState == ConfigState.Staging) { + } else if (currentState == CCIPConfigTypes.ConfigState.Running && newState == CCIPConfigTypes.ConfigState.Staging) { // On a blue/green proposal, the config count of the green config is the blue config count + 1. configCounts[0] = currentConfig[0].configCount; configCounts[1] = currentConfig[0].configCount + 1; - } else if (currentState == ConfigState.Staging && newState == ConfigState.Running) { + } else if (currentState == CCIPConfigTypes.ConfigState.Staging && newState == CCIPConfigTypes.ConfigState.Running) { // On a promotion, the config count of the green config becomes the blue config count. configCounts[0] = currentConfig[1].configCount; } else { revert InvalidConfigStateTransition(currentState, newState); } - OCR3ConfigWithMeta[] memory newConfigWithMeta = new OCR3ConfigWithMeta[](newConfig.length); + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta = + new CCIPConfigTypes.OCR3ConfigWithMeta[](newConfig.length); for (uint256 i = 0; i < configCounts.length; ++i) { _validateConfig(newConfig[i]); - newConfigWithMeta[i] = OCR3ConfigWithMeta({ + newConfigWithMeta[i] = CCIPConfigTypes.OCR3ConfigWithMeta({ config: newConfig[i], configCount: configCounts[i], configDigest: _computeConfigDigest(donId, configCounts[i], newConfig[i]) @@ -343,10 +307,10 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator /// @notice Group the OCR3 configurations by plugin type for further processing. /// @param ocr3Configs The OCR3 configurations to group. - function _groupByPluginType(OCR3Config[] memory ocr3Configs) + function _groupByPluginType(CCIPConfigTypes.OCR3Config[] memory ocr3Configs) internal pure - returns (OCR3Config[] memory commitConfigs, OCR3Config[] memory execConfigs) + returns (CCIPConfigTypes.OCR3Config[] memory commitConfigs, CCIPConfigTypes.OCR3Config[] memory execConfigs) { if (ocr3Configs.length > MAX_OCR3_CONFIGS_PER_DON) { revert TooManyOCR3Configs(); @@ -356,12 +320,12 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator // If we have less we will adjust the length later using mstore. // If the caller provides more than 2 configs per plugin type, we will revert due to out of bounds // access in the for loop below. - commitConfigs = new OCR3Config[](MAX_OCR3_CONFIGS_PER_PLUGIN); - execConfigs = new OCR3Config[](MAX_OCR3_CONFIGS_PER_PLUGIN); + commitConfigs = new CCIPConfigTypes.OCR3Config[](MAX_OCR3_CONFIGS_PER_PLUGIN); + execConfigs = new CCIPConfigTypes.OCR3Config[](MAX_OCR3_CONFIGS_PER_PLUGIN); uint256 commitCount; uint256 execCount; for (uint256 i = 0; i < ocr3Configs.length; ++i) { - if (ocr3Configs[i].pluginType == PluginType.Commit) { + if (ocr3Configs[i].pluginType == Internal.OCRPluginType.Commit) { commitConfigs[commitCount] = ocr3Configs[i]; ++commitCount; } else { @@ -379,9 +343,11 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator return (commitConfigs, execConfigs); } - function _validateConfig(OCR3Config memory cfg) internal view { + function _validateConfig(CCIPConfigTypes.OCR3Config memory cfg) internal view { if (cfg.chainSelector == 0) revert ChainSelectorNotSet(); - if (cfg.pluginType != PluginType.Commit && cfg.pluginType != PluginType.Execution) revert InvalidPluginType(); + if (cfg.pluginType != Internal.OCRPluginType.Commit && cfg.pluginType != Internal.OCRPluginType.Execution) { + revert InvalidPluginType(); + } // TODO: can we do more sophisticated validation than this? if (cfg.offrampAddress.length == 0) revert OfframpAddressCannotBeZero(); if (!s_remoteChainSelectors.contains(cfg.chainSelector)) revert ChainSelectorNotFound(cfg.chainSelector); @@ -424,7 +390,7 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator function _computeConfigDigest( uint32 donId, uint64 configCount, - OCR3Config memory ocr3Config + CCIPConfigTypes.OCR3Config memory ocr3Config ) internal pure returns (bytes32) { uint256 h = uint256( keccak256( @@ -458,7 +424,7 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator /// @param chainConfigAdds The chain configurations to add. function applyChainConfigUpdates( uint64[] calldata chainSelectorRemoves, - ChainConfigInfo[] calldata chainConfigAdds + CCIPConfigTypes.ChainConfigInfo[] calldata chainConfigAdds ) external onlyOwner { // Process removals first. for (uint256 i = 0; i < chainSelectorRemoves.length; ++i) { @@ -475,7 +441,7 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator // Process additions next. for (uint256 i = 0; i < chainConfigAdds.length; ++i) { - ChainConfig memory chainConfig = chainConfigAdds[i].chainConfig; + CCIPConfigTypes.ChainConfig memory chainConfig = chainConfigAdds[i].chainConfig; bytes32[] memory readers = chainConfig.readers; uint64 chainSelector = chainConfigAdds[i].chainSelector; diff --git a/contracts/src/v0.8/ccip/capability/interfaces/ICapabilitiesRegistry.sol b/contracts/src/v0.8/ccip/capability/interfaces/ICapabilitiesRegistry.sol index 5e3c2c4e8d..621c3686cf 100644 --- a/contracts/src/v0.8/ccip/capability/interfaces/ICapabilitiesRegistry.sol +++ b/contracts/src/v0.8/ccip/capability/interfaces/ICapabilitiesRegistry.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.24; interface ICapabilitiesRegistry { diff --git a/contracts/src/v0.8/ccip/capability/interfaces/IOCR3ConfigEncoder.sol b/contracts/src/v0.8/ccip/capability/interfaces/IOCR3ConfigEncoder.sol new file mode 100644 index 0000000000..6d0b0f72a5 --- /dev/null +++ b/contracts/src/v0.8/ccip/capability/interfaces/IOCR3ConfigEncoder.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import {CCIPConfigTypes} from "../libraries/CCIPConfigTypes.sol"; + +/// @dev This is so that we can generate gethwrappers and easily encode/decode OCR3Config +/// in the offchain integration tests. +interface IOCR3ConfigEncoder { + /// @dev Encodes an array of OCR3Config into a bytes array. For test usage only. + function exposeOCR3Config(CCIPConfigTypes.OCR3Config[] calldata config) external view returns (bytes memory); +} diff --git a/contracts/src/v0.8/ccip/capability/libraries/CCIPConfigTypes.sol b/contracts/src/v0.8/ccip/capability/libraries/CCIPConfigTypes.sol new file mode 100644 index 0000000000..5f052b3509 --- /dev/null +++ b/contracts/src/v0.8/ccip/capability/libraries/CCIPConfigTypes.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import {Internal} from "../../libraries/Internal.sol"; + +library CCIPConfigTypes { + /// @notice ConfigState indicates the state of the configuration. + /// A DON's configuration always starts out in the "Init" state - this is the starting state. + /// The only valid transition from "Init" is to the "Running" state - this is the first ever configuration. + /// The only valid transition from "Running" is to the "Staging" state - this is a blue/green proposal. + /// The only valid transition from "Staging" is back to the "Running" state - this is a promotion. + /// TODO: explain rollbacks? + enum ConfigState { + Init, + Running, + Staging + } + + /// @notice Chain configuration. + /// Changes to chain configuration are detected out-of-band in plugins and decoded offchain. + struct ChainConfig { + bytes32[] readers; // The P2P IDs of the readers for the chain. These IDs must be registered in the capabilities registry. + uint8 fChain; // The fault tolerance parameter of the chain. + bytes config; // The chain configuration. This is kept intentionally opaque so as to add fields in the future if needed. + } + + /// @notice Chain configuration information struct used in applyChainConfigUpdates and getAllChainConfigs. + struct ChainConfigInfo { + uint64 chainSelector; + ChainConfig chainConfig; + } + + /// @notice OCR3 configuration. + struct OCR3Config { + Internal.OCRPluginType pluginType; // ────────╮ The plugin that the configuration is for. + uint64 chainSelector; // | The (remote) chain that the configuration is for. + uint8 F; // | The "big F" parameter for the role DON. + uint64 offchainConfigVersion; // ─────────────╯ The version of the offchain configuration. + bytes offrampAddress; // The remote chain offramp address. + bytes32[] bootstrapP2PIds; // The bootstrap P2P IDs of the oracles that are part of the role DON. + // len(p2pIds) == len(signers) == len(transmitters) == 3 * F + 1 + // NOTE: indexes matter here! The p2p ID at index i corresponds to the signer at index i and the transmitter at index i. + // This is crucial in order to build the oracle ID <-> peer ID mapping offchain. + bytes32[] p2pIds; // The P2P IDs of the oracles that are part of the role DON. + bytes[] signers; // The onchain signing keys of nodes in the don. + bytes[] transmitters; // The onchain transmitter keys of nodes in the don. + bytes offchainConfig; // The offchain configuration for the OCR3 protocol. Protobuf encoded. + } + + /// @notice OCR3 configuration with metadata, specifically the config count and the config digest. + struct OCR3ConfigWithMeta { + OCR3Config config; // The OCR3 configuration. + uint64 configCount; // The config count used to compute the config digest. + bytes32 configDigest; // The config digest of the OCR3 configuration. + } +} diff --git a/contracts/src/v0.8/ccip/interfaces/IAny2EVMMultiOffRamp.sol b/contracts/src/v0.8/ccip/interfaces/IAny2EVMMultiOffRamp.sol deleted file mode 100644 index 484bd9c52c..0000000000 --- a/contracts/src/v0.8/ccip/interfaces/IAny2EVMMultiOffRamp.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -interface IAny2EVMMultiOffRamp { - /// @notice Returns the the current nonce for a receiver. - /// @param sourceChainSelector The source chain to retrieve the nonce for - /// @param sender The sender address - /// @return nonce The nonce value belonging to the sender address. - function getSenderNonce(uint64 sourceChainSelector, address sender) external view returns (uint64 nonce); -} diff --git a/contracts/src/v0.8/ccip/interfaces/INonceManager.sol b/contracts/src/v0.8/ccip/interfaces/INonceManager.sol index 1e1b915ce0..52408ae4f5 100644 --- a/contracts/src/v0.8/ccip/interfaces/INonceManager.sol +++ b/contracts/src/v0.8/ccip/interfaces/INonceManager.sol @@ -3,9 +3,21 @@ pragma solidity ^0.8.0; /// @notice Contract interface that allows managing sender nonces interface INonceManager { - /// @notice Increments the outbound nonce for the given sender on the given destination chain + /// @notice Increments the outbound nonce for a given sender on a given destination chain /// @param destChainSelector The destination chain selector /// @param sender The sender address /// @return The new outbound nonce function getIncrementedOutboundNonce(uint64 destChainSelector, address sender) external returns (uint64); + + /// @notice Increments the inbound nonce for a given sender on a given source chain + /// @notice The increment is only applied if the resulting nonce matches the expectedNonce + /// @param sourceChainSelector The destination chain selector + /// @param expectedNonce The expected inbound nonce + /// @param sender The encoded sender address + /// @return True if the nonce was incremented, false otherwise + function incrementInboundNonce( + uint64 sourceChainSelector, + uint64 expectedNonce, + bytes calldata sender + ) external returns (bool); } diff --git a/contracts/src/v0.8/ccip/libraries/Internal.sol b/contracts/src/v0.8/ccip/libraries/Internal.sol index e39fcf4c0f..9068ca3a10 100644 --- a/contracts/src/v0.8/ccip/libraries/Internal.sol +++ b/contracts/src/v0.8/ccip/libraries/Internal.sol @@ -71,7 +71,7 @@ library Internal { /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers. struct ExecutionReportSingleChain { uint64 sourceChainSelector; // Source chain selector for which the report is submitted - EVM2EVMMessage[] messages; + Any2EVMRampMessage[] messages; // Contains a bytes array for each message, each inner bytes array contains bytes per transferred token bytes[][] offchainTokenData; bytes32[] proofs; @@ -106,6 +106,7 @@ library Internal { bytes32 messageId; // a hash of the message data } + // TODO: create new const for EVM2AnyMessage /// @dev EVM2EVMMessage struct has 13 fields, including 3 variable arrays. /// Each variable array takes 1 more slot to store its length. /// When abi encoded, excluding array contents, @@ -113,6 +114,7 @@ library Internal { /// For structs that contain arrays, 1 more slot is added to the front, reaching a total of 17. uint256 public constant MESSAGE_FIXED_BYTES = 32 * 17; + // TODO: create new const for EVM2AnyMessage /// @dev Each token transfer adds 1 EVMTokenAmount and 1 bytes. /// When abiEncoded, each EVMTokenAmount takes 2 slots, each bytes takes 2 slots, excl bytes contents uint256 public constant MESSAGE_FIXED_BYTES_PER_TOKEN = 32 * 4; @@ -132,6 +134,12 @@ library Internal { bytes32 internal constant EVM_2_EVM_MESSAGE_HASH = keccak256("EVM2EVMMessageHashV2"); + /// @dev Used to hash messages for single-lane ramps. + /// OnRamp hash(EVM2EVMMessage) = OffRamp hash(EVM2EVMMessage) + /// The EVM2EVMMessage's messageId is expected to be the output of this hash function + /// @param original Message to hash + /// @param metadataHash Immutable metadata hash representing a lane with a fixed OnRamp + /// @return hashedMessage hashed message as a keccak256 function _hash(EVM2EVMMessage memory original, bytes32 metadataHash) internal pure returns (bytes32) { // Fixed-size message fields are included in nested hash to reduce stack pressure. // This hashing scheme is also used by RMN. If changing it, please notify the RMN maintainers. @@ -158,6 +166,43 @@ library Internal { ); } + bytes32 internal constant ANY_2_EVM_MESSAGE_HASH = keccak256("Any2EVMMessageHashV1"); + + /// @dev Used to hash messages for multi-lane family-agnostic OffRamps. + /// OnRamp hash(EVM2AnyMessage) != Any2EVMRampMessage.messageId + /// OnRamp hash(EVM2AnyMessage) != OffRamp hash(Any2EVMRampMessage) + /// @param original OffRamp message to hash + /// @param onRamp OnRamp to hash the message with - used to compute the metadataHash + /// @return hashedMessage hashed message as a keccak256 + function _hash(Any2EVMRampMessage memory original, bytes memory onRamp) internal pure returns (bytes32) { + // Fixed-size message fields are included in nested hash to reduce stack pressure. + // This hashing scheme is also used by RMN. If changing it, please notify the RMN maintainers. + return keccak256( + abi.encode( + MerkleMultiProof.LEAF_DOMAIN_SEPARATOR, + // Implicit metadata hash + keccak256( + abi.encode( + ANY_2_EVM_MESSAGE_HASH, original.header.sourceChainSelector, original.header.destChainSelector, onRamp + ) + ), + keccak256( + abi.encode( + original.header.messageId, + original.sender, + original.receiver, + original.header.sequenceNumber, + original.gasLimit, + original.header.nonce + ) + ), + keccak256(original.data), + keccak256(abi.encode(original.tokenAmounts)), + keccak256(abi.encode(original.sourceTokenData)) + ) + ); + } + /// @notice This methods provides validation for parsing abi encoded addresses by ensuring the /// address is within the EVM address space. If it isn't it will revert with an InvalidEVMAddress error, which /// we can catch and handle more gracefully than a revert from abi.decode. @@ -200,4 +245,29 @@ library Internal { Commit, Execution } + + /// @notice Family-agnostic header for OnRamp & OffRamp messages. + /// The messageId is not expected to match hash(message), since it may originate from another ramp family + // TODO: revisit if destChainSelector is required (likely sufficient to have it implicitly in the commit roots) + struct RampMessageHeader { + bytes32 messageId; // Unique identifier for the message, generated with the source chain's encoding scheme (i.e. not necessarily abi.encoded) + uint64 sourceChainSelector; // ───────╮ the chain selector of the source chain, note: not chainId + uint64 destChainSelector; // | the chain selector of the destination chain, note: not chainId + uint64 sequenceNumber; // │ sequence number, not unique across lanes + uint64 nonce; // ─────────────────────╯ nonce for this lane for this sender, not unique across senders/lanes + } + + /// @notice Family-agnostic message routed to an OffRamp + /// Note: hash(Any2EVMRampMessage) != hash(EVM2AnyRampMessage), hash(Any2EVMRampMessage) != messageId + /// due to encoding & parameter differences + struct Any2EVMRampMessage { + RampMessageHeader header; // Message header + bytes sender; // sender address on the source chain + bytes data; // arbitrary data payload supplied by the message sender + address receiver; // receiver address on the destination chain + uint256 gasLimit; // user supplied maximum gas amount available for dest chain execution + // TODO: revisit collapsing tokenAmounts + sourceTokenData into one struct array + Client.EVMTokenAmount[] tokenAmounts; // array of tokens and amounts to transfer + bytes[] sourceTokenData; // array of token data, one per token + } } diff --git a/contracts/src/v0.8/ccip/offRamp/EVM2EVMMultiOffRamp.sol b/contracts/src/v0.8/ccip/offRamp/EVM2EVMMultiOffRamp.sol index feb5ff360f..08c8086884 100644 --- a/contracts/src/v0.8/ccip/offRamp/EVM2EVMMultiOffRamp.sol +++ b/contracts/src/v0.8/ccip/offRamp/EVM2EVMMultiOffRamp.sol @@ -3,9 +3,8 @@ pragma solidity 0.8.24; import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; import {IAny2EVMMessageReceiver} from "../interfaces/IAny2EVMMessageReceiver.sol"; -import {IAny2EVMMultiOffRamp} from "../interfaces/IAny2EVMMultiOffRamp.sol"; -import {IAny2EVMOffRamp} from "../interfaces/IAny2EVMOffRamp.sol"; import {IMessageInterceptor} from "../interfaces/IMessageInterceptor.sol"; +import {INonceManager} from "../interfaces/INonceManager.sol"; import {IPoolV1} from "../interfaces/IPool.sol"; import {IPriceRegistry} from "../interfaces/IPriceRegistry.sol"; import {IRMN} from "../interfaces/IRMN.sol"; @@ -25,12 +24,12 @@ import {ERC165Checker} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts /// @notice EVM2EVMOffRamp enables OCR networks to execute multiple messages /// in an OffRamp in a single transaction. -/// @dev The EVM2EVMOnRamp, CommitStore and EVM2EVMOffRamp form an xchain upgradeable unit. Any change to one of them -/// results an onchain upgrade of all 3. +/// @dev The EVM2EVMMultiOnRamp and EVM2EVMMultiOffRamp form an xchain upgradeable unit. Any change to one of them +/// results an onchain upgrade of both contracts. /// @dev MultiOCR3Base is used to store multiple OCR configs for both the OffRamp and the CommitStore. /// The execution plugin type has to be configured without signature verification, and the commit /// plugin type with verification. -contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3Base { +contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { using ERC165Checker for address; using EnumerableMapAddresses for EnumerableMapAddresses.AddressToAddressMap; @@ -52,7 +51,6 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 error TokenHandlingError(bytes error); error EmptyReport(); error CursedByRMN(uint64 sourceChainSelector); - error InvalidMessageId(bytes32 messageId); error NotACompatiblePool(address notPool); error InvalidDataLength(uint256 expected, uint256 got); error InvalidNewState(uint64 sourceChainSelector, uint64 sequenceNumber, Internal.MessageExecutionState newState); @@ -60,12 +58,11 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 error StaleCommitReport(); error InvalidInterval(uint64 sourceChainSelector, Interval interval); error ZeroAddressNotAllowed(); + error InvalidMessageDestChainSelector(uint64 messageDestChainSelector); /// @dev Atlas depends on this event, if changing, please notify Atlas. event StaticConfigSet(StaticConfig staticConfig); event DynamicConfigSet(DynamicConfig dynamicConfig); - event SkippedIncorrectNonce(uint64 sourceChainSelector, uint64 nonce, address sender); - event SkippedSenderWithPreviousRampMessageInflight(uint64 sourceChainSelector, uint64 nonce, address sender); /// @dev RMN depends on this event, if changing, please notify the RMN maintainers. event ExecutionStateChanged( uint64 indexed sourceChainSelector, @@ -87,26 +84,21 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 uint64 chainSelector; // ───╮ Destination chainSelector address rmnProxy; // ───────╯ RMN proxy address address tokenAdminRegistry; // Token admin registry address + address nonceManager; // Address of the nonce manager } /// @notice Per-chain source config (defining a lane from a Source Chain -> Dest OffRamp) struct SourceChainConfig { - bool isEnabled; // ─────────╮ Flag whether the source chain is enabled or not - uint64 minSeqNr; // | The min sequence number expected for future messages - address prevOffRamp; // ────╯ Address of previous-version per-lane OffRamp. Used to be able to provide sequencing continuity during a zero downtime upgrade. - address onRamp; // OnRamp address on the source chain - /// @dev Ensures that 2 identical messages sent to 2 different lanes will have a distinct hash. - /// Must match the metadataHash used in computing leaf hashes offchain for the root committed in - /// the commitStore and i_metadataHash in the onRamp. - bytes32 metadataHash; // Source-chain specific message hash preimage to ensure global uniqueness + bool isEnabled; // ──────────╮ Flag whether the source chain is enabled or not + uint64 minSeqNr; // ─────────╯ The min sequence number expected for future messages + bytes onRamp; // OnRamp address on the source chain } /// @notice SourceChainConfig update args scoped to one source chain struct SourceChainConfigArgs { uint64 sourceChainSelector; // ───╮ Source chain selector of the config to update - bool isEnabled; // │ Flag whether the source chain is enabled or not - address prevOffRamp; // ───────────╯ Address of previous-version per-lane OffRamp. Used to be able to provide sequencing continuity during a zero downtime upgrade. - address onRamp; // OnRamp address on the source chain + bool isEnabled; // ────────────────╯ Flag whether the source chain is enabled or not + bytes onRamp; // OnRamp address on the source chain } /// @notice Dynamic offRamp config @@ -155,6 +147,8 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 address internal immutable i_rmnProxy; /// @dev The address of the token admin registry address internal immutable i_tokenAdminRegistry; + /// @dev The address of the nonce manager + address internal immutable i_nonceManager; // DYNAMIC CONFIG DynamicConfig internal s_dynamicConfig; @@ -164,11 +158,6 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 mapping(uint64 sourceChainSelector => SourceChainConfig sourceChainConfig) internal s_sourceChainConfigs; // STATE - /// @dev The expected nonce for a given sender per source chain. - /// Corresponds to s_senderNonce in the OnRamp for a lane, used to enforce that messages are - /// executed in the same order they are sent (assuming they are DON). Note that re-execution - /// of FAILED messages however, can be out of order. - mapping(uint64 sourceChainSelector => mapping(address sender => uint64 nonce)) internal s_senderNonce; /// @dev A mapping of sequence numbers (per source chain) to execution state using a bitmap with each execution /// state only taking up 2 bits of the uint256, packing 128 states into a single slot. /// Message state is tracked to ensure message can only be executed successfully once. @@ -181,7 +170,10 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 uint64 private s_latestPriceSequenceNumber; constructor(StaticConfig memory staticConfig, SourceChainConfigArgs[] memory sourceChainConfigs) MultiOCR3Base() { - if (staticConfig.rmnProxy == address(0) || staticConfig.tokenAdminRegistry == address(0)) { + if ( + staticConfig.rmnProxy == address(0) || staticConfig.tokenAdminRegistry == address(0) + || staticConfig.nonceManager == address(0) + ) { revert ZeroAddressNotAllowed(); } @@ -192,6 +184,7 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 i_chainSelector = staticConfig.chainSelector; i_rmnProxy = staticConfig.rmnProxy; i_tokenAdminRegistry = staticConfig.tokenAdminRegistry; + i_nonceManager = staticConfig.nonceManager; emit StaticConfigSet(staticConfig); _applySourceChainConfigUpdates(sourceChainConfigs); @@ -258,35 +251,6 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 return s_executionStates[sourceChainSelector][sequenceNumber / 128]; } - /// @inheritdoc IAny2EVMMultiOffRamp - function getSenderNonce(uint64 sourceChainSelector, address sender) external view returns (uint64) { - (uint64 nonce,) = _getSenderNonce(sourceChainSelector, sender); - return nonce; - } - - /// @notice Returns the the current nonce for a receiver. - /// @param sourceChainSelector The source chain to retrieve the nonce for - /// @param sender The sender address - /// @return nonce The nonce value belonging to the sender address. - /// @return isFromPrevRamp True if the nonce was retrieved from the prevOffRamps - function _getSenderNonce( - uint64 sourceChainSelector, - address sender - ) internal view returns (uint64 nonce, bool isFromPrevRamp) { - uint64 senderNonce = s_senderNonce[sourceChainSelector][sender]; - - if (senderNonce == 0) { - address prevOffRamp = s_sourceChainConfigs[sourceChainSelector].prevOffRamp; - if (prevOffRamp != address(0)) { - // If OffRamp was upgraded, check if sender has a nonce from the previous OffRamp. - // NOTE: assuming prevOffRamp is always a lane-specific off ramp - return (IAny2EVMOffRamp(prevOffRamp).getSenderNonce(sender), true); - } - } - - return (senderNonce, false); - } - /// @notice Manually executes a set of reports. /// @param reports Internal.ExecutionReportSingleChain[] - list of reports to execute /// @param gasLimitOverrides New gasLimit for each message per report @@ -377,14 +341,19 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 bytes32[] memory hashedLeaves = new bytes32[](numMsgs); for (uint256 i = 0; i < numMsgs; ++i) { - Internal.EVM2EVMMessage memory message = report.messages[i]; + Internal.Any2EVMRampMessage memory message = report.messages[i]; + + // Commits do not verify the destChainSelector in the message, since only the root is committed, + // so we have to check it explicitly + if (message.header.destChainSelector != i_chainSelector) { + revert InvalidMessageDestChainSelector(message.header.destChainSelector); + } + // We do this hash here instead of in _verifyMessages to avoid two separate loops - // over the same data, which increases gas cost - hashedLeaves[i] = Internal._hash(message, sourceChainConfig.metadataHash); - // For EVM2EVM offramps, the messageID is the leaf hash. - // Asserting that this is true ensures we don't accidentally commit and then execute - // a message with an unexpected hash. - if (hashedLeaves[i] != message.messageId) revert InvalidMessageId(message.messageId); + // over the same data, which increases gas cost. + // Hashing all of the message fields ensures that the message being executed is correct and not tampered with. + // Including the known OnRamp ensures that the message originates from the correct on ramp version + hashedLeaves[i] = Internal._hash(message, sourceChainConfig.onRamp); } // SECURITY CRITICAL CHECK @@ -395,14 +364,15 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 // Execute messages bool manualExecution = manualExecGasLimits.length != 0; for (uint256 i = 0; i < numMsgs; ++i) { - Internal.EVM2EVMMessage memory message = report.messages[i]; + Internal.Any2EVMRampMessage memory message = report.messages[i]; - Internal.MessageExecutionState originalState = getExecutionState(sourceChainSelector, message.sequenceNumber); + Internal.MessageExecutionState originalState = + getExecutionState(sourceChainSelector, message.header.sequenceNumber); if (originalState == Internal.MessageExecutionState.SUCCESS) { // If the message has already been executed, we skip it. We want to not revert on race conditions between // executing parties. This will allow us to open up manual exec while also attempting with the DON, without // reverting an entire DON batch when a user manually executes while the tx is inflight. - emit SkippedAlreadyExecutedMessage(sourceChainSelector, message.sequenceNumber); + emit SkippedAlreadyExecutedMessage(sourceChainSelector, message.header.sequenceNumber); continue; } // Two valid cases here, we either have never touched this message before, or we tried to execute @@ -413,7 +383,7 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 originalState == Internal.MessageExecutionState.UNTOUCHED || originalState == Internal.MessageExecutionState.FAILURE ) - ) revert AlreadyExecuted(sourceChainSelector, message.sequenceNumber); + ) revert AlreadyExecuted(sourceChainSelector, message.header.sequenceNumber); if (manualExecution) { bool isOldCommitReport = @@ -432,51 +402,33 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 // DON can only execute a message once // Acceptable state transitions: UNTOUCHED->SUCCESS, UNTOUCHED->FAILURE if (originalState != Internal.MessageExecutionState.UNTOUCHED) { - revert AlreadyAttempted(sourceChainSelector, message.sequenceNumber); + revert AlreadyAttempted(sourceChainSelector, message.header.sequenceNumber); } } - if (message.nonce > 0) { - // In the scenario where we upgrade offRamps, we still want to have sequential nonces. - // Referencing the old offRamp to check the expected nonce if none is set for a - // given sender allows us to skip the current message if it would not be the next according - // to the old offRamp. This preserves sequencing between updates. - (uint64 prevNonce, bool isFromPrevRamp) = _getSenderNonce(sourceChainSelector, message.sender); - if (isFromPrevRamp) { - if (prevNonce + 1 != message.nonce) { - // the starting v2 onramp nonce, i.e. the 1st message nonce v2 offramp is expected to receive, - // is guaranteed to equal (largest v1 onramp nonce + 1). - // if this message's nonce isn't (v1 offramp nonce + 1), then v1 offramp nonce != largest v1 onramp nonce, - // it tells us there are still messages inflight for v1 offramp - emit SkippedSenderWithPreviousRampMessageInflight(sourceChainSelector, message.nonce, message.sender); - continue; - } - // Otherwise this nonce is indeed the "transitional nonce", that is - // all messages sent to v1 ramp have been executed by the DON and the sequence can resume in V2. - // Note if first time user in V2, then prevNonce will be 0, and message.nonce = 1, so this will be a no-op. - s_senderNonce[sourceChainSelector][message.sender] = prevNonce; - } - - // UNTOUCHED messages MUST be executed in order always IF message.nonce > 0. - if (originalState == Internal.MessageExecutionState.UNTOUCHED) { - if (prevNonce + 1 != message.nonce) { - // We skip the message if the nonce is incorrect, since message.nonce > 0. - emit SkippedIncorrectNonce(sourceChainSelector, message.nonce, message.sender); - continue; - } - } + // Nonce changes per state transition (these only apply for ordered messages): + // UNTOUCHED -> FAILURE nonce bump + // UNTOUCHED -> SUCCESS nonce bump + // FAILURE -> FAILURE no nonce bump + // FAILURE -> SUCCESS no nonce bump + // UNTOUCHED messages MUST be executed in order always + if (message.header.nonce > 0 && originalState == Internal.MessageExecutionState.UNTOUCHED) { + // If a nonce is not incremented, that means it was skipped, and we can ignore the message + if ( + !INonceManager(i_nonceManager).incrementInboundNonce(sourceChainSelector, message.header.nonce, message.sender) + ) continue; } // Although we expect only valid messages will be committed, we check again // when executing as a defense in depth measure. bytes[] memory offchainTokenData = report.offchainTokenData[i]; if (message.tokenAmounts.length != offchainTokenData.length) { - revert TokenDataMismatch(sourceChainSelector, message.sequenceNumber); + revert TokenDataMismatch(sourceChainSelector, message.header.sequenceNumber); } - _setExecutionState(sourceChainSelector, message.sequenceNumber, Internal.MessageExecutionState.IN_PROGRESS); + _setExecutionState(sourceChainSelector, message.header.sequenceNumber, Internal.MessageExecutionState.IN_PROGRESS); (Internal.MessageExecutionState newState, bytes memory returnData) = _trialExecute(message, offchainTokenData); - _setExecutionState(sourceChainSelector, message.sequenceNumber, newState); + _setExecutionState(sourceChainSelector, message.header.sequenceNumber, newState); // Since it's hard to estimate whether manual execution will succeed, we // revert the entire transaction if it fails. This will show the user if @@ -487,36 +439,28 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 ) { // If manual execution fails, we revert the entire transaction, unless the originalState is UNTOUCHED as we // would still be making progress by changing the state from UNTOUCHED to FAILURE. - revert ExecutionError(message.messageId, returnData); + revert ExecutionError(message.header.messageId, returnData); } // The only valid prior states are UNTOUCHED and FAILURE (checked above) // The only valid post states are FAILURE and SUCCESS (checked below) if (newState != Internal.MessageExecutionState.FAILURE && newState != Internal.MessageExecutionState.SUCCESS) { - revert InvalidNewState(sourceChainSelector, message.sequenceNumber, newState); + revert InvalidNewState(sourceChainSelector, message.header.sequenceNumber, newState); } - // Nonce changes per state transition. - // These only apply for ordered messages. - // UNTOUCHED -> FAILURE nonce bump - // UNTOUCHED -> SUCCESS nonce bump - // FAILURE -> FAILURE no nonce bump - // FAILURE -> SUCCESS no nonce bump - if (message.nonce > 0 && originalState == Internal.MessageExecutionState.UNTOUCHED) { - s_senderNonce[sourceChainSelector][message.sender]++; - } - - emit ExecutionStateChanged(sourceChainSelector, message.sequenceNumber, message.messageId, newState, returnData); + emit ExecutionStateChanged( + sourceChainSelector, message.header.sequenceNumber, message.header.messageId, newState, returnData + ); } } /// @notice Try executing a message. - /// @param message Internal.EVM2EVMMessage memory message. + /// @param message Internal.Any2EVMRampMessage memory message. /// @param offchainTokenData Data provided by the DON for token transfers. /// @return the new state of the message, being either SUCCESS or FAILURE. /// @return revert data in bytes if CCIP receiver reverted during execution. function _trialExecute( - Internal.EVM2EVMMessage memory message, + Internal.Any2EVMRampMessage memory message, bytes[] memory offchainTokenData ) internal returns (Internal.MessageExecutionState, bytes memory) { try this.executeSingleMessage(message, offchainTokenData) {} @@ -534,7 +478,7 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 return (Internal.MessageExecutionState.FAILURE, err); } else { // If revert is not caused by CCIP receiver, it is unexpected, bubble up the revert. - revert ExecutionError(message.messageId, err); + revert ExecutionError(message.header.messageId, err); } } // If message execution succeeded, no CCIP receiver return data is expected, return with empty bytes. @@ -548,21 +492,27 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 /// its execution and enforce atomicity among successful message processing and token transfer. /// @dev We use ERC-165 to check for the ccipReceive interface to permit sending tokens to contracts /// (for example smart contract wallets) without an associated message. - function executeSingleMessage(Internal.EVM2EVMMessage memory message, bytes[] memory offchainTokenData) external { + function executeSingleMessage(Internal.Any2EVMRampMessage memory message, bytes[] memory offchainTokenData) external { if (msg.sender != address(this)) revert CanOnlySelfCall(); Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](0); if (message.tokenAmounts.length > 0) { destTokenAmounts = _releaseOrMintTokens( message.tokenAmounts, - abi.encode(message.sender), + message.sender, message.receiver, - message.sourceChainSelector, + message.header.sourceChainSelector, message.sourceTokenData, offchainTokenData ); } - Client.Any2EVMMessage memory any2EvmMessage = Internal._toAny2EVMMessage(message, destTokenAmounts); + Client.Any2EVMMessage memory any2EvmMessage = Client.Any2EVMMessage({ + messageId: message.header.messageId, + sourceChainSelector: message.header.sourceChainSelector, + sender: abi.encode(message.sender), + data: message.data, + destTokenAmounts: destTokenAmounts + }); address messageValidator = s_dynamicConfig.messageValidator; if (messageValidator != address(0)) { @@ -593,11 +543,6 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 if (!success) revert ReceiverError(returnData); } - /// @notice creates a unique hash to be used in message hashing. - function _metadataHash(uint64 sourceChainSelector, address onRamp, bytes32 prefix) internal view returns (bytes32) { - return keccak256(abi.encode(prefix, sourceChainSelector, i_chainSelector, onRamp)); - } - // ================================================================ // │ Commit │ // ================================================================ @@ -748,8 +693,12 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 /// @dev This function will always return the same struct as the contents is static and can never change. /// RMN depends on this function, if changing, please notify the RMN maintainers. function getStaticConfig() external view returns (StaticConfig memory) { - return - StaticConfig({chainSelector: i_chainSelector, rmnProxy: i_rmnProxy, tokenAdminRegistry: i_tokenAdminRegistry}); + return StaticConfig({ + chainSelector: i_chainSelector, + rmnProxy: i_rmnProxy, + tokenAdminRegistry: i_tokenAdminRegistry, + nonceManager: i_nonceManager + }); } /// @notice Returns the current dynamic config. @@ -782,24 +731,20 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 revert ZeroChainSelectorNotAllowed(); } - if (sourceConfigUpdate.onRamp == address(0)) { - revert ZeroAddressNotAllowed(); - } - SourceChainConfig storage currentConfig = s_sourceChainConfigs[sourceChainSelector]; + bytes memory currentOnRamp = currentConfig.onRamp; + bytes memory newOnRamp = sourceConfigUpdate.onRamp; // OnRamp can never be zero - if it is, then the source chain has been added for the first time - if (currentConfig.onRamp == address(0)) { - currentConfig.metadataHash = - _metadataHash(sourceChainSelector, sourceConfigUpdate.onRamp, Internal.EVM_2_EVM_MESSAGE_HASH); - currentConfig.onRamp = sourceConfigUpdate.onRamp; - currentConfig.prevOffRamp = sourceConfigUpdate.prevOffRamp; - currentConfig.minSeqNr = 1; + if (currentOnRamp.length == 0) { + if (newOnRamp.length == 0) { + revert ZeroAddressNotAllowed(); + } + currentConfig.onRamp = newOnRamp; + currentConfig.minSeqNr = 1; emit SourceChainSelectorAdded(sourceChainSelector); - } else if ( - currentConfig.onRamp != sourceConfigUpdate.onRamp || currentConfig.prevOffRamp != sourceConfigUpdate.prevOffRamp - ) { + } else if (keccak256(currentOnRamp) != keccak256(newOnRamp)) { revert InvalidStaticConfig(sourceChainSelector); } @@ -845,9 +790,11 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 /// @param sourceAmount The amount of tokens to be released/minted. /// @param originalSender The message sender on the source chain. /// @param receiver The address that will receive the tokens. + /// @param sourceChainSelector The remote source chain selector /// @param sourceTokenData A struct containing the local token address, the source pool address and optional data /// returned from the source pool. /// @param offchainTokenData Data fetched offchain by the DON. + /// @return destTokenAmount local token address with amount function _releaseOrMintSingleToken( uint256 sourceAmount, bytes memory originalSender, @@ -920,8 +867,12 @@ contract EVM2EVMMultiOffRamp is IAny2EVMMultiOffRamp, ITypeAndVersion, MultiOCR3 /// @notice Uses pools to release or mint a number of different tokens to a receiver address. /// @param sourceTokenAmounts List of tokens and amount values to be released/minted. - /// @param encodedSourceTokenData Array of token data returned by token pools on the source chain. + /// @param originalSender The message sender on the source chain. + /// @param receiver The address that will receive the tokens. + /// @param sourceChainSelector The remote source chain selector + /// @param encodedSourceTokenData Encoded source token data, decoding to Internal.SourceTokenData /// @param offchainTokenData Array of token data fetched offchain by the DON. + /// @return destTokenAmounts local token addresses with amounts /// @dev This function wrappes the token pool call in a try catch block to gracefully handle /// any non-rate limiting errors that may occur. If we encounter a rate limiting related error /// we bubble it up. If we encounter a non-rate limiting error we wrap it in a TokenHandlingError. diff --git a/contracts/src/v0.8/ccip/onRamp/EVM2EVMMultiOnRamp.sol b/contracts/src/v0.8/ccip/onRamp/EVM2EVMMultiOnRamp.sol index df3ba45b48..130309a9a6 100644 --- a/contracts/src/v0.8/ccip/onRamp/EVM2EVMMultiOnRamp.sol +++ b/contracts/src/v0.8/ccip/onRamp/EVM2EVMMultiOnRamp.sol @@ -54,7 +54,7 @@ contract EVM2EVMMultiOnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, OwnerIsCre event TokenTransferFeeConfigUpdated( uint64 indexed destChainSelector, address indexed token, TokenTransferFeeConfig tokenTransferFeeConfig ); - event TokenTransferFeeConfigDeleted(uint256 indexed destChainSelector, address indexed token); + event TokenTransferFeeConfigDeleted(uint64 indexed destChainSelector, address indexed token); /// RMN depends on this event, if changing, please notify the RMN maintainers. event CCIPSendRequested(uint64 indexed destChainSelector, Internal.EVM2EVMMessage message); event DestChainAdded(uint64 indexed destChainSelector, DestChainConfig destChainConfig); diff --git a/contracts/src/v0.8/ccip/test/NonceManager.t.sol b/contracts/src/v0.8/ccip/test/NonceManager.t.sol index 7c59e82272..b780f4cfad 100644 --- a/contracts/src/v0.8/ccip/test/NonceManager.t.sol +++ b/contracts/src/v0.8/ccip/test/NonceManager.t.sol @@ -2,19 +2,33 @@ pragma solidity 0.8.24; import {NonceManager} from "../NonceManager.sol"; +import {ICommitStore} from "../interfaces/ICommitStore.sol"; import {Client} from "../libraries/Client.sol"; import {Internal} from "../libraries/Internal.sol"; import {Pool} from "../libraries/Pool.sol"; import {RateLimiter} from "../libraries/RateLimiter.sol"; +import {EVM2EVMMultiOffRamp} from "../offRamp/EVM2EVMMultiOffRamp.sol"; import {EVM2EVMMultiOnRamp} from "../onRamp/EVM2EVMMultiOnRamp.sol"; import {EVM2EVMOnRamp} from "../onRamp/EVM2EVMOnRamp.sol"; + +import {BaseTest} from "./BaseTest.t.sol"; import {EVM2EVMMultiOnRampHelper} from "./helpers/EVM2EVMMultiOnRampHelper.sol"; +import {EVM2EVMOffRampHelper} from "./helpers/EVM2EVMOffRampHelper.sol"; import {EVM2EVMOnRampHelper} from "./helpers/EVM2EVMOnRampHelper.sol"; +import {MockCommitStore} from "./mocks/MockCommitStore.sol"; +import {EVM2EVMMultiOffRampSetup} from "./offRamp/EVM2EVMMultiOffRampSetup.t.sol"; import {EVM2EVMMultiOnRampSetup} from "./onRamp/EVM2EVMMultiOnRampSetup.t.sol"; -contract NonceManagerTest_getIncrementedOutboundNonce is EVM2EVMMultiOnRampSetup { +contract NonceManager_NonceIncrementation is BaseTest { + NonceManager private s_nonceManager; + + function setUp() public override { + address[] memory authorizedCallers = new address[](1); + authorizedCallers[0] = address(this); + s_nonceManager = new NonceManager(authorizedCallers); + } + function test_getIncrementedOutboundNonce_Success() public { - vm.startPrank(address(s_onRamp)); address sender = address(this); assertEq(s_nonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, sender), 0); @@ -22,59 +36,136 @@ contract NonceManagerTest_getIncrementedOutboundNonce is EVM2EVMMultiOnRampSetup uint64 outboundNonce = s_nonceManager.getIncrementedOutboundNonce(DEST_CHAIN_SELECTOR, sender); assertEq(outboundNonce, 1); } + + function test_incrementInboundNonce_Success() public { + address sender = address(this); + + s_nonceManager.incrementInboundNonce(SOURCE_CHAIN_SELECTOR, 1, abi.encode(sender)); + + assertEq(s_nonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR, abi.encode(sender)), 1); + } + + function test_incrementInboundNonce_Skip() public { + address sender = address(this); + uint64 expectedNonce = 2; + + vm.expectEmit(); + emit NonceManager.SkippedIncorrectNonce(SOURCE_CHAIN_SELECTOR, expectedNonce, abi.encode(sender)); + + s_nonceManager.incrementInboundNonce(SOURCE_CHAIN_SELECTOR, expectedNonce, abi.encode(sender)); + + assertEq(s_nonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR, abi.encode(sender)), 0); + } + + function test_incrementNoncesInboundAndOutbound_Success() public { + address sender = address(this); + + assertEq(s_nonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, sender), 0); + uint64 outboundNonce = s_nonceManager.getIncrementedOutboundNonce(DEST_CHAIN_SELECTOR, sender); + assertEq(outboundNonce, 1); + + // Inbound nonce unchanged + assertEq(s_nonceManager.getInboundNonce(DEST_CHAIN_SELECTOR, abi.encode(sender)), 0); + + s_nonceManager.incrementInboundNonce(DEST_CHAIN_SELECTOR, 1, abi.encode(sender)); + assertEq(s_nonceManager.getInboundNonce(DEST_CHAIN_SELECTOR, abi.encode(sender)), 1); + + // Outbound nonce unchanged + assertEq(s_nonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, sender), 1); + } } contract NonceManager_applyPreviousRampsUpdates is EVM2EVMMultiOnRampSetup { function test_SingleRampUpdate() public { - address prevOnRamp = vm.addr(1); + address prevOnRamp = makeAddr("prevOnRamp"); + address prevOffRamp = makeAddr("prevOffRamp"); NonceManager.PreviousRampsArgs[] memory previousRamps = new NonceManager.PreviousRampsArgs[](1); - previousRamps[0] = NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(prevOnRamp)); + previousRamps[0] = + NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(prevOnRamp, prevOffRamp)); vm.expectEmit(); - emit NonceManager.PreviousOnRampUpdated(DEST_CHAIN_SELECTOR, prevOnRamp); + emit NonceManager.PreviousRampsUpdated(DEST_CHAIN_SELECTOR, previousRamps[0].prevRamps); - s_nonceManager.applyPreviousRampsUpdates(previousRamps); + s_outboundNonceManager.applyPreviousRampsUpdates(previousRamps); - _assertPreviousRampsEqual(s_nonceManager.getPreviousRamps(DEST_CHAIN_SELECTOR), previousRamps[0].prevRamps); + _assertPreviousRampsEqual(s_outboundNonceManager.getPreviousRamps(DEST_CHAIN_SELECTOR), previousRamps[0].prevRamps); } function test_MultipleRampsUpdates() public { - address prevOnRamp1 = vm.addr(1); - address prevOnRamp2 = vm.addr(2); + address prevOnRamp1 = makeAddr("prevOnRamp1"); + address prevOnRamp2 = makeAddr("prevOnRamp2"); + address prevOffRamp1 = makeAddr("prevOffRamp1"); + address prevOffRamp2 = makeAddr("prevOffRamp2"); NonceManager.PreviousRampsArgs[] memory previousRamps = new NonceManager.PreviousRampsArgs[](2); - previousRamps[0] = NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(prevOnRamp1)); - previousRamps[1] = NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR + 1, NonceManager.PreviousRamps(prevOnRamp2)); + previousRamps[0] = + NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(prevOnRamp1, prevOffRamp1)); + previousRamps[1] = + NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR + 1, NonceManager.PreviousRamps(prevOnRamp2, prevOffRamp2)); vm.expectEmit(); - emit NonceManager.PreviousOnRampUpdated(DEST_CHAIN_SELECTOR, prevOnRamp1); + emit NonceManager.PreviousRampsUpdated(DEST_CHAIN_SELECTOR, previousRamps[0].prevRamps); vm.expectEmit(); - emit NonceManager.PreviousOnRampUpdated(DEST_CHAIN_SELECTOR + 1, prevOnRamp2); + emit NonceManager.PreviousRampsUpdated(DEST_CHAIN_SELECTOR + 1, previousRamps[1].prevRamps); - s_nonceManager.applyPreviousRampsUpdates(previousRamps); + s_outboundNonceManager.applyPreviousRampsUpdates(previousRamps); - _assertPreviousRampsEqual(s_nonceManager.getPreviousRamps(DEST_CHAIN_SELECTOR), previousRamps[0].prevRamps); - _assertPreviousRampsEqual(s_nonceManager.getPreviousRamps(DEST_CHAIN_SELECTOR + 1), previousRamps[1].prevRamps); + _assertPreviousRampsEqual(s_outboundNonceManager.getPreviousRamps(DEST_CHAIN_SELECTOR), previousRamps[0].prevRamps); + _assertPreviousRampsEqual( + s_outboundNonceManager.getPreviousRamps(DEST_CHAIN_SELECTOR + 1), previousRamps[1].prevRamps + ); } function test_ZeroInput() public { vm.recordLogs(); - s_nonceManager.applyPreviousRampsUpdates(new NonceManager.PreviousRampsArgs[](0)); + s_outboundNonceManager.applyPreviousRampsUpdates(new NonceManager.PreviousRampsArgs[](0)); assertEq(vm.getRecordedLogs().length, 0); } function test_PreviousRampAlreadySetOnRamp_Revert() public { NonceManager.PreviousRampsArgs[] memory previousRamps = new NonceManager.PreviousRampsArgs[](1); + address prevOnRamp = makeAddr("prevOnRamp"); previousRamps[0] = - NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(address(vm.addr(1)))); + NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(prevOnRamp, address(0))); - s_nonceManager.applyPreviousRampsUpdates(previousRamps); + s_outboundNonceManager.applyPreviousRampsUpdates(previousRamps); previousRamps[0] = - NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(address(vm.addr(2)))); + NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(prevOnRamp, address(0))); vm.expectRevert(NonceManager.PreviousRampAlreadySet.selector); - s_nonceManager.applyPreviousRampsUpdates(previousRamps); + s_outboundNonceManager.applyPreviousRampsUpdates(previousRamps); + } + + function test_PreviousRampAlreadySetOffRamp_Revert() public { + NonceManager.PreviousRampsArgs[] memory previousRamps = new NonceManager.PreviousRampsArgs[](1); + address prevOffRamp = makeAddr("prevOffRamp"); + previousRamps[0] = + NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(address(0), prevOffRamp)); + + s_outboundNonceManager.applyPreviousRampsUpdates(previousRamps); + + previousRamps[0] = + NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(address(0), prevOffRamp)); + + vm.expectRevert(NonceManager.PreviousRampAlreadySet.selector); + s_outboundNonceManager.applyPreviousRampsUpdates(previousRamps); + } + + function test_PreviousRampAlreadySetOnRampAndOffRamp_Revert() public { + NonceManager.PreviousRampsArgs[] memory previousRamps = new NonceManager.PreviousRampsArgs[](1); + address prevOnRamp = makeAddr("prevOnRamp"); + address prevOffRamp = makeAddr("prevOffRamp"); + previousRamps[0] = + NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(prevOnRamp, prevOffRamp)); + + s_outboundNonceManager.applyPreviousRampsUpdates(previousRamps); + + previousRamps[0] = + NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(prevOnRamp, prevOffRamp)); + + vm.expectRevert(NonceManager.PreviousRampAlreadySet.selector); + s_outboundNonceManager.applyPreviousRampsUpdates(previousRamps); } function _assertPreviousRampsEqual( @@ -82,15 +173,16 @@ contract NonceManager_applyPreviousRampsUpdates is EVM2EVMMultiOnRampSetup { NonceManager.PreviousRamps memory b ) internal pure { assertEq(a.prevOnRamp, b.prevOnRamp); + assertEq(a.prevOffRamp, b.prevOffRamp); } } -contract NonceManager_onRampUpgrade is EVM2EVMMultiOnRampSetup { +contract NonceManager_OnRampUpgrade is EVM2EVMMultiOnRampSetup { uint256 internal constant FEE_AMOUNT = 1234567890; EVM2EVMOnRampHelper internal s_prevOnRamp; function setUp() public virtual override { - EVM2EVMMultiOnRampSetup.setUp(); + super.setUp(); EVM2EVMOnRamp.FeeTokenConfigArgs[] memory feeTokenConfigArgs = new EVM2EVMOnRamp.FeeTokenConfigArgs[](1); feeTokenConfigArgs[0] = EVM2EVMOnRamp.FeeTokenConfigArgs({ @@ -149,14 +241,14 @@ contract NonceManager_onRampUpgrade is EVM2EVMMultiOnRampSetup { NonceManager.PreviousRampsArgs[] memory previousRamps = new NonceManager.PreviousRampsArgs[](1); previousRamps[0] = - NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(address(s_prevOnRamp))); - s_nonceManager.applyPreviousRampsUpdates(previousRamps); + NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(address(s_prevOnRamp), address(0))); + s_outboundNonceManager.applyPreviousRampsUpdates(previousRamps); EVM2EVMMultiOnRamp.DestChainConfigArgs[] memory destChainConfigArgs = _generateDestChainConfigArgs(); destChainConfigArgs[0].prevOnRamp = address(s_prevOnRamp); (s_onRamp, s_metadataHash) = _deployOnRamp( - SOURCE_CHAIN_SELECTOR, address(s_sourceRouter), address(s_nonceManager), address(s_tokenAdminRegistry) + SOURCE_CHAIN_SELECTOR, address(s_sourceRouter), address(s_outboundNonceManager), address(s_tokenAdminRegistry) ); vm.startPrank(address(s_sourceRouter)); @@ -172,24 +264,24 @@ contract NonceManager_onRampUpgrade is EVM2EVMMultiOnRampSetup { function test_UpgradeSenderNoncesReadsPreviousRamp_Success() public { Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - uint64 startNonce = s_nonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); + uint64 startNonce = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); for (uint64 i = 1; i < 4; ++i) { s_prevOnRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - assertEq(startNonce + i, s_nonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER)); + assertEq(startNonce + i, s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER)); } } function test_UpgradeNonceStartsAtV1Nonce_Success() public { Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - uint64 startNonce = s_nonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); + uint64 startNonce = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); // send 1 message from previous onramp s_prevOnRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, FEE_AMOUNT, OWNER); - assertEq(startNonce + 1, s_nonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER)); + assertEq(startNonce + 1, s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER)); // new onramp nonce should start from 2, while sequence number start from 1 vm.expectEmit(); @@ -198,7 +290,7 @@ contract NonceManager_onRampUpgrade is EVM2EVMMultiOnRampSetup { ); s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, FEE_AMOUNT, OWNER); - assertEq(startNonce + 2, s_nonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER)); + assertEq(startNonce + 2, s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER)); // after another send, nonce should be 3, and sequence number be 2 vm.expectEmit(); @@ -207,7 +299,7 @@ contract NonceManager_onRampUpgrade is EVM2EVMMultiOnRampSetup { ); s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, FEE_AMOUNT, OWNER); - assertEq(startNonce + 3, s_nonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER)); + assertEq(startNonce + 3, s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER)); } function test_UpgradeNonceNewSenderStartsAtZero_Success() public { @@ -225,3 +317,347 @@ contract NonceManager_onRampUpgrade is EVM2EVMMultiOnRampSetup { s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, FEE_AMOUNT, newSender); } } + +contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { + EVM2EVMOffRampHelper internal s_prevOffRamp; + EVM2EVMOffRampHelper[] internal s_nestedPrevOffRamps; + + address internal constant SINGLE_LANE_ON_RAMP_ADDRESS_1 = abi.decode(ON_RAMP_ADDRESS_1, (address)); + address internal constant SINGLE_LANE_ON_RAMP_ADDRESS_2 = abi.decode(ON_RAMP_ADDRESS_2, (address)); + address internal constant SINGLE_LANE_ON_RAMP_ADDRESS_3 = abi.decode(ON_RAMP_ADDRESS_3, (address)); + + function setUp() public virtual override { + super.setUp(); + + ICommitStore mockPrevCommitStore = new MockCommitStore(); + s_prevOffRamp = _deploySingleLaneOffRamp( + mockPrevCommitStore, s_destRouter, address(0), SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1 + ); + + s_nestedPrevOffRamps = new EVM2EVMOffRampHelper[](2); + s_nestedPrevOffRamps[0] = _deploySingleLaneOffRamp( + mockPrevCommitStore, s_destRouter, address(0), SOURCE_CHAIN_SELECTOR_2, SINGLE_LANE_ON_RAMP_ADDRESS_2 + ); + s_nestedPrevOffRamps[1] = _deploySingleLaneOffRamp( + mockPrevCommitStore, + s_destRouter, + address(s_nestedPrevOffRamps[0]), + SOURCE_CHAIN_SELECTOR_2, + SINGLE_LANE_ON_RAMP_ADDRESS_2 + ); + + NonceManager.PreviousRampsArgs[] memory previousRamps = new NonceManager.PreviousRampsArgs[](3); + previousRamps[0] = NonceManager.PreviousRampsArgs( + SOURCE_CHAIN_SELECTOR_1, NonceManager.PreviousRamps(address(0), address(s_prevOffRamp)) + ); + previousRamps[1] = NonceManager.PreviousRampsArgs( + SOURCE_CHAIN_SELECTOR_2, NonceManager.PreviousRamps(address(0), address(s_nestedPrevOffRamps[1])) + ); + previousRamps[2] = NonceManager.PreviousRampsArgs( + SOURCE_CHAIN_SELECTOR_3, NonceManager.PreviousRamps(SINGLE_LANE_ON_RAMP_ADDRESS_3, address(0)) + ); + s_inboundNonceManager.applyPreviousRampsUpdates(previousRamps); + + EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = + new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](3); + sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + isEnabled: true, + onRamp: ON_RAMP_ADDRESS_1 + }); + sourceChainConfigs[1] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_2, + isEnabled: true, + onRamp: ON_RAMP_ADDRESS_2 + }); + sourceChainConfigs[2] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_3, + isEnabled: true, + onRamp: ON_RAMP_ADDRESS_3 + }); + + _setupMultipleOffRampsFromConfigs(sourceChainConfigs); + + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_3, 1); + } + + function test_Upgraded_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + vm.expectEmit(); + emit EVM2EVMMultiOffRamp.ExecutionStateChanged( + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + Internal.MessageExecutionState.SUCCESS, + "" + ); + + s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); + } + + function test_NoPrevOffRampForChain_Success() public { + Internal.EVM2EVMMessage[] memory messages = + _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1); + uint64 startNonceChain3 = + s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, abi.encode(messages[0].sender)); + s_prevOffRamp.execute( + _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0) + ); + + // Nonce unchanged for chain 3 + assertEq( + startNonceChain3, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, abi.encode(messages[0].sender)) + ); + + Internal.Any2EVMRampMessage[] memory messagesChain3 = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3); + vm.expectEmit(); + emit EVM2EVMMultiOffRamp.ExecutionStateChanged( + SOURCE_CHAIN_SELECTOR_3, + messagesChain3[0].header.sequenceNumber, + messagesChain3[0].header.messageId, + Internal.MessageExecutionState.SUCCESS, + "" + ); + + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messagesChain3), new uint256[](0) + ); + assertEq( + startNonceChain3 + 1, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, messagesChain3[0].sender) + ); + } + + function test_UpgradedSenderNoncesReadsPreviousRamp_Success() public { + Internal.EVM2EVMMessage[] memory messages = + _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1); + uint64 startNonce = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)); + + for (uint64 i = 1; i < 4; ++i) { + s_prevOffRamp.execute( + _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0) + ); + + // messages contains a single message - update for the next execution + messages[0].nonce++; + messages[0].sequenceNumber++; + messages[0].messageId = Internal._hash(messages[0], s_prevOffRamp.metadataHash()); + + assertEq( + startNonce + i, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)) + ); + } + } + + function test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() public { + Internal.EVM2EVMMessage[] memory messages = + _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_2, SINGLE_LANE_ON_RAMP_ADDRESS_2); + uint64 startNonce = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_2, abi.encode(messages[0].sender)); + + for (uint64 i = 1; i < 4; ++i) { + s_nestedPrevOffRamps[0].execute( + _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_2, messages), new uint256[](0) + ); + + // messages contains a single message - update for the next execution + messages[0].nonce++; + messages[0].sequenceNumber++; + messages[0].messageId = Internal._hash(messages[0], s_nestedPrevOffRamps[0].metadataHash()); + + // Read through prev sender nonce through prevOffRamp -> prevPrevOffRamp + assertEq( + startNonce + i, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_2, abi.encode(messages[0].sender)) + ); + } + } + + function test_UpgradedNonceStartsAtV1Nonce_Success() public { + Internal.EVM2EVMMessage[] memory messages = + _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1); + + uint64 startNonce = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)); + s_prevOffRamp.execute( + _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0) + ); + + assertEq( + startNonce + 1, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)) + ); + + Internal.Any2EVMRampMessage[] memory messagesMultiRamp = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + messagesMultiRamp[0].header.nonce++; + messagesMultiRamp[0].header.messageId = Internal._hash(messagesMultiRamp[0], ON_RAMP_ADDRESS_1); + + vm.expectEmit(); + emit EVM2EVMMultiOffRamp.ExecutionStateChanged( + SOURCE_CHAIN_SELECTOR_1, + messagesMultiRamp[0].header.sequenceNumber, + messagesMultiRamp[0].header.messageId, + Internal.MessageExecutionState.SUCCESS, + "" + ); + + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp), new uint256[](0) + ); + assertEq( + startNonce + 2, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp[0].sender) + ); + + messagesMultiRamp[0].header.nonce++; + messagesMultiRamp[0].header.sequenceNumber++; + messagesMultiRamp[0].header.messageId = Internal._hash(messagesMultiRamp[0], ON_RAMP_ADDRESS_1); + + vm.expectEmit(); + emit EVM2EVMMultiOffRamp.ExecutionStateChanged( + SOURCE_CHAIN_SELECTOR_1, + messagesMultiRamp[0].header.sequenceNumber, + messagesMultiRamp[0].header.messageId, + Internal.MessageExecutionState.SUCCESS, + "" + ); + + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp), new uint256[](0) + ); + assertEq( + startNonce + 3, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp[0].sender) + ); + } + + function test_UpgradedNonceNewSenderStartsAtZero_Success() public { + Internal.EVM2EVMMessage[] memory messages = + _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1); + + s_prevOffRamp.execute( + _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0) + ); + + Internal.Any2EVMRampMessage[] memory messagesMultiRamp = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + bytes memory newSender = abi.encode(address(1234567)); + messagesMultiRamp[0].sender = newSender; + messagesMultiRamp[0].header.messageId = Internal._hash(messagesMultiRamp[0], ON_RAMP_ADDRESS_1); + + vm.expectEmit(); + emit EVM2EVMMultiOffRamp.ExecutionStateChanged( + SOURCE_CHAIN_SELECTOR_1, + messagesMultiRamp[0].header.sequenceNumber, + messagesMultiRamp[0].header.messageId, + Internal.MessageExecutionState.SUCCESS, + "" + ); + + // new sender nonce in new offramp should go from 0 -> 1 + assertEq(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, newSender), 0); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp), new uint256[](0) + ); + assertEq(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, newSender), 1); + } + + function test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + address newSender = address(1234567); + messages[0].sender = abi.encode(newSender); + messages[0].header.nonce = 2; + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); + + uint64 startNonce = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); + + // new offramp sees msg nonce higher than senderNonce + // it waits for previous offramp to execute + vm.expectEmit(); + emit NonceManager.SkippedIncorrectNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].header.nonce, messages[0].sender); + s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); + assertEq(startNonce, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); + + Internal.EVM2EVMMessage[] memory messagesSingleLane = + _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1); + + messagesSingleLane[0].nonce = 1; + messagesSingleLane[0].sender = newSender; + messagesSingleLane[0].messageId = Internal._hash(messagesSingleLane[0], s_prevOffRamp.metadataHash()); + + // previous offramp executes msg and increases nonce + s_prevOffRamp.execute( + _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesSingleLane), new uint256[](0) + ); + assertEq( + startNonce + 1, + s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messagesSingleLane[0].sender)) + ); + + messages[0].header.nonce = 2; + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); + + // new offramp is able to execute + vm.expectEmit(); + emit EVM2EVMMultiOffRamp.ExecutionStateChanged( + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + Internal.MessageExecutionState.SUCCESS, + "" + ); + + s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); + assertEq(startNonce + 2, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); + } + + function _generateSingleLaneRampReportFromMessages( + uint64 sourceChainSelector, + Internal.EVM2EVMMessage[] memory messages + ) internal pure returns (Internal.ExecutionReport memory) { + bytes[][] memory offchainTokenData = new bytes[][](messages.length); + + for (uint256 i = 0; i < messages.length; ++i) { + offchainTokenData[i] = new bytes[](messages[i].tokenAmounts.length); + } + + return Internal.ExecutionReport({ + proofs: new bytes32[](0), + proofFlagBits: 2 ** 256 - 1, + messages: messages, + offchainTokenData: offchainTokenData + }); + } + + function _generateSingleLaneSingleBasicMessage( + uint64 sourceChainSelector, + address onRamp + ) internal view returns (Internal.EVM2EVMMessage[] memory) { + Internal.EVM2EVMMessage[] memory messages = new Internal.EVM2EVMMessage[](1); + + bytes memory data = abi.encode(0); + messages[0] = Internal.EVM2EVMMessage({ + sequenceNumber: 1, + sender: OWNER, + nonce: 1, + gasLimit: GAS_LIMIT, + strict: false, + sourceChainSelector: sourceChainSelector, + receiver: address(s_receiver), + data: data, + tokenAmounts: new Client.EVMTokenAmount[](0), + sourceTokenData: new bytes[](0), + feeToken: s_destFeeToken, + feeTokenAmount: uint256(0), + messageId: "" + }); + + messages[0].messageId = Internal._hash( + messages[0], + keccak256(abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, sourceChainSelector, DEST_CHAIN_SELECTOR, onRamp)) + ); + + return messages; + } +} diff --git a/contracts/src/v0.8/ccip/test/capability/CCIPConfig.t.sol b/contracts/src/v0.8/ccip/test/capability/CCIPConfig.t.sol index b23e0cae0d..4caa4394be 100644 --- a/contracts/src/v0.8/ccip/test/capability/CCIPConfig.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/CCIPConfig.t.sol @@ -5,6 +5,8 @@ import {Test} from "forge-std/Test.sol"; import {CCIPConfig} from "../../capability/CCIPConfig.sol"; import {ICapabilitiesRegistry} from "../../capability/interfaces/ICapabilitiesRegistry.sol"; +import {CCIPConfigTypes} from "../../capability/libraries/CCIPConfigTypes.sol"; +import {Internal} from "../../libraries/Internal.sol"; import {CCIPConfigHelper} from "../helpers/CCIPConfigHelper.sol"; contract CCIPConfigSetup is Test { @@ -67,10 +69,10 @@ contract CCIPConfigSetup is Test { ); } // Add chain selector for chain 1. - CCIPConfig.ChainConfigInfo[] memory adds = new CCIPConfig.ChainConfigInfo[](1); - adds[0] = CCIPConfig.ChainConfigInfo({ + CCIPConfigTypes.ChainConfigInfo[] memory adds = new CCIPConfigTypes.ChainConfigInfo[](1); + adds[0] = CCIPConfigTypes.ChainConfigInfo({ chainSelector: 1, - chainConfig: CCIPConfig.ChainConfig({readers: p2pIds, fChain: 1, config: bytes("config1")}) + chainConfig: CCIPConfigTypes.ChainConfig({readers: p2pIds, fChain: 1, config: bytes("config1")}) }); vm.expectEmit(); @@ -92,14 +94,14 @@ contract CCIPConfig_chainConfig is CCIPConfigSetup { function test_applyChainConfigUpdates_addChainConfigs_Success() public { bytes32[] memory chainReaders = new bytes32[](1); chainReaders[0] = keccak256(abi.encode(1)); - CCIPConfig.ChainConfigInfo[] memory adds = new CCIPConfig.ChainConfigInfo[](2); - adds[0] = CCIPConfig.ChainConfigInfo({ + CCIPConfigTypes.ChainConfigInfo[] memory adds = new CCIPConfigTypes.ChainConfigInfo[](2); + adds[0] = CCIPConfigTypes.ChainConfigInfo({ chainSelector: 1, - chainConfig: CCIPConfig.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config1")}) + chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config1")}) }); - adds[1] = CCIPConfig.ChainConfigInfo({ + adds[1] = CCIPConfigTypes.ChainConfigInfo({ chainSelector: 2, - chainConfig: CCIPConfig.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config2")}) + chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config2")}) }); vm.mockCall( @@ -124,7 +126,7 @@ contract CCIPConfig_chainConfig is CCIPConfigSetup { emit CCIPConfig.ChainConfigSet(2, adds[1].chainConfig); s_ccipCC.applyChainConfigUpdates(new uint64[](0), adds); - CCIPConfig.ChainConfigInfo[] memory configs = s_ccipCC.getAllChainConfigs(); + CCIPConfigTypes.ChainConfigInfo[] memory configs = s_ccipCC.getAllChainConfigs(); assertEq(configs.length, 2, "chain configs length must be 2"); assertEq(configs[0].chainSelector, 1, "chain selector must match"); assertEq(configs[1].chainSelector, 2, "chain selector must match"); @@ -133,14 +135,14 @@ contract CCIPConfig_chainConfig is CCIPConfigSetup { function test_applyChainConfigUpdates_removeChainConfigs_Success() public { bytes32[] memory chainReaders = new bytes32[](1); chainReaders[0] = keccak256(abi.encode(1)); - CCIPConfig.ChainConfigInfo[] memory adds = new CCIPConfig.ChainConfigInfo[](2); - adds[0] = CCIPConfig.ChainConfigInfo({ + CCIPConfigTypes.ChainConfigInfo[] memory adds = new CCIPConfigTypes.ChainConfigInfo[](2); + adds[0] = CCIPConfigTypes.ChainConfigInfo({ chainSelector: 1, - chainConfig: CCIPConfig.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config1")}) + chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config1")}) }); - adds[1] = CCIPConfig.ChainConfigInfo({ + adds[1] = CCIPConfigTypes.ChainConfigInfo({ chainSelector: 2, - chainConfig: CCIPConfig.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config2")}) + chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config2")}) }); vm.mockCall( @@ -170,7 +172,7 @@ contract CCIPConfig_chainConfig is CCIPConfigSetup { vm.expectEmit(); emit CCIPConfig.ChainConfigRemoved(1); - s_ccipCC.applyChainConfigUpdates(removes, new CCIPConfig.ChainConfigInfo[](0)); + s_ccipCC.applyChainConfigUpdates(removes, new CCIPConfigTypes.ChainConfigInfo[](0)); } // Reverts. @@ -180,16 +182,16 @@ contract CCIPConfig_chainConfig is CCIPConfigSetup { removes[0] = uint64(1); vm.expectRevert(abi.encodeWithSelector(CCIPConfig.ChainSelectorNotFound.selector, 1)); - s_ccipCC.applyChainConfigUpdates(removes, new CCIPConfig.ChainConfigInfo[](0)); + s_ccipCC.applyChainConfigUpdates(removes, new CCIPConfigTypes.ChainConfigInfo[](0)); } function test_applyChainConfigUpdates_nodeNotInRegistry_Reverts() public { bytes32[] memory chainReaders = new bytes32[](1); chainReaders[0] = keccak256(abi.encode(1)); - CCIPConfig.ChainConfigInfo[] memory adds = new CCIPConfig.ChainConfigInfo[](1); - adds[0] = CCIPConfig.ChainConfigInfo({ + CCIPConfigTypes.ChainConfigInfo[] memory adds = new CCIPConfigTypes.ChainConfigInfo[](1); + adds[0] = CCIPConfigTypes.ChainConfigInfo({ chainSelector: 1, - chainConfig: CCIPConfig.ChainConfig({readers: chainReaders, fChain: 1, config: abi.encode(1, 2, 3)}) + chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: abi.encode(1, 2, 3)}) }); vm.mockCall( @@ -215,14 +217,14 @@ contract CCIPConfig_chainConfig is CCIPConfigSetup { function test__applyChainConfigUpdates_FChainNotPositive_Reverts() public { bytes32[] memory chainReaders = new bytes32[](1); chainReaders[0] = keccak256(abi.encode(1)); - CCIPConfig.ChainConfigInfo[] memory adds = new CCIPConfig.ChainConfigInfo[](2); - adds[0] = CCIPConfig.ChainConfigInfo({ + CCIPConfigTypes.ChainConfigInfo[] memory adds = new CCIPConfigTypes.ChainConfigInfo[](2); + adds[0] = CCIPConfigTypes.ChainConfigInfo({ chainSelector: 1, - chainConfig: CCIPConfig.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config1")}) + chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config1")}) }); - adds[1] = CCIPConfig.ChainConfigInfo({ + adds[1] = CCIPConfigTypes.ChainConfigInfo({ chainSelector: 2, - chainConfig: CCIPConfig.ChainConfig({readers: chainReaders, fChain: 0, config: bytes("config2")}) // bad fChain + chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 0, config: bytes("config2")}) // bad fChain }); vm.mockCall( @@ -253,8 +255,8 @@ contract CCIPConfig_validateConfig is CCIPConfigSetup { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); // Config is for 4 nodes, so f == 1. - CCIPConfig.OCR3Config memory config = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -274,8 +276,8 @@ contract CCIPConfig_validateConfig is CCIPConfigSetup { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); // Config is for 4 nodes, so f == 1. - CCIPConfig.OCR3Config memory config = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 0, // invalid bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -295,8 +297,8 @@ contract CCIPConfig_validateConfig is CCIPConfigSetup { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); // Config is for 4 nodes, so f == 1. - CCIPConfig.OCR3Config memory config = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: bytes(""), // invalid chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -316,8 +318,8 @@ contract CCIPConfig_validateConfig is CCIPConfigSetup { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); // Config is for 4 nodes, so f == 1. - CCIPConfig.OCR3Config memory config = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 2, // not set bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -337,8 +339,8 @@ contract CCIPConfig_validateConfig is CCIPConfigSetup { // 32 > 31 (max num oracles) (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(32); - CCIPConfig.OCR3Config memory config = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -363,8 +365,8 @@ contract CCIPConfig_validateConfig is CCIPConfigSetup { mstore(signers, 30) } - CCIPConfig.OCR3Config memory config = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -390,8 +392,8 @@ contract CCIPConfig_validateConfig is CCIPConfigSetup { mstore(transmitters, 3) } - CCIPConfig.OCR3Config memory config = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -411,8 +413,8 @@ contract CCIPConfig_validateConfig is CCIPConfigSetup { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); // Config is for 4 nodes, so f == 1. - CCIPConfig.OCR3Config memory config = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -431,8 +433,8 @@ contract CCIPConfig_validateConfig is CCIPConfigSetup { function test__validateConfig_FTooHigh_Reverts() public { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); - CCIPConfig.OCR3Config memory config = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -456,8 +458,8 @@ contract CCIPConfig_validateConfig is CCIPConfigSetup { } // Config is for 4 nodes, so f == 1. - CCIPConfig.OCR3Config memory config = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -479,8 +481,8 @@ contract CCIPConfig_validateConfig is CCIPConfigSetup { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); // Config is for 4 nodes, so f == 1. - CCIPConfig.OCR3Config memory config = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _makeBytes32Array(5, 0), // too many bootstrap p2pIds, 5 > 4 @@ -518,8 +520,8 @@ contract CCIPConfig_validateConfig is CCIPConfigSetup { ); // Config is for 4 nodes, so f == 1. - CCIPConfig.OCR3Config memory config = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -541,24 +543,24 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { function test__stateFromConfigLength_Success() public { uint256 configLen = 0; - CCIPConfig.ConfigState state = s_ccipCC.stateFromConfigLength(configLen); - assertEq(uint256(state), uint256(CCIPConfig.ConfigState.Init)); + CCIPConfigTypes.ConfigState state = s_ccipCC.stateFromConfigLength(configLen); + assertEq(uint256(state), uint256(CCIPConfigTypes.ConfigState.Init)); configLen = 1; state = s_ccipCC.stateFromConfigLength(configLen); - assertEq(uint256(state), uint256(CCIPConfig.ConfigState.Running)); + assertEq(uint256(state), uint256(CCIPConfigTypes.ConfigState.Running)); configLen = 2; state = s_ccipCC.stateFromConfigLength(configLen); - assertEq(uint256(state), uint256(CCIPConfig.ConfigState.Staging)); + assertEq(uint256(state), uint256(CCIPConfigTypes.ConfigState.Staging)); } function test__validateConfigStateTransition_Success() public { - s_ccipCC.validateConfigStateTransition(CCIPConfig.ConfigState.Init, CCIPConfig.ConfigState.Running); + s_ccipCC.validateConfigStateTransition(CCIPConfigTypes.ConfigState.Init, CCIPConfigTypes.ConfigState.Running); - s_ccipCC.validateConfigStateTransition(CCIPConfig.ConfigState.Running, CCIPConfig.ConfigState.Staging); + s_ccipCC.validateConfigStateTransition(CCIPConfigTypes.ConfigState.Running, CCIPConfigTypes.ConfigState.Staging); - s_ccipCC.validateConfigStateTransition(CCIPConfig.ConfigState.Staging, CCIPConfig.ConfigState.Running); + s_ccipCC.validateConfigStateTransition(CCIPConfigTypes.ConfigState.Staging, CCIPConfigTypes.ConfigState.Running); } function test__computeConfigDigest_Success() public { @@ -569,8 +571,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { bytes32[] memory p2pIds = _makeBytes32Array(4, 0); bytes[] memory signers = _makeBytesArray(2, 10); bytes[] memory transmitters = _makeBytesArray(2, 20); - CCIPConfig.OCR3Config memory config = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -594,7 +596,7 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { bytes32 configDigest3 = s_ccipCC.computeConfigDigest(donId, configCount, config); configCount = 1; - config.pluginType = CCIPConfig.PluginType.Execution; + config.pluginType = Internal.OCRPluginType.Execution; bytes32 configDigest4 = s_ccipCC.computeConfigDigest(donId, configCount, config); assertNotEq(configDigest1, configDigest2, "config digests 1 and 2 must not match"); @@ -612,10 +614,10 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { bytes32[] memory p2pIds = _makeBytes32Array(4, 0); bytes[] memory signers = _makeBytesArray(4, 10); bytes[] memory transmitters = _makeBytesArray(4, 20); - CCIPConfig.OCR3Config[] memory cfgs = new CCIPConfig.OCR3Config[](numCommitCfgs + numExecCfgs); + CCIPConfigTypes.OCR3Config[] memory cfgs = new CCIPConfigTypes.OCR3Config[](numCommitCfgs + numExecCfgs); for (uint256 i = 0; i < numCommitCfgs; i++) { - cfgs[i] = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + cfgs[i] = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -628,8 +630,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { }); } for (uint256 i = 0; i < numExecCfgs; i++) { - cfgs[numCommitCfgs + i] = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Execution, + cfgs[numCommitCfgs + i] = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Execution, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -641,17 +643,17 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfig: abi.encode("exec", numCommitCfgs + i) }); } - (CCIPConfig.OCR3Config[] memory commitCfgs, CCIPConfig.OCR3Config[] memory execCfgs) = + (CCIPConfigTypes.OCR3Config[] memory commitCfgs, CCIPConfigTypes.OCR3Config[] memory execCfgs) = s_ccipCC.groupByPluginType(cfgs); assertEq(commitCfgs.length, numCommitCfgs, "commitCfgs length must match"); assertEq(execCfgs.length, numExecCfgs, "execCfgs length must match"); for (uint256 i = 0; i < commitCfgs.length; i++) { - assertEq(uint8(commitCfgs[i].pluginType), uint8(CCIPConfig.PluginType.Commit), "plugin type must be commit"); + assertEq(uint8(commitCfgs[i].pluginType), uint8(Internal.OCRPluginType.Commit), "plugin type must be commit"); assertEq(commitCfgs[i].offchainConfig, abi.encode("commit", i), "offchain config must match"); } for (uint256 i = 0; i < execCfgs.length; i++) { - assertEq(uint8(execCfgs[i].pluginType), uint8(CCIPConfig.PluginType.Execution), "plugin type must be execution"); + assertEq(uint8(execCfgs[i].pluginType), uint8(Internal.OCRPluginType.Execution), "plugin type must be execution"); assertEq(execCfgs[i].offchainConfig, abi.encode("exec", numCommitCfgs + i), "offchain config must match"); } } @@ -659,10 +661,10 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { function test__computeNewConfigWithMeta_InitToRunning_Success() public { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); uint32 donId = 1; - CCIPConfig.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfig.OCR3ConfigWithMeta[](0); - CCIPConfig.OCR3Config[] memory newConfig = new CCIPConfig.OCR3Config[](1); - newConfig[0] = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](0); + CCIPConfigTypes.OCR3Config[] memory newConfig = new CCIPConfigTypes.OCR3Config[](1); + newConfig[0] = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -673,9 +675,9 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit") }); - CCIPConfig.ConfigState currentState = CCIPConfig.ConfigState.Init; - CCIPConfig.ConfigState newState = CCIPConfig.ConfigState.Running; - CCIPConfig.OCR3ConfigWithMeta[] memory newConfigWithMeta = + CCIPConfigTypes.ConfigState currentState = CCIPConfigTypes.ConfigState.Init; + CCIPConfigTypes.ConfigState newState = CCIPConfigTypes.ConfigState.Running; + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta = s_ccipCC.computeNewConfigWithMeta(donId, currentConfig, newConfig, currentState, newState); assertEq(newConfigWithMeta.length, 1, "new config with meta length must be 1"); assertEq(newConfigWithMeta[0].configCount, uint64(1), "config count must be 1"); @@ -694,8 +696,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { function test__computeNewConfigWithMeta_RunningToStaging_Success() public { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); uint32 donId = 1; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -706,8 +708,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit") }); - CCIPConfig.OCR3Config memory greenConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -719,23 +721,23 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfig: bytes("commit-new") }); - CCIPConfig.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfig.OCR3ConfigWithMeta[](1); - currentConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); + currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 1, config: blueConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) }); - CCIPConfig.OCR3Config[] memory newConfig = new CCIPConfig.OCR3Config[](2); + CCIPConfigTypes.OCR3Config[] memory newConfig = new CCIPConfigTypes.OCR3Config[](2); // existing blue config first. newConfig[0] = blueConfig; // green config next. newConfig[1] = greenConfig; - CCIPConfig.ConfigState currentState = CCIPConfig.ConfigState.Running; - CCIPConfig.ConfigState newState = CCIPConfig.ConfigState.Staging; + CCIPConfigTypes.ConfigState currentState = CCIPConfigTypes.ConfigState.Running; + CCIPConfigTypes.ConfigState newState = CCIPConfigTypes.ConfigState.Staging; - CCIPConfig.OCR3ConfigWithMeta[] memory newConfigWithMeta = + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta = s_ccipCC.computeNewConfigWithMeta(donId, currentConfig, newConfig, currentState, newState); assertEq(newConfigWithMeta.length, 2, "new config with meta length must be 2"); @@ -772,8 +774,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { function test__computeNewConfigWithMeta_StagingToRunning_Success() public { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); uint32 donId = 1; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -784,8 +786,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit") }); - CCIPConfig.OCR3Config memory greenConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -797,24 +799,24 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfig: bytes("commit-new") }); - CCIPConfig.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfig.OCR3ConfigWithMeta[](2); - currentConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); + currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 1, config: blueConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) }); - currentConfig[1] = CCIPConfig.OCR3ConfigWithMeta({ + currentConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 2, config: greenConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) }); - CCIPConfig.OCR3Config[] memory newConfig = new CCIPConfig.OCR3Config[](1); + CCIPConfigTypes.OCR3Config[] memory newConfig = new CCIPConfigTypes.OCR3Config[](1); newConfig[0] = greenConfig; - CCIPConfig.ConfigState currentState = CCIPConfig.ConfigState.Staging; - CCIPConfig.ConfigState newState = CCIPConfig.ConfigState.Running; + CCIPConfigTypes.ConfigState currentState = CCIPConfigTypes.ConfigState.Staging; + CCIPConfigTypes.ConfigState newState = CCIPConfigTypes.ConfigState.Running; - CCIPConfig.OCR3ConfigWithMeta[] memory newConfigWithMeta = + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta = s_ccipCC.computeNewConfigWithMeta(donId, currentConfig, newConfig, currentState, newState); assertEq(newConfigWithMeta.length, 1, "new config with meta length must be 1"); @@ -832,8 +834,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { function test__validateConfigTransition_InitToRunning_Success() public { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); uint32 donId = 1; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -845,13 +847,13 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfig: bytes("commit") }); - CCIPConfig.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfig.OCR3ConfigWithMeta[](1); - newConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); + newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 1, config: blueConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) }); - CCIPConfig.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfig.OCR3ConfigWithMeta[](0); + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](0); s_ccipCC.validateConfigTransition(currentConfig, newConfig); } @@ -859,8 +861,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { function test__validateConfigTransition_RunningToStaging_Success() public { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); uint32 donId = 1; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -871,8 +873,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit") }); - CCIPConfig.OCR3Config memory greenConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -884,20 +886,20 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfig: bytes("commit-new") }); - CCIPConfig.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfig.OCR3ConfigWithMeta[](2); - newConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); + newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 1, config: blueConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) }); - newConfig[1] = CCIPConfig.OCR3ConfigWithMeta({ + newConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 2, config: greenConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) }); - CCIPConfig.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfig.OCR3ConfigWithMeta[](1); - currentConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); + currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 1, config: blueConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) @@ -909,8 +911,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { function test__validateConfigTransition_StagingToRunning_Success() public { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); uint32 donId = 1; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -921,8 +923,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit") }); - CCIPConfig.OCR3Config memory greenConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -934,20 +936,20 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfig: bytes("commit-new") }); - CCIPConfig.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfig.OCR3ConfigWithMeta[](2); - currentConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); + currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 1, config: blueConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) }); - currentConfig[1] = CCIPConfig.OCR3ConfigWithMeta({ + currentConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 2, config: greenConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) }); - CCIPConfig.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfig.OCR3ConfigWithMeta[](1); - newConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); + newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 2, config: greenConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) @@ -968,10 +970,10 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { bytes32[] memory p2pIds = _makeBytes32Array(4, 0); bytes[] memory signers = _makeBytesArray(4, 10); bytes[] memory transmitters = _makeBytesArray(4, 20); - CCIPConfig.OCR3Config[] memory cfgs = new CCIPConfig.OCR3Config[](3); + CCIPConfigTypes.OCR3Config[] memory cfgs = new CCIPConfigTypes.OCR3Config[](3); for (uint256 i = 0; i < 3; i++) { - cfgs[i] = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + cfgs[i] = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -991,10 +993,10 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { bytes32[] memory p2pIds = _makeBytes32Array(4, 0); bytes[] memory signers = _makeBytesArray(4, 10); bytes[] memory transmitters = _makeBytesArray(4, 20); - CCIPConfig.OCR3Config[] memory cfgs = new CCIPConfig.OCR3Config[](3); + CCIPConfigTypes.OCR3Config[] memory cfgs = new CCIPConfigTypes.OCR3Config[](3); for (uint256 i = 0; i < 3; i++) { - cfgs[i] = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Execution, + cfgs[i] = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Execution, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -1011,15 +1013,15 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { } function test__groupByPluginType_TooManyOCR3Configs_Reverts() public { - CCIPConfig.OCR3Config[] memory cfgs = new CCIPConfig.OCR3Config[](5); + CCIPConfigTypes.OCR3Config[] memory cfgs = new CCIPConfigTypes.OCR3Config[](5); vm.expectRevert(CCIPConfig.TooManyOCR3Configs.selector); s_ccipCC.groupByPluginType(cfgs); } function test__validateConfigTransition_InitToRunning_WrongConfigCount_Reverts() public { uint32 donId = 1; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(_makeBytes32Array(4, 0), 0, 1), @@ -1031,13 +1033,13 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfig: bytes("commit") }); - CCIPConfig.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfig.OCR3ConfigWithMeta[](1); - newConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); + newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 0, config: blueConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) }); - CCIPConfig.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfig.OCR3ConfigWithMeta[](0); + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](0); vm.expectRevert(abi.encodeWithSelector(CCIPConfig.WrongConfigCount.selector, 0, 1)); s_ccipCC.validateConfigTransition(currentConfig, newConfig); @@ -1045,8 +1047,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { function test__validateConfigTransition_RunningToStaging_WrongConfigDigestBlueGreen_Reverts() public { uint32 donId = 1; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(_makeBytes32Array(4, 0), 0, 1), @@ -1057,8 +1059,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit") }); - CCIPConfig.OCR3Config memory greenConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(_makeBytes32Array(4, 0), 0, 1), @@ -1070,20 +1072,20 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfig: bytes("commit-new") }); - CCIPConfig.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfig.OCR3ConfigWithMeta[](1); - currentConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); + currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 1, config: blueConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) }); - CCIPConfig.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfig.OCR3ConfigWithMeta[](2); - newConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); + newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 1, config: blueConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 3, blueConfig) // wrong config digest (due to diff config count) }); - newConfig[1] = CCIPConfig.OCR3ConfigWithMeta({ + newConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 2, config: greenConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) @@ -1101,8 +1103,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { function test__validateConfigTransition_RunningToStaging_WrongConfigCount_Reverts() public { uint32 donId = 1; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(_makeBytes32Array(4, 0), 0, 1), @@ -1113,8 +1115,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit") }); - CCIPConfig.OCR3Config memory greenConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(_makeBytes32Array(4, 0), 0, 1), @@ -1126,20 +1128,20 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfig: bytes("commit-new") }); - CCIPConfig.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfig.OCR3ConfigWithMeta[](1); - currentConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); + currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 1, config: blueConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) }); - CCIPConfig.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfig.OCR3ConfigWithMeta[](2); - newConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); + newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 1, config: blueConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) }); - newConfig[1] = CCIPConfig.OCR3ConfigWithMeta({ + newConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 3, // wrong config count config: greenConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 3, greenConfig) @@ -1151,8 +1153,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { function test__validateConfigTransition_StagingToRunning_WrongConfigDigest_Reverts() public { uint32 donId = 1; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(_makeBytes32Array(4, 0), 0, 1), @@ -1163,8 +1165,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit") }); - CCIPConfig.OCR3Config memory greenConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(_makeBytes32Array(4, 0), 0, 1), @@ -1176,20 +1178,20 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { offchainConfig: bytes("commit-new") }); - CCIPConfig.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfig.OCR3ConfigWithMeta[](2); - currentConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); + currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 1, config: blueConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) }); - currentConfig[1] = CCIPConfig.OCR3ConfigWithMeta({ + currentConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 2, config: greenConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) }); - CCIPConfig.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfig.OCR3ConfigWithMeta[](1); - newConfig[0] = CCIPConfig.OCR3ConfigWithMeta({ + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); + newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ configCount: 2, config: greenConfig, configDigest: s_ccipCC.computeConfigDigest(donId, 3, greenConfig) // wrong config digest @@ -1206,8 +1208,8 @@ contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { } function test__validateConfigTransition_NonExistentConfigTransition_Reverts() public { - CCIPConfig.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfig.OCR3ConfigWithMeta[](3); - CCIPConfig.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfig.OCR3ConfigWithMeta[](1); + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](3); + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); vm.expectRevert(CCIPConfig.NonExistentConfigTransition.selector); s_ccipCC.validateConfigTransition(currentConfig, newConfig); } @@ -1219,8 +1221,8 @@ contract CCIPConfig__updatePluginConfig is CCIPConfigSetup { function test__updatePluginConfig_InitToRunning_Success() public { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); uint32 donId = 1; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -1231,13 +1233,14 @@ contract CCIPConfig__updatePluginConfig is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit") }); - CCIPConfig.OCR3Config[] memory configs = new CCIPConfig.OCR3Config[](1); + CCIPConfigTypes.OCR3Config[] memory configs = new CCIPConfigTypes.OCR3Config[](1); configs[0] = blueConfig; - s_ccipCC.updatePluginConfig(donId, CCIPConfig.PluginType.Commit, configs); + s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, configs); // should see the updated config in the contract state. - CCIPConfig.OCR3ConfigWithMeta[] memory storedConfig = s_ccipCC.getOCRConfig(donId, CCIPConfig.PluginType.Commit); + CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedConfig = + s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Commit); assertEq(storedConfig.length, 1, "don config length must be 1"); assertEq(storedConfig[0].configCount, uint64(1), "config count must be 1"); assertEq(uint256(storedConfig[0].config.pluginType), uint256(blueConfig.pluginType), "plugin type must match"); @@ -1247,9 +1250,9 @@ contract CCIPConfig__updatePluginConfig is CCIPConfigSetup { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); // add blue config. uint32 donId = 1; - CCIPConfig.PluginType pluginType = CCIPConfig.PluginType.Commit; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + Internal.OCRPluginType pluginType = Internal.OCRPluginType.Commit; + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -1260,13 +1263,13 @@ contract CCIPConfig__updatePluginConfig is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit") }); - CCIPConfig.OCR3Config[] memory startConfigs = new CCIPConfig.OCR3Config[](1); + CCIPConfigTypes.OCR3Config[] memory startConfigs = new CCIPConfigTypes.OCR3Config[](1); startConfigs[0] = blueConfig; // add blue AND green config to indicate an update. - s_ccipCC.updatePluginConfig(donId, CCIPConfig.PluginType.Commit, startConfigs); - CCIPConfig.OCR3Config memory greenConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, startConfigs); + CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -1277,22 +1280,23 @@ contract CCIPConfig__updatePluginConfig is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit-new") }); - CCIPConfig.OCR3Config[] memory blueAndGreen = new CCIPConfig.OCR3Config[](2); + CCIPConfigTypes.OCR3Config[] memory blueAndGreen = new CCIPConfigTypes.OCR3Config[](2); blueAndGreen[0] = blueConfig; blueAndGreen[1] = greenConfig; - s_ccipCC.updatePluginConfig(donId, CCIPConfig.PluginType.Commit, blueAndGreen); + s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, blueAndGreen); // should see the updated config in the contract state. - CCIPConfig.OCR3ConfigWithMeta[] memory storedConfig = s_ccipCC.getOCRConfig(donId, CCIPConfig.PluginType.Commit); + CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedConfig = + s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Commit); assertEq(storedConfig.length, 2, "don config length must be 2"); // 0 index is blue config, 1 index is green config. assertEq(storedConfig[1].configCount, uint64(2), "config count must be 2"); assertEq( - uint256(storedConfig[0].config.pluginType), uint256(CCIPConfig.PluginType.Commit), "plugin type must match" + uint256(storedConfig[0].config.pluginType), uint256(Internal.OCRPluginType.Commit), "plugin type must match" ); assertEq( - uint256(storedConfig[1].config.pluginType), uint256(CCIPConfig.PluginType.Commit), "plugin type must match" + uint256(storedConfig[1].config.pluginType), uint256(Internal.OCRPluginType.Commit), "plugin type must match" ); assertEq(storedConfig[0].config.offchainConfig, bytes("commit"), "blue offchain config must match"); assertEq(storedConfig[1].config.offchainConfig, bytes("commit-new"), "green offchain config must match"); @@ -1302,9 +1306,9 @@ contract CCIPConfig__updatePluginConfig is CCIPConfigSetup { (bytes32[] memory p2pIds, bytes[] memory signers, bytes[] memory transmitters) = _addChainConfig(4); // add blue config. uint32 donId = 1; - CCIPConfig.PluginType pluginType = CCIPConfig.PluginType.Commit; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + Internal.OCRPluginType pluginType = Internal.OCRPluginType.Commit; + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -1315,13 +1319,13 @@ contract CCIPConfig__updatePluginConfig is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit") }); - CCIPConfig.OCR3Config[] memory startConfigs = new CCIPConfig.OCR3Config[](1); + CCIPConfigTypes.OCR3Config[] memory startConfigs = new CCIPConfigTypes.OCR3Config[](1); startConfigs[0] = blueConfig; // add blue AND green config to indicate an update. - s_ccipCC.updatePluginConfig(donId, CCIPConfig.PluginType.Commit, startConfigs); - CCIPConfig.OCR3Config memory greenConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, startConfigs); + CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -1332,38 +1336,39 @@ contract CCIPConfig__updatePluginConfig is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit-new") }); - CCIPConfig.OCR3Config[] memory blueAndGreen = new CCIPConfig.OCR3Config[](2); + CCIPConfigTypes.OCR3Config[] memory blueAndGreen = new CCIPConfigTypes.OCR3Config[](2); blueAndGreen[0] = blueConfig; blueAndGreen[1] = greenConfig; - s_ccipCC.updatePluginConfig(donId, CCIPConfig.PluginType.Commit, blueAndGreen); + s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, blueAndGreen); // should see the updated config in the contract state. - CCIPConfig.OCR3ConfigWithMeta[] memory storedConfig = s_ccipCC.getOCRConfig(donId, CCIPConfig.PluginType.Commit); + CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedConfig = + s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Commit); assertEq(storedConfig.length, 2, "don config length must be 2"); // 0 index is blue config, 1 index is green config. assertEq(storedConfig[1].configCount, uint64(2), "config count must be 2"); assertEq( - uint256(storedConfig[0].config.pluginType), uint256(CCIPConfig.PluginType.Commit), "plugin type must match" + uint256(storedConfig[0].config.pluginType), uint256(Internal.OCRPluginType.Commit), "plugin type must match" ); assertEq( - uint256(storedConfig[1].config.pluginType), uint256(CCIPConfig.PluginType.Commit), "plugin type must match" + uint256(storedConfig[1].config.pluginType), uint256(Internal.OCRPluginType.Commit), "plugin type must match" ); assertEq(storedConfig[0].config.offchainConfig, bytes("commit"), "blue offchain config must match"); assertEq(storedConfig[1].config.offchainConfig, bytes("commit-new"), "green offchain config must match"); // promote green to blue. - CCIPConfig.OCR3Config[] memory promote = new CCIPConfig.OCR3Config[](1); + CCIPConfigTypes.OCR3Config[] memory promote = new CCIPConfigTypes.OCR3Config[](1); promote[0] = greenConfig; - s_ccipCC.updatePluginConfig(donId, CCIPConfig.PluginType.Commit, promote); + s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, promote); // should see the updated config in the contract state. - storedConfig = s_ccipCC.getOCRConfig(donId, CCIPConfig.PluginType.Commit); + storedConfig = s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Commit); assertEq(storedConfig.length, 1, "don config length must be 1"); assertEq(storedConfig[0].configCount, uint64(2), "config count must be 2"); assertEq( - uint256(storedConfig[0].config.pluginType), uint256(CCIPConfig.PluginType.Commit), "plugin type must match" + uint256(storedConfig[0].config.pluginType), uint256(Internal.OCRPluginType.Commit), "plugin type must match" ); assertEq(storedConfig[0].config.offchainConfig, bytes("commit-new"), "green offchain config must match"); } @@ -1371,17 +1376,17 @@ contract CCIPConfig__updatePluginConfig is CCIPConfigSetup { // Reverts. function test__updatePluginConfig_InvalidConfigLength_Reverts() public { uint32 donId = 1; - CCIPConfig.OCR3Config[] memory newConfig = new CCIPConfig.OCR3Config[](3); + CCIPConfigTypes.OCR3Config[] memory newConfig = new CCIPConfigTypes.OCR3Config[](3); vm.expectRevert(abi.encodeWithSelector(CCIPConfig.InvalidConfigLength.selector, uint256(3))); - s_ccipCC.updatePluginConfig(donId, CCIPConfig.PluginType.Commit, newConfig); + s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, newConfig); } function test__updatePluginConfig_InvalidConfigStateTransition_Reverts() public { uint32 donId = 1; - CCIPConfig.OCR3Config[] memory newConfig = new CCIPConfig.OCR3Config[](2); + CCIPConfigTypes.OCR3Config[] memory newConfig = new CCIPConfigTypes.OCR3Config[](2); // 0 -> 2 is an invalid state transition. vm.expectRevert(abi.encodeWithSelector(CCIPConfig.InvalidConfigStateTransition.selector, 0, 2)); - s_ccipCC.updatePluginConfig(donId, CCIPConfig.PluginType.Commit, newConfig); + s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, newConfig); } } @@ -1390,7 +1395,7 @@ contract CCIPConfig_beforeCapabilityConfigSet is CCIPConfigSetup { function test_beforeCapabilityConfigSet_ZeroLengthConfig_Success() public { changePrank(CAPABILITIES_REGISTRY); - CCIPConfig.OCR3Config[] memory configs = new CCIPConfig.OCR3Config[](0); + CCIPConfigTypes.OCR3Config[] memory configs = new CCIPConfigTypes.OCR3Config[](0); bytes memory encodedConfigs = abi.encode(configs); s_ccipCC.beforeCapabilityConfigSet(new bytes32[](0), encodedConfigs, 1, 1); } @@ -1400,8 +1405,8 @@ contract CCIPConfig_beforeCapabilityConfigSet is CCIPConfigSetup { changePrank(CAPABILITIES_REGISTRY); uint32 donId = 1; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -1412,17 +1417,18 @@ contract CCIPConfig_beforeCapabilityConfigSet is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit") }); - CCIPConfig.OCR3Config[] memory configs = new CCIPConfig.OCR3Config[](1); + CCIPConfigTypes.OCR3Config[] memory configs = new CCIPConfigTypes.OCR3Config[](1); configs[0] = blueConfig; bytes memory encoded = abi.encode(configs); s_ccipCC.beforeCapabilityConfigSet(new bytes32[](0), encoded, 1, donId); - CCIPConfig.OCR3ConfigWithMeta[] memory storedConfigs = s_ccipCC.getOCRConfig(donId, CCIPConfig.PluginType.Commit); + CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedConfigs = + s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Commit); assertEq(storedConfigs.length, 1, "config length must be 1"); assertEq(storedConfigs[0].configCount, uint64(1), "config count must be 1"); assertEq( - uint256(storedConfigs[0].config.pluginType), uint256(CCIPConfig.PluginType.Commit), "plugin type must be commit" + uint256(storedConfigs[0].config.pluginType), uint256(Internal.OCRPluginType.Commit), "plugin type must be commit" ); } @@ -1431,8 +1437,8 @@ contract CCIPConfig_beforeCapabilityConfigSet is CCIPConfigSetup { changePrank(CAPABILITIES_REGISTRY); uint32 donId = 1; - CCIPConfig.OCR3Config memory blueConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Execution, + CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Execution, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -1443,18 +1449,19 @@ contract CCIPConfig_beforeCapabilityConfigSet is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("exec") }); - CCIPConfig.OCR3Config[] memory configs = new CCIPConfig.OCR3Config[](1); + CCIPConfigTypes.OCR3Config[] memory configs = new CCIPConfigTypes.OCR3Config[](1); configs[0] = blueConfig; bytes memory encoded = abi.encode(configs); s_ccipCC.beforeCapabilityConfigSet(new bytes32[](0), encoded, 1, donId); - CCIPConfig.OCR3ConfigWithMeta[] memory storedConfigs = s_ccipCC.getOCRConfig(donId, CCIPConfig.PluginType.Execution); + CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedConfigs = + s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Execution); assertEq(storedConfigs.length, 1, "config length must be 1"); assertEq(storedConfigs[0].configCount, uint64(1), "config count must be 1"); assertEq( uint256(storedConfigs[0].config.pluginType), - uint256(CCIPConfig.PluginType.Execution), + uint256(Internal.OCRPluginType.Execution), "plugin type must be execution" ); } @@ -1464,8 +1471,8 @@ contract CCIPConfig_beforeCapabilityConfigSet is CCIPConfigSetup { changePrank(CAPABILITIES_REGISTRY); uint32 donId = 1; - CCIPConfig.OCR3Config memory blueCommitConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Commit, + CCIPConfigTypes.OCR3Config memory blueCommitConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Commit, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -1476,8 +1483,8 @@ contract CCIPConfig_beforeCapabilityConfigSet is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("commit") }); - CCIPConfig.OCR3Config memory blueExecConfig = CCIPConfig.OCR3Config({ - pluginType: CCIPConfig.PluginType.Execution, + CCIPConfigTypes.OCR3Config memory blueExecConfig = CCIPConfigTypes.OCR3Config({ + pluginType: Internal.OCRPluginType.Execution, offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), chainSelector: 1, bootstrapP2PIds: _subset(p2pIds, 0, 1), @@ -1488,30 +1495,30 @@ contract CCIPConfig_beforeCapabilityConfigSet is CCIPConfigSetup { offchainConfigVersion: 30, offchainConfig: bytes("exec") }); - CCIPConfig.OCR3Config[] memory configs = new CCIPConfig.OCR3Config[](2); + CCIPConfigTypes.OCR3Config[] memory configs = new CCIPConfigTypes.OCR3Config[](2); configs[0] = blueExecConfig; configs[1] = blueCommitConfig; bytes memory encoded = abi.encode(configs); s_ccipCC.beforeCapabilityConfigSet(new bytes32[](0), encoded, 1, donId); - CCIPConfig.OCR3ConfigWithMeta[] memory storedExecConfigs = - s_ccipCC.getOCRConfig(donId, CCIPConfig.PluginType.Execution); + CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedExecConfigs = + s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Execution); assertEq(storedExecConfigs.length, 1, "config length must be 1"); assertEq(storedExecConfigs[0].configCount, uint64(1), "config count must be 1"); assertEq( uint256(storedExecConfigs[0].config.pluginType), - uint256(CCIPConfig.PluginType.Execution), + uint256(Internal.OCRPluginType.Execution), "plugin type must be execution" ); - CCIPConfig.OCR3ConfigWithMeta[] memory storedCommitConfigs = - s_ccipCC.getOCRConfig(donId, CCIPConfig.PluginType.Commit); + CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedCommitConfigs = + s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Commit); assertEq(storedCommitConfigs.length, 1, "config length must be 1"); assertEq(storedCommitConfigs[0].configCount, uint64(1), "config count must be 1"); assertEq( uint256(storedCommitConfigs[0].config.pluginType), - uint256(CCIPConfig.PluginType.Commit), + uint256(Internal.OCRPluginType.Commit), "plugin type must be commit" ); } diff --git a/contracts/src/v0.8/ccip/test/e2e/MultiRampsEnd2End.sol b/contracts/src/v0.8/ccip/test/e2e/MultiRampsEnd2End.sol index a1213c5730..1d9a99f182 100644 --- a/contracts/src/v0.8/ccip/test/e2e/MultiRampsEnd2End.sol +++ b/contracts/src/v0.8/ccip/test/e2e/MultiRampsEnd2End.sol @@ -14,7 +14,7 @@ import "../onRamp/EVM2EVMMultiOnRampSetup.t.sol"; /// 2. Commit multiple merkle roots (1 for each source chain). /// 3. Batch execute all the committed messages. contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { - using Internal for Internal.EVM2EVMMessage; + using Internal for Internal.Any2EVMRampMessage; Router internal s_sourceRouter2; EVM2EVMMultiOnRampHelper internal s_onRamp2; @@ -81,7 +81,7 @@ contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { s_sourceRouter2.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), new Router.OffRamp[](0)); // Deploy offramp - _deployOffRamp(s_destRouter, s_mockRMN); + _deployOffRamp(s_destRouter, s_mockRMN, s_inboundNonceManager); // Enable source chains on offramp EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = @@ -89,14 +89,13 @@ contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR, isEnabled: true, - prevOffRamp: address(0), - onRamp: address(s_onRamp) + // Must match OnRamp address + onRamp: abi.encode(address(s_onRamp)) }); sourceChainConfigs[1] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR + 1, isEnabled: true, - prevOffRamp: address(0), - onRamp: address(s_onRamp2) + onRamp: abi.encode(address(s_onRamp2)) }); _setupMultipleOffRampsFromConfigs(sourceChainConfigs); @@ -110,10 +109,10 @@ contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { uint256 balance1Pre = token1.balanceOf(OWNER); // Send messages - Internal.EVM2EVMMessage[] memory messages1 = new Internal.EVM2EVMMessage[](2); + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); messages1[0] = _sendRequest(1, SOURCE_CHAIN_SELECTOR, 1, s_metadataHash, s_sourceRouter, s_tokenAdminRegistry); messages1[1] = _sendRequest(2, SOURCE_CHAIN_SELECTOR, 2, s_metadataHash, s_sourceRouter, s_tokenAdminRegistry); - Internal.EVM2EVMMessage[] memory messages2 = new Internal.EVM2EVMMessage[](1); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); messages2[0] = _sendRequest(1, SOURCE_CHAIN_SELECTOR + 1, 1, s_metadataHash2, s_sourceRouter2, s_tokenAdminRegistry2); @@ -126,13 +125,10 @@ contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { // Commit bytes32[] memory hashedMessages1 = new bytes32[](2); - hashedMessages1[0] = messages1[0]._hash(s_metadataHash); - messages1[0].messageId = hashedMessages1[0]; - hashedMessages1[1] = messages1[1]._hash(s_metadataHash); - messages1[1].messageId = hashedMessages1[1]; + hashedMessages1[0] = messages1[0]._hash(abi.encode(address(s_onRamp))); + hashedMessages1[1] = messages1[1]._hash(abi.encode(address(s_onRamp))); bytes32[] memory hashedMessages2 = new bytes32[](1); - hashedMessages2[0] = messages2[0]._hash(s_metadataHash2); - messages2[0].messageId = hashedMessages2[0]; + hashedMessages2[0] = messages2[0]._hash(abi.encode(address(s_onRamp2))); bytes32[] memory merkleRoots = new bytes32[](2); merkleRoots[0] = MerkleHelper.getMerkleRoot(hashedMessages1); @@ -141,12 +137,12 @@ contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { EVM2EVMMultiOffRamp.MerkleRoot[] memory roots = new EVM2EVMMultiOffRamp.MerkleRoot[](2); roots[0] = EVM2EVMMultiOffRamp.MerkleRoot({ sourceChainSelector: SOURCE_CHAIN_SELECTOR, - interval: EVM2EVMMultiOffRamp.Interval(messages1[0].sequenceNumber, messages1[1].sequenceNumber), + interval: EVM2EVMMultiOffRamp.Interval(messages1[0].header.sequenceNumber, messages1[1].header.sequenceNumber), merkleRoot: merkleRoots[0] }); roots[1] = EVM2EVMMultiOffRamp.MerkleRoot({ sourceChainSelector: SOURCE_CHAIN_SELECTOR + 1, - interval: EVM2EVMMultiOffRamp.Interval(messages2[0].sequenceNumber, messages2[0].sequenceNumber), + interval: EVM2EVMMultiOffRamp.Interval(messages2[0].header.sequenceNumber, messages2[0].header.sequenceNumber), merkleRoot: merkleRoots[1] }); @@ -178,8 +174,8 @@ contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR, - messages1[0].sequenceNumber, - messages1[0].messageId, + messages1[0].header.sequenceNumber, + messages1[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -187,8 +183,8 @@ contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR, - messages1[1].sequenceNumber, - messages1[1].messageId, + messages1[1].header.sequenceNumber, + messages1[1].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -196,8 +192,8 @@ contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR + 1, - messages2[0].sequenceNumber, - messages2[0].messageId, + messages2[0].header.sequenceNumber, + messages2[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -217,7 +213,7 @@ contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { bytes32 metadataHash, Router router, TokenAdminRegistry tokenAdminRegistry - ) public returns (Internal.EVM2EVMMessage memory) { + ) public returns (Internal.Any2EVMRampMessage memory) { Client.EVM2AnyMessage memory message = _generateTokenMessage(); uint256 expectedFee = router.getFee(DEST_CHAIN_SELECTOR, message); @@ -244,6 +240,20 @@ contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { router.ccipSend(DEST_CHAIN_SELECTOR, message); vm.pauseGasMetering(); - return msgEvent; + return Internal.Any2EVMRampMessage({ + header: Internal.RampMessageHeader({ + messageId: msgEvent.messageId, + sourceChainSelector: sourceChainSelector, + destChainSelector: DEST_CHAIN_SELECTOR, + sequenceNumber: msgEvent.sequenceNumber, + nonce: msgEvent.nonce + }), + sender: abi.encode(msgEvent.sender), + data: msgEvent.data, + receiver: msgEvent.receiver, + gasLimit: msgEvent.gasLimit, + tokenAmounts: msgEvent.tokenAmounts, + sourceTokenData: msgEvent.sourceTokenData + }); } } diff --git a/contracts/src/v0.8/ccip/test/helpers/CCIPConfigHelper.sol b/contracts/src/v0.8/ccip/test/helpers/CCIPConfigHelper.sol index f321ea5d40..74f03890d3 100644 --- a/contracts/src/v0.8/ccip/test/helpers/CCIPConfigHelper.sol +++ b/contracts/src/v0.8/ccip/test/helpers/CCIPConfigHelper.sol @@ -2,39 +2,44 @@ pragma solidity ^0.8.24; import {CCIPConfig} from "../../capability/CCIPConfig.sol"; +import {CCIPConfigTypes} from "../../capability/libraries/CCIPConfigTypes.sol"; +import {Internal} from "../../libraries/Internal.sol"; contract CCIPConfigHelper is CCIPConfig { constructor(address capabilitiesRegistry) CCIPConfig(capabilitiesRegistry) {} - function stateFromConfigLength(uint256 configLength) public pure returns (ConfigState) { + function stateFromConfigLength(uint256 configLength) public pure returns (CCIPConfigTypes.ConfigState) { return _stateFromConfigLength(configLength); } - function validateConfigStateTransition(ConfigState currentState, ConfigState newState) public pure { + function validateConfigStateTransition( + CCIPConfigTypes.ConfigState currentState, + CCIPConfigTypes.ConfigState newState + ) public pure { _validateConfigStateTransition(currentState, newState); } function validateConfigTransition( - OCR3ConfigWithMeta[] memory currentConfig, - OCR3ConfigWithMeta[] memory newConfigWithMeta + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig, + CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta ) public pure { _validateConfigTransition(currentConfig, newConfigWithMeta); } function computeNewConfigWithMeta( uint32 donId, - OCR3ConfigWithMeta[] memory currentConfig, - OCR3Config[] memory newConfig, - ConfigState currentState, - ConfigState newState - ) public view returns (OCR3ConfigWithMeta[] memory) { + CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig, + CCIPConfigTypes.OCR3Config[] memory newConfig, + CCIPConfigTypes.ConfigState currentState, + CCIPConfigTypes.ConfigState newState + ) public view returns (CCIPConfigTypes.OCR3ConfigWithMeta[] memory) { return _computeNewConfigWithMeta(donId, currentConfig, newConfig, currentState, newState); } - function groupByPluginType(OCR3Config[] memory ocr3Configs) + function groupByPluginType(CCIPConfigTypes.OCR3Config[] memory ocr3Configs) public pure - returns (OCR3Config[] memory commitConfigs, OCR3Config[] memory execConfigs) + returns (CCIPConfigTypes.OCR3Config[] memory commitConfigs, CCIPConfigTypes.OCR3Config[] memory execConfigs) { return _groupByPluginType(ocr3Configs); } @@ -42,16 +47,20 @@ contract CCIPConfigHelper is CCIPConfig { function computeConfigDigest( uint32 donId, uint64 configCount, - OCR3Config memory ocr3Config + CCIPConfigTypes.OCR3Config memory ocr3Config ) public pure returns (bytes32) { return _computeConfigDigest(donId, configCount, ocr3Config); } - function validateConfig(OCR3Config memory cfg) public view { + function validateConfig(CCIPConfigTypes.OCR3Config memory cfg) public view { _validateConfig(cfg); } - function updatePluginConfig(uint32 donId, PluginType pluginType, OCR3Config[] memory newConfig) public { + function updatePluginConfig( + uint32 donId, + Internal.OCRPluginType pluginType, + CCIPConfigTypes.OCR3Config[] memory newConfig + ) public { _updatePluginConfig(donId, pluginType, newConfig); } } diff --git a/contracts/src/v0.8/ccip/test/helpers/EVM2EVMMultiOffRampHelper.sol b/contracts/src/v0.8/ccip/test/helpers/EVM2EVMMultiOffRampHelper.sol index 83b9569425..0e2202869e 100644 --- a/contracts/src/v0.8/ccip/test/helpers/EVM2EVMMultiOffRampHelper.sol +++ b/contracts/src/v0.8/ccip/test/helpers/EVM2EVMMultiOffRampHelper.sol @@ -26,10 +26,6 @@ contract EVM2EVMMultiOffRampHelper is EVM2EVMMultiOffRamp, IgnoreContractSize { return s_executionStates[sourceChainSelector][bitmapIndex]; } - function metadataHash(uint64 sourceChainSelector, address onRamp) external view returns (bytes32) { - return _metadataHash(sourceChainSelector, onRamp, Internal.EVM_2_EVM_MESSAGE_HASH); - } - function releaseOrMintSingleToken( uint256 sourceTokenAmount, bytes calldata originalSender, @@ -57,7 +53,7 @@ contract EVM2EVMMultiOffRampHelper is EVM2EVMMultiOffRamp, IgnoreContractSize { } function trialExecute( - Internal.EVM2EVMMessage memory message, + Internal.Any2EVMRampMessage memory message, bytes[] memory offchainTokenData ) external returns (Internal.MessageExecutionState, bytes memory) { return _trialExecute(message, offchainTokenData); diff --git a/contracts/src/v0.8/ccip/test/helpers/MessageHasher.sol b/contracts/src/v0.8/ccip/test/helpers/MessageHasher.sol index fcb5819948..755b326c0d 100644 --- a/contracts/src/v0.8/ccip/test/helpers/MessageHasher.sol +++ b/contracts/src/v0.8/ccip/test/helpers/MessageHasher.sol @@ -1,11 +1,58 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +import {Client} from "../../libraries/Client.sol"; import {Internal} from "../../libraries/Internal.sol"; -// MessageHasher is a contract that provides a function to hash an EVM2EVMMessage. +/// @notice MessageHasher is a contract that utility functions to hash an Any2EVMRampMessage +/// and encode various preimages for the final hash of the message. contract MessageHasher { - function hash(Internal.EVM2EVMMessage memory msg, bytes32 metadataHash) public pure returns (bytes32) { - return Internal._hash(msg, metadataHash); + function hash(Internal.Any2EVMRampMessage memory message, bytes memory onRamp) public pure returns (bytes32) { + return Internal._hash(message, onRamp); + } + + function encodeTokenAmountsHashPreimage(Client.EVMTokenAmount[] memory tokenAmounts) + public + pure + returns (bytes memory) + { + return abi.encode(tokenAmounts); + } + + function encodeSourceTokenDataHashPreimage(bytes[] memory sourceTokenData) public pure returns (bytes memory) { + return abi.encode(sourceTokenData); + } + + function encodeMetadataHashPreimage( + bytes32 any2EVMMessageHash, + uint64 sourceChainSelector, + uint64 destChainSelector, + bytes memory onRamp + ) public pure returns (bytes memory) { + return abi.encode(any2EVMMessageHash, sourceChainSelector, destChainSelector, onRamp); + } + + function encodeFixedSizeFieldsHashPreimage( + bytes32 messageId, + bytes memory sender, + address receiver, + uint64 sequenceNumber, + uint256 gasLimit, + uint64 nonce + ) public pure returns (bytes memory) { + return abi.encode(messageId, sender, receiver, sequenceNumber, gasLimit, nonce); + } + + function encodeFinalHashPreimage( + bytes32 leafDomainSeparator, + bytes32 implicitMetadataHash, + bytes32 fixedSizeFieldsHash, + bytes32 dataHash, + bytes32 tokenAmountsHash, + bytes32 sourceTokenDataHash + ) public pure returns (bytes memory) { + return abi.encode( + leafDomainSeparator, implicitMetadataHash, fixedSizeFieldsHash, dataHash, tokenAmountsHash, sourceTokenDataHash + ); } } diff --git a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRamp.t.sol b/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRamp.t.sol index ab7057f340..c994cf5698 100644 --- a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRamp.t.sol +++ b/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRamp.t.sol @@ -8,6 +8,7 @@ import {IRMN} from "../../interfaces/IRMN.sol"; import {ITokenAdminRegistry} from "../../interfaces/ITokenAdminRegistry.sol"; import {CallWithExactGas} from "../../../shared/call/CallWithExactGas.sol"; +import {NonceManager} from "../../NonceManager.sol"; import {PriceRegistry} from "../../PriceRegistry.sol"; import {RMN} from "../../RMN.sol"; import {Router} from "../../Router.sol"; @@ -28,18 +29,18 @@ import {ConformingReceiver} from "../helpers/receivers/ConformingReceiver.sol"; import {MaybeRevertMessageReceiver} from "../helpers/receivers/MaybeRevertMessageReceiver.sol"; import {MaybeRevertMessageReceiverNo165} from "../helpers/receivers/MaybeRevertMessageReceiverNo165.sol"; import {ReentrancyAbuserMultiRamp} from "../helpers/receivers/ReentrancyAbuserMultiRamp.sol"; -import {MockCommitStore} from "../mocks/MockCommitStore.sol"; import {EVM2EVMMultiOffRampSetup} from "./EVM2EVMMultiOffRampSetup.t.sol"; +import {Vm} from "forge-std/Vm.sol"; import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {Vm} from "forge-std/Vm.sol"; contract EVM2EVMMultiOffRamp_constructor is EVM2EVMMultiOffRampSetup { function test_Constructor_Success() public { EVM2EVMMultiOffRamp.StaticConfig memory staticConfig = EVM2EVMMultiOffRamp.StaticConfig({ chainSelector: DEST_CHAIN_SELECTOR, rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(s_inboundNonceManager) }); EVM2EVMMultiOffRamp.DynamicConfig memory dynamicConfig = _generateDynamicMultiOffRampConfig(address(s_destRouter), address(s_priceRegistry)); @@ -48,32 +49,20 @@ contract EVM2EVMMultiOffRamp_constructor is EVM2EVMMultiOffRampSetup { new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](2); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - prevOffRamp: address(0), - onRamp: ON_RAMP_ADDRESS_1 + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true }); sourceChainConfigs[1] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1 + 1, - isEnabled: true, - prevOffRamp: address(0), - onRamp: address(uint160(ON_RAMP_ADDRESS_1) + 1) + onRamp: ON_RAMP_ADDRESS_2, + isEnabled: true }); - EVM2EVMMultiOffRamp.SourceChainConfig memory expectedSourceChainConfig1 = EVM2EVMMultiOffRamp.SourceChainConfig({ - isEnabled: true, - minSeqNr: 1, - prevOffRamp: address(0), - onRamp: sourceChainConfigs[0].onRamp, - metadataHash: s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, sourceChainConfigs[0].onRamp) - }); + EVM2EVMMultiOffRamp.SourceChainConfig memory expectedSourceChainConfig1 = + EVM2EVMMultiOffRamp.SourceChainConfig({isEnabled: true, minSeqNr: 1, onRamp: sourceChainConfigs[0].onRamp}); - EVM2EVMMultiOffRamp.SourceChainConfig memory expectedSourceChainConfig2 = EVM2EVMMultiOffRamp.SourceChainConfig({ - isEnabled: true, - minSeqNr: 1, - prevOffRamp: address(0), - onRamp: sourceChainConfigs[1].onRamp, - metadataHash: s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1 + 1, sourceChainConfigs[1].onRamp) - }); + EVM2EVMMultiOffRamp.SourceChainConfig memory expectedSourceChainConfig2 = + EVM2EVMMultiOffRamp.SourceChainConfig({isEnabled: true, minSeqNr: 1, onRamp: sourceChainConfigs[1].onRamp}); vm.expectEmit(); emit EVM2EVMMultiOffRamp.StaticConfigSet(staticConfig); @@ -151,9 +140,8 @@ contract EVM2EVMMultiOffRamp_constructor is EVM2EVMMultiOffRampSetup { new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - prevOffRamp: address(0), - onRamp: address(0) + onRamp: new bytes(0), + isEnabled: true }); vm.expectRevert(EVM2EVMMultiOffRamp.ZeroAddressNotAllowed.selector); @@ -162,7 +150,8 @@ contract EVM2EVMMultiOffRamp_constructor is EVM2EVMMultiOffRampSetup { EVM2EVMMultiOffRamp.StaticConfig({ chainSelector: DEST_CHAIN_SELECTOR, rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(s_inboundNonceManager) }), sourceChainConfigs ); @@ -174,12 +163,8 @@ contract EVM2EVMMultiOffRamp_constructor is EVM2EVMMultiOffRampSetup { EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ - sourceChainSelector: 0, - isEnabled: true, - prevOffRamp: address(0), - onRamp: address(0) - }); + sourceChainConfigs[0] = + EVM2EVMMultiOffRamp.SourceChainConfigArgs({sourceChainSelector: 0, onRamp: ON_RAMP_ADDRESS_1, isEnabled: true}); vm.expectRevert(EVM2EVMMultiOffRamp.ZeroChainSelectorNotAllowed.selector); @@ -187,7 +172,8 @@ contract EVM2EVMMultiOffRamp_constructor is EVM2EVMMultiOffRampSetup { EVM2EVMMultiOffRamp.StaticConfig({ chainSelector: DEST_CHAIN_SELECTOR, rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(s_inboundNonceManager) }), sourceChainConfigs ); @@ -206,7 +192,8 @@ contract EVM2EVMMultiOffRamp_constructor is EVM2EVMMultiOffRampSetup { EVM2EVMMultiOffRamp.StaticConfig({ chainSelector: DEST_CHAIN_SELECTOR, rmnProxy: ZERO_ADDRESS, - tokenAdminRegistry: address(s_tokenAdminRegistry) + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(s_inboundNonceManager) }), sourceChainConfigs ); @@ -225,7 +212,8 @@ contract EVM2EVMMultiOffRamp_constructor is EVM2EVMMultiOffRampSetup { EVM2EVMMultiOffRamp.StaticConfig({ chainSelector: 0, rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(s_inboundNonceManager) }), sourceChainConfigs ); @@ -244,7 +232,28 @@ contract EVM2EVMMultiOffRamp_constructor is EVM2EVMMultiOffRampSetup { EVM2EVMMultiOffRamp.StaticConfig({ chainSelector: DEST_CHAIN_SELECTOR, rmnProxy: address(s_mockRMN), - tokenAdminRegistry: ZERO_ADDRESS + tokenAdminRegistry: ZERO_ADDRESS, + nonceManager: address(s_inboundNonceManager) + }), + sourceChainConfigs + ); + } + + function test_ZeroNonceManager_Revert() public { + uint64[] memory sourceChainSelectors = new uint64[](1); + sourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; + + EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = + new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](0); + + vm.expectRevert(EVM2EVMMultiOffRamp.ZeroAddressNotAllowed.selector); + + s_offRamp = new EVM2EVMMultiOffRampHelper( + EVM2EVMMultiOffRamp.StaticConfig({ + chainSelector: DEST_CHAIN_SELECTOR, + rmnProxy: address(s_mockRMN), + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: ZERO_ADDRESS }), sourceChainConfigs ); @@ -309,74 +318,6 @@ contract EVM2EVMMultiOffRamp_setDynamicConfig is EVM2EVMMultiOffRampSetup { } } -contract EVM2EVMMultiOffRamp_metadataHash is EVM2EVMMultiOffRampSetup { - function test_MetadataHash_Success() public view { - bytes32 h = s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - assertEq( - h, - keccak256( - abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR_1, DEST_CHAIN_SELECTOR, ON_RAMP_ADDRESS_1) - ) - ); - } - - function test_MetadataHashChangesOnSourceChain_Success() public view { - bytes32 h = s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1 + 1, ON_RAMP_ADDRESS_1); - assertEq( - h, - keccak256( - abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR_1 + 1, DEST_CHAIN_SELECTOR, ON_RAMP_ADDRESS_1) - ) - ); - assertTrue(h != s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); - } - - function test_MetadataHashChangesOnOnRampAddress_Success() public view { - address mockOnRampAddress = address(uint160(ON_RAMP_ADDRESS_1) + 1); - bytes32 h = s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, mockOnRampAddress); - assertEq( - h, - keccak256( - abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR_1, DEST_CHAIN_SELECTOR, mockOnRampAddress) - ) - ); - assertTrue(h != s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); - } - - // NOTE: to get a reliable result, set fuzz runs to at least 1mil - /// forge-config: default.fuzz.runs = 32 - /// forge-config: ccip.fuzz.runs = 10000 - function test_Fuzz__MetadataHash_NoCollisions( - uint64 destChainSelector, - uint64 sourceChainSelector1, - uint64 sourceChainSelector2, - address onRamp1, - address onRamp2 - ) public { - // Edge case: metadata hash should be the same when values match - if (sourceChainSelector1 == sourceChainSelector2 && onRamp1 == onRamp2) { - return; - } - - // Disallow 0 source chain selectors - vm.assume(sourceChainSelector1 != 0); - vm.assume(sourceChainSelector2 != 0); - vm.assume(destChainSelector != 0); - - EVM2EVMMultiOffRamp.StaticConfig memory staticConfig = s_offRamp.getStaticConfig(); - EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = - new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](0); - - staticConfig.chainSelector = destChainSelector; - s_offRamp = new EVM2EVMMultiOffRampHelper(staticConfig, sourceChainConfigs); - - bytes32 h1 = s_offRamp.metadataHash(sourceChainSelector1, onRamp1); - bytes32 h2 = s_offRamp.metadataHash(sourceChainSelector2, onRamp2); - - assertTrue(h1 != h2); - } -} - contract EVM2EVMMultiOffRamp_ccipReceive is EVM2EVMMultiOffRampSetup { // Reverts @@ -397,109 +338,109 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { } function test_SingleMessageNoTokens_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages[0].sourceChainSelector, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - messages[0].nonce++; - messages[0].sequenceNumber++; - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].header.nonce++; + messages[0].header.sequenceNumber++; + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages[0].sourceChainSelector, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); - uint64 nonceBefore = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); + uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - assertGt(s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), nonceBefore); + assertGt(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), nonceBefore); } function test_SingleMessageNoTokensUnordered_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].nonce = 0; - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + messages[0].header.nonce = 0; + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages[0].sourceChainSelector, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); // Nonce never increments on unordered messages. - uint64 nonceBefore = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); + uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); assertEq( - s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), + s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), nonceBefore, "nonce must remain unchanged on unordered messages" ); - messages[0].sequenceNumber++; - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].header.sequenceNumber++; + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages[0].sourceChainSelector, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); // Nonce never increments on unordered messages. - nonceBefore = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); + nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); assertEq( - s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), + s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), nonceBefore, "nonce must remain unchanged on unordered messages" ); } function test_SingleMessageNoTokensOtherChain_Success() public { - Internal.EVM2EVMMessage[] memory messagesChain1 = + Internal.Any2EVMRampMessage[] memory messagesChain1 = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); s_offRamp.executeSingleReport( _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesChain1), new uint256[](0) ); - uint64 nonceChain1 = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messagesChain1[0].sender); + uint64 nonceChain1 = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messagesChain1[0].sender); assertGt(nonceChain1, 0); - Internal.EVM2EVMMessage[] memory messagesChain2 = + Internal.Any2EVMRampMessage[] memory messagesChain2 = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3); - assertEq(s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_3, messagesChain2[0].sender), 0); + assertEq(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, messagesChain2[0].sender), 0); s_offRamp.executeSingleReport( _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messagesChain2), new uint256[](0) ); - assertGt(s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_3, messagesChain2[0].sender), 0); + assertGt(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, messagesChain2[0].sender), 0); // Other chain's nonce is unaffected - assertEq(s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messagesChain1[0].sender), nonceChain1); + assertEq(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messagesChain1[0].sender), nonceChain1); } function test_ReceiverError_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); bytes memory realError1 = new bytes(2); realError1[0] = 0xbe; @@ -507,14 +448,13 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { s_reverting_receiver.setErr(realError1); messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages[0].sourceChainSelector, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.FAILURE, abi.encodeWithSelector( EVM2EVMMultiOffRamp.ReceiverError.selector, @@ -522,56 +462,57 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { ) ); // Nonce should increment on non-strict - assertEq(uint64(0), s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, address(OWNER))); + assertEq(uint64(0), s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER))); s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - assertEq(uint64(1), s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, address(OWNER))); + assertEq(uint64(1), s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER))); } function test_SkippedIncorrectNonce_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].nonce++; - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].header.nonce++; + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); vm.expectEmit(); - emit EVM2EVMMultiOffRamp.SkippedIncorrectNonce( - messages[0].sourceChainSelector, messages[0].nonce, messages[0].sender + emit NonceManager.SkippedIncorrectNonce( + messages[0].header.sourceChainSelector, messages[0].header.nonce, messages[0].sender ); s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); } function test_SkippedIncorrectNonceStillExecutes_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[1].nonce++; - messages[1].messageId = - Internal._hash(messages[1], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[1].header.nonce++; + messages[1].header.messageId = Internal._hash(messages[1], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); vm.expectEmit(); - emit EVM2EVMMultiOffRamp.SkippedIncorrectNonce(SOURCE_CHAIN_SELECTOR_1, messages[1].nonce, messages[1].sender); + emit NonceManager.SkippedIncorrectNonce(SOURCE_CHAIN_SELECTOR_1, messages[1].header.nonce, messages[1].sender); s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); } function test__execute_SkippedAlreadyExecutedMessage_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages[0].sourceChainSelector, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -579,22 +520,22 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); vm.expectEmit(); - emit EVM2EVMMultiOffRamp.SkippedAlreadyExecutedMessage(SOURCE_CHAIN_SELECTOR_1, messages[0].sequenceNumber); + emit EVM2EVMMultiOffRamp.SkippedAlreadyExecutedMessage(SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber); s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); } function test__execute_SkippedAlreadyExecutedMessageUnordered_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].nonce = 0; - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + messages[0].header.nonce = 0; + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages[0].sourceChainSelector, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -602,7 +543,7 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); vm.expectEmit(); - emit EVM2EVMMultiOffRamp.SkippedAlreadyExecutedMessage(SOURCE_CHAIN_SELECTOR_1, messages[0].sequenceNumber); + emit EVM2EVMMultiOffRamp.SkippedAlreadyExecutedMessage(SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber); s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); } @@ -610,17 +551,17 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { // Send a message to a contract that does not implement the CCIPReceiver interface // This should execute successfully. function test_SingleMessageToNonCCIPReceiver_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); MaybeRevertMessageReceiverNo165 newReceiver = new MaybeRevertMessageReceiverNo165(true); messages[0].receiver = address(newReceiver); - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages[0].sourceChainSelector, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -630,13 +571,14 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { function test_SingleMessagesNoTokensSuccess_gas() public { vm.pauseGasMetering(); - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages[0].sourceChainSelector, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -649,17 +591,17 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { function test_TwoMessagesWithTokensSuccess_gas() public { vm.pauseGasMetering(); - Internal.EVM2EVMMessage[] memory messages = _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); // Set message 1 to use another receiver to simulate more fair gas costs messages[1].receiver = address(s_secondary_receiver); - messages[1].messageId = - Internal._hash(messages[1], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[1].header.messageId = Internal._hash(messages[1], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -667,8 +609,8 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[1].sequenceNumber, - messages[1].messageId, + messages[1].header.sequenceNumber, + messages[1].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -680,17 +622,17 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { } function test_TwoMessagesWithTokensAndGE_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); // Set message 1 to use another receiver to simulate more fair gas costs messages[1].receiver = address(s_secondary_receiver); - messages[1].messageId = - Internal._hash(messages[1], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[1].header.messageId = Internal._hash(messages[1], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -698,21 +640,21 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[1].sequenceNumber, - messages[1].messageId, + messages[1].header.sequenceNumber, + messages[1].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); - assertEq(uint64(0), s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, OWNER)); + assertEq(uint64(0), s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER))); s_offRamp.executeSingleReport( _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), _getGasLimitsFromMessages(messages) ); - assertEq(uint64(2), s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, OWNER)); + assertEq(uint64(2), s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER))); } function test_Fuzz_InterleavingOrderedAndUnorderedMessages_Success(bool[7] memory orderings) public { - Internal.EVM2EVMMessage[] memory messages = new Internal.EVM2EVMMessage[](orderings.length); + Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](orderings.length); // number of tokens needs to be capped otherwise we hit UnsupportedNumberOfTokens. Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](3); for (uint256 i = 0; i < 3; ++i) { @@ -724,22 +666,21 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { messages[i] = _generateAny2EVMMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, uint64(i + 1), tokenAmounts, !orderings[i]); if (orderings[i]) { - messages[i].nonce = ++expectedNonce; + messages[i].header.nonce = ++expectedNonce; } - messages[i].messageId = - Internal._hash(messages[i], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[i].header.messageId = Internal._hash(messages[i], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[i].sequenceNumber, - messages[i].messageId, + messages[i].header.sequenceNumber, + messages[i].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); } - uint64 nonceBefore = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, OWNER); + uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER)); assertEq(uint64(0), nonceBefore, "nonce before exec should be 0"); s_offRamp.executeSingleReport( _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), _getGasLimitsFromMessages(messages) @@ -747,17 +688,20 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { // all executions should succeed. for (uint256 i = 0; i < orderings.length; ++i) { assertEq( - uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, messages[i].sequenceNumber)), + uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, messages[i].header.sequenceNumber)), uint256(Internal.MessageExecutionState.SUCCESS) ); } - assertEq(nonceBefore + expectedNonce, s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, OWNER)); + assertEq( + nonceBefore + expectedNonce, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER)) + ); } function test_InvalidSourcePoolAddress_Success() public { address fakePoolAddress = address(0x0000000000333333); - Internal.EVM2EVMMessage[] memory messages = _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); messages[0].sourceTokenData[0] = abi.encode( Internal.SourceTokenData({ sourcePoolAddress: abi.encode(fakePoolAddress), @@ -766,16 +710,14 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { }) ); - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); - messages[1].messageId = - Internal._hash(messages[1], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); + messages[1].header.messageId = Internal._hash(messages[1], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.FAILURE, abi.encodeWithSelector( EVM2EVMMultiOffRamp.TokenHandlingError.selector, @@ -798,34 +740,37 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { // Reverts - function test_InvalidMessageId_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].nonce++; - // MessageID no longer matches hash. - Internal.ExecutionReportSingleChain memory executionReport = - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.InvalidMessageId.selector, messages[0].messageId)); - s_offRamp.executeSingleReport(executionReport, new uint256[](0)); - } + function test_MismatchingDestChainSelector_Revert() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3); + messages[0].header.destChainSelector = DEST_CHAIN_SELECTOR + 1; - function test_MismatchingSourceChainSelector_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3); - messages[0].sourceChainSelector = SOURCE_CHAIN_SELECTOR_1; - // MessageID no longer matches hash. Internal.ExecutionReportSingleChain memory executionReport = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.InvalidMessageId.selector, messages[0].messageId)); + + vm.expectRevert( + abi.encodeWithSelector( + EVM2EVMMultiOffRamp.InvalidMessageDestChainSelector.selector, messages[0].header.destChainSelector + ) + ); s_offRamp.executeSingleReport(executionReport, new uint256[](0)); } - function test_MismatchingOnRampAddress_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_3)); - // MessageID no longer matches hash. + function test_MismatchingOnRampRoot_Revert() public { + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 0); + + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + EVM2EVMMultiOffRamp.CommitReport memory commitReport = _constructCommitReport( + // Root against mismatching on ramp + Internal._hash(messages[0], ON_RAMP_ADDRESS_3) + ); + _commit(commitReport, s_latestSequenceNumber); + Internal.ExecutionReportSingleChain memory executionReport = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.InvalidMessageId.selector, messages[0].messageId)); + vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.RootNotCommitted.selector, SOURCE_CHAIN_SELECTOR_1)); s_offRamp.executeSingleReport(executionReport, new uint256[](0)); } @@ -885,7 +830,7 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, proofs: new bytes32[](0), proofFlagBits: 0, - messages: new Internal.EVM2EVMMessage[](0), + messages: new Internal.Any2EVMRampMessage[](0), offchainTokenData: new bytes[][](0) }), new uint256[](0) @@ -896,7 +841,8 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 0); vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.RootNotCommitted.selector, SOURCE_CHAIN_SELECTOR_1)); - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); s_offRamp.executeSingleReport( _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), _getGasLimitsFromMessages(messages) ); @@ -909,7 +855,8 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { abi.encodeWithSelector(EVM2EVMMultiOffRamp.ManualExecutionNotYetEnabled.selector, SOURCE_CHAIN_SELECTOR_1) ); - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); s_offRamp.executeSingleReport( _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), _getGasLimitsFromMessages(messages) ); @@ -917,43 +864,45 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { function test_NonExistingSourceChain_Revert() public { uint64 newSourceChainSelector = SOURCE_CHAIN_SELECTOR_1 + 1; - address newOnRamp = address(uint160(ON_RAMP_ADDRESS_1) + 1); + bytes memory newOnRamp = abi.encode(ON_RAMP_ADDRESS, 1); - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(newSourceChainSelector, newOnRamp); + Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(newSourceChainSelector, newOnRamp); vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.SourceChainNotEnabled.selector, newSourceChainSelector)); s_offRamp.executeSingleReport(_generateReportFromMessages(newSourceChainSelector, messages), new uint256[](0)); } function test_DisabledSourceChain_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_2, ON_RAMP_ADDRESS_2); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_2, ON_RAMP_ADDRESS_2); vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.SourceChainNotEnabled.selector, SOURCE_CHAIN_SELECTOR_2)); s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_2, messages), new uint256[](0)); } function test_TokenDataMismatch_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); Internal.ExecutionReportSingleChain memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); report.offchainTokenData[0] = new bytes[](messages[0].tokenAmounts.length + 1); vm.expectRevert( abi.encodeWithSelector( - EVM2EVMMultiOffRamp.TokenDataMismatch.selector, SOURCE_CHAIN_SELECTOR_1, messages[0].sequenceNumber + EVM2EVMMultiOffRamp.TokenDataMismatch.selector, SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber ) ); s_offRamp.executeSingleReport(report, new uint256[](0)); } function test_RouterYULCall_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); // gas limit too high, Router's external call should revert messages[0].gasLimit = 1e36; messages[0].receiver = address(new ConformingReceiver(address(s_destRouter), s_destFeeToken)); - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); Internal.ExecutionReportSingleChain memory executionReport = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); @@ -961,7 +910,7 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { vm.expectRevert( abi.encodeWithSelector( EVM2EVMMultiOffRamp.ExecutionError.selector, - messages[0].messageId, + messages[0].header.messageId, abi.encodeWithSelector(CallWithExactGas.NotEnoughGasForCall.selector) ) ); @@ -969,7 +918,8 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { } function test_RetryFailedMessageWithoutManualExecution_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); bytes memory realError1 = new bytes(2); realError1[0] = 0xbe; @@ -977,14 +927,13 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { s_reverting_receiver.setErr(realError1); messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages[0].sourceChainSelector, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.FAILURE, abi.encodeWithSelector( EVM2EVMMultiOffRamp.ReceiverError.selector, @@ -995,276 +944,24 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { vm.expectRevert( abi.encodeWithSelector( - EVM2EVMMultiOffRamp.AlreadyAttempted.selector, SOURCE_CHAIN_SELECTOR_1, messages[0].sequenceNumber + EVM2EVMMultiOffRamp.AlreadyAttempted.selector, SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber ) ); s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); } -} - -contract EVM2EVMMultiOffRamp_execute_upgrade is EVM2EVMMultiOffRampSetup { - EVM2EVMOffRampHelper internal s_prevOffRamp; - EVM2EVMOffRampHelper[] internal s_nestedPrevOffRamps; - - function setUp() public virtual override { - super.setUp(); - ICommitStore mockPrevCommitStore = new MockCommitStore(); - s_prevOffRamp = _deploySingleLaneOffRamp( - mockPrevCommitStore, s_destRouter, address(0), SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1 - ); - - s_nestedPrevOffRamps = new EVM2EVMOffRampHelper[](2); - s_nestedPrevOffRamps[0] = _deploySingleLaneOffRamp( - mockPrevCommitStore, s_destRouter, address(0), SOURCE_CHAIN_SELECTOR_2, ON_RAMP_ADDRESS_2 - ); - s_nestedPrevOffRamps[1] = _deploySingleLaneOffRamp( - mockPrevCommitStore, s_destRouter, address(s_nestedPrevOffRamps[0]), SOURCE_CHAIN_SELECTOR_2, ON_RAMP_ADDRESS_2 - ); - - EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = - new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](3); - sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ + function _constructCommitReport(bytes32 merkleRoot) internal view returns (EVM2EVMMultiOffRamp.CommitReport memory) { + EVM2EVMMultiOffRamp.MerkleRoot[] memory roots = new EVM2EVMMultiOffRamp.MerkleRoot[](1); + roots[0] = EVM2EVMMultiOffRamp.MerkleRoot({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - prevOffRamp: address(s_prevOffRamp), - onRamp: ON_RAMP_ADDRESS_1 - }); - sourceChainConfigs[1] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_2, - isEnabled: true, - prevOffRamp: address(s_nestedPrevOffRamps[1]), - onRamp: ON_RAMP_ADDRESS_2 - }); - sourceChainConfigs[2] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_3, - isEnabled: true, - prevOffRamp: address(0), - onRamp: ON_RAMP_ADDRESS_3 + interval: EVM2EVMMultiOffRamp.Interval(1, 2), + merkleRoot: merkleRoot }); - _setupMultipleOffRampsFromConfigs(sourceChainConfigs); - - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_3, 1); - } - - function test_Upgraded_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - vm.expectEmit(); - emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, - Internal.MessageExecutionState.SUCCESS, - "" - ); - - s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - } - - function test_NoPrevOffRampForChain_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - uint64 startNonceChain3 = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_3, messages[0].sender); - s_prevOffRamp.execute(_generateSingleRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - - // Nonce unchanged for chain 3 - assertEq(startNonceChain3, s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_3, messages[0].sender)); - - Internal.EVM2EVMMessage[] memory messagesChain3 = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3); - vm.expectEmit(); - emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - SOURCE_CHAIN_SELECTOR_3, - messagesChain3[0].sequenceNumber, - messagesChain3[0].messageId, - Internal.MessageExecutionState.SUCCESS, - "" - ); - - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messagesChain3), new uint256[](0) - ); - assertEq(startNonceChain3 + 1, s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_3, messagesChain3[0].sender)); - } - - function test_UpgradedSenderNoncesReadsPreviousRamp_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - uint64 startNonce = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); - - for (uint64 i = 1; i < 4; ++i) { - s_prevOffRamp.execute(_generateSingleRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - - // messages contains a single message - update for the next execution - messages[0].nonce++; - messages[0].sequenceNumber++; - messages[0].messageId = Internal._hash(messages[0], s_prevOffRamp.metadataHash()); - - assertEq(startNonce + i, s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); - } - } - - function test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_2, ON_RAMP_ADDRESS_2); - uint64 startNonce = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_2, messages[0].sender); - - for (uint64 i = 1; i < 4; ++i) { - s_nestedPrevOffRamps[0].execute( - _generateSingleRampReportFromMessages(SOURCE_CHAIN_SELECTOR_2, messages), new uint256[](0) - ); - - // messages contains a single message - update for the next execution - messages[0].nonce++; - messages[0].sequenceNumber++; - messages[0].messageId = Internal._hash(messages[0], s_nestedPrevOffRamps[0].metadataHash()); - - // Read through prev sender nonce through prevOffRamp -> prevPrevOffRamp - assertEq(startNonce + i, s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_2, messages[0].sender)); - } - } - - function test_UpgradedNonceStartsAtV1Nonce_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - uint64 startNonce = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); - s_prevOffRamp.execute(_generateSingleRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - - assertEq(startNonce + 1, s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); - - messages[0].nonce++; - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); - - vm.expectEmit(); - emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, - Internal.MessageExecutionState.SUCCESS, - "" - ); - - s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - assertEq(startNonce + 2, s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); - - messages[0].nonce++; - messages[0].sequenceNumber++; - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); - - vm.expectEmit(); - emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, - Internal.MessageExecutionState.SUCCESS, - "" - ); - - s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - assertEq(startNonce + 3, s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); - } - - function test_UpgradedNonceNewSenderStartsAtZero_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - s_prevOffRamp.execute(_generateSingleRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - - address newSender = address(1234567); - messages[0].sender = newSender; - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); - - vm.expectEmit(); - emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, - Internal.MessageExecutionState.SUCCESS, - "" - ); - - // new sender nonce in new offramp should go from 0 -> 1 - assertEq(s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, newSender), 0); - s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - assertEq(s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, newSender), 1); - } - - function test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - address newSender = address(1234567); - messages[0].sender = newSender; - messages[0].nonce = 2; - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); - - uint64 startNonce = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); - - // new offramp sees msg nonce higher than senderNonce - // it waits for previous offramp to execute - vm.expectEmit(); - emit EVM2EVMMultiOffRamp.SkippedSenderWithPreviousRampMessageInflight( - SOURCE_CHAIN_SELECTOR_1, messages[0].nonce, newSender - ); - s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - assertEq(startNonce, s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); - - messages[0].nonce = 1; - messages[0].messageId = Internal._hash(messages[0], s_prevOffRamp.metadataHash()); - - // previous offramp executes msg and increases nonce - s_prevOffRamp.execute(_generateSingleRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - assertEq(startNonce + 1, s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); - - messages[0].nonce = 2; - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); - - // new offramp is able to execute - vm.expectEmit(); - emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, - Internal.MessageExecutionState.SUCCESS, - "" - ); - - s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - assertEq(startNonce + 2, s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); - } - - function test_UpgradedWithMultiRamp_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - vm.expectEmit(); - emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, - Internal.MessageExecutionState.SUCCESS, - "" - ); - s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - - address prevOffRamp = address(s_offRamp); - _deployOffRamp(s_destRouter, s_mockRMN); - - EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = - new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - prevOffRamp: address(prevOffRamp), - onRamp: ON_RAMP_ADDRESS_1 + return EVM2EVMMultiOffRamp.CommitReport({ + priceUpdates: getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), + merkleRoots: roots }); - _setupMultipleOffRampsFromConfigs(sourceChainConfigs); - - vm.expectRevert(); - s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); - - vm.expectRevert(); - s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); } } @@ -1276,13 +973,14 @@ contract EVM2EVMMultiOffRamp_executeSingleMessage is EVM2EVMMultiOffRampSetup { } function test_executeSingleMessage_NoTokens_Success() public { - Internal.EVM2EVMMessage memory message = + Internal.Any2EVMRampMessage memory message = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length)); } function test_executeSingleMessage_WithTokens_Success() public { - Internal.EVM2EVMMessage memory message = _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)[0]; + Internal.Any2EVMRampMessage memory message = + _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)[0]; bytes[] memory offchainTokenData = new bytes[](message.tokenAmounts.length); Internal.SourceTokenData memory sourceTokenData = abi.decode(message.sourceTokenData[0], (Internal.SourceTokenData)); @@ -1291,7 +989,7 @@ contract EVM2EVMMultiOffRamp_executeSingleMessage is EVM2EVMMultiOffRampSetup { abi.encodeWithSelector( LockReleaseTokenPool.releaseOrMint.selector, Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(message.sender), + originalSender: message.sender, receiver: message.receiver, amount: message.tokenAmounts[0].amount, localToken: s_destTokenBySourceToken[message.tokenAmounts[0].token], @@ -1311,13 +1009,13 @@ contract EVM2EVMMultiOffRamp_executeSingleMessage is EVM2EVMMultiOffRampSetup { vm.startPrank(OWNER); _enableInboundMessageValidator(); vm.startPrank(address(s_offRamp)); - Internal.EVM2EVMMessage memory message = + Internal.Any2EVMRampMessage memory message = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length)); } function test_NonContract_Success() public { - Internal.EVM2EVMMessage memory message = + Internal.Any2EVMRampMessage memory message = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); message.receiver = STRANGER; s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length)); @@ -1331,7 +1029,7 @@ contract EVM2EVMMultiOffRamp_executeSingleMessage is EVM2EVMMultiOffRampSetup { emit TokenPool.Released(address(s_offRamp), STRANGER, amounts[0]); vm.expectEmit(); emit TokenPool.Minted(address(s_offRamp), STRANGER, amounts[1]); - Internal.EVM2EVMMessage memory message = + Internal.Any2EVMRampMessage memory message = _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); message.receiver = STRANGER; s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length)); @@ -1346,7 +1044,7 @@ contract EVM2EVMMultiOffRamp_executeSingleMessage is EVM2EVMMultiOffRampSetup { bytes memory errorMessage = "Random token pool issue"; - Internal.EVM2EVMMessage memory message = + Internal.Any2EVMRampMessage memory message = _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); s_maybeRevertingPool.setShouldRevert(errorMessage); @@ -1356,7 +1054,7 @@ contract EVM2EVMMultiOffRamp_executeSingleMessage is EVM2EVMMultiOffRampSetup { } function test_ZeroGasDONExecution_Revert() public { - Internal.EVM2EVMMessage memory message = + Internal.Any2EVMRampMessage memory message = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); message.gasLimit = 0; @@ -1367,7 +1065,7 @@ contract EVM2EVMMultiOffRamp_executeSingleMessage is EVM2EVMMultiOffRampSetup { function test_MessageSender_Revert() public { vm.stopPrank(); - Internal.EVM2EVMMessage memory message = + Internal.Any2EVMRampMessage memory message = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); vm.expectRevert(EVM2EVMMultiOffRamp.CanOnlySelfCall.selector); s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length)); @@ -1378,9 +1076,9 @@ contract EVM2EVMMultiOffRamp_executeSingleMessage is EVM2EVMMultiOffRampSetup { vm.startPrank(OWNER); _enableInboundMessageValidator(); vm.startPrank(address(s_offRamp)); - Internal.EVM2EVMMessage memory message = + Internal.Any2EVMRampMessage memory message = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - s_inboundMessageValidator.setMessageIdValidationState(message.messageId, true); + s_inboundMessageValidator.setMessageIdValidationState(message.header.messageId, true); vm.expectRevert( abi.encodeWithSelector( IMessageInterceptor.MessageValidationError.selector, @@ -1396,15 +1094,15 @@ contract EVM2EVMMultiOffRamp_executeSingleMessage is EVM2EVMMultiOffRampSetup { _enableInboundMessageValidator(); vm.startPrank(address(s_offRamp)); - Internal.EVM2EVMMessage memory message = + Internal.Any2EVMRampMessage memory message = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); // Setup the receiver to a non-CCIP Receiver, which will skip the Router call (but should still perform the validation) MaybeRevertMessageReceiverNo165 newReceiver = new MaybeRevertMessageReceiverNo165(true); message.receiver = address(newReceiver); - message.messageId = Internal._hash(message, s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + message.header.messageId = Internal._hash(message, ON_RAMP_ADDRESS_1); - s_inboundMessageValidator.setMessageIdValidationState(message.messageId, true); + s_inboundMessageValidator.setMessageIdValidationState(message.header.messageId, true); vm.expectRevert( abi.encodeWithSelector( IMessageInterceptor.MessageValidationError.selector, @@ -1424,25 +1122,26 @@ contract EVM2EVMMultiOffRamp_batchExecute is EVM2EVMMultiOffRampSetup { } function test_SingleReport_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages[0].sourceChainSelector, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); - uint64 nonceBefore = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); + uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); s_offRamp.batchExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[][](1)); - assertGt(s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), nonceBefore); + assertGt(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), nonceBefore); } function test_MultipleReportsSameChain_Success() public { - Internal.EVM2EVMMessage[] memory messages1 = new Internal.EVM2EVMMessage[](2); - Internal.EVM2EVMMessage[] memory messages2 = new Internal.EVM2EVMMessage[](1); + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); @@ -1454,39 +1153,39 @@ contract EVM2EVMMultiOffRamp_batchExecute is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages1[0].sourceChainSelector, - messages1[0].sequenceNumber, - messages1[0].messageId, + messages1[0].header.sourceChainSelector, + messages1[0].header.sequenceNumber, + messages1[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages1[1].sourceChainSelector, - messages1[1].sequenceNumber, - messages1[1].messageId, + messages1[1].header.sourceChainSelector, + messages1[1].header.sequenceNumber, + messages1[1].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages2[0].sourceChainSelector, - messages2[0].sequenceNumber, - messages2[0].messageId, + messages2[0].header.sourceChainSelector, + messages2[0].header.sequenceNumber, + messages2[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); - uint64 nonceBefore = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages1[0].sender); + uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages1[0].sender); s_offRamp.batchExecute(reports, new uint256[][](2)); - assertGt(s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages1[0].sender), nonceBefore); + assertGt(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages1[0].sender), nonceBefore); } function test_MultipleReportsDifferentChains_Success() public { - Internal.EVM2EVMMessage[] memory messages1 = new Internal.EVM2EVMMessage[](2); - Internal.EVM2EVMMessage[] memory messages2 = new Internal.EVM2EVMMessage[](1); + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); @@ -1498,35 +1197,35 @@ contract EVM2EVMMultiOffRamp_batchExecute is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages1[0].sourceChainSelector, - messages1[0].sequenceNumber, - messages1[0].messageId, + messages1[0].header.sourceChainSelector, + messages1[0].header.sequenceNumber, + messages1[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages1[1].sourceChainSelector, - messages1[1].sequenceNumber, - messages1[1].messageId, + messages1[1].header.sourceChainSelector, + messages1[1].header.sequenceNumber, + messages1[1].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages2[0].sourceChainSelector, - messages2[0].sequenceNumber, - messages2[0].messageId, + messages2[0].header.sourceChainSelector, + messages2[0].header.sequenceNumber, + messages2[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); s_offRamp.batchExecute(reports, new uint256[][](2)); - uint64 nonceChain1 = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages1[0].sender); - uint64 nonceChain3 = s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_3, messages2[0].sender); + uint64 nonceChain1 = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages1[0].sender); + uint64 nonceChain3 = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, messages2[0].sender); assertTrue(nonceChain1 != nonceChain3); assertGt(nonceChain1, 0); @@ -1534,7 +1233,8 @@ contract EVM2EVMMultiOffRamp_batchExecute is EVM2EVMMultiOffRampSetup { } function test_MultipleReportsSkipDuplicate_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); @@ -1542,15 +1242,15 @@ contract EVM2EVMMultiOffRamp_batchExecute is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages[0].sourceChainSelector, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); vm.expectEmit(); - emit EVM2EVMMultiOffRamp.SkippedAlreadyExecutedMessage(SOURCE_CHAIN_SELECTOR_1, messages[0].sequenceNumber); + emit EVM2EVMMultiOffRamp.SkippedAlreadyExecutedMessage(SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber); s_offRamp.batchExecute(reports, new uint256[][](2)); } @@ -1581,8 +1281,8 @@ contract EVM2EVMMultiOffRamp_batchExecute is EVM2EVMMultiOffRampSetup { } function test_OutOfBoundsGasLimitsAccess_Revert() public { - Internal.EVM2EVMMessage[] memory messages1 = new Internal.EVM2EVMMessage[](2); - Internal.EVM2EVMMessage[] memory messages2 = new Internal.EVM2EVMMessage[](1); + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); @@ -1607,10 +1307,10 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { } function test_manuallyExecute_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); s_offRamp.batchExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[][](1)); s_reverting_receiver.setRevert(false); @@ -1618,8 +1318,8 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -1630,10 +1330,10 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { } function test_manuallyExecute_WithGasOverride_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); s_offRamp.batchExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[][](1)); s_reverting_receiver.setRevert(false); @@ -1641,8 +1341,8 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -1655,20 +1355,22 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { } function test_manuallyExecute_DoesNotRevertIfUntouched_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); - assertEq(messages[0].nonce - 1, s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); + assertEq( + messages[0].header.nonce - 1, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender) + ); s_reverting_receiver.setRevert(true); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.FAILURE, abi.encodeWithSelector( EVM2EVMMultiOffRamp.ReceiverError.selector, @@ -1681,25 +1383,25 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); - assertEq(messages[0].nonce, s_offRamp.getSenderNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); + assertEq( + messages[0].header.nonce, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender) + ); } function test_manuallyExecute_WithMultiReportGasOverride_Success() public { - Internal.EVM2EVMMessage[] memory messages1 = new Internal.EVM2EVMMessage[](3); - Internal.EVM2EVMMessage[] memory messages2 = new Internal.EVM2EVMMessage[](2); + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](3); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](2); for (uint64 i = 0; i < 3; ++i) { messages1[i] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, i + 1); messages1[i].receiver = address(s_reverting_receiver); - messages1[i].messageId = - Internal._hash(messages1[i], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages1[i].header.messageId = Internal._hash(messages1[i], ON_RAMP_ADDRESS_1); } for (uint64 i = 0; i < 2; ++i) { messages2[i] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, i + 1); messages2[i].receiver = address(s_reverting_receiver); - messages2[i].messageId = - Internal._hash(messages2[i], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3)); + messages2[i].header.messageId = Internal._hash(messages2[i], ON_RAMP_ADDRESS_3); } Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); @@ -1718,8 +1420,8 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages1[i].sequenceNumber, - messages1[i].messageId, + messages1[i].header.sequenceNumber, + messages1[i].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -1731,8 +1433,8 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_3, - messages2[i].sequenceNumber, - messages2[i].messageId, + messages2[i].header.sequenceNumber, + messages2[i].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -1744,20 +1446,19 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { } function test_manuallyExecute_WithPartialMessages_Success() public { - Internal.EVM2EVMMessage[] memory messages = new Internal.EVM2EVMMessage[](3); + Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](3); for (uint64 i = 0; i < 3; ++i) { messages[i] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, i + 1); } messages[1].receiver = address(s_reverting_receiver); - messages[1].messageId = - Internal._hash(messages[1], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[1].header.messageId = Internal._hash(messages[1], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -1765,8 +1466,8 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[1].sequenceNumber, - messages[1].messageId, + messages[1].header.sequenceNumber, + messages[1].header.messageId, Internal.MessageExecutionState.FAILURE, abi.encodeWithSelector( EVM2EVMMultiOffRamp.ReceiverError.selector, @@ -1777,8 +1478,8 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[2].sequenceNumber, - messages[2].messageId, + messages[2].header.sequenceNumber, + messages[2].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -1788,7 +1489,7 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { s_reverting_receiver.setRevert(false); // Only the 2nd message reverted - Internal.EVM2EVMMessage[] memory newMessages = new Internal.EVM2EVMMessage[](1); + Internal.Any2EVMRampMessage[] memory newMessages = new Internal.Any2EVMRampMessage[](1); newMessages[0] = messages[1]; uint256[][] memory gasLimitOverrides = new uint256[][](1); @@ -1798,8 +1499,8 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - newMessages[0].sequenceNumber, - newMessages[0].messageId, + newMessages[0].header.sequenceNumber, + newMessages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -1808,17 +1509,17 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { } function test_manuallyExecute_LowGasLimit_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); messages[0].gasLimit = 1; messages[0].receiver = address(new ConformingReceiver(address(s_destRouter), s_destFeeToken)); - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.FAILURE, abi.encodeWithSelector(EVM2EVMMultiOffRamp.ReceiverError.selector, "") ); @@ -1834,8 +1535,8 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -1845,7 +1546,8 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { // Reverts function test_manuallyExecute_ForkedChain_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); Internal.ExecutionReportSingleChain[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); @@ -1861,7 +1563,7 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { } function test_ManualExecGasLimitMismatchSingleReport_Revert() public { - Internal.EVM2EVMMessage[] memory messages = new Internal.EVM2EVMMessage[](2); + Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](2); messages[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); messages[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); @@ -1892,8 +1594,8 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { } function test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() public { - Internal.EVM2EVMMessage[] memory messages1 = new Internal.EVM2EVMMessage[](2); - Internal.EVM2EVMMessage[] memory messages2 = new Internal.EVM2EVMMessage[](1); + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); @@ -1935,7 +1637,8 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { } function test_ManualExecInvalidGasLimit_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); uint256[][] memory gasLimitOverrides = new uint256[][](1); gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); @@ -1950,11 +1653,11 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { } function test_manuallyExecute_FailedTx_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); s_offRamp.batchExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[][](1)); @@ -1966,7 +1669,7 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { vm.expectRevert( abi.encodeWithSelector( EVM2EVMMultiOffRamp.ExecutionError.selector, - messages[0].messageId, + messages[0].header.messageId, abi.encodeWithSelector( EVM2EVMMultiOffRamp.ReceiverError.selector, abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, bytes("")) @@ -1988,10 +1691,10 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { // For this test any message will be flagged as correct by the // commitStore. In a real scenario the abuser would have to actually // send the message that they want to replay. - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); messages[0].tokenAmounts = new Client.EVMTokenAmount[](1); messages[0].tokenAmounts[0] = Client.EVMTokenAmount({token: s_sourceFeeToken, amount: tokenAmount}); - messages[0].receiver = address(receiver); messages[0].sourceTokenData = new bytes[](1); messages[0].sourceTokenData[0] = abi.encode( Internal.SourceTokenData({ @@ -2001,8 +1704,9 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { }) ); - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + messages[0].receiver = address(receiver); + + messages[0].header.messageId = Internal._hash(messages[0], ON_RAMP_ADDRESS_1); Internal.ExecutionReportSingleChain memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); @@ -2017,14 +1721,16 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { // This means the first tx is marked `FAILURE` with the error message of the second tx. vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages[0].sourceChainSelector, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.FAILURE, abi.encodeWithSelector( EVM2EVMMultiOffRamp.ReceiverError.selector, abi.encodeWithSelector( - EVM2EVMMultiOffRamp.AlreadyExecuted.selector, messages[0].sourceChainSelector, messages[0].sequenceNumber + EVM2EVMMultiOffRamp.AlreadyExecuted.selector, + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber ) ) ); @@ -2045,15 +1751,16 @@ contract EVM2EVMMultiOffRamp_execute is EVM2EVMMultiOffRampSetup { // Asserts that execute completes function test_SingleReport_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); Internal.ExecutionReportSingleChain[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -2067,8 +1774,8 @@ contract EVM2EVMMultiOffRamp_execute is EVM2EVMMultiOffRampSetup { } function test_MultipleReports_Success() public { - Internal.EVM2EVMMessage[] memory messages1 = new Internal.EVM2EVMMessage[](2); - Internal.EVM2EVMMessage[] memory messages2 = new Internal.EVM2EVMMessage[](1); + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); @@ -2080,27 +1787,27 @@ contract EVM2EVMMultiOffRamp_execute is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages1[0].sourceChainSelector, - messages1[0].sequenceNumber, - messages1[0].messageId, + messages1[0].header.sourceChainSelector, + messages1[0].header.sequenceNumber, + messages1[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages1[1].sourceChainSelector, - messages1[1].sequenceNumber, - messages1[1].messageId, + messages1[1].header.sourceChainSelector, + messages1[1].header.sequenceNumber, + messages1[1].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages2[0].sourceChainSelector, - messages2[0].sequenceNumber, - messages2[0].messageId, + messages2[0].header.sourceChainSelector, + messages2[0].header.sequenceNumber, + messages2[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -2116,7 +1823,7 @@ contract EVM2EVMMultiOffRamp_execute is EVM2EVMMultiOffRampSetup { function test_LargeBatch_Success() public { Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](10); for (uint64 i = 0; i < reports.length; ++i) { - Internal.EVM2EVMMessage[] memory messages = new Internal.EVM2EVMMessage[](3); + Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](3); messages[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1 + i * 3); messages[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2 + i * 3); messages[2] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3 + i * 3); @@ -2128,9 +1835,9 @@ contract EVM2EVMMultiOffRamp_execute is EVM2EVMMultiOffRampSetup { for (uint64 j = 0; j < reports[i].messages.length; ++j) { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - reports[i].messages[j].sourceChainSelector, - reports[i].messages[j].sequenceNumber, - reports[i].messages[j].messageId, + reports[i].messages[j].header.sourceChainSelector, + reports[i].messages[j].header.sequenceNumber, + reports[i].messages[j].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -2148,8 +1855,8 @@ contract EVM2EVMMultiOffRamp_execute is EVM2EVMMultiOffRampSetup { function test_MultipleReportsWithPartialValidationFailures_Success() public { _enableInboundMessageValidator(); - Internal.EVM2EVMMessage[] memory messages1 = new Internal.EVM2EVMMessage[](2); - Internal.EVM2EVMMessage[] memory messages2 = new Internal.EVM2EVMMessage[](1); + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); @@ -2159,14 +1866,14 @@ contract EVM2EVMMultiOffRamp_execute is EVM2EVMMultiOffRampSetup { reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages2); - s_inboundMessageValidator.setMessageIdValidationState(messages1[0].messageId, true); - s_inboundMessageValidator.setMessageIdValidationState(messages2[0].messageId, true); + s_inboundMessageValidator.setMessageIdValidationState(messages1[0].header.messageId, true); + s_inboundMessageValidator.setMessageIdValidationState(messages2[0].header.messageId, true); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages1[0].sourceChainSelector, - messages1[0].sequenceNumber, - messages1[0].messageId, + messages1[0].header.sourceChainSelector, + messages1[0].header.sequenceNumber, + messages1[0].header.messageId, Internal.MessageExecutionState.FAILURE, abi.encodeWithSelector( IMessageInterceptor.MessageValidationError.selector, @@ -2176,18 +1883,18 @@ contract EVM2EVMMultiOffRamp_execute is EVM2EVMMultiOffRampSetup { vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages1[1].sourceChainSelector, - messages1[1].sequenceNumber, - messages1[1].messageId, + messages1[1].header.sourceChainSelector, + messages1[1].header.sequenceNumber, + messages1[1].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); vm.expectEmit(); emit EVM2EVMMultiOffRamp.ExecutionStateChanged( - messages2[0].sourceChainSelector, - messages2[0].sequenceNumber, - messages2[0].messageId, + messages2[0].header.sourceChainSelector, + messages2[0].header.sequenceNumber, + messages2[0].header.messageId, Internal.MessageExecutionState.FAILURE, abi.encodeWithSelector( IMessageInterceptor.MessageValidationError.selector, @@ -2208,7 +1915,8 @@ contract EVM2EVMMultiOffRamp_execute is EVM2EVMMultiOffRampSetup { function test_UnauthorizedTransmitter_Revert() public { bytes32[3] memory reportContext = [s_configDigestExec, s_configDigestExec, s_configDigestExec]; - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); Internal.ExecutionReportSingleChain[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); @@ -2220,7 +1928,8 @@ contract EVM2EVMMultiOffRamp_execute is EVM2EVMMultiOffRampSetup { _redeployOffRampWithNoOCRConfigs(); s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); Internal.ExecutionReportSingleChain[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); @@ -2246,7 +1955,8 @@ contract EVM2EVMMultiOffRamp_execute is EVM2EVMMultiOffRampSetup { }); s_offRamp.setOCR3Configs(ocrConfigs); - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); Internal.ExecutionReportSingleChain[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); @@ -2274,7 +1984,8 @@ contract EVM2EVMMultiOffRamp_execute is EVM2EVMMultiOffRampSetup { }); s_offRamp.setOCR3Configs(ocrConfigs); - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); Internal.ExecutionReportSingleChain[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); @@ -2303,7 +2014,8 @@ contract EVM2EVMMultiOffRamp_execute is EVM2EVMMultiOffRampSetup { function test_NonArray_Revert() public { bytes32[3] memory reportContext = [s_configDigestExec, s_configDigestExec, s_configDigestExec]; - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); Internal.ExecutionReportSingleChain memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); vm.startPrank(s_validTransmitters[0]); @@ -2475,7 +2187,7 @@ contract EVM2EVMMultiOffRamp_trialExecute is EVM2EVMMultiOffRampSetup { amounts[0] = 1000; amounts[1] = 50; - Internal.EVM2EVMMessage memory message = + Internal.Any2EVMRampMessage memory message = _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); IERC20 dstToken0 = IERC20(s_destTokens[0]); uint256 startingBalance = dstToken0.balanceOf(message.receiver); @@ -2499,7 +2211,7 @@ contract EVM2EVMMultiOffRamp_trialExecute is EVM2EVMMultiOffRampSetup { bytes memory errorMessage = "Random token pool issue"; - Internal.EVM2EVMMessage memory message = + Internal.Any2EVMRampMessage memory message = _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); s_maybeRevertingPool.setShouldRevert(errorMessage); @@ -2519,7 +2231,7 @@ contract EVM2EVMMultiOffRamp_trialExecute is EVM2EVMMultiOffRampSetup { bytes memory errorMessage = abi.encodeWithSelector(RateLimiter.BucketOverfilled.selector); - Internal.EVM2EVMMessage memory message = + Internal.Any2EVMRampMessage memory message = _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); s_maybeRevertingPool.setShouldRevert(errorMessage); @@ -2533,7 +2245,7 @@ contract EVM2EVMMultiOffRamp_trialExecute is EVM2EVMMultiOffRampSetup { function test_TokenPoolIsNotAContract_Success() public { uint256[] memory amounts = new uint256[](2); amounts[0] = 10000; - Internal.EVM2EVMMessage memory message = + Internal.Any2EVMRampMessage memory message = _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); // Happy path, pool is correct @@ -2553,12 +2265,7 @@ contract EVM2EVMMultiOffRamp_trialExecute is EVM2EVMMultiOffRampSetup { }) ); - message.messageId = Internal._hash( - message, - keccak256( - abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR_1, DEST_CHAIN_SELECTOR, ON_RAMP_ADDRESS_1) - ) - ); + message.header.messageId = Internal._hash(message, ON_RAMP_ADDRESS_1); // Unhappy path, no revert but marked as failed. (newState, err) = s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length)); @@ -2576,12 +2283,7 @@ contract EVM2EVMMultiOffRamp_trialExecute is EVM2EVMMultiOffRampSetup { }) ); - message.messageId = Internal._hash( - message, - keccak256( - abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR_1, DEST_CHAIN_SELECTOR, ON_RAMP_ADDRESS_1) - ) - ); + message.header.messageId = Internal._hash(message, ON_RAMP_ADDRESS_1); (newState, err) = s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length)); @@ -2976,19 +2678,13 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - prevOffRamp: address(0), - onRamp: ON_RAMP_ADDRESS_1 - }); - - EVM2EVMMultiOffRamp.SourceChainConfig memory expectedSourceChainConfig = EVM2EVMMultiOffRamp.SourceChainConfig({ - isEnabled: true, - minSeqNr: 1, - prevOffRamp: address(0), onRamp: ON_RAMP_ADDRESS_1, - metadataHash: s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) + isEnabled: true }); + EVM2EVMMultiOffRamp.SourceChainConfig memory expectedSourceChainConfig = + EVM2EVMMultiOffRamp.SourceChainConfig({isEnabled: true, minSeqNr: 1, onRamp: ON_RAMP_ADDRESS_1}); + vm.expectEmit(); emit EVM2EVMMultiOffRamp.SourceChainSelectorAdded(SOURCE_CHAIN_SELECTOR_1); @@ -3005,21 +2701,15 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - prevOffRamp: address(0), - onRamp: ON_RAMP_ADDRESS_1 + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true }); s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); sourceChainConfigs[0].isEnabled = false; - EVM2EVMMultiOffRamp.SourceChainConfig memory expectedSourceChainConfig = EVM2EVMMultiOffRamp.SourceChainConfig({ - isEnabled: false, - minSeqNr: 1, - prevOffRamp: address(0), - onRamp: sourceChainConfigs[0].onRamp, - metadataHash: s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, sourceChainConfigs[0].onRamp) - }); + EVM2EVMMultiOffRamp.SourceChainConfig memory expectedSourceChainConfig = + EVM2EVMMultiOffRamp.SourceChainConfig({isEnabled: false, minSeqNr: 1, onRamp: ON_RAMP_ADDRESS_1}); vm.expectEmit(); emit EVM2EVMMultiOffRamp.SourceChainConfigSet(SOURCE_CHAIN_SELECTOR_1, expectedSourceChainConfig); @@ -3043,21 +2733,18 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](3); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - prevOffRamp: address(0), - onRamp: ON_RAMP_ADDRESS_1 + onRamp: abi.encode(ON_RAMP_ADDRESS_1, 0), + isEnabled: true }); sourceChainConfigs[1] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1 + 1, - isEnabled: false, - prevOffRamp: address(999), - onRamp: address(uint160(ON_RAMP_ADDRESS_1) + 7) + onRamp: abi.encode(ON_RAMP_ADDRESS_1, 1), + isEnabled: false }); sourceChainConfigs[2] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1 + 2, - isEnabled: true, - prevOffRamp: address(1000), - onRamp: address(uint160(ON_RAMP_ADDRESS_1) + 42) + onRamp: abi.encode(ON_RAMP_ADDRESS_1, 2), + isEnabled: true }); EVM2EVMMultiOffRamp.SourceChainConfig[] memory expectedSourceChainConfigs = @@ -3066,9 +2753,7 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam expectedSourceChainConfigs[i] = EVM2EVMMultiOffRamp.SourceChainConfig({ isEnabled: sourceChainConfigs[i].isEnabled, minSeqNr: 1, - prevOffRamp: sourceChainConfigs[i].prevOffRamp, - onRamp: sourceChainConfigs[i].onRamp, - metadataHash: s_offRamp.metadataHash(sourceChainConfigs[i].sourceChainSelector, sourceChainConfigs[i].onRamp) + onRamp: abi.encode(ON_RAMP_ADDRESS_1, i) }); vm.expectEmit(); @@ -3082,15 +2767,10 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - // uint64[] memory resultSourceChainSelectors = s_offRamp.getSourceChainSelectors(); - // assertEq(resultSourceChainSelectors.length, 3); - for (uint256 i = 0; i < 3; ++i) { _assertSourceChainConfigEquality( s_offRamp.getSourceChainConfig(sourceChainConfigs[i].sourceChainSelector), expectedSourceChainConfigs[i] ); - - // assertEq(resultSourceChainSelectors[i], sourceChainConfigs[i].sourceChainSelector); } } @@ -3099,32 +2779,27 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam ) public { // Skip invalid inputs vm.assume(sourceChainConfigArgs.sourceChainSelector != 0); - vm.assume(sourceChainConfigArgs.onRamp != address(0)); + vm.assume(sourceChainConfigArgs.onRamp.length != 0); EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](2); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - prevOffRamp: address(42), - onRamp: ON_RAMP_ADDRESS_1 + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true }); sourceChainConfigs[1] = sourceChainConfigArgs; // Handle cases when an update occurs - bool isNewChain = true; - if (sourceChainConfigs[1].sourceChainSelector == SOURCE_CHAIN_SELECTOR_1) { - sourceChainConfigs[1].prevOffRamp = sourceChainConfigs[0].prevOffRamp; + bool isNewChain = sourceChainConfigs[1].sourceChainSelector != SOURCE_CHAIN_SELECTOR_1; + if (!isNewChain) { sourceChainConfigs[1].onRamp = sourceChainConfigs[0].onRamp; - isNewChain = false; } EVM2EVMMultiOffRamp.SourceChainConfig memory expectedSourceChainConfig = EVM2EVMMultiOffRamp.SourceChainConfig({ isEnabled: sourceChainConfigArgs.isEnabled, minSeqNr: 1, - prevOffRamp: sourceChainConfigArgs.prevOffRamp, - onRamp: sourceChainConfigArgs.onRamp, - metadataHash: s_offRamp.metadataHash(sourceChainConfigArgs.sourceChainSelector, sourceChainConfigArgs.onRamp) + onRamp: sourceChainConfigArgs.onRamp }); if (isNewChain) { @@ -3149,9 +2824,8 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - prevOffRamp: address(0), - onRamp: address(0) + onRamp: new bytes(0), + isEnabled: true }); vm.expectRevert(EVM2EVMMultiOffRamp.ZeroAddressNotAllowed.selector); @@ -3161,12 +2835,8 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam function test_ZeroSourceChainSelector_Revert() public { EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ - sourceChainSelector: 0, - isEnabled: true, - prevOffRamp: address(0), - onRamp: ON_RAMP_ADDRESS_1 - }); + sourceChainConfigs[0] = + EVM2EVMMultiOffRamp.SourceChainConfigArgs({sourceChainSelector: 0, onRamp: ON_RAMP_ADDRESS_1, isEnabled: true}); vm.expectRevert(EVM2EVMMultiOffRamp.ZeroChainSelectorNotAllowed.selector); s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); @@ -3177,51 +2847,13 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - prevOffRamp: address(0), - onRamp: ON_RAMP_ADDRESS_1 - }); - - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - - sourceChainConfigs[0].onRamp = address(uint160(sourceChainConfigs[0].onRamp) + 1); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.InvalidStaticConfig.selector, SOURCE_CHAIN_SELECTOR_1)); - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - } - - function test_ReplaceExistingChainPrevOffRamp_Revert() public { - EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = - new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - prevOffRamp: address(0), - onRamp: ON_RAMP_ADDRESS_1 - }); - - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - - sourceChainConfigs[0].prevOffRamp = address(uint160(sourceChainConfigs[0].prevOffRamp) + 1); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.InvalidStaticConfig.selector, SOURCE_CHAIN_SELECTOR_1)); - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - } - - function test_ReplaceExistingChainOnRampAndPrevOffRamp_Revert() public { - EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = - new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - prevOffRamp: address(0), - onRamp: ON_RAMP_ADDRESS_1 + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true }); s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - sourceChainConfigs[0].onRamp = address(uint160(sourceChainConfigs[0].onRamp) + 1); - sourceChainConfigs[0].prevOffRamp = address(uint160(sourceChainConfigs[0].prevOffRamp) + 1); + sourceChainConfigs[0].onRamp = ON_RAMP_ADDRESS_2; vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.InvalidStaticConfig.selector, SOURCE_CHAIN_SELECTOR_1)); s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); @@ -3669,7 +3301,7 @@ contract EVM2EVMMultiOffRamp_resetUnblessedRoots is EVM2EVMMultiOffRampSetup { function setUp() public virtual override { super.setUp(); _setupRealRMN(); - _deployOffRamp(s_destRouter, s_realRMN); + _deployOffRamp(s_destRouter, s_realRMN, s_inboundNonceManager); _setupMultipleOffRamps(); } @@ -3735,7 +3367,7 @@ contract EVM2EVMMultiOffRamp_verify is EVM2EVMMultiOffRampSetup { function setUp() public virtual override { super.setUp(); _setupRealRMN(); - _deployOffRamp(s_destRouter, s_realRMN); + _deployOffRamp(s_destRouter, s_realRMN, s_inboundNonceManager); _setupMultipleOffRamps(); } diff --git a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRampSetup.t.sol b/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRampSetup.t.sol index b53fb503fb..7355d6a072 100644 --- a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRampSetup.t.sol +++ b/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRampSetup.t.sol @@ -8,11 +8,11 @@ import {ICommitStore} from "../../interfaces/ICommitStore.sol"; import {IRMN} from "../../interfaces/IRMN.sol"; import {AuthorizedCallers} from "../../../shared/access/AuthorizedCallers.sol"; +import {NonceManager} from "../../NonceManager.sol"; import {RMN} from "../../RMN.sol"; import {Router} from "../../Router.sol"; import {Client} from "../../libraries/Client.sol"; import {Internal} from "../../libraries/Internal.sol"; - import {MultiOCR3Base} from "../../ocr/MultiOCR3Base.sol"; import {EVM2EVMMultiOffRamp} from "../../offRamp/EVM2EVMMultiOffRamp.sol"; import {EVM2EVMOffRamp} from "../../offRamp/EVM2EVMOffRamp.sol"; @@ -22,7 +22,6 @@ import {TokenSetup} from "../TokenSetup.t.sol"; import {EVM2EVMMultiOffRampHelper} from "../helpers/EVM2EVMMultiOffRampHelper.sol"; import {EVM2EVMOffRampHelper} from "../helpers/EVM2EVMOffRampHelper.sol"; import {MaybeRevertingBurnMintTokenPool} from "../helpers/MaybeRevertingBurnMintTokenPool.sol"; - import {MessageInterceptorHelper} from "../helpers/MessageInterceptorHelper.sol"; import {MaybeRevertMessageReceiver} from "../helpers/receivers/MaybeRevertMessageReceiver.sol"; import {MockCommitStore} from "../mocks/MockCommitStore.sol"; @@ -36,9 +35,9 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba uint64 internal constant SOURCE_CHAIN_SELECTOR_2 = 6433500567565415381; uint64 internal constant SOURCE_CHAIN_SELECTOR_3 = 4051577828743386545; - address internal constant ON_RAMP_ADDRESS_1 = ON_RAMP_ADDRESS; - address internal constant ON_RAMP_ADDRESS_2 = 0xaA3f843Cf8E33B1F02dd28303b6bD87B1aBF8AE4; - address internal constant ON_RAMP_ADDRESS_3 = 0x71830C37Cb193e820de488Da111cfbFcC680a1b9; + bytes internal constant ON_RAMP_ADDRESS_1 = abi.encode(ON_RAMP_ADDRESS); + bytes internal constant ON_RAMP_ADDRESS_2 = abi.encode(0xaA3f843Cf8E33B1F02dd28303b6bD87B1aBF8AE4); + bytes internal constant ON_RAMP_ADDRESS_3 = abi.encode(0x71830C37Cb193e820de488Da111cfbFcC680a1b9); address internal constant BLESS_VOTE_ADDR = address(8888); @@ -50,6 +49,7 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba EVM2EVMMultiOffRampHelper internal s_offRamp; MessageInterceptorHelper internal s_inboundMessageValidator; + NonceManager internal s_inboundNonceManager; RMN internal s_realRMN; address internal s_sourceTokenPool = makeAddr("sourceTokenPool"); @@ -71,11 +71,12 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba s_reverting_receiver = new MaybeRevertMessageReceiver(true); s_maybeRevertingPool = MaybeRevertingBurnMintTokenPool(s_destPoolByToken[s_destTokens[1]]); + s_inboundNonceManager = new NonceManager(new address[](0)); - _deployOffRamp(s_destRouter, s_mockRMN); + _deployOffRamp(s_destRouter, s_mockRMN, s_inboundNonceManager); } - function _deployOffRamp(Router router, IRMN rmnProxy) internal { + function _deployOffRamp(Router router, IRMN rmnProxy, NonceManager nonceManager) internal { EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](0); @@ -83,7 +84,8 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba EVM2EVMMultiOffRamp.StaticConfig({ chainSelector: DEST_CHAIN_SELECTOR, rmnProxy: address(rmnProxy), - tokenAdminRegistry: address(s_tokenAdminRegistry) + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(nonceManager) }), sourceChainConfigs ); @@ -112,6 +114,12 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba s_offRamp.setDynamicConfig(_generateDynamicMultiOffRampConfig(address(router), address(s_priceRegistry))); s_offRamp.setOCR3Configs(ocrConfigs); + address[] memory authorizedCallers = new address[](1); + authorizedCallers[0] = address(s_offRamp); + NonceManager(nonceManager).applyAuthorizedCallerUpdates( + AuthorizedCallers.AuthorizedCallerArgs({addedCallers: authorizedCallers, removedCallers: new address[](0)}) + ); + address[] memory priceUpdaters = new address[](1); priceUpdaters[0] = address(s_offRamp); s_priceRegistry.applyAuthorizedCallerUpdates( @@ -167,21 +175,18 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](3); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - prevOffRamp: address(0), - onRamp: ON_RAMP_ADDRESS_1 + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true }); sourceChainConfigs[1] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_2, - isEnabled: false, - prevOffRamp: address(0), - onRamp: ON_RAMP_ADDRESS_2 + onRamp: ON_RAMP_ADDRESS_2, + isEnabled: false }); sourceChainConfigs[2] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_3, - isEnabled: true, - prevOffRamp: address(0), - onRamp: ON_RAMP_ADDRESS_3 + onRamp: ON_RAMP_ADDRESS_3, + isEnabled: true }); _setupMultipleOffRampsFromConfigs(sourceChainConfigs); } @@ -191,17 +196,17 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba { s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](sourceChainConfigs.length); - Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](2 * onRampUpdates.length); + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](0); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](2 * sourceChainConfigs.length); for (uint256 i = 0; i < sourceChainConfigs.length; ++i) { uint64 sourceChainSelector = sourceChainConfigs[i].sourceChainSelector; - onRampUpdates[i] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: sourceChainConfigs[i].onRamp}); - offRampUpdates[2 * i] = Router.OffRamp({sourceChainSelector: sourceChainSelector, offRamp: address(s_offRamp)}); - offRampUpdates[2 * i + 1] = - Router.OffRamp({sourceChainSelector: sourceChainSelector, offRamp: address(sourceChainConfigs[i].prevOffRamp)}); + offRampUpdates[2 * i + 1] = Router.OffRamp({ + sourceChainSelector: sourceChainSelector, + offRamp: s_inboundNonceManager.getPreviousRamps(sourceChainSelector).prevOffRamp + }); } s_destRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); @@ -236,7 +241,7 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba }); } - function _convertToGeneralMessage(Internal.EVM2EVMMessage memory original) + function _convertToGeneralMessage(Internal.Any2EVMRampMessage memory original) internal view returns (Client.Any2EVMMessage memory message) @@ -255,8 +260,8 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba } return Client.Any2EVMMessage({ - messageId: original.messageId, - sourceChainSelector: original.sourceChainSelector, + messageId: original.header.messageId, + sourceChainSelector: original.header.sourceChainSelector, sender: abi.encode(original.sender), data: original.data, destTokenAmounts: destTokenAmounts @@ -265,18 +270,18 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba function _generateAny2EVMMessageNoTokens( uint64 sourceChainSelector, - address onRamp, + bytes memory onRamp, uint64 sequenceNumber - ) internal view returns (Internal.EVM2EVMMessage memory) { + ) internal view returns (Internal.Any2EVMRampMessage memory) { return _generateAny2EVMMessage(sourceChainSelector, onRamp, sequenceNumber, new Client.EVMTokenAmount[](0), false); } function _generateAny2EVMMessageWithTokens( uint64 sourceChainSelector, - address onRamp, + bytes memory onRamp, uint64 sequenceNumber, uint256[] memory amounts - ) internal view returns (Internal.EVM2EVMMessage memory) { + ) internal view returns (Internal.Any2EVMRampMessage memory) { Client.EVMTokenAmount[] memory tokenAmounts = getCastedSourceEVMTokenAmountsWithZeroAmounts(); for (uint256 i = 0; i < tokenAmounts.length; ++i) { tokenAmounts[i].amount = amounts[i]; @@ -286,26 +291,26 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba function _generateAny2EVMMessage( uint64 sourceChainSelector, - address onRamp, + bytes memory onRamp, uint64 sequenceNumber, Client.EVMTokenAmount[] memory tokenAmounts, bool allowOutOfOrderExecution - ) internal view returns (Internal.EVM2EVMMessage memory) { + ) internal view returns (Internal.Any2EVMRampMessage memory) { bytes memory data = abi.encode(0); - Internal.EVM2EVMMessage memory message = Internal.EVM2EVMMessage({ - sequenceNumber: sequenceNumber, - sender: OWNER, - nonce: allowOutOfOrderExecution ? 0 : sequenceNumber, - gasLimit: GAS_LIMIT, - strict: false, - sourceChainSelector: sourceChainSelector, - receiver: address(s_receiver), + Internal.Any2EVMRampMessage memory message = Internal.Any2EVMRampMessage({ + header: Internal.RampMessageHeader({ + messageId: "", + sourceChainSelector: sourceChainSelector, + destChainSelector: DEST_CHAIN_SELECTOR, + sequenceNumber: sequenceNumber, + nonce: allowOutOfOrderExecution ? 0 : sequenceNumber + }), + sender: abi.encode(OWNER), data: data, + receiver: address(s_receiver), tokenAmounts: tokenAmounts, sourceTokenData: new bytes[](tokenAmounts.length), - feeToken: s_destFeeToken, - feeTokenAmount: uint256(0), - messageId: "" + gasLimit: GAS_LIMIT }); // Correctly set the TokenDataPayload for each token. Tokens have to be set up in the TokenSetup. @@ -319,27 +324,25 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba ); } - message.messageId = Internal._hash( - message, keccak256(abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, sourceChainSelector, DEST_CHAIN_SELECTOR, onRamp)) - ); + message.header.messageId = Internal._hash(message, onRamp); return message; } function _generateSingleBasicMessage( uint64 sourceChainSelector, - address onRamp - ) internal view returns (Internal.EVM2EVMMessage[] memory) { - Internal.EVM2EVMMessage[] memory messages = new Internal.EVM2EVMMessage[](1); + bytes memory onRamp + ) internal view returns (Internal.Any2EVMRampMessage[] memory) { + Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](1); messages[0] = _generateAny2EVMMessageNoTokens(sourceChainSelector, onRamp, 1); return messages; } function _generateMessagesWithTokens( uint64 sourceChainSelector, - address onRamp - ) internal view returns (Internal.EVM2EVMMessage[] memory) { - Internal.EVM2EVMMessage[] memory messages = new Internal.EVM2EVMMessage[](2); + bytes memory onRamp + ) internal view returns (Internal.Any2EVMRampMessage[] memory) { + Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](2); Client.EVMTokenAmount[] memory tokenAmounts = getCastedSourceEVMTokenAmountsWithZeroAmounts(); tokenAmounts[0].amount = 1e18; tokenAmounts[1].amount = 5e18; @@ -349,24 +352,9 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba return messages; } - function _generateSingleRampReportFromMessages( - uint64 sourceChainSelector, - Internal.EVM2EVMMessage[] memory messages - ) internal pure returns (Internal.ExecutionReport memory) { - Internal.ExecutionReportSingleChain memory singleChainReport = - _generateReportFromMessages(sourceChainSelector, messages); - - return Internal.ExecutionReport({ - proofs: singleChainReport.proofs, - proofFlagBits: singleChainReport.proofFlagBits, - messages: singleChainReport.messages, - offchainTokenData: singleChainReport.offchainTokenData - }); - } - function _generateReportFromMessages( uint64 sourceChainSelector, - Internal.EVM2EVMMessage[] memory messages + Internal.Any2EVMRampMessage[] memory messages ) internal pure returns (Internal.ExecutionReportSingleChain memory) { bytes[][] memory offchainTokenData = new bytes[][](messages.length); @@ -385,14 +373,14 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba function _generateBatchReportFromMessages( uint64 sourceChainSelector, - Internal.EVM2EVMMessage[] memory messages + Internal.Any2EVMRampMessage[] memory messages ) internal pure returns (Internal.ExecutionReportSingleChain[] memory) { Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](1); reports[0] = _generateReportFromMessages(sourceChainSelector, messages); return reports; } - function _getGasLimitsFromMessages(Internal.EVM2EVMMessage[] memory messages) + function _getGasLimitsFromMessages(Internal.Any2EVMRampMessage[] memory messages) internal pure returns (uint256[] memory) @@ -423,9 +411,7 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba ) internal pure { assertEq(config1.isEnabled, config2.isEnabled); assertEq(config1.minSeqNr, config2.minSeqNr); - assertEq(config1.prevOffRamp, config2.prevOffRamp); assertEq(config1.onRamp, config2.onRamp); - assertEq(config1.metadataHash, config2.metadataHash); } function _getDefaultSourceTokenData(Client.EVMTokenAmount[] memory srcTokenAmounts) @@ -457,12 +443,18 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba EVM2EVMMultiOffRamp.StaticConfig({ chainSelector: DEST_CHAIN_SELECTOR, rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(s_inboundNonceManager) }), new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](0) ); s_offRamp.setDynamicConfig(_generateDynamicMultiOffRampConfig(address(s_destRouter), address(s_priceRegistry))); + address[] memory authorizedCallers = new address[](1); + authorizedCallers[0] = address(s_offRamp); + s_inboundNonceManager.applyAuthorizedCallerUpdates( + AuthorizedCallers.AuthorizedCallerArgs({addedCallers: authorizedCallers, removedCallers: new address[](0)}) + ); _setupMultipleOffRamps(); address[] memory priceUpdaters = new address[](1); diff --git a/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRamp.t.sol b/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRamp.t.sol index 4b04a6fc16..2b16c87e2e 100644 --- a/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRamp.t.sol +++ b/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRamp.t.sol @@ -24,7 +24,7 @@ contract EVM2EVMMultiOnRamp_constructor is EVM2EVMMultiOnRampSetup { chainSelector: SOURCE_CHAIN_SELECTOR, maxFeeJuelsPerMsg: MAX_MSG_FEES_JUELS, rmnProxy: address(s_mockRMN), - nonceManager: address(s_nonceManager), + nonceManager: address(s_outboundNonceManager), tokenAdminRegistry: address(s_tokenAdminRegistry) }); EVM2EVMMultiOnRamp.DynamicConfig memory dynamicConfig = @@ -56,7 +56,7 @@ contract EVM2EVMMultiOnRamp_constructor is EVM2EVMMultiOnRampSetup { ); _deployOnRamp( - SOURCE_CHAIN_SELECTOR, address(s_sourceRouter), address(s_nonceManager), address(s_tokenAdminRegistry) + SOURCE_CHAIN_SELECTOR, address(s_sourceRouter), address(s_outboundNonceManager), address(s_tokenAdminRegistry) ); EVM2EVMMultiOnRamp.DestChainConfig memory expectedDestChainConfig = EVM2EVMMultiOnRamp.DestChainConfig({ @@ -99,7 +99,7 @@ contract EVM2EVMMultiOnRamp_constructor is EVM2EVMMultiOnRampSetup { chainSelector: SOURCE_CHAIN_SELECTOR, maxFeeJuelsPerMsg: MAX_NOP_FEES_JUELS, rmnProxy: address(s_mockRMN), - nonceManager: address(s_nonceManager), + nonceManager: address(s_outboundNonceManager), tokenAdminRegistry: address(s_tokenAdminRegistry) }), _generateDynamicMultiOnRampConfig(address(s_sourceRouter), address(s_priceRegistry)), @@ -117,7 +117,7 @@ contract EVM2EVMMultiOnRamp_constructor is EVM2EVMMultiOnRampSetup { chainSelector: 0, maxFeeJuelsPerMsg: MAX_NOP_FEES_JUELS, rmnProxy: address(s_mockRMN), - nonceManager: address(s_nonceManager), + nonceManager: address(s_outboundNonceManager), tokenAdminRegistry: address(s_tokenAdminRegistry) }), _generateDynamicMultiOnRampConfig(address(s_sourceRouter), address(s_priceRegistry)), @@ -135,7 +135,7 @@ contract EVM2EVMMultiOnRamp_constructor is EVM2EVMMultiOnRampSetup { chainSelector: SOURCE_CHAIN_SELECTOR, maxFeeJuelsPerMsg: MAX_NOP_FEES_JUELS, rmnProxy: address(0), - nonceManager: address(s_nonceManager), + nonceManager: address(s_outboundNonceManager), tokenAdminRegistry: address(s_tokenAdminRegistry) }), _generateDynamicMultiOnRampConfig(address(s_sourceRouter), address(s_priceRegistry)), @@ -171,7 +171,7 @@ contract EVM2EVMMultiOnRamp_constructor is EVM2EVMMultiOnRampSetup { chainSelector: SOURCE_CHAIN_SELECTOR, maxFeeJuelsPerMsg: MAX_NOP_FEES_JUELS, rmnProxy: address(s_mockRMN), - nonceManager: address(s_nonceManager), + nonceManager: address(s_outboundNonceManager), tokenAdminRegistry: address(0) }), _generateDynamicMultiOnRampConfig(address(s_sourceRouter), address(s_priceRegistry)), @@ -489,7 +489,7 @@ contract EVM2EVMMultiOnRamp_forwardFromRouter is EVM2EVMMultiOnRampSetup { Client.EVM2AnyMessage memory message = _generateEmptyMessage(); for (uint64 i = 1; i < 4; ++i) { - uint64 nonceBefore = s_nonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); + uint64 nonceBefore = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); uint64 sequenceNumberBefore = s_onRamp.getDestChainConfig(DEST_CHAIN_SELECTOR).sequenceNumber; vm.expectEmit(); @@ -497,7 +497,7 @@ contract EVM2EVMMultiOnRamp_forwardFromRouter is EVM2EVMMultiOnRampSetup { s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - uint64 nonceAfter = s_nonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); + uint64 nonceAfter = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); uint64 sequenceNumberAfter = s_onRamp.getDestChainConfig(DEST_CHAIN_SELECTOR).sequenceNumber; assertEq(nonceAfter, nonceBefore + 1); assertEq(sequenceNumberAfter, sequenceNumberBefore + 1); @@ -511,7 +511,7 @@ contract EVM2EVMMultiOnRamp_forwardFromRouter is EVM2EVMMultiOnRampSetup { ); for (uint64 i = 1; i < 4; ++i) { - uint64 nonceBefore = s_nonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); + uint64 nonceBefore = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); uint64 sequenceNumberBefore = s_onRamp.getDestChainConfig(DEST_CHAIN_SELECTOR).sequenceNumber; vm.expectEmit(); @@ -519,7 +519,7 @@ contract EVM2EVMMultiOnRamp_forwardFromRouter is EVM2EVMMultiOnRampSetup { s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - uint64 nonceAfter = s_nonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); + uint64 nonceAfter = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); uint64 sequenceNumberAfter = s_onRamp.getDestChainConfig(DEST_CHAIN_SELECTOR).sequenceNumber; assertEq(nonceAfter, nonceBefore); assertEq(sequenceNumberAfter, sequenceNumberBefore + 1); diff --git a/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRampSetup.t.sol b/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRampSetup.t.sol index 5b62b0f8e4..d9e2921421 100644 --- a/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRampSetup.t.sol +++ b/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMMultiOnRampSetup.t.sol @@ -35,7 +35,7 @@ contract EVM2EVMMultiOnRampSetup is TokenSetup, PriceRegistrySetup { EVM2EVMMultiOnRampHelper internal s_onRamp; MessageInterceptorHelper internal s_outboundMessageValidator; address[] internal s_offRamps; - NonceManager internal s_nonceManager; + NonceManager internal s_outboundNonceManager; address internal s_destTokenPool = makeAddr("destTokenPool"); address internal s_destToken = makeAddr("destToken"); @@ -104,9 +104,9 @@ contract EVM2EVMMultiOnRampSetup is TokenSetup, PriceRegistrySetup { ); s_outboundMessageValidator = new MessageInterceptorHelper(); - s_nonceManager = new NonceManager(new address[](0)); + s_outboundNonceManager = new NonceManager(new address[](0)); (s_onRamp, s_metadataHash) = _deployOnRamp( - SOURCE_CHAIN_SELECTOR, address(s_sourceRouter), address(s_nonceManager), address(s_tokenAdminRegistry) + SOURCE_CHAIN_SELECTOR, address(s_sourceRouter), address(s_outboundNonceManager), address(s_tokenAdminRegistry) ); s_offRamps = new address[](2); diff --git a/core/gethwrappers/ccip/generated/ccip_config/ccip_config.go b/core/gethwrappers/ccip/generated/ccip_config/ccip_config.go index a50f757806..3d879a047c 100644 --- a/core/gethwrappers/ccip/generated/ccip_config/ccip_config.go +++ b/core/gethwrappers/ccip/generated/ccip_config/ccip_config.go @@ -30,18 +30,18 @@ var ( _ = abi.ConvertType ) -type CCIPConfigChainConfig struct { +type CCIPConfigTypesChainConfig struct { Readers [][32]byte FChain uint8 Config []byte } -type CCIPConfigChainConfigInfo struct { +type CCIPConfigTypesChainConfigInfo struct { ChainSelector uint64 - ChainConfig CCIPConfigChainConfig + ChainConfig CCIPConfigTypesChainConfig } -type CCIPConfigOCR3Config struct { +type CCIPConfigTypesOCR3Config struct { PluginType uint8 ChainSelector uint64 F uint8 @@ -54,14 +54,14 @@ type CCIPConfigOCR3Config struct { OffchainConfig []byte } -type CCIPConfigOCR3ConfigWithMeta struct { - Config CCIPConfigOCR3Config +type CCIPConfigTypesOCR3ConfigWithMeta struct { + Config CCIPConfigTypesOCR3Config ConfigCount uint64 ConfigDigest [32]byte } var CCIPConfigMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"capabilitiesRegistry\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainConfigNotSetForChain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainSelectorNotFound\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ChainSelectorNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FChainMustBePositive\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FMustBePositive\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FTooHigh\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"InvalidConfigLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"enumCCIPConfig.ConfigState\",\"name\":\"currentState\",\"type\":\"uint8\"},{\"internalType\":\"enumCCIPConfig.ConfigState\",\"name\":\"proposedState\",\"type\":\"uint8\"}],\"name\":\"InvalidConfigStateTransition\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPluginType\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeNotInRegistry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonExistentConfigTransition\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"got\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minimum\",\"type\":\"uint256\"}],\"name\":\"NotEnoughTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OfframpAddressCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCapabilitiesRegistryCanCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"p2pIdsLength\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"signersLength\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"transmittersLength\",\"type\":\"uint256\"}],\"name\":\"P2PIdsLengthNotMatching\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyBootstrapP2PIds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOCR3Configs\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManySigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyTransmitters\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"got\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"expected\",\"type\":\"uint64\"}],\"name\":\"WrongConfigCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"got\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"}],\"name\":\"WrongConfigDigest\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"got\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"}],\"name\":\"WrongConfigDigestBlueGreen\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"CapabilityConfigurationSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainConfigRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32[]\",\"name\":\"readers\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint8\",\"name\":\"fChain\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structCCIPConfig.ChainConfig\",\"name\":\"chainConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigSet\",\"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\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64[]\",\"name\":\"chainSelectorRemoves\",\"type\":\"uint64[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32[]\",\"name\":\"readers\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint8\",\"name\":\"fChain\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCCIPConfig.ChainConfig\",\"name\":\"chainConfig\",\"type\":\"tuple\"}],\"internalType\":\"structCCIPConfig.ChainConfigInfo[]\",\"name\":\"chainConfigAdds\",\"type\":\"tuple[]\"}],\"name\":\"applyChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"beforeCapabilityConfigSet\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllChainConfigs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32[]\",\"name\":\"readers\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint8\",\"name\":\"fChain\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCCIPConfig.ChainConfig\",\"name\":\"chainConfig\",\"type\":\"tuple\"}],\"internalType\":\"structCCIPConfig.ChainConfigInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"name\":\"getCapabilityConfiguration\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"configuration\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"enumCCIPConfig.PluginType\",\"name\":\"pluginType\",\"type\":\"uint8\"}],\"name\":\"getOCRConfig\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"enumCCIPConfig.PluginType\",\"name\":\"pluginType\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"offrampAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"bootstrapP2PIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"p2pIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"signers\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes[]\",\"name\":\"transmitters\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"internalType\":\"structCCIPConfig.OCR3Config\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"internalType\":\"structCCIPConfig.OCR3ConfigWithMeta[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"capabilitiesRegistry\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainConfigNotSetForChain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainSelectorNotFound\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ChainSelectorNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FChainMustBePositive\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FMustBePositive\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FTooHigh\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"InvalidConfigLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"enumCCIPConfigTypes.ConfigState\",\"name\":\"currentState\",\"type\":\"uint8\"},{\"internalType\":\"enumCCIPConfigTypes.ConfigState\",\"name\":\"proposedState\",\"type\":\"uint8\"}],\"name\":\"InvalidConfigStateTransition\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPluginType\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeNotInRegistry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonExistentConfigTransition\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"got\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minimum\",\"type\":\"uint256\"}],\"name\":\"NotEnoughTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OfframpAddressCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCapabilitiesRegistryCanCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"p2pIdsLength\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"signersLength\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"transmittersLength\",\"type\":\"uint256\"}],\"name\":\"P2PIdsLengthNotMatching\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyBootstrapP2PIds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOCR3Configs\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManySigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyTransmitters\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"got\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"expected\",\"type\":\"uint64\"}],\"name\":\"WrongConfigCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"got\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"}],\"name\":\"WrongConfigDigest\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"got\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"}],\"name\":\"WrongConfigDigestBlueGreen\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"CapabilityConfigurationSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainConfigRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32[]\",\"name\":\"readers\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint8\",\"name\":\"fChain\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structCCIPConfigTypes.ChainConfig\",\"name\":\"chainConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigSet\",\"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\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64[]\",\"name\":\"chainSelectorRemoves\",\"type\":\"uint64[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32[]\",\"name\":\"readers\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint8\",\"name\":\"fChain\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCCIPConfigTypes.ChainConfig\",\"name\":\"chainConfig\",\"type\":\"tuple\"}],\"internalType\":\"structCCIPConfigTypes.ChainConfigInfo[]\",\"name\":\"chainConfigAdds\",\"type\":\"tuple[]\"}],\"name\":\"applyChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"beforeCapabilityConfigSet\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllChainConfigs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32[]\",\"name\":\"readers\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint8\",\"name\":\"fChain\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCCIPConfigTypes.ChainConfig\",\"name\":\"chainConfig\",\"type\":\"tuple\"}],\"internalType\":\"structCCIPConfigTypes.ChainConfigInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"name\":\"getCapabilityConfiguration\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"configuration\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"enumInternal.OCRPluginType\",\"name\":\"pluginType\",\"type\":\"uint8\"}],\"name\":\"getOCRConfig\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"enumInternal.OCRPluginType\",\"name\":\"pluginType\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"offrampAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"bootstrapP2PIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"p2pIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"signers\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes[]\",\"name\":\"transmitters\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"internalType\":\"structCCIPConfigTypes.OCR3Config\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"internalType\":\"structCCIPConfigTypes.OCR3ConfigWithMeta[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", Bin: "0x60a06040523480156200001157600080fd5b506040516200417c3803806200417c83398101604081905262000034916200017e565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000d3565b5050506001600160a01b0316608052620001b0565b336001600160a01b038216036200012d5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000602082840312156200019157600080fd5b81516001600160a01b0381168114620001a957600080fd5b9392505050565b608051613fa9620001d360003960008181610e4e01526110e30152613fa96000f3fe608060405234801561001057600080fd5b50600436106100be5760003560e01c80638da5cb5b11610076578063f2fde38b1161005b578063f2fde38b146101bc578063f442c89a146101cf578063fba64a7c146101e257600080fd5b80638da5cb5b1461017f578063ddc042a8146101a757600080fd5b80634bd0473f116100a75780634bd0473f1461013457806379ba5097146101545780638318ed5d1461015e57600080fd5b806301ffc9a7146100c3578063181f5a77146100eb575b600080fd5b6100d66100d1366004612d5f565b6101f5565b60405190151581526020015b60405180910390f35b6101276040518060400160405280601481526020017f43434950436f6e66696720312e362e302d64657600000000000000000000000081525081565b6040516100e29190612e05565b610147610142366004612e49565b61028e565b6040516100e29190612f75565b61015c61075e565b005b61012761016c366004613152565b5060408051602081019091526000815290565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100e2565b6101af610860565b6040516100e291906131b3565b61015c6101ca366004613243565b610a52565b61015c6101dd3660046132c5565b610a66565b61015c6101f0366004613349565b610e36565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f78bea72100000000000000000000000000000000000000000000000000000000148061028857507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b63ffffffff821660009081526005602052604081206060918360018111156102b8576102b8612e7e565b60018111156102c9576102c9612e7e565b8152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b8282101561075257600084815260209020604080516101a08101909152600984029091018054829060608201908390829060ff16600181111561033c5761033c612e7e565b600181111561034d5761034d612e7e565b8152815467ffffffffffffffff61010082048116602084015260ff690100000000000000000083041660408401526a01000000000000000000009091041660608201526001820180546080909201916103a590613406565b80601f01602080910402602001604051908101604052809291908181526020018280546103d190613406565b801561041e5780601f106103f35761010080835404028352916020019161041e565b820191906000526020600020905b81548152906001019060200180831161040157829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561047657602002820191906000526020600020905b815481526020019060010190808311610462575b50505050508152602001600382018054806020026020016040519081016040528092919081815260200182805480156104ce57602002820191906000526020600020905b8154815260200190600101908083116104ba575b5050505050815260200160048201805480602002602001604051908101604052809291908181526020016000905b828210156105a857838290600052602060002001805461051b90613406565b80601f016020809104026020016040519081016040528092919081815260200182805461054790613406565b80156105945780601f1061056957610100808354040283529160200191610594565b820191906000526020600020905b81548152906001019060200180831161057757829003601f168201915b5050505050815260200190600101906104fc565b50505050815260200160058201805480602002602001604051908101604052809291908181526020016000905b828210156106815783829060005260206000200180546105f490613406565b80601f016020809104026020016040519081016040528092919081815260200182805461062090613406565b801561066d5780601f106106425761010080835404028352916020019161066d565b820191906000526020600020905b81548152906001019060200180831161065057829003601f168201915b5050505050815260200190600101906105d5565b50505050815260200160068201805461069990613406565b80601f01602080910402602001604051908101604052809291908181526020018280546106c590613406565b80156107125780601f106106e757610100808354040283529160200191610712565b820191906000526020600020905b8154815290600101906020018083116106f557829003601f168201915b505050919092525050508152600782015467ffffffffffffffff1660208083019190915260089092015460409091015290825260019290920191016102f7565b50505050905092915050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146107e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6060600061086e6003610ef7565b9050600061087c6003610f0b565b67ffffffffffffffff81111561089457610894613459565b6040519080825280602002602001820160405280156108cd57816020015b6108ba612af0565b8152602001906001900390816108b25790505b50905060005b8251811015610a4b5760008382815181106108f0576108f0613488565b60209081029190910181015160408051808201825267ffffffffffffffff83168082526000908152600285528290208251815460808188028301810190955260608201818152959750929586019490939192849284919084018282801561097657602002820191906000526020600020905b815481526020019060010190808311610962575b5050509183525050600182015460ff1660208201526002820180546040909201916109a090613406565b80601f01602080910402602001604051908101604052809291908181526020018280546109cc90613406565b8015610a195780601f106109ee57610100808354040283529160200191610a19565b820191906000526020600020905b8154815290600101906020018083116109fc57829003601f168201915b505050505081525050815250838381518110610a3757610a37613488565b6020908102919091010152506001016108d3565b5092915050565b610a5a610f15565b610a6381610f98565b50565b610a6e610f15565b60005b83811015610c5457610ab5858583818110610a8e57610a8e613488565b9050602002016020810190610aa391906134b7565b60039067ffffffffffffffff1661108d565b610b1f57848482818110610acb57610acb613488565b9050602002016020810190610ae091906134b7565b6040517f1bd4d2d200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016107db565b60026000868684818110610b3557610b35613488565b9050602002016020810190610b4a91906134b7565b67ffffffffffffffff1681526020810191909152604001600090812090610b718282612b38565b6001820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055610ba9600283016000612b56565b5050610be7858583818110610bc057610bc0613488565b9050602002016020810190610bd591906134b7565b60039067ffffffffffffffff166110a5565b507f2a680691fef3b2d105196805935232c661ce703e92d464ef0b94a7bc62d714f0858583818110610c1b57610c1b613488565b9050602002016020810190610c3091906134b7565b60405167ffffffffffffffff909116815260200160405180910390a1600101610a71565b5060005b81811015610e2f576000838383818110610c7457610c74613488565b9050602002810190610c8691906134d2565b610c94906020810190613510565b610c9d90613712565b80519091506000858585818110610cb657610cb6613488565b9050602002810190610cc891906134d2565b610cd69060208101906134b7565b905060005b8251811015610d0e57610d06838281518110610cf957610cf9613488565b60200260200101516110b1565b600101610cdb565b50826020015160ff16600003610d50576040517fa9b3766e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff81166000908152600260209081526040909120845180518693610d80928492910190612b90565b5060208201516001820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff90921691909117905560408201516002820190610dcd90826137f9565b50610de791506003905067ffffffffffffffff83166111ca565b507f05dd57854af2c291a94ea52e7c43d80bc3be7fa73022f98b735dea86642fa5e08184604051610e19929190613913565b60405180910390a1505050806001019050610c58565b5050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610ea5576040517fac7a7efd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610eb3848601866139be565b9050600080610ec1836111d6565b8151919350915015610ed957610ed98460008461142f565b805115610eec57610eec8460018361142f565b505050505050505050565b60606000610f0483611c10565b9392505050565b6000610288825490565b60005473ffffffffffffffffffffffffffffffffffffffff163314610f96576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107db565b565b3373ffffffffffffffffffffffffffffffffffffffff821603611017576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107db565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008181526001830160205260408120541515610f04565b6000610f048383611c6c565b6040517f50c946fe000000000000000000000000000000000000000000000000000000008152600481018290526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906350c946fe90602401600060405180830381865afa15801561113f573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526111859190810190613c2f565b60808101519091506111c6576040517f8907a4fa000000000000000000000000000000000000000000000000000000008152600481018390526024016107db565b5050565b6000610f048383611d5f565b606080600460ff1683511115611218576040517f8854586400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160028082526060820190925290816020015b61129c6040805161014081019091528060008152602001600067ffffffffffffffff168152602001600060ff168152602001600067ffffffffffffffff1681526020016060815260200160608152602001606081526020016060815260200160608152602001606081525090565b81526020019060019003908161122e57505060408051600280825260608201909252919350602082015b6113346040805161014081019091528060008152602001600067ffffffffffffffff168152602001600060ff168152602001600067ffffffffffffffff1681526020016060815260200160608152602001606081526020016060815260200160608152602001606081525090565b8152602001906001900390816112c657905050905060008060005b855181101561142257600086828151811061136c5761136c613488565b602002602001015160000151600181111561138957611389612e7e565b036113d6578581815181106113a0576113a0613488565b60200260200101518584815181106113ba576113ba613488565b6020026020010181905250826113cf90613d36565b925061141a565b8581815181106113e8576113e8613488565b602002602001015184838151811061140257611402613488565b60200260200101819052508161141790613d36565b91505b60010161134f565b5090835281529092909150565b63ffffffff831660009081526005602052604081208184600181111561145757611457612e7e565b600181111561146857611468612e7e565b8152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b828210156118f157600084815260209020604080516101a08101909152600984029091018054829060608201908390829060ff1660018111156114db576114db612e7e565b60018111156114ec576114ec612e7e565b8152815467ffffffffffffffff61010082048116602084015260ff690100000000000000000083041660408401526a010000000000000000000090910416606082015260018201805460809092019161154490613406565b80601f016020809104026020016040519081016040528092919081815260200182805461157090613406565b80156115bd5780601f10611592576101008083540402835291602001916115bd565b820191906000526020600020905b8154815290600101906020018083116115a057829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561161557602002820191906000526020600020905b815481526020019060010190808311611601575b505050505081526020016003820180548060200260200160405190810160405280929190818152602001828054801561166d57602002820191906000526020600020905b815481526020019060010190808311611659575b5050505050815260200160048201805480602002602001604051908101604052809291908181526020016000905b828210156117475783829060005260206000200180546116ba90613406565b80601f01602080910402602001604051908101604052809291908181526020018280546116e690613406565b80156117335780601f1061170857610100808354040283529160200191611733565b820191906000526020600020905b81548152906001019060200180831161171657829003601f168201915b50505050508152602001906001019061169b565b50505050815260200160058201805480602002602001604051908101604052809291908181526020016000905b8282101561182057838290600052602060002001805461179390613406565b80601f01602080910402602001604051908101604052809291908181526020018280546117bf90613406565b801561180c5780601f106117e15761010080835404028352916020019161180c565b820191906000526020600020905b8154815290600101906020018083116117ef57829003601f168201915b505050505081526020019060010190611774565b50505050815260200160068201805461183890613406565b80601f016020809104026020016040519081016040528092919081815260200182805461186490613406565b80156118b15780601f10611886576101008083540402835291602001916118b1565b820191906000526020600020905b81548152906001019060200180831161189457829003601f168201915b505050919092525050508152600782015467ffffffffffffffff166020808301919091526008909201546040909101529082526001929092019101611496565b50505050905060006119038251611dae565b905060006119118451611dae565b905061191d8282611e00565b600061192c8785878686611ebc565b905061193884826122a8565b63ffffffff871660009081526005602052604081209087600181111561196057611960612e7e565b600181111561197157611971612e7e565b8152602001908152602001600020600061198b9190612bdb565b60005b8151811015611c065763ffffffff88166000908152600560205260408120908860018111156119bf576119bf612e7e565b60018111156119d0576119d0612e7e565b81526020019081526020016000208282815181106119f0576119f0613488565b6020908102919091018101518254600181810185556000948552929093208151805160099095029091018054929490939192849283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016908381811115611a5a57611a5a612e7e565b021790555060208201518154604084015160608501517fffffffffffffffffffffffffffffffffffffffffffff000000000000000000ff90921661010067ffffffffffffffff948516027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff1617690100000000000000000060ff90921691909102177fffffffffffffffffffffffffffff0000000000000000ffffffffffffffffffff166a0100000000000000000000929091169190910217815560808201516001820190611b2990826137f9565b5060a08201518051611b45916002840191602090910190612b90565b5060c08201518051611b61916003840191602090910190612b90565b5060e08201518051611b7d916004840191602090910190612bfc565b506101008201518051611b9a916005840191602090910190612bfc565b506101208201516006820190611bb090826137f9565b50505060208201516007820180547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff90921691909117905560409091015160089091015560010161198e565b5050505050505050565b606081600001805480602002602001604051908101604052809291908181526020018280548015611c6057602002820191906000526020600020905b815481526020019060010190808311611c4c575b50505050509050919050565b60008181526001830160205260408120548015611d55576000611c90600183613d6e565b8554909150600090611ca490600190613d6e565b9050818114611d09576000866000018281548110611cc457611cc4613488565b9060005260206000200154905080876000018481548110611ce757611ce7613488565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080611d1a57611d1a613d81565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610288565b6000915050610288565b6000818152600183016020526040812054611da657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610288565b506000610288565b60006002821115611dee576040517f3e478526000000000000000000000000000000000000000000000000000000008152600481018390526024016107db565b81600281111561028857610288612e7e565b6000826002811115611e1457611e14612e7e565b826002811115611e2657611e26612e7e565b611e309190613db0565b90508060011480611e7c5750807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff148015611e7c57506002836002811115611e7a57611e7a612e7e565b145b15611e8657505050565b82826040517f0a6b675b0000000000000000000000000000000000000000000000000000000081526004016107db929190613de0565b60606000845167ffffffffffffffff811115611eda57611eda613459565b604051908082528060200260200182016040528015611f03578160200160208202803683370190505b5090506000846002811115611f1a57611f1a612e7e565b148015611f3857506001836002811115611f3657611f36612e7e565b145b15611f7957600181600081518110611f5257611f52613488565b602002602001019067ffffffffffffffff16908167ffffffffffffffff16815250506120e1565b6001846002811115611f8d57611f8d612e7e565b148015611fab57506002836002811115611fa957611fa9612e7e565b145b156120425785600081518110611fc357611fc3613488565b60200260200101516020015181600081518110611fe257611fe2613488565b602002602001019067ffffffffffffffff16908167ffffffffffffffff16815250508560008151811061201757612017613488565b602002602001015160200151600161202f9190613dfb565b81600181518110611f5257611f52613488565b600284600281111561205657612056612e7e565b1480156120745750600183600281111561207257612072612e7e565b145b156120ab578560018151811061208c5761208c613488565b60200260200101516020015181600081518110611f5257611f52613488565b83836040517f0a6b675b0000000000000000000000000000000000000000000000000000000081526004016107db929190613de0565b6000855167ffffffffffffffff8111156120fd576120fd613459565b6040519080825280602002602001820160405280156121b357816020015b604080516101a081018252600060608083018281526080840183905260a0840183905260c0840183905260e084018290526101008401829052610120840182905261014084018290526101608401829052610180840191909152825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161211b5790505b50905060005b825181101561229c576121e48782815181106121d7576121d7613488565b6020026020010151612627565b604051806060016040528088838151811061220157612201613488565b6020026020010151815260200184838151811061222057612220613488565b602002602001015167ffffffffffffffff1681526020016122748b86858151811061224d5761224d613488565b60200260200101518b868151811061226757612267613488565b6020026020010151612a1b565b81525082828151811061228957612289613488565b60209081029190910101526001016121b9565b50979650505050505050565b81518151811580156122ba5750806001145b1561235c57826000815181106122d2576122d2613488565b60200260200101516020015167ffffffffffffffff16600114612356578260008151811061230257612302613488565b60209081029190910181015101516040517fc1658eb800000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152600160248201526044016107db565b50505050565b81600114801561236c5750806002145b15612522578360008151811061238457612384613488565b602002602001015160400151836000815181106123a3576123a3613488565b6020026020010151604001511461242f57826000815181106123c7576123c7613488565b602002602001015160400151846000815181106123e6576123e6613488565b6020026020010151604001516040517fc7ccdd7f0000000000000000000000000000000000000000000000000000000081526004016107db929190918252602082015260400190565b8360008151811061244257612442613488565b602002602001015160200151600161245a9190613dfb565b67ffffffffffffffff168360018151811061247757612477613488565b60200260200101516020015167ffffffffffffffff161461235657826001815181106124a5576124a5613488565b602002602001015160200151846000815181106124c4576124c4613488565b60200260200101516020015160016124dc9190613dfb565b6040517fc1658eb800000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9283166004820152911660248201526044016107db565b8160021480156125325750806001145b156125f5578360018151811061254a5761254a613488565b6020026020010151604001518360008151811061256957612569613488565b60200260200101516040015114612356578260008151811061258d5761258d613488565b602002602001015160400151846001815181106125ac576125ac613488565b6020026020010151604001516040517f9e9756700000000000000000000000000000000000000000000000000000000081526004016107db929190918252602082015260400190565b6040517f1f1b2bb600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806020015167ffffffffffffffff1660000361266f576040517f698cf8e000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008151600181111561268457612684612e7e565b141580156126a557506001815160018111156126a2576126a2612e7e565b14155b156126dc576040517f3302dbd700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80608001515160000361271b576040517f358c192700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208101516127369060039067ffffffffffffffff1661108d565b61277e5760208101516040517f1bd4d2d200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016107db565b60e081015151601f10156127be576040517f1b925da600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61010081015151601f10156127ff576040517f645960ff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208082015167ffffffffffffffff1660009081526002909152604081206001015461282f9060ff166003613e1c565b61283a906001613e38565b60ff1690508082610100015151101561289157610100820151516040517f548dd21f0000000000000000000000000000000000000000000000000000000081526004810191909152602481018290526044016107db565b816040015160ff166000036128d2576040517f39d1a4d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516128e2906003613e1c565b60ff168260e001515111612922576040517f4856694e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160e00151518260c00151511415806129465750816101000151518260c001515114155b156129a15760c08201515160e083015151610100840151516040517fba900f6d0000000000000000000000000000000000000000000000000000000081526004810193909352602483019190915260448201526064016107db565b8160c00151518260a001515111156129e5576040517f8473d80700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8260e0015151811015612a1657612a0e8360c001518281518110610cf957610cf9613488565b6001016129e8565b505050565b60008082602001518584600001518560800151878760a001518860c001518960e001518a61010001518b604001518c606001518d6101200151604051602001612a6f9c9b9a99989796959493929190613ebc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0a000000000000000000000000000000000000000000000000000000000000179150509392505050565b6040518060400160405280600067ffffffffffffffff168152602001612b33604051806060016040528060608152602001600060ff168152602001606081525090565b905290565b5080546000825590600052602060002090810190610a639190612c4e565b508054612b6290613406565b6000825580601f10612b72575050565b601f016020900490600052602060002090810190610a639190612c4e565b828054828255906000526020600020908101928215612bcb579160200282015b82811115612bcb578251825591602001919060010190612bb0565b50612bd7929150612c4e565b5090565b5080546000825560090290600052602060002090810190610a639190612c63565b828054828255906000526020600020908101928215612c42579160200282015b82811115612c425782518290612c3290826137f9565b5091602001919060010190612c1c565b50612bd7929150612d24565b5b80821115612bd75760008155600101612c4f565b80821115612bd75780547fffffffffffffffffffffffffffff00000000000000000000000000000000000016815560008181612ca26001830182612b56565b612cb0600283016000612b38565b612cbe600383016000612b38565b612ccc600483016000612d41565b612cda600583016000612d41565b612ce8600683016000612b56565b5050506007810180547fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016905560006008820155600901612c63565b80821115612bd7576000612d388282612b56565b50600101612d24565b5080546000825590600052602060002090810190610a639190612d24565b600060208284031215612d7157600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610f0457600080fd5b6000815180845260005b81811015612dc757602081850181015186830182015201612dab565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000610f046020830184612da1565b63ffffffff81168114610a6357600080fd5b8035612e3581612e18565b919050565b803560028110612e3557600080fd5b60008060408385031215612e5c57600080fd5b8235612e6781612e18565b9150612e7560208401612e3a565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60028110612ebd57612ebd612e7e565b9052565b60008151808452602080850194506020840160005b83811015612ef257815187529582019590820190600101612ed6565b509495945050505050565b60008282518085526020808601955060208260051b8401016020860160005b84811015612f68577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0868403018952612f56838351612da1565b98840198925090830190600101612f1c565b5090979650505050505050565b600060208083018184528085518083526040925060408601915060408160051b87010184880160005b83811015613144577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0898403018552815160608151818652612fe38287018251612ead565b898101516080612ffe8189018367ffffffffffffffff169052565b8a830151915060a0613014818a018460ff169052565b938301519360c092506130328984018667ffffffffffffffff169052565b818401519450610140915060e082818b01526130526101a08b0187612da1565b95508185015191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0610100818c890301818d01526130918885612ec1565b97508587015195506101209350818c890301848d01526130b18887612ec1565b9750828701519550818c890301858d01526130cc8887612efd565b975080870151955050808b8803016101608c01526130ea8786612efd565b9650828601519550808b8803016101808c0152505050505061310c8282612da1565b915050888201516131288a87018267ffffffffffffffff169052565b5090870151938701939093529386019390860190600101612f9e565b509098975050505050505050565b60006020828403121561316457600080fd5b8135610f0481612e18565b60008151606084526131846060850182612ec1565b905060ff6020840151166020850152604083015184820360408601526131aa8282612da1565b95945050505050565b600060208083018184528085518083526040925060408601915060408160051b87010184880160005b83811015613144578883037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00185528151805167ffffffffffffffff1684528701518784018790526132308785018261316f565b95880195935050908601906001016131dc565b60006020828403121561325557600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610f0457600080fd5b60008083601f84011261328b57600080fd5b50813567ffffffffffffffff8111156132a357600080fd5b6020830191508360208260051b85010111156132be57600080fd5b9250929050565b600080600080604085870312156132db57600080fd5b843567ffffffffffffffff808211156132f357600080fd5b6132ff88838901613279565b9096509450602087013591508082111561331857600080fd5b5061332587828801613279565b95989497509550505050565b803567ffffffffffffffff81168114612e3557600080fd5b6000806000806000806080878903121561336257600080fd5b863567ffffffffffffffff8082111561337a57600080fd5b6133868a838b01613279565b9098509650602089013591508082111561339f57600080fd5b818901915089601f8301126133b357600080fd5b8135818111156133c257600080fd5b8a60208285010111156133d457600080fd5b6020830196508095505050506133ec60408801613331565b91506133fa60608801612e2a565b90509295509295509295565b600181811c9082168061341a57607f821691505b602082108103613453577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156134c957600080fd5b610f0482613331565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261350657600080fd5b9190910192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa183360301811261350657600080fd5b604051610140810167ffffffffffffffff8111828210171561356857613568613459565b60405290565b60405160e0810167ffffffffffffffff8111828210171561356857613568613459565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156135d8576135d8613459565b604052919050565b600067ffffffffffffffff8211156135fa576135fa613459565b5060051b60200190565b600082601f83011261361557600080fd5b8135602061362a613625836135e0565b613591565b8083825260208201915060208460051b87010193508684111561364c57600080fd5b602086015b848110156136685780358352918301918301613651565b509695505050505050565b803560ff81168114612e3557600080fd5b600082601f83011261369557600080fd5b813567ffffffffffffffff8111156136af576136af613459565b6136e060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613591565b8181528460208386010111156136f557600080fd5b816020850160208301376000918101602001919091529392505050565b60006060823603121561372457600080fd5b6040516060810167ffffffffffffffff828210818311171561374857613748613459565b81604052843591508082111561375d57600080fd5b61376936838701613604565b835261377760208601613673565b6020840152604085013591508082111561379057600080fd5b5061379d36828601613684565b60408301525092915050565b601f821115612a16576000816000526020600020601f850160051c810160208610156137d25750805b601f850160051c820191505b818110156137f1578281556001016137de565b505050505050565b815167ffffffffffffffff81111561381357613813613459565b613827816138218454613406565b846137a9565b602080601f83116001811461387a57600084156138445750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556137f1565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156138c7578886015182559484019460019091019084016138a8565b508582101561390357878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b67ffffffffffffffff83168152604060208201526000613936604083018461316f565b949350505050565b600082601f83011261394f57600080fd5b8135602061395f613625836135e0565b82815260059290921b8401810191818101908684111561397e57600080fd5b8286015b8481101561366857803567ffffffffffffffff8111156139a25760008081fd5b6139b08986838b0101613684565b845250918301918301613982565b600060208083850312156139d157600080fd5b823567ffffffffffffffff808211156139e957600080fd5b818501915085601f8301126139fd57600080fd5b8135613a0b613625826135e0565b81815260059190911b83018401908481019088831115613a2a57600080fd5b8585015b83811015613bb857803585811115613a4557600080fd5b8601610140818c037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0011215613a7a57600080fd5b613a82613544565b613a8d898301612e3a565b8152613a9b60408301613331565b89820152613aab60608301613673565b6040820152613abc60808301613331565b606082015260a082013587811115613ad357600080fd5b613ae18d8b83860101613684565b60808301525060c082013587811115613af957600080fd5b613b078d8b83860101613604565b60a08301525060e082013587811115613b1f57600080fd5b613b2d8d8b83860101613604565b60c0830152506101008083013588811115613b4757600080fd5b613b558e8c8387010161393e565b60e0840152506101208084013589811115613b6f57600080fd5b613b7d8f8d8388010161393e565b8385015250610140840135915088821115613b9757600080fd5b613ba58e8c84870101613684565b9083015250845250918601918601613a2e565b5098975050505050505050565b8051612e3581612e18565b600082601f830112613be157600080fd5b81516020613bf1613625836135e0565b8083825260208201915060208460051b870101935086841115613c1357600080fd5b602086015b848110156136685780518352918301918301613c18565b600060208284031215613c4157600080fd5b815167ffffffffffffffff80821115613c5957600080fd5b9083019060e08286031215613c6d57600080fd5b613c7561356e565b613c7e83613bc5565b8152613c8c60208401613bc5565b6020820152613c9d60408401613bc5565b6040820152606083015160608201526080830151608082015260a083015182811115613cc857600080fd5b613cd487828601613bd0565b60a08301525060c083015182811115613cec57600080fd5b613cf887828601613bd0565b60c08301525095945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613d6757613d67613d07565b5060010190565b8181038181111561028857610288613d07565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b8181036000831280158383131683831282161715610a4b57610a4b613d07565b60038110612ebd57612ebd612e7e565b60408101613dee8285613dd0565b610f046020830184613dd0565b67ffffffffffffffff818116838216019080821115610a4b57610a4b613d07565b60ff8181168382160290811690818114610a4b57610a4b613d07565b60ff818116838216019081111561028857610288613d07565b60008282518085526020808601955060208260051b8401016020860160005b84811015612f68577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0868403018952613eaa838351612da1565b98840198925090830190600101613e70565b67ffffffffffffffff8d16815263ffffffff8c166020820152613ee2604082018c612ead565b61018060608201526000613efa61018083018c612da1565b67ffffffffffffffff8b16608084015282810360a0840152613f1c818b612ec1565b905082810360c0840152613f30818a612ec1565b905082810360e0840152613f448189613e51565b9050828103610100840152613f598188613e51565b60ff8716610120850152905067ffffffffffffffff8516610140840152828103610160840152613f898185612da1565b9f9e50505050505050505050505050505056fea164736f6c6343000818000a", } @@ -201,25 +201,25 @@ func (_CCIPConfig *CCIPConfigTransactorRaw) Transact(opts *bind.TransactOpts, me return _CCIPConfig.Contract.contract.Transact(opts, method, params...) } -func (_CCIPConfig *CCIPConfigCaller) GetAllChainConfigs(opts *bind.CallOpts) ([]CCIPConfigChainConfigInfo, error) { +func (_CCIPConfig *CCIPConfigCaller) GetAllChainConfigs(opts *bind.CallOpts) ([]CCIPConfigTypesChainConfigInfo, error) { var out []interface{} err := _CCIPConfig.contract.Call(opts, &out, "getAllChainConfigs") if err != nil { - return *new([]CCIPConfigChainConfigInfo), err + return *new([]CCIPConfigTypesChainConfigInfo), err } - out0 := *abi.ConvertType(out[0], new([]CCIPConfigChainConfigInfo)).(*[]CCIPConfigChainConfigInfo) + out0 := *abi.ConvertType(out[0], new([]CCIPConfigTypesChainConfigInfo)).(*[]CCIPConfigTypesChainConfigInfo) return out0, err } -func (_CCIPConfig *CCIPConfigSession) GetAllChainConfigs() ([]CCIPConfigChainConfigInfo, error) { +func (_CCIPConfig *CCIPConfigSession) GetAllChainConfigs() ([]CCIPConfigTypesChainConfigInfo, error) { return _CCIPConfig.Contract.GetAllChainConfigs(&_CCIPConfig.CallOpts) } -func (_CCIPConfig *CCIPConfigCallerSession) GetAllChainConfigs() ([]CCIPConfigChainConfigInfo, error) { +func (_CCIPConfig *CCIPConfigCallerSession) GetAllChainConfigs() ([]CCIPConfigTypesChainConfigInfo, error) { return _CCIPConfig.Contract.GetAllChainConfigs(&_CCIPConfig.CallOpts) } @@ -245,25 +245,25 @@ func (_CCIPConfig *CCIPConfigCallerSession) GetCapabilityConfiguration(arg0 uint return _CCIPConfig.Contract.GetCapabilityConfiguration(&_CCIPConfig.CallOpts, arg0) } -func (_CCIPConfig *CCIPConfigCaller) GetOCRConfig(opts *bind.CallOpts, donId uint32, pluginType uint8) ([]CCIPConfigOCR3ConfigWithMeta, error) { +func (_CCIPConfig *CCIPConfigCaller) GetOCRConfig(opts *bind.CallOpts, donId uint32, pluginType uint8) ([]CCIPConfigTypesOCR3ConfigWithMeta, error) { var out []interface{} err := _CCIPConfig.contract.Call(opts, &out, "getOCRConfig", donId, pluginType) if err != nil { - return *new([]CCIPConfigOCR3ConfigWithMeta), err + return *new([]CCIPConfigTypesOCR3ConfigWithMeta), err } - out0 := *abi.ConvertType(out[0], new([]CCIPConfigOCR3ConfigWithMeta)).(*[]CCIPConfigOCR3ConfigWithMeta) + out0 := *abi.ConvertType(out[0], new([]CCIPConfigTypesOCR3ConfigWithMeta)).(*[]CCIPConfigTypesOCR3ConfigWithMeta) return out0, err } -func (_CCIPConfig *CCIPConfigSession) GetOCRConfig(donId uint32, pluginType uint8) ([]CCIPConfigOCR3ConfigWithMeta, error) { +func (_CCIPConfig *CCIPConfigSession) GetOCRConfig(donId uint32, pluginType uint8) ([]CCIPConfigTypesOCR3ConfigWithMeta, error) { return _CCIPConfig.Contract.GetOCRConfig(&_CCIPConfig.CallOpts, donId, pluginType) } -func (_CCIPConfig *CCIPConfigCallerSession) GetOCRConfig(donId uint32, pluginType uint8) ([]CCIPConfigOCR3ConfigWithMeta, error) { +func (_CCIPConfig *CCIPConfigCallerSession) GetOCRConfig(donId uint32, pluginType uint8) ([]CCIPConfigTypesOCR3ConfigWithMeta, error) { return _CCIPConfig.Contract.GetOCRConfig(&_CCIPConfig.CallOpts, donId, pluginType) } @@ -345,15 +345,15 @@ func (_CCIPConfig *CCIPConfigTransactorSession) AcceptOwnership() (*types.Transa return _CCIPConfig.Contract.AcceptOwnership(&_CCIPConfig.TransactOpts) } -func (_CCIPConfig *CCIPConfigTransactor) ApplyChainConfigUpdates(opts *bind.TransactOpts, chainSelectorRemoves []uint64, chainConfigAdds []CCIPConfigChainConfigInfo) (*types.Transaction, error) { +func (_CCIPConfig *CCIPConfigTransactor) ApplyChainConfigUpdates(opts *bind.TransactOpts, chainSelectorRemoves []uint64, chainConfigAdds []CCIPConfigTypesChainConfigInfo) (*types.Transaction, error) { return _CCIPConfig.contract.Transact(opts, "applyChainConfigUpdates", chainSelectorRemoves, chainConfigAdds) } -func (_CCIPConfig *CCIPConfigSession) ApplyChainConfigUpdates(chainSelectorRemoves []uint64, chainConfigAdds []CCIPConfigChainConfigInfo) (*types.Transaction, error) { +func (_CCIPConfig *CCIPConfigSession) ApplyChainConfigUpdates(chainSelectorRemoves []uint64, chainConfigAdds []CCIPConfigTypesChainConfigInfo) (*types.Transaction, error) { return _CCIPConfig.Contract.ApplyChainConfigUpdates(&_CCIPConfig.TransactOpts, chainSelectorRemoves, chainConfigAdds) } -func (_CCIPConfig *CCIPConfigTransactorSession) ApplyChainConfigUpdates(chainSelectorRemoves []uint64, chainConfigAdds []CCIPConfigChainConfigInfo) (*types.Transaction, error) { +func (_CCIPConfig *CCIPConfigTransactorSession) ApplyChainConfigUpdates(chainSelectorRemoves []uint64, chainConfigAdds []CCIPConfigTypesChainConfigInfo) (*types.Transaction, error) { return _CCIPConfig.Contract.ApplyChainConfigUpdates(&_CCIPConfig.TransactOpts, chainSelectorRemoves, chainConfigAdds) } @@ -676,7 +676,7 @@ func (it *CCIPConfigChainConfigSetIterator) Close() error { type CCIPConfigChainConfigSet struct { ChainSelector uint64 - ChainConfig CCIPConfigChainConfig + ChainConfig CCIPConfigTypesChainConfig Raw types.Log } @@ -1047,11 +1047,11 @@ func (_CCIPConfig *CCIPConfig) Address() common.Address { } type CCIPConfigInterface interface { - GetAllChainConfigs(opts *bind.CallOpts) ([]CCIPConfigChainConfigInfo, error) + GetAllChainConfigs(opts *bind.CallOpts) ([]CCIPConfigTypesChainConfigInfo, error) GetCapabilityConfiguration(opts *bind.CallOpts, arg0 uint32) ([]byte, error) - GetOCRConfig(opts *bind.CallOpts, donId uint32, pluginType uint8) ([]CCIPConfigOCR3ConfigWithMeta, error) + GetOCRConfig(opts *bind.CallOpts, donId uint32, pluginType uint8) ([]CCIPConfigTypesOCR3ConfigWithMeta, error) Owner(opts *bind.CallOpts) (common.Address, error) @@ -1061,7 +1061,7 @@ type CCIPConfigInterface interface { AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) - ApplyChainConfigUpdates(opts *bind.TransactOpts, chainSelectorRemoves []uint64, chainConfigAdds []CCIPConfigChainConfigInfo) (*types.Transaction, error) + ApplyChainConfigUpdates(opts *bind.TransactOpts, chainSelectorRemoves []uint64, chainConfigAdds []CCIPConfigTypesChainConfigInfo) (*types.Transaction, error) BeforeCapabilityConfigSet(opts *bind.TransactOpts, arg0 [][32]byte, config []byte, arg2 uint64, donId uint32) (*types.Transaction, error) diff --git a/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp/evm_2_evm_multi_offramp.go b/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp/evm_2_evm_multi_offramp.go index 70f1c46dda..b2fcdc4670 100644 --- a/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp/evm_2_evm_multi_offramp.go +++ b/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp/evm_2_evm_multi_offramp.go @@ -69,24 +69,22 @@ type EVM2EVMMultiOffRampMerkleRoot struct { } type EVM2EVMMultiOffRampSourceChainConfig struct { - IsEnabled bool - MinSeqNr uint64 - PrevOffRamp common.Address - OnRamp common.Address - MetadataHash [32]byte + IsEnabled bool + MinSeqNr uint64 + OnRamp []byte } type EVM2EVMMultiOffRampSourceChainConfigArgs struct { SourceChainSelector uint64 IsEnabled bool - PrevOffRamp common.Address - OnRamp common.Address + OnRamp []byte } type EVM2EVMMultiOffRampStaticConfig struct { ChainSelector uint64 RmnProxy common.Address TokenAdminRegistry common.Address + NonceManager common.Address } type EVM2EVMMultiOffRampUnblessedRoot struct { @@ -94,25 +92,19 @@ type EVM2EVMMultiOffRampUnblessedRoot struct { MerkleRoot [32]byte } -type InternalEVM2EVMMessage struct { - SourceChainSelector uint64 - Sender common.Address - Receiver common.Address - SequenceNumber uint64 - GasLimit *big.Int - Strict bool - Nonce uint64 - FeeToken common.Address - FeeTokenAmount *big.Int - Data []byte - TokenAmounts []ClientEVMTokenAmount - SourceTokenData [][]byte - MessageId [32]byte +type InternalAny2EVMRampMessage struct { + Header InternalRampMessageHeader + Sender []byte + Data []byte + Receiver common.Address + GasLimit *big.Int + TokenAmounts []ClientEVMTokenAmount + SourceTokenData [][]byte } type InternalExecutionReportSingleChain struct { SourceChainSelector uint64 - Messages []InternalEVM2EVMMessage + Messages []InternalAny2EVMRampMessage OffchainTokenData [][][]byte Proofs [][32]byte ProofFlagBits *big.Int @@ -128,6 +120,14 @@ type InternalPriceUpdates struct { GasPriceUpdates []InternalGasPriceUpdate } +type InternalRampMessageHeader struct { + MessageId [32]byte + SourceChainSelector uint64 + DestChainSelector uint64 + SequenceNumber uint64 + Nonce uint64 +} + type InternalTokenPriceUpdate struct { SourceToken common.Address UsdPerToken *big.Int @@ -156,8 +156,8 @@ type MultiOCR3BaseOCRConfigArgs struct { } var EVM2EVMMultiOffRampMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"prevOffRamp\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"onRamp\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"AlreadyAttempted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"AlreadyExecuted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CanOnlySelfCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"actual\",\"type\":\"bytes32\"}],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"error\",\"type\":\"bytes\"}],\"name\":\"ExecutionError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"ForkedChain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"enumMultiOCR3Base.InvalidConfigErrorType\",\"name\":\"errorType\",\"type\":\"uint8\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"got\",\"type\":\"uint256\"}],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidEVMAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"min\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"max\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.Interval\",\"name\":\"interval\",\"type\":\"tuple\"}],\"name\":\"InvalidInterval\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"newLimit\",\"type\":\"uint256\"}],\"name\":\"InvalidManualExecutionGasLimit\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"}],\"name\":\"InvalidMessageId\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"newState\",\"type\":\"uint8\"}],\"name\":\"InvalidNewState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRoot\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidStaticConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LeavesCannotBeEmpty\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ManualExecutionGasLimitMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"ManualExecutionNotYetEnabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorReason\",\"type\":\"bytes\"}],\"name\":\"MessageValidationError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonUniqueSignatures\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"notPool\",\"type\":\"address\"}],\"name\":\"NotACompatiblePool\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OracleCannotBeZeroAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"error\",\"type\":\"bytes\"}],\"name\":\"ReceiverError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"name\":\"RootAlreadyCommitted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"RootNotCommitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SignaturesOutOfRegistration\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StaleCommitReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"StaticConfigCannotBeChanged\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"TokenDataMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"error\",\"type\":\"bytes\"}],\"name\":\"TokenHandlingError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnexpectedTokenData\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"WrongMessageLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WrongNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroChainSelectorNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"min\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"max\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.Interval\",\"name\":\"interval\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.MerkleRoot[]\",\"name\":\"merkleRoots\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.CommitReport\",\"name\":\"report\",\"type\":\"tuple\"}],\"name\":\"CommitReportAccepted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"DynamicConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"state\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"name\":\"ExecutionStateChanged\",\"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\":\"root\",\"type\":\"bytes32\"}],\"name\":\"RootRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"SkippedAlreadyExecutedMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SkippedIncorrectNonce\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SkippedSenderWithPreviousRampMessageInflight\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"prevOffRamp\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"onRamp\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"metadataHash\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfig\",\"name\":\"sourceConfig\",\"type\":\"tuple\"}],\"name\":\"SourceChainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainSelectorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"}],\"name\":\"StaticConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"prevOffRamp\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"onRamp\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigUpdates\",\"type\":\"tuple[]\"}],\"name\":\"applySourceChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"destTokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structClient.Any2EVMMessage\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"ccipReceive\",\"outputs\":[],\"stateMutability\":\"pure\",\"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\":\"commit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"}],\"name\":\"execute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"strict\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"}],\"internalType\":\"structInternal.EVM2EVMMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[]\"}],\"name\":\"executeSingleMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDynamicConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"getExecutionState\",\"outputs\":[{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestPriceSequenceNumber\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"getMerkleRoot\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"getSenderNonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"getSourceChainConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"prevOffRamp\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"onRamp\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"metadataHash\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStaticConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"isBlessed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"latestConfigDetails\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"n\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"}],\"internalType\":\"structMultiOCR3Base.ConfigInfo\",\"name\":\"configInfo\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfig\",\"name\":\"ocrConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"strict\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"}],\"internalType\":\"structInternal.EVM2EVMMessage[]\",\"name\":\"messages\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[][]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[][]\"},{\"internalType\":\"bytes32[]\",\"name\":\"proofs\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofFlagBits\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.ExecutionReportSingleChain[]\",\"name\":\"reports\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[][]\",\"name\":\"gasLimitOverrides\",\"type\":\"uint256[][]\"}],\"name\":\"manuallyExecute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.UnblessedRoot[]\",\"name\":\"rootToReset\",\"type\":\"tuple[]\"}],\"name\":\"resetUnblessedRoots\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"setDynamicConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfigArgs[]\",\"name\":\"ocrConfigArgs\",\"type\":\"tuple[]\"}],\"name\":\"setOCR3Configs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x6101006040523480156200001257600080fd5b506040516200678738038062006787833981016040819052620000359162000648565b33806000816200008c5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000bf57620000bf81620001c1565b5050466080525060208201516001600160a01b03161580620000ec575060408201516001600160a01b0316155b156200010b576040516342bcdf7f60e11b815260040160405180910390fd5b81516001600160401b0316600003620001375760405163c656089560e01b815260040160405180910390fd5b81516001600160401b0390811660a052602080840180516001600160a01b0390811660c05260408087018051831660e052815188519096168652925182169385019390935290511682820152517f2f56698ec552a5d53d27d6f4b3dd8b6989f6426b6151a36aff61160c1d07efdf9181900360600190a1620001b9816200026c565b5050620007d0565b336001600160a01b038216036200021b5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000083565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60005b81518110156200051c576000828281518110620002905762000290620007ba565b60200260200101519050600081600001519050806001600160401b0316600003620002ce5760405163c656089560e01b815260040160405180910390fd5b60608201516001600160a01b0316620002fa576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160401b038116600090815260076020526040902060018101546001600160a01b031662000400576200035c8284606001517f8acd72527118c8324937b1a42e02cd246697c3b633f1742f3cae11de233722b36200052060201b60201c565b600282015560608301516001820180546001600160a01b039283166001600160a01b03199091161790556040808501518354610100600160481b03199190931669010000000000000000000216610100600160e81b031990921691909117610100178255516001600160401b03831681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a16200046f565b606083015160018201546001600160a01b03908116911614158062000444575060408301518154690100000000000000000090046001600160a01b03908116911614155b156200046f5760405163c39a620560e01b81526001600160401b038316600482015260240162000083565b6020830151815490151560ff199091161781556040516001600160401b038316907fa73c588738263db34ef8c1942db8f99559bc6696f6a812d42e76bafb4c0e8d309062000505908490815460ff811615158252600881901c6001600160401b0316602083015260481c6001600160a01b0390811660408301526001830154166060820152600290910154608082015260a00190565b60405180910390a25050508060010190506200026f565b5050565b60a0805160408051602081018590526001600160401b0380881692820192909252911660608201526001600160a01b0384166080820152600091016040516020818303038152906040528051906020012090509392505050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715620005b557620005b56200057a565b60405290565b604051608081016001600160401b0381118282101715620005b557620005b56200057a565b604051601f8201601f191681016001600160401b03811182821017156200060b576200060b6200057a565b604052919050565b80516001600160401b03811681146200062b57600080fd5b919050565b80516001600160a01b03811681146200062b57600080fd5b6000808284036080808212156200065e57600080fd5b6060808312156200066e57600080fd5b6200067862000590565b9250620006858662000613565b835260206200069681880162000630565b818501526040620006aa6040890162000630565b604086015260608801519496506001600160401b0380861115620006cd57600080fd5b858901955089601f870112620006e257600080fd5b855181811115620006f757620006f76200057a565b62000707848260051b01620005e0565b818152848101925060079190911b87018401908b8211156200072857600080fd5b968401965b81881015620007a85786888d031215620007475760008081fd5b62000751620005bb565b6200075c8962000613565b8152858901518015158114620007725760008081fd5b818701526200078389860162000630565b8582015262000794878a0162000630565b81880152835296860196918401916200072d565b80985050505050505050509250929050565b634e487b7160e01b600052603260045260246000fd5b60805160a05160c05160e051615f5a6200082d6000396000818161021301526131a40152600081816101e401528181611573015261162a0152600081816101b401526130c6015260008181611bc60152611c120152615f5a6000f3fe608060405234801561001057600080fd5b50600436106101825760003560e01c806385572ffb116100d8578063d2a15d351161008c578063f52121a511610066578063f52121a514610668578063f716f99f1461067b578063ff888fb11461068e57600080fd5b8063d2a15d3514610534578063e9d68a8e14610547578063f2fde38b1461065557600080fd5b80638da5cb5b116100bd5780638da5cb5b146104b4578063c673e584146104cf578063ccd37ba3146104ef57600080fd5b806385572ffb146104935780638b364334146104a157600080fd5b8063403b2d631161013a5780637437ff9f116101145780637437ff9f1461036357806379ba5097146104785780637f63b7111461048057600080fd5b8063403b2d631461031d578063542625af146103305780635e36480c1461034357600080fd5b80632d04ab761161016b5780632d04ab76146102ce578063311cd513146102e35780633f4b04aa146102f657600080fd5b806306285c6914610187578063181f5a7714610285575b600080fd5b610243604080516060810182526000808252602082018190529181019190915260405180606001604052807f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250905090565b60408051825167ffffffffffffffff1681526020808401516001600160a01b039081169183019190915292820151909216908201526060015b60405180910390f35b6102c16040518060400160405280601d81526020017f45564d3245564d4d756c74694f666652616d7020312e362e302d64657600000081525081565b60405161027c91906141e0565b6102e16102dc36600461428b565b6106b1565b005b6102e16102f136600461433e565b610a77565b600b5467ffffffffffffffff165b60405167ffffffffffffffff909116815260200161027c565b6102e161032b3660046144eb565b610ae0565b6102e161033e366004614b0f565b610c9d565b610356610351366004614c3a565b610e42565b60405161027c9190614c9d565b61040f6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506040805160c0810182526004546001600160a01b03808216835263ffffffff74010000000000000000000000000000000000000000830481166020850152600160c01b8304811694840194909452600160e01b90910490921660608201526005548216608082015260065490911660a082015290565b60405161027c9190600060c0820190506001600160a01b03808451168352602084015163ffffffff808216602086015280604087015116604086015280606087015116606086015250508060808501511660808401528060a08501511660a08401525092915050565b6102e1610e98565b6102e161048e366004614cab565b610f56565b6102e1610182366004614d8f565b6103046104af366004614dca565b610f6a565b6000546040516001600160a01b03909116815260200161027c565b6104e26104dd366004614e09565b610f80565b60405161027c9190614e69565b6105266104fd366004614ede565b67ffffffffffffffff919091166000908152600a60209081526040808320938352929052205490565b60405190815260200161027c565b6102e1610542366004614f0a565b6110de565b6105f8610555366004614f7f565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091525067ffffffffffffffff908116600090815260076020908152604091829020825160a081018452815460ff8116151582526101008104909516928101929092526001600160a01b0369010000000000000000009094048416928201929092526001820154909216606083015260020154608082015290565b60405161027c9190600060a08201905082511515825267ffffffffffffffff602084015116602083015260408301516001600160a01b03808216604085015280606086015116606085015250506080830151608083015292915050565b6102e1610663366004614f9c565b611198565b6102e1610676366004614fb9565b6111a9565b6102e1610689366004615085565b6114db565b6106a161069c3660046151d0565b61151d565b604051901515815260200161027c565b60006106bf87890189615361565b805151519091501515806106d857508051602001515115155b156107d857600b5460208a01359067ffffffffffffffff8083169116101561079757600b805467ffffffffffffffff191667ffffffffffffffff831617905560065482516040517f3937306f0000000000000000000000000000000000000000000000000000000081526001600160a01b0390921691633937306f916107609160040161559f565b600060405180830381600087803b15801561077a57600080fd5b505af115801561078e573d6000803e3d6000fd5b505050506107d6565b8160200151516000036107d6576040517f2261116700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b60005b8160200151518110156109c057600082602001518281518110610800576108006154cc565b6020026020010151905060008160000151905061081c816115de565b6000610827826116e0565b602084015151815491925067ffffffffffffffff90811661010090920416141580610869575060208084015190810151905167ffffffffffffffff9182169116115b156108b257825160208401516040517feefb0cac0000000000000000000000000000000000000000000000000000000081526108a99291906004016155b2565b60405180910390fd5b6040830151806108ee576040517f504570e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b835167ffffffffffffffff166000908152600a60209081526040808320848452909152902054156109615783516040517f32cf0cbf00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602481018290526044016108a9565b60208085015101516109749060016155fd565b825468ffffffffffffffff00191661010067ffffffffffffffff92831602179092559251166000908152600a6020908152604080832094835293905291909120429055506001016107db565b507f3a3950e13dd607cc37980db0ef14266c40d2bba9c01b2e44bfe549808883095d816040516109f09190615625565b60405180910390a1610a6c60008a8a8a8a8a8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808e0282810182019093528d82529093508d92508c9182918501908490808284376000920191909152508b9250611740915050565b505050505050505050565b610ab7610a86828401846156c2565b6040805160008082526020820190925290610ab1565b6060815260200190600190039081610a9c5790505b50611ab7565b604080516000808252602082019092529050610ada600185858585866000611740565b50505050565b610ae8611b67565b60a08101516001600160a01b03161580610b0a575080516001600160a01b0316155b15610b41576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516004805460208085018051604080880180516060808b0180516001600160a01b039b8c167fffffffffffffffff000000000000000000000000000000000000000000000000909a168a177401000000000000000000000000000000000000000063ffffffff988916021777ffffffffffffffffffffffffffffffffffffffffffffffff16600160c01b948816949094026001600160e01b031693909317600160e01b93871693909302929092179098556080808b0180516005805473ffffffffffffffffffffffffffffffffffffffff19908116928e1692909217905560a0808e01805160068054909416908f161790925586519a8b5297518716988a0198909852925185169388019390935251909216958501959095525185169383019390935251909216908201527f0da37fd00459f4f5f0b8210d31525e4910ae674b8bab34b561d146bb45773a4c9060c00160405180910390a150565b610ca5611bc3565b815181518114610ce1576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610e32576000848281518110610d0057610d006154cc565b60200260200101519050600081602001515190506000858481518110610d2857610d286154cc565b6020026020010151905080518214610d6c576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b82811015610e23576000828281518110610d8b57610d8b6154cc565b6020026020010151905080600014158015610dc6575084602001518281518110610db757610db76154cc565b60200260200101516080015181105b15610e1a5784516040517fc8e9605100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff909116600482015260248101839052604481018290526064016108a9565b50600101610d6f565b50505050806001019050610ce4565b50610e3d8383611ab7565b505050565b6000610e50600160046156f7565b6002610e5d608085615720565b67ffffffffffffffff16610e719190615747565b610e7b8585611c44565b901c166003811115610e8f57610e8f614c73565b90505b92915050565b6001546001600160a01b03163314610ef25760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016108a9565b600080543373ffffffffffffffffffffffffffffffffffffffff19808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610f5e611b67565b610f6781611c8b565b50565b600080610f778484611f9c565b50949350505050565b610fc36040805160e081019091526000606082018181526080830182905260a0830182905260c08301919091528190815260200160608152602001606081525090565b60ff808316600090815260026020818152604092839020835160e081018552815460608201908152600183015480881660808401526101008104881660a0840152620100009004909616151560c08201529485529182018054845181840281018401909552808552929385830193909283018282801561106c57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161104e575b50505050508152602001600382018054806020026020016040519081016040528092919081815260200182805480156110ce57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116110b0575b5050505050815250509050919050565b6110e6611b67565b60005b81811015610e3d576000838383818110611105576111056154cc565b90506040020180360381019061111b919061575e565b905061112a816020015161151d565b61118f57805167ffffffffffffffff166000908152600a602090815260408083208285018051855290835281842093909355915191519182527f202f1139a3e334b6056064c0e9b19fd07e44a88d8f6e5ded571b24cf8c371f12910160405180910390a15b506001016110e9565b6111a0611b67565b610f67816120ad565b3330146111e2576040517f371a732800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516000808252602082019092528161121f565b60408051808201909152600080825260208201528152602001906001900390816111f85790505b5061014084015151909150156112805761127d836101400151846020015160405160200161125c91906001600160a01b0391909116815260200190565b60408051601f19818403018152918152860151865161016088015187612163565b90505b600061128c8483612210565b6005549091506001600160a01b03168015611379576040517f08d450a10000000000000000000000000000000000000000000000000000000081526001600160a01b038216906308d450a1906112e6908590600401615846565b600060405180830381600087803b15801561130057600080fd5b505af1925050508015611311575060015b611379573d80801561133f576040519150601f19603f3d011682016040523d82523d6000602084013e611344565b606091505b50806040517f09c253250000000000000000000000000000000000000000000000000000000081526004016108a991906141e0565b6101208501515115801561138f57506080850151155b806113a6575060408501516001600160a01b03163b155b806113e6575060408501516113e4906001600160a01b03167f85572ffb000000000000000000000000000000000000000000000000000000006122b3565b155b156113f2575050505050565b60048054608087015160408089015190517f3cf9798300000000000000000000000000000000000000000000000000000000815260009485946001600160a01b031693633cf979839361144d938a9361138893929101615859565b6000604051808303816000875af115801561146c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261149491908101906158da565b5091509150816114d257806040517f0a8d6e8c0000000000000000000000000000000000000000000000000000000081526004016108a991906141e0565b50505050505050565b6114e3611b67565b60005b815181101561151957611511828281518110611504576115046154cc565b60200260200101516122cf565b6001016114e6565b5050565b6040805180820182523081526020810183815291517f4d61677100000000000000000000000000000000000000000000000000000000815290516001600160a01b039081166004830152915160248201526000917f00000000000000000000000000000000000000000000000000000000000000001690634d61677190604401602060405180830381865afa1580156115ba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e929190615934565b6040517f2cbc26bb000000000000000000000000000000000000000000000000000000008152608082901b77ffffffffffffffff000000000000000000000000000000001660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632cbc26bb90602401602060405180830381865afa158015611679573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061169d9190615934565b15610f67576040517ffdbd6a7200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108a9565b67ffffffffffffffff81166000908152600760205260408120805460ff16610e92576040517fed053c5900000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016108a9565b60ff8781166000908152600260209081526040808320815160808101835281548152600190910154808616938201939093526101008304851691810191909152620100009091049092161515606083015287359061179f8760a4615951565b90508260600151156117e75784516117b8906020615747565b86516117c5906020615747565b6117d09060a0615951565b6117da9190615951565b6117e49082615951565b90505b368114611829576040517f8e1192e1000000000000000000000000000000000000000000000000000000008152600481018290523660248201526044016108a9565b50815181146118715781516040517f93df584c0000000000000000000000000000000000000000000000000000000081526004810191909152602481018290526044016108a9565b611879611bc3565b60ff808a16600090815260036020908152604080832033845282528083208151808301909252805480861683529394919390928401916101009091041660028111156118c7576118c7614c73565b60028111156118d8576118d8614c73565b90525090506002816020015160028111156118f5576118f5614c73565b1480156119495750600260008b60ff1660ff168152602001908152602001600020600301816000015160ff1681548110611931576119316154cc565b6000918252602090912001546001600160a01b031633145b61197f576040517fda0f08e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50816060015115611a6157602082015161199a906001615964565b60ff168551146119d6576040517f71253a2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8351855114611a11576040517fa75d88af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008787604051611a2392919061597d565b604051908190038120611a3a918b9060200161598d565b604051602081830303815290604052805190602001209050611a5f8a828888886125da565b505b6040805182815260208a81013567ffffffffffffffff169082015260ff8b16917f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef0910160405180910390a2505050505050505050565b8151600003611af1576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160408051600080825260208201909252911591905b8451811015611b6057611b58858281518110611b2657611b266154cc565b602002602001015184611b5257858381518110611b4557611b456154cc565b60200260200101516127f1565b836127f1565b600101611b08565b5050505050565b6000546001600160a01b03163314611bc15760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016108a9565b565b467f000000000000000000000000000000000000000000000000000000000000000014611bc1576040517f0f01ce850000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201524660248201526044016108a9565b67ffffffffffffffff8216600090815260096020526040812081611c696080856159a1565b67ffffffffffffffff1681526020810191909152604001600020549392505050565b60005b8151811015611519576000828281518110611cab57611cab6154cc565b602002602001015190506000816000015190508067ffffffffffffffff16600003611d02576040517fc656089500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608201516001600160a01b0316611d46576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8116600090815260076020526040902060018101546001600160a01b0316611e6957611da08284606001517f8acd72527118c8324937b1a42e02cd246697c3b633f1742f3cae11de233722b36130c0565b600282015560608301516001820180546001600160a01b0392831673ffffffffffffffffffffffffffffffffffffffff19909116179055604080850151835468ffffffffffffffff001991909316690100000000000000000002167fffffff00000000000000000000000000000000000000000000000000000000ff909216919091176101001782555167ffffffffffffffff831681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a1611eef565b606083015160018201546001600160a01b039081169116141580611eac575060408301518154690100000000000000000090046001600160a01b03908116911614155b15611eef576040517fc39a620500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff831660048201526024016108a9565b6020830151815490151560ff1990911617815560405167ffffffffffffffff8316907fa73c588738263db34ef8c1942db8f99559bc6696f6a812d42e76bafb4c0e8d3090611f86908490815460ff811615158252600881901c67ffffffffffffffff16602083015260481c6001600160a01b0390811660408301526001830154166060820152600290910154608082015260a00190565b60405180910390a2505050806001019050611c8e565b67ffffffffffffffff80831660009081526008602090815260408083206001600160a01b0386168452909152812054909182911680820361209f5767ffffffffffffffff8516600090815260076020526040902054690100000000000000000090046001600160a01b0316801561209d576040517f856c82470000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015282169063856c824790602401602060405180830381865afa15801561206c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061209091906159c8565b60019350935050506120a6565b505b9150600090505b9250929050565b336001600160a01b038216036121055760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016108a9565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b8560005b8751811015612205576121e0888281518110612185576121856154cc565b6020026020010151602001518888888886815181106121a6576121a66154cc565b60200260200101518060200190518101906121c191906159e5565b8887815181106121d3576121d36154cc565b6020026020010151613143565b8282815181106121f2576121f26154cc565b6020908102919091010152600101612167565b509695505050505050565b6040805160a08101825260008082526020820152606091810182905281810182905260808101919091526040518060a001604052808461018001518152602001846000015167ffffffffffffffff168152602001846020015160405160200161228891906001600160a01b0391909116815260200190565b6040516020818303038152906040528152602001846101200151815260200183815250905092915050565b60006122be836134bc565b8015610e8f5750610e8f8383613508565b806040015160ff166000036122fa576000604051631b3fab5160e11b81526004016108a99190615a9a565b60208082015160ff8082166000908152600290935260408320600181015492939092839216900361234b576060840151600182018054911515620100000262ff0000199092169190911790556123a0565b6060840151600182015460ff62010000909104161515901515146123a0576040517f87f6037c00000000000000000000000000000000000000000000000000000000815260ff841660048201526024016108a9565b60a08401518051601f60ff821611156123cf576001604051631b3fab5160e11b81526004016108a99190615a9a565b612435858560030180548060200260200160405190810160405280929190818152602001828054801561242b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161240d575b50505050506135aa565b856060015115612547576124a3858560020180548060200260200160405190810160405280929190818152602001828054801561242b576020028201919060005260206000209081546001600160a01b0316815260019091019060200180831161240d5750505050506135aa565b608086015180516124bd90600287019060208401906140ee565b50805160018501805461ff00191661010060ff841690810291909117909155601f1015612500576002604051631b3fab5160e11b81526004016108a99190615a9a565b6040880151612510906003615ab4565b60ff168160ff1611612538576003604051631b3fab5160e11b81526004016108a99190615a9a565b61254487836001613613565b50505b61255385836002613613565b815161256890600386019060208501906140ee565b5060408681015160018501805460ff191660ff8316179055875180865560a089015192517fab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f547936125c1938a939260028b01929190615ad0565b60405180910390a16125d285613793565b505050505050565b6125e261415c565b835160005b818110156127e7576000600188868460208110612606576126066154cc565b61261391901a601b615964565b898581518110612625576126256154cc565b602002602001015189868151811061263f5761263f6154cc565b60200260200101516040516000815260200160405260405161267d949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa15801561269f573d6000803e3d6000fd5b505060408051601f1981015160ff808e166000908152600360209081528582206001600160a01b0385168352815285822085870190965285548084168652939750909550929392840191610100900416600281111561270057612700614c73565b600281111561271157612711614c73565b905250905060018160200151600281111561272e5761272e614c73565b14612765576040517fca31867a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051859060ff16601f811061277c5761277c6154cc565b6020020151156127b8576040517ff67bc7c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600185826000015160ff16601f81106127d3576127d36154cc565b9115156020909202015250506001016125e7565b5050505050505050565b81516127fc816115de565b6000612807826116e0565b602085015151909150600081900361284a576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460400151518114612888576040517f57e0e08300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008167ffffffffffffffff8111156128a3576128a3614392565b6040519080825280602002602001820160405280156128cc578160200160208202803683370190505b50905060005b82811015612991576000876020015182815181106128f2576128f26154cc565b6020026020010151905061290a8186600201546137af565b83838151811061291c5761291c6154cc565b602002602001018181525050806101800151838381518110612940576129406154cc565b602002602001015114612988578061018001516040517f345039be0000000000000000000000000000000000000000000000000000000081526004016108a991815260200190565b506001016128d2565b5060006129a8858389606001518a6080015161390b565b9050806000036129f0576040517f7dd17a7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff861660048201526024016108a9565b8551151560005b84811015610a6c57600089602001518281518110612a1757612a176154cc565b602002602001015190506000612a31898360600151610e42565b90506002816003811115612a4757612a47614c73565b03612a9d5760608201516040805167ffffffffffffffff808d16825290921660208301527f3b575419319662b2a6f5e2467d84521517a3382b908eb3d557bb3fdb0c50e23c910160405180910390a150506130b8565b6000816003811115612ab157612ab1614c73565b1480612ace57506003816003811115612acc57612acc614c73565b145b612b1e5760608201516040517f25507e7f00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808c16600483015290911660248201526044016108a9565b8315612bff5760045460009074010000000000000000000000000000000000000000900463ffffffff16612b5287426156f7565b1190508080612b7257506003826003811115612b7057612b70614c73565b145b612bb4576040517fa9cfc86200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8b1660048201526024016108a9565b8a8481518110612bc657612bc66154cc565b6020026020010151600014612bf9578a8481518110612be757612be76154cc565b60200260200101518360800181815250505b50612c64565b6000816003811115612c1357612c13614c73565b14612c645760608201516040517f3ef2a99c00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808c16600483015290911660248201526044016108a9565b60c082015167ffffffffffffffff1615612e2057600080612c898b8560200151611f9c565b915091508015612d785760c084015167ffffffffffffffff16612cad8360016155fd565b67ffffffffffffffff1614612d305760c084015160208501516040517f5444a3301c7c42dd164cbf6ba4b72bf02504f86c049b06a27fc2b662e334bdbd92612d1f928f9267ffffffffffffffff93841681529190921660208201526001600160a01b0391909116604082015260600190565b60405180910390a1505050506130b8565b67ffffffffffffffff8b81166000908152600860209081526040808320888301516001600160a01b031684529091529020805467ffffffffffffffff19169184169190911790555b6000836003811115612d8c57612d8c614c73565b03612e1d5760c084015167ffffffffffffffff16612dab8360016155fd565b67ffffffffffffffff1614612e1d5760c084015160208501516040517f852dc8e405695593e311bd83991cf39b14a328f304935eac6d3d55617f911d8992612d1f928f9267ffffffffffffffff93841681529190921660208201526001600160a01b0391909116604082015260600190565b50505b60008b604001518481518110612e3857612e386154cc565b6020026020010151905080518361014001515114612e9c5760608301516040517f1cfe6d8b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808d16600483015290911660248201526044016108a9565b612eac8a84606001516001613961565b600080612eb98584613a09565b91509150612ecc8c866060015184613961565b868015612eea57506003826003811115612ee857612ee8614c73565b145b8015612f0857506000846003811115612f0557612f05614c73565b14155b15612f4857846101800151816040517f2b11b8d90000000000000000000000000000000000000000000000000000000081526004016108a9929190615b56565b6003826003811115612f5c57612f5c614c73565b14158015612f7c57506002826003811115612f7957612f79614c73565b14155b15612fbc5760608501516040517f926c5a3e0000000000000000000000000000000000000000000000000000000081526108a9918e918590600401615b6f565b60008560c0015167ffffffffffffffff16118015612feb57506000846003811115612fe957612fe9614c73565b145b156130595767ffffffffffffffff808d166000908152600860209081526040808320898301516001600160a01b0316845290915281208054909216919061303183615b95565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505b846101800151856060015167ffffffffffffffff168d67ffffffffffffffff167f8c324ce1367b83031769f6a813e3bb4c117aba2185789d66b98b791405be6df285856040516130aa929190615bbc565b60405180910390a450505050505b6001016129f7565b600081847f000000000000000000000000000000000000000000000000000000000000000085604051602001613123949392919093845267ffffffffffffffff9283166020850152911660408301526001600160a01b0316606082015260800190565b6040516020818303038152906040528051906020012090505b9392505050565b604080518082019091526000808252602082015260006131668460200151613c53565b6040517fbbe4f6db0000000000000000000000000000000000000000000000000000000081526001600160a01b0380831660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063bbe4f6db90602401602060405180830381865afa1580156131eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061320f9190615bdc565b90506001600160a01b038116158061325757506132556001600160a01b0382167faff2afbf000000000000000000000000000000000000000000000000000000006122b3565b155b15613299576040517fae9b4ce90000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016108a9565b600080613364633907753760e01b6040518061010001604052808d81526020018b67ffffffffffffffff1681526020018c6001600160a01b031681526020018e8152602001876001600160a01b031681526020018a6000015181526020018a604001518152602001898152506040516024016133159190615bf9565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152600454859063ffffffff600160e01b909104166113886084613c95565b509150915081613389578060405163e1cd550960e01b81526004016108a991906141e0565b80516020146133d15780516040517f78ef80240000000000000000000000000000000000000000000000000000000081526020600482015260248101919091526044016108a9565b6000818060200190518101906133e79190615cd0565b604080516001600160a01b038d16602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b03167fa9059cbb0000000000000000000000000000000000000000000000000000000017905260045491925061346a918790600160c01b900463ffffffff166113886084613c95565b50909350915082613490578160405163e1cd550960e01b81526004016108a991906141e0565b604080518082019091526001600160a01b03909516855260208501525091925050509695505050505050565b60006134e8827f01ffc9a700000000000000000000000000000000000000000000000000000000613508565b8015610e925750613501826001600160e01b0319613508565b1592915050565b604080516001600160e01b03198316602480830191909152825180830390910181526044909101909152602080820180516001600160e01b03167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d91506000519050828015613593575060208210155b801561359f5750600081115b979650505050505050565b60005b8151811015610e3d5760ff8316600090815260036020526040812083519091908490849081106135df576135df6154cc565b6020908102919091018101516001600160a01b03168252810191909152604001600020805461ffff191690556001016135ad565b60005b82518160ff161015610ada576000838260ff1681518110613639576136396154cc565b602002602001015190506000600281111561365657613656614c73565b60ff80871660009081526003602090815260408083206001600160a01b0387168452909152902054610100900416600281111561369557613695614c73565b146136b6576004604051631b3fab5160e11b81526004016108a99190615a9a565b6001600160a01b0381166136f6576040517fd6c62c9b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052808360ff16815260200184600281111561371c5761371c614c73565b905260ff80871660009081526003602090815260408083206001600160a01b0387168452825290912083518154931660ff198416811782559184015190929091839161ffff19161761010083600281111561377957613779614c73565b0217905550905050508061378c90615ce9565b9050613616565b60ff8116610f6757600b805467ffffffffffffffff1916905550565b60008060001b8284602001518560400151866060015187608001518860a001518960c001518a60e001518b61010001516040516020016138459897969594939291906001600160a01b039889168152968816602088015267ffffffffffffffff95861660408801526060870194909452911515608086015290921660a0840152921660c082015260e08101919091526101000190565b604051602081830303815290604052805190602001208561012001518051906020012086610140015160405160200161387e9190615d08565b604051602081830303815290604052805190602001208761016001516040516020016138aa9190615dbd565b60408051601f198184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e0015b60405160208183030381529060405280519060200120905092915050565b600080613919858585613dbb565b90506139248161151d565b613932576000915050613959565b67ffffffffffffffff86166000908152600a60209081526040808320938352929052205490505b949350505050565b60006002613970608085615720565b67ffffffffffffffff166139849190615747565b905060006139928585611c44565b9050816139a1600160046156f7565b901b1916818360038111156139b8576139b8614c73565b67ffffffffffffffff871660009081526009602052604081209190921b929092179182916139e76080886159a1565b67ffffffffffffffff1681526020810191909152604001600020555050505050565b6040517ff52121a5000000000000000000000000000000000000000000000000000000008152600090606090309063f52121a590613a4d9087908790600401615dd0565b600060405180830381600087803b158015613a6757600080fd5b505af1925050508015613a78575060015b613c38573d808015613aa6576040519150601f19603f3d011682016040523d82523d6000602084013e613aab565b606091505b506000613ab782615f15565b90507f0a8d6e8c000000000000000000000000000000000000000000000000000000006001600160e01b031982161480613b01575063e1cd550960e01b6001600160e01b03198216145b80613b1c575063046b337b60e51b6001600160e01b03198216145b80613b5057507f78ef8024000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b80613b8457507f0c3b563c000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b80613bb857507fae9b4ce9000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b80613bec57507f09c25325000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b15613bfd57506003925090506120a6565b856101800151826040517f2b11b8d90000000000000000000000000000000000000000000000000000000081526004016108a9929190615b56565b50506040805160208101909152600081526002909250929050565b60008151602014613c79578160405163046b337b60e51b81526004016108a991906141e0565b610e9282806020019051810190613c909190615cd0565b614059565b6000606060008361ffff1667ffffffffffffffff811115613cb857613cb8614392565b6040519080825280601f01601f191660200182016040528015613ce2576020820181803683370190505b509150863b613d15577f0c3b563c0000000000000000000000000000000000000000000000000000000060005260046000fd5b5a85811015613d48577fafa32a2c0000000000000000000000000000000000000000000000000000000060005260046000fd5b8590036040810481038710613d81577f37c3be290000000000000000000000000000000000000000000000000000000060005260046000fd5b505a6000808a5160208c0160008c8cf193505a900390503d84811115613da45750835b808352806000602085013e50955095509592505050565b8251825160009190818303613dfc576040517f11a6b26400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101018211801590613e1057506101018111155b613e2d576040516309bde33960e01b815260040160405180910390fd5b60001982820101610100811115613e57576040516309bde33960e01b815260040160405180910390fd5b80600003613e845786600081518110613e7257613e726154cc565b6020026020010151935050505061313c565b60008167ffffffffffffffff811115613e9f57613e9f614392565b604051908082528060200260200182016040528015613ec8578160200160208202803683370190505b50905060008080805b85811015613ff25760006001821b8b811603613f2c5788851015613f15578c5160018601958e918110613f0657613f066154cc565b60200260200101519050613f4e565b8551600185019487918110613f0657613f066154cc565b8b5160018401938d918110613f4357613f436154cc565b602002602001015190505b600089861015613f7e578d5160018701968f918110613f6f57613f6f6154cc565b60200260200101519050613fa0565b8651600186019588918110613f9557613f956154cc565b602002602001015190505b82851115613fc1576040516309bde33960e01b815260040160405180910390fd5b613fcb82826140ad565b878481518110613fdd57613fdd6154cc565b60209081029190910101525050600101613ed1565b50600185038214801561400457508683145b801561400f57508581145b61402c576040516309bde33960e01b815260040160405180910390fd5b836001860381518110614041576140416154cc565b60200260200101519750505050505050509392505050565b60006001600160a01b03821180614071575061040082105b156140a95760408051602081018490520160408051601f198184030181529082905263046b337b60e51b82526108a9916004016141e0565b5090565b60008183106140c5576140c082846140cb565b610e8f565b610e8f83835b6040805160016020820152908101839052606081018290526000906080016138ed565b828054828255906000526020600020908101928215614150579160200282015b82811115614150578251825473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0390911617825560209092019160019091019061410e565b506140a992915061417b565b604051806103e00160405280601f906020820280368337509192915050565b5b808211156140a9576000815560010161417c565b60005b838110156141ab578181015183820152602001614193565b50506000910152565b600081518084526141cc816020860160208601614190565b601f01601f19169290920160200192915050565b602081526000610e8f60208301846141b4565b8060608101831015610e9257600080fd5b60008083601f84011261421657600080fd5b50813567ffffffffffffffff81111561422e57600080fd5b6020830191508360208285010111156120a657600080fd5b60008083601f84011261425857600080fd5b50813567ffffffffffffffff81111561427057600080fd5b6020830191508360208260051b85010111156120a657600080fd5b60008060008060008060008060e0898b0312156142a757600080fd5b6142b18a8a6141f3565b9750606089013567ffffffffffffffff808211156142ce57600080fd5b6142da8c838d01614204565b909950975060808b01359150808211156142f357600080fd5b6142ff8c838d01614246565b909750955060a08b013591508082111561431857600080fd5b506143258b828c01614246565b999c989b50969995989497949560c00135949350505050565b60008060006080848603121561435357600080fd5b61435d85856141f3565b9250606084013567ffffffffffffffff81111561437957600080fd5b61438586828701614204565b9497909650939450505050565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff811182821017156143cb576143cb614392565b60405290565b6040805190810167ffffffffffffffff811182821017156143cb576143cb614392565b6040516101a0810167ffffffffffffffff811182821017156143cb576143cb614392565b60405160a0810167ffffffffffffffff811182821017156143cb576143cb614392565b6040516080810167ffffffffffffffff811182821017156143cb576143cb614392565b6040516060810167ffffffffffffffff811182821017156143cb576143cb614392565b604051601f8201601f1916810167ffffffffffffffff811182821017156144aa576144aa614392565b604052919050565b6001600160a01b0381168114610f6757600080fd5b80356144d2816144b2565b919050565b803563ffffffff811681146144d257600080fd5b600060c082840312156144fd57600080fd5b6145056143a8565b8235614510816144b2565b815261451e602084016144d7565b602082015261452f604084016144d7565b6040820152614540606084016144d7565b60608201526080830135614553816144b2565b608082015260a0830135614566816144b2565b60a08201529392505050565b600067ffffffffffffffff82111561458c5761458c614392565b5060051b60200190565b67ffffffffffffffff81168114610f6757600080fd5b80356144d281614596565b8015158114610f6757600080fd5b80356144d2816145b7565b600067ffffffffffffffff8211156145ea576145ea614392565b50601f01601f191660200190565b600082601f83011261460957600080fd5b813561461c614617826145d0565b614481565b81815284602083860101111561463157600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f83011261465f57600080fd5b8135602061466f61461783614572565b82815260069290921b8401810191818101908684111561468e57600080fd5b8286015b8481101561220557604081890312156146ab5760008081fd5b6146b36143d1565b81356146be816144b2565b81528185013585820152835291830191604001614692565b600082601f8301126146e757600080fd5b813560206146f761461783614572565b82815260059290921b8401810191818101908684111561471657600080fd5b8286015b8481101561220557803567ffffffffffffffff81111561473a5760008081fd5b6147488986838b01016145f8565b84525091830191830161471a565b60006101a0828403121561476957600080fd5b6147716143f4565b905061477c826145ac565b815261478a602083016144c7565b602082015261479b604083016144c7565b60408201526147ac606083016145ac565b6060820152608082013560808201526147c760a083016145c5565b60a08201526147d860c083016145ac565b60c08201526147e960e083016144c7565b60e082015261010082810135908201526101208083013567ffffffffffffffff8082111561481657600080fd5b614822868387016145f8565b8385015261014092508285013591508082111561483e57600080fd5b61484a8683870161464e565b8385015261016092508285013591508082111561486657600080fd5b50614873858286016146d6565b82840152505061018080830135818301525092915050565b600082601f83011261489c57600080fd5b813560206148ac61461783614572565b82815260059290921b840181019181810190868411156148cb57600080fd5b8286015b8481101561220557803567ffffffffffffffff8111156148ef5760008081fd5b6148fd8986838b0101614756565b8452509183019183016148cf565b600082601f83011261491c57600080fd5b8135602061492c61461783614572565b82815260059290921b8401810191818101908684111561494b57600080fd5b8286015b8481101561220557803567ffffffffffffffff81111561496f5760008081fd5b61497d8986838b01016146d6565b84525091830191830161494f565b600082601f83011261499c57600080fd5b813560206149ac61461783614572565b8083825260208201915060208460051b8701019350868411156149ce57600080fd5b602086015b8481101561220557803583529183019183016149d3565b600082601f8301126149fb57600080fd5b81356020614a0b61461783614572565b82815260059290921b84018101918181019086841115614a2a57600080fd5b8286015b8481101561220557803567ffffffffffffffff80821115614a4f5760008081fd5b9088019060a0828b03601f1901811315614a695760008081fd5b614a71614418565b614a7c8885016145ac565b815260408085013584811115614a925760008081fd5b614aa08e8b8389010161488b565b8a8401525060608086013585811115614ab95760008081fd5b614ac78f8c838a010161490b565b8385015250608091508186013585811115614ae25760008081fd5b614af08f8c838a010161498b565b9184019190915250919093013590830152508352918301918301614a2e565b6000806040808486031215614b2357600080fd5b833567ffffffffffffffff80821115614b3b57600080fd5b614b47878388016149ea565b9450602091508186013581811115614b5e57600080fd5b8601601f81018813614b6f57600080fd5b8035614b7d61461782614572565b81815260059190911b8201840190848101908a831115614b9c57600080fd5b8584015b83811015614c2857803586811115614bb85760008081fd5b8501603f81018d13614bca5760008081fd5b87810135614bda61461782614572565b81815260059190911b82018a0190898101908f831115614bfa5760008081fd5b928b01925b82841015614c185783358252928a0192908a0190614bff565b8652505050918601918601614ba0565b50809750505050505050509250929050565b60008060408385031215614c4d57600080fd5b8235614c5881614596565b91506020830135614c6881614596565b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b60048110614c9957614c99614c73565b9052565b60208101610e928284614c89565b60006020808385031215614cbe57600080fd5b823567ffffffffffffffff811115614cd557600080fd5b8301601f81018513614ce657600080fd5b8035614cf461461782614572565b81815260079190911b82018301908381019087831115614d1357600080fd5b928401925b8284101561359f5760808489031215614d315760008081fd5b614d3961443b565b8435614d4481614596565b815284860135614d53816145b7565b81870152604085810135614d66816144b2565b90820152606085810135614d79816144b2565b9082015282526080939093019290840190614d18565b600060208284031215614da157600080fd5b813567ffffffffffffffff811115614db857600080fd5b820160a0818503121561313c57600080fd5b60008060408385031215614ddd57600080fd5b8235614de881614596565b91506020830135614c68816144b2565b803560ff811681146144d257600080fd5b600060208284031215614e1b57600080fd5b610e8f82614df8565b60008151808452602080850194506020840160005b83811015614e5e5781516001600160a01b031687529582019590820190600101614e39565b509495945050505050565b60208152600082518051602084015260ff602082015116604084015260ff604082015116606084015260608101511515608084015250602083015160c060a0840152614eb860e0840182614e24565b90506040840151601f198483030160c0850152614ed58282614e24565b95945050505050565b60008060408385031215614ef157600080fd5b8235614efc81614596565b946020939093013593505050565b60008060208385031215614f1d57600080fd5b823567ffffffffffffffff80821115614f3557600080fd5b818501915085601f830112614f4957600080fd5b813581811115614f5857600080fd5b8660208260061b8501011115614f6d57600080fd5b60209290920196919550909350505050565b600060208284031215614f9157600080fd5b813561313c81614596565b600060208284031215614fae57600080fd5b813561313c816144b2565b60008060408385031215614fcc57600080fd5b823567ffffffffffffffff80821115614fe457600080fd5b614ff086838701614756565b9350602085013591508082111561500657600080fd5b50615013858286016146d6565b9150509250929050565b600082601f83011261502e57600080fd5b8135602061503e61461783614572565b8083825260208201915060208460051b87010193508684111561506057600080fd5b602086015b84811015612205578035615078816144b2565b8352918301918301615065565b6000602080838503121561509857600080fd5b823567ffffffffffffffff808211156150b057600080fd5b818501915085601f8301126150c457600080fd5b81356150d261461782614572565b81815260059190911b830184019084810190888311156150f157600080fd5b8585015b838110156151c35780358581111561510c57600080fd5b860160c0818c03601f190112156151235760008081fd5b61512b6143a8565b888201358152604061513e818401614df8565b8a830152606061514f818501614df8565b82840152608091506151628285016145c5565b9083015260a0838101358981111561517a5760008081fd5b6151888f8d8388010161501d565b838501525060c08401359150888211156151a25760008081fd5b6151b08e8c8487010161501d565b90830152508452509186019186016150f5565b5098975050505050505050565b6000602082840312156151e257600080fd5b5035919050565b80356001600160e01b03811681146144d257600080fd5b600082601f83011261521157600080fd5b8135602061522161461783614572565b82815260069290921b8401810191818101908684111561524057600080fd5b8286015b84811015612205576040818903121561525d5760008081fd5b6152656143d1565b813561527081614596565b815261527d8286016151e9565b81860152835291830191604001615244565b600082601f8301126152a057600080fd5b813560206152b061461783614572565b82815260079290921b840181019181810190868411156152cf57600080fd5b8286015b848110156122055780880360808112156152ed5760008081fd5b6152f561445e565b823561530081614596565b81526040601f1983018113156153165760008081fd5b61531e6143d1565b92508684013561532d81614596565b83528381013561533c81614596565b83880152818701929092526060830135918101919091528352918301916080016152d3565b6000602080838503121561537457600080fd5b823567ffffffffffffffff8082111561538c57600080fd5b818501915060408083880312156153a257600080fd5b6153aa6143d1565b8335838111156153b957600080fd5b84016040818a0312156153cb57600080fd5b6153d36143d1565b8135858111156153e257600080fd5b8201601f81018b136153f357600080fd5b803561540161461782614572565b81815260069190911b8201890190898101908d83111561542057600080fd5b928a01925b828410156154705787848f03121561543d5760008081fd5b6154456143d1565b8435615450816144b2565b815261545d858d016151e9565b818d0152825292870192908a0190615425565b84525050508187013593508484111561548857600080fd5b6154948a858401615200565b81880152825250838501359150828211156154ae57600080fd5b6154ba8883860161528f565b85820152809550505050505092915050565b634e487b7160e01b600052603260045260246000fd5b805160408084528151848201819052600092602091908201906060870190855b8181101561553957835180516001600160a01b031684528501516001600160e01b0316858401529284019291850191600101615502565b50508583015187820388850152805180835290840192506000918401905b80831015615593578351805167ffffffffffffffff1683528501516001600160e01b031685830152928401926001929092019190850190615557565b50979650505050505050565b602081526000610e8f60208301846154e2565b67ffffffffffffffff831681526060810161313c6020830184805167ffffffffffffffff908116835260209182015116910152565b634e487b7160e01b600052601160045260246000fd5b67ffffffffffffffff81811683821601908082111561561e5761561e6155e7565b5092915050565b60006020808352606084516040808487015261564460608701836154e2565b87850151878203601f19016040890152805180835290860193506000918601905b808310156151c357845167ffffffffffffffff8151168352878101516156a489850182805167ffffffffffffffff908116835260209182015116910152565b50840151828701529386019360019290920191608090910190615665565b6000602082840312156156d457600080fd5b813567ffffffffffffffff8111156156eb57600080fd5b613959848285016149ea565b81810381811115610e9257610e926155e7565b634e487b7160e01b600052601260045260246000fd5b600067ffffffffffffffff8084168061573b5761573b61570a565b92169190910692915050565b8082028115828204841417610e9257610e926155e7565b60006040828403121561577057600080fd5b6157786143d1565b823561578381614596565b81526020928301359281019290925250919050565b60008151808452602080850194506020840160005b83811015614e5e57815180516001600160a01b0316885260209081015190880152604087019650908201906001016157ad565b8051825267ffffffffffffffff60208201511660208301526000604082015160a0604085015261581360a08501826141b4565b90506060830151848203606086015261582c82826141b4565b91505060808301518482036080860152614ed58282615798565b602081526000610e8f60208301846157e0565b60808152600061586c60808301876157e0565b61ffff9590951660208301525060408101929092526001600160a01b0316606090910152919050565b600082601f8301126158a657600080fd5b81516158b4614617826145d0565b8181528460208386010111156158c957600080fd5b613959826020830160208701614190565b6000806000606084860312156158ef57600080fd5b83516158fa816145b7565b602085015190935067ffffffffffffffff81111561591757600080fd5b61592386828701615895565b925050604084015190509250925092565b60006020828403121561594657600080fd5b815161313c816145b7565b80820180821115610e9257610e926155e7565b60ff8181168382160190811115610e9257610e926155e7565b8183823760009101908152919050565b828152606082602083013760800192915050565b600067ffffffffffffffff808416806159bc576159bc61570a565b92169190910492915050565b6000602082840312156159da57600080fd5b815161313c81614596565b6000602082840312156159f757600080fd5b815167ffffffffffffffff80821115615a0f57600080fd5b9083019060608286031215615a2357600080fd5b615a2b61445e565b825182811115615a3a57600080fd5b615a4687828601615895565b825250602083015182811115615a5b57600080fd5b615a6787828601615895565b602083015250604083015182811115615a7f57600080fd5b615a8b87828601615895565b60408301525095945050505050565b6020810160058310615aae57615aae614c73565b91905290565b60ff818116838216029081169081811461561e5761561e6155e7565b600060a0820160ff88168352602087602085015260a0604085015281875480845260c086019150886000526020600020935060005b81811015615b2a5784546001600160a01b031683526001948501949284019201615b05565b50508481036060860152615b3e8188614e24565b935050505060ff831660808301529695505050505050565b82815260406020820152600061395960408301846141b4565b67ffffffffffffffff848116825283166020820152606081016139596040830184614c89565b600067ffffffffffffffff808316818103615bb257615bb26155e7565b6001019392505050565b615bc68184614c89565b60406020820152600061395960408301846141b4565b600060208284031215615bee57600080fd5b815161313c816144b2565b6020815260008251610100806020850152615c186101208501836141b4565b91506020850151615c35604086018267ffffffffffffffff169052565b5060408501516001600160a01b038116606086015250606085015160808501526080850151615c6f60a08601826001600160a01b03169052565b5060a0850151601f19808685030160c0870152615c8c84836141b4565b935060c08701519150808685030160e0870152615ca984836141b4565b935060e0870151915080868503018387015250615cc683826141b4565b9695505050505050565b600060208284031215615ce257600080fd5b5051919050565b600060ff821660ff8103615cff57615cff6155e7565b60010192915050565b6020808252825182820181905260009190848201906040850190845b81811015615d5757835180516001600160a01b031684526020908101519084015260408301938501939250600101615d24565b50909695505050505050565b60008282518085526020808601955060208260051b8401016020860160005b84811015615db057601f19868403018952615d9e8383516141b4565b98840198925090830190600101615d82565b5090979650505050505050565b602081526000610e8f6020830184615d63565b60408152615deb60408201845167ffffffffffffffff169052565b60006020840151615e0760608401826001600160a01b03169052565b5060408401516001600160a01b038116608084015250606084015167ffffffffffffffff811660a084015250608084015160c083015260a084015180151560e08401525060c0840151610100615e688185018367ffffffffffffffff169052565b60e08601519150610120615e86818601846001600160a01b03169052565b81870151925061014091508282860152808701519250506101a06101608181870152615eb66101e08701856141b4565b9350828801519250603f19610180818887030181890152615ed78686615798565b9550828a01519450818887030184890152615ef28686615d63565b9550808a01516101c089015250505050508281036020840152614ed58185615d63565b6000815160208301516001600160e01b031980821693506004831015615f455780818460040360031b1b83161693505b50505091905056fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"AlreadyAttempted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"AlreadyExecuted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CanOnlySelfCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"actual\",\"type\":\"bytes32\"}],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"error\",\"type\":\"bytes\"}],\"name\":\"ExecutionError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"ForkedChain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"enumMultiOCR3Base.InvalidConfigErrorType\",\"name\":\"errorType\",\"type\":\"uint8\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"got\",\"type\":\"uint256\"}],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidEVMAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"min\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"max\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.Interval\",\"name\":\"interval\",\"type\":\"tuple\"}],\"name\":\"InvalidInterval\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"newLimit\",\"type\":\"uint256\"}],\"name\":\"InvalidManualExecutionGasLimit\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"messageDestChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidMessageDestChainSelector\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"newState\",\"type\":\"uint8\"}],\"name\":\"InvalidNewState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRoot\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidStaticConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LeavesCannotBeEmpty\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ManualExecutionGasLimitMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"ManualExecutionNotYetEnabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorReason\",\"type\":\"bytes\"}],\"name\":\"MessageValidationError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonUniqueSignatures\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"notPool\",\"type\":\"address\"}],\"name\":\"NotACompatiblePool\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OracleCannotBeZeroAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"error\",\"type\":\"bytes\"}],\"name\":\"ReceiverError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"name\":\"RootAlreadyCommitted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"RootNotCommitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SignaturesOutOfRegistration\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StaleCommitReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"StaticConfigCannotBeChanged\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"TokenDataMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"error\",\"type\":\"bytes\"}],\"name\":\"TokenHandlingError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnexpectedTokenData\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"WrongMessageLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WrongNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroChainSelectorNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"min\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"max\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.Interval\",\"name\":\"interval\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.MerkleRoot[]\",\"name\":\"merkleRoots\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.CommitReport\",\"name\":\"report\",\"type\":\"tuple\"}],\"name\":\"CommitReportAccepted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"DynamicConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"state\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"name\":\"ExecutionStateChanged\",\"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\":\"root\",\"type\":\"bytes32\"}],\"name\":\"RootRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"SkippedAlreadyExecutedMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfig\",\"name\":\"sourceConfig\",\"type\":\"tuple\"}],\"name\":\"SourceChainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainSelectorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"}],\"name\":\"StaticConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigUpdates\",\"type\":\"tuple[]\"}],\"name\":\"applySourceChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"destTokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structClient.Any2EVMMessage\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"ccipReceive\",\"outputs\":[],\"stateMutability\":\"pure\",\"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\":\"commit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"}],\"name\":\"execute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[]\"}],\"name\":\"executeSingleMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDynamicConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"getExecutionState\",\"outputs\":[{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestPriceSequenceNumber\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"getMerkleRoot\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"getSourceChainConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStaticConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"isBlessed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"latestConfigDetails\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"n\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"}],\"internalType\":\"structMultiOCR3Base.ConfigInfo\",\"name\":\"configInfo\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfig\",\"name\":\"ocrConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage[]\",\"name\":\"messages\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[][]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[][]\"},{\"internalType\":\"bytes32[]\",\"name\":\"proofs\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofFlagBits\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.ExecutionReportSingleChain[]\",\"name\":\"reports\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[][]\",\"name\":\"gasLimitOverrides\",\"type\":\"uint256[][]\"}],\"name\":\"manuallyExecute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.UnblessedRoot[]\",\"name\":\"rootToReset\",\"type\":\"tuple[]\"}],\"name\":\"resetUnblessedRoots\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"setDynamicConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfigArgs[]\",\"name\":\"ocrConfigArgs\",\"type\":\"tuple[]\"}],\"name\":\"setOCR3Configs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x6101206040523480156200001257600080fd5b506040516200694f3803806200694f8339810160408190526200003591620005bd565b33806000816200008c5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000bf57620000bf81620001ee565b5050466080525060208201516001600160a01b03161580620000ec575060408201516001600160a01b0316155b8062000103575060608201516001600160a01b0316155b1562000122576040516342bcdf7f60e11b815260040160405180910390fd5b81516001600160401b03166000036200014e5760405163c656089560e01b815260040160405180910390fd5b81516001600160401b0390811660a052602080840180516001600160a01b0390811660c05260408087018051831660e0526060808901805185166101005283518a519098168852945184169587019590955251821690850152905116908201527f683eb52ee924eb817377cfa8f41f238f4bb7a877da5267869dfffbad85f564d89060800160405180910390a1620001e68162000299565b505062000a03565b336001600160a01b03821603620002485760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000083565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60005b8151811015620004eb576000828281518110620002bd57620002bd620007cc565b60200260200101519050600081600001519050806001600160401b0316600003620002fb5760405163c656089560e01b815260040160405180910390fd5b6001600160401b03811660009081526007602052604081206001810180549192916200032790620007e2565b80601f01602080910402602001604051908101604052809291908181526020018280546200035590620007e2565b8015620003a65780601f106200037a57610100808354040283529160200191620003a6565b820191906000526020600020905b8154815290600101906020018083116200038857829003601f168201915b50505050509050600084604001519050815160000362000449578051600003620003e3576040516342bcdf7f60e11b815260040160405180910390fd5b60018301620003f3828262000873565b508254610100600160481b0319166101001783556040516001600160401b03851681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a162000484565b8080519060200120828051906020012014620004845760405163c39a620560e01b81526001600160401b038516600482015260240162000083565b6020850151835460ff19169015151783556040516001600160401b038516907f4f49973170c548fddd4a48341b75e131818913f38f44d47af57e8735eee588ba90620004d29086906200093f565b60405180910390a250505050508060010190506200029c565b5050565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b03811182821017156200052a576200052a620004ef565b60405290565b604051606081016001600160401b03811182821017156200052a576200052a620004ef565b604051601f8201601f191681016001600160401b0381118282101715620005805762000580620004ef565b604052919050565b80516001600160401b0381168114620005a057600080fd5b919050565b80516001600160a01b0381168114620005a057600080fd5b60008082840360a0811215620005d257600080fd5b6080811215620005e157600080fd5b50620005ec62000505565b620005f78462000588565b8152602062000608818601620005a5565b818301526200061a60408601620005a5565b60408301526200062d60608601620005a5565b606083015260808501519193506001600160401b03808311156200065057600080fd5b828601925086601f8401126200066557600080fd5b8251818111156200067a576200067a620004ef565b8060051b6200068b84820162000555565b918252848101840191848101908a841115620006a657600080fd5b85870192505b83831015620007bb57825185811115620006c557600080fd5b8701601f196060828e0382011215620006dd57600080fd5b620006e762000530565b620006f489840162000588565b8152604083015180151581146200070a57600080fd5b818a01526060830151888111156200072157600080fd5b8084019350508d603f8401126200073757600080fd5b88830151888111156200074e576200074e620004ef565b620007608a84601f8401160162000555565b92508083528e60408286010111156200077857600080fd5b60005b818110156200079957848101604001518482018c01528a016200077b565b5060009083018a015260408101919091528352509185019190850190620006ac565b809750505050505050509250929050565b634e487b7160e01b600052603260045260246000fd5b600181811c90821680620007f757607f821691505b6020821081036200081857634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200086e576000816000526020600020601f850160051c81016020861015620008495750805b601f850160051c820191505b818110156200086a5782815560010162000855565b5050505b505050565b81516001600160401b038111156200088f576200088f620004ef565b620008a781620008a08454620007e2565b846200081e565b602080601f831160018114620008df5760008415620008c65750858301515b600019600386901b1c1916600185901b1785556200086a565b600085815260208120601f198616915b828110156200091057888601518255948401946001909101908401620008ef565b50858210156200092f5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6020808252825460ff811615158383015260081c6001600160401b031660408301526060808301526001808401805460009392919084906200098181620007e2565b80608089015260a06001831660008114620009a55760018114620009c257620009f4565b60ff19841660a08b015260a083151560051b8b01019450620009f4565b85600052602060002060005b84811015620009eb5781548c8201850152908801908901620009ce565b8b0160a0019550505b50929998505050505050505050565b60805160a05160c05160e05161010051615edd62000a726000396000818161023e0152612be101526000818161020f0152612ecb0152600081816101e0015281816115ca01526116810152600081816101b00152612755015260008181611fb901526120050152615edd6000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806385572ffb116100d8578063ccd37ba31161008c578063f2fde38b11610066578063f2fde38b146105b1578063f716f99f146105c4578063ff888fb1146105d757600080fd5b8063ccd37ba314610539578063d2a15d351461057e578063e9d68a8e1461059157600080fd5b8063a12a9870116100bd578063a12a9870146104f3578063bec0b14314610506578063c673e5841461051957600080fd5b806385572ffb146104ca5780638da5cb5b146104d857600080fd5b8063403b2d631161012f5780635ffb5ced116101145780635ffb5ced146103855780637437ff9f1461039857806379ba5097146104c257600080fd5b8063403b2d63146103525780635e36480c1461036557600080fd5b80632d04ab76116101605780632d04ab761461030e578063311cd513146103235780633f4b04aa1461033657600080fd5b806306285c691461017c578063181f5a77146102c5575b600080fd5b61026e60408051608081018252600080825260208201819052918101829052606081019190915260405180608001604052807f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250905090565b6040516102bc9190815167ffffffffffffffff1681526020808301516001600160a01b0390811691830191909152604080840151821690830152606092830151169181019190915260800190565b60405180910390f35b6103016040518060400160405280601d81526020017f45564d3245564d4d756c74694f666652616d7020312e362e302d64657600000081525081565b6040516102bc9190613f11565b61032161031c366004613fbc565b6105fa565b005b61032161033136600461406f565b6109c0565b600a5460405167ffffffffffffffff90911681526020016102bc565b6103216103603660046141f8565b610a29565b610378610373366004614297565b610bfb565b6040516102bc91906142f4565b610321610393366004614600565b610c51565b6104596040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506040805160c0810182526004546001600160a01b03808216835263ffffffff7401000000000000000000000000000000000000000083048116602085015278010000000000000000000000000000000000000000000000008304811694840194909452600160e01b90910490921660608201526005548216608082015260065490911660a082015290565b6040516102bc9190600060c0820190506001600160a01b03808451168352602084015163ffffffff808216602086015280604087015116604086015280606087015116606086015250508060808501511660808401528060a08501511660a08401525092915050565b610321610fa9565b610321610177366004614664565b6000546040516001600160a01b0390911681526020016102bc565b6103216105013660046146b8565b611067565b610321610514366004614a46565b61107b565b61052c610527366004614b82565b611220565b6040516102bc9190614be2565b610570610547366004614c57565b67ffffffffffffffff919091166000908152600960209081526040808320938352929052205490565b6040519081526020016102bc565b61032161058c366004614c81565b61137e565b6105a461059f366004614cf6565b611438565b6040516102bc9190614d11565b6103216105bf366004614d4c565b611521565b6103216105d2366004614dd1565b611532565b6105ea6105e5366004614f0f565b611574565b60405190151581526020016102bc565b600061060887890189615098565b8051515190915015158061062157508051602001515115155b1561072157600a5460208a01359067ffffffffffffffff808316911610156106e057600a805467ffffffffffffffff191667ffffffffffffffff831617905560065482516040517f3937306f0000000000000000000000000000000000000000000000000000000081526001600160a01b0390921691633937306f916106a9916004016152d6565b600060405180830381600087803b1580156106c357600080fd5b505af11580156106d7573d6000803e3d6000fd5b5050505061071f565b81602001515160000361071f576040517f2261116700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b60005b8160200151518110156109095760008260200151828151811061074957610749615203565b6020026020010151905060008160000151905061076581611635565b600061077082611737565b602084015151815491925067ffffffffffffffff908116610100909204161415806107b2575060208084015190810151905167ffffffffffffffff9182169116115b156107fb57825160208401516040517feefb0cac0000000000000000000000000000000000000000000000000000000081526107f29291906004016152e9565b60405180910390fd5b604083015180610837576040517f504570e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b835167ffffffffffffffff166000908152600960209081526040808320848452909152902054156108aa5783516040517f32cf0cbf00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602481018290526044016107f2565b60208085015101516108bd906001615334565b825468ffffffffffffffff00191661010067ffffffffffffffff928316021790925592511660009081526009602090815260408083209483529390529190912042905550600101610724565b507f3a3950e13dd607cc37980db0ef14266c40d2bba9c01b2e44bfe549808883095d81604051610939919061535c565b60405180910390a16109b560008a8a8a8a8a8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808e0282810182019093528d82529093508d92508c9182918501908490808284376000920191909152508b9250611797915050565b505050505050505050565b610a006109cf828401846153f9565b60408051600080825260208201909252906109fa565b60608152602001906001900390816109e55790505b50611b0e565b604080516000808252602082019092529050610a23600185858585866000611797565b50505050565b610a31611bbe565b60a08101516001600160a01b03161580610a53575080516001600160a01b0316155b15610a8a576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516004805460208085018051604080880180516060808b0180516001600160a01b039b8c167fffffffffffffffff000000000000000000000000000000000000000000000000909a168a177401000000000000000000000000000000000000000063ffffffff988916021777ffffffffffffffffffffffffffffffffffffffffffffffff167801000000000000000000000000000000000000000000000000948816949094026001600160e01b031693909317600160e01b93871693909302929092179098556080808b0180516005805473ffffffffffffffffffffffffffffffffffffffff19908116928e1692909217905560a0808e01805160068054909416908f161790925586519a8b5297518716988a0198909852925185169388019390935251909216958501959095525185169383019390935251909216908201527f0da37fd00459f4f5f0b8210d31525e4910ae674b8bab34b561d146bb45773a4c9060c00160405180910390a150565b6000610c096001600461542e565b6002610c16608085615457565b67ffffffffffffffff16610c2a919061547e565b610c348585611c1a565b901c166003811115610c4857610c486142ca565b90505b92915050565b333014610c8a576040517f371a732800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160008082526020820190925281610cc7565b6040805180820190915260008082526020820152815260200190600190039081610ca05790505b5060a08401515190915015610cff57610cfc8360a00151846020015185606001518660000151602001518760c0015187611c61565b90505b6040805160a0810182528451518152845160209081015167ffffffffffffffff1681830152808601518351600094840192610d3b929101613f11565b60408051601f19818403018152918152908252868101516020830152018390526005549091506001600160a01b03168015610e48576040517f08d450a10000000000000000000000000000000000000000000000000000000081526001600160a01b038216906308d450a190610db5908590600401615543565b600060405180830381600087803b158015610dcf57600080fd5b505af1925050508015610de0575060015b610e48573d808015610e0e576040519150601f19603f3d011682016040523d82523d6000602084013e610e13565b606091505b50806040517f09c253250000000000000000000000000000000000000000000000000000000081526004016107f29190613f11565b604085015151158015610e5d57506080850151155b80610e74575060608501516001600160a01b03163b155b80610eb457506060850151610eb2906001600160a01b03167f85572ffb00000000000000000000000000000000000000000000000000000000611d0e565b155b15610ec0575050505050565b60048054608087015160608801516040517f3cf9798300000000000000000000000000000000000000000000000000000000815260009485946001600160a01b031693633cf9798393610f1b938a9361138893929101615556565b6000604051808303816000875af1158015610f3a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f6291908101906155d7565b509150915081610fa057806040517f0a8d6e8c0000000000000000000000000000000000000000000000000000000081526004016107f29190613f11565b50505050505050565b6001546001600160a01b031633146110035760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016107f2565b600080543373ffffffffffffffffffffffffffffffffffffffff19808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b61106f611bbe565b61107881611d2a565b50565b611083611fb6565b8151815181146110bf576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156112105760008482815181106110de576110de615203565b6020026020010151905060008160200151519050600085848151811061110657611106615203565b602002602001015190508051821461114a576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8281101561120157600082828151811061116957611169615203565b60200260200101519050806000141580156111a457508460200151828151811061119557611195615203565b60200260200101516080015181105b156111f85784516040517fc8e9605100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff909116600482015260248101839052604481018290526064016107f2565b5060010161114d565b505050508060010190506110c2565b5061121b8383611b0e565b505050565b6112636040805160e081019091526000606082018181526080830182905260a0830182905260c08301919091528190815260200160608152602001606081525090565b60ff808316600090815260026020818152604092839020835160e081018552815460608201908152600183015480881660808401526101008104881660a0840152620100009004909616151560c08201529485529182018054845181840281018401909552808552929385830193909283018282801561130c57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116112ee575b505050505081526020016003820180548060200260200160405190810160405280929190818152602001828054801561136e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611350575b5050505050815250509050919050565b611386611bbe565b60005b8181101561121b5760008383838181106113a5576113a5615203565b9050604002018036038101906113bb9190615631565b90506113ca8160200151611574565b61142f57805167ffffffffffffffff1660009081526009602090815260408083208285018051855290835281842093909355915191519182527f202f1139a3e334b6056064c0e9b19fd07e44a88d8f6e5ded571b24cf8c371f12910160405180910390a15b50600101611389565b60408051606080820183526000808352602080840182905283850183905267ffffffffffffffff8681168352600782529185902085519384018652805460ff8116151585526101009004909216908301526001810180549394929391928401916114a19061566a565b80601f01602080910402602001604051908101604052809291908181526020018280546114cd9061566a565b801561136e5780601f106114ef5761010080835404028352916020019161136e565b820191906000526020600020905b8154815290600101906020018083116114fd57505050919092525091949350505050565b611529611bbe565b61107881612037565b61153a611bbe565b60005b81518110156115705761156882828151811061155b5761155b615203565b60200260200101516120ed565b60010161153d565b5050565b6040805180820182523081526020810183815291517f4d61677100000000000000000000000000000000000000000000000000000000815290516001600160a01b039081166004830152915160248201526000917f00000000000000000000000000000000000000000000000000000000000000001690634d61677190604401602060405180830381865afa158015611611573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4b91906156a4565b6040517f2cbc26bb000000000000000000000000000000000000000000000000000000008152608082901b77ffffffffffffffff000000000000000000000000000000001660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632cbc26bb90602401602060405180830381865afa1580156116d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116f491906156a4565b15611078576040517ffdbd6a7200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016107f2565b67ffffffffffffffff81166000908152600760205260408120805460ff16610c4b576040517fed053c5900000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016107f2565b60ff878116600090815260026020908152604080832081516080810183528154815260019091015480861693820193909352610100830485169181019190915262010000909104909216151560608301528735906117f68760a46156c1565b905082606001511561183e57845161180f90602061547e565b865161181c90602061547e565b6118279060a06156c1565b61183191906156c1565b61183b90826156c1565b90505b368114611880576040517f8e1192e1000000000000000000000000000000000000000000000000000000008152600481018290523660248201526044016107f2565b50815181146118c85781516040517f93df584c0000000000000000000000000000000000000000000000000000000081526004810191909152602481018290526044016107f2565b6118d0611fb6565b60ff808a166000908152600360209081526040808320338452825280832081518083019092528054808616835293949193909284019161010090910416600281111561191e5761191e6142ca565b600281111561192f5761192f6142ca565b905250905060028160200151600281111561194c5761194c6142ca565b1480156119a05750600260008b60ff1660ff168152602001908152602001600020600301816000015160ff168154811061198857611988615203565b6000918252602090912001546001600160a01b031633145b6119d6576040517fda0f08e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50816060015115611ab85760208201516119f19060016156d4565b60ff16855114611a2d576040517f71253a2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8351855114611a68576040517fa75d88af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008787604051611a7a9291906156ed565b604051908190038120611a91918b906020016156fd565b604051602081830303815290604052805190602001209050611ab68a82888888612431565b505b6040805182815260208a81013567ffffffffffffffff169082015260ff8b16917f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef0910160405180910390a2505050505050505050565b8151600003611b48576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160408051600080825260208201909252911591905b8451811015611bb757611baf858281518110611b7d57611b7d615203565b602002602001015184611ba957858381518110611b9c57611b9c615203565b6020026020010151612648565b83612648565b600101611b5f565b5050505050565b6000546001600160a01b03163314611c185760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107f2565b565b67ffffffffffffffff8216600090815260086020526040812081611c3f608085615711565b67ffffffffffffffff1681526020810191909152604001600020549392505050565b8560005b8751811015611d0357611cde888281518110611c8357611c83615203565b602002602001015160200151888888888681518110611ca457611ca4615203565b6020026020010151806020019051810190611cbf9190615738565b888781518110611cd157611cd1615203565b6020026020010151612e6a565b828281518110611cf057611cf0615203565b6020908102919091010152600101611c65565b509695505050505050565b6000611d19836131f8565b8015610c485750610c488383613244565b60005b8151811015611570576000828281518110611d4a57611d4a615203565b602002602001015190506000816000015190508067ffffffffffffffff16600003611da1576040517fc656089500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff81166000908152600760205260408120600181018054919291611dcc9061566a565b80601f0160208091040260200160405190810160405280929190818152602001828054611df89061566a565b8015611e455780601f10611e1a57610100808354040283529160200191611e45565b820191906000526020600020905b815481529060010190602001808311611e2857829003601f168201915b505050505090506000846040015190508151600003611efe578051600003611e99576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018301611ea78282615835565b50825468ffffffffffffffff00191661010017835560405167ffffffffffffffff851681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a1611f51565b8080519060200120828051906020012014611f51576040517fc39a620500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff851660048201526024016107f2565b6020850151835460ff191690151517835560405167ffffffffffffffff8516907f4f49973170c548fddd4a48341b75e131818913f38f44d47af57e8735eee588ba90611f9e9086906158f5565b60405180910390a25050505050806001019050611d2d565b467f000000000000000000000000000000000000000000000000000000000000000014611c18576040517f0f01ce850000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201524660248201526044016107f2565b336001600160a01b0382160361208f5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107f2565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b806040015160ff16600003612118576000604051631b3fab5160e11b81526004016107f291906159b1565b60208082015160ff8082166000908152600290935260408320600181015492939092839216900361218557606084015160018201805491151562010000027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff9092169190911790556121da565b6060840151600182015460ff62010000909104161515901515146121da576040517f87f6037c00000000000000000000000000000000000000000000000000000000815260ff841660048201526024016107f2565b60a08401518051601f60ff82161115612209576001604051631b3fab5160e11b81526004016107f291906159b1565b61226f858560030180548060200260200160405190810160405280929190818152602001828054801561226557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612247575b50505050506132e6565b85606001511561239e576122dd8585600201805480602002602001604051908101604052809291908181526020018280548015612265576020028201919060005260206000209081546001600160a01b031681526001909101906020018083116122475750505050506132e6565b608086015180516122f79060028701906020840190613e1f565b5080516001850180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff841690810291909117909155601f1015612357576002604051631b3fab5160e11b81526004016107f291906159b1565b60408801516123679060036159cb565b60ff168160ff161161238f576003604051631b3fab5160e11b81526004016107f291906159b1565b61239b8783600161334f565b50505b6123aa8583600261334f565b81516123bf9060038601906020850190613e1f565b5060408681015160018501805460ff191660ff8316179055875180865560a089015192517fab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f54793612418938a939260028b019291906159e7565b60405180910390a1612429856134cf565b505050505050565b612439613e8d565b835160005b8181101561263e57600060018886846020811061245d5761245d615203565b61246a91901a601b6156d4565b89858151811061247c5761247c615203565b602002602001015189868151811061249657612496615203565b6020026020010151604051600081526020016040526040516124d4949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa1580156124f6573d6000803e3d6000fd5b505060408051601f1981015160ff808e166000908152600360209081528582206001600160a01b03851683528152858220858701909652855480841686529397509095509293928401916101009004166002811115612557576125576142ca565b6002811115612568576125686142ca565b9052509050600181602001516002811115612585576125856142ca565b146125bc576040517fca31867a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051859060ff16601f81106125d3576125d3615203565b60200201511561260f576040517ff67bc7c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600185826000015160ff16601f811061262a5761262a615203565b91151560209092020152505060010161243e565b5050505050505050565b815161265381611635565b600061265e82611737565b60208501515190915060008190036126a1576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84604001515181146126df576040517f57e0e08300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008167ffffffffffffffff8111156126fa576126fa6140c3565b604051908082528060200260200182016040528015612723578160200160208202803683370190505b50905060005b828110156128985760008760200151828151811061274957612749615203565b602002602001015190507f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681600001516040015167ffffffffffffffff16146127dc57805160409081015190517f38432a2200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016107f2565b612872818660010180546127ef9061566a565b80601f016020809104026020016040519081016040528092919081815260200182805461281b9061566a565b80156128685780601f1061283d57610100808354040283529160200191612868565b820191906000526020600020905b81548152906001019060200180831161284b57829003601f168201915b50505050506134eb565b83838151811061288457612884615203565b602090810291909101015250600101612729565b5060006128af858389606001518a6080015161363d565b9050806000036128f7576040517f7dd17a7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff861660048201526024016107f2565b8551151560005b848110156109b55760008960200151828151811061291e5761291e615203565b60200260200101519050600061293c89836000015160600151610bfb565b90506002816003811115612952576129526142ca565b036129a9578151606001516040805167ffffffffffffffff808d16825290921660208301527f3b575419319662b2a6f5e2467d84521517a3382b908eb3d557bb3fdb0c50e23c910160405180910390a15050612e62565b60008160038111156129bd576129bd6142ca565b14806129da575060038160038111156129d8576129d86142ca565b145b612a2b578151606001516040517f25507e7f00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808c16600483015290911660248201526044016107f2565b8315612b0c5760045460009074010000000000000000000000000000000000000000900463ffffffff16612a5f874261542e565b1190508080612a7f57506003826003811115612a7d57612a7d6142ca565b145b612ac1576040517fa9cfc86200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8b1660048201526024016107f2565b8a8481518110612ad357612ad3615203565b6020026020010151600014612b06578a8481518110612af457612af4615203565b60200260200101518360800181815250505b50612b72565b6000816003811115612b2057612b206142ca565b14612b72578151606001516040517f3ef2a99c00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808c16600483015290911660248201526044016107f2565b81516080015167ffffffffffffffff1615801590612ba157506000816003811115612b9f57612b9f6142ca565b145b15612c665781516080015160208301516040517fe0e03cae0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263e0e03cae92612c18928e929190600401615a6d565b6020604051808303816000875af1158015612c37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c5b91906156a4565b612c66575050612e62565b60008b604001518481518110612c7e57612c7e615203565b6020026020010151905080518360a001515114612ce2578251606001516040517f1cfe6d8b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808d16600483015290911660248201526044016107f2565b612cf68a8460000151606001516001613693565b600080612d03858461373b565b91509150612d1a8c86600001516060015184613693565b868015612d3857506003826003811115612d3657612d366142ca565b145b8015612d5657506000846003811115612d5357612d536142ca565b14155b15612d93578451516040517f2b11b8d90000000000000000000000000000000000000000000000000000000081526107f291908390600401615a9a565b6003826003811115612da757612da76142ca565b14158015612dc757506002826003811115612dc457612dc46142ca565b14155b15612e08578451606001516040517f926c5a3e0000000000000000000000000000000000000000000000000000000081526107f2918e918590600401615ab3565b8451805160609091015160405167ffffffffffffffff918216918f16907f8c324ce1367b83031769f6a813e3bb4c117aba2185789d66b98b791405be6df290612e549087908790615ad9565b60405180910390a450505050505b6001016128fe565b60408051808201909152600080825260208201526000612e8d8460200151613983565b6040517fbbe4f6db0000000000000000000000000000000000000000000000000000000081526001600160a01b0380831660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063bbe4f6db90602401602060405180830381865afa158015612f12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f369190615af9565b90506001600160a01b0381161580612f7e5750612f7c6001600160a01b0382167faff2afbf00000000000000000000000000000000000000000000000000000000611d0e565b155b15612fc0576040517fae9b4ce90000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016107f2565b60008061308b633907753760e01b6040518061010001604052808d81526020018b67ffffffffffffffff1681526020018c6001600160a01b031681526020018e8152602001876001600160a01b031681526020018a6000015181526020018a6040015181526020018981525060405160240161303c9190615b16565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152600454859063ffffffff600160e01b9091041661138860846139c5565b5091509150816130b0578060405163e1cd550960e01b81526004016107f29190613f11565b80516020146130f85780516040517f78ef80240000000000000000000000000000000000000000000000000000000081526020600482015260248101919091526044016107f2565b60008180602001905181019061310e9190615bed565b604080516001600160a01b038d16602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b03167fa9059cbb000000000000000000000000000000000000000000000000000000001790526004549192506131a69187907801000000000000000000000000000000000000000000000000900463ffffffff1661138860846139c5565b509093509150826131cc578160405163e1cd550960e01b81526004016107f29190613f11565b604080518082019091526001600160a01b03909516855260208501525091925050509695505050505050565b6000613224827f01ffc9a700000000000000000000000000000000000000000000000000000000613244565b8015610c4b575061323d826001600160e01b0319613244565b1592915050565b604080516001600160e01b03198316602480830191909152825180830390910181526044909101909152602080820180516001600160e01b03167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d915060005190508280156132cf575060208210155b80156132db5750600081115b979650505050505050565b60005b815181101561121b5760ff83166000908152600360205260408120835190919084908490811061331b5761331b615203565b6020908102919091018101516001600160a01b03168252810191909152604001600020805461ffff191690556001016132e9565b60005b82518160ff161015610a23576000838260ff168151811061337557613375615203565b6020026020010151905060006002811115613392576133926142ca565b60ff80871660009081526003602090815260408083206001600160a01b038716845290915290205461010090041660028111156133d1576133d16142ca565b146133f2576004604051631b3fab5160e11b81526004016107f291906159b1565b6001600160a01b038116613432576040517fd6c62c9b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052808360ff168152602001846002811115613458576134586142ca565b905260ff80871660009081526003602090815260408083206001600160a01b0387168452825290912083518154931660ff198416811782559184015190929091839161ffff1916176101008360028111156134b5576134b56142ca565b021790555090505050806134c890615c06565b9050613352565b60ff811661107857600a805467ffffffffffffffff1916905550565b815160208082015160409283015192516000938493613531937f2425b0b9f9054c76ff151b0a175b18f37a4a4e82013a72e9f15c9caa095ed21f93909291889101615c25565b60408051601f1981840301815290829052805160209182012086518051888401516060808b0151908401516080808d0151950151959761357a9794969395929491939101615c58565b604051602081830303815290604052805190602001208560400151805190602001208660a001516040516020016135b19190615cab565b604051602081830303815290604052805190602001208760c001516040516020016135dc9190615d60565b60408051601f198184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e0015b60405160208183030381529060405280519060200120905092915050565b60008061364b858585613aeb565b905061365681611574565b61366457600091505061368b565b67ffffffffffffffff86166000908152600960209081526040808320938352929052205490505b949350505050565b600060026136a2608085615457565b67ffffffffffffffff166136b6919061547e565b905060006136c48585611c1a565b9050816136d36001600461542e565b901b1916818360038111156136ea576136ea6142ca565b67ffffffffffffffff871660009081526008602052604081209190921b92909217918291613719608088615711565b67ffffffffffffffff1681526020810191909152604001600020555050505050565b6040517f5ffb5ced0000000000000000000000000000000000000000000000000000000081526000906060903090635ffb5ced9061377f9087908790600401615d73565b600060405180830381600087803b15801561379957600080fd5b505af19250505080156137aa575060015b613967573d8080156137d8576040519150601f19603f3d011682016040523d82523d6000602084013e6137dd565b606091505b5060006137e982615e98565b90507f0a8d6e8c000000000000000000000000000000000000000000000000000000006001600160e01b031982161480613833575063e1cd550960e01b6001600160e01b03198216145b8061384e575063046b337b60e51b6001600160e01b03198216145b8061388257507f78ef8024000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b806138b657507f0c3b563c000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b806138ea57507fae9b4ce9000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b8061391e57507f09c25325000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b1561392f575060039250905061397c565b8551516040517f2b11b8d90000000000000000000000000000000000000000000000000000000081526107f291908490600401615a9a565b50506040805160208101909152600081526002905b9250929050565b600081516020146139a9578160405163046b337b60e51b81526004016107f29190613f11565b610c4b828060200190518101906139c09190615bed565b613d8a565b6000606060008361ffff1667ffffffffffffffff8111156139e8576139e86140c3565b6040519080825280601f01601f191660200182016040528015613a12576020820181803683370190505b509150863b613a45577f0c3b563c0000000000000000000000000000000000000000000000000000000060005260046000fd5b5a85811015613a78577fafa32a2c0000000000000000000000000000000000000000000000000000000060005260046000fd5b8590036040810481038710613ab1577f37c3be290000000000000000000000000000000000000000000000000000000060005260046000fd5b505a6000808a5160208c0160008c8cf193505a900390503d84811115613ad45750835b808352806000602085013e50955095509592505050565b8251825160009190818303613b2c576040517f11a6b26400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101018211801590613b4057506101018111155b613b5d576040516309bde33960e01b815260040160405180910390fd5b60001982820101610100811115613b87576040516309bde33960e01b815260040160405180910390fd5b80600003613bb45786600081518110613ba257613ba2615203565b60200260200101519350505050613d83565b60008167ffffffffffffffff811115613bcf57613bcf6140c3565b604051908082528060200260200182016040528015613bf8578160200160208202803683370190505b50905060008080805b85811015613d225760006001821b8b811603613c5c5788851015613c45578c5160018601958e918110613c3657613c36615203565b60200260200101519050613c7e565b8551600185019487918110613c3657613c36615203565b8b5160018401938d918110613c7357613c73615203565b602002602001015190505b600089861015613cae578d5160018701968f918110613c9f57613c9f615203565b60200260200101519050613cd0565b8651600186019588918110613cc557613cc5615203565b602002602001015190505b82851115613cf1576040516309bde33960e01b815260040160405180910390fd5b613cfb8282613dde565b878481518110613d0d57613d0d615203565b60209081029190910101525050600101613c01565b506001850382148015613d3457508683145b8015613d3f57508581145b613d5c576040516309bde33960e01b815260040160405180910390fd5b836001860381518110613d7157613d71615203565b60200260200101519750505050505050505b9392505050565b60006001600160a01b03821180613da2575061040082105b15613dda5760408051602081018490520160408051601f198184030181529082905263046b337b60e51b82526107f291600401613f11565b5090565b6000818310613df657613df18284613dfc565b610c48565b610c4883835b60408051600160208201529081018390526060810182905260009060800161361f565b828054828255906000526020600020908101928215613e81579160200282015b82811115613e81578251825473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909116178255602090920191600190910190613e3f565b50613dda929150613eac565b604051806103e00160405280601f906020820280368337509192915050565b5b80821115613dda5760008155600101613ead565b60005b83811015613edc578181015183820152602001613ec4565b50506000910152565b60008151808452613efd816020860160208601613ec1565b601f01601f19169290920160200192915050565b602081526000610c486020830184613ee5565b8060608101831015610c4b57600080fd5b60008083601f840112613f4757600080fd5b50813567ffffffffffffffff811115613f5f57600080fd5b60208301915083602082850101111561397c57600080fd5b60008083601f840112613f8957600080fd5b50813567ffffffffffffffff811115613fa157600080fd5b6020830191508360208260051b850101111561397c57600080fd5b60008060008060008060008060e0898b031215613fd857600080fd5b613fe28a8a613f24565b9750606089013567ffffffffffffffff80821115613fff57600080fd5b61400b8c838d01613f35565b909950975060808b013591508082111561402457600080fd5b6140308c838d01613f77565b909750955060a08b013591508082111561404957600080fd5b506140568b828c01613f77565b999c989b50969995989497949560c00135949350505050565b60008060006080848603121561408457600080fd5b61408e8585613f24565b9250606084013567ffffffffffffffff8111156140aa57600080fd5b6140b686828701613f35565b9497909650939450505050565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff811182821017156140fc576140fc6140c3565b60405290565b60405160a0810167ffffffffffffffff811182821017156140fc576140fc6140c3565b6040805190810167ffffffffffffffff811182821017156140fc576140fc6140c3565b60405160e0810167ffffffffffffffff811182821017156140fc576140fc6140c3565b6040516060810167ffffffffffffffff811182821017156140fc576140fc6140c3565b604051601f8201601f1916810167ffffffffffffffff811182821017156141b7576141b76140c3565b604052919050565b6001600160a01b038116811461107857600080fd5b80356141df816141bf565b919050565b803563ffffffff811681146141df57600080fd5b600060c0828403121561420a57600080fd5b6142126140d9565b823561421d816141bf565b815261422b602084016141e4565b602082015261423c604084016141e4565b604082015261424d606084016141e4565b60608201526080830135614260816141bf565b608082015260a0830135614273816141bf565b60a08201529392505050565b803567ffffffffffffffff811681146141df57600080fd5b600080604083850312156142aa57600080fd5b6142b38361427f565b91506142c16020840161427f565b90509250929050565b634e487b7160e01b600052602160045260246000fd5b600481106142f0576142f06142ca565b9052565b60208101610c4b82846142e0565b600060a0828403121561431457600080fd5b61431c614102565b90508135815261432e6020830161427f565b602082015261433f6040830161427f565b60408201526143506060830161427f565b60608201526143616080830161427f565b608082015292915050565b600067ffffffffffffffff821115614386576143866140c3565b50601f01601f191660200190565b600082601f8301126143a557600080fd5b81356143b86143b38261436c565b61418e565b8181528460208386010111156143cd57600080fd5b816020850160208301376000918101602001919091529392505050565b600067ffffffffffffffff821115614404576144046140c3565b5060051b60200190565b600082601f83011261441f57600080fd5b8135602061442f6143b3836143ea565b82815260069290921b8401810191818101908684111561444e57600080fd5b8286015b84811015611d03576040818903121561446b5760008081fd5b614473614125565b813561447e816141bf565b81528185013585820152835291830191604001614452565b600082601f8301126144a757600080fd5b813560206144b76143b3836143ea565b82815260059290921b840181019181810190868411156144d657600080fd5b8286015b84811015611d0357803567ffffffffffffffff8111156144fa5760008081fd5b6145088986838b0101614394565b8452509183019183016144da565b6000610160828403121561452957600080fd5b614531614148565b905061453d8383614302565b815260a082013567ffffffffffffffff8082111561455a57600080fd5b61456685838601614394565b602084015260c084013591508082111561457f57600080fd5b61458b85838601614394565b604084015261459c60e085016141d4565b606084015261010084013560808401526101208401359150808211156145c157600080fd5b6145cd8583860161440e565b60a08401526101408401359150808211156145e757600080fd5b506145f484828501614496565b60c08301525092915050565b6000806040838503121561461357600080fd5b823567ffffffffffffffff8082111561462b57600080fd5b61463786838701614516565b9350602085013591508082111561464d57600080fd5b5061465a85828601614496565b9150509250929050565b60006020828403121561467657600080fd5b813567ffffffffffffffff81111561468d57600080fd5b820160a08185031215613d8357600080fd5b801515811461107857600080fd5b80356141df8161469f565b600060208083850312156146cb57600080fd5b823567ffffffffffffffff808211156146e357600080fd5b818501915085601f8301126146f757600080fd5b81356147056143b3826143ea565b81815260059190911b8301840190848101908883111561472457600080fd5b8585015b838110156147b4578035858111156147405760008081fd5b86016060818c03601f19018113156147585760008081fd5b61476061416b565b61476b8a840161427f565b815260408084013561477c8161469f565b828c01529183013591888311156147935760008081fd5b6147a18e8c85870101614394565b9082015285525050918601918601614728565b5098975050505050505050565b600082601f8301126147d257600080fd5b813560206147e26143b3836143ea565b82815260059290921b8401810191818101908684111561480157600080fd5b8286015b84811015611d0357803567ffffffffffffffff8111156148255760008081fd5b6148338986838b0101614516565b845250918301918301614805565b600082601f83011261485257600080fd5b813560206148626143b3836143ea565b82815260059290921b8401810191818101908684111561488157600080fd5b8286015b84811015611d0357803567ffffffffffffffff8111156148a55760008081fd5b6148b38986838b0101614496565b845250918301918301614885565b600082601f8301126148d257600080fd5b813560206148e26143b3836143ea565b8083825260208201915060208460051b87010193508684111561490457600080fd5b602086015b84811015611d035780358352918301918301614909565b600082601f83011261493157600080fd5b813560206149416143b3836143ea565b82815260059290921b8401810191818101908684111561496057600080fd5b8286015b84811015611d0357803567ffffffffffffffff808211156149855760008081fd5b818901915060a080601f19848d030112156149a05760008081fd5b6149a8614102565b6149b388850161427f565b8152604080850135848111156149c95760008081fd5b6149d78e8b838901016147c1565b8a84015250606080860135858111156149f05760008081fd5b6149fe8f8c838a0101614841565b8385015250608091508186013585811115614a195760008081fd5b614a278f8c838a01016148c1565b9184019190915250919093013590830152508352918301918301614964565b6000806040808486031215614a5a57600080fd5b833567ffffffffffffffff80821115614a7257600080fd5b614a7e87838801614920565b9450602091508186013581811115614a9557600080fd5b8601601f81018813614aa657600080fd5b8035614ab46143b3826143ea565b81815260059190911b8201840190848101908a831115614ad357600080fd5b8584015b83811015614b5f57803586811115614aef5760008081fd5b8501603f81018d13614b015760008081fd5b87810135614b116143b3826143ea565b81815260059190911b82018a0190898101908f831115614b315760008081fd5b928b01925b82841015614b4f5783358252928a0192908a0190614b36565b8652505050918601918601614ad7565b50809750505050505050509250929050565b803560ff811681146141df57600080fd5b600060208284031215614b9457600080fd5b610c4882614b71565b60008151808452602080850194506020840160005b83811015614bd75781516001600160a01b031687529582019590820190600101614bb2565b509495945050505050565b60208152600082518051602084015260ff602082015116604084015260ff604082015116606084015260608101511515608084015250602083015160c060a0840152614c3160e0840182614b9d565b90506040840151601f198483030160c0850152614c4e8282614b9d565b95945050505050565b60008060408385031215614c6a57600080fd5b614c738361427f565b946020939093013593505050565b60008060208385031215614c9457600080fd5b823567ffffffffffffffff80821115614cac57600080fd5b818501915085601f830112614cc057600080fd5b813581811115614ccf57600080fd5b8660208260061b8501011115614ce457600080fd5b60209290920196919550909350505050565b600060208284031215614d0857600080fd5b610c488261427f565b6020815281511515602082015267ffffffffffffffff60208301511660408201526000604083015160608084015261368b6080840182613ee5565b600060208284031215614d5e57600080fd5b8135613d83816141bf565b600082601f830112614d7a57600080fd5b81356020614d8a6143b3836143ea565b8083825260208201915060208460051b870101935086841115614dac57600080fd5b602086015b84811015611d03578035614dc4816141bf565b8352918301918301614db1565b60006020808385031215614de457600080fd5b823567ffffffffffffffff80821115614dfc57600080fd5b818501915085601f830112614e1057600080fd5b8135614e1e6143b3826143ea565b81815260059190911b83018401908481019088831115614e3d57600080fd5b8585015b838110156147b457803585811115614e5857600080fd5b860160c0818c03601f19011215614e6f5760008081fd5b614e776140d9565b8882013581526040614e8a818401614b71565b8a8301526060614e9b818501614b71565b8284015260809150614eae8285016146ad565b9083015260a08381013589811115614ec65760008081fd5b614ed48f8d83880101614d69565b838501525060c0840135915088821115614eee5760008081fd5b614efc8e8c84870101614d69565b9083015250845250918601918601614e41565b600060208284031215614f2157600080fd5b5035919050565b80356001600160e01b03811681146141df57600080fd5b600082601f830112614f5057600080fd5b81356020614f606143b3836143ea565b82815260069290921b84018101918181019086841115614f7f57600080fd5b8286015b84811015611d035760408189031215614f9c5760008081fd5b614fa4614125565b614fad8261427f565b8152614fba858301614f28565b81860152835291830191604001614f83565b600082601f830112614fdd57600080fd5b81356020614fed6143b3836143ea565b82815260079290921b8401810191818101908684111561500c57600080fd5b8286015b84811015611d0357808803608081121561502a5760008081fd5b61503261416b565b61503b8361427f565b8152604080601f19840112156150515760008081fd5b615059614125565b925061506687850161427f565b835261507381850161427f565b8388015281870192909252606083013591810191909152835291830191608001615010565b600060208083850312156150ab57600080fd5b823567ffffffffffffffff808211156150c357600080fd5b818501915060408083880312156150d957600080fd5b6150e1614125565b8335838111156150f057600080fd5b84016040818a03121561510257600080fd5b61510a614125565b81358581111561511957600080fd5b8201601f81018b1361512a57600080fd5b80356151386143b3826143ea565b81815260069190911b8201890190898101908d83111561515757600080fd5b928a01925b828410156151a75787848f0312156151745760008081fd5b61517c614125565b8435615187816141bf565b8152615194858d01614f28565b818d0152825292870192908a019061515c565b8452505050818701359350848411156151bf57600080fd5b6151cb8a858401614f3f565b81880152825250838501359150828211156151e557600080fd5b6151f188838601614fcc565b85820152809550505050505092915050565b634e487b7160e01b600052603260045260246000fd5b805160408084528151848201819052600092602091908201906060870190855b8181101561527057835180516001600160a01b031684528501516001600160e01b0316858401529284019291850191600101615239565b50508583015187820388850152805180835290840192506000918401905b808310156152ca578351805167ffffffffffffffff1683528501516001600160e01b03168583015292840192600192909201919085019061528e565b50979650505050505050565b602081526000610c486020830184615219565b67ffffffffffffffff8316815260608101613d836020830184805167ffffffffffffffff908116835260209182015116910152565b634e487b7160e01b600052601160045260246000fd5b67ffffffffffffffff8181168382160190808211156153555761535561531e565b5092915050565b60006020808352606084516040808487015261537b6060870183615219565b87850151878203601f19016040890152805180835290860193506000918601905b808310156147b457845167ffffffffffffffff8151168352878101516153db89850182805167ffffffffffffffff908116835260209182015116910152565b5084015182870152938601936001929092019160809091019061539c565b60006020828403121561540b57600080fd5b813567ffffffffffffffff81111561542257600080fd5b61368b84828501614920565b81810381811115610c4b57610c4b61531e565b634e487b7160e01b600052601260045260246000fd5b600067ffffffffffffffff8084168061547257615472615441565b92169190910692915050565b8082028115828204841417610c4b57610c4b61531e565b60008151808452602080850194506020840160005b83811015614bd757815180516001600160a01b0316885260209081015190880152604087019650908201906001016154aa565b8051825267ffffffffffffffff60208201511660208301526000604082015160a0604085015261551060a0850182613ee5565b9050606083015184820360608601526155298282613ee5565b91505060808301518482036080860152614c4e8282615495565b602081526000610c4860208301846154dd565b60808152600061556960808301876154dd565b61ffff9590951660208301525060408101929092526001600160a01b0316606090910152919050565b600082601f8301126155a357600080fd5b81516155b16143b38261436c565b8181528460208386010111156155c657600080fd5b61368b826020830160208701613ec1565b6000806000606084860312156155ec57600080fd5b83516155f78161469f565b602085015190935067ffffffffffffffff81111561561457600080fd5b61562086828701615592565b925050604084015190509250925092565b60006040828403121561564357600080fd5b61564b614125565b6156548361427f565b8152602083013560208201528091505092915050565b600181811c9082168061567e57607f821691505b60208210810361569e57634e487b7160e01b600052602260045260246000fd5b50919050565b6000602082840312156156b657600080fd5b8151613d838161469f565b80820180821115610c4b57610c4b61531e565b60ff8181168382160190811115610c4b57610c4b61531e565b8183823760009101908152919050565b828152606082602083013760800192915050565b600067ffffffffffffffff8084168061572c5761572c615441565b92169190910492915050565b60006020828403121561574a57600080fd5b815167ffffffffffffffff8082111561576257600080fd5b908301906060828603121561577657600080fd5b61577e61416b565b82518281111561578d57600080fd5b61579987828601615592565b8252506020830151828111156157ae57600080fd5b6157ba87828601615592565b6020830152506040830151828111156157d257600080fd5b6157de87828601615592565b60408301525095945050505050565b601f82111561121b576000816000526020600020601f850160051c810160208610156158165750805b601f850160051c820191505b8181101561242957828155600101615822565b815167ffffffffffffffff81111561584f5761584f6140c3565b6158638161585d845461566a565b846157ed565b602080601f83116001811461589857600084156158805750858301515b600019600386901b1c1916600185901b178555612429565b600085815260208120601f198616915b828110156158c7578886015182559484019460019091019084016158a8565b50858210156158e55787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020808352835460ff81161515602085015267ffffffffffffffff8160081c166040850152506001808501606080860152600081546159358161566a565b80608089015260a060018316600081146159565760018114615972576159a2565b60ff19841660a08b015260a083151560051b8b010194506159a2565b85600052602060002060005b848110156159995781548c820185015290880190890161597e565b8b0160a0019550505b50929998505050505050505050565b60208101600583106159c5576159c56142ca565b91905290565b60ff81811683821602908116908181146153555761535561531e565b600060a0820160ff88168352602087602085015260a0604085015281875480845260c086019150886000526020600020935060005b81811015615a415784546001600160a01b031683526001948501949284019201615a1c565b50508481036060860152615a558188614b9d565b935050505060ff831660808301529695505050505050565b600067ffffffffffffffff808616835280851660208401525060606040830152614c4e6060830184613ee5565b82815260406020820152600061368b6040830184613ee5565b67ffffffffffffffff8481168252831660208201526060810161368b60408301846142e0565b615ae381846142e0565b60406020820152600061368b6040830184613ee5565b600060208284031215615b0b57600080fd5b8151613d83816141bf565b6020815260008251610100806020850152615b35610120850183613ee5565b91506020850151615b52604086018267ffffffffffffffff169052565b5060408501516001600160a01b038116606086015250606085015160808501526080850151615b8c60a08601826001600160a01b03169052565b5060a0850151601f19808685030160c0870152615ba98483613ee5565b935060c08701519150808685030160e0870152615bc68483613ee5565b935060e0870151915080868503018387015250615be38382613ee5565b9695505050505050565b600060208284031215615bff57600080fd5b5051919050565b600060ff821660ff8103615c1c57615c1c61531e565b60010192915050565b848152600067ffffffffffffffff808616602084015280851660408401525060806060830152615be36080830184613ee5565b86815260c060208201526000615c7160c0830188613ee5565b6001600160a01b039690961660408301525067ffffffffffffffff9384166060820152608081019290925290911660a09091015292915050565b6020808252825182820181905260009190848201906040850190845b81811015615cfa57835180516001600160a01b031684526020908101519084015260408301938501939250600101615cc7565b50909695505050505050565b60008282518085526020808601955060208260051b8401016020860160005b84811015615d5357601f19868403018952615d41838351613ee5565b98840198925090830190600101615d25565b5090979650505050505050565b602081526000610c486020830184615d06565b60408152615dc460408201845180518252602081015167ffffffffffffffff808216602085015280604084015116604085015280606084015116606085015280608084015116608085015250505050565b600060208401516101608060e0850152615de26101a0850183613ee5565b915060408601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08086850301610100870152615e1f8483613ee5565b935060608801519150615e3e6101208701836001600160a01b03169052565b608088015161014087015260a08801519150808685030183870152615e638483615495565b935060c0880151925080868503016101808701525050615e838282615d06565b9150508281036020840152614c4e8185615d06565b6000815160208301516001600160e01b031980821693506004831015615ec85780818460040360031b1b83161693505b50505091905056fea164736f6c6343000818000a", } var EVM2EVMMultiOffRampABI = EVM2EVMMultiOffRampMetaData.ABI @@ -404,28 +404,6 @@ func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerSession) GetMerkleRoot(sour return _EVM2EVMMultiOffRamp.Contract.GetMerkleRoot(&_EVM2EVMMultiOffRamp.CallOpts, sourceChainSelector, root) } -func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCaller) GetSenderNonce(opts *bind.CallOpts, sourceChainSelector uint64, sender common.Address) (uint64, error) { - var out []interface{} - err := _EVM2EVMMultiOffRamp.contract.Call(opts, &out, "getSenderNonce", sourceChainSelector, sender) - - if err != nil { - return *new(uint64), err - } - - out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) - - return out0, err - -} - -func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) GetSenderNonce(sourceChainSelector uint64, sender common.Address) (uint64, error) { - return _EVM2EVMMultiOffRamp.Contract.GetSenderNonce(&_EVM2EVMMultiOffRamp.CallOpts, sourceChainSelector, sender) -} - -func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerSession) GetSenderNonce(sourceChainSelector uint64, sender common.Address) (uint64, error) { - return _EVM2EVMMultiOffRamp.Contract.GetSenderNonce(&_EVM2EVMMultiOffRamp.CallOpts, sourceChainSelector, sender) -} - func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCaller) GetSourceChainConfig(opts *bind.CallOpts, sourceChainSelector uint64) (EVM2EVMMultiOffRampSourceChainConfig, error) { var out []interface{} err := _EVM2EVMMultiOffRamp.contract.Call(opts, &out, "getSourceChainConfig", sourceChainSelector) @@ -606,15 +584,15 @@ func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorSession) Execute(report return _EVM2EVMMultiOffRamp.Contract.Execute(&_EVM2EVMMultiOffRamp.TransactOpts, reportContext, report) } -func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactor) ExecuteSingleMessage(opts *bind.TransactOpts, message InternalEVM2EVMMessage, offchainTokenData [][]byte) (*types.Transaction, error) { +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactor) ExecuteSingleMessage(opts *bind.TransactOpts, message InternalAny2EVMRampMessage, offchainTokenData [][]byte) (*types.Transaction, error) { return _EVM2EVMMultiOffRamp.contract.Transact(opts, "executeSingleMessage", message, offchainTokenData) } -func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) ExecuteSingleMessage(message InternalEVM2EVMMessage, offchainTokenData [][]byte) (*types.Transaction, error) { +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) ExecuteSingleMessage(message InternalAny2EVMRampMessage, offchainTokenData [][]byte) (*types.Transaction, error) { return _EVM2EVMMultiOffRamp.Contract.ExecuteSingleMessage(&_EVM2EVMMultiOffRamp.TransactOpts, message, offchainTokenData) } -func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorSession) ExecuteSingleMessage(message InternalEVM2EVMMessage, offchainTokenData [][]byte) (*types.Transaction, error) { +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorSession) ExecuteSingleMessage(message InternalAny2EVMRampMessage, offchainTokenData [][]byte) (*types.Transaction, error) { return _EVM2EVMMultiOffRamp.Contract.ExecuteSingleMessage(&_EVM2EVMMultiOffRamp.TransactOpts, message, offchainTokenData) } @@ -1687,244 +1665,6 @@ func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseSkippedAlreadyExec return event, nil } -type EVM2EVMMultiOffRampSkippedIncorrectNonceIterator struct { - Event *EVM2EVMMultiOffRampSkippedIncorrectNonce - - contract *bind.BoundContract - event string - - logs chan types.Log - sub ethereum.Subscription - done bool - fail error -} - -func (it *EVM2EVMMultiOffRampSkippedIncorrectNonceIterator) Next() bool { - - if it.fail != nil { - return false - } - - if it.done { - select { - case log := <-it.logs: - it.Event = new(EVM2EVMMultiOffRampSkippedIncorrectNonce) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - - select { - case log := <-it.logs: - it.Event = new(EVM2EVMMultiOffRampSkippedIncorrectNonce) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -func (it *EVM2EVMMultiOffRampSkippedIncorrectNonceIterator) Error() error { - return it.fail -} - -func (it *EVM2EVMMultiOffRampSkippedIncorrectNonceIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -type EVM2EVMMultiOffRampSkippedIncorrectNonce struct { - SourceChainSelector uint64 - Nonce uint64 - Sender common.Address - Raw types.Log -} - -func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterSkippedIncorrectNonce(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampSkippedIncorrectNonceIterator, error) { - - logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "SkippedIncorrectNonce") - if err != nil { - return nil, err - } - return &EVM2EVMMultiOffRampSkippedIncorrectNonceIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "SkippedIncorrectNonce", logs: logs, sub: sub}, nil -} - -func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchSkippedIncorrectNonce(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampSkippedIncorrectNonce) (event.Subscription, error) { - - logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "SkippedIncorrectNonce") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - - event := new(EVM2EVMMultiOffRampSkippedIncorrectNonce) - if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "SkippedIncorrectNonce", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseSkippedIncorrectNonce(log types.Log) (*EVM2EVMMultiOffRampSkippedIncorrectNonce, error) { - event := new(EVM2EVMMultiOffRampSkippedIncorrectNonce) - if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "SkippedIncorrectNonce", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -type EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflightIterator struct { - Event *EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflight - - contract *bind.BoundContract - event string - - logs chan types.Log - sub ethereum.Subscription - done bool - fail error -} - -func (it *EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflightIterator) Next() bool { - - if it.fail != nil { - return false - } - - if it.done { - select { - case log := <-it.logs: - it.Event = new(EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflight) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - - select { - case log := <-it.logs: - it.Event = new(EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflight) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -func (it *EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflightIterator) Error() error { - return it.fail -} - -func (it *EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflightIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -type EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflight struct { - SourceChainSelector uint64 - Nonce uint64 - Sender common.Address - Raw types.Log -} - -func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterSkippedSenderWithPreviousRampMessageInflight(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflightIterator, error) { - - logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "SkippedSenderWithPreviousRampMessageInflight") - if err != nil { - return nil, err - } - return &EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflightIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "SkippedSenderWithPreviousRampMessageInflight", logs: logs, sub: sub}, nil -} - -func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchSkippedSenderWithPreviousRampMessageInflight(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflight) (event.Subscription, error) { - - logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "SkippedSenderWithPreviousRampMessageInflight") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - - event := new(EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflight) - if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "SkippedSenderWithPreviousRampMessageInflight", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseSkippedSenderWithPreviousRampMessageInflight(log types.Log) (*EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflight, error) { - event := new(EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflight) - if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "SkippedSenderWithPreviousRampMessageInflight", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - type EVM2EVMMultiOffRampSourceChainConfigSetIterator struct { Event *EVM2EVMMultiOffRampSourceChainConfigSet @@ -2434,10 +2174,6 @@ func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRamp) ParseLog(log types.Log) (genera return _EVM2EVMMultiOffRamp.ParseRootRemoved(log) case _EVM2EVMMultiOffRamp.abi.Events["SkippedAlreadyExecutedMessage"].ID: return _EVM2EVMMultiOffRamp.ParseSkippedAlreadyExecutedMessage(log) - case _EVM2EVMMultiOffRamp.abi.Events["SkippedIncorrectNonce"].ID: - return _EVM2EVMMultiOffRamp.ParseSkippedIncorrectNonce(log) - case _EVM2EVMMultiOffRamp.abi.Events["SkippedSenderWithPreviousRampMessageInflight"].ID: - return _EVM2EVMMultiOffRamp.ParseSkippedSenderWithPreviousRampMessageInflight(log) case _EVM2EVMMultiOffRamp.abi.Events["SourceChainConfigSet"].ID: return _EVM2EVMMultiOffRamp.ParseSourceChainConfigSet(log) case _EVM2EVMMultiOffRamp.abi.Events["SourceChainSelectorAdded"].ID: @@ -2484,16 +2220,8 @@ func (EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage) Topic() common.Hash { return common.HexToHash("0x3b575419319662b2a6f5e2467d84521517a3382b908eb3d557bb3fdb0c50e23c") } -func (EVM2EVMMultiOffRampSkippedIncorrectNonce) Topic() common.Hash { - return common.HexToHash("0x852dc8e405695593e311bd83991cf39b14a328f304935eac6d3d55617f911d89") -} - -func (EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflight) Topic() common.Hash { - return common.HexToHash("0x5444a3301c7c42dd164cbf6ba4b72bf02504f86c049b06a27fc2b662e334bdbd") -} - func (EVM2EVMMultiOffRampSourceChainConfigSet) Topic() common.Hash { - return common.HexToHash("0xa73c588738263db34ef8c1942db8f99559bc6696f6a812d42e76bafb4c0e8d30") + return common.HexToHash("0x4f49973170c548fddd4a48341b75e131818913f38f44d47af57e8735eee588ba") } func (EVM2EVMMultiOffRampSourceChainSelectorAdded) Topic() common.Hash { @@ -2501,7 +2229,7 @@ func (EVM2EVMMultiOffRampSourceChainSelectorAdded) Topic() common.Hash { } func (EVM2EVMMultiOffRampStaticConfigSet) Topic() common.Hash { - return common.HexToHash("0x2f56698ec552a5d53d27d6f4b3dd8b6989f6426b6151a36aff61160c1d07efdf") + return common.HexToHash("0x683eb52ee924eb817377cfa8f41f238f4bb7a877da5267869dfffbad85f564d8") } func (EVM2EVMMultiOffRampTransmitted) Topic() common.Hash { @@ -2523,8 +2251,6 @@ type EVM2EVMMultiOffRampInterface interface { GetMerkleRoot(opts *bind.CallOpts, sourceChainSelector uint64, root [32]byte) (*big.Int, error) - GetSenderNonce(opts *bind.CallOpts, sourceChainSelector uint64, sender common.Address) (uint64, error) - GetSourceChainConfig(opts *bind.CallOpts, sourceChainSelector uint64) (EVM2EVMMultiOffRampSourceChainConfig, error) GetStaticConfig(opts *bind.CallOpts) (EVM2EVMMultiOffRampStaticConfig, error) @@ -2545,7 +2271,7 @@ type EVM2EVMMultiOffRampInterface interface { Execute(opts *bind.TransactOpts, reportContext [3][32]byte, report []byte) (*types.Transaction, error) - ExecuteSingleMessage(opts *bind.TransactOpts, message InternalEVM2EVMMessage, offchainTokenData [][]byte) (*types.Transaction, error) + ExecuteSingleMessage(opts *bind.TransactOpts, message InternalAny2EVMRampMessage, offchainTokenData [][]byte) (*types.Transaction, error) ManuallyExecute(opts *bind.TransactOpts, reports []InternalExecutionReportSingleChain, gasLimitOverrides [][]*big.Int) (*types.Transaction, error) @@ -2605,18 +2331,6 @@ type EVM2EVMMultiOffRampInterface interface { ParseSkippedAlreadyExecutedMessage(log types.Log) (*EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage, error) - FilterSkippedIncorrectNonce(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampSkippedIncorrectNonceIterator, error) - - WatchSkippedIncorrectNonce(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampSkippedIncorrectNonce) (event.Subscription, error) - - ParseSkippedIncorrectNonce(log types.Log) (*EVM2EVMMultiOffRampSkippedIncorrectNonce, error) - - FilterSkippedSenderWithPreviousRampMessageInflight(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflightIterator, error) - - WatchSkippedSenderWithPreviousRampMessageInflight(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflight) (event.Subscription, error) - - ParseSkippedSenderWithPreviousRampMessageInflight(log types.Log) (*EVM2EVMMultiOffRampSkippedSenderWithPreviousRampMessageInflight, error) - FilterSourceChainConfigSet(opts *bind.FilterOpts, sourceChainSelector []uint64) (*EVM2EVMMultiOffRampSourceChainConfigSetIterator, error) WatchSourceChainConfigSet(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampSourceChainConfigSet, sourceChainSelector []uint64) (event.Subscription, error) diff --git a/core/gethwrappers/ccip/generated/evm_2_evm_multi_onramp/evm_2_evm_multi_onramp.go b/core/gethwrappers/ccip/generated/evm_2_evm_multi_onramp/evm_2_evm_multi_onramp.go index 6b45c341a0..fadeefb48b 100644 --- a/core/gethwrappers/ccip/generated/evm_2_evm_multi_onramp/evm_2_evm_multi_onramp.go +++ b/core/gethwrappers/ccip/generated/evm_2_evm_multi_onramp/evm_2_evm_multi_onramp.go @@ -137,8 +137,8 @@ type InternalEVM2EVMMessage struct { } var EVM2EVMMultiOnRampMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfigArgs[]\",\"name\":\"destChainConfigArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.PremiumMultiplierWeiPerEthArgs[]\",\"name\":\"premiumMultiplierWeiPerEthArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigSingleTokenArgs[]\",\"name\":\"tokenTransferFeeConfigs\",\"type\":\"tuple[]\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigArgs[]\",\"name\":\"tokenTransferFeeConfigArgs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CannotSendZeroTokens\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"DestinationChainNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExtraArgOutOfOrderExecutionMustBeTrue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GetSupportedTokensFunctionalityRemovedCheckAdminRegistry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"}],\"name\":\"InvalidDestBytesOverhead\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidDestChainConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidEVMAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidExtraArgsTag\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"msgFeeJuels\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint256\"}],\"name\":\"MessageFeeTooHigh\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MessageGasLimitTooHigh\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"maxSize\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actualSize\",\"type\":\"uint256\"}],\"name\":\"MessageTooLarge\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorReason\",\"type\":\"bytes\"}],\"name\":\"MessageValidationError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeCalledByRouter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"NotAFeeToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustSetOriginalSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SourceTokenDataTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedNumberOfTokens\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"UnsupportedToken\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"strict\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structInternal.EVM2EVMMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"CCIPSendRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"metadataHash\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfig\",\"name\":\"destChainConfig\",\"type\":\"tuple\"}],\"name\":\"DestChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"DestChainDynamicConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeValueJuels\",\"type\":\"uint256\"}],\"name\":\"FeePaid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FeeTokenWithdrawn\",\"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\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"name\":\"PremiumMultiplierWeiPerEthUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"destChainSelector\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"TokenTransferFeeConfigDeleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"name\":\"TokenTransferFeeConfigUpdated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfigArgs[]\",\"name\":\"destChainConfigArgs\",\"type\":\"tuple[]\"}],\"name\":\"applyDestChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.PremiumMultiplierWeiPerEthArgs[]\",\"name\":\"premiumMultiplierWeiPerEthArgs\",\"type\":\"tuple[]\"}],\"name\":\"applyPremiumMultiplierWeiPerEthUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigSingleTokenArgs[]\",\"name\":\"tokenTransferFeeConfigs\",\"type\":\"tuple[]\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigArgs[]\",\"name\":\"tokenTransferFeeConfigArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigRemoveArgs[]\",\"name\":\"tokensToUseDefaultFeeConfigs\",\"type\":\"tuple[]\"}],\"name\":\"applyTokenTransferFeeConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structClient.EVM2AnyMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"}],\"name\":\"forwardFromRouter\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getDestChainConfig\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"metadataHash\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDynamicConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getExpectedNextSequenceNumber\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structClient.EVM2AnyMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"getFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"contractIERC20\",\"name\":\"sourceToken\",\"type\":\"address\"}],\"name\":\"getPoolBySourceToken\",\"outputs\":[{\"internalType\":\"contractIPoolV1\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getPremiumMultiplierWeiPerEth\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStaticConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"name\":\"getSupportedTokens\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenTransferFeeConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"setDynamicConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawFeeTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6101406040523480156200001257600080fd5b5060405162006b4538038062006b458339810160408190526200003591620014af565b33806000816200008c5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000bf57620000bf8162000214565b505085516001600160a01b031615905080620000e6575060208501516001600160401b0316155b80620000fd575060608501516001600160a01b0316155b8062000114575060808501516001600160a01b0316155b806200012b575060a08501516001600160a01b0316155b156200014a576040516306b7c75960e31b815260040160405180910390fd5b84516001600160a01b0390811660a090815260208701516001600160401b031660c05260408701516001600160601b031660809081526060880151831660e0528701518216610100528601511661012052620001a684620002bf565b620001b18362000482565b620001bc8262000a00565b60408051600080825260208201909252620002099183919062000202565b6040805180820190915260008082526020820152815260200190600190039081620001da5790505b5062000acc565b5050505050620018f7565b336001600160a01b038216036200026e5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000083565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60208101516001600160a01b03161580620002e5575060608101516001600160a01b0316155b1562000304576040516306b7c75960e31b815260040160405180910390fd5b8051600280546001600160a01b039283166001600160a01b0319918216179091556020808401516003805491851691841691909117905560408085015160048054918616918516919091179055606080860151600580549187169190951617909355805160c0808201835260a080518716835290516001600160401b031693820193909352608080516001600160601b03168284015260e05186169482019490945261010051851693810193909352610120519093169082015290517f4012fe74115805c44a121d8f9edc3d234df8f05f53b52864b8e5e8a30384b8aa916200047791849082516001600160a01b0390811682526020808501516001600160401b0316818401526040808601516001600160601b03168185015260608087015184168186015260808088015185169086015260a0968701518416968501969096528451831660c085015290840151821660e084015283015181166101008301529190920151166101208201526101400190565b60405180910390a150565b60005b8151811015620009fc576000828281518110620004a657620004a6620015f5565b602002602001015190506000838381518110620004c757620004c7620015f5565b6020026020010151600001519050806001600160401b031660001480620004fe5750602082015161018001516001600160401b0316155b15620005295760405163c35aa79d60e01b81526001600160401b038216600482015260240162000083565b600060066000836001600160401b03166001600160401b0316815260200190815260200160002090506000836040015190506000604051806080016040528086602001518152602001836001600160a01b031681526020018460020160149054906101000a90046001600160401b03166001600160401b031681526020018460030154815250905080600001518360000160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548161ffff021916908361ffff16021790555060408201518160000160036101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160076101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600b6101000a81548163ffffffff021916908363ffffffff16021790555060a082015181600001600f6101000a81548161ffff021916908361ffff16021790555060c08201518160000160116101000a81548163ffffffff021916908363ffffffff16021790555060e08201518160000160156101000a81548161ffff021916908361ffff1602179055506101008201518160000160176101000a81548161ffff021916908361ffff1602179055506101208201518160000160196101000a81548161ffff021916908361ffff16021790555061014082015181600001601b6101000a81548163ffffffff021916908363ffffffff1602179055506101608201518160010160006101000a81548163ffffffff021916908363ffffffff1602179055506101808201518160010160046101000a8154816001600160401b0302191690836001600160401b031602179055506101a082015181600101600c6101000a8154816001600160401b0302191690836001600160401b031602179055506101c08201518160010160146101000a81548163ffffffff021916908363ffffffff1602179055506101e08201518160010160186101000a81548160ff02191690831515021790555090505082600301546000801b03620009195760c051604080517f8acd72527118c8324937b1a42e02cd246697c3b633f1742f3cae11de233722b360208201526001600160401b0392831691810191909152908516606082015230608082015260a00160408051601f1981840301815291905280516020909101206060820181905260038401556001600160a01b03821615620008d0576002830180546001600160a01b0319166001600160a01b0384161790555b836001600160401b03167f4be92415590c4554edbd7d3fbf308eeb6e5d00ababade0791f835b4a18416ed6846040516200090b91906200160b565b60405180910390a2620009eb565b60028301546001600160a01b03838116911614620009565760405163c35aa79d60e01b81526001600160401b038516600482015260240162000083565b60208560200151610160015163ffffffff161015620009a357602085015161016001516040516312766e0160e11b81526000600482015263ffffffff909116602482015260440162000083565b836001600160401b03167f1173725509a61aed633c58cd12e31c104cd26ca24f6c270c8ac81c5f6f12e8e98660200151604051620009e29190620017b5565b60405180910390a25b505050505080600101905062000485565b5050565b60005b8151811015620009fc57600082828151811062000a245762000a24620015f5565b6020026020010151600001519050600083838151811062000a495762000a49620015f5565b6020908102919091018101518101516001600160a01b03841660008181526007845260409081902080546001600160401b0319166001600160401b0385169081179091559051908152919350917fbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d910160405180910390a2505060010162000a03565b60005b825181101562000d3957600083828151811062000af05762000af0620015f5565b6020026020010151905060008160000151905060005b82602001515181101562000d2a5760008360200151828151811062000b2f5762000b2f620015f5565b602002602001015160200151905060008460200151838151811062000b585762000b58620015f5565b60200260200101516000015190506020826080015163ffffffff16101562000bb15760808201516040516312766e0160e11b81526001600160a01b038316600482015263ffffffff909116602482015260440162000083565b6001600160401b03841660008181526008602090815260408083206001600160a01b0386168085529083529281902086518154938801518389015160608a015160808b015160a08c01511515600160901b0260ff60901b1963ffffffff928316600160701b021664ffffffffff60701b199383166a01000000000000000000000263ffffffff60501b1961ffff90961668010000000000000000029590951665ffffffffffff60401b19968416640100000000026001600160401b0319909b16939097169290921798909817939093169390931717919091161792909217909155519091907f94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b59062000d17908690600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b60405180910390a3505060010162000b06565b50505080600101905062000acf565b5060005b815181101562000dff57600082828151811062000d5e5762000d5e620015f5565b6020026020010151600001519050600083838151811062000d835762000d83620015f5565b6020908102919091018101518101516001600160401b03841660008181526008845260408082206001600160a01b038516808452955280822080546001600160981b03191690555192945090917ffa22e84f9c809b5b7e94f084eb45cf17a5e4703cecef8f27ed35e54b719bffcd9190a3505060010162000d3d565b505050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b038111828210171562000e3f5762000e3f62000e04565b60405290565b60405161020081016001600160401b038111828210171562000e3f5762000e3f62000e04565b604080519081016001600160401b038111828210171562000e3f5762000e3f62000e04565b60405160c081016001600160401b038111828210171562000e3f5762000e3f62000e04565b604051601f8201601f191681016001600160401b038111828210171562000ee05762000ee062000e04565b604052919050565b80516001600160a01b038116811462000f0057600080fd5b919050565b80516001600160401b038116811462000f0057600080fd5b60006080828403121562000f3057600080fd5b604051608081016001600160401b038111828210171562000f555762000f5562000e04565b60405290508062000f668362000ee8565b815262000f766020840162000ee8565b602082015262000f896040840162000ee8565b604082015262000f9c6060840162000ee8565b60608201525092915050565b60006001600160401b0382111562000fc45762000fc462000e04565b5060051b60200190565b8051801515811462000f0057600080fd5b805161ffff8116811462000f0057600080fd5b805163ffffffff8116811462000f0057600080fd5b600082601f8301126200101957600080fd5b81516020620010326200102c8362000fa8565b62000eb5565b82815261024092830285018201928282019190878511156200105357600080fd5b8387015b85811015620012125780890382811215620010725760008081fd5b6200107c62000e1a565b620010878362000f05565b815261020080601f19840112156200109f5760008081fd5b620010a962000e45565b9250620010b888850162000fce565b83526040620010c981860162000fdf565b898501526060620010dc81870162000ff2565b828601526080620010ef81880162000ff2565b8287015260a091506200110482880162000ff2565b9086015260c06200111787820162000fdf565b8287015260e091506200112c82880162000ff2565b908601526101006200114087820162000fdf565b8287015261012091506200115682880162000fdf565b908601526101406200116a87820162000fdf565b8287015261016091506200118082880162000ff2565b908601526101806200119487820162000ff2565b828701526101a09150620011aa82880162000f05565b908601526101c0620011be87820162000f05565b828701526101e09150620011d482880162000ff2565b90860152620011e586840162000fce565b81860152508389840152620011fe610220860162000ee8565b908301525085525092840192810162001057565b5090979650505050505050565b600082601f8301126200123157600080fd5b81516020620012446200102c8362000fa8565b82815260069290921b840181019181810190868411156200126457600080fd5b8286015b84811015620012ba5760408189031215620012835760008081fd5b6200128d62000e6b565b620012988262000ee8565b8152620012a785830162000f05565b8186015283529183019160400162001268565b509695505050505050565b600082601f830112620012d757600080fd5b81516020620012ea6200102c8362000fa8565b82815260059290921b840181019181810190868411156200130a57600080fd5b8286015b84811015620012ba5780516001600160401b03808211156200132f57600080fd5b908801906040601f19838c0381018213156200134a57600080fd5b6200135462000e6b565b6200136189860162000f05565b815282850151848111156200137557600080fd5b8086019550508c603f8601126200138b57600080fd5b888501519350620013a06200102c8562000fa8565b84815260e09094028501830193898101908e861115620013bf57600080fd5b958401955b858710156200149857868f0360e0811215620013df57600080fd5b620013e962000e6b565b620013f48962000ee8565b815260c086830112156200140757600080fd5b6200141162000e90565b9150620014208d8a0162000ff2565b82526200142f878a0162000ff2565b8d8301526200144160608a0162000fdf565b878301526200145360808a0162000ff2565b60608301526200146660a08a0162000ff2565b60808301526200147960c08a0162000fce565b60a0830152808d0191909152825260e09690960195908a0190620013c4565b828b0152508752505050928401925083016200130e565b60008060008060008587036101a0811215620014ca57600080fd5b60c0811215620014d957600080fd5b50620014e462000e90565b620014ef8762000ee8565b8152620014ff6020880162000f05565b602082015260408701516001600160601b03811681146200151f57600080fd5b6040820152620015326060880162000ee8565b6060820152620015456080880162000ee8565b60808201526200155860a0880162000ee8565b60a082015294506200156e8760c0880162000f1d565b6101408701519094506001600160401b03808211156200158d57600080fd5b6200159b89838a0162001007565b9450610160880151915080821115620015b357600080fd5b620015c189838a016200121f565b9350610180880151915080821115620015d957600080fd5b50620015e888828901620012c5565b9150509295509295909350565b634e487b7160e01b600052603260045260246000fd5b815460ff81161515825261026082019061ffff600882901c8116602085015263ffffffff601883901c811660408601526200165360608601828560381c1663ffffffff169052565b6200166b60808601828560581c1663ffffffff169052565b6200168160a08601838560781c1661ffff169052565b6200169960c08601828560881c1663ffffffff169052565b620016af60e08601838560a81c1661ffff169052565b620016c66101008601838560b81c1661ffff169052565b620016dd6101208601838560c81c1661ffff169052565b620016f66101408601828560d81c1663ffffffff169052565b600186015463ffffffff8282161661016087015292506001600160401b03602084901c811661018087015291506200173f6101a08601838560601c166001600160401b03169052565b620017586101c08601828560a01c1663ffffffff169052565b506200176f6101e0850160ff8460c01c1615159052565b60028501546001600160a01b0381166102008601529150620017a26102208501828460a01c166001600160401b03169052565b5050600383015461024083015292915050565b81511515815261020081016020830151620017d6602084018261ffff169052565b506040830151620017ef604084018263ffffffff169052565b50606083015162001808606084018263ffffffff169052565b50608083015162001821608084018263ffffffff169052565b5060a08301516200183860a084018261ffff169052565b5060c08301516200185160c084018263ffffffff169052565b5060e08301516200186860e084018261ffff169052565b506101008381015161ffff9081169184019190915261012080850151909116908301526101408084015163ffffffff9081169184019190915261016080850151821690840152610180808501516001600160401b03908116918501919091526101a080860151909116908401526101c080850151909116908301526101e0928301511515929091019190915290565b60805160a05160c05160e0516101005161012051615195620019b0600039600081816102da01528181610f260152612adf0152600081816102ab01528181612ab7015261323e01526000818161027c01528181612a8d0152612bf30152600081816102180152818161265001528181612a2801526130f90152600081816101e901528181612a0301528181612eda0152612f8401526000818161024801528181612a5a0152818161304601526130b601526151956000f3fe608060405234801561001057600080fd5b50600436106101515760003560e01c806379ba5097116100cd578063a69c64c011610081578063df0aa9e911610066578063df0aa9e9146108c0578063f2fde38b146108d3578063fbca3b74146108e657600080fd5b8063a69c64c01461089a578063a6f3ab6c146108ad57600080fd5b80638da5cb5b116100b25780638da5cb5b146108635780639041be3d14610874578063932e53c81461088757600080fd5b806379ba5097146106f857806382b49eb01461070057600080fd5b80633a019940116101245780636def4ce7116101095780636def4ce7146103b65780637437ff9f14610672578063770e2dc4146106e557600080fd5b80633a0199401461038157806348a98aa41461038b57600080fd5b8063061877e31461015657806306285c69146101a7578063181f5a771461031757806320487ded14610360575b600080fd5b610189610164366004613945565b6001600160a01b031660009081526007602052604090205467ffffffffffffffff1690565b60405167ffffffffffffffff90911681526020015b60405180910390f35b61030a6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091526040518060c001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250905090565b60405161019e9190613962565b6103536040518060400160405280601c81526020017f45564d3245564d4d756c74694f6e52616d7020312e362e302d6465760000000081525081565b60405161019e9190613a1e565b61037361036e366004613a6a565b610906565b60405190815260200161019e565b610389610d27565b005b61039e610399366004613aba565b610eeb565b6040516001600160a01b03909116815260200161019e565b6106656103c4366004613af3565b604080516102808101825260006080820181815260a0830182905260c0830182905260e08301829052610100830182905261012083018290526101408301829052610160830182905261018083018290526101a083018290526101c083018290526101e08301829052610200830182905261022083018290526102408301829052610260830182905282526020820181905291810182905260608101919091525067ffffffffffffffff908116600090815260066020908152604091829020825161028081018452815460ff80821615156080840190815261ffff610100808504821660a087015263ffffffff63010000008604811660c08801526701000000000000008604811660e08801526b01000000000000000000000086048116918701919091526f01000000000000000000000000000000850482166101208701527101000000000000000000000000000000000085048116610140870152750100000000000000000000000000000000000000000085048216610160870152770100000000000000000000000000000000000000000000008504821661018087015279010000000000000000000000000000000000000000000000000085049091166101a08601527b0100000000000000000000000000000000000000000000000000000090930483166101c085015260018501548084166101e0860152640100000000810489166102008601526c010000000000000000000000008104891661022086015274010000000000000000000000000000000000000000808204909416610240860152780100000000000000000000000000000000000000000000000090049091161515610260840152825260028301546001600160a01b0381169483019490945290920490931691810191909152600390910154606082015290565b60405161019e9190613c41565b6106d860408051608081018252600080825260208201819052918101829052606081019190915250604080516080810182526002546001600160a01b03908116825260035481166020830152600454811692820192909252600554909116606082015290565b60405161019e9190613c8e565b6103896106f3366004613ec1565b610f9a565b610389610fb0565b61080361070e366004613aba565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9190911660009081526008602090815260408083206001600160a01b0394909416835292815290829020825160c081018452905463ffffffff8082168352640100000000820481169383019390935268010000000000000000810461ffff16938201939093526a01000000000000000000008304821660608201526e01000000000000000000000000000083049091166080820152720100000000000000000000000000000000000090910460ff16151560a082015290565b60405161019e9190600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b6000546001600160a01b031661039e565b610189610882366004613af3565b611079565b61038961089536600461413b565b6110bd565b6103896108a8366004614348565b6110d1565b6103896108bb366004614406565b6110e2565b6103736108ce36600461448b565b6110f3565b6103896108e1366004613945565b61157b565b6108f96108f4366004613af3565b61158c565b60405161019e91906144f7565b67ffffffffffffffff82166000908152600660205260408120805460ff1661096b576040517f99ac52f200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff851660048201526024015b60405180910390fd5b600061099961097d6080860186614544565b6001850154640100000000900467ffffffffffffffff166115c0565b90506109ca856109ac6020870187614544565b84519091506109be6040890189614592565b9050856020015161172d565b60006007816109df6080880160608901613945565b6001600160a01b03168152602081019190915260400160009081205467ffffffffffffffff169150819003610a5c57610a1e6080860160608701613945565b6040517fa7499d200000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610962565b60035460009081906001600160a01b031663ffdb4b37610a8260808a0160608b01613945565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b03909116600482015267ffffffffffffffff8b1660248201526044016040805180830381865afa158015610aed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b119190614608565b90925090506000808080610b2860408c018c614592565b90501115610b6357610b578b610b4460808d0160608e01613945565b87610b5260408f018f614592565b6118a3565b91945092509050610b9a565b6001880154610b979074010000000000000000000000000000000000000000900463ffffffff16662386f26fc10000614661565b92505b875460009077010000000000000000000000000000000000000000000000900461ffff1615610c0657610c038c6dffffffffffffffffffffffffffff607088901c16610be960208f018f614544565b90508e8060400190610bfb9190614592565b905086611c94565b90505b600089600101600c9054906101000a900467ffffffffffffffff1667ffffffffffffffff168463ffffffff168b600001600f9054906101000a900461ffff1661ffff168e8060200190610c599190614544565b610c64929150614661565b8c548c51610c87916b010000000000000000000000900463ffffffff1690614678565b610c919190614678565b610c9b9190614678565b610cb5906dffffffffffffffffffffffffffff8916614661565b610cbf9190614661565b90507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff87168282610cf667ffffffffffffffff8c1689614661565b610d009190614678565b610d0a9190614678565b610d14919061468b565b9a50505050505050505050505b92915050565b600354604080517fcdc73d5100000000000000000000000000000000000000000000000000000000815290516000926001600160a01b03169163cdc73d5191600480830192869291908290030181865afa158015610d89573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610db191908101906146c6565b6005549091506001600160a01b031660005b8251811015610ee6576000838281518110610de057610de0614755565b60209081029190910101516040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610e4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e729190614784565b90508015610edc57610e8e6001600160a01b0383168583611d9a565b816001600160a01b0316846001600160a01b03167f508d7d183612c18fc339b42618912b9fa3239f631dd7ec0671f950200a0fa66e83604051610ed391815260200190565b60405180910390a35b5050600101610dc3565b505050565b6040517fbbe4f6db0000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063bbe4f6db90602401602060405180830381865afa158015610f6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f93919061479d565b9392505050565b610fa2611e1a565b610fac8282611e76565b5050565b6001546001600160a01b0316331461100a5760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610962565b60008054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b67ffffffffffffffff8082166000908152600660205260408120600201549091610d21917401000000000000000000000000000000000000000090041660016147ba565b6110c5611e1a565b6110ce81612256565b50565b6110d9611e1a565b6110ce81612849565b6110ea611e1a565b6110ce81612926565b67ffffffffffffffff841660009081526006602052604081208161111a8288888888612b3f565b905060005b8161014001515181101561151157600061113c6040890189614592565b8381811061114c5761114c614755565b90506040020180360381019061116291906147e2565b905080602001516000036111a2576040517f5cf0444900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006111b28a8360000151610eeb565b90506001600160a01b038116158061126857506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527faff2afbf0000000000000000000000000000000000000000000000000000000060048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa158015611242573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611266919061481c565b155b156112ad5781516040517fbf16aab60000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610962565b6000816001600160a01b0316639a4575b96040518060a001604052808d80600001906112d99190614544565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525067ffffffffffffffff8f166020808301919091526001600160a01b03808e16604080850191909152918901516060840152885116608090920191909152517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526113849190600401614839565b6000604051808303816000875af11580156113a3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113cb9190810190614906565b9050602081602001515111801561142c575067ffffffffffffffff8b16600090815260086020908152604080832086516001600160a01b0316845282529091205490820151516e01000000000000000000000000000090910463ffffffff16105b156114715782516040517f36f536ca0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610962565b805161147c906133f7565b5060408051606081019091526001600160a01b03831660808201528060a0810160405160208183030381529060405281526020018260000151815260200182602001518152506040516020016114d29190614997565b60405160208183030381529060405285610160015185815181106114f8576114f8614755565b602002602001018190525050505080600101905061111f565b50611520818360030154613452565b61018082015260405167ffffffffffffffff8816907fc79f9c3e610deac14de4e704195fe17eab0983ee9916866bc04d16a00f54daa690611562908490614aa2565b60405180910390a261018001519150505b949350505050565b611583611e1a565b6110ce816135ad565b60606040517f9e7177c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805180820190915260008082526020820152600083900361160157506040805180820190915267ffffffffffffffff8216815260006020820152610f93565b600061160d8486614bd7565b90507fe7e230f0000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000082160161167a576116658460048188614c1f565b8101906116729190614c49565b915050610f93565b7f6859a837000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008216016116fb5760408051808201909152806116db866004818a614c1f565b8101906116e89190614c81565b815260006020909101529150610f939050565b6040517f5247fdce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8516600090815260066020526040902080546301000000900463ffffffff168511156117a65780546040517f86933789000000000000000000000000000000000000000000000000000000008152630100000090910463ffffffff16600482015260248101869052604401610962565b8054670100000000000000900463ffffffff168411156117f2576040517f4c4fc93a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054610100900461ffff16831115611836576040517f4c056b6a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018101547801000000000000000000000000000000000000000000000000900460ff168015611864575081155b1561189b576040517fee433e9900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b6000808083815b81811015611c875760008787838181106118c6576118c6614755565b9050604002018036038101906118dc91906147e2565b905060006001600160a01b03166118f78c8360000151610eeb565b6001600160a01b0316036119455780516040517fbf16aab60000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610962565b67ffffffffffffffff8b16600090815260086020908152604080832084516001600160a01b03168452825291829020825160c081018452905463ffffffff8082168352640100000000820481169383019390935268010000000000000000810461ffff16938201939093526a01000000000000000000008304821660608201526e01000000000000000000000000000083049091166080820152720100000000000000000000000000000000000090910460ff16151560a08201819052611ab35767ffffffffffffffff8c1660009081526006602052604090208054611a5390790100000000000000000000000000000000000000000000000000900461ffff16662386f26fc10000614661565b611a5d9089614678565b8154909850611a91907b01000000000000000000000000000000000000000000000000000000900463ffffffff1688614c9a565b6001820154909750611aa99063ffffffff1687614c9a565b9550505050611c7f565b604081015160009061ffff1615611bcf5760008c6001600160a01b031684600001516001600160a01b031614611b725760035484516040517f4ab35b0b0000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820152911690634ab35b0b90602401602060405180830381865afa158015611b47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b6b9190614cb7565b9050611b75565b508a5b620186a0836040015161ffff16611bb78660200151847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1661366e90919063ffffffff16565b611bc19190614661565b611bcb919061468b565b9150505b6060820151611bde9088614c9a565b9650816080015186611bf09190614c9a565b8251909650600090611c0f9063ffffffff16662386f26fc10000614661565b905080821015611c2e57611c23818a614678565b985050505050611c7f565b6000836020015163ffffffff16662386f26fc10000611c4d9190614661565b905080831115611c6d57611c61818b614678565b99505050505050611c7f565b611c77838b614678565b995050505050505b6001016118aa565b5050955095509592505050565b60008063ffffffff8316611ca9608086614661565b611cb587610220614678565b611cbf9190614678565b611cc99190614678565b67ffffffffffffffff8816600090815260066020526040812080549293509171010000000000000000000000000000000000810463ffffffff1690611d2b907501000000000000000000000000000000000000000000900461ffff1685614661565b611d359190614678565b825490915077010000000000000000000000000000000000000000000000900461ffff16611d736dffffffffffffffffffffffffffff8a1683614661565b611d7d9190614661565b611d8d90655af3107a4000614661565b9998505050505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052610ee69084906136ab565b6000546001600160a01b03163314611e745760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610962565b565b60005b825181101561217f576000838281518110611e9657611e96614755565b6020026020010151905060008160000151905060005b82602001515181101561217157600083602001518281518110611ed157611ed1614755565b6020026020010151602001519050600084602001518381518110611ef757611ef7614755565b60200260200101516000015190506020826080015163ffffffff161015611f675760808201516040517f24ecdc020000000000000000000000000000000000000000000000000000000081526001600160a01b038316600482015263ffffffff9091166024820152604401610962565b67ffffffffffffffff841660008181526008602090815260408083206001600160a01b0386168085529083529281902086518154938801518389015160608a015160808b015160a08c015115157201000000000000000000000000000000000000027fffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffff63ffffffff9283166e01000000000000000000000000000002167fffffffffffffffffffffffffff0000000000ffffffffffffffffffffffffffff9383166a0100000000000000000000027fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff61ffff9096166801000000000000000002959095167fffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffff968416640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b16939097169290921798909817939093169390931717919091161792909217909155519091907f94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b59061215f908690600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b60405180910390a35050600101611eac565b505050806001019050611e79565b5060005b8151811015610ee65760008282815181106121a0576121a0614755565b602002602001015160000151905060008383815181106121c2576121c2614755565b60209081029190910181015181015167ffffffffffffffff841660008181526008845260408082206001600160a01b038516808452955280822080547fffffffffffffffffffffffffff000000000000000000000000000000000000001690555192945090917ffa22e84f9c809b5b7e94f084eb45cf17a5e4703cecef8f27ed35e54b719bffcd9190a35050600101612183565b60005b8151811015610fac57600082828151811061227657612276614755565b60200260200101519050600083838151811061229457612294614755565b60200260200101516000015190508067ffffffffffffffff16600014806122cc57506020820151610180015167ffffffffffffffff16155b1561230f576040517fc35aa79d00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff82166004820152602401610962565b6000600660008367ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002090506000836040015190506000604051806080016040528086602001518152602001836001600160a01b031681526020018460020160149054906101000a900467ffffffffffffffff1667ffffffffffffffff1681526020018460030154815250905080600001518360000160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548161ffff021916908361ffff16021790555060408201518160000160036101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160076101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600b6101000a81548163ffffffff021916908363ffffffff16021790555060a082015181600001600f6101000a81548161ffff021916908361ffff16021790555060c08201518160000160116101000a81548163ffffffff021916908363ffffffff16021790555060e08201518160000160156101000a81548161ffff021916908361ffff1602179055506101008201518160000160176101000a81548161ffff021916908361ffff1602179055506101208201518160000160196101000a81548161ffff021916908361ffff16021790555061014082015181600001601b6101000a81548163ffffffff021916908363ffffffff1602179055506101608201518160010160006101000a81548163ffffffff021916908363ffffffff1602179055506101808201518160010160046101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506101a082015181600101600c6101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506101c08201518160010160146101000a81548163ffffffff021916908363ffffffff1602179055506101e08201518160010160186101000a81548160ff02191690831515021790555090505082600301546000801b0361273957604080517f8acd72527118c8324937b1a42e02cd246697c3b633f1742f3cae11de233722b3602082015267ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811692820192909252908516606082015230608082015260a00160408051601f1981840301815291905280516020909101206060820181905260038401556001600160a01b038216156126f2576002830180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384161790555b8367ffffffffffffffff167f4be92415590c4554edbd7d3fbf308eeb6e5d00ababade0791f835b4a18416ed68460405161272c9190614cd2565b60405180910390a2612839565b60028301546001600160a01b0383811691161461278e576040517fc35aa79d00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff85166004820152602401610962565b60208560200151610160015163ffffffff1610156127f257602085015161016001516040517f24ecdc020000000000000000000000000000000000000000000000000000000081526000600482015263ffffffff9091166024820152604401610962565b8367ffffffffffffffff167f1173725509a61aed633c58cd12e31c104cd26ca24f6c270c8ac81c5f6f12e8e986602001516040516128309190614e73565b60405180910390a25b5050505050806001019050612259565b60005b8151811015610fac57600082828151811061286957612869614755565b6020026020010151600001519050600083838151811061288b5761288b614755565b6020908102919091018101518101516001600160a01b03841660008181526007845260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff85169081179091559051908152919350917fbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d910160405180910390a2505060010161284c565b60208101516001600160a01b0316158061294b575060608101516001600160a01b0316155b15612982576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600280547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b03938416179091556020808401516003805484169185169190911790556040808501516004805485169186169190911790556060808601516005805490951690861617909355805160c0810182527f0000000000000000000000000000000000000000000000000000000000000000851681527f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff16928101929092527f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16828201527f00000000000000000000000000000000000000000000000000000000000000008416928201929092527f0000000000000000000000000000000000000000000000000000000000000000831660808201527f000000000000000000000000000000000000000000000000000000000000000090921660a0830152517f4012fe74115805c44a121d8f9edc3d234df8f05f53b52864b8e5e8a30384b8aa91612b34918490614e82565b60405180910390a150565b604080516101a08101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e082018390526101008201839052610120820181905261014082018190526101608201526101808101919091526040517f2cbc26bb000000000000000000000000000000000000000000000000000000008152608086901b77ffffffffffffffff000000000000000000000000000000001660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632cbc26bb90602401602060405180830381865afa158015612c42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c66919061481c565b15612ca9576040517ffdbd6a7200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff86166004820152602401610962565b6001600160a01b038216612ce9576040517fa4ec747900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002546001600160a01b03163314612d2d576040517f1c0a352900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855460ff16612d74576040517f99ac52f200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff86166004820152602401610962565b6000612da2612d866080870187614544565b60018a0154640100000000900467ffffffffffffffff166115c0565b90506000612db36040870187614592565b9150612dda905087612dc86020890189614544565b9050846000015184866020015161172d565b8015612ece576004546001600160a01b03168015612ecc576040517fe0a0e5060000000000000000000000000000000000000000000000000000000081526001600160a01b0382169063e0a0e50690612e39908b908b90600401614fe4565b600060405180830381600087803b158015612e5357600080fd5b505af1925050508015612e64575060015b612ecc573d808015612e92576040519150601f19603f3d011682016040523d82523d6000602084013e612e97565b606091505b50806040517f09c253250000000000000000000000000000000000000000000000000000000081526004016109629190613a1e565b505b60006001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016612f0a6080890160608a01613945565b6001600160a01b031603612f1f575084612ff2565b6003546001600160a01b03166241e5be612f3f60808a0160608b01613945565b60405160e083901b7fffffffff000000000000000000000000000000000000000000000000000000001681526001600160a01b039182166004820152602481018a90527f00000000000000000000000000000000000000000000000000000000000000009091166044820152606401602060405180830381865afa158015612fcb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fef9190614784565b90505b6130026080880160608901613945565b6001600160a01b03167f075a2720282fdf622141dae0b048ef90a21a7e57c134c76912d19d006b3b3f6f8260405161303c91815260200190565b60405180910390a27f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff168111156130e3576040517f6a92a483000000000000000000000000000000000000000000000000000000008152600481018290526bffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152604401610962565b604080516101a08101825267ffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001681526001600160a01b038716602082015290810161317461313a8a80614544565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506133f792505050565b6001600160a01b031681526020018a600201601481819054906101000a900467ffffffffffffffff166131a690615102565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905567ffffffffffffffff1681526020018460000151815260200160001515815260200184602001516132b0576040517fea458c0c00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8b1660048201526001600160a01b0388811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063ea458c0c906044016020604051808303816000875af1158015613287573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132ab9190615129565b6132b3565b60005b67ffffffffffffffff1681526020016132d260808a0160608b01613945565b6001600160a01b031681526020018781526020018880602001906132f69190614544565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060200161333d60408a018a614592565b808060200260200160405190810160405280939291908181526020016000905b828210156133895761337a604083028601368190038101906147e2565b8152602001906001019061335d565b505050505081526020018367ffffffffffffffff8111156133ac576133ac613cca565b6040519080825280602002602001820160405280156133df57816020015b60608152602001906001900390816133ca5790505b50815260006020909101529998505050505050505050565b6000815160201461343657816040517f8d666f600000000000000000000000000000000000000000000000000000000081526004016109629190613a1e565b610d218280602001905181019061344d9190614784565b613790565b60008060001b8284602001518560400151866060015187608001518860a001518960c001518a60e001518b61010001516040516020016134e89897969594939291906001600160a01b039889168152968816602088015267ffffffffffffffff95861660408801526060870194909452911515608086015290921660a0840152921660c082015260e08101919091526101000190565b60405160208183030381529060405280519060200120856101200151805190602001208661014001516040516020016135219190615146565b6040516020818303038152906040528051906020012087610160015160405160200161354d9190615159565b60408051601f198184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e00160405160208183030381529060405280519060200120905092915050565b336001600160a01b038216036136055760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610962565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000670de0b6b3a76400006136a1837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8616614661565b610f93919061468b565b6000613700826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166137fd9092919063ffffffff16565b805190915015610ee6578080602001905181019061371e919061481c565b610ee65760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610962565b60006001600160a01b038211806137a8575061040082105b156137f95760408051602081018490520160408051601f19818403018152908290527f8d666f6000000000000000000000000000000000000000000000000000000000825261096291600401613a1e565b5090565b6060611573848460008585600080866001600160a01b03168587604051613824919061516c565b60006040518083038185875af1925050503d8060008114613861576040519150601f19603f3d011682016040523d82523d6000602084013e613866565b606091505b509150915061387787838387613882565b979650505050505050565b606083156138f15782516000036138ea576001600160a01b0385163b6138ea5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610962565b5081611573565b61157383838151156139065781518083602001fd5b8060405162461bcd60e51b81526004016109629190613a1e565b6001600160a01b03811681146110ce57600080fd5b803561394081613920565b919050565b60006020828403121561395757600080fd5b8135610f9381613920565b60c08101610d2182846001600160a01b0380825116835267ffffffffffffffff60208301511660208401526bffffffffffffffffffffffff60408301511660408401528060608301511660608401528060808301511660808401528060a08301511660a0840152505050565b60005b838110156139e95781810151838201526020016139d1565b50506000910152565b60008151808452613a0a8160208601602086016139ce565b601f01601f19169290920160200192915050565b602081526000610f9360208301846139f2565b67ffffffffffffffff811681146110ce57600080fd5b803561394081613a31565b600060a08284031215613a6457600080fd5b50919050565b60008060408385031215613a7d57600080fd5b8235613a8881613a31565b9150602083013567ffffffffffffffff811115613aa457600080fd5b613ab085828601613a52565b9150509250929050565b60008060408385031215613acd57600080fd5b8235613ad881613a31565b91506020830135613ae881613920565b809150509250929050565b600060208284031215613b0557600080fd5b8135610f9381613a31565b8051151582526020810151613b2b602084018261ffff169052565b506040810151613b43604084018263ffffffff169052565b506060810151613b5b606084018263ffffffff169052565b506080810151613b73608084018263ffffffff169052565b5060a0810151613b8960a084018261ffff169052565b5060c0810151613ba160c084018263ffffffff169052565b5060e0810151613bb760e084018261ffff169052565b506101008181015161ffff9081169184019190915261012080830151909116908301526101408082015163ffffffff90811691840191909152610160808301518216908401526101808083015167ffffffffffffffff908116918501919091526101a080840151909116908401526101c080830151909116908301526101e0908101511515910152565b600061026082019050613c55828451613b10565b60208301516001600160a01b0316610200830152604083015167ffffffffffffffff166102208301526060909201516102409091015290565b60808101610d21828480516001600160a01b03908116835260208083015182169084015260408083015182169084015260609182015116910152565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613d1c57613d1c613cca565b60405290565b60405160c0810167ffffffffffffffff81118282101715613d1c57613d1c613cca565b6040516060810167ffffffffffffffff81118282101715613d1c57613d1c613cca565b604051610200810167ffffffffffffffff81118282101715613d1c57613d1c613cca565b604051601f8201601f1916810167ffffffffffffffff81118282101715613db557613db5613cca565b604052919050565b600067ffffffffffffffff821115613dd757613dd7613cca565b5060051b60200190565b803563ffffffff8116811461394057600080fd5b803561ffff8116811461394057600080fd5b80151581146110ce57600080fd5b803561394081613e07565b600082601f830112613e3157600080fd5b81356020613e46613e4183613dbd565b613d8c565b82815260069290921b84018101918181019086841115613e6557600080fd5b8286015b84811015613eb65760408189031215613e825760008081fd5b613e8a613cf9565b8135613e9581613a31565b815281850135613ea481613920565b81860152835291830191604001613e69565b509695505050505050565b60008060408385031215613ed457600080fd5b67ffffffffffffffff83351115613eea57600080fd5b83601f843585010112613efc57600080fd5b613f0c613e418435850135613dbd565b8335840180358083526020808401939260059290921b90910101861015613f3257600080fd5b602085358601015b85358601803560051b016020018110156141055767ffffffffffffffff81351115613f6457600080fd5b6040601f19823588358901018903011215613f7e57600080fd5b613f86613cf9565b613f996020833589358a01010135613a31565b863587018235016020810135825267ffffffffffffffff6040909101351115613fc157600080fd5b86358701823501604081013501603f81018913613fdd57600080fd5b613fed613e416020830135613dbd565b602082810135808352908201919060e00283016040018b101561400f57600080fd5b604083015b604060e06020860135028501018110156140ec5760e0818d03121561403857600080fd5b614040613cf9565b61404a8235613920565b8135815260c0601f19838f0301121561406257600080fd5b61406a613d22565b61407660208401613de1565b815261408460408401613de1565b602082015261409560608401613df5565b60408201526140a660808401613de1565b60608201526140b760a08401613de1565b60808201526140c960c0840135613e07565b60c083013560a0820152602082810191909152908452929092019160e001614014565b5060208481019190915292865250509283019201613f3a565b5092505067ffffffffffffffff6020840135111561412257600080fd5b6141328460208501358501613e20565b90509250929050565b6000602080838503121561414e57600080fd5b823567ffffffffffffffff81111561416557600080fd5b8301601f8101851361417657600080fd5b8035614184613e4182613dbd565b81815261024091820283018401918482019190888411156141a457600080fd5b938501935b8385101561433c57848903818112156141c25760008081fd5b6141ca613d45565b86356141d581613a31565b8152610200601f1983018113156141ec5760008081fd5b6141f4613d68565b9250614201898901613e15565b83526040614210818a01613df5565b8a8501526060614221818b01613de1565b828601526080614232818c01613de1565b8287015260a09150614245828c01613de1565b9086015260c06142568b8201613df5565b8287015260e09150614269828c01613de1565b9086015261010061427b8b8201613df5565b82870152610120915061428f828c01613df5565b908601526101406142a18b8201613df5565b8287015261016091506142b5828c01613de1565b908601526101806142c78b8201613de1565b828701526101a091506142db828c01613a47565b908601526101c06142ed8b8201613a47565b828701526101e09150614301828c01613de1565b908601526143108a8401613e15565b8186015250838a8401526143276102208a01613935565b908301525084525093840193918501916141a9565b50979650505050505050565b6000602080838503121561435b57600080fd5b823567ffffffffffffffff81111561437257600080fd5b8301601f8101851361438357600080fd5b8035614391613e4182613dbd565b81815260069190911b820183019083810190878311156143b057600080fd5b928401925b8284101561387757604084890312156143ce5760008081fd5b6143d6613cf9565b84356143e181613920565b8152848601356143f081613a31565b81870152825260409390930192908401906143b5565b60006080828403121561441857600080fd5b6040516080810181811067ffffffffffffffff8211171561443b5761443b613cca565b604052823561444981613920565b8152602083013561445981613920565b6020820152604083013561446c81613920565b6040820152606083013561447f81613920565b60608201529392505050565b600080600080608085870312156144a157600080fd5b84356144ac81613a31565b9350602085013567ffffffffffffffff8111156144c857600080fd5b6144d487828801613a52565b9350506040850135915060608501356144ec81613920565b939692955090935050565b6020808252825182820181905260009190848201906040850190845b818110156145385783516001600160a01b031683529284019291840191600101614513565b50909695505050505050565b6000808335601e1984360301811261455b57600080fd5b83018035915067ffffffffffffffff82111561457657600080fd5b60200191503681900382131561458b57600080fd5b9250929050565b6000808335601e198436030181126145a957600080fd5b83018035915067ffffffffffffffff8211156145c457600080fd5b6020019150600681901b360382131561458b57600080fd5b80517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461394057600080fd5b6000806040838503121561461b57600080fd5b614624836145dc565b9150614132602084016145dc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610d2157610d21614632565b80820180821115610d2157610d21614632565b6000826146c1577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600060208083850312156146d957600080fd5b825167ffffffffffffffff8111156146f057600080fd5b8301601f8101851361470157600080fd5b805161470f613e4182613dbd565b81815260059190911b8201830190838101908783111561472e57600080fd5b928401925b8284101561387757835161474681613920565b82529284019290840190614733565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561479657600080fd5b5051919050565b6000602082840312156147af57600080fd5b8151610f9381613920565b67ffffffffffffffff8181168382160190808211156147db576147db614632565b5092915050565b6000604082840312156147f457600080fd5b6147fc613cf9565b823561480781613920565b81526020928301359281019290925250919050565b60006020828403121561482e57600080fd5b8151610f9381613e07565b602081526000825160a0602084015261485560c08401826139f2565b905067ffffffffffffffff602085015116604084015260408401516001600160a01b038082166060860152606086015160808601528060808701511660a086015250508091505092915050565b600082601f8301126148b357600080fd5b815167ffffffffffffffff8111156148cd576148cd613cca565b6148e06020601f19601f84011601613d8c565b8181528460208386010111156148f557600080fd5b6115738260208301602087016139ce565b60006020828403121561491857600080fd5b815167ffffffffffffffff8082111561493057600080fd5b908301906040828603121561494457600080fd5b61494c613cf9565b82518281111561495b57600080fd5b614967878286016148a2565b82525060208301518281111561497c57600080fd5b614988878286016148a2565b60208301525095945050505050565b6020815260008251606060208401526149b360808401826139f2565b90506020840151601f19808584030160408601526149d183836139f2565b92506040860151915080858403016060860152506149ef82826139f2565b95945050505050565b60008151808452602080850194506020840160005b83811015614a3d57815180516001600160a01b031688528301518388015260409096019590820190600101614a0d565b509495945050505050565b60008282518085526020808601955060208260051b8401016020860160005b84811015614a9557601f19868403018952614a838383516139f2565b98840198925090830190600101614a67565b5090979650505050505050565b60208152614abd60208201835167ffffffffffffffff169052565b60006020830151614ad960408401826001600160a01b03169052565b5060408301516001600160a01b038116606084015250606083015167ffffffffffffffff8116608084015250608083015160a083015260a0830151614b2260c084018215159052565b5060c083015167ffffffffffffffff811660e08401525060e0830151610100614b55818501836001600160a01b03169052565b840151610120848101919091528401516101a061014080860182905291925090614b836101c08601846139f2565b9250808601519050601f19610160818786030181880152614ba485846149f8565b945080880151925050610180818786030181880152614bc38584614a48565b970151959092019490945250929392505050565b7fffffffff000000000000000000000000000000000000000000000000000000008135818116916004851015614c175780818660040360031b1b83161692505b505092915050565b60008085851115614c2f57600080fd5b83861115614c3c57600080fd5b5050820193919092039150565b600060408284031215614c5b57600080fd5b614c63613cf9565b823581526020830135614c7581613e07565b60208201529392505050565b600060208284031215614c9357600080fd5b5035919050565b63ffffffff8181168382160190808211156147db576147db614632565b600060208284031215614cc957600080fd5b610f93826145dc565b815460ff81161515825261026082019061ffff600882901c8116602085015263ffffffff601883901c81166040860152614d1960608601828560381c1663ffffffff169052565b614d3060808601828560581c1663ffffffff169052565b614d4560a08601838560781c1661ffff169052565b614d5c60c08601828560881c1663ffffffff169052565b614d7160e08601838560a81c1661ffff169052565b614d876101008601838560b81c1661ffff169052565b614d9d6101208601838560c81c1661ffff169052565b614db56101408601828560d81c1663ffffffff169052565b600186015463ffffffff82821616610160870152925067ffffffffffffffff602084901c81166101808701529150614dff6101a08601838560601c1667ffffffffffffffff169052565b614e176101c08601828560a01c1663ffffffff169052565b50614e2d6101e0850160ff8460c01c1615159052565b60028501546001600160a01b0381166102008601529150614e606102208501828460a01c1667ffffffffffffffff169052565b5050600383015461024083015292915050565b6102008101610d218284613b10565b6101408101614eef82856001600160a01b0380825116835267ffffffffffffffff60208301511660208401526bffffffffffffffffffffffff60408301511660408401528060608301511660608401528060808301511660808401528060a08301511660a0840152505050565b82516001600160a01b0390811660c08401526020840151811660e084015260408401518116610100840152606084015116610120830152610f93565b6000808335601e19843603018112614f4257600080fd5b830160208101925035905067ffffffffffffffff811115614f6257600080fd5b80360382131561458b57600080fd5b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b8183526000602080850194508260005b85811015614a3d578135614fbf81613920565b6001600160a01b03168752818301358388015260409687019690910190600101614fac565b600067ffffffffffffffff8085168352604060208401526150058485614f2b565b60a0604086015261501a60e086018284614f71565b91505061502a6020860186614f2b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc080878503016060880152615060848385614f71565b935060408801359250601e1988360301831261507b57600080fd5b6020928801928301923591508482111561509457600080fd5b8160061b36038313156150a657600080fd5b808785030160808801526150bb848385614f9c565b94506150c960608901613935565b6001600160a01b03811660a089015293506150e76080890189614f2b565b94509250808786030160c08801525050613877838383614f71565b600067ffffffffffffffff80831681810361511f5761511f614632565b6001019392505050565b60006020828403121561513b57600080fd5b8151610f9381613a31565b602081526000610f9360208301846149f8565b602081526000610f936020830184614a48565b6000825161517e8184602087016139ce565b919091019291505056fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfigArgs[]\",\"name\":\"destChainConfigArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.PremiumMultiplierWeiPerEthArgs[]\",\"name\":\"premiumMultiplierWeiPerEthArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigSingleTokenArgs[]\",\"name\":\"tokenTransferFeeConfigs\",\"type\":\"tuple[]\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigArgs[]\",\"name\":\"tokenTransferFeeConfigArgs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CannotSendZeroTokens\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"DestinationChainNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExtraArgOutOfOrderExecutionMustBeTrue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GetSupportedTokensFunctionalityRemovedCheckAdminRegistry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"}],\"name\":\"InvalidDestBytesOverhead\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidDestChainConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidEVMAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidExtraArgsTag\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"msgFeeJuels\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint256\"}],\"name\":\"MessageFeeTooHigh\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MessageGasLimitTooHigh\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"maxSize\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actualSize\",\"type\":\"uint256\"}],\"name\":\"MessageTooLarge\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorReason\",\"type\":\"bytes\"}],\"name\":\"MessageValidationError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeCalledByRouter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"NotAFeeToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustSetOriginalSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SourceTokenDataTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedNumberOfTokens\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"UnsupportedToken\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"strict\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structInternal.EVM2EVMMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"CCIPSendRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"metadataHash\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfig\",\"name\":\"destChainConfig\",\"type\":\"tuple\"}],\"name\":\"DestChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"DestChainDynamicConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeValueJuels\",\"type\":\"uint256\"}],\"name\":\"FeePaid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FeeTokenWithdrawn\",\"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\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"name\":\"PremiumMultiplierWeiPerEthUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"TokenTransferFeeConfigDeleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"name\":\"TokenTransferFeeConfigUpdated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfigArgs[]\",\"name\":\"destChainConfigArgs\",\"type\":\"tuple[]\"}],\"name\":\"applyDestChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.PremiumMultiplierWeiPerEthArgs[]\",\"name\":\"premiumMultiplierWeiPerEthArgs\",\"type\":\"tuple[]\"}],\"name\":\"applyPremiumMultiplierWeiPerEthUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigSingleTokenArgs[]\",\"name\":\"tokenTransferFeeConfigs\",\"type\":\"tuple[]\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigArgs[]\",\"name\":\"tokenTransferFeeConfigArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfigRemoveArgs[]\",\"name\":\"tokensToUseDefaultFeeConfigs\",\"type\":\"tuple[]\"}],\"name\":\"applyTokenTransferFeeConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structClient.EVM2AnyMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"}],\"name\":\"forwardFromRouter\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getDestChainConfig\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainDynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"prevOnRamp\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"metadataHash\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDynamicConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getExpectedNextSequenceNumber\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structClient.EVM2AnyMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"getFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"contractIERC20\",\"name\":\"sourceToken\",\"type\":\"address\"}],\"name\":\"getPoolBySourceToken\",\"outputs\":[{\"internalType\":\"contractIPoolV1\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getPremiumMultiplierWeiPerEth\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStaticConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"name\":\"getSupportedTokens\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenTransferFeeConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"setDynamicConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawFeeTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6101406040523480156200001257600080fd5b5060405162006b4538038062006b458339810160408190526200003591620014af565b33806000816200008c5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000bf57620000bf8162000214565b505085516001600160a01b031615905080620000e6575060208501516001600160401b0316155b80620000fd575060608501516001600160a01b0316155b8062000114575060808501516001600160a01b0316155b806200012b575060a08501516001600160a01b0316155b156200014a576040516306b7c75960e31b815260040160405180910390fd5b84516001600160a01b0390811660a090815260208701516001600160401b031660c05260408701516001600160601b031660809081526060880151831660e0528701518216610100528601511661012052620001a684620002bf565b620001b18362000482565b620001bc8262000a00565b60408051600080825260208201909252620002099183919062000202565b6040805180820190915260008082526020820152815260200190600190039081620001da5790505b5062000acc565b5050505050620018f7565b336001600160a01b038216036200026e5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000083565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60208101516001600160a01b03161580620002e5575060608101516001600160a01b0316155b1562000304576040516306b7c75960e31b815260040160405180910390fd5b8051600280546001600160a01b039283166001600160a01b0319918216179091556020808401516003805491851691841691909117905560408085015160048054918616918516919091179055606080860151600580549187169190951617909355805160c0808201835260a080518716835290516001600160401b031693820193909352608080516001600160601b03168284015260e05186169482019490945261010051851693810193909352610120519093169082015290517f4012fe74115805c44a121d8f9edc3d234df8f05f53b52864b8e5e8a30384b8aa916200047791849082516001600160a01b0390811682526020808501516001600160401b0316818401526040808601516001600160601b03168185015260608087015184168186015260808088015185169086015260a0968701518416968501969096528451831660c085015290840151821660e084015283015181166101008301529190920151166101208201526101400190565b60405180910390a150565b60005b8151811015620009fc576000828281518110620004a657620004a6620015f5565b602002602001015190506000838381518110620004c757620004c7620015f5565b6020026020010151600001519050806001600160401b031660001480620004fe5750602082015161018001516001600160401b0316155b15620005295760405163c35aa79d60e01b81526001600160401b038216600482015260240162000083565b600060066000836001600160401b03166001600160401b0316815260200190815260200160002090506000836040015190506000604051806080016040528086602001518152602001836001600160a01b031681526020018460020160149054906101000a90046001600160401b03166001600160401b031681526020018460030154815250905080600001518360000160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548161ffff021916908361ffff16021790555060408201518160000160036101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160076101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600b6101000a81548163ffffffff021916908363ffffffff16021790555060a082015181600001600f6101000a81548161ffff021916908361ffff16021790555060c08201518160000160116101000a81548163ffffffff021916908363ffffffff16021790555060e08201518160000160156101000a81548161ffff021916908361ffff1602179055506101008201518160000160176101000a81548161ffff021916908361ffff1602179055506101208201518160000160196101000a81548161ffff021916908361ffff16021790555061014082015181600001601b6101000a81548163ffffffff021916908363ffffffff1602179055506101608201518160010160006101000a81548163ffffffff021916908363ffffffff1602179055506101808201518160010160046101000a8154816001600160401b0302191690836001600160401b031602179055506101a082015181600101600c6101000a8154816001600160401b0302191690836001600160401b031602179055506101c08201518160010160146101000a81548163ffffffff021916908363ffffffff1602179055506101e08201518160010160186101000a81548160ff02191690831515021790555090505082600301546000801b03620009195760c051604080517f8acd72527118c8324937b1a42e02cd246697c3b633f1742f3cae11de233722b360208201526001600160401b0392831691810191909152908516606082015230608082015260a00160408051601f1981840301815291905280516020909101206060820181905260038401556001600160a01b03821615620008d0576002830180546001600160a01b0319166001600160a01b0384161790555b836001600160401b03167f4be92415590c4554edbd7d3fbf308eeb6e5d00ababade0791f835b4a18416ed6846040516200090b91906200160b565b60405180910390a2620009eb565b60028301546001600160a01b03838116911614620009565760405163c35aa79d60e01b81526001600160401b038516600482015260240162000083565b60208560200151610160015163ffffffff161015620009a357602085015161016001516040516312766e0160e11b81526000600482015263ffffffff909116602482015260440162000083565b836001600160401b03167f1173725509a61aed633c58cd12e31c104cd26ca24f6c270c8ac81c5f6f12e8e98660200151604051620009e29190620017b5565b60405180910390a25b505050505080600101905062000485565b5050565b60005b8151811015620009fc57600082828151811062000a245762000a24620015f5565b6020026020010151600001519050600083838151811062000a495762000a49620015f5565b6020908102919091018101518101516001600160a01b03841660008181526007845260409081902080546001600160401b0319166001600160401b0385169081179091559051908152919350917fbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d910160405180910390a2505060010162000a03565b60005b825181101562000d3957600083828151811062000af05762000af0620015f5565b6020026020010151905060008160000151905060005b82602001515181101562000d2a5760008360200151828151811062000b2f5762000b2f620015f5565b602002602001015160200151905060008460200151838151811062000b585762000b58620015f5565b60200260200101516000015190506020826080015163ffffffff16101562000bb15760808201516040516312766e0160e11b81526001600160a01b038316600482015263ffffffff909116602482015260440162000083565b6001600160401b03841660008181526008602090815260408083206001600160a01b0386168085529083529281902086518154938801518389015160608a015160808b015160a08c01511515600160901b0260ff60901b1963ffffffff928316600160701b021664ffffffffff60701b199383166a01000000000000000000000263ffffffff60501b1961ffff90961668010000000000000000029590951665ffffffffffff60401b19968416640100000000026001600160401b0319909b16939097169290921798909817939093169390931717919091161792909217909155519091907f94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b59062000d17908690600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b60405180910390a3505060010162000b06565b50505080600101905062000acf565b5060005b815181101562000dff57600082828151811062000d5e5762000d5e620015f5565b6020026020010151600001519050600083838151811062000d835762000d83620015f5565b6020908102919091018101518101516001600160401b03841660008181526008845260408082206001600160a01b038516808452955280822080546001600160981b03191690555192945090917f4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b9190a3505060010162000d3d565b505050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b038111828210171562000e3f5762000e3f62000e04565b60405290565b60405161020081016001600160401b038111828210171562000e3f5762000e3f62000e04565b604080519081016001600160401b038111828210171562000e3f5762000e3f62000e04565b60405160c081016001600160401b038111828210171562000e3f5762000e3f62000e04565b604051601f8201601f191681016001600160401b038111828210171562000ee05762000ee062000e04565b604052919050565b80516001600160a01b038116811462000f0057600080fd5b919050565b80516001600160401b038116811462000f0057600080fd5b60006080828403121562000f3057600080fd5b604051608081016001600160401b038111828210171562000f555762000f5562000e04565b60405290508062000f668362000ee8565b815262000f766020840162000ee8565b602082015262000f896040840162000ee8565b604082015262000f9c6060840162000ee8565b60608201525092915050565b60006001600160401b0382111562000fc45762000fc462000e04565b5060051b60200190565b8051801515811462000f0057600080fd5b805161ffff8116811462000f0057600080fd5b805163ffffffff8116811462000f0057600080fd5b600082601f8301126200101957600080fd5b81516020620010326200102c8362000fa8565b62000eb5565b82815261024092830285018201928282019190878511156200105357600080fd5b8387015b85811015620012125780890382811215620010725760008081fd5b6200107c62000e1a565b620010878362000f05565b815261020080601f19840112156200109f5760008081fd5b620010a962000e45565b9250620010b888850162000fce565b83526040620010c981860162000fdf565b898501526060620010dc81870162000ff2565b828601526080620010ef81880162000ff2565b8287015260a091506200110482880162000ff2565b9086015260c06200111787820162000fdf565b8287015260e091506200112c82880162000ff2565b908601526101006200114087820162000fdf565b8287015261012091506200115682880162000fdf565b908601526101406200116a87820162000fdf565b8287015261016091506200118082880162000ff2565b908601526101806200119487820162000ff2565b828701526101a09150620011aa82880162000f05565b908601526101c0620011be87820162000f05565b828701526101e09150620011d482880162000ff2565b90860152620011e586840162000fce565b81860152508389840152620011fe610220860162000ee8565b908301525085525092840192810162001057565b5090979650505050505050565b600082601f8301126200123157600080fd5b81516020620012446200102c8362000fa8565b82815260069290921b840181019181810190868411156200126457600080fd5b8286015b84811015620012ba5760408189031215620012835760008081fd5b6200128d62000e6b565b620012988262000ee8565b8152620012a785830162000f05565b8186015283529183019160400162001268565b509695505050505050565b600082601f830112620012d757600080fd5b81516020620012ea6200102c8362000fa8565b82815260059290921b840181019181810190868411156200130a57600080fd5b8286015b84811015620012ba5780516001600160401b03808211156200132f57600080fd5b908801906040601f19838c0381018213156200134a57600080fd5b6200135462000e6b565b6200136189860162000f05565b815282850151848111156200137557600080fd5b8086019550508c603f8601126200138b57600080fd5b888501519350620013a06200102c8562000fa8565b84815260e09094028501830193898101908e861115620013bf57600080fd5b958401955b858710156200149857868f0360e0811215620013df57600080fd5b620013e962000e6b565b620013f48962000ee8565b815260c086830112156200140757600080fd5b6200141162000e90565b9150620014208d8a0162000ff2565b82526200142f878a0162000ff2565b8d8301526200144160608a0162000fdf565b878301526200145360808a0162000ff2565b60608301526200146660a08a0162000ff2565b60808301526200147960c08a0162000fce565b60a0830152808d0191909152825260e09690960195908a0190620013c4565b828b0152508752505050928401925083016200130e565b60008060008060008587036101a0811215620014ca57600080fd5b60c0811215620014d957600080fd5b50620014e462000e90565b620014ef8762000ee8565b8152620014ff6020880162000f05565b602082015260408701516001600160601b03811681146200151f57600080fd5b6040820152620015326060880162000ee8565b6060820152620015456080880162000ee8565b60808201526200155860a0880162000ee8565b60a082015294506200156e8760c0880162000f1d565b6101408701519094506001600160401b03808211156200158d57600080fd5b6200159b89838a0162001007565b9450610160880151915080821115620015b357600080fd5b620015c189838a016200121f565b9350610180880151915080821115620015d957600080fd5b50620015e888828901620012c5565b9150509295509295909350565b634e487b7160e01b600052603260045260246000fd5b815460ff81161515825261026082019061ffff600882901c8116602085015263ffffffff601883901c811660408601526200165360608601828560381c1663ffffffff169052565b6200166b60808601828560581c1663ffffffff169052565b6200168160a08601838560781c1661ffff169052565b6200169960c08601828560881c1663ffffffff169052565b620016af60e08601838560a81c1661ffff169052565b620016c66101008601838560b81c1661ffff169052565b620016dd6101208601838560c81c1661ffff169052565b620016f66101408601828560d81c1663ffffffff169052565b600186015463ffffffff8282161661016087015292506001600160401b03602084901c811661018087015291506200173f6101a08601838560601c166001600160401b03169052565b620017586101c08601828560a01c1663ffffffff169052565b506200176f6101e0850160ff8460c01c1615159052565b60028501546001600160a01b0381166102008601529150620017a26102208501828460a01c166001600160401b03169052565b5050600383015461024083015292915050565b81511515815261020081016020830151620017d6602084018261ffff169052565b506040830151620017ef604084018263ffffffff169052565b50606083015162001808606084018263ffffffff169052565b50608083015162001821608084018263ffffffff169052565b5060a08301516200183860a084018261ffff169052565b5060c08301516200185160c084018263ffffffff169052565b5060e08301516200186860e084018261ffff169052565b506101008381015161ffff9081169184019190915261012080850151909116908301526101408084015163ffffffff9081169184019190915261016080850151821690840152610180808501516001600160401b03908116918501919091526101a080860151909116908401526101c080850151909116908301526101e0928301511515929091019190915290565b60805160a05160c05160e0516101005161012051615195620019b0600039600081816102da01528181610f260152612adf0152600081816102ab01528181612ab7015261323e01526000818161027c01528181612a8d0152612bf30152600081816102180152818161265001528181612a2801526130f90152600081816101e901528181612a0301528181612eda0152612f8401526000818161024801528181612a5a0152818161304601526130b601526151956000f3fe608060405234801561001057600080fd5b50600436106101515760003560e01c806379ba5097116100cd578063a69c64c011610081578063df0aa9e911610066578063df0aa9e9146108c0578063f2fde38b146108d3578063fbca3b74146108e657600080fd5b8063a69c64c01461089a578063a6f3ab6c146108ad57600080fd5b80638da5cb5b116100b25780638da5cb5b146108635780639041be3d14610874578063932e53c81461088757600080fd5b806379ba5097146106f857806382b49eb01461070057600080fd5b80633a019940116101245780636def4ce7116101095780636def4ce7146103b65780637437ff9f14610672578063770e2dc4146106e557600080fd5b80633a0199401461038157806348a98aa41461038b57600080fd5b8063061877e31461015657806306285c69146101a7578063181f5a771461031757806320487ded14610360575b600080fd5b610189610164366004613945565b6001600160a01b031660009081526007602052604090205467ffffffffffffffff1690565b60405167ffffffffffffffff90911681526020015b60405180910390f35b61030a6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091526040518060c001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250905090565b60405161019e9190613962565b6103536040518060400160405280601c81526020017f45564d3245564d4d756c74694f6e52616d7020312e362e302d6465760000000081525081565b60405161019e9190613a1e565b61037361036e366004613a6a565b610906565b60405190815260200161019e565b610389610d27565b005b61039e610399366004613aba565b610eeb565b6040516001600160a01b03909116815260200161019e565b6106656103c4366004613af3565b604080516102808101825260006080820181815260a0830182905260c0830182905260e08301829052610100830182905261012083018290526101408301829052610160830182905261018083018290526101a083018290526101c083018290526101e08301829052610200830182905261022083018290526102408301829052610260830182905282526020820181905291810182905260608101919091525067ffffffffffffffff908116600090815260066020908152604091829020825161028081018452815460ff80821615156080840190815261ffff610100808504821660a087015263ffffffff63010000008604811660c08801526701000000000000008604811660e08801526b01000000000000000000000086048116918701919091526f01000000000000000000000000000000850482166101208701527101000000000000000000000000000000000085048116610140870152750100000000000000000000000000000000000000000085048216610160870152770100000000000000000000000000000000000000000000008504821661018087015279010000000000000000000000000000000000000000000000000085049091166101a08601527b0100000000000000000000000000000000000000000000000000000090930483166101c085015260018501548084166101e0860152640100000000810489166102008601526c010000000000000000000000008104891661022086015274010000000000000000000000000000000000000000808204909416610240860152780100000000000000000000000000000000000000000000000090049091161515610260840152825260028301546001600160a01b0381169483019490945290920490931691810191909152600390910154606082015290565b60405161019e9190613c41565b6106d860408051608081018252600080825260208201819052918101829052606081019190915250604080516080810182526002546001600160a01b03908116825260035481166020830152600454811692820192909252600554909116606082015290565b60405161019e9190613c8e565b6103896106f3366004613ec1565b610f9a565b610389610fb0565b61080361070e366004613aba565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9190911660009081526008602090815260408083206001600160a01b0394909416835292815290829020825160c081018452905463ffffffff8082168352640100000000820481169383019390935268010000000000000000810461ffff16938201939093526a01000000000000000000008304821660608201526e01000000000000000000000000000083049091166080820152720100000000000000000000000000000000000090910460ff16151560a082015290565b60405161019e9190600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b6000546001600160a01b031661039e565b610189610882366004613af3565b611079565b61038961089536600461413b565b6110bd565b6103896108a8366004614348565b6110d1565b6103896108bb366004614406565b6110e2565b6103736108ce36600461448b565b6110f3565b6103896108e1366004613945565b61157b565b6108f96108f4366004613af3565b61158c565b60405161019e91906144f7565b67ffffffffffffffff82166000908152600660205260408120805460ff1661096b576040517f99ac52f200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff851660048201526024015b60405180910390fd5b600061099961097d6080860186614544565b6001850154640100000000900467ffffffffffffffff166115c0565b90506109ca856109ac6020870187614544565b84519091506109be6040890189614592565b9050856020015161172d565b60006007816109df6080880160608901613945565b6001600160a01b03168152602081019190915260400160009081205467ffffffffffffffff169150819003610a5c57610a1e6080860160608701613945565b6040517fa7499d200000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610962565b60035460009081906001600160a01b031663ffdb4b37610a8260808a0160608b01613945565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b03909116600482015267ffffffffffffffff8b1660248201526044016040805180830381865afa158015610aed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b119190614608565b90925090506000808080610b2860408c018c614592565b90501115610b6357610b578b610b4460808d0160608e01613945565b87610b5260408f018f614592565b6118a3565b91945092509050610b9a565b6001880154610b979074010000000000000000000000000000000000000000900463ffffffff16662386f26fc10000614661565b92505b875460009077010000000000000000000000000000000000000000000000900461ffff1615610c0657610c038c6dffffffffffffffffffffffffffff607088901c16610be960208f018f614544565b90508e8060400190610bfb9190614592565b905086611c94565b90505b600089600101600c9054906101000a900467ffffffffffffffff1667ffffffffffffffff168463ffffffff168b600001600f9054906101000a900461ffff1661ffff168e8060200190610c599190614544565b610c64929150614661565b8c548c51610c87916b010000000000000000000000900463ffffffff1690614678565b610c919190614678565b610c9b9190614678565b610cb5906dffffffffffffffffffffffffffff8916614661565b610cbf9190614661565b90507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff87168282610cf667ffffffffffffffff8c1689614661565b610d009190614678565b610d0a9190614678565b610d14919061468b565b9a50505050505050505050505b92915050565b600354604080517fcdc73d5100000000000000000000000000000000000000000000000000000000815290516000926001600160a01b03169163cdc73d5191600480830192869291908290030181865afa158015610d89573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610db191908101906146c6565b6005549091506001600160a01b031660005b8251811015610ee6576000838281518110610de057610de0614755565b60209081029190910101516040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610e4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e729190614784565b90508015610edc57610e8e6001600160a01b0383168583611d9a565b816001600160a01b0316846001600160a01b03167f508d7d183612c18fc339b42618912b9fa3239f631dd7ec0671f950200a0fa66e83604051610ed391815260200190565b60405180910390a35b5050600101610dc3565b505050565b6040517fbbe4f6db0000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063bbe4f6db90602401602060405180830381865afa158015610f6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f93919061479d565b9392505050565b610fa2611e1a565b610fac8282611e76565b5050565b6001546001600160a01b0316331461100a5760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610962565b60008054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b67ffffffffffffffff8082166000908152600660205260408120600201549091610d21917401000000000000000000000000000000000000000090041660016147ba565b6110c5611e1a565b6110ce81612256565b50565b6110d9611e1a565b6110ce81612849565b6110ea611e1a565b6110ce81612926565b67ffffffffffffffff841660009081526006602052604081208161111a8288888888612b3f565b905060005b8161014001515181101561151157600061113c6040890189614592565b8381811061114c5761114c614755565b90506040020180360381019061116291906147e2565b905080602001516000036111a2576040517f5cf0444900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006111b28a8360000151610eeb565b90506001600160a01b038116158061126857506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527faff2afbf0000000000000000000000000000000000000000000000000000000060048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa158015611242573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611266919061481c565b155b156112ad5781516040517fbf16aab60000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610962565b6000816001600160a01b0316639a4575b96040518060a001604052808d80600001906112d99190614544565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525067ffffffffffffffff8f166020808301919091526001600160a01b03808e16604080850191909152918901516060840152885116608090920191909152517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526113849190600401614839565b6000604051808303816000875af11580156113a3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113cb9190810190614906565b9050602081602001515111801561142c575067ffffffffffffffff8b16600090815260086020908152604080832086516001600160a01b0316845282529091205490820151516e01000000000000000000000000000090910463ffffffff16105b156114715782516040517f36f536ca0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610962565b805161147c906133f7565b5060408051606081019091526001600160a01b03831660808201528060a0810160405160208183030381529060405281526020018260000151815260200182602001518152506040516020016114d29190614997565b60405160208183030381529060405285610160015185815181106114f8576114f8614755565b602002602001018190525050505080600101905061111f565b50611520818360030154613452565b61018082015260405167ffffffffffffffff8816907fc79f9c3e610deac14de4e704195fe17eab0983ee9916866bc04d16a00f54daa690611562908490614aa2565b60405180910390a261018001519150505b949350505050565b611583611e1a565b6110ce816135ad565b60606040517f9e7177c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805180820190915260008082526020820152600083900361160157506040805180820190915267ffffffffffffffff8216815260006020820152610f93565b600061160d8486614bd7565b90507fe7e230f0000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000082160161167a576116658460048188614c1f565b8101906116729190614c49565b915050610f93565b7f6859a837000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008216016116fb5760408051808201909152806116db866004818a614c1f565b8101906116e89190614c81565b815260006020909101529150610f939050565b6040517f5247fdce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8516600090815260066020526040902080546301000000900463ffffffff168511156117a65780546040517f86933789000000000000000000000000000000000000000000000000000000008152630100000090910463ffffffff16600482015260248101869052604401610962565b8054670100000000000000900463ffffffff168411156117f2576040517f4c4fc93a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054610100900461ffff16831115611836576040517f4c056b6a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018101547801000000000000000000000000000000000000000000000000900460ff168015611864575081155b1561189b576040517fee433e9900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b6000808083815b81811015611c875760008787838181106118c6576118c6614755565b9050604002018036038101906118dc91906147e2565b905060006001600160a01b03166118f78c8360000151610eeb565b6001600160a01b0316036119455780516040517fbf16aab60000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610962565b67ffffffffffffffff8b16600090815260086020908152604080832084516001600160a01b03168452825291829020825160c081018452905463ffffffff8082168352640100000000820481169383019390935268010000000000000000810461ffff16938201939093526a01000000000000000000008304821660608201526e01000000000000000000000000000083049091166080820152720100000000000000000000000000000000000090910460ff16151560a08201819052611ab35767ffffffffffffffff8c1660009081526006602052604090208054611a5390790100000000000000000000000000000000000000000000000000900461ffff16662386f26fc10000614661565b611a5d9089614678565b8154909850611a91907b01000000000000000000000000000000000000000000000000000000900463ffffffff1688614c9a565b6001820154909750611aa99063ffffffff1687614c9a565b9550505050611c7f565b604081015160009061ffff1615611bcf5760008c6001600160a01b031684600001516001600160a01b031614611b725760035484516040517f4ab35b0b0000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820152911690634ab35b0b90602401602060405180830381865afa158015611b47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b6b9190614cb7565b9050611b75565b508a5b620186a0836040015161ffff16611bb78660200151847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1661366e90919063ffffffff16565b611bc19190614661565b611bcb919061468b565b9150505b6060820151611bde9088614c9a565b9650816080015186611bf09190614c9a565b8251909650600090611c0f9063ffffffff16662386f26fc10000614661565b905080821015611c2e57611c23818a614678565b985050505050611c7f565b6000836020015163ffffffff16662386f26fc10000611c4d9190614661565b905080831115611c6d57611c61818b614678565b99505050505050611c7f565b611c77838b614678565b995050505050505b6001016118aa565b5050955095509592505050565b60008063ffffffff8316611ca9608086614661565b611cb587610220614678565b611cbf9190614678565b611cc99190614678565b67ffffffffffffffff8816600090815260066020526040812080549293509171010000000000000000000000000000000000810463ffffffff1690611d2b907501000000000000000000000000000000000000000000900461ffff1685614661565b611d359190614678565b825490915077010000000000000000000000000000000000000000000000900461ffff16611d736dffffffffffffffffffffffffffff8a1683614661565b611d7d9190614661565b611d8d90655af3107a4000614661565b9998505050505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052610ee69084906136ab565b6000546001600160a01b03163314611e745760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610962565b565b60005b825181101561217f576000838281518110611e9657611e96614755565b6020026020010151905060008160000151905060005b82602001515181101561217157600083602001518281518110611ed157611ed1614755565b6020026020010151602001519050600084602001518381518110611ef757611ef7614755565b60200260200101516000015190506020826080015163ffffffff161015611f675760808201516040517f24ecdc020000000000000000000000000000000000000000000000000000000081526001600160a01b038316600482015263ffffffff9091166024820152604401610962565b67ffffffffffffffff841660008181526008602090815260408083206001600160a01b0386168085529083529281902086518154938801518389015160608a015160808b015160a08c015115157201000000000000000000000000000000000000027fffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffff63ffffffff9283166e01000000000000000000000000000002167fffffffffffffffffffffffffff0000000000ffffffffffffffffffffffffffff9383166a0100000000000000000000027fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff61ffff9096166801000000000000000002959095167fffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffff968416640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b16939097169290921798909817939093169390931717919091161792909217909155519091907f94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b59061215f908690600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b60405180910390a35050600101611eac565b505050806001019050611e79565b5060005b8151811015610ee65760008282815181106121a0576121a0614755565b602002602001015160000151905060008383815181106121c2576121c2614755565b60209081029190910181015181015167ffffffffffffffff841660008181526008845260408082206001600160a01b038516808452955280822080547fffffffffffffffffffffffffff000000000000000000000000000000000000001690555192945090917f4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b9190a35050600101612183565b60005b8151811015610fac57600082828151811061227657612276614755565b60200260200101519050600083838151811061229457612294614755565b60200260200101516000015190508067ffffffffffffffff16600014806122cc57506020820151610180015167ffffffffffffffff16155b1561230f576040517fc35aa79d00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff82166004820152602401610962565b6000600660008367ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002090506000836040015190506000604051806080016040528086602001518152602001836001600160a01b031681526020018460020160149054906101000a900467ffffffffffffffff1667ffffffffffffffff1681526020018460030154815250905080600001518360000160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548161ffff021916908361ffff16021790555060408201518160000160036101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160076101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600b6101000a81548163ffffffff021916908363ffffffff16021790555060a082015181600001600f6101000a81548161ffff021916908361ffff16021790555060c08201518160000160116101000a81548163ffffffff021916908363ffffffff16021790555060e08201518160000160156101000a81548161ffff021916908361ffff1602179055506101008201518160000160176101000a81548161ffff021916908361ffff1602179055506101208201518160000160196101000a81548161ffff021916908361ffff16021790555061014082015181600001601b6101000a81548163ffffffff021916908363ffffffff1602179055506101608201518160010160006101000a81548163ffffffff021916908363ffffffff1602179055506101808201518160010160046101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506101a082015181600101600c6101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506101c08201518160010160146101000a81548163ffffffff021916908363ffffffff1602179055506101e08201518160010160186101000a81548160ff02191690831515021790555090505082600301546000801b0361273957604080517f8acd72527118c8324937b1a42e02cd246697c3b633f1742f3cae11de233722b3602082015267ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811692820192909252908516606082015230608082015260a00160408051601f1981840301815291905280516020909101206060820181905260038401556001600160a01b038216156126f2576002830180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384161790555b8367ffffffffffffffff167f4be92415590c4554edbd7d3fbf308eeb6e5d00ababade0791f835b4a18416ed68460405161272c9190614cd2565b60405180910390a2612839565b60028301546001600160a01b0383811691161461278e576040517fc35aa79d00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff85166004820152602401610962565b60208560200151610160015163ffffffff1610156127f257602085015161016001516040517f24ecdc020000000000000000000000000000000000000000000000000000000081526000600482015263ffffffff9091166024820152604401610962565b8367ffffffffffffffff167f1173725509a61aed633c58cd12e31c104cd26ca24f6c270c8ac81c5f6f12e8e986602001516040516128309190614e73565b60405180910390a25b5050505050806001019050612259565b60005b8151811015610fac57600082828151811061286957612869614755565b6020026020010151600001519050600083838151811061288b5761288b614755565b6020908102919091018101518101516001600160a01b03841660008181526007845260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff85169081179091559051908152919350917fbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d910160405180910390a2505060010161284c565b60208101516001600160a01b0316158061294b575060608101516001600160a01b0316155b15612982576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600280547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b03938416179091556020808401516003805484169185169190911790556040808501516004805485169186169190911790556060808601516005805490951690861617909355805160c0810182527f0000000000000000000000000000000000000000000000000000000000000000851681527f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff16928101929092527f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16828201527f00000000000000000000000000000000000000000000000000000000000000008416928201929092527f0000000000000000000000000000000000000000000000000000000000000000831660808201527f000000000000000000000000000000000000000000000000000000000000000090921660a0830152517f4012fe74115805c44a121d8f9edc3d234df8f05f53b52864b8e5e8a30384b8aa91612b34918490614e82565b60405180910390a150565b604080516101a08101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e082018390526101008201839052610120820181905261014082018190526101608201526101808101919091526040517f2cbc26bb000000000000000000000000000000000000000000000000000000008152608086901b77ffffffffffffffff000000000000000000000000000000001660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632cbc26bb90602401602060405180830381865afa158015612c42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c66919061481c565b15612ca9576040517ffdbd6a7200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff86166004820152602401610962565b6001600160a01b038216612ce9576040517fa4ec747900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002546001600160a01b03163314612d2d576040517f1c0a352900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855460ff16612d74576040517f99ac52f200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff86166004820152602401610962565b6000612da2612d866080870187614544565b60018a0154640100000000900467ffffffffffffffff166115c0565b90506000612db36040870187614592565b9150612dda905087612dc86020890189614544565b9050846000015184866020015161172d565b8015612ece576004546001600160a01b03168015612ecc576040517fe0a0e5060000000000000000000000000000000000000000000000000000000081526001600160a01b0382169063e0a0e50690612e39908b908b90600401614fe4565b600060405180830381600087803b158015612e5357600080fd5b505af1925050508015612e64575060015b612ecc573d808015612e92576040519150601f19603f3d011682016040523d82523d6000602084013e612e97565b606091505b50806040517f09c253250000000000000000000000000000000000000000000000000000000081526004016109629190613a1e565b505b60006001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016612f0a6080890160608a01613945565b6001600160a01b031603612f1f575084612ff2565b6003546001600160a01b03166241e5be612f3f60808a0160608b01613945565b60405160e083901b7fffffffff000000000000000000000000000000000000000000000000000000001681526001600160a01b039182166004820152602481018a90527f00000000000000000000000000000000000000000000000000000000000000009091166044820152606401602060405180830381865afa158015612fcb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fef9190614784565b90505b6130026080880160608901613945565b6001600160a01b03167f075a2720282fdf622141dae0b048ef90a21a7e57c134c76912d19d006b3b3f6f8260405161303c91815260200190565b60405180910390a27f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff168111156130e3576040517f6a92a483000000000000000000000000000000000000000000000000000000008152600481018290526bffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152604401610962565b604080516101a08101825267ffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001681526001600160a01b038716602082015290810161317461313a8a80614544565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506133f792505050565b6001600160a01b031681526020018a600201601481819054906101000a900467ffffffffffffffff166131a690615102565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905567ffffffffffffffff1681526020018460000151815260200160001515815260200184602001516132b0576040517fea458c0c00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8b1660048201526001600160a01b0388811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063ea458c0c906044016020604051808303816000875af1158015613287573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132ab9190615129565b6132b3565b60005b67ffffffffffffffff1681526020016132d260808a0160608b01613945565b6001600160a01b031681526020018781526020018880602001906132f69190614544565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060200161333d60408a018a614592565b808060200260200160405190810160405280939291908181526020016000905b828210156133895761337a604083028601368190038101906147e2565b8152602001906001019061335d565b505050505081526020018367ffffffffffffffff8111156133ac576133ac613cca565b6040519080825280602002602001820160405280156133df57816020015b60608152602001906001900390816133ca5790505b50815260006020909101529998505050505050505050565b6000815160201461343657816040517f8d666f600000000000000000000000000000000000000000000000000000000081526004016109629190613a1e565b610d218280602001905181019061344d9190614784565b613790565b60008060001b8284602001518560400151866060015187608001518860a001518960c001518a60e001518b61010001516040516020016134e89897969594939291906001600160a01b039889168152968816602088015267ffffffffffffffff95861660408801526060870194909452911515608086015290921660a0840152921660c082015260e08101919091526101000190565b60405160208183030381529060405280519060200120856101200151805190602001208661014001516040516020016135219190615146565b6040516020818303038152906040528051906020012087610160015160405160200161354d9190615159565b60408051601f198184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e00160405160208183030381529060405280519060200120905092915050565b336001600160a01b038216036136055760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610962565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000670de0b6b3a76400006136a1837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8616614661565b610f93919061468b565b6000613700826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166137fd9092919063ffffffff16565b805190915015610ee6578080602001905181019061371e919061481c565b610ee65760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610962565b60006001600160a01b038211806137a8575061040082105b156137f95760408051602081018490520160408051601f19818403018152908290527f8d666f6000000000000000000000000000000000000000000000000000000000825261096291600401613a1e565b5090565b6060611573848460008585600080866001600160a01b03168587604051613824919061516c565b60006040518083038185875af1925050503d8060008114613861576040519150601f19603f3d011682016040523d82523d6000602084013e613866565b606091505b509150915061387787838387613882565b979650505050505050565b606083156138f15782516000036138ea576001600160a01b0385163b6138ea5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610962565b5081611573565b61157383838151156139065781518083602001fd5b8060405162461bcd60e51b81526004016109629190613a1e565b6001600160a01b03811681146110ce57600080fd5b803561394081613920565b919050565b60006020828403121561395757600080fd5b8135610f9381613920565b60c08101610d2182846001600160a01b0380825116835267ffffffffffffffff60208301511660208401526bffffffffffffffffffffffff60408301511660408401528060608301511660608401528060808301511660808401528060a08301511660a0840152505050565b60005b838110156139e95781810151838201526020016139d1565b50506000910152565b60008151808452613a0a8160208601602086016139ce565b601f01601f19169290920160200192915050565b602081526000610f9360208301846139f2565b67ffffffffffffffff811681146110ce57600080fd5b803561394081613a31565b600060a08284031215613a6457600080fd5b50919050565b60008060408385031215613a7d57600080fd5b8235613a8881613a31565b9150602083013567ffffffffffffffff811115613aa457600080fd5b613ab085828601613a52565b9150509250929050565b60008060408385031215613acd57600080fd5b8235613ad881613a31565b91506020830135613ae881613920565b809150509250929050565b600060208284031215613b0557600080fd5b8135610f9381613a31565b8051151582526020810151613b2b602084018261ffff169052565b506040810151613b43604084018263ffffffff169052565b506060810151613b5b606084018263ffffffff169052565b506080810151613b73608084018263ffffffff169052565b5060a0810151613b8960a084018261ffff169052565b5060c0810151613ba160c084018263ffffffff169052565b5060e0810151613bb760e084018261ffff169052565b506101008181015161ffff9081169184019190915261012080830151909116908301526101408082015163ffffffff90811691840191909152610160808301518216908401526101808083015167ffffffffffffffff908116918501919091526101a080840151909116908401526101c080830151909116908301526101e0908101511515910152565b600061026082019050613c55828451613b10565b60208301516001600160a01b0316610200830152604083015167ffffffffffffffff166102208301526060909201516102409091015290565b60808101610d21828480516001600160a01b03908116835260208083015182169084015260408083015182169084015260609182015116910152565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613d1c57613d1c613cca565b60405290565b60405160c0810167ffffffffffffffff81118282101715613d1c57613d1c613cca565b6040516060810167ffffffffffffffff81118282101715613d1c57613d1c613cca565b604051610200810167ffffffffffffffff81118282101715613d1c57613d1c613cca565b604051601f8201601f1916810167ffffffffffffffff81118282101715613db557613db5613cca565b604052919050565b600067ffffffffffffffff821115613dd757613dd7613cca565b5060051b60200190565b803563ffffffff8116811461394057600080fd5b803561ffff8116811461394057600080fd5b80151581146110ce57600080fd5b803561394081613e07565b600082601f830112613e3157600080fd5b81356020613e46613e4183613dbd565b613d8c565b82815260069290921b84018101918181019086841115613e6557600080fd5b8286015b84811015613eb65760408189031215613e825760008081fd5b613e8a613cf9565b8135613e9581613a31565b815281850135613ea481613920565b81860152835291830191604001613e69565b509695505050505050565b60008060408385031215613ed457600080fd5b67ffffffffffffffff83351115613eea57600080fd5b83601f843585010112613efc57600080fd5b613f0c613e418435850135613dbd565b8335840180358083526020808401939260059290921b90910101861015613f3257600080fd5b602085358601015b85358601803560051b016020018110156141055767ffffffffffffffff81351115613f6457600080fd5b6040601f19823588358901018903011215613f7e57600080fd5b613f86613cf9565b613f996020833589358a01010135613a31565b863587018235016020810135825267ffffffffffffffff6040909101351115613fc157600080fd5b86358701823501604081013501603f81018913613fdd57600080fd5b613fed613e416020830135613dbd565b602082810135808352908201919060e00283016040018b101561400f57600080fd5b604083015b604060e06020860135028501018110156140ec5760e0818d03121561403857600080fd5b614040613cf9565b61404a8235613920565b8135815260c0601f19838f0301121561406257600080fd5b61406a613d22565b61407660208401613de1565b815261408460408401613de1565b602082015261409560608401613df5565b60408201526140a660808401613de1565b60608201526140b760a08401613de1565b60808201526140c960c0840135613e07565b60c083013560a0820152602082810191909152908452929092019160e001614014565b5060208481019190915292865250509283019201613f3a565b5092505067ffffffffffffffff6020840135111561412257600080fd5b6141328460208501358501613e20565b90509250929050565b6000602080838503121561414e57600080fd5b823567ffffffffffffffff81111561416557600080fd5b8301601f8101851361417657600080fd5b8035614184613e4182613dbd565b81815261024091820283018401918482019190888411156141a457600080fd5b938501935b8385101561433c57848903818112156141c25760008081fd5b6141ca613d45565b86356141d581613a31565b8152610200601f1983018113156141ec5760008081fd5b6141f4613d68565b9250614201898901613e15565b83526040614210818a01613df5565b8a8501526060614221818b01613de1565b828601526080614232818c01613de1565b8287015260a09150614245828c01613de1565b9086015260c06142568b8201613df5565b8287015260e09150614269828c01613de1565b9086015261010061427b8b8201613df5565b82870152610120915061428f828c01613df5565b908601526101406142a18b8201613df5565b8287015261016091506142b5828c01613de1565b908601526101806142c78b8201613de1565b828701526101a091506142db828c01613a47565b908601526101c06142ed8b8201613a47565b828701526101e09150614301828c01613de1565b908601526143108a8401613e15565b8186015250838a8401526143276102208a01613935565b908301525084525093840193918501916141a9565b50979650505050505050565b6000602080838503121561435b57600080fd5b823567ffffffffffffffff81111561437257600080fd5b8301601f8101851361438357600080fd5b8035614391613e4182613dbd565b81815260069190911b820183019083810190878311156143b057600080fd5b928401925b8284101561387757604084890312156143ce5760008081fd5b6143d6613cf9565b84356143e181613920565b8152848601356143f081613a31565b81870152825260409390930192908401906143b5565b60006080828403121561441857600080fd5b6040516080810181811067ffffffffffffffff8211171561443b5761443b613cca565b604052823561444981613920565b8152602083013561445981613920565b6020820152604083013561446c81613920565b6040820152606083013561447f81613920565b60608201529392505050565b600080600080608085870312156144a157600080fd5b84356144ac81613a31565b9350602085013567ffffffffffffffff8111156144c857600080fd5b6144d487828801613a52565b9350506040850135915060608501356144ec81613920565b939692955090935050565b6020808252825182820181905260009190848201906040850190845b818110156145385783516001600160a01b031683529284019291840191600101614513565b50909695505050505050565b6000808335601e1984360301811261455b57600080fd5b83018035915067ffffffffffffffff82111561457657600080fd5b60200191503681900382131561458b57600080fd5b9250929050565b6000808335601e198436030181126145a957600080fd5b83018035915067ffffffffffffffff8211156145c457600080fd5b6020019150600681901b360382131561458b57600080fd5b80517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461394057600080fd5b6000806040838503121561461b57600080fd5b614624836145dc565b9150614132602084016145dc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610d2157610d21614632565b80820180821115610d2157610d21614632565b6000826146c1577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600060208083850312156146d957600080fd5b825167ffffffffffffffff8111156146f057600080fd5b8301601f8101851361470157600080fd5b805161470f613e4182613dbd565b81815260059190911b8201830190838101908783111561472e57600080fd5b928401925b8284101561387757835161474681613920565b82529284019290840190614733565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561479657600080fd5b5051919050565b6000602082840312156147af57600080fd5b8151610f9381613920565b67ffffffffffffffff8181168382160190808211156147db576147db614632565b5092915050565b6000604082840312156147f457600080fd5b6147fc613cf9565b823561480781613920565b81526020928301359281019290925250919050565b60006020828403121561482e57600080fd5b8151610f9381613e07565b602081526000825160a0602084015261485560c08401826139f2565b905067ffffffffffffffff602085015116604084015260408401516001600160a01b038082166060860152606086015160808601528060808701511660a086015250508091505092915050565b600082601f8301126148b357600080fd5b815167ffffffffffffffff8111156148cd576148cd613cca565b6148e06020601f19601f84011601613d8c565b8181528460208386010111156148f557600080fd5b6115738260208301602087016139ce565b60006020828403121561491857600080fd5b815167ffffffffffffffff8082111561493057600080fd5b908301906040828603121561494457600080fd5b61494c613cf9565b82518281111561495b57600080fd5b614967878286016148a2565b82525060208301518281111561497c57600080fd5b614988878286016148a2565b60208301525095945050505050565b6020815260008251606060208401526149b360808401826139f2565b90506020840151601f19808584030160408601526149d183836139f2565b92506040860151915080858403016060860152506149ef82826139f2565b95945050505050565b60008151808452602080850194506020840160005b83811015614a3d57815180516001600160a01b031688528301518388015260409096019590820190600101614a0d565b509495945050505050565b60008282518085526020808601955060208260051b8401016020860160005b84811015614a9557601f19868403018952614a838383516139f2565b98840198925090830190600101614a67565b5090979650505050505050565b60208152614abd60208201835167ffffffffffffffff169052565b60006020830151614ad960408401826001600160a01b03169052565b5060408301516001600160a01b038116606084015250606083015167ffffffffffffffff8116608084015250608083015160a083015260a0830151614b2260c084018215159052565b5060c083015167ffffffffffffffff811660e08401525060e0830151610100614b55818501836001600160a01b03169052565b840151610120848101919091528401516101a061014080860182905291925090614b836101c08601846139f2565b9250808601519050601f19610160818786030181880152614ba485846149f8565b945080880151925050610180818786030181880152614bc38584614a48565b970151959092019490945250929392505050565b7fffffffff000000000000000000000000000000000000000000000000000000008135818116916004851015614c175780818660040360031b1b83161692505b505092915050565b60008085851115614c2f57600080fd5b83861115614c3c57600080fd5b5050820193919092039150565b600060408284031215614c5b57600080fd5b614c63613cf9565b823581526020830135614c7581613e07565b60208201529392505050565b600060208284031215614c9357600080fd5b5035919050565b63ffffffff8181168382160190808211156147db576147db614632565b600060208284031215614cc957600080fd5b610f93826145dc565b815460ff81161515825261026082019061ffff600882901c8116602085015263ffffffff601883901c81166040860152614d1960608601828560381c1663ffffffff169052565b614d3060808601828560581c1663ffffffff169052565b614d4560a08601838560781c1661ffff169052565b614d5c60c08601828560881c1663ffffffff169052565b614d7160e08601838560a81c1661ffff169052565b614d876101008601838560b81c1661ffff169052565b614d9d6101208601838560c81c1661ffff169052565b614db56101408601828560d81c1663ffffffff169052565b600186015463ffffffff82821616610160870152925067ffffffffffffffff602084901c81166101808701529150614dff6101a08601838560601c1667ffffffffffffffff169052565b614e176101c08601828560a01c1663ffffffff169052565b50614e2d6101e0850160ff8460c01c1615159052565b60028501546001600160a01b0381166102008601529150614e606102208501828460a01c1667ffffffffffffffff169052565b5050600383015461024083015292915050565b6102008101610d218284613b10565b6101408101614eef82856001600160a01b0380825116835267ffffffffffffffff60208301511660208401526bffffffffffffffffffffffff60408301511660408401528060608301511660608401528060808301511660808401528060a08301511660a0840152505050565b82516001600160a01b0390811660c08401526020840151811660e084015260408401518116610100840152606084015116610120830152610f93565b6000808335601e19843603018112614f4257600080fd5b830160208101925035905067ffffffffffffffff811115614f6257600080fd5b80360382131561458b57600080fd5b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b8183526000602080850194508260005b85811015614a3d578135614fbf81613920565b6001600160a01b03168752818301358388015260409687019690910190600101614fac565b600067ffffffffffffffff8085168352604060208401526150058485614f2b565b60a0604086015261501a60e086018284614f71565b91505061502a6020860186614f2b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc080878503016060880152615060848385614f71565b935060408801359250601e1988360301831261507b57600080fd5b6020928801928301923591508482111561509457600080fd5b8160061b36038313156150a657600080fd5b808785030160808801526150bb848385614f9c565b94506150c960608901613935565b6001600160a01b03811660a089015293506150e76080890189614f2b565b94509250808786030160c08801525050613877838383614f71565b600067ffffffffffffffff80831681810361511f5761511f614632565b6001019392505050565b60006020828403121561513b57600080fd5b8151610f9381613a31565b602081526000610f9360208301846149f8565b602081526000610f936020830184614a48565b6000825161517e8184602087016139ce565b919091019291505056fea164736f6c6343000818000a", } var EVM2EVMMultiOnRampABI = EVM2EVMMultiOnRampMetaData.ABI @@ -1960,12 +1960,12 @@ func (it *EVM2EVMMultiOnRampTokenTransferFeeConfigDeletedIterator) Close() error } type EVM2EVMMultiOnRampTokenTransferFeeConfigDeleted struct { - DestChainSelector *big.Int + DestChainSelector uint64 Token common.Address Raw types.Log } -func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) FilterTokenTransferFeeConfigDeleted(opts *bind.FilterOpts, destChainSelector []*big.Int, token []common.Address) (*EVM2EVMMultiOnRampTokenTransferFeeConfigDeletedIterator, error) { +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) FilterTokenTransferFeeConfigDeleted(opts *bind.FilterOpts, destChainSelector []uint64, token []common.Address) (*EVM2EVMMultiOnRampTokenTransferFeeConfigDeletedIterator, error) { var destChainSelectorRule []interface{} for _, destChainSelectorItem := range destChainSelector { @@ -1983,7 +1983,7 @@ func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) FilterTokenTransferFeeCon return &EVM2EVMMultiOnRampTokenTransferFeeConfigDeletedIterator{contract: _EVM2EVMMultiOnRamp.contract, event: "TokenTransferFeeConfigDeleted", logs: logs, sub: sub}, nil } -func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) WatchTokenTransferFeeConfigDeleted(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampTokenTransferFeeConfigDeleted, destChainSelector []*big.Int, token []common.Address) (event.Subscription, error) { +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) WatchTokenTransferFeeConfigDeleted(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampTokenTransferFeeConfigDeleted, destChainSelector []uint64, token []common.Address) (event.Subscription, error) { var destChainSelectorRule []interface{} for _, destChainSelectorItem := range destChainSelector { @@ -2245,7 +2245,7 @@ func (EVM2EVMMultiOnRampPremiumMultiplierWeiPerEthUpdated) Topic() common.Hash { } func (EVM2EVMMultiOnRampTokenTransferFeeConfigDeleted) Topic() common.Hash { - return common.HexToHash("0xfa22e84f9c809b5b7e94f084eb45cf17a5e4703cecef8f27ed35e54b719bffcd") + return common.HexToHash("0x4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b") } func (EVM2EVMMultiOnRampTokenTransferFeeConfigUpdated) Topic() common.Hash { @@ -2355,9 +2355,9 @@ type EVM2EVMMultiOnRampInterface interface { ParsePremiumMultiplierWeiPerEthUpdated(log types.Log) (*EVM2EVMMultiOnRampPremiumMultiplierWeiPerEthUpdated, error) - FilterTokenTransferFeeConfigDeleted(opts *bind.FilterOpts, destChainSelector []*big.Int, token []common.Address) (*EVM2EVMMultiOnRampTokenTransferFeeConfigDeletedIterator, error) + FilterTokenTransferFeeConfigDeleted(opts *bind.FilterOpts, destChainSelector []uint64, token []common.Address) (*EVM2EVMMultiOnRampTokenTransferFeeConfigDeletedIterator, error) - WatchTokenTransferFeeConfigDeleted(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampTokenTransferFeeConfigDeleted, destChainSelector []*big.Int, token []common.Address) (event.Subscription, error) + WatchTokenTransferFeeConfigDeleted(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampTokenTransferFeeConfigDeleted, destChainSelector []uint64, token []common.Address) (event.Subscription, error) ParseTokenTransferFeeConfigDeleted(log types.Log) (*EVM2EVMMultiOnRampTokenTransferFeeConfigDeleted, error) diff --git a/core/gethwrappers/ccip/generated/message_hasher/message_hasher.go b/core/gethwrappers/ccip/generated/message_hasher/message_hasher.go index d777771ff1..f41fb552d4 100644 --- a/core/gethwrappers/ccip/generated/message_hasher/message_hasher.go +++ b/core/gethwrappers/ccip/generated/message_hasher/message_hasher.go @@ -33,25 +33,27 @@ type ClientEVMTokenAmount struct { Amount *big.Int } -type InternalEVM2EVMMessage struct { +type InternalAny2EVMRampMessage struct { + Header InternalRampMessageHeader + Sender []byte + Data []byte + Receiver common.Address + GasLimit *big.Int + TokenAmounts []ClientEVMTokenAmount + SourceTokenData [][]byte +} + +type InternalRampMessageHeader struct { + MessageId [32]byte SourceChainSelector uint64 - Sender common.Address - Receiver common.Address + DestChainSelector uint64 SequenceNumber uint64 - GasLimit *big.Int - Strict bool Nonce uint64 - FeeToken common.Address - FeeTokenAmount *big.Int - Data []byte - TokenAmounts []ClientEVMTokenAmount - SourceTokenData [][]byte - MessageId [32]byte } var MessageHasherMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"strict\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"}],\"internalType\":\"structInternal.EVM2EVMMessage\",\"name\":\"msg\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"metadataHash\",\"type\":\"bytes32\"}],\"name\":\"hash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b50610771806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063c9f1502914610030575b600080fd5b61004361003e3660046104d2565b610055565b60405190815260200160405180910390f35b60006100618383610068565b9392505050565b60008060001b8284602001518560400151866060015187608001518860a001518960c001518a60e001518b610100015160405160200161010b98979695949392919073ffffffffffffffffffffffffffffffffffffffff9889168152968816602088015267ffffffffffffffff95861660408801526060870194909452911515608086015290921660a0840152921660c082015260e08101919091526101000190565b60405160208183030381529060405280519060200120856101200151805190602001208661014001516040516020016101449190610631565b604051602081830303815290604052805190602001208761016001516040516020016101709190610696565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e00160405160208183030381529060405280519060200120905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715610240576102406101ee565b60405290565b6040516101a0810167ffffffffffffffff81118282101715610240576102406101ee565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156102b1576102b16101ee565b604052919050565b803567ffffffffffffffff811681146102d157600080fd5b919050565b803573ffffffffffffffffffffffffffffffffffffffff811681146102d157600080fd5b803580151581146102d157600080fd5b600082601f83011261031b57600080fd5b813567ffffffffffffffff811115610335576103356101ee565b61036660207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161026a565b81815284602083860101111561037b57600080fd5b816020850160208301376000918101602001919091529392505050565b600067ffffffffffffffff8211156103b2576103b26101ee565b5060051b60200190565b600082601f8301126103cd57600080fd5b813560206103e26103dd83610398565b61026a565b82815260069290921b8401810191818101908684111561040157600080fd5b8286015b84811015610447576040818903121561041e5760008081fd5b61042661021d565b61042f826102d6565b81528185013585820152835291830191604001610405565b509695505050505050565b600082601f83011261046357600080fd5b813560206104736103dd83610398565b82815260059290921b8401810191818101908684111561049257600080fd5b8286015b8481101561044757803567ffffffffffffffff8111156104b65760008081fd5b6104c48986838b010161030a565b845250918301918301610496565b600080604083850312156104e557600080fd5b823567ffffffffffffffff808211156104fd57600080fd5b908401906101a0828703121561051257600080fd5b61051a610246565b610523836102b9565b8152610531602084016102d6565b6020820152610542604084016102d6565b6040820152610553606084016102b9565b60608201526080830135608082015261056e60a084016102fa565b60a082015261057f60c084016102b9565b60c082015261059060e084016102d6565b60e0820152610100838101359082015261012080840135838111156105b457600080fd5b6105c08982870161030a565b82840152505061014080840135838111156105da57600080fd5b6105e6898287016103bc565b828401525050610160808401358381111561060057600080fd5b61060c89828701610452565b9183019190915250610180928301359281019290925250946020939093013593505050565b602080825282518282018190526000919060409081850190868401855b82811015610689578151805173ffffffffffffffffffffffffffffffffffffffff16855286015186850152928401929085019060010161064e565b5091979650505050505050565b6000602080830181845280855180835260408601915060408160051b87010192508387016000805b83811015610756577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc089870301855282518051808852835b81811015610711578281018a01518982018b015289016106f6565b508781018901849052601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169096018701955093860193918601916001016106be565b50939897505050505050505056fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"leafDomainSeparator\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"implicitMetadataHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"fixedSizeFieldsHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"dataHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"tokenAmountsHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"sourceTokenDataHash\",\"type\":\"bytes32\"}],\"name\":\"encodeFinalHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"name\":\"encodeFixedSizeFieldsHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"any2EVMMessageHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"name\":\"encodeMetadataHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"}],\"name\":\"encodeSourceTokenDataHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"name\":\"encodeTokenAmountsHashPreimage\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[]\",\"name\":\"sourceTokenData\",\"type\":\"bytes[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"name\":\"hash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b50610bb8806100206000396000f3fe608060405234801561001057600080fd5b50600436106100725760003560e01c8063bf0619ad11610050578063bf0619ad146100d0578063c2cfa165146100e3578063cd34ba82146100f657600080fd5b80633b61b52e14610077578063a1e747df1461009d578063a91d3aeb146100bd575b600080fd5b61008a6100853660046106e3565b610109565b6040519081526020015b60405180910390f35b6100b06100ab366004610812565b61011c565b60405161009491906108de565b6100b06100cb3660046108f1565b61014e565b6100b06100de366004610972565b610186565b6100b06100f13660046109b5565b6101bd565b6100b06101043660046109f2565b6101e6565b600061011583836101f9565b9392505050565b6060848484846040516020016101359493929190610a27565b6040516020818303038152906040529050949350505050565b606086868686868660405160200161016b96959493929190610a64565b60405160208183030381529060405290509695505050505050565b604080516020810188905290810186905260608181018690526080820185905260a0820184905260c082018390529060e00161016b565b6060816040516020016101d09190610ac4565b6040516020818303038152906040529050919050565b6060816040516020016101d09190610b46565b81516020808201516040928301519251600093849361023f937f2425b0b9f9054c76ff151b0a175b18f37a4a4e82013a72e9f15c9caa095ed21f93909291889101610a27565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052805160209182012086518051888401516060808b0151908401516080808d015195015195976102a69794969395929491939101610a64565b604051602081830303815290604052805190602001208560400151805190602001208660a001516040516020016102dd9190610b46565b604051602081830303815290604052805190602001208760c001516040516020016103089190610ac4565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e00160405160208183030381529060405280519060200120905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff811182821017156103d8576103d8610386565b60405290565b60405160e0810167ffffffffffffffff811182821017156103d8576103d8610386565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561044857610448610386565b604052919050565b803567ffffffffffffffff8116811461046857600080fd5b919050565b600060a0828403121561047f57600080fd5b60405160a0810181811067ffffffffffffffff821117156104a2576104a2610386565b604052823581529050806104b860208401610450565b60208201526104c960408401610450565b60408201526104da60608401610450565b60608201526104eb60808401610450565b60808201525092915050565b600082601f83011261050857600080fd5b813567ffffffffffffffff81111561052257610522610386565b61055360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610401565b81815284602083860101111561056857600080fd5b816020850160208301376000918101602001919091529392505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461046857600080fd5b600067ffffffffffffffff8211156105c3576105c3610386565b5060051b60200190565b600082601f8301126105de57600080fd5b813560206105f36105ee836105a9565b610401565b82815260069290921b8401810191818101908684111561061257600080fd5b8286015b84811015610658576040818903121561062f5760008081fd5b6106376103b5565b61064082610585565b81528185013585820152835291830191604001610616565b509695505050505050565b600082601f83011261067457600080fd5b813560206106846105ee836105a9565b82815260059290921b840181019181810190868411156106a357600080fd5b8286015b8481101561065857803567ffffffffffffffff8111156106c75760008081fd5b6106d58986838b01016104f7565b8452509183019183016106a7565b600080604083850312156106f657600080fd5b823567ffffffffffffffff8082111561070e57600080fd5b90840190610160828703121561072357600080fd5b61072b6103de565b610735878461046d565b815260a08301358281111561074957600080fd5b610755888286016104f7565b60208301525060c08301358281111561076d57600080fd5b610779888286016104f7565b60408301525061078b60e08401610585565b60608201526101008301356080820152610120830135828111156107ae57600080fd5b6107ba888286016105cd565b60a083015250610140830135828111156107d357600080fd5b6107df88828601610663565b60c083015250935060208501359150808211156107fb57600080fd5b50610808858286016104f7565b9150509250929050565b6000806000806080858703121561082857600080fd5b8435935061083860208601610450565b925061084660408601610450565b9150606085013567ffffffffffffffff81111561086257600080fd5b61086e878288016104f7565b91505092959194509250565b6000815180845260005b818110156108a057602081850181015186830182015201610884565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000610115602083018461087a565b60008060008060008060c0878903121561090a57600080fd5b86359550602087013567ffffffffffffffff81111561092857600080fd5b61093489828a016104f7565b95505061094360408801610585565b935061095160608801610450565b92506080870135915061096660a08801610450565b90509295509295509295565b60008060008060008060c0878903121561098b57600080fd5b505084359660208601359650604086013595606081013595506080810135945060a0013592509050565b6000602082840312156109c757600080fd5b813567ffffffffffffffff8111156109de57600080fd5b6109ea84828501610663565b949350505050565b600060208284031215610a0457600080fd5b813567ffffffffffffffff811115610a1b57600080fd5b6109ea848285016105cd565b848152600067ffffffffffffffff808616602084015280851660408401525060806060830152610a5a608083018461087a565b9695505050505050565b86815260c060208201526000610a7d60c083018861087a565b73ffffffffffffffffffffffffffffffffffffffff9690961660408301525067ffffffffffffffff9384166060820152608081019290925290911660a09091015292915050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b82811015610b39577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452610b2785835161087a565b94509285019290850190600101610aed565b5092979650505050505050565b602080825282518282018190526000919060409081850190868401855b82811015610b9e578151805173ffffffffffffffffffffffffffffffffffffffff168552860151868501529284019290850190600101610b63565b509197965050505050505056fea164736f6c6343000818000a", } var MessageHasherABI = MessageHasherMetaData.ABI @@ -190,9 +192,119 @@ func (_MessageHasher *MessageHasherTransactorRaw) Transact(opts *bind.TransactOp return _MessageHasher.Contract.contract.Transact(opts, method, params...) } -func (_MessageHasher *MessageHasherCaller) Hash(opts *bind.CallOpts, msg InternalEVM2EVMMessage, metadataHash [32]byte) ([32]byte, error) { +func (_MessageHasher *MessageHasherCaller) EncodeFinalHashPreimage(opts *bind.CallOpts, leafDomainSeparator [32]byte, implicitMetadataHash [32]byte, fixedSizeFieldsHash [32]byte, dataHash [32]byte, tokenAmountsHash [32]byte, sourceTokenDataHash [32]byte) ([]byte, error) { + var out []interface{} + err := _MessageHasher.contract.Call(opts, &out, "encodeFinalHashPreimage", leafDomainSeparator, implicitMetadataHash, fixedSizeFieldsHash, dataHash, tokenAmountsHash, sourceTokenDataHash) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +func (_MessageHasher *MessageHasherSession) EncodeFinalHashPreimage(leafDomainSeparator [32]byte, implicitMetadataHash [32]byte, fixedSizeFieldsHash [32]byte, dataHash [32]byte, tokenAmountsHash [32]byte, sourceTokenDataHash [32]byte) ([]byte, error) { + return _MessageHasher.Contract.EncodeFinalHashPreimage(&_MessageHasher.CallOpts, leafDomainSeparator, implicitMetadataHash, fixedSizeFieldsHash, dataHash, tokenAmountsHash, sourceTokenDataHash) +} + +func (_MessageHasher *MessageHasherCallerSession) EncodeFinalHashPreimage(leafDomainSeparator [32]byte, implicitMetadataHash [32]byte, fixedSizeFieldsHash [32]byte, dataHash [32]byte, tokenAmountsHash [32]byte, sourceTokenDataHash [32]byte) ([]byte, error) { + return _MessageHasher.Contract.EncodeFinalHashPreimage(&_MessageHasher.CallOpts, leafDomainSeparator, implicitMetadataHash, fixedSizeFieldsHash, dataHash, tokenAmountsHash, sourceTokenDataHash) +} + +func (_MessageHasher *MessageHasherCaller) EncodeFixedSizeFieldsHashPreimage(opts *bind.CallOpts, messageId [32]byte, sender []byte, receiver common.Address, sequenceNumber uint64, gasLimit *big.Int, nonce uint64) ([]byte, error) { + var out []interface{} + err := _MessageHasher.contract.Call(opts, &out, "encodeFixedSizeFieldsHashPreimage", messageId, sender, receiver, sequenceNumber, gasLimit, nonce) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +func (_MessageHasher *MessageHasherSession) EncodeFixedSizeFieldsHashPreimage(messageId [32]byte, sender []byte, receiver common.Address, sequenceNumber uint64, gasLimit *big.Int, nonce uint64) ([]byte, error) { + return _MessageHasher.Contract.EncodeFixedSizeFieldsHashPreimage(&_MessageHasher.CallOpts, messageId, sender, receiver, sequenceNumber, gasLimit, nonce) +} + +func (_MessageHasher *MessageHasherCallerSession) EncodeFixedSizeFieldsHashPreimage(messageId [32]byte, sender []byte, receiver common.Address, sequenceNumber uint64, gasLimit *big.Int, nonce uint64) ([]byte, error) { + return _MessageHasher.Contract.EncodeFixedSizeFieldsHashPreimage(&_MessageHasher.CallOpts, messageId, sender, receiver, sequenceNumber, gasLimit, nonce) +} + +func (_MessageHasher *MessageHasherCaller) EncodeMetadataHashPreimage(opts *bind.CallOpts, any2EVMMessageHash [32]byte, sourceChainSelector uint64, destChainSelector uint64, onRamp []byte) ([]byte, error) { var out []interface{} - err := _MessageHasher.contract.Call(opts, &out, "hash", msg, metadataHash) + err := _MessageHasher.contract.Call(opts, &out, "encodeMetadataHashPreimage", any2EVMMessageHash, sourceChainSelector, destChainSelector, onRamp) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +func (_MessageHasher *MessageHasherSession) EncodeMetadataHashPreimage(any2EVMMessageHash [32]byte, sourceChainSelector uint64, destChainSelector uint64, onRamp []byte) ([]byte, error) { + return _MessageHasher.Contract.EncodeMetadataHashPreimage(&_MessageHasher.CallOpts, any2EVMMessageHash, sourceChainSelector, destChainSelector, onRamp) +} + +func (_MessageHasher *MessageHasherCallerSession) EncodeMetadataHashPreimage(any2EVMMessageHash [32]byte, sourceChainSelector uint64, destChainSelector uint64, onRamp []byte) ([]byte, error) { + return _MessageHasher.Contract.EncodeMetadataHashPreimage(&_MessageHasher.CallOpts, any2EVMMessageHash, sourceChainSelector, destChainSelector, onRamp) +} + +func (_MessageHasher *MessageHasherCaller) EncodeSourceTokenDataHashPreimage(opts *bind.CallOpts, sourceTokenData [][]byte) ([]byte, error) { + var out []interface{} + err := _MessageHasher.contract.Call(opts, &out, "encodeSourceTokenDataHashPreimage", sourceTokenData) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +func (_MessageHasher *MessageHasherSession) EncodeSourceTokenDataHashPreimage(sourceTokenData [][]byte) ([]byte, error) { + return _MessageHasher.Contract.EncodeSourceTokenDataHashPreimage(&_MessageHasher.CallOpts, sourceTokenData) +} + +func (_MessageHasher *MessageHasherCallerSession) EncodeSourceTokenDataHashPreimage(sourceTokenData [][]byte) ([]byte, error) { + return _MessageHasher.Contract.EncodeSourceTokenDataHashPreimage(&_MessageHasher.CallOpts, sourceTokenData) +} + +func (_MessageHasher *MessageHasherCaller) EncodeTokenAmountsHashPreimage(opts *bind.CallOpts, tokenAmounts []ClientEVMTokenAmount) ([]byte, error) { + var out []interface{} + err := _MessageHasher.contract.Call(opts, &out, "encodeTokenAmountsHashPreimage", tokenAmounts) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +func (_MessageHasher *MessageHasherSession) EncodeTokenAmountsHashPreimage(tokenAmounts []ClientEVMTokenAmount) ([]byte, error) { + return _MessageHasher.Contract.EncodeTokenAmountsHashPreimage(&_MessageHasher.CallOpts, tokenAmounts) +} + +func (_MessageHasher *MessageHasherCallerSession) EncodeTokenAmountsHashPreimage(tokenAmounts []ClientEVMTokenAmount) ([]byte, error) { + return _MessageHasher.Contract.EncodeTokenAmountsHashPreimage(&_MessageHasher.CallOpts, tokenAmounts) +} + +func (_MessageHasher *MessageHasherCaller) Hash(opts *bind.CallOpts, message InternalAny2EVMRampMessage, onRamp []byte) ([32]byte, error) { + var out []interface{} + err := _MessageHasher.contract.Call(opts, &out, "hash", message, onRamp) if err != nil { return *new([32]byte), err @@ -204,12 +316,12 @@ func (_MessageHasher *MessageHasherCaller) Hash(opts *bind.CallOpts, msg Interna } -func (_MessageHasher *MessageHasherSession) Hash(msg InternalEVM2EVMMessage, metadataHash [32]byte) ([32]byte, error) { - return _MessageHasher.Contract.Hash(&_MessageHasher.CallOpts, msg, metadataHash) +func (_MessageHasher *MessageHasherSession) Hash(message InternalAny2EVMRampMessage, onRamp []byte) ([32]byte, error) { + return _MessageHasher.Contract.Hash(&_MessageHasher.CallOpts, message, onRamp) } -func (_MessageHasher *MessageHasherCallerSession) Hash(msg InternalEVM2EVMMessage, metadataHash [32]byte) ([32]byte, error) { - return _MessageHasher.Contract.Hash(&_MessageHasher.CallOpts, msg, metadataHash) +func (_MessageHasher *MessageHasherCallerSession) Hash(message InternalAny2EVMRampMessage, onRamp []byte) ([32]byte, error) { + return _MessageHasher.Contract.Hash(&_MessageHasher.CallOpts, message, onRamp) } func (_MessageHasher *MessageHasher) Address() common.Address { @@ -217,7 +329,17 @@ func (_MessageHasher *MessageHasher) Address() common.Address { } type MessageHasherInterface interface { - Hash(opts *bind.CallOpts, msg InternalEVM2EVMMessage, metadataHash [32]byte) ([32]byte, error) + EncodeFinalHashPreimage(opts *bind.CallOpts, leafDomainSeparator [32]byte, implicitMetadataHash [32]byte, fixedSizeFieldsHash [32]byte, dataHash [32]byte, tokenAmountsHash [32]byte, sourceTokenDataHash [32]byte) ([]byte, error) + + EncodeFixedSizeFieldsHashPreimage(opts *bind.CallOpts, messageId [32]byte, sender []byte, receiver common.Address, sequenceNumber uint64, gasLimit *big.Int, nonce uint64) ([]byte, error) + + EncodeMetadataHashPreimage(opts *bind.CallOpts, any2EVMMessageHash [32]byte, sourceChainSelector uint64, destChainSelector uint64, onRamp []byte) ([]byte, error) + + EncodeSourceTokenDataHashPreimage(opts *bind.CallOpts, sourceTokenData [][]byte) ([]byte, error) + + EncodeTokenAmountsHashPreimage(opts *bind.CallOpts, tokenAmounts []ClientEVMTokenAmount) ([]byte, error) + + Hash(opts *bind.CallOpts, message InternalAny2EVMRampMessage, onRamp []byte) ([32]byte, error) Address() common.Address } diff --git a/core/gethwrappers/ccip/generated/ocr3_config_encoder/ocr3_config_encoder.go b/core/gethwrappers/ccip/generated/ocr3_config_encoder/ocr3_config_encoder.go new file mode 100644 index 0000000000..399ae5dbd6 --- /dev/null +++ b/core/gethwrappers/ccip/generated/ocr3_config_encoder/ocr3_config_encoder.go @@ -0,0 +1,196 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package ocr3_config_encoder + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type CCIPConfigTypesOCR3Config struct { + PluginType uint8 + ChainSelector uint64 + F uint8 + OffchainConfigVersion uint64 + OfframpAddress []byte + BootstrapP2PIds [][32]byte + P2pIds [][32]byte + Signers [][]byte + Transmitters [][]byte + OffchainConfig []byte +} + +var IOCR3ConfigEncoderMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"enumInternal.OCRPluginType\",\"name\":\"pluginType\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"offrampAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"bootstrapP2PIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"p2pIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"signers\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes[]\",\"name\":\"transmitters\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"internalType\":\"structCCIPConfigTypes.OCR3Config[]\",\"name\":\"config\",\"type\":\"tuple[]\"}],\"name\":\"exposeOCR3Config\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", +} + +var IOCR3ConfigEncoderABI = IOCR3ConfigEncoderMetaData.ABI + +type IOCR3ConfigEncoder struct { + address common.Address + abi abi.ABI + IOCR3ConfigEncoderCaller + IOCR3ConfigEncoderTransactor + IOCR3ConfigEncoderFilterer +} + +type IOCR3ConfigEncoderCaller struct { + contract *bind.BoundContract +} + +type IOCR3ConfigEncoderTransactor struct { + contract *bind.BoundContract +} + +type IOCR3ConfigEncoderFilterer struct { + contract *bind.BoundContract +} + +type IOCR3ConfigEncoderSession struct { + Contract *IOCR3ConfigEncoder + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type IOCR3ConfigEncoderCallerSession struct { + Contract *IOCR3ConfigEncoderCaller + CallOpts bind.CallOpts +} + +type IOCR3ConfigEncoderTransactorSession struct { + Contract *IOCR3ConfigEncoderTransactor + TransactOpts bind.TransactOpts +} + +type IOCR3ConfigEncoderRaw struct { + Contract *IOCR3ConfigEncoder +} + +type IOCR3ConfigEncoderCallerRaw struct { + Contract *IOCR3ConfigEncoderCaller +} + +type IOCR3ConfigEncoderTransactorRaw struct { + Contract *IOCR3ConfigEncoderTransactor +} + +func NewIOCR3ConfigEncoder(address common.Address, backend bind.ContractBackend) (*IOCR3ConfigEncoder, error) { + abi, err := abi.JSON(strings.NewReader(IOCR3ConfigEncoderABI)) + if err != nil { + return nil, err + } + contract, err := bindIOCR3ConfigEncoder(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &IOCR3ConfigEncoder{address: address, abi: abi, IOCR3ConfigEncoderCaller: IOCR3ConfigEncoderCaller{contract: contract}, IOCR3ConfigEncoderTransactor: IOCR3ConfigEncoderTransactor{contract: contract}, IOCR3ConfigEncoderFilterer: IOCR3ConfigEncoderFilterer{contract: contract}}, nil +} + +func NewIOCR3ConfigEncoderCaller(address common.Address, caller bind.ContractCaller) (*IOCR3ConfigEncoderCaller, error) { + contract, err := bindIOCR3ConfigEncoder(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &IOCR3ConfigEncoderCaller{contract: contract}, nil +} + +func NewIOCR3ConfigEncoderTransactor(address common.Address, transactor bind.ContractTransactor) (*IOCR3ConfigEncoderTransactor, error) { + contract, err := bindIOCR3ConfigEncoder(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &IOCR3ConfigEncoderTransactor{contract: contract}, nil +} + +func NewIOCR3ConfigEncoderFilterer(address common.Address, filterer bind.ContractFilterer) (*IOCR3ConfigEncoderFilterer, error) { + contract, err := bindIOCR3ConfigEncoder(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &IOCR3ConfigEncoderFilterer{contract: contract}, nil +} + +func bindIOCR3ConfigEncoder(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := IOCR3ConfigEncoderMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_IOCR3ConfigEncoder *IOCR3ConfigEncoderRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IOCR3ConfigEncoder.Contract.IOCR3ConfigEncoderCaller.contract.Call(opts, result, method, params...) +} + +func (_IOCR3ConfigEncoder *IOCR3ConfigEncoderRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IOCR3ConfigEncoder.Contract.IOCR3ConfigEncoderTransactor.contract.Transfer(opts) +} + +func (_IOCR3ConfigEncoder *IOCR3ConfigEncoderRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IOCR3ConfigEncoder.Contract.IOCR3ConfigEncoderTransactor.contract.Transact(opts, method, params...) +} + +func (_IOCR3ConfigEncoder *IOCR3ConfigEncoderCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IOCR3ConfigEncoder.Contract.contract.Call(opts, result, method, params...) +} + +func (_IOCR3ConfigEncoder *IOCR3ConfigEncoderTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IOCR3ConfigEncoder.Contract.contract.Transfer(opts) +} + +func (_IOCR3ConfigEncoder *IOCR3ConfigEncoderTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IOCR3ConfigEncoder.Contract.contract.Transact(opts, method, params...) +} + +func (_IOCR3ConfigEncoder *IOCR3ConfigEncoderCaller) ExposeOCR3Config(opts *bind.CallOpts, config []CCIPConfigTypesOCR3Config) ([]byte, error) { + var out []interface{} + err := _IOCR3ConfigEncoder.contract.Call(opts, &out, "exposeOCR3Config", config) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +func (_IOCR3ConfigEncoder *IOCR3ConfigEncoderSession) ExposeOCR3Config(config []CCIPConfigTypesOCR3Config) ([]byte, error) { + return _IOCR3ConfigEncoder.Contract.ExposeOCR3Config(&_IOCR3ConfigEncoder.CallOpts, config) +} + +func (_IOCR3ConfigEncoder *IOCR3ConfigEncoderCallerSession) ExposeOCR3Config(config []CCIPConfigTypesOCR3Config) ([]byte, error) { + return _IOCR3ConfigEncoder.Contract.ExposeOCR3Config(&_IOCR3ConfigEncoder.CallOpts, config) +} + +func (_IOCR3ConfigEncoder *IOCR3ConfigEncoder) Address() common.Address { + return _IOCR3ConfigEncoder.address +} + +type IOCR3ConfigEncoderInterface interface { + ExposeOCR3Config(opts *bind.CallOpts, config []CCIPConfigTypesOCR3Config) ([]byte, error) + + Address() common.Address +} diff --git a/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 8b5f3055ff..02a4b18652 100644 --- a/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -5,24 +5,25 @@ burn_from_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnFromMintTokenPool burn_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.bin fee3f82935ce7a26c65e12f19a472a4fccdae62755abdb42d8b0a01f0f06981a burn_mint_token_pool_and_proxy: ../../../contracts/solc/v0.8.24/BurnMintTokenPoolAndProxy/BurnMintTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/BurnMintTokenPoolAndProxy/BurnMintTokenPoolAndProxy.bin c7efa00d2be62a97a814730c8e13aa70794ebfdd38a9f3b3c11554a5dfd70478 burn_with_from_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.bin a0728e186af74968101135a58a483320ced9ab79b22b1b24ac6994254ee79097 -ccip_config: ../../../contracts/solc/v0.8.24/CCIPConfig/CCIPConfig.abi ../../../contracts/solc/v0.8.24/CCIPConfig/CCIPConfig.bin ef9e1f61b288bc31dda1c4e9d0bb8885b7b0bf1fe35bf74af8b12568e7532010 +ccip_config: ../../../contracts/solc/v0.8.24/CCIPConfig/CCIPConfig.abi ../../../contracts/solc/v0.8.24/CCIPConfig/CCIPConfig.bin c44460757ca0e1b228734b32b9ab03221b93d77bb9f8e2970830779a8be2cb78 commit_store: ../../../contracts/solc/v0.8.24/CommitStore/CommitStore.abi ../../../contracts/solc/v0.8.24/CommitStore/CommitStore.bin ddc26c10c2a52b59624faae9005827b09b98db4566887a736005e8cc37cf8a51 commit_store_helper: ../../../contracts/solc/v0.8.24/CommitStoreHelper/CommitStoreHelper.abi ../../../contracts/solc/v0.8.24/CommitStoreHelper/CommitStoreHelper.bin ebd8aac686fa28a71d4212bcd25a28f8f640d50dce5e50498b2f6b8534890b69 ether_sender_receiver: ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.abi ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.bin 09510a3f773f108a3c231e8d202835c845ded862d071ec54c4f89c12d868b8de -evm_2_evm_multi_offramp: ../../../contracts/solc/v0.8.24/EVM2EVMMultiOffRamp/EVM2EVMMultiOffRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMMultiOffRamp/EVM2EVMMultiOffRamp.bin 4bd848e0d37d36bec5cad3b50dce994e53c1c753668af6fc4a60658e7c3149a2 -evm_2_evm_multi_onramp: ../../../contracts/solc/v0.8.24/EVM2EVMMultiOnRamp/EVM2EVMMultiOnRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMMultiOnRamp/EVM2EVMMultiOnRamp.bin f62d32fffd8f9f1e6e48a6e146c29415d67b081d31b9667f701b088c558f3bcf +evm_2_evm_multi_offramp: ../../../contracts/solc/v0.8.24/EVM2EVMMultiOffRamp/EVM2EVMMultiOffRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMMultiOffRamp/EVM2EVMMultiOffRamp.bin 09ac589a267ac59ae3fef7534bebe6da38c427c48b60b5304b9e55408e028337 +evm_2_evm_multi_onramp: ../../../contracts/solc/v0.8.24/EVM2EVMMultiOnRamp/EVM2EVMMultiOnRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMMultiOnRamp/EVM2EVMMultiOnRamp.bin 8edc6f93d7d5f045776cd843c0fae8b94a73835d2c939b2f94fa906ca5f26b97 evm_2_evm_offramp: ../../../contracts/solc/v0.8.24/EVM2EVMOffRamp/EVM2EVMOffRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMOffRamp/EVM2EVMOffRamp.bin b6132cb22370d62b1b20174bbe832ec87df61f6ab65f7fe2515733bdd10a30f5 evm_2_evm_onramp: ../../../contracts/solc/v0.8.24/EVM2EVMOnRamp/EVM2EVMOnRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMOnRamp/EVM2EVMOnRamp.bin 383e9930fbc1b7fbb6554cc8857229d207fd6742e87c7fb1a37002347e8de8e2 lock_release_token_pool: ../../../contracts/solc/v0.8.24/LockReleaseTokenPool/LockReleaseTokenPool.abi ../../../contracts/solc/v0.8.24/LockReleaseTokenPool/LockReleaseTokenPool.bin c65c226e1e4d38414bd4a1b76fc8aca3cb3dd98df61268424c44564f455d3752 lock_release_token_pool_and_proxy: ../../../contracts/solc/v0.8.24/LockReleaseTokenPoolAndProxy/LockReleaseTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/LockReleaseTokenPoolAndProxy/LockReleaseTokenPoolAndProxy.bin 8b929fab79d1caeea4c57e08cc523eb8ab45ec5c08f46da866b82c15ba94d9ad maybe_revert_message_receiver: ../../../contracts/solc/v0.8.24/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.abi ../../../contracts/solc/v0.8.24/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.bin d73956c26232ebcc4a5444429fa99cbefed960e323be9b5a24925885c2e477d5 -message_hasher: ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.abi ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.bin 595d2e1c5172dd1838c9f56c1da09c9243975b2be98c9174c1fcab6802585e20 +message_hasher: ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.abi ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.bin b16f2182c6f262f4864315c90d35efc0342f5ee3580896e9bca50a5b98d51282 mock_arm_contract: ../../../contracts/solc/v0.8.24/MockRMN1_0/MockRMN.abi ../../../contracts/solc/v0.8.24/MockRMN1_0/MockRMN.bin e7a3a6c3eda5fb882e16bcc2b4340f78523acb67907bcdcaf3c8ffc51488688e mock_usdc_token_messenger: ../../../contracts/solc/v0.8.24/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.abi ../../../contracts/solc/v0.8.24/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.bin e0cf17a38b438239fc6294ddca88f86b6c39e4542aefd9815b2d92987191b8bd mock_usdc_token_transmitter: ../../../contracts/solc/v0.8.24/MockE2EUSDCTransmitter/MockE2EUSDCTransmitter.abi ../../../contracts/solc/v0.8.24/MockE2EUSDCTransmitter/MockE2EUSDCTransmitter.bin 33bdad70822e889de7c720ed20085cf9cd3f8eba8b68f26bd6535197749595fe mock_v3_aggregator_contract: ../../../contracts/solc/v0.8.24/MockV3Aggregator/MockV3Aggregator.abi ../../../contracts/solc/v0.8.24/MockV3Aggregator/MockV3Aggregator.bin 518e19efa2ff52b0fefd8e597b05765317ee7638189bfe34ca43de2f6599faf4 multi_aggregate_rate_limiter: ../../../contracts/solc/v0.8.24/MultiAggregateRateLimiter/MultiAggregateRateLimiter.abi ../../../contracts/solc/v0.8.24/MultiAggregateRateLimiter/MultiAggregateRateLimiter.bin abb0ecb1ed8621f26e43b39f5fa25f3d0b6d6c184fa37c404c4389605ecb74e7 nonce_manager: ../../../contracts/solc/v0.8.24/NonceManager/NonceManager.abi ../../../contracts/solc/v0.8.24/NonceManager/NonceManager.bin cdc11c1ab4c1c3fd77f30215e9c579404a6e60eb9adc213d73ca0773c3bb5784 +ocr3_config_encoder: ../../../contracts/solc/v0.8.24/IOCR3ConfigEncoder/IOCR3ConfigEncoder.abi ../../../contracts/solc/v0.8.24/IOCR3ConfigEncoder/IOCR3ConfigEncoder.bin e21180898e1ad54a045ee20add85a2793c681425ea06f66d1a9e5cab128b6487 ping_pong_demo: ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.abi ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.bin 1588313bb5e781d181a825247d30828f59007700f36b4b9b00391592b06ff4b4 price_registry: ../../../contracts/solc/v0.8.24/PriceRegistry/PriceRegistry.abi ../../../contracts/solc/v0.8.24/PriceRegistry/PriceRegistry.bin 0b3e253684d7085aa11f9179b71453b9db9d11cabea41605d5b4ac4128f85bfb registry_module_owner_custom: ../../../contracts/solc/v0.8.24/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.abi ../../../contracts/solc/v0.8.24/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.bin cbe7698bfd811b485ac3856daf073a7bdebeefdf2583403ca4a19d5b7e2d4ae8 diff --git a/core/gethwrappers/ccip/go_generate.go b/core/gethwrappers/ccip/go_generate.go index 864c73d785..a5eaa37523 100644 --- a/core/gethwrappers/ccip/go_generate.go +++ b/core/gethwrappers/ccip/go_generate.go @@ -29,6 +29,7 @@ package ccip //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/Router/Router.abi ../../../contracts/solc/v0.8.24/Router/Router.bin Router router //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/PriceRegistry/PriceRegistry.abi ../../../contracts/solc/v0.8.24/PriceRegistry/PriceRegistry.bin PriceRegistry price_registry //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/CCIPConfig/CCIPConfig.abi ../../../contracts/solc/v0.8.24/CCIPConfig/CCIPConfig.bin CCIPConfig ccip_config +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/IOCR3ConfigEncoder/IOCR3ConfigEncoder.abi ../../../contracts/solc/v0.8.24/IOCR3ConfigEncoder/IOCR3ConfigEncoder.bin IOCR3ConfigEncoder ocr3_config_encoder //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.abi ../../../contracts/solc/v0.8.24/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.bin MaybeRevertMessageReceiver maybe_revert_message_receiver //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.abi ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.bin PingPongDemo ping_pong_demo diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 55a1cab749..ae6df592a4 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -109,7 +109,7 @@ require ( github.com/danieljoos/wincred v1.1.2 // indirect github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/deckarep/golang-set/v2 v2.3.0 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect @@ -375,5 +375,4 @@ replace ( // until merged upstream: https://github.com/mwitkow/grpc-proxy/pull/69 github.com/mwitkow/grpc-proxy => github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f - ) diff --git a/core/scripts/go.sum b/core/scripts/go.sum index c9878e90f1..0fc45c7d74 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -297,8 +297,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g= -github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= diff --git a/core/services/ccipcapability/launcher/README.md b/core/services/ccipcapability/launcher/README.md new file mode 100644 index 0000000000..e3cdc14205 --- /dev/null +++ b/core/services/ccipcapability/launcher/README.md @@ -0,0 +1,69 @@ +# CCIP Capability Launcher + +The CCIP capability launcher is responsible for listening to +[Capabilities Registry](../../../../contracts/src/v0.8/keystone/CapabilitiesRegistry.sol) (CR) updates +for the particular CCIP capability (labelled name, version) pair and reacting to them. In +particular, there are three kinds of events that would affect a particular capability: + +1. DON Creation: when `addDON` is called on the CR, the capabilities of this new DON are specified. +If CCIP is one of those capabilities, the launcher will launch a commit and an execution plugin +with the OCR configuration specified in the DON creation process. See +[Types.sol](../../../../contracts/src/v0.8/ccip/capability/libraries/Types.sol) for more details +on what the OCR configuration contains. +2. DON update: when `updateDON` is called on the CR, capabilities of the DON can be updated. In the +CCIP use case specifically, `updateDON` is used to update OCR configuration of that DON. Updates +follow the blue/green deployment pattern (explained in detail below with a state diagram). In this +scenario the launcher must either launch brand new instances of the commit and execution plugins +(in the event a green deployment is made) or promote the currently running green instance to be +the blue instance. +3. DON deletion: when `deleteDON` is called on the CR, the launcher must shut down all running plugins +related to that DON. When a DON is deleted it effectively means that it should no longer function. +DON deletion is permanent. + +## Architecture Diagram + +![CCIP Capability Launcher](./ccip_capability_launcher.png) + +The above diagram shows how the CCIP capability launcher interacts with the rest of the components +in the CCIP system. + +The CCIP capability job, which is created on the Chainlink node, will spin up the CCIP capability +launcher alongside the home chain reader, which reads the [CCIPConfig.sol](../../../../contracts/src/v0.8/ccip/capability/CCIPConfig.sol) +contract deployed on the home chain (typically Ethereum Mainnet, though could be "any chain" in theory). + +Injected into the launcher is the [OracleCreator](../types/types.go) object which knows how to spin up CCIP +oracles (both bootstrap and plugin oracles). This is used by the launcher at the appropriate time in order +to create oracle instances but not start them right away. + +After all the required oracles have been created, the launcher will start and shut them down as required +in order to match the configuration that was posted on-chain in the CR and the CCIPConfig.sol contract. + + +## Config State Diagram + +![CCIP Config State Machine](./ccip_config_state_machine.png) + +CCIP's blue/green deployment paradigm is intentionally kept as simple as possible. + +Every CCIP DON starts in the `Init` state. Upon DON creation, which must provide a valid OCR +configuration, the CCIP DON will move into the `Running` state. In this state, the DON is +presumed to be fully functional from a configuration standpoint. + +When we want to update configuration, we propose a new configuration to the CR that consists of +an array of two OCR configurations: + +1. The first element of the array is the current OCR configuration that is running (termed "blue"). +2. The second element of the array is the future OCR configuration that we want to run (termed "green"). + +Various checks are done on-chain in order to validate this particular state transition, in particular, +related to config counts. Doing this will move the state of the configuration to the `Staging` state. + +In the `Staging` state, there are effectively four plugins running - one (commit, execution) pair for the +blue configuration, and one (commit, execution) pair for the green configuration. However, only the blue +configuration will actually be writing on-chain, where as the green configuration will be "dry running", +i.e doing everything except transmitting. + +This allows us to test out new configurations without committing to them immediately. + +Finally, from the `Staging` state, there is only one transition, which is to promote the green configuration +to be the new blue configuration, and go back into the `Running` state. diff --git a/core/services/ccipcapability/launcher/bluegreen.go b/core/services/ccipcapability/launcher/bluegreen.go new file mode 100644 index 0000000000..442925c04e --- /dev/null +++ b/core/services/ccipcapability/launcher/bluegreen.go @@ -0,0 +1,177 @@ +package launcher + +import ( + "fmt" + + "go.uber.org/multierr" + + ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" + cctypes "github.com/smartcontractkit/chainlink/v2/core/services/ccipcapability/types" +) + +// blueGreenDeployment represents a blue-green deployment of OCR instances. +type blueGreenDeployment struct { + // blue is the blue OCR instance. + // blue must always be present. + blue cctypes.CCIPOracle + + // bootstrapBlue is the bootstrap node of the blue OCR instance. + // Only a subset of the DON will be running bootstrap instances, + // so this may be nil. + bootstrapBlue cctypes.CCIPOracle + + // green is the green OCR instance. + // green may or may not be present. + // green must never be present if blue is not present. + // TODO: should we enforce this invariant somehow? + green cctypes.CCIPOracle + + // bootstrapGreen is the bootstrap node of the green OCR instance. + // Only a subset of the DON will be running bootstrap instances, + // so this may be nil, even when green is not nil. + bootstrapGreen cctypes.CCIPOracle +} + +// ccipDeployment represents blue-green deployments of both commit and exec +// OCR instances. +type ccipDeployment struct { + commit blueGreenDeployment + exec blueGreenDeployment +} + +// Close shuts down all OCR instances in the deployment. +func (c *ccipDeployment) Close() error { + var err error + + // shutdown blue commit instances. + err = multierr.Append(err, c.commit.blue.Close()) + if c.commit.bootstrapBlue != nil { + err = multierr.Append(err, c.commit.bootstrapBlue.Close()) + } + + // shutdown green commit instances. + if c.commit.green != nil { + err = multierr.Append(err, c.commit.green.Close()) + } + if c.commit.bootstrapGreen != nil { + err = multierr.Append(err, c.commit.bootstrapGreen.Close()) + } + + // shutdown blue exec instances. + err = multierr.Append(err, c.exec.blue.Close()) + if c.exec.bootstrapBlue != nil { + err = multierr.Append(err, c.exec.bootstrapBlue.Close()) + } + + // shutdown green exec instances. + if c.exec.green != nil { + err = multierr.Append(err, c.exec.green.Close()) + } + if c.exec.bootstrapGreen != nil { + err = multierr.Append(err, c.exec.bootstrapGreen.Close()) + } + + return err +} + +// StartBlue starts the blue OCR instances. +func (c *ccipDeployment) StartBlue() error { + var err error + + err = multierr.Append(err, c.commit.blue.Start()) + if c.commit.bootstrapBlue != nil { + err = multierr.Append(err, c.commit.bootstrapBlue.Start()) + } + err = multierr.Append(err, c.exec.blue.Start()) + if c.exec.bootstrapBlue != nil { + err = multierr.Append(err, c.exec.bootstrapBlue.Start()) + } + + return err +} + +// CloseBlue shuts down the blue OCR instances. +func (c *ccipDeployment) CloseBlue() error { + var err error + + err = multierr.Append(err, c.commit.blue.Close()) + if c.commit.bootstrapBlue != nil { + err = multierr.Append(err, c.commit.bootstrapBlue.Close()) + } + err = multierr.Append(err, c.exec.blue.Close()) + if c.exec.bootstrapBlue != nil { + err = multierr.Append(err, c.exec.bootstrapBlue.Close()) + } + + return err +} + +// HandleBlueGreen handles the blue-green deployment transition. +// prevDeployment is the previous deployment state. +// there are two possible cases: +// +// 1. both blue and green are present in prevDeployment, but only blue is present in c. +// this is a promotion of green to blue, so we need to shut down the blue deployment +// and make green the new blue. In this case green is already running, so there's no +// need to start it. However, we need to shut down the blue deployment. +// +// 2. only blue is present in prevDeployment, both blue and green are present in c. +// In this case, blue is already running, so there's no need to start it. We need to +// start green. +func (c *ccipDeployment) HandleBlueGreen(prevDeployment *ccipDeployment) error { + if prevDeployment == nil { + return fmt.Errorf("previous deployment is nil") + } + + var err error + if prevDeployment.commit.green != nil && c.commit.green == nil { + err = multierr.Append(err, prevDeployment.commit.blue.Close()) + if prevDeployment.commit.bootstrapBlue != nil { + err = multierr.Append(err, prevDeployment.commit.bootstrapBlue.Close()) + } + } else if prevDeployment.commit.green == nil && c.commit.green != nil { + err = multierr.Append(err, c.commit.green.Start()) + if c.commit.bootstrapGreen != nil { + err = multierr.Append(err, c.commit.bootstrapGreen.Start()) + } + } else { + return fmt.Errorf("invalid blue-green deployment transition") + } + + if prevDeployment.exec.green != nil && c.exec.green == nil { + err = multierr.Append(err, prevDeployment.exec.blue.Close()) + if prevDeployment.exec.bootstrapBlue != nil { + err = multierr.Append(err, prevDeployment.exec.bootstrapBlue.Close()) + } + } else if prevDeployment.exec.green == nil && c.exec.green != nil { + err = multierr.Append(err, c.exec.green.Start()) + if c.exec.bootstrapGreen != nil { + err = multierr.Append(err, c.exec.bootstrapGreen.Start()) + } + } else { + return fmt.Errorf("invalid blue-green deployment transition") + } + + return err +} + +// HasGreenInstance returns true if the deployment has a green instance for the +// given plugin type. +func (c *ccipDeployment) HasGreenInstance(pluginType cctypes.PluginType) bool { + switch pluginType { + case cctypes.PluginTypeCCIPCommit: + return c.commit.green != nil + case cctypes.PluginTypeCCIPExec: + return c.exec.green != nil + default: + return false + } +} + +func isNewGreenInstance(pluginType cctypes.PluginType, ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta, prevDeployment ccipDeployment) bool { + return len(ocrConfigs) == 2 && !prevDeployment.HasGreenInstance(pluginType) +} + +func isPromotion(pluginType cctypes.PluginType, ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta, prevDeployment ccipDeployment) bool { + return len(ocrConfigs) == 1 && prevDeployment.HasGreenInstance(pluginType) +} diff --git a/core/services/ccipcapability/launcher/bluegreen_test.go b/core/services/ccipcapability/launcher/bluegreen_test.go new file mode 100644 index 0000000000..a18e02eda3 --- /dev/null +++ b/core/services/ccipcapability/launcher/bluegreen_test.go @@ -0,0 +1,1042 @@ +package launcher + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/require" + + ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" + cctypes "github.com/smartcontractkit/chainlink/v2/core/services/ccipcapability/types" + mocktypes "github.com/smartcontractkit/chainlink/v2/core/services/ccipcapability/types/mocks" +) + +func Test_ccipDeployment_Close(t *testing.T) { + type args struct { + commitBlue *mocktypes.CCIPOracle + commitBlueBootstrap *mocktypes.CCIPOracle + commitGreen *mocktypes.CCIPOracle + commitGreenBootstrap *mocktypes.CCIPOracle + execBlue *mocktypes.CCIPOracle + execBlueBootstrap *mocktypes.CCIPOracle + execGreen *mocktypes.CCIPOracle + execGreenBootstrap *mocktypes.CCIPOracle + } + tests := []struct { + name string + args args + expect func(t *testing.T, args args) + asserts func(t *testing.T, args args) + wantErr bool + }{ + { + name: "no errors, blue only", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: nil, + commitGreenBootstrap: nil, + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: nil, + execGreenBootstrap: nil, + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Close").Return(nil).Once() + args.execBlue.On("Close").Return(nil).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + }, + wantErr: false, + }, + { + name: "no errors, blue and green", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Close").Return(nil).Once() + args.commitGreen.On("Close").Return(nil).Once() + args.execBlue.On("Close").Return(nil).Once() + args.execGreen.On("Close").Return(nil).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.commitGreen.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + args.execGreen.AssertExpectations(t) + }, + wantErr: false, + }, + { + name: "error on commit blue", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: nil, + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: nil, + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Close").Return(errors.New("failed")).Once() + args.execBlue.On("Close").Return(nil).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + }, + wantErr: true, + }, + { + name: "bootstrap blue also closed", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + execBlueBootstrap: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Close").Return(nil).Once() + args.commitBlueBootstrap.On("Close").Return(nil).Once() + args.execBlue.On("Close").Return(nil).Once() + args.execBlueBootstrap.On("Close").Return(nil).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.commitBlueBootstrap.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + args.execBlueBootstrap.AssertExpectations(t) + }, + wantErr: false, + }, + { + name: "bootstrap green also closed", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: mocktypes.NewCCIPOracle(t), + commitGreen: mocktypes.NewCCIPOracle(t), + commitGreenBootstrap: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + execBlueBootstrap: mocktypes.NewCCIPOracle(t), + execGreen: mocktypes.NewCCIPOracle(t), + execGreenBootstrap: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Close").Return(nil).Once() + args.commitBlueBootstrap.On("Close").Return(nil).Once() + args.commitGreen.On("Close").Return(nil).Once() + args.commitGreenBootstrap.On("Close").Return(nil).Once() + args.execBlue.On("Close").Return(nil).Once() + args.execBlueBootstrap.On("Close").Return(nil).Once() + args.execGreen.On("Close").Return(nil).Once() + args.execGreenBootstrap.On("Close").Return(nil).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.commitBlueBootstrap.AssertExpectations(t) + args.commitGreen.AssertExpectations(t) + args.commitGreenBootstrap.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + args.execBlueBootstrap.AssertExpectations(t) + args.execGreen.AssertExpectations(t) + args.execGreenBootstrap.AssertExpectations(t) + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &ccipDeployment{ + commit: blueGreenDeployment{ + blue: tt.args.commitBlue, + }, + exec: blueGreenDeployment{ + blue: tt.args.execBlue, + }, + } + if tt.args.commitGreen != nil { + c.commit.green = tt.args.commitGreen + } + if tt.args.commitBlueBootstrap != nil { + c.commit.bootstrapBlue = tt.args.commitBlueBootstrap + } + if tt.args.commitGreenBootstrap != nil { + c.commit.bootstrapGreen = tt.args.commitGreenBootstrap + } + + if tt.args.execGreen != nil { + c.exec.green = tt.args.execGreen + } + if tt.args.execBlueBootstrap != nil { + c.exec.bootstrapBlue = tt.args.execBlueBootstrap + } + if tt.args.execGreenBootstrap != nil { + c.exec.bootstrapGreen = tt.args.execGreenBootstrap + } + + tt.expect(t, tt.args) + defer tt.asserts(t, tt.args) + err := c.Close() + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func Test_ccipDeployment_StartBlue(t *testing.T) { + type args struct { + commitBlue *mocktypes.CCIPOracle + commitBlueBootstrap *mocktypes.CCIPOracle + execBlue *mocktypes.CCIPOracle + execBlueBootstrap *mocktypes.CCIPOracle + } + tests := []struct { + name string + args args + expect func(t *testing.T, args args) + asserts func(t *testing.T, args args) + wantErr bool + }{ + { + name: "no errors, no bootstrap", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: nil, + execBlueBootstrap: nil, + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Start").Return(nil).Once() + args.execBlue.On("Start").Return(nil).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + }, + wantErr: false, + }, + { + name: "no errors, with bootstrap", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: mocktypes.NewCCIPOracle(t), + execBlueBootstrap: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Start").Return(nil).Once() + args.commitBlueBootstrap.On("Start").Return(nil).Once() + args.execBlue.On("Start").Return(nil).Once() + args.execBlueBootstrap.On("Start").Return(nil).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.commitBlueBootstrap.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + args.execBlueBootstrap.AssertExpectations(t) + }, + wantErr: false, + }, + { + name: "error on commit blue", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: nil, + execBlueBootstrap: nil, + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Start").Return(errors.New("failed")).Once() + args.execBlue.On("Start").Return(nil).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + }, + wantErr: true, + }, + { + name: "error on exec blue", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: nil, + execBlueBootstrap: nil, + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Start").Return(nil).Once() + args.execBlue.On("Start").Return(errors.New("failed")).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + }, + wantErr: true, + }, + { + name: "error on commit blue bootstrap", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: mocktypes.NewCCIPOracle(t), + execBlueBootstrap: nil, + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Start").Return(nil).Once() + args.commitBlueBootstrap.On("Start").Return(errors.New("failed")).Once() + args.execBlue.On("Start").Return(nil).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.commitBlueBootstrap.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + }, + wantErr: true, + }, + { + name: "error on exec blue bootstrap", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: nil, + execBlueBootstrap: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Start").Return(nil).Once() + args.execBlue.On("Start").Return(nil).Once() + args.execBlueBootstrap.On("Start").Return(errors.New("failed")).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + args.execBlueBootstrap.AssertExpectations(t) + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &ccipDeployment{ + commit: blueGreenDeployment{ + blue: tt.args.commitBlue, + }, + exec: blueGreenDeployment{ + blue: tt.args.execBlue, + }, + } + if tt.args.commitBlueBootstrap != nil { + c.commit.bootstrapBlue = tt.args.commitBlueBootstrap + } + if tt.args.execBlueBootstrap != nil { + c.exec.bootstrapBlue = tt.args.execBlueBootstrap + } + + tt.expect(t, tt.args) + defer tt.asserts(t, tt.args) + err := c.StartBlue() + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func Test_ccipDeployment_CloseBlue(t *testing.T) { + type args struct { + commitBlue *mocktypes.CCIPOracle + commitBlueBootstrap *mocktypes.CCIPOracle + execBlue *mocktypes.CCIPOracle + execBlueBootstrap *mocktypes.CCIPOracle + } + tests := []struct { + name string + args args + expect func(t *testing.T, args args) + asserts func(t *testing.T, args args) + wantErr bool + }{ + { + name: "no errors, no bootstrap", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: nil, + execBlueBootstrap: nil, + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Close").Return(nil).Once() + args.execBlue.On("Close").Return(nil).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + }, + wantErr: false, + }, + { + name: "no errors, with bootstrap", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: mocktypes.NewCCIPOracle(t), + execBlueBootstrap: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Close").Return(nil).Once() + args.commitBlueBootstrap.On("Close").Return(nil).Once() + args.execBlue.On("Close").Return(nil).Once() + args.execBlueBootstrap.On("Close").Return(nil).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.commitBlueBootstrap.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + args.execBlueBootstrap.AssertExpectations(t) + }, + wantErr: false, + }, + { + name: "error on commit blue", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: nil, + execBlueBootstrap: nil, + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Close").Return(errors.New("failed")).Once() + args.execBlue.On("Close").Return(nil).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + }, + wantErr: true, + }, + { + name: "error on exec blue", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: nil, + execBlueBootstrap: nil, + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Close").Return(nil).Once() + args.execBlue.On("Close").Return(errors.New("failed")).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + }, + wantErr: true, + }, + { + name: "error on commit blue bootstrap", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: mocktypes.NewCCIPOracle(t), + execBlueBootstrap: nil, + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Close").Return(nil).Once() + args.commitBlueBootstrap.On("Close").Return(errors.New("failed")).Once() + args.execBlue.On("Close").Return(nil).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.commitBlueBootstrap.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + }, + wantErr: true, + }, + { + name: "error on exec blue bootstrap", + args: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: nil, + execBlueBootstrap: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args) { + args.commitBlue.On("Close").Return(nil).Once() + args.execBlue.On("Close").Return(nil).Once() + args.execBlueBootstrap.On("Close").Return(errors.New("failed")).Once() + }, + asserts: func(t *testing.T, args args) { + args.commitBlue.AssertExpectations(t) + args.execBlue.AssertExpectations(t) + args.execBlueBootstrap.AssertExpectations(t) + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &ccipDeployment{ + commit: blueGreenDeployment{ + blue: tt.args.commitBlue, + }, + exec: blueGreenDeployment{ + blue: tt.args.execBlue, + }, + } + if tt.args.commitBlueBootstrap != nil { + c.commit.bootstrapBlue = tt.args.commitBlueBootstrap + } + if tt.args.execBlueBootstrap != nil { + c.exec.bootstrapBlue = tt.args.execBlueBootstrap + } + + tt.expect(t, tt.args) + defer tt.asserts(t, tt.args) + err := c.CloseBlue() + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func Test_ccipDeployment_HandleBlueGreen_PrevDeploymentNil(t *testing.T) { + require.Error(t, (&ccipDeployment{}).HandleBlueGreen(nil)) +} + +func Test_ccipDeployment_HandleBlueGreen(t *testing.T) { + type args struct { + commitBlue *mocktypes.CCIPOracle + commitBlueBootstrap *mocktypes.CCIPOracle + commitGreen *mocktypes.CCIPOracle + commitGreenBootstrap *mocktypes.CCIPOracle + execBlue *mocktypes.CCIPOracle + execBlueBootstrap *mocktypes.CCIPOracle + execGreen *mocktypes.CCIPOracle + execGreenBootstrap *mocktypes.CCIPOracle + } + tests := []struct { + name string + argsPrevDeployment args + argsFutureDeployment args + expect func(t *testing.T, args args, argsPrevDeployment args) + asserts func(t *testing.T, args args, argsPrevDeployment args) + wantErr bool + }{ + { + name: "promotion blue to green, no bootstrap", + argsPrevDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: mocktypes.NewCCIPOracle(t), + }, + argsFutureDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: nil, + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: nil, + }, + expect: func(t *testing.T, args args, argsPrevDeployment args) { + argsPrevDeployment.commitBlue.On("Close").Return(nil).Once() + argsPrevDeployment.execBlue.On("Close").Return(nil).Once() + }, + asserts: func(t *testing.T, args args, argsPrevDeployment args) { + argsPrevDeployment.commitBlue.AssertExpectations(t) + argsPrevDeployment.execBlue.AssertExpectations(t) + }, + wantErr: false, + }, + { + name: "promotion blue to green, with bootstrap", + argsPrevDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: mocktypes.NewCCIPOracle(t), + commitGreen: mocktypes.NewCCIPOracle(t), + commitGreenBootstrap: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + execBlueBootstrap: mocktypes.NewCCIPOracle(t), + execGreen: mocktypes.NewCCIPOracle(t), + execGreenBootstrap: mocktypes.NewCCIPOracle(t), + }, + argsFutureDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: mocktypes.NewCCIPOracle(t), + commitGreen: nil, + commitGreenBootstrap: nil, + execBlue: mocktypes.NewCCIPOracle(t), + execBlueBootstrap: mocktypes.NewCCIPOracle(t), + execGreen: nil, + execGreenBootstrap: nil, + }, + expect: func(t *testing.T, args args, argsPrevDeployment args) { + argsPrevDeployment.commitBlue.On("Close").Return(nil).Once() + argsPrevDeployment.commitBlueBootstrap.On("Close").Return(nil).Once() + argsPrevDeployment.execBlue.On("Close").Return(nil).Once() + argsPrevDeployment.execBlueBootstrap.On("Close").Return(nil).Once() + }, + asserts: func(t *testing.T, args args, argsPrevDeployment args) { + argsPrevDeployment.commitBlue.AssertExpectations(t) + argsPrevDeployment.commitBlueBootstrap.AssertExpectations(t) + argsPrevDeployment.execBlue.AssertExpectations(t) + argsPrevDeployment.execBlueBootstrap.AssertExpectations(t) + }, + wantErr: false, + }, + { + name: "new green deployment, no bootstrap", + argsPrevDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: nil, + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: nil, + }, + argsFutureDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args, argsPrevDeployment args) { + args.commitGreen.On("Start").Return(nil).Once() + args.execGreen.On("Start").Return(nil).Once() + }, + asserts: func(t *testing.T, args args, argsPrevDeployment args) { + args.commitGreen.AssertExpectations(t) + args.execGreen.AssertExpectations(t) + }, + wantErr: false, + }, + { + name: "new green deployment, with bootstrap", + argsPrevDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: mocktypes.NewCCIPOracle(t), + commitGreen: nil, + commitGreenBootstrap: nil, + execBlue: mocktypes.NewCCIPOracle(t), + execBlueBootstrap: mocktypes.NewCCIPOracle(t), + execGreen: nil, + execGreenBootstrap: nil, + }, + argsFutureDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: mocktypes.NewCCIPOracle(t), + commitGreen: mocktypes.NewCCIPOracle(t), + commitGreenBootstrap: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + execBlueBootstrap: mocktypes.NewCCIPOracle(t), + execGreen: mocktypes.NewCCIPOracle(t), + execGreenBootstrap: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args, argsPrevDeployment args) { + args.commitGreen.On("Start").Return(nil).Once() + args.commitGreenBootstrap.On("Start").Return(nil).Once() + args.execGreen.On("Start").Return(nil).Once() + args.execGreenBootstrap.On("Start").Return(nil).Once() + }, + asserts: func(t *testing.T, args args, argsPrevDeployment args) { + args.commitGreen.AssertExpectations(t) + args.commitGreenBootstrap.AssertExpectations(t) + args.execGreen.AssertExpectations(t) + args.execGreenBootstrap.AssertExpectations(t) + }, + wantErr: false, + }, + { + name: "error on commit green start", + argsPrevDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: nil, + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: nil, + }, + argsFutureDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args, argsPrevDeployment args) { + args.commitGreen.On("Start").Return(errors.New("failed")).Once() + args.execGreen.On("Start").Return(nil).Once() + }, + asserts: func(t *testing.T, args args, argsPrevDeployment args) { + args.commitGreen.AssertExpectations(t) + args.execGreen.AssertExpectations(t) + }, + wantErr: true, + }, + { + name: "error on exec green start", + argsPrevDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: nil, + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: nil, + }, + argsFutureDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args, argsPrevDeployment args) { + args.commitGreen.On("Start").Return(nil).Once() + args.execGreen.On("Start").Return(errors.New("failed")).Once() + }, + asserts: func(t *testing.T, args args, argsPrevDeployment args) { + args.commitGreen.AssertExpectations(t) + args.execGreen.AssertExpectations(t) + }, + wantErr: true, + }, + { + name: "error on commit green bootstrap start", + argsPrevDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: mocktypes.NewCCIPOracle(t), + commitGreen: nil, + commitGreenBootstrap: nil, + execBlue: mocktypes.NewCCIPOracle(t), + execBlueBootstrap: mocktypes.NewCCIPOracle(t), + execGreen: nil, + execGreenBootstrap: nil, + }, + argsFutureDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitBlueBootstrap: mocktypes.NewCCIPOracle(t), + commitGreen: mocktypes.NewCCIPOracle(t), + commitGreenBootstrap: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + execBlueBootstrap: mocktypes.NewCCIPOracle(t), + execGreen: mocktypes.NewCCIPOracle(t), + execGreenBootstrap: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args, argsPrevDeployment args) { + args.commitGreen.On("Start").Return(nil).Once() + args.commitGreenBootstrap.On("Start").Return(errors.New("failed")).Once() + args.execGreen.On("Start").Return(nil).Once() + args.execGreenBootstrap.On("Start").Return(nil).Once() + }, + asserts: func(t *testing.T, args args, argsPrevDeployment args) { + args.commitGreen.AssertExpectations(t) + args.commitGreenBootstrap.AssertExpectations(t) + args.execGreen.AssertExpectations(t) + args.execGreenBootstrap.AssertExpectations(t) + }, + wantErr: true, + }, + { + name: "invalid blue-green deployment transition commit: both prev and future deployment have green", + argsPrevDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: mocktypes.NewCCIPOracle(t), + }, + argsFutureDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args, argsPrevDeployment args) {}, + asserts: func(t *testing.T, args args, argsPrevDeployment args) {}, + wantErr: true, + }, + { + name: "invalid blue-green deployment transition exec: both prev and future deployment have green", + argsPrevDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: nil, + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: mocktypes.NewCCIPOracle(t), + }, + argsFutureDeployment: args{ + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: mocktypes.NewCCIPOracle(t), + }, + expect: func(t *testing.T, args args, argsPrevDeployment args) { + args.commitGreen.On("Start").Return(nil).Once() + }, + asserts: func(t *testing.T, args args, argsPrevDeployment args) { + args.commitGreen.AssertExpectations(t) + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + futDeployment := &ccipDeployment{ + commit: blueGreenDeployment{ + blue: tt.argsFutureDeployment.commitBlue, + }, + exec: blueGreenDeployment{ + blue: tt.argsFutureDeployment.execBlue, + }, + } + if tt.argsFutureDeployment.commitGreen != nil { + futDeployment.commit.green = tt.argsFutureDeployment.commitGreen + } + if tt.argsFutureDeployment.commitBlueBootstrap != nil { + futDeployment.commit.bootstrapBlue = tt.argsFutureDeployment.commitBlueBootstrap + } + if tt.argsFutureDeployment.commitGreenBootstrap != nil { + futDeployment.commit.bootstrapGreen = tt.argsFutureDeployment.commitGreenBootstrap + } + if tt.argsFutureDeployment.execGreen != nil { + futDeployment.exec.green = tt.argsFutureDeployment.execGreen + } + if tt.argsFutureDeployment.execBlueBootstrap != nil { + futDeployment.exec.bootstrapBlue = tt.argsFutureDeployment.execBlueBootstrap + } + if tt.argsFutureDeployment.execGreenBootstrap != nil { + futDeployment.exec.bootstrapGreen = tt.argsFutureDeployment.execGreenBootstrap + } + + prevDeployment := &ccipDeployment{ + commit: blueGreenDeployment{ + blue: tt.argsPrevDeployment.commitBlue, + }, + exec: blueGreenDeployment{ + blue: tt.argsPrevDeployment.execBlue, + }, + } + if tt.argsPrevDeployment.commitGreen != nil { + prevDeployment.commit.green = tt.argsPrevDeployment.commitGreen + } + if tt.argsPrevDeployment.commitBlueBootstrap != nil { + prevDeployment.commit.bootstrapBlue = tt.argsPrevDeployment.commitBlueBootstrap + } + if tt.argsPrevDeployment.commitGreenBootstrap != nil { + prevDeployment.commit.bootstrapGreen = tt.argsPrevDeployment.commitGreenBootstrap + } + if tt.argsPrevDeployment.execGreen != nil { + prevDeployment.exec.green = tt.argsPrevDeployment.execGreen + } + if tt.argsPrevDeployment.execBlueBootstrap != nil { + prevDeployment.exec.bootstrapBlue = tt.argsPrevDeployment.execBlueBootstrap + } + if tt.argsPrevDeployment.execGreenBootstrap != nil { + prevDeployment.exec.bootstrapGreen = tt.argsPrevDeployment.execGreenBootstrap + } + + tt.expect(t, tt.argsFutureDeployment, tt.argsPrevDeployment) + defer tt.asserts(t, tt.argsFutureDeployment, tt.argsPrevDeployment) + err := futDeployment.HandleBlueGreen(prevDeployment) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func Test_isNewGreenInstance(t *testing.T) { + type args struct { + pluginType cctypes.PluginType + ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta + prevDeployment ccipDeployment + } + tests := []struct { + name string + args args + want bool + }{ + { + "prev deployment only blue", + args{ + pluginType: cctypes.PluginTypeCCIPCommit, + ocrConfigs: []ccipreaderpkg.OCR3ConfigWithMeta{ + {}, {}, + }, + prevDeployment: ccipDeployment{ + commit: blueGreenDeployment{ + blue: mocktypes.NewCCIPOracle(t), + }, + }, + }, + true, + }, + { + "green -> blue promotion", + args{ + pluginType: cctypes.PluginTypeCCIPCommit, + ocrConfigs: []ccipreaderpkg.OCR3ConfigWithMeta{ + {}, + }, + prevDeployment: ccipDeployment{ + commit: blueGreenDeployment{ + blue: mocktypes.NewCCIPOracle(t), + green: mocktypes.NewCCIPOracle(t), + }, + }, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := isNewGreenInstance(tt.args.pluginType, tt.args.ocrConfigs, tt.args.prevDeployment) + require.Equal(t, tt.want, got) + }) + } +} + +func Test_isPromotion(t *testing.T) { + type args struct { + pluginType cctypes.PluginType + ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta + prevDeployment ccipDeployment + } + tests := []struct { + name string + args args + want bool + }{ + { + "prev deployment only blue", + args{ + pluginType: cctypes.PluginTypeCCIPCommit, + ocrConfigs: []ccipreaderpkg.OCR3ConfigWithMeta{ + {}, {}, + }, + prevDeployment: ccipDeployment{ + commit: blueGreenDeployment{ + blue: mocktypes.NewCCIPOracle(t), + }, + }, + }, + false, + }, + { + "green -> blue promotion", + args{ + pluginType: cctypes.PluginTypeCCIPCommit, + ocrConfigs: []ccipreaderpkg.OCR3ConfigWithMeta{ + {}, + }, + prevDeployment: ccipDeployment{ + commit: blueGreenDeployment{ + blue: mocktypes.NewCCIPOracle(t), + green: mocktypes.NewCCIPOracle(t), + }, + }, + }, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isPromotion(tt.args.pluginType, tt.args.ocrConfigs, tt.args.prevDeployment); got != tt.want { + t.Errorf("isPromotion() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_ccipDeployment_HasGreenInstance(t *testing.T) { + type fields struct { + commit blueGreenDeployment + exec blueGreenDeployment + } + type args struct { + pluginType cctypes.PluginType + } + tests := []struct { + name string + fields fields + args args + want bool + }{ + { + "commit green present", + fields{ + commit: blueGreenDeployment{ + blue: mocktypes.NewCCIPOracle(t), + green: mocktypes.NewCCIPOracle(t), + }, + }, + args{ + pluginType: cctypes.PluginTypeCCIPCommit, + }, + true, + }, + { + "commit green not present", + fields{ + commit: blueGreenDeployment{ + blue: mocktypes.NewCCIPOracle(t), + }, + }, + args{ + pluginType: cctypes.PluginTypeCCIPCommit, + }, + false, + }, + { + "exec green present", + fields{ + exec: blueGreenDeployment{ + blue: mocktypes.NewCCIPOracle(t), + green: mocktypes.NewCCIPOracle(t), + }, + }, + args{ + pluginType: cctypes.PluginTypeCCIPExec, + }, + true, + }, + { + "exec green not present", + fields{ + exec: blueGreenDeployment{ + blue: mocktypes.NewCCIPOracle(t), + }, + }, + args{ + pluginType: cctypes.PluginTypeCCIPExec, + }, + false, + }, + { + "invalid plugin type", + fields{}, + args{ + pluginType: cctypes.PluginType(100), + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &ccipDeployment{} + if tt.fields.commit.blue != nil { + c.commit.blue = tt.fields.commit.blue + } + if tt.fields.commit.green != nil { + c.commit.green = tt.fields.commit.green + } + if tt.fields.exec.blue != nil { + c.exec.blue = tt.fields.exec.blue + } + if tt.fields.exec.green != nil { + c.exec.green = tt.fields.exec.green + } + got := c.HasGreenInstance(tt.args.pluginType) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/core/services/ccipcapability/launcher/ccip_capability_launcher.png b/core/services/ccipcapability/launcher/ccip_capability_launcher.png new file mode 100644 index 0000000000..5e90d5ff7d Binary files /dev/null and b/core/services/ccipcapability/launcher/ccip_capability_launcher.png differ diff --git a/core/services/ccipcapability/launcher/ccip_config_state_machine.png b/core/services/ccipcapability/launcher/ccip_config_state_machine.png new file mode 100644 index 0000000000..ece40e6c19 Binary files /dev/null and b/core/services/ccipcapability/launcher/ccip_config_state_machine.png differ diff --git a/core/services/ccipcapability/launcher/diff.go b/core/services/ccipcapability/launcher/diff.go new file mode 100644 index 0000000000..d56bf9dd12 --- /dev/null +++ b/core/services/ccipcapability/launcher/diff.go @@ -0,0 +1,180 @@ +package launcher + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/crypto" + + ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" + kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" + "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" +) + +// diffResult contains the added, removed and updated CCIP DONs. +// It is determined by using the `diff` function below. +type diffResult struct { + added map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo + removed map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo + updated map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo +} + +// diff compares the old and new state and returns the added, removed and updated CCIP DONs. +func diff( + capabilityVersion, + capabilityLabelledName string, + oldState, + newState registrysyncer.State, +) (diffResult, error) { + ccipCapability, err := checkCapabilityPresence(capabilityVersion, capabilityLabelledName, newState) + if err != nil { + return diffResult{}, fmt.Errorf("failed to check capability presence: %w", err) + } + + newCCIPDONs, err := filterCCIPDONs(ccipCapability, newState) + if err != nil { + return diffResult{}, fmt.Errorf("failed to filter CCIP DONs from new state: %w", err) + } + + currCCIPDONs, err := filterCCIPDONs(ccipCapability, oldState) + if err != nil { + return diffResult{}, fmt.Errorf("failed to filter CCIP DONs from old state: %w", err) + } + + // compare curr with new and launch or update OCR instances as needed + diffRes, err := compareDONs(currCCIPDONs, newCCIPDONs) + if err != nil { + return diffResult{}, fmt.Errorf("failed to compare CCIP DONs: %w", err) + } + + return diffRes, nil +} + +// compareDONs compares the current and new CCIP DONs and returns the added, removed and updated DONs. +func compareDONs( + currCCIPDONs, + newCCIPDONs map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo, +) ( + dr diffResult, + err error, +) { + added := make(map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo) + removed := make(map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo) + updated := make(map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo) + + for id, don := range newCCIPDONs { + if currDONState, ok := currCCIPDONs[id]; !ok { + // Not in current state, so mark as added. + added[id] = don + } else { + // If its in the current state and the config count for the DON has changed, mark as updated. + // Since the registry returns the full state we need to compare the config count. + if don.ConfigCount > currDONState.ConfigCount { + updated[id] = don + } + } + } + + for id, don := range currCCIPDONs { + if _, ok := newCCIPDONs[id]; !ok { + // In current state but not in latest registry state, so should remove. + removed[id] = don + } + } + + return diffResult{ + added: added, + removed: removed, + updated: updated, + }, nil +} + +// filterCCIPDONs filters the CCIP DONs from the given state. +func filterCCIPDONs( + ccipCapability kcr.CapabilitiesRegistryCapabilityInfo, + state registrysyncer.State, +) (map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo, error) { + ccipDONs := make(map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo) + for _, don := range state.IDsToDONs { + for _, donCapabilities := range don.CapabilityConfigurations { + hid, err := hashedCapabilityId(ccipCapability.LabelledName, ccipCapability.Version) + if err != nil { + return nil, fmt.Errorf("failed to hash capability id: %w", err) + } + if donCapabilities.CapabilityId == hid { + ccipDONs[registrysyncer.DonID(don.Id)] = don + } + } + } + + return ccipDONs, nil +} + +// checkCapabilityPresence checks if the capability with the given version and +// labelled name is present in the given capability registry state. +func checkCapabilityPresence( + capabilityVersion, + capabilityLabelledName string, + state registrysyncer.State, +) (kcr.CapabilitiesRegistryCapabilityInfo, error) { + // Sanity check to make sure the capability registry has the capability we are looking for. + hid, err := hashedCapabilityId(capabilityLabelledName, capabilityVersion) + if err != nil { + return kcr.CapabilitiesRegistryCapabilityInfo{}, fmt.Errorf("failed to hash capability id: %w", err) + } + ccipCapability, ok := state.IDsToCapabilities[hid] + if !ok { + return kcr.CapabilitiesRegistryCapabilityInfo{}, + fmt.Errorf("failed to find capability with name %s and version %s in capability registry state", + capabilityLabelledName, capabilityVersion) + } + + return ccipCapability, nil +} + +// hashedCapabilityId returns the hashed capability id in a manner equivalent to the capability registry. +func hashedCapabilityId(capabilityLabelledName, capabilityVersion string) (r [32]byte, err error) { + tabi := `[{"type": "string"}, {"type": "string"}]` + abiEncoded, err := utils.ABIEncode(tabi, capabilityLabelledName, capabilityVersion) + if err != nil { + return r, fmt.Errorf("failed to ABI encode capability version and labelled name: %w", err) + } + + h := crypto.Keccak256(abiEncoded) + copy(r[:], h) + return r, nil +} + +// mustHashedCapabilityId is a helper function that panics if the hashedCapabilityId function returns an error. +// should only use in tests. +func mustHashedCapabilityId(capabilityLabelledName, capabilityVersion string) [32]byte { + r, err := hashedCapabilityId(capabilityLabelledName, capabilityVersion) + if err != nil { + panic(err) + } + return r +} + +// isMemberOfDON returns true if and only if the given p2pID is a member of the given DON. +func isMemberOfDON(don kcr.CapabilitiesRegistryDONInfo, p2pID ragep2ptypes.PeerID) bool { + for _, node := range don.NodeP2PIds { + if node == p2pID { + return true + } + } + return false +} + +// isMemberOfBootstrapSubcommittee returns true if and only if the given p2pID is a member of the given bootstrap subcommittee. +func isMemberOfBootstrapSubcommittee( + bootstrapP2PIDs [][32]byte, + p2pID ragep2ptypes.PeerID, +) bool { + for _, bootstrapID := range bootstrapP2PIDs { + if bootstrapID == p2pID { + return true + } + } + return false +} diff --git a/core/services/ccipcapability/launcher/diff_test.go b/core/services/ccipcapability/launcher/diff_test.go new file mode 100644 index 0000000000..c7a54cad2f --- /dev/null +++ b/core/services/ccipcapability/launcher/diff_test.go @@ -0,0 +1,508 @@ +package launcher + +import ( + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" + "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" + "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" +) + +func Test_diff(t *testing.T) { + type args struct { + capabilityVersion string + capabilityLabelledName string + oldState registrysyncer.State + newState registrysyncer.State + } + tests := []struct { + name string + args args + want diffResult + wantErr bool + }{ + { + "no diff", + args{ + capabilityVersion: "v1.0.0", + capabilityLabelledName: "ccip", + oldState: registrysyncer.State{ + IDsToCapabilities: map[registrysyncer.HashedCapabilityID]kcr.CapabilitiesRegistryCapabilityInfo{ + mustHashedCapabilityId("ccip", "v1.0.0"): { + LabelledName: "ccip", + Version: "v1.0.0", + }, + }, + IDsToDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + CapabilityConfigurations: []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: mustHashedCapabilityId("ccip", "v1.0.0"), + }, + }, + }, + }, + IDsToNodes: map[types.PeerID]kcr.CapabilitiesRegistryNodeInfo{}, + }, + newState: registrysyncer.State{ + IDsToCapabilities: map[registrysyncer.HashedCapabilityID]kcr.CapabilitiesRegistryCapabilityInfo{ + mustHashedCapabilityId("ccip", "v1.0.0"): { + LabelledName: "ccip", + Version: "v1.0.0", + }, + }, + IDsToDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + CapabilityConfigurations: []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: mustHashedCapabilityId("ccip", "v1.0.0"), + }, + }, + }, + }, + IDsToNodes: map[types.PeerID]kcr.CapabilitiesRegistryNodeInfo{}, + }, + }, + diffResult{ + added: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + removed: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + updated: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + }, + false, + }, + { + "capability not present", + args{ + capabilityVersion: "v1.0.0", + capabilityLabelledName: "ccip", + oldState: registrysyncer.State{ + IDsToCapabilities: map[registrysyncer.HashedCapabilityID]kcr.CapabilitiesRegistryCapabilityInfo{ + mustHashedCapabilityId("ccip", "v1.1.0"): { + LabelledName: "ccip", + Version: "v1.1.0", + }, + }, + }, + newState: registrysyncer.State{ + IDsToCapabilities: map[registrysyncer.HashedCapabilityID]kcr.CapabilitiesRegistryCapabilityInfo{ + mustHashedCapabilityId("ccip", "v1.1.0"): { + LabelledName: "ccip", + Version: "v1.1.0", + }, + }, + }, + }, + diffResult{}, + true, + }, + { + "diff present, new don", + args{ + capabilityVersion: "v1.0.0", + capabilityLabelledName: "ccip", + oldState: registrysyncer.State{ + IDsToCapabilities: map[registrysyncer.HashedCapabilityID]kcr.CapabilitiesRegistryCapabilityInfo{ + mustHashedCapabilityId("ccip", "v1.0.0"): { + LabelledName: "ccip", + Version: "v1.0.0", + }, + }, + IDsToDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + }, + newState: registrysyncer.State{ + IDsToCapabilities: map[registrysyncer.HashedCapabilityID]kcr.CapabilitiesRegistryCapabilityInfo{ + mustHashedCapabilityId("ccip", "v1.0.0"): { + LabelledName: "ccip", + Version: "v1.0.0", + }, + }, + IDsToDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + CapabilityConfigurations: []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: mustHashedCapabilityId("ccip", "v1.0.0"), + }, + }, + }, + }, + }, + }, + diffResult{ + added: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + CapabilityConfigurations: []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: mustHashedCapabilityId("ccip", "v1.0.0"), + }, + }, + }, + }, + removed: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + updated: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := diff(tt.args.capabilityVersion, tt.args.capabilityLabelledName, tt.args.oldState, tt.args.newState) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tt.want, got) + } + }) + } +} + +func Test_compareDONs(t *testing.T) { + type args struct { + currCCIPDONs map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo + newCCIPDONs map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo + } + tests := []struct { + name string + args args + wantAdded map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo + wantRemoved map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo + wantUpdated map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo + wantErr bool + }{ + { + "added dons", + args{ + currCCIPDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + newCCIPDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + }, + }, + }, + map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + }, + }, + map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + false, + }, + { + "removed dons", + args{ + currCCIPDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + }, + }, + newCCIPDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + }, + map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + }, + }, + map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + false, + }, + { + "updated dons", + args{ + currCCIPDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + ConfigCount: 1, + }, + }, + newCCIPDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + ConfigCount: 2, + }, + }, + }, + map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + ConfigCount: 2, + }, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dr, err := compareDONs(tt.args.currCCIPDONs, tt.args.newCCIPDONs) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tt.wantAdded, dr.added) + require.Equal(t, tt.wantRemoved, dr.removed) + require.Equal(t, tt.wantUpdated, dr.updated) + } + }) + } +} + +func Test_filterCCIPDONs(t *testing.T) { + type args struct { + ccipCapability kcr.CapabilitiesRegistryCapabilityInfo + state registrysyncer.State + } + tests := []struct { + name string + args args + want map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo + wantErr bool + }{ + { + "one ccip don", + args{ + ccipCapability: kcr.CapabilitiesRegistryCapabilityInfo{ + LabelledName: "ccip", + Version: "v1.0.0", + }, + state: registrysyncer.State{ + IDsToDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + CapabilityConfigurations: []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: mustHashedCapabilityId("ccip", "v1.0.0"), + }, + }, + }, + }, + }, + }, + map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + CapabilityConfigurations: []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: mustHashedCapabilityId("ccip", "v1.0.0"), + }, + }, + }, + }, + false, + }, + { + "no ccip dons", + args{ + ccipCapability: kcr.CapabilitiesRegistryCapabilityInfo{ + LabelledName: "ccip", + Version: "v1.0.0", + }, + state: registrysyncer.State{ + IDsToDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + CapabilityConfigurations: []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: mustHashedCapabilityId("ccip", "v1.1.0"), + }, + }, + }, + }, + }, + }, + map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + false, + }, + { + "don with multiple capabilities, one of them ccip", + args{ + ccipCapability: kcr.CapabilitiesRegistryCapabilityInfo{ + LabelledName: "ccip", + Version: "v1.0.0", + }, + state: registrysyncer.State{ + IDsToDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + CapabilityConfigurations: []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: mustHashedCapabilityId("ccip", "v1.0.0"), + }, + { + CapabilityId: mustHashedCapabilityId("ccip", "v1.1.0"), + }, + }, + }, + }, + }, + }, + map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + CapabilityConfigurations: []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: mustHashedCapabilityId("ccip", "v1.0.0"), + }, + { + CapabilityId: mustHashedCapabilityId("ccip", "v1.1.0"), + }, + }, + }, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := filterCCIPDONs(tt.args.ccipCapability, tt.args.state) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tt.want, got) + } + }) + } +} + +func Test_checkCapabilityPresence(t *testing.T) { + type args struct { + capabilityVersion string + capabilityLabelledName string + state registrysyncer.State + } + tests := []struct { + name string + args args + want kcr.CapabilitiesRegistryCapabilityInfo + wantErr bool + }{ + { + "in registry state", + args{ + capabilityVersion: "v1.0.0", + capabilityLabelledName: "ccip", + state: registrysyncer.State{ + IDsToCapabilities: map[registrysyncer.HashedCapabilityID]kcr.CapabilitiesRegistryCapabilityInfo{ + mustHashedCapabilityId("ccip", "v1.0.0"): { + LabelledName: "ccip", + Version: "v1.0.0", + }, + mustHashedCapabilityId("ccip", "v1.1.0"): { + LabelledName: "ccip", + Version: "v1.1.0", + }, + }, + }, + }, + kcr.CapabilitiesRegistryCapabilityInfo{ + LabelledName: "ccip", + Version: "v1.0.0", + }, + false, + }, + { + "not in registry state", + args{ + capabilityVersion: "v1.0.0", + capabilityLabelledName: "ccip", + state: registrysyncer.State{ + IDsToCapabilities: map[registrysyncer.HashedCapabilityID]kcr.CapabilitiesRegistryCapabilityInfo{ + mustHashedCapabilityId("ccip", "v1.1.0"): { + LabelledName: "ccip", + Version: "v1.1.0", + }, + }, + }, + }, + kcr.CapabilitiesRegistryCapabilityInfo{}, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := checkCapabilityPresence(tt.args.capabilityVersion, tt.args.capabilityLabelledName, tt.args.state) + if (err != nil) != tt.wantErr { + t.Errorf("checkCapabilityPresence() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("checkCapabilityPresence() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_hashedCapabilityId(t *testing.T) { + transactor := testutils.MustNewSimTransactor(t) + sb := backends.NewSimulatedBackend(core.GenesisAlloc{ + transactor.From: {Balance: assets.Ether(1000).ToInt()}, + }, 30e6) + + crAddress, _, _, err := kcr.DeployCapabilitiesRegistry(transactor, sb) + require.NoError(t, err) + sb.Commit() + + cr, err := kcr.NewCapabilitiesRegistry(crAddress, sb) + require.NoError(t, err) + + // add a capability, ignore cap config for simplicity. + _, err = cr.AddCapabilities(transactor, []kcr.CapabilitiesRegistryCapability{ + { + LabelledName: "ccip", + Version: "v1.0.0", + CapabilityType: 0, + ResponseType: 0, + ConfigurationContract: common.Address{}, + }, + }) + require.NoError(t, err) + sb.Commit() + + hidExpected, err := cr.GetHashedCapabilityId(nil, "ccip", "v1.0.0") + require.NoError(t, err) + + hid, err := hashedCapabilityId("ccip", "v1.0.0") + require.NoError(t, err) + + require.Equal(t, hidExpected, hid) +} + +func Test_isMemberOfDON(t *testing.T) { + var p2pIDs [][32]byte + for i := range [4]struct{}{} { + p2pIDs = append(p2pIDs, p2pkey.MustNewV2XXXTestingOnly(big.NewInt(int64(i+1))).PeerID()) + } + don := kcr.CapabilitiesRegistryDONInfo{ + Id: 1, + NodeP2PIds: p2pIDs, + } + require.True(t, isMemberOfDON(don, ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID()))) + require.False(t, isMemberOfDON(don, ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(5)).PeerID()))) +} + +func Test_isMemberOfBootstrapSubcommittee(t *testing.T) { + var bootstrapKeys [][32]byte + for i := range [4]struct{}{} { + bootstrapKeys = append(bootstrapKeys, p2pkey.MustNewV2XXXTestingOnly(big.NewInt(int64(i+1))).PeerID()) + } + require.True(t, isMemberOfBootstrapSubcommittee(bootstrapKeys, ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID()))) + require.False(t, isMemberOfBootstrapSubcommittee(bootstrapKeys, ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(5)).PeerID()))) +} diff --git a/core/services/ccipcapability/launcher/integration_test.go b/core/services/ccipcapability/launcher/integration_test.go new file mode 100644 index 0000000000..da04ef4cbc --- /dev/null +++ b/core/services/ccipcapability/launcher/integration_test.go @@ -0,0 +1,112 @@ +package launcher + +import ( + "testing" + "time" + + "github.com/onsi/gomega" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + cctypes "github.com/smartcontractkit/chainlink/v2/core/services/ccipcapability/types" + it "github.com/smartcontractkit/chainlink/v2/core/services/ocr3/plugins/ccip_integration_tests" + "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" +) + +func TestIntegration_Launcher(t *testing.T) { + ctx := testutils.Context(t) + lggr := logger.TestLogger(t) + uni := it.NewTestUniverse(ctx, t, lggr) + // We need 3*f + 1 p2pIDs to have enough nodes to bootstrap + var arr []int64 + n := int(it.FChainA*3 + 1) + for i := 0; i <= n; i++ { + arr = append(arr, int64(i)) + } + p2pIDs := it.P2pIDsFromInts(arr) + uni.AddCapability(p2pIDs) + + regSyncer, err := registrysyncer.New(lggr, uni, uni.CapReg.Address().String()) + require.NoError(t, err) + + hcr := uni.HomeChainReader + launcher := New( + it.CcipCapabilityVersion, + it.CcipCapabilityLabelledName, + p2pIDs[0], + logger.TestLogger(t), + hcr, + &oracleCreatorPrints{ + t: t, + }, + 1*time.Second, + ) + regSyncer.AddLauncher(launcher) + + require.NoError(t, launcher.Start(ctx)) + require.NoError(t, regSyncer.Start(ctx)) + t.Cleanup(func() { require.NoError(t, regSyncer.Close()) }) + t.Cleanup(func() { require.NoError(t, launcher.Close()) }) + + chainAConf := it.SetupConfigInfo(it.ChainA, p2pIDs, it.FChainA, []byte("ChainA")) + chainBConf := it.SetupConfigInfo(it.ChainB, p2pIDs[1:], it.FChainB, []byte("ChainB")) + chainCConf := it.SetupConfigInfo(it.ChainC, p2pIDs[2:], it.FChainC, []byte("ChainC")) + inputConfig := []ccip_config.CCIPConfigTypesChainConfigInfo{ + chainAConf, + chainBConf, + chainCConf, + } + _, err = uni.CcipCfg.ApplyChainConfigUpdates(uni.Transactor, nil, inputConfig) + require.NoError(t, err) + uni.Backend.Commit() + + ccipCapabilityID, err := uni.CapReg.GetHashedCapabilityId(nil, it.CcipCapabilityLabelledName, it.CcipCapabilityVersion) + require.NoError(t, err) + + uni.AddDONToRegistry( + ccipCapabilityID, + it.ChainA, + it.FChainA, + p2pIDs[1], + p2pIDs) + + gomega.NewWithT(t).Eventually(func() bool { + return len(launcher.runningDONIDs()) == 1 + }, testutils.WaitTimeout(t), testutils.TestInterval).Should(gomega.BeTrue()) +} + +type oraclePrints struct { + t *testing.T + pluginType cctypes.PluginType + config cctypes.OCR3ConfigWithMeta + isBootstrap bool +} + +func (o *oraclePrints) Start() error { + o.t.Logf("Starting oracle (pluginType: %s, isBootstrap: %t) with config %+v\n", o.pluginType, o.isBootstrap, o.config) + return nil +} + +func (o *oraclePrints) Close() error { + o.t.Logf("Closing oracle (pluginType: %s, isBootstrap: %t) with config %+v\n", o.pluginType, o.isBootstrap, o.config) + return nil +} + +type oracleCreatorPrints struct { + t *testing.T +} + +func (o *oracleCreatorPrints) CreatePluginOracle(pluginType cctypes.PluginType, config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { + o.t.Logf("Creating plugin oracle (pluginType: %s) with config %+v\n", pluginType, config) + return &oraclePrints{pluginType: pluginType, config: config, t: o.t}, nil +} + +func (o *oracleCreatorPrints) CreateBootstrapOracle(config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { + o.t.Logf("Creating bootstrap oracle with config %+v\n", config) + return &oraclePrints{pluginType: cctypes.PluginTypeCCIPCommit, config: config, isBootstrap: true, t: o.t}, nil +} + +var _ cctypes.OracleCreator = &oracleCreatorPrints{} +var _ cctypes.CCIPOracle = &oraclePrints{} diff --git a/core/services/ccipcapability/launcher/launcher.go b/core/services/ccipcapability/launcher/launcher.go new file mode 100644 index 0000000000..66aaef8cc0 --- /dev/null +++ b/core/services/ccipcapability/launcher/launcher.go @@ -0,0 +1,434 @@ +package launcher + +import ( + "context" + "fmt" + "sync" + "time" + + "go.uber.org/multierr" + + ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" + + ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" + "github.com/smartcontractkit/chainlink-common/pkg/services" + + ccipreader "github.com/smartcontractkit/chainlink-ccip/pkg/reader" + kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" + "github.com/smartcontractkit/chainlink/v2/core/logger" + cctypes "github.com/smartcontractkit/chainlink/v2/core/services/ccipcapability/types" + "github.com/smartcontractkit/chainlink/v2/core/services/job" + p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" + "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" +) + +var ( + _ job.ServiceCtx = (*launcher)(nil) + _ registrysyncer.Launcher = (*launcher)(nil) +) + +func New( + capabilityVersion, + capabilityLabelledName string, + p2pID ragep2ptypes.PeerID, + lggr logger.Logger, + homeChainReader ccipreader.HomeChain, + oracleCreator cctypes.OracleCreator, + tickInterval time.Duration, +) *launcher { + return &launcher{ + capabilityVersion: capabilityVersion, + capabilityLabelledName: capabilityLabelledName, + p2pID: p2pID, + lggr: lggr, + homeChainReader: homeChainReader, + regState: registrysyncer.State{ + IDsToDONs: make(map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo), + IDsToNodes: make(map[p2ptypes.PeerID]kcr.CapabilitiesRegistryNodeInfo), + IDsToCapabilities: make(map[registrysyncer.HashedCapabilityID]kcr.CapabilitiesRegistryCapabilityInfo), + }, + oracleCreator: oracleCreator, + dons: make(map[registrysyncer.DonID]*ccipDeployment), + tickInterval: tickInterval, + } +} + +// launcher manages the lifecycles of the CCIP capability on all chains. +type launcher struct { + services.StateMachine + + capabilityVersion string + capabilityLabelledName string + p2pID ragep2ptypes.PeerID + lggr logger.Logger + homeChainReader ccipreader.HomeChain + stopChan chan struct{} + // latestState is the latest capability registry state received from the syncer. + latestState registrysyncer.State + // regState is the latest capability registry state that we have successfully processed. + regState registrysyncer.State + oracleCreator cctypes.OracleCreator + lock sync.RWMutex + wg sync.WaitGroup + tickInterval time.Duration + + // dons is a map of CCIP DON IDs to the OCR instances that are running on them. + // we can have up to two OCR instances per CCIP plugin, since we are running two plugins, + // thats four OCR instances per CCIP DON maximum. + dons map[registrysyncer.DonID]*ccipDeployment +} + +// Launch implements registrysyncer.Launcher. +func (l *launcher) Launch(ctx context.Context, state registrysyncer.State) error { + l.lock.Lock() + defer l.lock.Unlock() + l.lggr.Debugw("Received new state from syncer", "dons", state.IDsToDONs) + l.latestState = state + return nil +} + +func (l *launcher) getLatestState() registrysyncer.State { + l.lock.RLock() + defer l.lock.RUnlock() + return l.latestState +} + +func (l *launcher) runningDONIDs() []registrysyncer.DonID { + l.lock.RLock() + defer l.lock.RUnlock() + var runningDONs []registrysyncer.DonID + for id := range l.dons { + runningDONs = append(runningDONs, id) + } + return runningDONs +} + +// Close implements job.ServiceCtx. +func (l *launcher) Close() error { + return l.StateMachine.StopOnce("launcher", func() error { + // shut down the monitor goroutine. + close(l.stopChan) + l.wg.Wait() + + // shut down all running oracles. + var err error + for _, ceDep := range l.dons { + err = multierr.Append(err, ceDep.Close()) + } + + return err + }) +} + +// Start implements job.ServiceCtx. +func (l *launcher) Start(context.Context) error { + return l.StartOnce("launcher", func() error { + l.stopChan = make(chan struct{}) + l.wg.Add(1) + go l.monitor() + return nil + }) +} + +func (l *launcher) monitor() { + defer l.wg.Done() + ticker := time.NewTicker(l.tickInterval) + for { + select { + case <-l.stopChan: + return + case <-ticker.C: + if err := l.tick(); err != nil { + l.lggr.Errorw("Failed to tick", "err", err) + } + } + } +} + +func (l *launcher) tick() error { + // Ensure that the home chain reader is healthy. + // For new jobs it may be possible that the home chain reader is not yet ready + // so we won't be able to fetch configs and start any OCR instances. + if ready := l.homeChainReader.Ready(); ready != nil { + return fmt.Errorf("home chain reader is not ready: %w", ready) + } + + // Fetch the latest state from the capability registry and determine if we need to + // launch or update any OCR instances. + latestState := l.getLatestState() + + diffRes, err := diff(l.capabilityVersion, l.capabilityLabelledName, l.regState, latestState) + if err != nil { + return fmt.Errorf("failed to diff capability registry states: %w", err) + } + + err = l.processDiff(diffRes) + if err != nil { + return fmt.Errorf("failed to process diff: %w", err) + } + + return nil +} + +// processDiff processes the diff between the current and latest capability registry states. +// for any added OCR instances, it will launch them. +// for any removed OCR instances, it will shut them down. +// for any updated OCR instances, it will restart them with the new configuration. +func (l *launcher) processDiff(diff diffResult) error { + err := l.processRemoved(diff.removed) + err = multierr.Append(err, l.processAdded(diff.added)) + err = multierr.Append(err, l.processUpdate(diff.updated)) + + return err +} + +func (l *launcher) processUpdate(updated map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo) error { + l.lock.Lock() + defer l.lock.Unlock() + + for donID, don := range updated { + prevDeployment, ok := l.dons[registrysyncer.DonID(don.Id)] + if !ok { + return fmt.Errorf("invariant violation: expected to find CCIP DON %d in the map of running deployments", don.Id) + } + + futDeployment, err := updateDON( + l.lggr, + l.p2pID, + l.homeChainReader, + l.oracleCreator, + *prevDeployment, + don, + ) + if err != nil { + return err + } + if err := futDeployment.HandleBlueGreen(prevDeployment); err != nil { + // TODO: how to handle a failed blue-green deployment? + return fmt.Errorf("failed to handle blue-green deployment for CCIP DON %d: %w", donID, err) + } + + // update state. + l.dons[donID] = futDeployment + // update the state with the latest config. + // this way if one of the starts errors, we don't retry all of them. + l.regState.IDsToDONs[donID] = updated[donID] + } + + return nil +} + +func (l *launcher) processAdded(added map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo) error { + l.lock.Lock() + defer l.lock.Unlock() + + for donID, don := range added { + dep, err := createDON( + l.lggr, + l.p2pID, + l.homeChainReader, + l.oracleCreator, + don, + ) + if err != nil { + return err + } + if dep == nil { + // not a member of this DON. + continue + } + + if err := dep.StartBlue(); err != nil { + if shutdownErr := dep.CloseBlue(); shutdownErr != nil { + l.lggr.Errorw("Failed to shutdown blue instance after failed start", "donId", donID, "err", shutdownErr) + } + return fmt.Errorf("failed to start oracles for CCIP DON %d: %w", donID, err) + } + + // update state. + l.dons[donID] = dep + // update the state with the latest config. + // this way if one of the starts errors, we don't retry all of them. + l.regState.IDsToDONs[donID] = added[donID] + } + + return nil +} + +func (l *launcher) processRemoved(removed map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo) error { + l.lock.Lock() + defer l.lock.Unlock() + + for id := range removed { + ceDep, ok := l.dons[id] + if !ok { + // not running this particular DON. + continue + } + + if err := ceDep.Close(); err != nil { + return fmt.Errorf("failed to shutdown oracles for CCIP DON %d: %w", id, err) + } + + // after a successful shutdown we can safely remove the DON deployment from the map. + delete(l.dons, id) + delete(l.regState.IDsToDONs, id) + } + + return nil +} + +// updateDON is a pure function that handles the case where a DON in the capability registry +// has received a new configuration. +// It returns a new ccipDeployment that can then be used to perform the blue-green deployment, +// based on the previous deployment. +func updateDON( + lggr logger.Logger, + p2pID ragep2ptypes.PeerID, + homeChainReader ccipreader.HomeChain, + oracleCreator cctypes.OracleCreator, + prevDeployment ccipDeployment, + don kcr.CapabilitiesRegistryDONInfo, +) (futDeployment *ccipDeployment, err error) { + if !isMemberOfDON(don, p2pID) { + lggr.Infow("Not a member of this DON, skipping", "donId", don.Id, "p2pId", p2pID.String()) + return nil, nil + } + + // this should be a retryable error. + commitOCRConfigs, err := homeChainReader.GetOCRConfigs(context.Background(), don.Id, uint8(cctypes.PluginTypeCCIPCommit)) + if err != nil { + return nil, fmt.Errorf("failed to fetch OCR configs for CCIP commit plugin (don id: %d) from home chain config contract: %w", + don.Id, err) + } + + execOCRConfigs, err := homeChainReader.GetOCRConfigs(context.Background(), don.Id, uint8(cctypes.PluginTypeCCIPExec)) + if err != nil { + return nil, fmt.Errorf("failed to fetch OCR configs for CCIP exec plugin (don id: %d) from home chain config contract: %w", + don.Id, err) + } + + commitBgd, err := createFutureBlueGreenDeployment(prevDeployment, commitOCRConfigs, oracleCreator, cctypes.PluginTypeCCIPCommit) + if err != nil { + return nil, fmt.Errorf("failed to create future blue-green deployment for CCIP commit plugin: %w, don id: %d", err, don.Id) + } + + execBgd, err := createFutureBlueGreenDeployment(prevDeployment, execOCRConfigs, oracleCreator, cctypes.PluginTypeCCIPExec) + if err != nil { + return nil, fmt.Errorf("failed to create future blue-green deployment for CCIP exec plugin: %w, don id: %d", err, don.Id) + } + + return &ccipDeployment{ + commit: commitBgd, + exec: execBgd, + }, nil +} + +// valid cases: +// a) len(ocrConfigs) == 2 && !prevDeployment.HasGreenInstance(pluginType): this is a new green instance. +// b) len(ocrConfigs) == 1 && prevDeployment.HasGreenInstance(): this is a promotion of green->blue. +// All other cases are invalid. This is enforced in the ccip config contract. +func createFutureBlueGreenDeployment( + prevDeployment ccipDeployment, + ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta, + oracleCreator cctypes.OracleCreator, + pluginType cctypes.PluginType, +) (blueGreenDeployment, error) { + var deployment blueGreenDeployment + if isNewGreenInstance(pluginType, ocrConfigs, prevDeployment) { + // this is a new green instance. + greenOracle, err := oracleCreator.CreatePluginOracle(pluginType, cctypes.OCR3ConfigWithMeta(ocrConfigs[1])) + if err != nil { + return blueGreenDeployment{}, fmt.Errorf("failed to create CCIP commit oracle: %w", err) + } + + deployment.blue = prevDeployment.commit.blue + deployment.green = greenOracle + } else if isPromotion(pluginType, ocrConfigs, prevDeployment) { + // this is a promotion of green->blue. + deployment.blue = prevDeployment.commit.green + } else { + return blueGreenDeployment{}, fmt.Errorf("invariant violation: expected 1 or 2 OCR configs for CCIP plugin (type: %d), got %d", pluginType, len(ocrConfigs)) + } + + return deployment, nil +} + +// createDON is a pure function that handles the case where a new DON is added to the capability registry. +// It returns a new ccipDeployment that can then be used to start the blue instance. +func createDON( + lggr logger.Logger, + p2pID ragep2ptypes.PeerID, + homeChainReader ccipreader.HomeChain, + oracleCreator cctypes.OracleCreator, + don kcr.CapabilitiesRegistryDONInfo, +) (*ccipDeployment, error) { + if !isMemberOfDON(don, p2pID) { + lggr.Infow("Not a member of this DON, skipping", "donId", don.Id, "p2pId", p2pID.String()) + return nil, nil + } + + // this should be a retryable error. + commitOCRConfigs, err := homeChainReader.GetOCRConfigs(context.Background(), don.Id, uint8(cctypes.PluginTypeCCIPCommit)) + if err != nil { + return nil, fmt.Errorf("failed to fetch OCR configs for CCIP commit plugin (don id: %d) from home chain config contract: %w", + don.Id, err) + } + + execOCRConfigs, err := homeChainReader.GetOCRConfigs(context.Background(), don.Id, uint8(cctypes.PluginTypeCCIPExec)) + if err != nil { + return nil, fmt.Errorf("failed to fetch OCR configs for CCIP exec plugin (don id: %d) from home chain config contract: %w", + don.Id, err) + } + + // upon creation we should only have one OCR config per plugin type. + if len(commitOCRConfigs) != 1 { + return nil, fmt.Errorf("expected exactly one OCR config for CCIP commit plugin (don id: %d), got %d", don.Id, len(commitOCRConfigs)) + } + + if len(execOCRConfigs) != 1 { + return nil, fmt.Errorf("expected exactly one OCR config for CCIP exec plugin (don id: %d), got %d", don.Id, len(execOCRConfigs)) + } + + commitOracle, commitBootstrap, err := createOracle(p2pID, oracleCreator, cctypes.PluginTypeCCIPCommit, commitOCRConfigs) + if err != nil { + return nil, fmt.Errorf("failed to create CCIP commit oracle: %w", err) + } + + execOracle, execBootstrap, err := createOracle(p2pID, oracleCreator, cctypes.PluginTypeCCIPExec, execOCRConfigs) + if err != nil { + return nil, fmt.Errorf("failed to create CCIP exec oracle: %w", err) + } + + return &ccipDeployment{ + commit: blueGreenDeployment{ + blue: commitOracle, + bootstrapBlue: commitBootstrap, + }, + exec: blueGreenDeployment{ + blue: execOracle, + bootstrapBlue: execBootstrap, + }, + }, nil +} + +func createOracle( + p2pID ragep2ptypes.PeerID, + oracleCreator cctypes.OracleCreator, + pluginType cctypes.PluginType, + ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta, +) (pluginOracle, bootstrapOracle cctypes.CCIPOracle, err error) { + pluginOracle, err = oracleCreator.CreatePluginOracle(pluginType, cctypes.OCR3ConfigWithMeta(ocrConfigs[0])) + if err != nil { + return nil, nil, fmt.Errorf("failed to create CCIP plugin oracle (plugintype: %d): %w", pluginType, err) + } + + if isMemberOfBootstrapSubcommittee(ocrConfigs[0].Config.BootstrapP2PIds, p2pID) { + bootstrapOracle, err = oracleCreator.CreateBootstrapOracle(cctypes.OCR3ConfigWithMeta(ocrConfigs[0])) + if err != nil { + return nil, nil, fmt.Errorf("failed to create CCIP bootstrap oracle (plugintype: %d): %w", pluginType, err) + } + } + + return pluginOracle, bootstrapOracle, nil +} diff --git a/core/services/ccipcapability/launcher/launcher_test.go b/core/services/ccipcapability/launcher/launcher_test.go new file mode 100644 index 0000000000..6f8fa7b664 --- /dev/null +++ b/core/services/ccipcapability/launcher/launcher_test.go @@ -0,0 +1,495 @@ +package launcher + +import ( + "errors" + "math/big" + "reflect" + "testing" + + ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" + + kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + cctypes "github.com/smartcontractkit/chainlink/v2/core/services/ccipcapability/types" + mockcctypes "github.com/smartcontractkit/chainlink/v2/core/services/ccipcapability/types/mocks" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" + "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" +) + +func Test_createOracle(t *testing.T) { + var p2pKeys []ragep2ptypes.PeerID + for i := 0; i < 3; i++ { + p2pKeys = append(p2pKeys, ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(int64(i+1))).PeerID())) + } + myP2PKey := p2pKeys[0] + type args struct { + p2pID ragep2ptypes.PeerID + oracleCreator *mockcctypes.OracleCreator + pluginType cctypes.PluginType + ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta + } + tests := []struct { + name string + args args + expect func(t *testing.T, args args, oracleCreator *mockcctypes.OracleCreator) + wantErr bool + }{ + { + "success, no bootstrap", + args{ + myP2PKey, + mockcctypes.NewOracleCreator(t), + cctypes.PluginTypeCCIPCommit, + []ccipreaderpkg.OCR3ConfigWithMeta{ + { + Config: ccipreaderpkg.OCR3Config{}, + ConfigCount: 1, + ConfigDigest: testutils.Random32Byte(), + }, + }, + }, + func(t *testing.T, args args, oracleCreator *mockcctypes.OracleCreator) { + oracleCreator. + On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, cctypes.OCR3ConfigWithMeta(args.ocrConfigs[0])). + Return(mockcctypes.NewCCIPOracle(t), nil) + }, + false, + }, + { + "success, with bootstrap", + args{ + myP2PKey, + mockcctypes.NewOracleCreator(t), + cctypes.PluginTypeCCIPCommit, + []ccipreaderpkg.OCR3ConfigWithMeta{ + { + Config: ccipreaderpkg.OCR3Config{ + BootstrapP2PIds: [][32]byte{myP2PKey}, + }, + ConfigCount: 1, + ConfigDigest: testutils.Random32Byte(), + }, + }, + }, + func(t *testing.T, args args, oracleCreator *mockcctypes.OracleCreator) { + oracleCreator. + On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, cctypes.OCR3ConfigWithMeta(args.ocrConfigs[0])). + Return(mockcctypes.NewCCIPOracle(t), nil) + oracleCreator. + On("CreateBootstrapOracle", cctypes.OCR3ConfigWithMeta(args.ocrConfigs[0])). + Return(mockcctypes.NewCCIPOracle(t), nil) + }, + false, + }, + { + "error creating plugin oracle", + args{ + myP2PKey, + mockcctypes.NewOracleCreator(t), + cctypes.PluginTypeCCIPCommit, + []ccipreaderpkg.OCR3ConfigWithMeta{ + { + Config: ccipreaderpkg.OCR3Config{}, + ConfigCount: 1, + ConfigDigest: testutils.Random32Byte(), + }, + }, + }, + func(t *testing.T, args args, oracleCreator *mockcctypes.OracleCreator) { + oracleCreator. + On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, cctypes.OCR3ConfigWithMeta(args.ocrConfigs[0])). + Return(nil, errors.New("error creating oracle")) + }, + true, + }, + { + "error creating bootstrap oracle", + args{ + myP2PKey, + mockcctypes.NewOracleCreator(t), + cctypes.PluginTypeCCIPCommit, + []ccipreaderpkg.OCR3ConfigWithMeta{ + { + Config: ccipreaderpkg.OCR3Config{ + BootstrapP2PIds: [][32]byte{myP2PKey}, + }, + ConfigCount: 1, + ConfigDigest: testutils.Random32Byte(), + }, + }, + }, + func(t *testing.T, args args, oracleCreator *mockcctypes.OracleCreator) { + oracleCreator. + On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, cctypes.OCR3ConfigWithMeta(args.ocrConfigs[0])). + Return(mockcctypes.NewCCIPOracle(t), nil) + oracleCreator. + On("CreateBootstrapOracle", cctypes.OCR3ConfigWithMeta(args.ocrConfigs[0])). + Return(nil, errors.New("error creating oracle")) + }, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.expect(t, tt.args, tt.args.oracleCreator) + _, _, err := createOracle(tt.args.p2pID, tt.args.oracleCreator, tt.args.pluginType, tt.args.ocrConfigs) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func Test_createDON(t *testing.T) { + type args struct { + lggr logger.Logger + p2pID ragep2ptypes.PeerID + homeChainReader *mockcctypes.HomeChainReader + oracleCreator *mockcctypes.OracleCreator + don kcr.CapabilitiesRegistryDONInfo + } + tests := []struct { + name string + args args + expect func(t *testing.T, args args, oracleCreator *mockcctypes.OracleCreator, homeChainReader *mockcctypes.HomeChainReader) + wantErr bool + }{ + { + "not a member of the DON", + args{ + logger.TestLogger(t), + ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID()), + mockcctypes.NewHomeChainReader(t), + mockcctypes.NewOracleCreator(t), + kcr.CapabilitiesRegistryDONInfo{ + NodeP2PIds: [][32]byte{ + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(2)).PeerID(), + }, + Id: 2, + }, + }, + func(t *testing.T, args args, oracleCreator *mockcctypes.OracleCreator, homeChainReader *mockcctypes.HomeChainReader) { + }, + false, + }, + { + "success, no bootstrap", + args{ + logger.TestLogger(t), + ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID()), + mockcctypes.NewHomeChainReader(t), + mockcctypes.NewOracleCreator(t), + kcr.CapabilitiesRegistryDONInfo{ + NodeP2PIds: [][32]byte{ + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID(), + }, + Id: 1, + }, + }, + func(t *testing.T, args args, oracleCreator *mockcctypes.OracleCreator, homeChainReader *mockcctypes.HomeChainReader) { + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPCommit)). + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{}}, nil) + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPExec)). + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{}}, nil) + oracleCreator. + On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, mock.Anything). + Return(mockcctypes.NewCCIPOracle(t), nil) + oracleCreator. + On("CreatePluginOracle", cctypes.PluginTypeCCIPExec, mock.Anything). + Return(mockcctypes.NewCCIPOracle(t), nil) + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.expect != nil { + tt.expect(t, tt.args, tt.args.oracleCreator, tt.args.homeChainReader) + } + _, err := createDON(tt.args.lggr, tt.args.p2pID, tt.args.homeChainReader, tt.args.oracleCreator, tt.args.don) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func Test_createFutureBlueGreenDeployment(t *testing.T) { + type args struct { + prevDeployment ccipDeployment + ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta + oracleCreator *mockcctypes.OracleCreator + pluginType cctypes.PluginType + } + tests := []struct { + name string + args args + want blueGreenDeployment + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := createFutureBlueGreenDeployment(tt.args.prevDeployment, tt.args.ocrConfigs, tt.args.oracleCreator, tt.args.pluginType) + if (err != nil) != tt.wantErr { + t.Errorf("createFutureBlueGreenDeployment() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("createFutureBlueGreenDeployment() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_updateDON(t *testing.T) { + type args struct { + lggr logger.Logger + p2pID ragep2ptypes.PeerID + homeChainReader *mockcctypes.HomeChainReader + oracleCreator *mockcctypes.OracleCreator + prevDeployment ccipDeployment + don kcr.CapabilitiesRegistryDONInfo + } + tests := []struct { + name string + args args + wantFutDeployment *ccipDeployment + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotFutDeployment, err := updateDON(tt.args.lggr, tt.args.p2pID, tt.args.homeChainReader, tt.args.oracleCreator, tt.args.prevDeployment, tt.args.don) + if (err != nil) != tt.wantErr { + t.Errorf("updateDON() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotFutDeployment, tt.wantFutDeployment) { + t.Errorf("updateDON() = %v, want %v", gotFutDeployment, tt.wantFutDeployment) + } + }) + } +} + +func Test_launcher_processDiff(t *testing.T) { + type fields struct { + lggr logger.Logger + p2pID ragep2ptypes.PeerID + homeChainReader *mockcctypes.HomeChainReader + oracleCreator *mockcctypes.OracleCreator + dons map[registrysyncer.DonID]*ccipDeployment + regState registrysyncer.State + } + type args struct { + diff diffResult + } + tests := []struct { + name string + fields fields + args args + assert func(t *testing.T, l *launcher) + wantErr bool + }{ + { + "don removed success", + fields{ + dons: map[registrysyncer.DonID]*ccipDeployment{ + 1: { + commit: blueGreenDeployment{ + blue: newMock(t, + func(t *testing.T) *mockcctypes.CCIPOracle { return mockcctypes.NewCCIPOracle(t) }, + func(m *mockcctypes.CCIPOracle) { + m.On("Close").Return(nil) + }), + }, + exec: blueGreenDeployment{ + blue: newMock(t, + func(t *testing.T) *mockcctypes.CCIPOracle { return mockcctypes.NewCCIPOracle(t) }, + func(m *mockcctypes.CCIPOracle) { + m.On("Close").Return(nil) + }), + }, + }, + }, + regState: registrysyncer.State{ + IDsToDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + }, + }, + }, + }, + args{ + diff: diffResult{ + removed: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + }, + }, + }, + }, + func(t *testing.T, l *launcher) { + require.Len(t, l.dons, 0) + require.Len(t, l.regState.IDsToDONs, 0) + }, + false, + }, + { + "don added success", + fields{ + lggr: logger.TestLogger(t), + p2pID: ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID()), + homeChainReader: newMock(t, func(t *testing.T) *mockcctypes.HomeChainReader { + return mockcctypes.NewHomeChainReader(t) + }, func(m *mockcctypes.HomeChainReader) { + m.On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPCommit)). + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{}}, nil) + m.On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPExec)). + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{}}, nil) + }), + oracleCreator: newMock(t, func(t *testing.T) *mockcctypes.OracleCreator { + return mockcctypes.NewOracleCreator(t) + }, func(m *mockcctypes.OracleCreator) { + commitOracle := mockcctypes.NewCCIPOracle(t) + commitOracle.On("Start").Return(nil) + execOracle := mockcctypes.NewCCIPOracle(t) + execOracle.On("Start").Return(nil) + m.On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, mock.Anything). + Return(commitOracle, nil) + m.On("CreatePluginOracle", cctypes.PluginTypeCCIPExec, mock.Anything). + Return(execOracle, nil) + }), + dons: map[registrysyncer.DonID]*ccipDeployment{}, + regState: registrysyncer.State{ + IDsToDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{}, + }, + }, + args{ + diff: diffResult{ + added: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + NodeP2PIds: [][32]byte{ + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID(), + }, + }, + }, + }, + }, + func(t *testing.T, l *launcher) { + require.Len(t, l.dons, 1) + require.Len(t, l.regState.IDsToDONs, 1) + }, + false, + }, + { + "don updated new green instance success", + fields{ + lggr: logger.TestLogger(t), + p2pID: ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID()), + homeChainReader: newMock(t, func(t *testing.T) *mockcctypes.HomeChainReader { + return mockcctypes.NewHomeChainReader(t) + }, func(m *mockcctypes.HomeChainReader) { + m.On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPCommit)). + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{}, {}}, nil) + m.On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPExec)). + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{}, {}}, nil) + }), + oracleCreator: newMock(t, func(t *testing.T) *mockcctypes.OracleCreator { + return mockcctypes.NewOracleCreator(t) + }, func(m *mockcctypes.OracleCreator) { + commitOracle := mockcctypes.NewCCIPOracle(t) + commitOracle.On("Start").Return(nil) + execOracle := mockcctypes.NewCCIPOracle(t) + execOracle.On("Start").Return(nil) + m.On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, mock.Anything). + Return(commitOracle, nil) + m.On("CreatePluginOracle", cctypes.PluginTypeCCIPExec, mock.Anything). + Return(execOracle, nil) + }), + dons: map[registrysyncer.DonID]*ccipDeployment{ + 1: { + commit: blueGreenDeployment{ + blue: newMock(t, func(t *testing.T) *mockcctypes.CCIPOracle { + return mockcctypes.NewCCIPOracle(t) + }, func(m *mockcctypes.CCIPOracle) {}), + }, + exec: blueGreenDeployment{ + blue: newMock(t, func(t *testing.T) *mockcctypes.CCIPOracle { + return mockcctypes.NewCCIPOracle(t) + }, func(m *mockcctypes.CCIPOracle) {}), + }, + }, + }, + regState: registrysyncer.State{ + IDsToDONs: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + NodeP2PIds: [][32]byte{ + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID(), + }, + }, + }, + }, + }, + args{ + diff: diffResult{ + updated: map[registrysyncer.DonID]kcr.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + NodeP2PIds: [][32]byte{ + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID(), + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(2)).PeerID(), // new node in don + }, + }, + }, + }, + }, + func(t *testing.T, l *launcher) { + require.Len(t, l.dons, 1) + require.Len(t, l.regState.IDsToDONs, 1) + require.Len(t, l.regState.IDsToDONs[1].NodeP2PIds, 2) + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + l := &launcher{ + dons: tt.fields.dons, + regState: tt.fields.regState, + p2pID: tt.fields.p2pID, + lggr: tt.fields.lggr, + homeChainReader: tt.fields.homeChainReader, + oracleCreator: tt.fields.oracleCreator, + } + err := l.processDiff(tt.args.diff) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + tt.assert(t, l) + }) + } +} + +func newMock[T any](t *testing.T, newer func(t *testing.T) T, expect func(m T)) T { + o := newer(t) + expect(o) + return o +} diff --git a/core/services/ccipcapability/types/mocks/ccip_oracle.go b/core/services/ccipcapability/types/mocks/ccip_oracle.go new file mode 100644 index 0000000000..ebb3ad6801 --- /dev/null +++ b/core/services/ccipcapability/types/mocks/ccip_oracle.go @@ -0,0 +1,60 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// CCIPOracle is an autogenerated mock type for the CCIPOracle type +type CCIPOracle struct { + mock.Mock +} + +// Close provides a mock function with given fields: +func (_m *CCIPOracle) Close() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Start provides a mock function with given fields: +func (_m *CCIPOracle) Start() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Start") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewCCIPOracle creates a new instance of CCIPOracle. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewCCIPOracle(t interface { + mock.TestingT + Cleanup(func()) +}) *CCIPOracle { + mock := &CCIPOracle{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/services/ccipcapability/types/mocks/home_chain_reader.go b/core/services/ccipcapability/types/mocks/home_chain_reader.go new file mode 100644 index 0000000000..a5a581a1d2 --- /dev/null +++ b/core/services/ccipcapability/types/mocks/home_chain_reader.go @@ -0,0 +1,129 @@ +package mocks + +import ( + "context" + + mapset "github.com/deckarep/golang-set/v2" + "github.com/stretchr/testify/mock" + + ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" + + cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" + + "github.com/smartcontractkit/libocr/ragep2p/types" +) + +var _ ccipreaderpkg.HomeChain = (*HomeChainReader)(nil) + +type HomeChainReader struct { + mock.Mock +} + +func (_m *HomeChainReader) GetChainConfig(chainSelector cciptypes.ChainSelector) (ccipreaderpkg.ChainConfig, error) { + //TODO implement me + panic("implement me") +} + +func (_m *HomeChainReader) GetAllChainConfigs() (map[cciptypes.ChainSelector]ccipreaderpkg.ChainConfig, error) { + //TODO implement me + panic("implement me") +} + +func (_m *HomeChainReader) GetSupportedChainsForPeer(id types.PeerID) (mapset.Set[cciptypes.ChainSelector], error) { + //TODO implement me + panic("implement me") +} + +func (_m *HomeChainReader) GetKnownCCIPChains() (mapset.Set[cciptypes.ChainSelector], error) { + //TODO implement me + panic("implement me") +} + +func (_m *HomeChainReader) GetFChain() (map[cciptypes.ChainSelector]int, error) { + //TODO implement me + panic("implement me") +} + +func (_m *HomeChainReader) Start(ctx context.Context) error { + //TODO implement me + panic("implement me") +} + +func (_m *HomeChainReader) Close() error { + //TODO implement me + panic("implement me") +} + +func (_m *HomeChainReader) HealthReport() map[string]error { + //TODO implement me + panic("implement me") +} + +func (_m *HomeChainReader) Name() string { + //TODO implement me + panic("implement me") +} + +// GetOCRConfigs provides a mock function with given fields: ctx, donID, pluginType +func (_m *HomeChainReader) GetOCRConfigs(ctx context.Context, donID uint32, pluginType uint8) ([]ccipreaderpkg.OCR3ConfigWithMeta, error) { + ret := _m.Called(ctx, donID, pluginType) + + if len(ret) == 0 { + panic("no return value specified for GetOCRConfigs") + } + + var r0 []ccipreaderpkg.OCR3ConfigWithMeta + var r1 error + if rf, ok := ret.Get(0).(func(ctx context.Context, donID uint32, pluginType uint8) ([]ccipreaderpkg.OCR3ConfigWithMeta, error)); ok { + return rf(ctx, donID, pluginType) + } + if rf, ok := ret.Get(0).(func(ctx context.Context, donID uint32, pluginType uint8) []ccipreaderpkg.OCR3ConfigWithMeta); ok { + r0 = rf(ctx, donID, pluginType) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]ccipreaderpkg.OCR3ConfigWithMeta) + } + } + + if rf, ok := ret.Get(1).(func(ctx context.Context, donID uint32, pluginType uint8) error); ok { + r1 = rf(ctx, donID, pluginType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +func (_m *HomeChainReader) Ready() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Ready") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewHomeChainReader creates a new instance of HomeChainReader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewHomeChainReader(t interface { + mock.TestingT + Cleanup(func()) +}) *HomeChainReader { + mock := &HomeChainReader{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/services/ccipcapability/types/mocks/oracle_creator.go b/core/services/ccipcapability/types/mocks/oracle_creator.go new file mode 100644 index 0000000000..ce729d1afe --- /dev/null +++ b/core/services/ccipcapability/types/mocks/oracle_creator.go @@ -0,0 +1,87 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package mocks + +import ( + types "github.com/smartcontractkit/chainlink/v2/core/services/ccipcapability/types" + mock "github.com/stretchr/testify/mock" +) + +// OracleCreator is an autogenerated mock type for the OracleCreator type +type OracleCreator struct { + mock.Mock +} + +// CreateBootstrapOracle provides a mock function with given fields: config +func (_m *OracleCreator) CreateBootstrapOracle(config types.OCR3ConfigWithMeta) (types.CCIPOracle, error) { + ret := _m.Called(config) + + if len(ret) == 0 { + panic("no return value specified for CreateBootstrapOracle") + } + + var r0 types.CCIPOracle + var r1 error + if rf, ok := ret.Get(0).(func(types.OCR3ConfigWithMeta) (types.CCIPOracle, error)); ok { + return rf(config) + } + if rf, ok := ret.Get(0).(func(types.OCR3ConfigWithMeta) types.CCIPOracle); ok { + r0 = rf(config) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.CCIPOracle) + } + } + + if rf, ok := ret.Get(1).(func(types.OCR3ConfigWithMeta) error); ok { + r1 = rf(config) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CreatePluginOracle provides a mock function with given fields: pluginType, config +func (_m *OracleCreator) CreatePluginOracle(pluginType types.PluginType, config types.OCR3ConfigWithMeta) (types.CCIPOracle, error) { + ret := _m.Called(pluginType, config) + + if len(ret) == 0 { + panic("no return value specified for CreatePluginOracle") + } + + var r0 types.CCIPOracle + var r1 error + if rf, ok := ret.Get(0).(func(types.PluginType, types.OCR3ConfigWithMeta) (types.CCIPOracle, error)); ok { + return rf(pluginType, config) + } + if rf, ok := ret.Get(0).(func(types.PluginType, types.OCR3ConfigWithMeta) types.CCIPOracle); ok { + r0 = rf(pluginType, config) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.CCIPOracle) + } + } + + if rf, ok := ret.Get(1).(func(types.PluginType, types.OCR3ConfigWithMeta) error); ok { + r1 = rf(pluginType, config) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewOracleCreator creates a new instance of OracleCreator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewOracleCreator(t interface { + mock.TestingT + Cleanup(func()) +}) *OracleCreator { + mock := &OracleCreator{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/services/ccipcapability/types/types.go b/core/services/ccipcapability/types/types.go new file mode 100644 index 0000000000..50e4fb19f4 --- /dev/null +++ b/core/services/ccipcapability/types/types.go @@ -0,0 +1,50 @@ +package types + +import ( + ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" +) + +// OCR3ConfigWithMeta is a type alias in order to generate correct mocks for the OracleCreator interface. +type OCR3ConfigWithMeta ccipreaderpkg.OCR3ConfigWithMeta + +// PluginType represents the type of CCIP plugin. +// It mirrors the OCRPluginType in Internal.sol. +type PluginType uint8 + +const ( + PluginTypeCCIPCommit PluginType = 0 + PluginTypeCCIPExec PluginType = 1 +) + +func (pt PluginType) String() string { + switch pt { + case PluginTypeCCIPCommit: + return "CCIPCommit" + case PluginTypeCCIPExec: + return "CCIPExec" + default: + return "Unknown" + } +} + +// CCIPOracle represents either a CCIP commit or exec oracle or a bootstrap node. +// +//go:generate mockery --name CCIPOracle --output ./mocks/ --case underscore +type CCIPOracle interface { + Close() error + Start() error +} + +// OracleCreator is an interface for creating CCIP oracles. +// Whether the oracle uses a LOOPP or not is an implementation detail. +// +//go:generate mockery --name OracleCreator --output ./mocks/ --case underscore +type OracleCreator interface { + // CreatePlugin creates a new oracle that will run either the commit or exec ccip plugin. + // The oracle must be returned unstarted. + CreatePluginOracle(pluginType PluginType, config OCR3ConfigWithMeta) (CCIPOracle, error) + + // CreateBootstrapOracle creates a new bootstrap node with the given OCR config. + // The oracle must be returned unstarted. + CreateBootstrapOracle(config OCR3ConfigWithMeta) (CCIPOracle, error) +} diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 003ed4a17c..55c16e5e7b 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -11,7 +11,6 @@ import ( "gopkg.in/guregu/null.v4" - chainselectors "github.com/smartcontractkit/chain-selectors" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" "github.com/smartcontractkit/chainlink-common/pkg/types/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" @@ -19,6 +18,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" + chainselectors "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/libocr/commontypes" + libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "google.golang.org/grpc" + ocr2keepers20 "github.com/smartcontractkit/chainlink-automation/pkg/v2" ocr2keepers20config "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" ocr2keepers20coordinator "github.com/smartcontractkit/chainlink-automation/pkg/v2/coordinator" @@ -37,11 +43,6 @@ import ( "github.com/smartcontractkit/chainlink-vrf/altbn_128" dkgpkg "github.com/smartcontractkit/chainlink-vrf/dkg" "github.com/smartcontractkit/chainlink-vrf/ocr2vrf" - "github.com/smartcontractkit/libocr/commontypes" - libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "google.golang.org/grpc" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" @@ -276,12 +277,12 @@ func (d *Delegate) JobType() job.Type { return job.OffchainReporting2 } -func (d *Delegate) BeforeJobCreated(spec job.Job) { +func (d *Delegate) BeforeJobCreated(_ job.Job) { // This is only called first time the job is created d.isNewlyCreatedJob = true } -func (d *Delegate) AfterJobCreated(spec job.Job) {} -func (d *Delegate) BeforeJobDeleted(spec job.Job) {} +func (d *Delegate) AfterJobCreated(_ job.Job) {} +func (d *Delegate) BeforeJobDeleted(_ job.Job) {} func (d *Delegate) OnDeleteJob(ctx context.Context, jb job.Job) error { // If the job spec is malformed in any way, we report the error but return nil so that // the job deletion itself isn't blocked. @@ -1284,10 +1285,10 @@ func (d *Delegate) newServicesOCR2VRF( lggr.ErrorIf(d.jobORM.RecordError(ctx, jb.ID, msg), "unable to record error") }) dkgReportingPluginFactoryDecorator := func(wrapped ocrtypes.ReportingPluginFactory) ocrtypes.ReportingPluginFactory { - return promwrapper.NewPromFactory(wrapped, "DKG", string(relay.NetworkEVM), chain.ID()) + return promwrapper.NewPromFactory(wrapped, "DKG", relay.NetworkEVM, chain.ID()) } vrfReportingPluginFactoryDecorator := func(wrapped ocrtypes.ReportingPluginFactory) ocrtypes.ReportingPluginFactory { - return promwrapper.NewPromFactory(wrapped, "OCR2VRF", string(relay.NetworkEVM), chain.ID()) + return promwrapper.NewPromFactory(wrapped, "OCR2VRF", relay.NetworkEVM, chain.ID()) } noopMonitoringEndpoint := telemetry.NoopAgent{} oracles, err2 := ocr2vrf.NewOCR2VRF(ocr2vrf.DKGVRFArgs{ @@ -1812,11 +1813,6 @@ func (d *Delegate) newServicesCCIPCommit(ctx context.Context, lggr logger.Sugare return nil, ErrJobSpecNoRelayer{Err: err, PluginName: string(spec.PluginType)} } - dstChain, err := d.legacyChains.Get(dstRid.ChainID) - if err != nil { - return nil, fmt.Errorf("ccip services; failed to get chain %s: %w", dstRid.ChainID, err) - } - logError := func(msg string) { lggr.ErrorIf(d.jobORM.RecordError(context.Background(), jb.ID, msg), "unable to record error") } @@ -1843,12 +1839,6 @@ func (d *Delegate) newServicesCCIPCommit(ctx context.Context, lggr logger.Sugare return nil, err } - srcChainIDstr := strconv.FormatUint(srcChainID, 10) - srcChain, err := d.legacyChains.Get(srcChainIDstr) - if err != nil { - return nil, fmt.Errorf("open source chain: %w", err) - } - oracleArgsNoPlugin := libocr2.OCR2OracleArgs{ BinaryNetworkEndpointFactory: d.peerWrapper.Peer2, V2Bootstrappers: bootstrapPeers, @@ -1868,7 +1858,7 @@ func (d *Delegate) newServicesCCIPCommit(ctx context.Context, lggr logger.Sugare MetricsRegisterer: prometheus.WrapRegistererWith(map[string]string{"job_name": jb.Name.ValueOrZero()}, prometheus.DefaultRegisterer), } - return ccipcommit.NewCommitServices(ctx, d.ds, srcProvider, dstProvider, srcChain, dstChain, d.legacyChains, jb, lggr, d.pipelineRunner, oracleArgsNoPlugin, d.isNewlyCreatedJob, int64(srcChainID), dstChainID, logError) + return ccipcommit.NewCommitServices(ctx, d.ds, srcProvider, dstProvider, d.legacyChains, jb, lggr, d.pipelineRunner, oracleArgsNoPlugin, d.isNewlyCreatedJob, int64(srcChainID), dstChainID, logError) } func newCCIPCommitPluginBytes(isSourceProvider bool, sourceStartBlock uint64, destStartBlock uint64) config.CommitPluginConfig { diff --git a/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go b/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go index ae3199d1f5..243b6c6170 100644 --- a/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go +++ b/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go @@ -43,7 +43,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" ) -func NewCommitServices(ctx context.Context, ds sqlutil.DataSource, srcProvider commontypes.CCIPCommitProvider, dstProvider commontypes.CCIPCommitProvider, srcChain legacyevm.Chain, dstChain legacyevm.Chain, chainSet legacyevm.LegacyChainContainer, jb job.Job, lggr logger.Logger, pr pipeline.Runner, argsNoPlugin libocr2.OCR2OracleArgs, new bool, sourceChainID int64, destChainID int64, logError func(string)) ([]job.ServiceCtx, error) { +func NewCommitServices(ctx context.Context, ds sqlutil.DataSource, srcProvider commontypes.CCIPCommitProvider, dstProvider commontypes.CCIPCommitProvider, chainSet legacyevm.LegacyChainContainer, jb job.Job, lggr logger.Logger, pr pipeline.Runner, argsNoPlugin libocr2.OCR2OracleArgs, new bool, sourceChainID int64, destChainID int64, logError func(string)) ([]job.ServiceCtx, error) { spec := jb.OCR2OracleSpec var pluginConfig ccipconfig.CommitPluginJobSpecConfig @@ -53,6 +53,9 @@ func NewCommitServices(ctx context.Context, ds sqlutil.DataSource, srcProvider c } commitStoreAddress := common.HexToAddress(spec.ContractID) + + // commit store contract doesn't exist on the source chain, but we have an implementation of it + // to get access to a gas estimator on the source chain srcCommitStore, err := srcProvider.NewCommitStoreReader(ctx, ccipcalc.EvmAddrToGeneric(commitStoreAddress)) if err != nil { return nil, err @@ -133,6 +136,7 @@ func NewCommitServices(ctx context.Context, ds sqlutil.DataSource, srcProvider c // Prom wrappers onRampReader = observability.NewObservedOnRampReader(onRampReader, sourceChainID, ccip.CommitPluginLabel) commitStoreReader = observability.NewObservedCommitStoreReader(commitStoreReader, destChainID, ccip.CommitPluginLabel) + offRampReader = observability.NewObservedOffRampReader(offRampReader, destChainID, ccip.CommitPluginLabel) metricsCollector := ccip.NewPluginMetricsCollector(ccip.CommitPluginLabel, sourceChainID, destChainID) chainHealthCheck := cache.NewObservedChainHealthCheck( @@ -217,7 +221,7 @@ func CommitReportToEthTxMeta(typ ccipconfig.ContractType, ver semver.Version) (f // https://github.com/smartcontractkit/ccip/blob/68e2197472fb017dd4e5630d21e7878d58bc2a44/core/services/feeds/service.go#L716 // TODO once that transaction is broken up, we should be able to simply rely on oracle.Close() to cleanup the filters. // Until then we have to deterministically reload the readers from the spec (and thus their filters) and close them. -func UnregisterCommitPluginLpFilters(ctx context.Context, lggr logger.Logger, jb job.Job, chainSet legacyevm.LegacyChainContainer) error { +func UnregisterCommitPluginLpFilters(_ context.Context, lggr logger.Logger, jb job.Job, chainSet legacyevm.LegacyChainContainer) error { params, err := extractJobSpecParams(jb, chainSet) if err != nil { return err diff --git a/core/services/ocr2/plugins/ccip/internal/cache/commit_roots.go b/core/services/ocr2/plugins/ccip/internal/cache/commit_roots.go index 6f850ad39c..ae5fe8fcde 100644 --- a/core/services/ocr2/plugins/ccip/internal/cache/commit_roots.go +++ b/core/services/ocr2/plugins/ccip/internal/cache/commit_roots.go @@ -131,38 +131,41 @@ func (s *commitRootsCache) Snooze(merkleRoot [32]byte) { } func (s *commitRootsCache) OldestRootTimestamp() time.Time { - messageVisibilityInterval := time.Now().Add(-s.messageVisibilityInterval) - timestamp, ok := s.pickOldestRootBlockTimestamp(messageVisibilityInterval) - - if ok { - return timestamp - } - - s.rootsQueueMu.Lock() - defer s.rootsQueueMu.Unlock() - - // If rootsSearchFilter is before messageVisibilityInterval, it means that we have roots that are stuck forever and will never be executed - // In that case, we wipe out the entire queue. Next round should start from the messageVisibilityInterval and rebuild cache from scratch. - s.unexecutedRootsQueue = orderedmap.New[string, time.Time]() - return messageVisibilityInterval + return time.Now().Add(-s.messageVisibilityInterval) + // TODO we can't rely on block timestamps, because in case of re-org they can change and therefore affect + // the logic in the case. In the meantime, always fallback to the default behaviour and use permissionlessThresholdWindow + //timestamp, ok := s.pickOldestRootBlockTimestamp(messageVisibilityInterval) + // + //if ok { + // return timestamp + //} + // + //s.rootsQueueMu.Lock() + //defer s.rootsQueueMu.Unlock() + // + //// If rootsSearchFilter is before messageVisibilityInterval, it means that we have roots that are stuck forever and will never be executed + //// In that case, we wipe out the entire queue. Next round should start from the messageVisibilityInterval and rebuild cache from scratch. + //s.unexecutedRootsQueue = orderedmap.New[string, time.Time]() + //return messageVisibilityInterval } -func (s *commitRootsCache) pickOldestRootBlockTimestamp(messageVisibilityInterval time.Time) (time.Time, bool) { - s.rootsQueueMu.RLock() - defer s.rootsQueueMu.RUnlock() - - // If there are no roots in the queue, we can return the messageVisibilityInterval - if s.oldestRootTimestamp.IsZero() { - return messageVisibilityInterval, true - } +//func (s *commitRootsCache) pickOldestRootBlockTimestamp(messageVisibilityInterval time.Time) (time.Time, bool) { +// s.rootsQueueMu.RLock() +// defer s.rootsQueueMu.RUnlock() +// +// // If there are no roots in the queue, we can return the messageVisibilityInterval +// if s.oldestRootTimestamp.IsZero() { +// return messageVisibilityInterval, true +// } +// +// if s.oldestRootTimestamp.After(messageVisibilityInterval) { +// // Query used for fetching roots from the database is exclusive (block_timestamp > :timestamp) +// // so we need to subtract 1 second from the head timestamp to make sure that this root is included in the results +// return s.oldestRootTimestamp.Add(-time.Second), true +// } +// return time.Time{}, false +//} - if s.oldestRootTimestamp.After(messageVisibilityInterval) { - // Query used for fetching roots from the database is exclusive (block_timestamp > :timestamp) - // so we need to subtract 1 second from the head timestamp to make sure that this root is included in the results - return s.oldestRootTimestamp.Add(-time.Second), true - } - return time.Time{}, false -} func (s *commitRootsCache) AppendUnexecutedRoot(merkleRoot [32]byte, blockTimestamp time.Time) { prettyMerkleRoot := merkleRootToString(merkleRoot) diff --git a/core/services/ocr2/plugins/ccip/internal/cache/commit_roots_test.go b/core/services/ocr2/plugins/ccip/internal/cache/commit_roots_test.go index 9dd8c365aa..f79e365d0c 100644 --- a/core/services/ocr2/plugins/ccip/internal/cache/commit_roots_test.go +++ b/core/services/ocr2/plugins/ccip/internal/cache/commit_roots_test.go @@ -67,60 +67,60 @@ func Test_UnexecutedRoots(t *testing.T) { roots: []rootWithTs{}, permissionLessThreshold: 1 * time.Hour, }, - { - name: "returns first root when all are not executed", - roots: []rootWithTs{ - {r1, t1}, - {r2, t2}, - {r3, t3}, - }, - permissionLessThreshold: 10 * time.Hour, - expectedTimestamp: t1, - }, - { - name: "returns first root when tail of queue is executed", - roots: []rootWithTs{ - {r1, t1}, - {r2, t2}, - {r3, t3}, - }, - executedRoots: [][32]byte{r2, r3}, - permissionLessThreshold: 10 * time.Hour, - expectedTimestamp: t1, - }, - { - name: "returns first not executed root", - roots: []rootWithTs{ - {r1, t1}, - {r2, t2}, - {r3, t3}, - }, - executedRoots: [][32]byte{r1, r2}, - permissionLessThreshold: 10 * time.Hour, - expectedTimestamp: t3, - }, - { - name: "returns r2 timestamp when r1 and r3 are executed", - roots: []rootWithTs{ - {r1, t1}, - {r2, t2}, - {r3, t3}, - }, - executedRoots: [][32]byte{r1, r3}, - permissionLessThreshold: 10 * time.Hour, - expectedTimestamp: t2, - }, - { - name: "returns oldest root even when all are executed", - roots: []rootWithTs{ - {r1, t1}, - {r2, t2}, - {r3, t3}, - }, - executedRoots: [][32]byte{r1, r2, r3}, - permissionLessThreshold: 10 * time.Hour, - expectedTimestamp: t3, - }, + //{ + // name: "returns first root when all are not executed", + // roots: []rootWithTs{ + // {r1, t1}, + // {r2, t2}, + // {r3, t3}, + // }, + // permissionLessThreshold: 10 * time.Hour, + // expectedTimestamp: t1, + //}, + //{ + // name: "returns first root when tail of queue is executed", + // roots: []rootWithTs{ + // {r1, t1}, + // {r2, t2}, + // {r3, t3}, + // }, + // executedRoots: [][32]byte{r2, r3}, + // permissionLessThreshold: 10 * time.Hour, + // expectedTimestamp: t1, + //}, + //{ + // name: "returns first not executed root", + // roots: []rootWithTs{ + // {r1, t1}, + // {r2, t2}, + // {r3, t3}, + // }, + // executedRoots: [][32]byte{r1, r2}, + // permissionLessThreshold: 10 * time.Hour, + // expectedTimestamp: t3, + //}, + //{ + // name: "returns r2 timestamp when r1 and r3 are executed", + // roots: []rootWithTs{ + // {r1, t1}, + // {r2, t2}, + // {r3, t3}, + // }, + // executedRoots: [][32]byte{r1, r3}, + // permissionLessThreshold: 10 * time.Hour, + // expectedTimestamp: t2, + //}, + //{ + // name: "returns oldest root even when all are executed", + // roots: []rootWithTs{ + // {r1, t1}, + // {r2, t2}, + // {r3, t3}, + // }, + // executedRoots: [][32]byte{r1, r2, r3}, + // permissionLessThreshold: 10 * time.Hour, + // expectedTimestamp: t3, + //}, { name: "returns permissionLessThreshold when all roots ale older that threshold", roots: []rootWithTs{ @@ -161,12 +161,12 @@ func Test_UnexecutedRootsScenario(t *testing.T) { k1 := [32]byte{1} k2 := [32]byte{2} k3 := [32]byte{3} - k4 := [32]byte{4} + //k4 := [32]byte{4} t1 := time.Now().Add(-4 * time.Hour) t2 := time.Now().Add(-3 * time.Hour) t3 := time.Now().Add(-2 * time.Hour) - t4 := time.Now().Add(-1 * time.Hour) + //t4 := time.Now().Add(-1 * time.Hour) // First check should return permissionLessThreshold window commitTs := c.OldestRootTimestamp() @@ -176,42 +176,47 @@ func Test_UnexecutedRootsScenario(t *testing.T) { c.AppendUnexecutedRoot(k2, t2) c.AppendUnexecutedRoot(k3, t3) - // After loading roots it should return the first one - commitTs = c.OldestRootTimestamp() - assert.Equal(t, t1.Add(-time.Second), commitTs) - - // Marking root in the middle as executed shouldn't change the commitTs - c.MarkAsExecuted(k2) - commitTs = c.OldestRootTimestamp() - assert.Equal(t, t1.Add(-time.Second), commitTs) - - // Marking k1 as executed when k2 is already executed should return timestamp of k3 - c.MarkAsExecuted(k1) - commitTs = c.OldestRootTimestamp() - assert.Equal(t, t3.Add(-time.Second), commitTs) - - // Marking all as executed should return timestamp of the latest - c.MarkAsExecuted(k3) commitTs = c.OldestRootTimestamp() - assert.Equal(t, t3.Add(-time.Second), commitTs) - - // Adding k4 should return timestamp of k4 - c.AppendUnexecutedRoot(k4, t4) - commitTs = c.OldestRootTimestamp() - assert.Equal(t, t4.Add(-time.Second), commitTs) - - c.MarkAsExecuted(k4) - commitTs = c.OldestRootTimestamp() - assert.Equal(t, t4.Add(-time.Second), commitTs) + assert.True(t, commitTs.Before(time.Now().Add(-permissionLessThreshold))) - // Appending already executed roots should be ignored - c.AppendUnexecutedRoot(k1, t1) - c.AppendUnexecutedRoot(k2, t2) - commitTs = c.OldestRootTimestamp() - assert.Equal(t, t4.Add(-time.Second), commitTs) + //// After loading roots it should return the first one + //commitTs = c.OldestRootTimestamp() + //assert.Equal(t, t1.Add(-time.Second), commitTs) + // + //// Marking root in the middle as executed shouldn't change the commitTs + //c.MarkAsExecuted(k2) + //commitTs = c.OldestRootTimestamp() + //assert.Equal(t, t1.Add(-time.Second), commitTs) + // + //// Marking k1 as executed when k2 is already executed should return timestamp of k3 + //c.MarkAsExecuted(k1) + //commitTs = c.OldestRootTimestamp() + //assert.Equal(t, t3.Add(-time.Second), commitTs) + // + //// Marking all as executed should return timestamp of the latest + //c.MarkAsExecuted(k3) + //commitTs = c.OldestRootTimestamp() + //assert.Equal(t, t3.Add(-time.Second), commitTs) + // + //// Adding k4 should return timestamp of k4 + //c.AppendUnexecutedRoot(k4, t4) + //commitTs = c.OldestRootTimestamp() + //assert.Equal(t, t4.Add(-time.Second), commitTs) + // + //c.MarkAsExecuted(k4) + //commitTs = c.OldestRootTimestamp() + //assert.Equal(t, t4.Add(-time.Second), commitTs) + // + //// Appending already executed roots should be ignored + //c.AppendUnexecutedRoot(k1, t1) + //c.AppendUnexecutedRoot(k2, t2) + //commitTs = c.OldestRootTimestamp() + //assert.Equal(t, t4.Add(-time.Second), commitTs) } func Test_UnexecutedRootsStaleQueue(t *testing.T) { + t.Skip("This test needs caching to properly handle re-orgs") + permissionLessThreshold := 5 * time.Hour c := newCommitRootsCache(logger.TestLogger(t), permissionLessThreshold, 1*time.Hour, 1*time.Millisecond, 1*time.Millisecond) diff --git a/core/services/ocr2/plugins/ccip/internal/pricegetter/evm.go b/core/services/ocr2/plugins/ccip/internal/pricegetter/evm.go index c8a70cfdc0..830d56cd3a 100644 --- a/core/services/ocr2/plugins/ccip/internal/pricegetter/evm.go +++ b/core/services/ocr2/plugins/ccip/internal/pricegetter/evm.go @@ -144,6 +144,9 @@ func (d *DynamicPriceGetter) performBatchCall(ctx context.Context, chainID uint6 calls = append(calls, batchCalls.latestRoundDataCalls...) results, err := evmCaller.BatchCall(ctx, 0, calls) + if err != nil { + return fmt.Errorf("batch call on chain %d failed: %w", chainID, err) + } // Extract results. decimals := make([]uint8, 0, nbDecimalCalls) diff --git a/core/services/ocr2/plugins/ccip/internal/pricegetter/evm_test.go b/core/services/ocr2/plugins/ccip/internal/pricegetter/evm_test.go index 8d1bf67ab1..673b9776c7 100644 --- a/core/services/ocr2/plugins/ccip/internal/pricegetter/evm_test.go +++ b/core/services/ocr2/plugins/ccip/internal/pricegetter/evm_test.go @@ -25,6 +25,7 @@ type testParameters struct { evmClients map[uint64]DynamicPriceGetterClient tokens []common.Address expectedTokenPrices map[common.Address]big.Int + evmCallErr bool invalidConfigErrorExpected bool priceResolutionErrorExpected bool } @@ -58,6 +59,10 @@ func TestDynamicPriceGetter(t *testing.T) { name: "no_aggregator_for_token", param: testParamNoAggregatorForToken(t), }, + { + name: "batchCall_returns_err", + param: testParamBatchCallReturnsErr(t), + }, } for _, test := range tests { @@ -82,6 +87,12 @@ func TestDynamicPriceGetter(t *testing.T) { tokens = append(tokens, tokenAddr) } prices, err := pg.TokenPricesUSD(ctx, tokens) + + if test.param.evmCallErr { + require.Error(t, err) + return + } + if test.param.priceResolutionErrorExpected { require.Error(t, err) return @@ -454,6 +465,50 @@ func testParamNoAggregatorForToken(t *testing.T) testParameters { } } +func testParamBatchCallReturnsErr(t *testing.T) testParameters { + tk1 := utils.RandomAddress() + tk2 := utils.RandomAddress() + tk3 := utils.RandomAddress() + cfg := config.DynamicPriceGetterConfig{ + AggregatorPrices: map[common.Address]config.AggregatorPriceConfig{ + tk1: { + ChainID: 101, + AggregatorContractAddress: utils.RandomAddress(), + }, + tk2: { + ChainID: 102, + AggregatorContractAddress: utils.RandomAddress(), + }, + }, + StaticPrices: map[common.Address]config.StaticPriceConfig{ + tk3: { + ChainID: 103, + Price: big.NewInt(1_234_000), + }, + }, + } + // Real LINK/USD example from OP. + round1 := aggregator_v3_interface.LatestRoundData{ + RoundId: big.NewInt(1000), + Answer: big.NewInt(1396818990), + StartedAt: big.NewInt(1704896575), + UpdatedAt: big.NewInt(1704896575), + AnsweredInRound: big.NewInt(1000), + } + evmClients := map[uint64]DynamicPriceGetterClient{ + uint64(101): mockClient(t, []uint8{8}, []aggregator_v3_interface.LatestRoundData{round1}), + uint64(102): { + BatchCaller: mockErrCaller(t), + }, + } + return testParameters{ + cfg: cfg, + evmClients: evmClients, + tokens: []common.Address{tk1, tk2, tk3}, + evmCallErr: true, + } +} + func mockClient(t *testing.T, decimals []uint8, rounds []aggregator_v3_interface.LatestRoundData) DynamicPriceGetterClient { return DynamicPriceGetterClient{ BatchCaller: mockCaller(t, decimals, rounds), @@ -479,6 +534,12 @@ func mockCaller(t *testing.T, decimals []uint8, rounds []aggregator_v3_interface return caller } +func mockErrCaller(t *testing.T) *rpclibmocks.EvmBatchCaller { + caller := rpclibmocks.NewEvmBatchCaller(t) + caller.On("BatchCall", mock.Anything, uint64(0), mock.Anything).Return(nil, assert.AnError).Maybe() + return caller +} + // multExp returns the result of multiplying x by 10^e. func multExp(x *big.Int, e int64) *big.Int { return big.NewInt(0).Mul(x, big.NewInt(0).Exp(big.NewInt(10), big.NewInt(e), nil)) diff --git a/core/services/ocr2/plugins/liquiditymanager/bridge/arb/l1_to_l2.go b/core/services/ocr2/plugins/liquiditymanager/bridge/arb/l1_to_l2.go index 072bcd4dfa..fc424d8f67 100644 --- a/core/services/ocr2/plugins/liquiditymanager/bridge/arb/l1_to_l2.go +++ b/core/services/ocr2/plugins/liquiditymanager/bridge/arb/l1_to_l2.go @@ -56,6 +56,7 @@ type l1ToL2Bridge struct { } func NewL1ToL2Bridge( + ctx context.Context, lggr logger.Logger, localSelector, remoteSelector models.NetworkSelector, @@ -95,8 +96,6 @@ func NewL1ToL2Bridge( remoteChain.Name, "", ) - // FIXME Makram please pass the valid context - ctx := context.Background() err = l1LogPoller.RegisterFilter(ctx, logpoller.Filter{ Addresses: []common.Address{l1LiquidityManagerAddress}, Name: l1FilterName, diff --git a/core/services/ocr2/plugins/liquiditymanager/bridge/arb/l2_to_l1.go b/core/services/ocr2/plugins/liquiditymanager/bridge/arb/l2_to_l1.go index ea06c363e0..f71a3bd40a 100644 --- a/core/services/ocr2/plugins/liquiditymanager/bridge/arb/l2_to_l1.go +++ b/core/services/ocr2/plugins/liquiditymanager/bridge/arb/l2_to_l1.go @@ -54,6 +54,7 @@ type l2ToL1Bridge struct { } func NewL2ToL1Bridge( + ctx context.Context, lggr logger.Logger, localSelector, remoteSelector models.NetworkSelector, @@ -81,8 +82,6 @@ func NewL2ToL1Bridge( remoteChain.Name, "", ) - // FIXME Makram fix the context plax - ctx := context.Background() err := l2LogPoller.RegisterFilter( ctx, logpoller.Filter{ diff --git a/core/services/ocr2/plugins/liquiditymanager/bridge/bridge.go b/core/services/ocr2/plugins/liquiditymanager/bridge/bridge.go index c6fb25d3d1..24af25c48e 100644 --- a/core/services/ocr2/plugins/liquiditymanager/bridge/bridge.go +++ b/core/services/ocr2/plugins/liquiditymanager/bridge/bridge.go @@ -60,7 +60,8 @@ type Bridge interface { //go:generate mockery --name Factory --output ./mocks --filename bridge_factory_mock.go --case=underscore type Factory interface { - NewBridge(source, dest models.NetworkSelector) (Bridge, error) + NewBridge(ctx context.Context, source, dest models.NetworkSelector) (Bridge, error) + GetBridge(source, dest models.NetworkSelector) (Bridge, error) } type Opt func(c *factory) @@ -106,7 +107,7 @@ func WithEvmDep( } } -func (f *factory) NewBridge(source, dest models.NetworkSelector) (Bridge, error) { +func (f *factory) NewBridge(ctx context.Context, source, dest models.NetworkSelector) (Bridge, error) { if source == dest { return nil, fmt.Errorf("no bridge between the same network and itself: %d", source) } @@ -114,12 +115,12 @@ func (f *factory) NewBridge(source, dest models.NetworkSelector) (Bridge, error) bridge, err := f.GetBridge(source, dest) if errors.Is(err, ErrBridgeNotFound) { f.lggr.Infow("Bridge not found, initializing new bridge", "source", source, "dest", dest) - return f.initBridge(source, dest) + return f.initBridge(ctx, source, dest) } return bridge, err } -func (f *factory) initBridge(source, dest models.NetworkSelector) (Bridge, error) { +func (f *factory) initBridge(ctx context.Context, source, dest models.NetworkSelector) (Bridge, error) { f.lggr.Debugw("Initializing bridge", "source", source, "dest", dest) var bridge Bridge @@ -155,6 +156,7 @@ func (f *factory) initBridge(source, dest models.NetworkSelector) (Bridge, error "l2BridgeAdapter", l2BridgeAdapter, ) bridge, err = arb.NewL2ToL1Bridge( + ctx, f.lggr, source, dest, @@ -198,6 +200,7 @@ func (f *factory) initBridge(source, dest models.NetworkSelector) (Bridge, error "l2BridgeAdapter", l2BridgeAdapter, ) bridge, err = opstack.NewL2ToL1Bridge( + ctx, f.lggr, source, dest, @@ -245,6 +248,7 @@ func (f *factory) initBridge(source, dest models.NetworkSelector) (Bridge, error "l1BridgeAdapter", l1BridgeAdapter, ) bridge, err = arb.NewL1ToL2Bridge( + ctx, f.lggr, source, dest, @@ -267,6 +271,7 @@ func (f *factory) initBridge(source, dest models.NetworkSelector) (Bridge, error "l1BridgeAdapter", l1BridgeAdapter, ) bridge, err = opstack.NewL1ToL2Bridge( + ctx, f.lggr, source, dest, @@ -310,6 +315,7 @@ func (f *factory) initBridge(source, dest models.NetworkSelector) (Bridge, error return nil, fmt.Errorf("bridge adapter not found for dest selector %d in deps for selector %d", dest, source) } bridge, err = testonlybridge.New( + ctx, source, dest, sourceDeps.liquidityManagerAddress, diff --git a/core/services/ocr2/plugins/liquiditymanager/bridge/mocks/bridge_factory_mock.go b/core/services/ocr2/plugins/liquiditymanager/bridge/mocks/bridge_factory_mock.go index ca0781b149..53f1bf86cd 100644 --- a/core/services/ocr2/plugins/liquiditymanager/bridge/mocks/bridge_factory_mock.go +++ b/core/services/ocr2/plugins/liquiditymanager/bridge/mocks/bridge_factory_mock.go @@ -3,7 +3,10 @@ package mocks import ( + context "context" + bridge "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/liquiditymanager/bridge" + mock "github.com/stretchr/testify/mock" models "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/liquiditymanager/models" @@ -14,12 +17,12 @@ type Factory struct { mock.Mock } -// NewBridge provides a mock function with given fields: source, dest -func (_m *Factory) NewBridge(source models.NetworkSelector, dest models.NetworkSelector) (bridge.Bridge, error) { +// GetBridge provides a mock function with given fields: source, dest +func (_m *Factory) GetBridge(source models.NetworkSelector, dest models.NetworkSelector) (bridge.Bridge, error) { ret := _m.Called(source, dest) if len(ret) == 0 { - panic("no return value specified for NewBridge") + panic("no return value specified for GetBridge") } var r0 bridge.Bridge @@ -44,6 +47,36 @@ func (_m *Factory) NewBridge(source models.NetworkSelector, dest models.NetworkS return r0, r1 } +// NewBridge provides a mock function with given fields: ctx, source, dest +func (_m *Factory) NewBridge(ctx context.Context, source models.NetworkSelector, dest models.NetworkSelector) (bridge.Bridge, error) { + ret := _m.Called(ctx, source, dest) + + if len(ret) == 0 { + panic("no return value specified for NewBridge") + } + + var r0 bridge.Bridge + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.NetworkSelector, models.NetworkSelector) (bridge.Bridge, error)); ok { + return rf(ctx, source, dest) + } + if rf, ok := ret.Get(0).(func(context.Context, models.NetworkSelector, models.NetworkSelector) bridge.Bridge); ok { + r0 = rf(ctx, source, dest) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(bridge.Bridge) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, models.NetworkSelector, models.NetworkSelector) error); ok { + r1 = rf(ctx, source, dest) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // NewFactory creates a new instance of Factory. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewFactory(t interface { diff --git a/core/services/ocr2/plugins/liquiditymanager/bridge/opstack/l1_to_l2.go b/core/services/ocr2/plugins/liquiditymanager/bridge/opstack/l1_to_l2.go index f80cc86634..e9cf63c98a 100644 --- a/core/services/ocr2/plugins/liquiditymanager/bridge/opstack/l1_to_l2.go +++ b/core/services/ocr2/plugins/liquiditymanager/bridge/opstack/l1_to_l2.go @@ -47,6 +47,7 @@ type l1ToL2Bridge struct { } func NewL1ToL2Bridge( + ctx context.Context, lggr logger.Logger, localSelector, remoteSelector models.NetworkSelector, @@ -77,8 +78,6 @@ func NewL1ToL2Bridge( "", ) - // TODO: FIXME pass valid context - ctx := context.Background() err := l1LogPoller.RegisterFilter(ctx, logpoller.Filter{ Addresses: []common.Address{l1LiquidityManagerAddress}, // emits LiquidityTransferred Name: l1FilterName, diff --git a/core/services/ocr2/plugins/liquiditymanager/bridge/opstack/l2_to_l1.go b/core/services/ocr2/plugins/liquiditymanager/bridge/opstack/l2_to_l1.go index ce354593a7..ea04652e31 100644 --- a/core/services/ocr2/plugins/liquiditymanager/bridge/opstack/l2_to_l1.go +++ b/core/services/ocr2/plugins/liquiditymanager/bridge/opstack/l2_to_l1.go @@ -41,6 +41,7 @@ type l2ToL1Bridge struct { } func NewL2ToL1Bridge( + ctx context.Context, lggr logger.Logger, localSelector, remoteSelector models.NetworkSelector, @@ -68,8 +69,6 @@ func NewL2ToL1Bridge( remoteChain.Name, "", ) - // TODO (ogtownsend): pass context from above - ctx := context.Background() err := l2LogPoller.RegisterFilter( ctx, logpoller.Filter{ diff --git a/core/services/ocr2/plugins/liquiditymanager/bridge/testonlybridge/l1_to_l1.go b/core/services/ocr2/plugins/liquiditymanager/bridge/testonlybridge/l1_to_l1.go index 7bed294bbb..1ee8096df2 100644 --- a/core/services/ocr2/plugins/liquiditymanager/bridge/testonlybridge/l1_to_l1.go +++ b/core/services/ocr2/plugins/liquiditymanager/bridge/testonlybridge/l1_to_l1.go @@ -51,13 +51,13 @@ type testBridge struct { } func New( + ctx context.Context, sourceSelector, destSelector models.NetworkSelector, sourceLiquidityManagerAddress, destLiquidityManagerAddress, sourceAdapter, destAdapter models.Address, sourceLogPoller, destLogPoller logpoller.LogPoller, sourceClient, destClient client.Client, lggr logger.Logger, ) (*testBridge, error) { - ctx := context.Background() err := sourceLogPoller.RegisterFilter( ctx, logpoller.Filter{ diff --git a/core/services/ocr2/plugins/liquiditymanager/bridge/testonlybridge/l1_to_l1_test.go b/core/services/ocr2/plugins/liquiditymanager/bridge/testonlybridge/l1_to_l1_test.go index af0c5b002d..24cc849e8e 100644 --- a/core/services/ocr2/plugins/liquiditymanager/bridge/testonlybridge/l1_to_l1_test.go +++ b/core/services/ocr2/plugins/liquiditymanager/bridge/testonlybridge/l1_to_l1_test.go @@ -728,7 +728,7 @@ func TestNew(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.expect(t, tt.args) defer tt.assert(t, tt.args) - got, err := New(tt.args.sourceSelector, tt.args.destSelector, tt.args.sourceLiquidityManagerAddress, tt.args.destLiquidityManagerAddress, tt.args.sourceAdapter, tt.args.destAdapter, tt.args.sourceLogPoller, tt.args.destLogPoller, tt.args.sourceClient, tt.args.destClient, tt.args.lggr) + got, err := New(testutils.Context(t), tt.args.sourceSelector, tt.args.destSelector, tt.args.sourceLiquidityManagerAddress, tt.args.destLiquidityManagerAddress, tt.args.sourceAdapter, tt.args.destAdapter, tt.args.sourceLogPoller, tt.args.destLogPoller, tt.args.sourceClient, tt.args.destClient, tt.args.lggr) if tt.wantErr { require.Error(t, err) } else { diff --git a/core/services/ocr2/plugins/liquiditymanager/factory.go b/core/services/ocr2/plugins/liquiditymanager/factory.go index 13e7509c2f..754fcca9a8 100644 --- a/core/services/ocr2/plugins/liquiditymanager/factory.go +++ b/core/services/ocr2/plugins/liquiditymanager/factory.go @@ -67,7 +67,7 @@ func (p PluginFactory) buildRebalancer() (rebalalgo.RebalancingAlgo, error) { case models.RebalancerTypeMinLiquidity: return rebalalgo.NewMinLiquidityRebalancer(p.lggr), nil case models.RebalancerTypeTargetAndMin: - return rebalalgo.NewTargetMinBalancer(p.lggr), nil + return rebalalgo.NewTargetMinBalancer(p.lggr, p.config), nil default: return nil, fmt.Errorf("invalid rebalancer type %s", p.config.RebalancerConfig.Type) } diff --git a/core/services/ocr2/plugins/liquiditymanager/graph/graph.go b/core/services/ocr2/plugins/liquiditymanager/graph/graph.go index 287272a9d1..fcea5e5957 100644 --- a/core/services/ocr2/plugins/liquiditymanager/graph/graph.go +++ b/core/services/ocr2/plugins/liquiditymanager/graph/graph.go @@ -10,11 +10,12 @@ import ( // GraphWriter provides write access to the liquidity graph. type GraphWriter interface { + // Add adds new data and connection to the graph. Add(from, to Data) error - // AddEdges adds a list of edges to the graph. - AddEdges(edges []models.Edge) error // SetLiquidity sets the liquidity of the provided network. SetLiquidity(n models.NetworkSelector, liquidity *big.Int) bool + // SetTargetLiquidity sets the target liquidity of the provided network. + SetTargetLiquidity(n models.NetworkSelector, liquidity *big.Int) bool } // NodeReader provides read access to the data saved in the graph nodes. diff --git a/core/services/ocr2/plugins/liquiditymanager/graph/reader_test.go b/core/services/ocr2/plugins/liquiditymanager/graph/reader_test.go new file mode 100644 index 0000000000..58721e9081 --- /dev/null +++ b/core/services/ocr2/plugins/liquiditymanager/graph/reader_test.go @@ -0,0 +1,182 @@ +package graph + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/liquiditymanager/models" +) + +func TestGrpah_NodeReaderGetters(t *testing.T) { + g := NewGraph() + + data1 := Data{ + Liquidity: big.NewInt(1), + TokenAddress: models.Address(common.HexToAddress("0x11")), + LiquidityManagerAddress: models.Address(common.HexToAddress("0x12")), + XChainLiquidityManagers: map[models.NetworkSelector]XChainLiquidityManagerData{}, + ConfigDigest: models.ConfigDigest{ + ConfigDigest: [32]byte{1}, + }, + NetworkSelector: models.NetworkSelector(1), + } + require.True(t, g.(GraphTest).AddNetwork(models.NetworkSelector(1), data1)) + + tests := []struct { + name string + net models.NetworkSelector + data *Data + }{ + { + name: "happy path", + net: models.NetworkSelector(1), + data: &data1, + }, + { + name: "not exist", + net: models.NetworkSelector(333), + data: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + liq, err := g.GetLiquidity(tc.net) + if tc.data == nil { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.data.Liquidity, liq) + } + + tokenAddr, err := g.GetTokenAddress(tc.net) + if tc.data == nil { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.data.TokenAddress, tokenAddr) + } + + liqManagerAddr, err := g.GetLiquidityManagerAddress(tc.net) + if tc.data == nil { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.data.LiquidityManagerAddress, liqManagerAddr) + } + + xChainData, err := g.GetXChainLiquidityManagerData(tc.net) + if tc.data == nil { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.data.XChainLiquidityManagers, xChainData) + } + + data, err := g.GetData(tc.net) + if tc.data == nil { + require.Error(t, err) + } else { + require.NoError(t, err) + require.True(t, tc.data.Equals(data)) + } + }) + } +} + +func TestGraph_FindPath(t *testing.T) { + g := NewGraph() + + data1 := Data{ + Liquidity: big.NewInt(1), + TokenAddress: models.Address(common.HexToAddress("0x11")), + LiquidityManagerAddress: models.Address(common.HexToAddress("0x12")), + XChainLiquidityManagers: map[models.NetworkSelector]XChainLiquidityManagerData{}, + ConfigDigest: models.ConfigDigest{ + ConfigDigest: [32]byte{1}, + }, + NetworkSelector: models.NetworkSelector(1), + } + require.True(t, g.(GraphTest).AddNetwork(models.NetworkSelector(1), data1)) + + data2 := Data{ + Liquidity: big.NewInt(2), + TokenAddress: models.Address(common.HexToAddress("0x21")), + LiquidityManagerAddress: models.Address(common.HexToAddress("0x22")), + XChainLiquidityManagers: map[models.NetworkSelector]XChainLiquidityManagerData{}, + ConfigDigest: models.ConfigDigest{ + ConfigDigest: [32]byte{2}, + }, + NetworkSelector: models.NetworkSelector(2), + } + require.True(t, g.(GraphTest).AddNetwork(models.NetworkSelector(2), data2)) + + data3 := Data{ + Liquidity: big.NewInt(3), + TokenAddress: models.Address(common.HexToAddress("0x31")), + LiquidityManagerAddress: models.Address(common.HexToAddress("0x32")), + XChainLiquidityManagers: map[models.NetworkSelector]XChainLiquidityManagerData{}, + ConfigDigest: models.ConfigDigest{ + ConfigDigest: [32]byte{3}, + }, + NetworkSelector: models.NetworkSelector(3), + } + require.True(t, g.(GraphTest).AddNetwork(models.NetworkSelector(3), data3)) + + require.NoError(t, g.(GraphTest).AddConnection(models.NetworkSelector(1), models.NetworkSelector(2))) + require.NoError(t, g.(GraphTest).AddConnection(models.NetworkSelector(2), models.NetworkSelector(3))) + + tests := []struct { + name string + from models.NetworkSelector + to models.NetworkSelector + maxEdges int + want []models.NetworkSelector + }{ + { + name: "happy path 2 edges", + from: models.NetworkSelector(1), + to: models.NetworkSelector(3), + maxEdges: 2, + want: []models.NetworkSelector{models.NetworkSelector(2), models.NetworkSelector(3)}, + }, + { + name: "happy path 1 edge", + from: models.NetworkSelector(1), + to: models.NetworkSelector(2), + maxEdges: 1, + want: []models.NetworkSelector{models.NetworkSelector(2)}, + }, + { + name: "not enough edges", + from: models.NetworkSelector(1), + to: models.NetworkSelector(3), + maxEdges: 1, + want: []models.NetworkSelector{}, + }, + { + name: "no path", + from: models.NetworkSelector(2), + to: models.NetworkSelector(10), + want: []models.NetworkSelector{}, + }, + { + name: "same node", + from: models.NetworkSelector(1), + to: models.NetworkSelector(1), + want: []models.NetworkSelector{}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + path := g.FindPath(tc.from, tc.to, tc.maxEdges, func(nodes ...Data) bool { + return true + }) + require.Equal(t, tc.want, path) + }) + } +} diff --git a/core/services/ocr2/plugins/liquiditymanager/graph/writer.go b/core/services/ocr2/plugins/liquiditymanager/graph/writer.go index 2da8207d0d..8671087b12 100644 --- a/core/services/ocr2/plugins/liquiditymanager/graph/writer.go +++ b/core/services/ocr2/plugins/liquiditymanager/graph/writer.go @@ -20,19 +20,28 @@ func (g *liquidityGraph) Add(from, to Data) error { return nil } -func (g *liquidityGraph) AddEdges(edges []models.Edge) error { +func (g *liquidityGraph) SetLiquidity(n models.NetworkSelector, liquidity *big.Int) bool { g.lock.Lock() defer g.lock.Unlock() - for _, edge := range edges { - if err := g.addConnection(edge.Source, edge.Dest); err != nil { - return fmt.Errorf("add connection %d -> %d: %w", edge.Source, edge.Dest, err) - } + if !g.hasNetwork(n) { + return false } - return nil + + prev := g.data[n] + g.data[n] = Data{ + Liquidity: liquidity, + TokenAddress: prev.TokenAddress, + LiquidityManagerAddress: prev.LiquidityManagerAddress, + ConfigDigest: prev.ConfigDigest, + NetworkSelector: prev.NetworkSelector, + MinimumLiquidity: prev.MinimumLiquidity, + TargetLiquidity: prev.TargetLiquidity, + } + return true } -func (g *liquidityGraph) SetLiquidity(n models.NetworkSelector, liquidity *big.Int) bool { +func (g *liquidityGraph) SetTargetLiquidity(n models.NetworkSelector, target *big.Int) bool { g.lock.Lock() defer g.lock.Unlock() @@ -42,13 +51,13 @@ func (g *liquidityGraph) SetLiquidity(n models.NetworkSelector, liquidity *big.I prev := g.data[n] g.data[n] = Data{ - Liquidity: liquidity, + Liquidity: prev.Liquidity, TokenAddress: prev.TokenAddress, LiquidityManagerAddress: prev.LiquidityManagerAddress, ConfigDigest: prev.ConfigDigest, NetworkSelector: prev.NetworkSelector, MinimumLiquidity: prev.MinimumLiquidity, - TargetLiquidity: prev.TargetLiquidity, + TargetLiquidity: target, } return true } diff --git a/core/services/ocr2/plugins/liquiditymanager/models/config.go b/core/services/ocr2/plugins/liquiditymanager/models/config.go index cf3249e4d1..aaa4258651 100644 --- a/core/services/ocr2/plugins/liquiditymanager/models/config.go +++ b/core/services/ocr2/plugins/liquiditymanager/models/config.go @@ -17,12 +17,10 @@ type PluginConfig struct { } type RebalancerConfig struct { - Type string `json:"type"` -} - -type NetworkTarget struct { - Network NetworkSelector `json:"network,string"` - Target *big.Int `json:"target"` + Type string `json:"type"` + DefaultTarget *big.Int `json:"defaultTarget"` + // NetworkTargetOverrides is a map of NetworkSelector to big Int amounts + NetworkTargetOverrides map[NetworkSelector]*big.Int `json:"networkTargetOverrides"` } func ValidateRebalancerConfig(config RebalancerConfig) error { @@ -47,6 +45,7 @@ var ( AllRebalancerTypes = []string{ RebalancerTypePingPong, RebalancerTypeMinLiquidity, + RebalancerTypeTargetAndMin, } ) diff --git a/core/services/ocr2/plugins/liquiditymanager/plugin.go b/core/services/ocr2/plugins/liquiditymanager/plugin.go index e5b0d37ec3..9917196061 100644 --- a/core/services/ocr2/plugins/liquiditymanager/plugin.go +++ b/core/services/ocr2/plugins/liquiditymanager/plugin.go @@ -507,7 +507,7 @@ func (p *Plugin) loadPendingTransfers(ctx context.Context, lggr logger.Logger) ( } for _, edge := range edges { logger := lggr.With("sourceNetwork", edge.Source, "sourceChainID", edge.Source.ChainID(), "destNetwork", edge.Dest, "destChainID", edge.Dest.ChainID()) - bridge, err := p.bridgeFactory.NewBridge(edge.Source, edge.Dest) + bridge, err := p.bridgeFactory.NewBridge(ctx, edge.Source, edge.Dest) if err != nil { return nil, fmt.Errorf("init bridge: %w", err) } @@ -607,7 +607,7 @@ func (p *Plugin) computeResolvedTransfersQuorum(observations []models.Observatio } medianizedNativeFee := rebalcalc.BigIntSortedMiddle(bridgeFees) medianizedDateUnix := rebalcalc.BigIntSortedMiddle(datesUnix) - bridge, err := p.bridgeFactory.NewBridge(k.From, k.To) + bridge, err := p.bridgeFactory.GetBridge(k.From, k.To) if err != nil { return nil, fmt.Errorf("init bridge: %w", err) } @@ -650,7 +650,7 @@ func (p *Plugin) resolveProposedTransfers(ctx context.Context, lggr logger.Logge resolvedTransfers := make([]models.Transfer, 0, len(outcome.ProposedTransfers)) for _, proposedTransfer := range outcome.ProposedTransfers { - bridge, err := p.bridgeFactory.NewBridge(proposedTransfer.From, proposedTransfer.To) + bridge, err := p.bridgeFactory.NewBridge(ctx, proposedTransfer.From, proposedTransfer.To) if err != nil { return nil, fmt.Errorf("init bridge: %w", err) } diff --git a/core/services/ocr2/plugins/liquiditymanager/plugin_test.go b/core/services/ocr2/plugins/liquiditymanager/plugin_test.go index cb3af9d08f..aeb0501083 100644 --- a/core/services/ocr2/plugins/liquiditymanager/plugin_test.go +++ b/core/services/ocr2/plugins/liquiditymanager/plugin_test.go @@ -289,7 +289,7 @@ func TestPlugin_Observation(t *testing.T) { for sourceDest, bridgeFn := range tc.bridges { br, err2 := bridgeFn(t) p.bridgeFactory. - On("NewBridge", sourceDest[0], sourceDest[1]). + On("NewBridge", ctx, sourceDest[0], sourceDest[1]). Return(br, err2) } @@ -660,7 +660,7 @@ func TestPlugin_Outcome(t *testing.T) { for sourceDest, bridgeFn := range tc.bridges { br, err := bridgeFn(t) p.bridgeFactory. - On("NewBridge", sourceDest[0], sourceDest[1]). + On("GetBridge", sourceDest[0], sourceDest[1]). Return(br, err) } @@ -1135,7 +1135,8 @@ func TestPlugin_E2EWithMocks(t *testing.T) { for _, edge := range edges { br, ok := n.bridges[[2]models.NetworkSelector{edge.Source, edge.Dest}] require.True(t, ok, "the test case is wrong, bridge is not defined %d->%d", edge.Source, edge.Dest) - n.bridgeFactory.On("NewBridge", edge.Source, edge.Dest).Return(br, nil).Maybe() + n.bridgeFactory.On("NewBridge", mock.Anything /* cancelContext */, edge.Source, edge.Dest).Return(br, nil).Maybe() + n.bridgeFactory.On("GetBridge", edge.Source, edge.Dest).Return(br, nil).Maybe() pendingTransfers := make([]models.PendingTransfer, 0) for _, tr := range round.pendingTransfersPerNode[i] { diff --git a/core/services/ocr2/plugins/liquiditymanager/rebalalgo/target_balance_with_minimum.go b/core/services/ocr2/plugins/liquiditymanager/rebalalgo/target_balance_with_minimum.go index c479b0f657..b7e492f541 100644 --- a/core/services/ocr2/plugins/liquiditymanager/rebalalgo/target_balance_with_minimum.go +++ b/core/services/ocr2/plugins/liquiditymanager/rebalalgo/target_balance_with_minimum.go @@ -13,16 +13,18 @@ import ( // TargetMinBalancer tries to reach balance using a target and minimum liquidity that is configured on each network. type TargetMinBalancer struct { - lggr logger.Logger + lggr logger.Logger + config models.PluginConfig } -func NewTargetMinBalancer(lggr logger.Logger) *TargetMinBalancer { +func NewTargetMinBalancer(lggr logger.Logger, config models.PluginConfig) *TargetMinBalancer { return &TargetMinBalancer{ - lggr: lggr.With("service", "TargetMinBalancer"), + lggr: lggr.With("service", "TargetMinBalancer"), + config: config, } } -type determineTransfersFunc func(graphLater graph.Graph, targetNetwork models.NetworkSelector, networkFunds map[models.NetworkSelector]*Funds) ([]models.ProposedTransfer, error) +type determineTransfersFunc func(graphFuture graph.Graph, targetNetwork models.NetworkSelector, networkFunds map[models.NetworkSelector]*Funds) ([]models.ProposedTransfer, error) func (r *TargetMinBalancer) ComputeTransfersToBalance(graphNow graph.Graph, nonExecutedTransfers []UnexecutedTransfer) ([]models.ProposedTransfer, error) { nonExecutedTransfers = filterUnexecutedTransfers(nonExecutedTransfers) @@ -59,13 +61,13 @@ func (r *TargetMinBalancer) ComputeTransfersToBalance(graphNow graph.Graph, nonE func (r *TargetMinBalancer) rebalancingRound(graphNow graph.Graph, nonExecutedTransfers []UnexecutedTransfer, transfersFunc determineTransfersFunc) ([]models.ProposedTransfer, error) { var err error - graphLater, err := getExpectedGraph(graphNow, nonExecutedTransfers) + graphFuture, err := r.getExpectedGraph(graphNow, nonExecutedTransfers) if err != nil { return nil, fmt.Errorf("get expected graph: %w", err) } r.lggr.Debugf("finding networks that require funding") - networksRequiringFunding, networkFunds, err := r.findNetworksRequiringFunding(graphNow, graphLater) + networksRequiringFunding, networkFunds, err := r.findNetworksRequiringFunding(graphNow, graphFuture) if err != nil { return nil, fmt.Errorf("find networks that require funding: %w", err) } @@ -75,17 +77,17 @@ func (r *TargetMinBalancer) rebalancingRound(graphNow graph.Graph, nonExecutedTr proposedTransfers := make([]models.ProposedTransfer, 0) for _, net := range networksRequiringFunding { r.lggr.Debugf("finding transfers for network %v", net) - potentialTransfers, err1 := transfersFunc(graphLater, net, networkFunds) + potentialTransfers, err1 := transfersFunc(graphFuture, net, networkFunds) if err1 != nil { return nil, fmt.Errorf("finding transfers for network %v: %w", net, err1) } - dataLater, err2 := graphLater.GetData(net) + dataFuture, err2 := graphFuture.GetData(net) if err2 != nil { - return nil, fmt.Errorf("get data later of net %v: %w", net, err2) + return nil, fmt.Errorf("get future data of net %v: %w", net, err2) } - liqDiffLater := new(big.Int).Sub(dataLater.TargetLiquidity, dataLater.Liquidity) - netProposedTransfers, err3 := r.applyProposedTransfers(graphLater, potentialTransfers, liqDiffLater) + liqDiffFuture := new(big.Int).Sub(dataFuture.TargetLiquidity, dataFuture.Liquidity) + netProposedTransfers, err3 := r.applyProposedTransfers(graphFuture, potentialTransfers, liqDiffFuture) if err3 != nil { return nil, fmt.Errorf("applying transfers: %w", err3) } @@ -95,62 +97,101 @@ func (r *TargetMinBalancer) rebalancingRound(graphNow graph.Graph, nonExecutedTr return proposedTransfers, nil } -func (r *TargetMinBalancer) findNetworksRequiringFunding(graphNow, graphLater graph.Graph) ([]models.NetworkSelector, map[models.NetworkSelector]*Funds, error) { - mapNetworkFunds := make(map[models.NetworkSelector]*Funds) - liqDiffsLater := make(map[models.NetworkSelector]*big.Int) +// getExpectedGraph returns a copy of the graph instance with all the non-executed transfers applied and targets set for each network. +func (r *TargetMinBalancer) getExpectedGraph(g graph.Graph, nonExecutedTransfers []UnexecutedTransfer) (graph.Graph, error) { + expG := g.Clone() + + for _, tr := range nonExecutedTransfers { + liqTo, err := expG.GetLiquidity(tr.ToNetwork()) + if err != nil { + return nil, err + } + expG.SetLiquidity(tr.ToNetwork(), big.NewInt(0).Add(liqTo, tr.TransferAmount())) + + // we only subtract from the sender if the transfer is still in progress, otherwise the source value would have already been updated + switch tr.TransferStatus() { + case models.TransferStatusProposed, models.TransferStatusInflight: + liqFrom, err := expG.GetLiquidity(tr.FromNetwork()) + if err != nil { + return nil, err + } + expG.SetLiquidity(tr.FromNetwork(), big.NewInt(0).Sub(liqFrom, tr.TransferAmount())) + } + } - //TODO: LM-23 Create minTokenTransfer config to filter-out small rebalance txs - // check that the transfer is not tiny, we should only transfer if it is significant. What is too tiny? - // we could prevent this by only making a network requiring funding if its below X% of the target + for _, selector := range expG.GetNetworks() { + target := r.config.RebalancerConfig.DefaultTarget + override, ok := r.config.RebalancerConfig.NetworkTargetOverrides[selector] + if ok { + target = override + } + expG.SetTargetLiquidity(selector, target) + } + + return expG, nil +} + +func (r *TargetMinBalancer) findNetworksRequiringFunding(graphNow, graphFuture graph.Graph) ([]models.NetworkSelector, map[models.NetworkSelector]*Funds, error) { + mapNetworkFunds := make(map[models.NetworkSelector]*Funds) + liqDiffsFuture := make(map[models.NetworkSelector]*big.Int) res := make([]models.NetworkSelector, 0) for _, net := range graphNow.GetNetworks() { //use min here for transferable. because we don't know when the transfers will complete and want to avoid issues - transferableAmount, ataErr := availableTransferableAmount(graphNow, graphLater, net) + transferableAmount, ataErr := availableTransferableAmount(graphNow, graphFuture, net) if ataErr != nil { return nil, nil, fmt.Errorf("getting available transferrable amount for net %d: %v", net, ataErr) } - dataLater, err := graphLater.GetData(net) + dataFuture, err := graphFuture.GetData(net) if err != nil { - return nil, nil, fmt.Errorf("get data later of net %v: %w", net, err) + return nil, nil, fmt.Errorf("get future data of net %v: %w", net, err) } - liqDiffLater := new(big.Int).Sub(dataLater.TargetLiquidity, dataLater.Liquidity) + liqDiffFuture := new(big.Int).Sub(dataFuture.TargetLiquidity, dataFuture.Liquidity) mapNetworkFunds[net] = &Funds{ AvailableAmount: transferableAmount, } - if liqDiffLater.Cmp(big.NewInt(0)) <= 0 { + if liqDiffFuture.Cmp(big.NewInt(0)) <= 0 { + continue + } + + // only if we are below 5% else it's too little to worry about now + fivePercent := big.NewInt(5) + hundred := big.NewInt(100) + value := new(big.Int).Mul(dataFuture.TargetLiquidity, fivePercent) + value.Div(value, hundred) + if liqDiffFuture.Cmp(value) < 0 { continue } - liqDiffsLater[net] = liqDiffLater + liqDiffsFuture[net] = liqDiffFuture res = append(res, net) } - sort.Slice(res, func(i, j int) bool { return liqDiffsLater[res[i]].Cmp(liqDiffsLater[res[j]]) > 0 }) + sort.Slice(res, func(i, j int) bool { return liqDiffsFuture[res[i]].Cmp(liqDiffsFuture[res[j]]) > 0 }) return res, mapNetworkFunds, nil } -func (r *TargetMinBalancer) oneHopTransfers(graphLater graph.Graph, targetNetwork models.NetworkSelector, networkFunds map[models.NetworkSelector]*Funds) ([]models.ProposedTransfer, error) { +func (r *TargetMinBalancer) oneHopTransfers(graphFuture graph.Graph, targetNetwork models.NetworkSelector, networkFunds map[models.NetworkSelector]*Funds) ([]models.ProposedTransfer, error) { zero := big.NewInt(0) potentialTransfers := make([]models.ProposedTransfer, 0) - neighbors, exist := graphLater.GetNeighbors(targetNetwork, false) + neighbors, exist := graphFuture.GetNeighbors(targetNetwork, false) if !exist { r.lggr.Debugf("no neighbors found for %v", targetNetwork) return nil, nil } - targetLater, err := graphLater.GetData(targetNetwork) + targetFuture, err := graphFuture.GetData(targetNetwork) if err != nil { - return nil, fmt.Errorf("get data later of net %v: %w", targetNetwork, err) + return nil, fmt.Errorf("get data Future of net %v: %w", targetNetwork, err) } for _, source := range neighbors { - transferAmount := new(big.Int).Sub(targetLater.TargetLiquidity, targetLater.Liquidity) + transferAmount := new(big.Int).Sub(targetFuture.TargetLiquidity, targetFuture.Liquidity) r.lggr.Debugf("checking transfer from %v to %v for amount %v", source, targetNetwork, transferAmount) //source network available transferable amount - srcData, dErr := graphLater.GetData(source) + srcData, dErr := graphFuture.GetData(source) if dErr != nil { - return nil, fmt.Errorf("error during GetData for %v in graphLater: %v", source, dErr) + return nil, fmt.Errorf("error during GetData for %v in graphFuture: %v", source, dErr) } srcAvailableAmount := new(big.Int).Sub(srcData.Liquidity, srcData.MinimumLiquidity) srcAmountToTarget := new(big.Int).Sub(srcData.Liquidity, srcData.TargetLiquidity) @@ -180,40 +221,40 @@ func (r *TargetMinBalancer) oneHopTransfers(graphLater graph.Graph, targetNetwor } // twoHopTransfers finds networks that can increase liquidity of the target network with an intermediate network. -func (r *TargetMinBalancer) twoHopTransfers(graphLater graph.Graph, targetNetwork models.NetworkSelector, networkFunds map[models.NetworkSelector]*Funds) ([]models.ProposedTransfer, error) { +func (r *TargetMinBalancer) twoHopTransfers(graphFuture graph.Graph, targetNetwork models.NetworkSelector, networkFunds map[models.NetworkSelector]*Funds) ([]models.ProposedTransfer, error) { zero := big.NewInt(0) iterator := func(nodes ...graph.Data) bool { return true } potentialTransfers := make([]models.ProposedTransfer, 0) - for _, source := range graphLater.GetNetworks() { + for _, source := range graphFuture.GetNetworks() { if source == targetNetwork { continue } - path := graphLater.FindPath(source, targetNetwork, 2, iterator) + path := graphFuture.FindPath(source, targetNetwork, 2, iterator) if len(path) != 2 { continue } middle := path[0] - targetData, err := graphLater.GetData(targetNetwork) + targetData, err := graphFuture.GetData(targetNetwork) if err != nil { - return nil, fmt.Errorf("error during GetData for %v in graphLater: %v", targetNetwork, err) + return nil, fmt.Errorf("error during GetData for %v in graphFuture: %v", targetNetwork, err) } transferAmount := new(big.Int).Sub(targetData.TargetLiquidity, targetData.Liquidity) r.lggr.Debugf("checking transfer from %v -> %v -> %v for amount %v", source, middle, targetNetwork, transferAmount) //source network available transferable amount - srcData, dErr := graphLater.GetData(source) + srcData, dErr := graphFuture.GetData(source) if dErr != nil { - return nil, fmt.Errorf("error during GetData for %v in graphLater: %v", source, dErr) + return nil, fmt.Errorf("error during GetData for %v in graphFuture: %v", source, dErr) } srcAvailableAmount := new(big.Int).Sub(srcData.Liquidity, srcData.MinimumLiquidity) srcAmountToTarget := new(big.Int).Sub(srcData.Liquidity, srcData.TargetLiquidity) //middle network available transferable amount - middleData, dErr := graphLater.GetData(middle) + middleData, dErr := graphFuture.GetData(middle) if dErr != nil { - return nil, fmt.Errorf("error during GetData for %v in graphLater: %v", middle, dErr) + return nil, fmt.Errorf("error during GetData for %v in graphFuture: %v", middle, dErr) } middleAvailableAmount := new(big.Int).Sub(middleData.Liquidity, middleData.MinimumLiquidity) middleAmountToTarget := new(big.Int).Sub(middleData.Liquidity, middleData.TargetLiquidity) @@ -251,7 +292,7 @@ func (r *TargetMinBalancer) twoHopTransfers(graphLater graph.Graph, targetNetwor // applyProposedTransfers applies the proposed transfers to the graph. // increments the raised funds and gives a refund to the sender if more funds have been raised than the required amount. // It updates the liquidity of the sender and receiver networks in the graph. It stops further transfers if all funds have been raised. -func (r *TargetMinBalancer) applyProposedTransfers(graphLater graph.Graph, potentialTransfers []models.ProposedTransfer, requiredAmount *big.Int) ([]models.ProposedTransfer, error) { +func (r *TargetMinBalancer) applyProposedTransfers(graphFuture graph.Graph, potentialTransfers []models.ProposedTransfer, requiredAmount *big.Int) ([]models.ProposedTransfer, error) { // sort by amount,sender,receiver sort.Slice(potentialTransfers, func(i, j int) bool { if potentialTransfers[i].Amount.Cmp(potentialTransfers[j].Amount) == 0 { @@ -272,7 +313,7 @@ func (r *TargetMinBalancer) applyProposedTransfers(graphLater graph.Graph, poten continue } - senderData, err := graphLater.GetData(d.From) + senderData, err := graphFuture.GetData(d.From) if err != nil { return nil, fmt.Errorf("get liquidity of sender %v: %w", d.From, err) } @@ -298,17 +339,17 @@ func (r *TargetMinBalancer) applyProposedTransfers(graphLater graph.Graph, poten r.lggr.Debugf("applying transfer: %v", d) proposedTransfers = append(proposedTransfers, models.ProposedTransfer{From: d.From, To: d.To, Amount: d.Amount}) - liqBefore, err := graphLater.GetLiquidity(d.To) + liqBefore, err := graphFuture.GetLiquidity(d.To) if err != nil { return nil, fmt.Errorf("get liquidity of transfer receiver %v: %w", d.To, err) } - graphLater.SetLiquidity(d.To, big.NewInt(0).Add(liqBefore, d.Amount.ToInt())) + graphFuture.SetLiquidity(d.To, big.NewInt(0).Add(liqBefore, d.Amount.ToInt())) - liqBefore, err = graphLater.GetLiquidity(d.From) + liqBefore, err = graphFuture.GetLiquidity(d.From) if err != nil { return nil, fmt.Errorf("get liquidity of sender %v: %w", d.From, err) } - graphLater.SetLiquidity(d.From, big.NewInt(0).Sub(liqBefore, d.Amount.ToInt())) + graphFuture.SetLiquidity(d.From, big.NewInt(0).Sub(liqBefore, d.Amount.ToInt())) if fundsRaised.Cmp(requiredAmount) >= 0 { r.lggr.Debugf("all funds raised skipping further transfers") diff --git a/core/services/ocr2/plugins/liquiditymanager/rebalalgo/target_balance_with_minimum_test.go b/core/services/ocr2/plugins/liquiditymanager/rebalalgo/target_balance_with_minimum_test.go index 150fad455b..77e0f90d30 100644 --- a/core/services/ocr2/plugins/liquiditymanager/rebalalgo/target_balance_with_minimum_test.go +++ b/core/services/ocr2/plugins/liquiditymanager/rebalalgo/target_balance_with_minimum_test.go @@ -226,21 +226,30 @@ func TestTargetMinBalancer_ComputeTransfersToBalance_arb_eth_opt_0(t *testing.T) for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + overrides := make(map[models.NetworkSelector]*big.Int) g := graph.NewGraph() for net, b := range tc.balances { g.(graph.GraphTest).AddNetwork(net, graph.Data{ Liquidity: big.NewInt(b), NetworkSelector: net, MinimumLiquidity: big.NewInt(tc.minimums[net]), - TargetLiquidity: big.NewInt(tc.targets[net]), }) + overrides[net] = big.NewInt(tc.targets[net]) } assert.NoError(t, g.(graph.GraphTest).AddConnection(eth, arb)) assert.NoError(t, g.(graph.GraphTest).AddConnection(arb, eth)) assert.NoError(t, g.(graph.GraphTest).AddConnection(eth, opt)) assert.NoError(t, g.(graph.GraphTest).AddConnection(opt, eth)) - r := NewTargetMinBalancer(lggr) + pluginConfigOverrides := models.PluginConfig{ + RebalancerConfig: models.RebalancerConfig{ + Type: "target-and-min", + DefaultTarget: big.NewInt(5), + NetworkTargetOverrides: overrides, + }, + } + + r := NewTargetMinBalancer(lggr, pluginConfigOverrides) unexecuted := make([]UnexecutedTransfer, 0, len(tc.pendingTransfers)) for _, tr := range tc.pendingTransfers { @@ -336,21 +345,30 @@ func TestTargetMinBalancer_ComputeTransfersToBalance_arb_eth_opt_pending_status_ for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + overrides := make(map[models.NetworkSelector]*big.Int) + g := graph.NewGraph() for net, b := range tc.balances { g.(graph.GraphTest).AddNetwork(net, graph.Data{ Liquidity: big.NewInt(b), NetworkSelector: net, MinimumLiquidity: big.NewInt(tc.minimums[net]), - TargetLiquidity: big.NewInt(tc.targets[net]), }) + overrides[net] = big.NewInt(tc.targets[net]) } assert.NoError(t, g.(graph.GraphTest).AddConnection(eth, arb)) assert.NoError(t, g.(graph.GraphTest).AddConnection(arb, eth)) assert.NoError(t, g.(graph.GraphTest).AddConnection(eth, opt)) assert.NoError(t, g.(graph.GraphTest).AddConnection(opt, eth)) - r := NewTargetMinBalancer(lggr) + pluginConfigOverrides := models.PluginConfig{ + RebalancerConfig: models.RebalancerConfig{ + Type: "target-and-min", + DefaultTarget: big.NewInt(5), + NetworkTargetOverrides: overrides, + }, + } + r := NewTargetMinBalancer(lggr, pluginConfigOverrides) unexecuted := make([]UnexecutedTransfer, 0, len(tc.pendingTransfers)) for _, tr := range tc.pendingTransfers { @@ -476,14 +494,15 @@ func TestTargetMinBalancer_ComputeTransfersToBalance_arb_eth_opt_base(t *testing for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + overrides := make(map[models.NetworkSelector]*big.Int) g := graph.NewGraph() for net, b := range tc.balances { g.(graph.GraphTest).AddNetwork(net, graph.Data{ Liquidity: big.NewInt(b), NetworkSelector: net, MinimumLiquidity: big.NewInt(tc.minimums[net]), - TargetLiquidity: big.NewInt(tc.targets[net]), }) + overrides[net] = big.NewInt(tc.targets[net]) } assert.NoError(t, g.(graph.GraphTest).AddConnection(eth, arb)) assert.NoError(t, g.(graph.GraphTest).AddConnection(arb, eth)) @@ -492,7 +511,14 @@ func TestTargetMinBalancer_ComputeTransfersToBalance_arb_eth_opt_base(t *testing assert.NoError(t, g.(graph.GraphTest).AddConnection(eth, base)) assert.NoError(t, g.(graph.GraphTest).AddConnection(base, eth)) - r := NewTargetMinBalancer(lggr) + pluginConfigOverrides := models.PluginConfig{ + RebalancerConfig: models.RebalancerConfig{ + Type: "target-and-min", + DefaultTarget: big.NewInt(5), + NetworkTargetOverrides: overrides, + }, + } + r := NewTargetMinBalancer(lggr, pluginConfigOverrides) unexecuted := make([]UnexecutedTransfer, 0, len(tc.pendingTransfers)) for _, tr := range tc.pendingTransfers { @@ -586,14 +612,16 @@ func TestTargetMinBalancer_ComputeTransfersToBalance_islands_in_graph(t *testing for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + overrides := make(map[models.NetworkSelector]*big.Int) + g := graph.NewGraph() for net, b := range tc.balances { g.(graph.GraphTest).AddNetwork(net, graph.Data{ Liquidity: big.NewInt(b), NetworkSelector: net, MinimumLiquidity: big.NewInt(tc.minimums[net]), - TargetLiquidity: big.NewInt(tc.targets[net]), }) + overrides[net] = big.NewInt(tc.targets[net]) } assert.NoError(t, g.(graph.GraphTest).AddConnection(eth, arb)) assert.NoError(t, g.(graph.GraphTest).AddConnection(arb, eth)) @@ -602,7 +630,92 @@ func TestTargetMinBalancer_ComputeTransfersToBalance_islands_in_graph(t *testing assert.NoError(t, g.(graph.GraphTest).AddConnection(eth, base)) assert.NoError(t, g.(graph.GraphTest).AddConnection(base, eth)) - r := NewTargetMinBalancer(lggr) + pluginConfigOverrides := models.PluginConfig{ + RebalancerConfig: models.RebalancerConfig{ + Type: "target-and-min", + DefaultTarget: big.NewInt(5), + NetworkTargetOverrides: overrides, + }, + } + r := NewTargetMinBalancer(lggr, pluginConfigOverrides) + + unexecuted := make([]UnexecutedTransfer, 0, len(tc.pendingTransfers)) + for _, tr := range tc.pendingTransfers { + unexecuted = append(unexecuted, models.PendingTransfer{ + Transfer: models.Transfer{ + From: tr.From, + To: tr.To, + Amount: tr.Amount, + }, + Status: tr.Status, + }) + } + transfersToBalance, err := r.ComputeTransfersToBalance(g, unexecuted) + assert.NoError(t, err) + + for _, tr := range transfersToBalance { + t.Logf("actual transfer: %s -> %s = %s", tr.From, tr.To, tr.Amount) + } + sort.Sort(models.ProposedTransfers(tc.expTransfers)) + require.Len(t, transfersToBalance, len(tc.expTransfers)) + for i, tr := range tc.expTransfers { + t.Logf("expected transfer: %s -> %s = %s", tr.From, tr.To, tr.Amount) + assert.Equal(t, tr.From, transfersToBalance[i].From) + assert.Equal(t, tr.To, transfersToBalance[i].To) + assert.Equal(t, tr.Amount.Int64(), transfersToBalance[i].Amount.Int64()) + } + }) + } +} + +func TestTargetMinBalancer_ComputeTransfersToBalance_no_tiny_transfers(t *testing.T) { + testCases := []struct { + name string + balances map[models.NetworkSelector]int64 + minimums map[models.NetworkSelector]int64 + targets map[models.NetworkSelector]int64 + pendingTransfers []models.ProposedTransfer + expTransfers []models.ProposedTransfer + }{ + { + name: "arb and opt below but not more than 5%, so even tho eth has funds we wait", + balances: map[models.NetworkSelector]int64{eth: 2100, arb: 1950, opt: 1950}, + minimums: map[models.NetworkSelector]int64{eth: 500, arb: 500, opt: 500}, + targets: map[models.NetworkSelector]int64{eth: 2000, arb: 2000, opt: 2000}, + pendingTransfers: []models.ProposedTransfer{}, + expTransfers: []models.ProposedTransfer{}, + }, + } + + lggr := logger.TestLogger(t) + lggr.SetLogLevel(zapcore.DebugLevel) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + overrides := make(map[models.NetworkSelector]*big.Int) + + g := graph.NewGraph() + for net, b := range tc.balances { + g.(graph.GraphTest).AddNetwork(net, graph.Data{ + Liquidity: big.NewInt(b), + NetworkSelector: net, + MinimumLiquidity: big.NewInt(tc.minimums[net]), + }) + overrides[net] = big.NewInt(tc.targets[net]) + } + assert.NoError(t, g.(graph.GraphTest).AddConnection(eth, arb)) + assert.NoError(t, g.(graph.GraphTest).AddConnection(arb, eth)) + assert.NoError(t, g.(graph.GraphTest).AddConnection(eth, opt)) + assert.NoError(t, g.(graph.GraphTest).AddConnection(opt, eth)) + + pluginConfigOverrides := models.PluginConfig{ + RebalancerConfig: models.RebalancerConfig{ + Type: "target-and-min", + DefaultTarget: big.NewInt(5), + NetworkTargetOverrides: overrides, + }, + } + r := NewTargetMinBalancer(lggr, pluginConfigOverrides) unexecuted := make([]UnexecutedTransfer, 0, len(tc.pendingTransfers)) for _, tr := range tc.pendingTransfers { diff --git a/core/services/ocr3/plugins/ccip/.golangci.yml b/core/services/ocr3/plugins/ccip/.golangci.yml deleted file mode 100644 index 6765266f3d..0000000000 --- a/core/services/ocr3/plugins/ccip/.golangci.yml +++ /dev/null @@ -1,93 +0,0 @@ -run: - timeout: 60s -linters: - enable: - - exhaustive - - exportloopref - - revive - - goimports - - gosec - - misspell - - rowserrcheck - - errorlint - - unconvert - - sqlclosecheck - - noctx - - depguard - - lll -linters-settings: - exhaustive: - default-signifies-exhaustive: true - goimports: - local-prefixes: "github.com/smartcontractkit/ccipocr3" - gosec: - excludes: - errorlint: - errorf: true # Disallow formatting of errors without %w - revive: - confidence: 0.8 - rules: - - name: blank-imports - - name: context-as-argument - - name: context-keys-type - - name: dot-imports - - name: error-return - - name: error-strings - - name: error-naming - - name: if-return - - name: increment-decrement - - name: var-naming - - name: var-declaration - - name: package-comments - - name: range - - name: receiver-naming - - name: time-naming - - name: unexported-return - - name: indent-error-flow - - name: errorf - - name: empty-block - - name: superfluous-else - # - name: unused-parameter - - name: unreachable-code - - name: redefines-builtin-id - - name: waitgroup-by-value - - name: unconditional-recursion - - name: struct-tag - - name: string-format - - name: string-of-int - - name: range-val-address - - name: range-val-in-closure - - name: modifies-value-receiver - - name: modifies-parameter - - name: identical-branches - - name: get-return - #- name: flag-parameter - - name: early-return - - name: defer - - name: constant-logical-expr - - name: confusing-naming - - name: confusing-results - - name: bool-literal-in-expr - - name: atomic - depguard: - rules: - main: - list-mode: lax - deny: - - pkg: github.com/test-go/testify/assert - desc: Use github.com/stretchr/testify/assert instead - - pkg: github.com/test-go/testify/mock - desc: Use github.com/stretchr/testify/mock instead - - pkg: github.com/test-go/testify/require - desc: Use github.com/stretchr/testify/require instead - lll: - # Max line length, lines longer will be reported. - # '\t' is counted as 1 character by default, and can be changed with the tab-width option. - # Default: 120. - line-length: 120 -issues: - exclude-rules: - - path: test - text: "^G404:" - linters: - - gosec diff --git a/core/services/ocr3/plugins/ccip/Makefile b/core/services/ocr3/plugins/ccip/Makefile deleted file mode 100644 index 358b75feb5..0000000000 --- a/core/services/ocr3/plugins/ccip/Makefile +++ /dev/null @@ -1,12 +0,0 @@ - -ensure_go_1_21: - @go version | grep -q 'go1.21' || (echo "Please use go1.21" && exit 1) - -ensure_golangcilint_1_59: - @golangci-lint --version | grep -q '1.59' || (echo "Please use golangci-lint 1.59" && exit 1) - -test: ensure_go_1_21 - go test -race -fullpath -shuffle on -count 10 ./... - -lint: ensure_go_1_21 - golangci-lint run -c .golangci.yml diff --git a/core/services/ocr3/plugins/ccip/commit/factory.go b/core/services/ocr3/plugins/ccip/commit/factory.go deleted file mode 100644 index 2b10231c01..0000000000 --- a/core/services/ocr3/plugins/ccip/commit/factory.go +++ /dev/null @@ -1,98 +0,0 @@ -package commit - -import ( - "context" - "math/big" - - "github.com/smartcontractkit/ccipocr3/internal/reader" - - "google.golang.org/grpc" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" - "github.com/smartcontractkit/chainlink-common/pkg/types/core" - - "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - libocrtypes "github.com/smartcontractkit/libocr/ragep2p/types" -) - -// PluginFactoryConstructor implements common OCR3ReportingPluginClient and is used for initializing a plugin factory -// and a validation service. -type PluginFactoryConstructor struct{} - -func NewPluginFactoryConstructor() *PluginFactoryConstructor { - return &PluginFactoryConstructor{} -} -func (p PluginFactoryConstructor) NewReportingPluginFactory( - ctx context.Context, - config core.ReportingPluginServiceConfig, - grpcProvider grpc.ClientConnInterface, - pipelineRunner core.PipelineRunnerService, - telemetry core.TelemetryService, - errorLog core.ErrorLog, - capRegistry core.CapabilitiesRegistry, - keyValueStore core.KeyValueStore, - relayerSet core.RelayerSet, -) (core.OCR3ReportingPluginFactory, error) { - return NewPluginFactory(), nil -} - -func (p PluginFactoryConstructor) NewValidationService(ctx context.Context) (core.ValidationService, error) { - panic("implement me") -} - -// PluginFactory implements common ReportingPluginFactory and is used for (re-)initializing commit plugin instances. -type PluginFactory struct{} - -func NewPluginFactory() *PluginFactory { - return &PluginFactory{} -} - -func (p PluginFactory) NewReportingPlugin(config ocr3types.ReportingPluginConfig, -) (ocr3types.ReportingPlugin[[]byte], ocr3types.ReportingPluginInfo, error) { - // TODO: Get this from ocr config, it's the mapping of the oracleId index in the DON - var oracleIDToP2pID map[commontypes.OracleID]libocrtypes.PeerID - onChainTokenPricesReader := reader.NewOnchainTokenPricesReader( - reader.TokenPriceConfig{ // TODO: Inject config - StaticPrices: map[ocr2types.Account]big.Int{}, - }, - nil, // TODO: Inject this - ) - return NewPlugin( - context.Background(), - config.OracleID, - oracleIDToP2pID, - cciptypes.CommitPluginConfig{}, - nil, //ccipReader - onChainTokenPricesReader, - nil, //reportCodec - nil, //msgHasher - nil, // lggr - nil, //homeChain - ), ocr3types.ReportingPluginInfo{}, nil -} - -func (p PluginFactory) Name() string { - panic("implement me") -} - -func (p PluginFactory) Start(ctx context.Context) error { - panic("implement me") -} - -func (p PluginFactory) Close() error { - panic("implement me") -} - -func (p PluginFactory) Ready() error { - panic("implement me") -} - -func (p PluginFactory) HealthReport() map[string]error { - panic("implement me") -} - -// Interface compatibility checks. -var _ core.OCR3ReportingPluginClient = &PluginFactoryConstructor{} -var _ core.OCR3ReportingPluginFactory = &PluginFactory{} diff --git a/core/services/ocr3/plugins/ccip/commit/plugin.go b/core/services/ocr3/plugins/ccip/commit/plugin.go deleted file mode 100644 index d6e28eab05..0000000000 --- a/core/services/ocr3/plugins/ccip/commit/plugin.go +++ /dev/null @@ -1,382 +0,0 @@ -package commit - -import ( - "context" - "fmt" - "sort" - "time" - - mapset "github.com/deckarep/golang-set/v2" - - "github.com/smartcontractkit/ccipocr3/internal/reader" - - "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - libocrtypes "github.com/smartcontractkit/libocr/ragep2p/types" - - "github.com/smartcontractkit/ccipocr3/internal/libs/slicelib" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" -) - -// Plugin implements the main ocr3 ccip commit plugin logic. -// To learn more about the plugin lifecycle, see the ocr3types.ReportingPlugin interface. -// -// NOTE: If you are changing core plugin logic, you should also update the commit plugin python spec. -type Plugin struct { - nodeID commontypes.OracleID - oracleIDToP2pID map[commontypes.OracleID]libocrtypes.PeerID - cfg cciptypes.CommitPluginConfig - ccipReader cciptypes.CCIPReader - tokenPricesReader cciptypes.TokenPricesReader - reportCodec cciptypes.CommitPluginCodec - msgHasher cciptypes.MessageHasher - lggr logger.Logger - - homeChain reader.HomeChain -} - -func NewPlugin( - _ context.Context, - nodeID commontypes.OracleID, - oracleIDToP2pID map[commontypes.OracleID]libocrtypes.PeerID, - cfg cciptypes.CommitPluginConfig, - ccipReader cciptypes.CCIPReader, - tokenPricesReader cciptypes.TokenPricesReader, - reportCodec cciptypes.CommitPluginCodec, - msgHasher cciptypes.MessageHasher, - lggr logger.Logger, - homeChain reader.HomeChain, -) *Plugin { - return &Plugin{ - nodeID: nodeID, - oracleIDToP2pID: oracleIDToP2pID, - cfg: cfg, - ccipReader: ccipReader, - tokenPricesReader: tokenPricesReader, - reportCodec: reportCodec, - msgHasher: msgHasher, - lggr: lggr, - homeChain: homeChain, - } -} - -// Query phase is not used. -func (p *Plugin) Query(_ context.Context, _ ocr3types.OutcomeContext) (types.Query, error) { - return types.Query{}, nil -} - -// Observation phase is used to discover max chain sequence numbers, new messages, gas and token prices. -// -// Max Chain Sequence Numbers: -// -// It is the sequence number of the last known committed message for each known source chain. -// If there was a previous outcome we start with the max sequence numbers of the previous outcome. -// We then read the sequence numbers from the destination chain and override when the on-chain sequence number -// is greater than previous outcome or when previous outcome did not contain a sequence number for a known source chain. -// -// New Messages: -// -// We discover new ccip messages only for the chains that the current node is allowed to read from based on the -// previously discovered max chain sequence numbers. For each chain we scan for new messages -// in the [max_sequence_number+1, max_sequence_number+1+p.cfg.NewMsgScanBatchSize] range. -// -// Gas Prices: -// -// We discover the gas prices for each readable source chain. -// -// Token Prices: -// -// We discover the token prices only for the tokens that are used to pay for ccip fees. -// The fee tokens are configured in the plugin config. -func (p *Plugin) Observation( - ctx context.Context, outctx ocr3types.OutcomeContext, _ types.Query, -) (types.Observation, error) { - supportedChains, err := p.supportedChains() - if err != nil { - return types.Observation{}, fmt.Errorf("error finding supported chains by node: %w", err) - } - - msgBaseDetails := make([]cciptypes.CCIPMsgBaseDetails, 0) - latestCommittedSeqNumsObservation, err := observeLatestCommittedSeqNums( - ctx, p.lggr, p.ccipReader, supportedChains, p.cfg.DestChain, p.knownSourceChainsSlice(), - ) - if err != nil { - return types.Observation{}, fmt.Errorf("observe latest committed sequence numbers: %w", err) - } - - var tokenPrices []cciptypes.TokenPrice - if p.cfg.TokenPricesObserver { - tokenPrices, err = observeTokenPrices( - ctx, - p.tokenPricesReader, - p.cfg.PricedTokens, - ) - if err != nil { - return types.Observation{}, fmt.Errorf("observe token prices: %w", err) - } - } - - // Find the gas prices for each source chain. - var gasPrices []cciptypes.GasPriceChain - gasPrices, err = observeGasPrices(ctx, p.ccipReader, p.knownSourceChainsSlice()) - if err != nil { - return types.Observation{}, fmt.Errorf("observe gas prices: %w", err) - } - - fChain, err := p.homeChain.GetFChain() - if err != nil { - return types.Observation{}, fmt.Errorf("get f chain: %w", err) - } - - // If there's no previous outcome (first round ever), we only observe the latest committed sequence numbers. - // and on the next round we use those to look for messages. - if outctx.PreviousOutcome == nil { - p.lggr.Debugw("first round ever, can't observe new messages yet") - return cciptypes.NewCommitPluginObservation( - msgBaseDetails, gasPrices, tokenPrices, latestCommittedSeqNumsObservation, fChain, - ).Encode() - } - - prevOutcome, err := cciptypes.DecodeCommitPluginOutcome(outctx.PreviousOutcome) - if err != nil { - return types.Observation{}, fmt.Errorf("decode commit plugin previous outcome: %w", err) - } - p.lggr.Debugw("previous outcome decoded", "outcome", prevOutcome.String()) - - // Always observe based on previous outcome. We'll filter out stale messages in the outcome phase. - newMsgs, err := observeNewMsgs( - ctx, - p.lggr, - p.ccipReader, - p.msgHasher, - supportedChains, - prevOutcome.MaxSeqNums, // TODO: Chainlink common PR to rename. - p.cfg.NewMsgScanBatchSize, - ) - if err != nil { - return types.Observation{}, fmt.Errorf("observe new messages: %w", err) - } - - p.lggr.Infow("submitting observation", - "observedNewMsgs", len(newMsgs), - "gasPrices", len(gasPrices), - "tokenPrices", len(tokenPrices), - "latestCommittedSeqNums", latestCommittedSeqNumsObservation, - "fChain", fChain) - - for _, msg := range newMsgs { - msgBaseDetails = append(msgBaseDetails, msg.CCIPMsgBaseDetails) - } - - return cciptypes.NewCommitPluginObservation( - msgBaseDetails, gasPrices, tokenPrices, latestCommittedSeqNumsObservation, fChain, - ).Encode() - -} - -func (p *Plugin) ValidateObservation(_ ocr3types.OutcomeContext, _ types.Query, ao types.AttributedObservation) error { - obs, err := cciptypes.DecodeCommitPluginObservation(ao.Observation) - if err != nil { - return fmt.Errorf("decode commit plugin observation: %w", err) - } - - if err := validateObservedSequenceNumbers(obs.NewMsgs, obs.MaxSeqNums); err != nil { - return fmt.Errorf("validate sequence numbers: %w", err) - } - - destSupportedChains, err := p.supportedChains() - if err != nil { - return fmt.Errorf("error finding supported chains by node: %w", err) - } - - err = validateObserverReadingEligibility(obs.NewMsgs, obs.MaxSeqNums, destSupportedChains, p.cfg.DestChain) - if err != nil { - return fmt.Errorf("validate observer %d reading eligibility: %w", ao.Observer, err) - } - - if err := validateObservedTokenPrices(obs.TokenPrices); err != nil { - return fmt.Errorf("validate token prices: %w", err) - } - - if err := validateObservedGasPrices(obs.GasPrices); err != nil { - return fmt.Errorf("validate gas prices: %w", err) - } - - return nil -} - -func (p *Plugin) ObservationQuorum(_ ocr3types.OutcomeContext, _ types.Query) (ocr3types.Quorum, error) { - // Across all chains we require at least 2F+1 observations. - return ocr3types.QuorumTwoFPlusOne, nil -} - -// Outcome phase is used to construct the final outcome based on the observations of multiple followers. -// -// The outcome contains: -// - Max Sequence Numbers: The max sequence number for each source chain. -// - Merkle Roots: One merkle tree root per source chain. The leaves of the tree are the IDs of the observed messages. -// The merkle root data type contains information about the chain and the sequence numbers range. -func (p *Plugin) Outcome( - _ ocr3types.OutcomeContext, _ types.Query, aos []types.AttributedObservation, -) (ocr3types.Outcome, error) { - decodedObservations := make([]cciptypes.CommitPluginObservation, 0) - for _, ao := range aos { - obs, err := cciptypes.DecodeCommitPluginObservation(ao.Observation) - if err != nil { - return ocr3types.Outcome{}, fmt.Errorf("decode commit plugin observation: %w", err) - } - decodedObservations = append(decodedObservations, obs) - } - - fChains := fChainConsensus(decodedObservations) - - fChainDest, ok := fChains[p.cfg.DestChain] - if !ok { - return ocr3types.Outcome{}, fmt.Errorf("missing destination chain %d in fChain config", p.cfg.DestChain) - } - - maxSeqNums := maxSeqNumsConsensus(p.lggr, fChainDest, decodedObservations) - p.lggr.Debugw("max sequence numbers consensus", "maxSeqNumsConsensus", maxSeqNums) - - merkleRoots, err := newMsgsConsensus(p.lggr, maxSeqNums, decodedObservations, fChains) - if err != nil { - return ocr3types.Outcome{}, fmt.Errorf("new messages consensus: %w", err) - } - p.lggr.Debugw("new messages consensus", "merkleRoots", merkleRoots) - - tokenPrices, err := tokenPricesConsensus(decodedObservations, fChainDest) - if err != nil { - return ocr3types.Outcome{}, fmt.Errorf("token prices consensus: %w", err) - } - - gasPrices := gasPricesConsensus(p.lggr, decodedObservations, fChainDest) - p.lggr.Debugw("gas prices consensus", "gasPrices", gasPrices) - - outcome := cciptypes.NewCommitPluginOutcome(maxSeqNums, merkleRoots, tokenPrices, gasPrices) - if outcome.IsEmpty() { - p.lggr.Debugw("empty outcome") - return ocr3types.Outcome{}, nil - } - p.lggr.Debugw("sending outcome", "outcome", outcome) - - return outcome.Encode() -} - -func (p *Plugin) Reports(seqNr uint64, outcome ocr3types.Outcome) ([]ocr3types.ReportWithInfo[[]byte], error) { - outc, err := cciptypes.DecodeCommitPluginOutcome(outcome) - if err != nil { - p.lggr.Errorw("decode commit plugin outcome", "outcome", outcome, "err", err) - return nil, fmt.Errorf("decode commit plugin outcome: %w", err) - } - - /* - todo: Once token/gas prices are implemented, we would want to probably check if outc.MerkleRoots is empty or not - and only create a report if outc.MerkleRoots is non-empty OR gas/token price timer has expired - */ - - rep := cciptypes.NewCommitPluginReport(outc.MerkleRoots, outc.TokenPrices, outc.GasPrices) - - encodedReport, err := p.reportCodec.Encode(context.Background(), rep) - if err != nil { - return nil, fmt.Errorf("encode commit plugin report: %w", err) - } - - return []ocr3types.ReportWithInfo[[]byte]{{Report: encodedReport, Info: nil}}, nil -} - -func (p *Plugin) ShouldAcceptAttestedReport( - ctx context.Context, u uint64, r ocr3types.ReportWithInfo[[]byte], -) (bool, error) { - decodedReport, err := p.reportCodec.Decode(ctx, r.Report) - if err != nil { - return false, fmt.Errorf("decode commit plugin report: %w", err) - } - - isEmpty := decodedReport.IsEmpty() - if isEmpty { - p.lggr.Infow("skipping empty report") - return false, nil - } - - return true, nil -} - -func (p *Plugin) ShouldTransmitAcceptedReport( - ctx context.Context, u uint64, r ocr3types.ReportWithInfo[[]byte], -) (bool, error) { - isWriter, err := p.supportsDestChain() - if err != nil { - return false, fmt.Errorf("can't know if it's a writer: %w", err) - } - if !isWriter { - p.lggr.Debugw("not a writer, skipping report transmission") - return false, nil - } - - decodedReport, err := p.reportCodec.Decode(ctx, r.Report) - if err != nil { - return false, fmt.Errorf("decode commit plugin report: %w", err) - } - - p.lggr.Debugw("transmitting report", - "roots", len(decodedReport.MerkleRoots), - "tokenPriceUpdates", len(decodedReport.PriceUpdates.TokenPriceUpdates), - "gasPriceUpdates", len(decodedReport.PriceUpdates.GasPriceUpdates), - ) - - // todo: if report is stale -> do not transmit (check the spec for the exact condition) - return true, nil -} - -func (p *Plugin) Close() error { - timeout := 10 * time.Second - ctx, cf := context.WithTimeout(context.Background(), timeout) - defer cf() - - if err := p.ccipReader.Close(ctx); err != nil { - return fmt.Errorf("close ccip reader: %w", err) - } - return nil -} - -func (p *Plugin) knownSourceChainsSlice() []cciptypes.ChainSelector { - knownSourceChains, err := p.homeChain.GetKnownCCIPChains() - if err != nil { - p.lggr.Errorw("error getting known chains", "err", err) - return nil - } - knownSourceChainsSlice := knownSourceChains.ToSlice() - sort.Slice( - knownSourceChainsSlice, - func(i, j int) bool { return knownSourceChainsSlice[i] < knownSourceChainsSlice[j] }, - ) - return slicelib.Filter(knownSourceChainsSlice, func(ch cciptypes.ChainSelector) bool { return ch != p.cfg.DestChain }) -} - -func (p *Plugin) supportedChains() (mapset.Set[cciptypes.ChainSelector], error) { - p2pID, exists := p.oracleIDToP2pID[p.nodeID] - if !exists { - return nil, fmt.Errorf("oracle ID %d not found in oracleIDToP2pID", p.nodeID) - } - supportedChains, err := p.homeChain.GetSupportedChainsForPeer(p2pID) - if err != nil { - p.lggr.Warnw("error getting supported chains", err) - return mapset.NewSet[cciptypes.ChainSelector](), fmt.Errorf("error getting supported chains: %w", err) - } - - return supportedChains, nil -} - -func (p *Plugin) supportsDestChain() (bool, error) { - destChainConfig, err := p.homeChain.GetChainConfig(p.cfg.DestChain) - if err != nil { - return false, fmt.Errorf("get chain config: %w", err) - } - return destChainConfig.SupportedNodes.Contains(p.oracleIDToP2pID[p.nodeID]), nil -} - -// Interface compatibility checks. -var _ ocr3types.ReportingPlugin[[]byte] = &Plugin{} diff --git a/core/services/ocr3/plugins/ccip/commit/plugin_e2e_test.go b/core/services/ocr3/plugins/ccip/commit/plugin_e2e_test.go deleted file mode 100644 index a1786ad973..0000000000 --- a/core/services/ocr3/plugins/ccip/commit/plugin_e2e_test.go +++ /dev/null @@ -1,519 +0,0 @@ -package commit - -import ( - "context" - "reflect" - "strconv" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/libocr/commontypes" - - "github.com/smartcontractkit/ccipocr3/internal/libs/testhelpers" - "github.com/smartcontractkit/ccipocr3/internal/mocks" - "github.com/smartcontractkit/ccipocr3/internal/reader" - - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - libocrtypes "github.com/smartcontractkit/libocr/ragep2p/types" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" -) - -func TestPlugin(t *testing.T) { - ctx := context.Background() - lggr := logger.Test(t) - - testCases := []struct { - name string - description string - nodes []nodeSetup - expErr func(*testing.T, error) - expOutcome cciptypes.CommitPluginOutcome - expTransmittedReports []cciptypes.CommitPluginReport - initialOutcome cciptypes.CommitPluginOutcome - }{ - { - name: "EmptyOutcome", - description: "Empty observations are returned by all nodes which leads to an empty outcome.", - nodes: setupEmptyOutcome(ctx, t, lggr), - expErr: func(t *testing.T, err error) { assert.Equal(t, testhelpers.ErrEmptyOutcome, err) }, - }, - { - name: "AllNodesReadAllChains", - description: "Nodes observe the latest sequence numbers and new messages after those sequence numbers. " + - "They also observe gas prices. In this setup all nodes can read all chains.", - nodes: setupAllNodesReadAllChains(ctx, t, lggr), - expOutcome: cciptypes.CommitPluginOutcome{ - MaxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: chainA, SeqNum: 10}, - {ChainSel: chainB, SeqNum: 20}, - }, - MerkleRoots: []cciptypes.MerkleRootChain{ - {ChainSel: chainB, MerkleRoot: cciptypes.Bytes32{}, SeqNumsRange: cciptypes.NewSeqNumRange(21, 22)}, - }, - TokenPrices: []cciptypes.TokenPrice{}, - GasPrices: []cciptypes.GasPriceChain{ - {ChainSel: chainA, GasPrice: cciptypes.NewBigIntFromInt64(1000)}, - {ChainSel: chainB, GasPrice: cciptypes.NewBigIntFromInt64(20_000)}, - }, - }, - expTransmittedReports: []cciptypes.CommitPluginReport{ - { - MerkleRoots: []cciptypes.MerkleRootChain{ - {ChainSel: chainB, SeqNumsRange: cciptypes.NewSeqNumRange(21, 22)}, - }, - PriceUpdates: cciptypes.PriceUpdates{ - TokenPriceUpdates: []cciptypes.TokenPrice{}, - GasPriceUpdates: []cciptypes.GasPriceChain{ - {ChainSel: chainA, GasPrice: cciptypes.NewBigIntFromInt64(1000)}, - {ChainSel: chainB, GasPrice: cciptypes.NewBigIntFromInt64(20_000)}, - }, - }, - }, - }, - initialOutcome: cciptypes.CommitPluginOutcome{ - MaxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: chainA, SeqNum: 10}, - {ChainSel: chainB, SeqNum: 20}, - }, - MerkleRoots: []cciptypes.MerkleRootChain{}, - TokenPrices: []cciptypes.TokenPrice{}, - GasPrices: []cciptypes.GasPriceChain{}, - }, - }, - { - name: "NodesDoNotAgreeOnMsgs", - description: "Nodes do not agree on messages which leads to an outcome with empty merkle roots.", - nodes: setupNodesDoNotAgreeOnMsgs(ctx, t, lggr), - expOutcome: cciptypes.CommitPluginOutcome{ - MaxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: chainA, SeqNum: 10}, - {ChainSel: chainB, SeqNum: 20}, - }, - MerkleRoots: []cciptypes.MerkleRootChain{}, - TokenPrices: []cciptypes.TokenPrice{}, - GasPrices: []cciptypes.GasPriceChain{ - {ChainSel: chainA, GasPrice: cciptypes.NewBigIntFromInt64(1000)}, - {ChainSel: chainB, GasPrice: cciptypes.NewBigIntFromInt64(20_000)}, - }, - }, - expTransmittedReports: []cciptypes.CommitPluginReport{ - { - MerkleRoots: []cciptypes.MerkleRootChain{}, - PriceUpdates: cciptypes.PriceUpdates{ - TokenPriceUpdates: []cciptypes.TokenPrice{}, - GasPriceUpdates: []cciptypes.GasPriceChain{ - {ChainSel: chainA, GasPrice: cciptypes.NewBigIntFromInt64(1000)}, - {ChainSel: chainB, GasPrice: cciptypes.NewBigIntFromInt64(20_000)}, - }, - }, - }, - }, - initialOutcome: cciptypes.CommitPluginOutcome{ - MaxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: chainA, SeqNum: 10}, - {ChainSel: chainB, SeqNum: 20}, - }, - MerkleRoots: []cciptypes.MerkleRootChain{}, - TokenPrices: []cciptypes.TokenPrice{}, - GasPrices: []cciptypes.GasPriceChain{}, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - t.Log("-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-") - t.Logf(">>> [%s]\n", tc.name) - t.Logf(">>> %s\n", tc.description) - defer t.Log("-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-") - - nodesSetup := tc.nodes - nodes := make([]ocr3types.ReportingPlugin[[]byte], 0, len(nodesSetup)) - for _, n := range nodesSetup { - nodes = append(nodes, n.node) - } - - nodeIDs := make([]commontypes.OracleID, 0, len(nodesSetup)) - for _, n := range nodesSetup { - nodeIDs = append(nodeIDs, n.node.nodeID) - } - o, err := tc.initialOutcome.Encode() - require.NoError(t, err) - runner := testhelpers.NewOCR3Runner(nodes, nodeIDs, o) - - res, err := runner.RunRound(ctx) - if tc.expErr != nil { - tc.expErr(t, err) - } else { - assert.NoError(t, err) - } - - if !reflect.DeepEqual(tc.expOutcome, cciptypes.CommitPluginOutcome{}) { - outcome, err := cciptypes.DecodeCommitPluginOutcome(res.Outcome) - assert.NoError(t, err) - assert.Equal(t, tc.expOutcome.TokenPrices, outcome.TokenPrices) - assert.Equal(t, tc.expOutcome.MaxSeqNums, outcome.MaxSeqNums) - assert.Equal(t, tc.expOutcome.GasPrices, outcome.GasPrices) - - assert.Equal(t, len(tc.expOutcome.MerkleRoots), len(outcome.MerkleRoots)) - for i, exp := range tc.expOutcome.MerkleRoots { - assert.Equal(t, exp.ChainSel, outcome.MerkleRoots[i].ChainSel) - assert.Equal(t, exp.SeqNumsRange, outcome.MerkleRoots[i].SeqNumsRange) - } - } - - assert.Equal(t, len(tc.expTransmittedReports), len(res.Transmitted)) - for i, exp := range tc.expTransmittedReports { - actual, err := nodesSetup[0].reportCodec.Decode(ctx, res.Transmitted[i].Report) - assert.NoError(t, err) - assert.Equal(t, exp.PriceUpdates, actual.PriceUpdates) - assert.Equal(t, len(exp.MerkleRoots), len(actual.MerkleRoots)) - for j, expRoot := range exp.MerkleRoots { - assert.Equal(t, expRoot.ChainSel, actual.MerkleRoots[j].ChainSel) - assert.Equal(t, expRoot.SeqNumsRange, actual.MerkleRoots[j].SeqNumsRange) - } - } - }) - } -} - -func setupEmptyOutcome(ctx context.Context, t *testing.T, lggr logger.Logger) []nodeSetup { - cfg := cciptypes.CommitPluginConfig{ - DestChain: chainC, - PricedTokens: []types.Account{tokenX}, - TokenPricesObserver: false, - NewMsgScanBatchSize: 256, - } - - chainConfigInfos := []reader.ChainConfigInfo{ - { - ChainSelector: chainC, - ChainConfig: reader.HomeChainConfigMapper{ - FChain: 1, - Readers: []libocrtypes.PeerID{ - {1}, {2}, {3}, - }, - Config: []byte{0}, - }, - }, - } - - homeChain := setupHomeChainPoller(lggr, chainConfigInfos) - err := homeChain.Start(ctx) - if err != nil { - return nil - } - - oracleIDToP2pID := GetP2pIDs(1, 2, 3) - nodes := []nodeSetup{ - newNode(ctx, t, lggr, 1, cfg, homeChain, oracleIDToP2pID), - newNode(ctx, t, lggr, 2, cfg, homeChain, oracleIDToP2pID), - newNode(ctx, t, lggr, 3, cfg, homeChain, oracleIDToP2pID), - } - - for _, n := range nodes { - // All nodes have issue reading the latest sequence number, should lead to empty outcomes - n.ccipReader.On( - "NextSeqNum", - ctx, - mock.Anything, - ).Return([]cciptypes.SeqNum{}, nil) - } - - err = homeChain.Close() - if err != nil { - return nil - } - return nodes -} - -func setupAllNodesReadAllChains(ctx context.Context, t *testing.T, lggr logger.Logger) []nodeSetup { - cfg := cciptypes.CommitPluginConfig{ - DestChain: chainC, - PricedTokens: []types.Account{tokenX}, - TokenPricesObserver: false, - NewMsgScanBatchSize: 256, - } - - chainConfigInfos := []reader.ChainConfigInfo{ - { - ChainSelector: chainA, - ChainConfig: reader.HomeChainConfigMapper{ - FChain: 1, - Readers: []libocrtypes.PeerID{ - {1}, {2}, {3}, - }, - Config: []byte{0}, - }, - }, - { - ChainSelector: chainB, - ChainConfig: reader.HomeChainConfigMapper{ - FChain: 1, - Readers: []libocrtypes.PeerID{ - {1}, {2}, {3}, - }, - Config: []byte{0}, - }, - }, - { - ChainSelector: chainC, - ChainConfig: reader.HomeChainConfigMapper{ - FChain: 1, - Readers: []libocrtypes.PeerID{ - {1}, {2}, {3}, - }, - Config: []byte{0}, - }, - }, - } - - homeChain := setupHomeChainPoller(lggr, chainConfigInfos) - err := homeChain.Start(ctx) - if err != nil { - return nil - } - oracleIDToP2pID := GetP2pIDs(1, 2, 3) - n1 := newNode(ctx, t, lggr, 1, cfg, homeChain, oracleIDToP2pID) - n2 := newNode(ctx, t, lggr, 2, cfg, homeChain, oracleIDToP2pID) - n3 := newNode(ctx, t, lggr, 3, cfg, homeChain, oracleIDToP2pID) - nodes := []nodeSetup{n1, n2, n3} - - for _, n := range nodes { - // then they fetch new msgs, there is nothing new on chainA - n.ccipReader.On( - "MsgsBetweenSeqNums", - ctx, - chainA, - cciptypes.NewSeqNumRange(11, cciptypes.SeqNum(11+cfg.NewMsgScanBatchSize)), - ).Return([]cciptypes.CCIPMsg{}, nil) - - // and there are two new message on chainB - n.ccipReader.On( - "MsgsBetweenSeqNums", - ctx, - chainB, - cciptypes.NewSeqNumRange(21, cciptypes.SeqNum(21+cfg.NewMsgScanBatchSize)), - ).Return([]cciptypes.CCIPMsg{ - { - CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ - MsgHash: cciptypes.Bytes32{1}, ID: "1", SourceChain: chainB, SeqNum: 21, - }, - }, - { - CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ - MsgHash: cciptypes.Bytes32{2}, ID: "2", SourceChain: chainB, SeqNum: 22, - }, - }, - }, nil) - - n.ccipReader.On("GasPrices", ctx, []cciptypes.ChainSelector{chainA, chainB}). - Return([]cciptypes.BigInt{ - cciptypes.NewBigIntFromInt64(1000), - cciptypes.NewBigIntFromInt64(20_000), - }, nil) - - // all nodes observe the same sequence numbers 10 for chainA and 20 for chainB - n.ccipReader.On("NextSeqNum", ctx, []cciptypes.ChainSelector{chainA, chainB}). - Return([]cciptypes.SeqNum{10, 20}, nil) - - } - - // No need to keep it running in the background anymore for this test - err = homeChain.Close() - if err != nil { - return nil - } - - return nodes -} - -func setupNodesDoNotAgreeOnMsgs(ctx context.Context, t *testing.T, lggr logger.Logger) []nodeSetup { - cfg := cciptypes.CommitPluginConfig{ - DestChain: chainC, - PricedTokens: []types.Account{tokenX}, - TokenPricesObserver: false, - NewMsgScanBatchSize: 256, - } - - chainConfigInfos := []reader.ChainConfigInfo{ - { - ChainSelector: chainA, - ChainConfig: reader.HomeChainConfigMapper{ - FChain: 1, - Readers: []libocrtypes.PeerID{ - {1}, {2}, {3}, - }, - Config: []byte{0}, - }, - }, - { - ChainSelector: chainB, - ChainConfig: reader.HomeChainConfigMapper{ - FChain: 1, - Readers: []libocrtypes.PeerID{ - {1}, {2}, {3}, - }, - Config: []byte{0}, - }, - }, - { - ChainSelector: chainC, - ChainConfig: reader.HomeChainConfigMapper{ - FChain: 1, - Readers: []libocrtypes.PeerID{ - {1}, {2}, {3}, - }, - Config: []byte{0}, - }, - }, - } - - homeChain := setupHomeChainPoller(lggr, chainConfigInfos) - err := homeChain.Start(ctx) - if err != nil { - return nil - } - oracleIDToP2pID := GetP2pIDs(1, 2, 3) - n1 := newNode(ctx, t, lggr, 1, cfg, homeChain, oracleIDToP2pID) - n2 := newNode(ctx, t, lggr, 2, cfg, homeChain, oracleIDToP2pID) - n3 := newNode(ctx, t, lggr, 3, cfg, homeChain, oracleIDToP2pID) - nodes := []nodeSetup{n1, n2, n3} - - for i, n := range nodes { - // all nodes observe the same sequence numbers 10 for chainA and 20 for chainB - n.ccipReader.On("NextSeqNum", ctx, []cciptypes.ChainSelector{chainA, chainB}). - Return([]cciptypes.SeqNum{10, 20}, nil) - - // then they fetch new msgs, there is nothing new on chainA - n.ccipReader.On( - "MsgsBetweenSeqNums", - ctx, - chainA, - cciptypes.NewSeqNumRange(11, cciptypes.SeqNum(11+cfg.NewMsgScanBatchSize)), - ).Return([]cciptypes.CCIPMsg{}, nil) - - // and there are two new message on chainB - n.ccipReader.On( - "MsgsBetweenSeqNums", - ctx, - chainB, - cciptypes.NewSeqNumRange( - 21, - cciptypes.SeqNum(21+cfg.NewMsgScanBatchSize), - ), - ).Return([]cciptypes.CCIPMsg{ - {CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ - MsgHash: cciptypes.Bytes32{1}, - ID: "1" + strconv.Itoa(i), - SourceChain: chainB, - SeqNum: 21 + cciptypes.SeqNum(i*10)}}, - {CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ - MsgHash: cciptypes.Bytes32{2}, - ID: "2" + strconv.Itoa(i), - SourceChain: chainB, - SeqNum: 22 + cciptypes.SeqNum(i*20)}}, - }, nil) - - n.ccipReader.On("GasPrices", ctx, []cciptypes.ChainSelector{chainA, chainB}). - Return([]cciptypes.BigInt{ - cciptypes.NewBigIntFromInt64(1000), - cciptypes.NewBigIntFromInt64(20_000), - }, nil) - } - - // No need to keep it running in the background anymore for this test - err = homeChain.Close() - if err != nil { - return nil - } - - return nodes -} - -type nodeSetup struct { - node *Plugin - ccipReader *mocks.CCIPReader - priceReader *mocks.TokenPricesReader - reportCodec *mocks.CommitPluginJSONReportCodec - msgHasher *mocks.MessageHasher -} - -func newNode( - ctx context.Context, - t *testing.T, - lggr logger.Logger, - id int, - cfg cciptypes.CommitPluginConfig, - homeChain reader.HomeChain, - oracleIDToP2pID map[commontypes.OracleID]libocrtypes.PeerID, -) nodeSetup { - ccipReader := mocks.NewCCIPReader() - priceReader := mocks.NewTokenPricesReader() - reportCodec := mocks.NewCommitPluginJSONReportCodec() - msgHasher := mocks.NewMessageHasher() - - node1 := NewPlugin( - context.Background(), - commontypes.OracleID(id), - oracleIDToP2pID, - cfg, - ccipReader, - priceReader, - reportCodec, - msgHasher, - lggr, - homeChain, - ) - - return nodeSetup{ - node: node1, - ccipReader: ccipReader, - priceReader: priceReader, - reportCodec: reportCodec, - msgHasher: msgHasher, - } -} - -func setupHomeChainPoller(lggr logger.Logger, chainConfigInfos []reader.ChainConfigInfo) reader.HomeChain { - homeChainReader := mocks.NewContractReaderMock() - homeChainReader.On( - "GetLatestValue", mock.Anything, "CCIPCapabilityConfiguration", "getAllChainConfigs", mock.Anything, mock.Anything, - ).Run( - func(args mock.Arguments) { - arg := args.Get(4).(*[]reader.ChainConfigInfo) - *arg = chainConfigInfos - }).Return(nil) - - homeChain := reader.NewHomeChainConfigPoller( - homeChainReader, - lggr, - // to prevent linting error because of logging after finishing tests, we close the poller after each test, having - // lower polling interval make it catch up faster - 10*time.Millisecond, - ) - - return homeChain -} -func GetP2pIDs(ids ...int) map[commontypes.OracleID]libocrtypes.PeerID { - res := make(map[commontypes.OracleID]libocrtypes.PeerID) - for _, id := range ids { - res[commontypes.OracleID(id)] = libocrtypes.PeerID{byte(id)} - } - return res -} - -var ( - chainA = cciptypes.ChainSelector(1) - chainB = cciptypes.ChainSelector(2) - chainC = cciptypes.ChainSelector(3) - - tokenX = types.Account("tk_xxx") -) diff --git a/core/services/ocr3/plugins/ccip/commit/plugin_functions.go b/core/services/ocr3/plugins/ccip/commit/plugin_functions.go deleted file mode 100644 index 9f4c73faef..0000000000 --- a/core/services/ocr3/plugins/ccip/commit/plugin_functions.go +++ /dev/null @@ -1,598 +0,0 @@ -package commit - -import ( - "context" - "fmt" - "sort" - - mapset "github.com/deckarep/golang-set/v2" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "golang.org/x/sync/errgroup" - - "github.com/smartcontractkit/ccipocr3/internal/libs/slicelib" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" - - "github.com/smartcontractkit/chainlink-common/pkg/hashutil" - "github.com/smartcontractkit/chainlink-common/pkg/merklemulti" -) - -// observeLatestCommittedSeqNums finds the maximum committed sequence numbers for each source chain. -// If we cannot observe the dest we return an empty slice and no error. -func observeLatestCommittedSeqNums( - ctx context.Context, - lggr logger.Logger, - ccipReader cciptypes.CCIPReader, - readableChains mapset.Set[cciptypes.ChainSelector], - destChain cciptypes.ChainSelector, - knownSourceChains []cciptypes.ChainSelector, -) ([]cciptypes.SeqNumChain, error) { - sort.Slice(knownSourceChains, func(i, j int) bool { return knownSourceChains[i] < knownSourceChains[j] }) - latestCommittedSeqNumsObservation := make([]cciptypes.SeqNumChain, 0) - if readableChains.Contains(destChain) { - lggr.Debugw("reading latest committed sequence from destination") - onChainLatestCommittedSeqNums, err := ccipReader.NextSeqNum(ctx, knownSourceChains) - if err != nil { - return latestCommittedSeqNumsObservation, fmt.Errorf("get next seq nums: %w", err) - } - lggr.Debugw("observed latest committed sequence numbers on destination", - "latestCommittedSeqNumsObservation", onChainLatestCommittedSeqNums) - for i, ch := range knownSourceChains { - latestCommittedSeqNumsObservation = append( - latestCommittedSeqNumsObservation, - cciptypes.NewSeqNumChain(ch, onChainLatestCommittedSeqNums[i]), - ) - } - } - return latestCommittedSeqNumsObservation, nil -} - -// observeNewMsgs finds the new messages for each supported chain based on the provided max sequence numbers. -// If latestCommitSeqNums is empty (first ever OCR round), it will return an empty slice. -func observeNewMsgs( - ctx context.Context, - lggr logger.Logger, - ccipReader cciptypes.CCIPReader, - msgHasher cciptypes.MessageHasher, - readableChains mapset.Set[cciptypes.ChainSelector], - latestCommittedSeqNums []cciptypes.SeqNumChain, - msgScanBatchSize int, -) ([]cciptypes.CCIPMsg, error) { - // Find the new msgs for each supported chain based on the discovered max sequence numbers. - newMsgsPerChain := make([][]cciptypes.CCIPMsg, len(latestCommittedSeqNums)) - eg := new(errgroup.Group) - - for chainIdx, seqNumChain := range latestCommittedSeqNums { - if !readableChains.Contains(seqNumChain.ChainSel) { - lggr.Debugw("reading chain is not supported", "chain", seqNumChain.ChainSel) - continue - } - - seqNumChain := seqNumChain - chainIdx := chainIdx - eg.Go(func() error { - minSeqNum := seqNumChain.SeqNum + 1 - maxSeqNum := minSeqNum + cciptypes.SeqNum(msgScanBatchSize) - lggr.Debugw("scanning for new messages", - "chain", seqNumChain.ChainSel, "minSeqNum", minSeqNum, "maxSeqNum", maxSeqNum) - - newMsgs, err := ccipReader.MsgsBetweenSeqNums( - ctx, seqNumChain.ChainSel, cciptypes.NewSeqNumRange(minSeqNum, maxSeqNum)) - if err != nil { - return fmt.Errorf("get messages between seq nums: %w", err) - } - - if len(newMsgs) > 0 { - lggr.Debugw("discovered new messages", "chain", seqNumChain.ChainSel, "newMsgs", len(newMsgs)) - } else { - lggr.Debugw("no new messages discovered", "chain", seqNumChain.ChainSel) - } - - for i := range newMsgs { - h, err := msgHasher.Hash(ctx, newMsgs[i]) - if err != nil { - return fmt.Errorf("hash message: %w", err) - } - newMsgs[i].MsgHash = h // populate msgHash field - } - - newMsgsPerChain[chainIdx] = newMsgs - return nil - }) - } - - if err := eg.Wait(); err != nil { - return nil, fmt.Errorf("wait for new msg observations: %w", err) - } - - observedNewMsgs := make([]cciptypes.CCIPMsg, 0) - for chainIdx := range latestCommittedSeqNums { - observedNewMsgs = append(observedNewMsgs, newMsgsPerChain[chainIdx]...) - } - return observedNewMsgs, nil -} - -func observeTokenPrices( - ctx context.Context, - tokenPricesReader cciptypes.TokenPricesReader, - tokens []types.Account, -) ([]cciptypes.TokenPrice, error) { - tokenPrices, err := tokenPricesReader.GetTokenPricesUSD(ctx, tokens) - if err != nil { - return nil, fmt.Errorf("get token prices: %w", err) - } - - if len(tokenPrices) != len(tokens) { - return nil, fmt.Errorf("internal critical error token prices length mismatch: got %d, want %d", - len(tokenPrices), len(tokens)) - } - - tokenPricesUSD := make([]cciptypes.TokenPrice, 0, len(tokens)) - for i, token := range tokens { - tokenPricesUSD = append(tokenPricesUSD, cciptypes.NewTokenPrice(token, tokenPrices[i])) - } - - return tokenPricesUSD, nil -} - -func observeGasPrices( - ctx context.Context, - ccipReader cciptypes.CCIPReader, - chains []cciptypes.ChainSelector, -) ([]cciptypes.GasPriceChain, error) { - if len(chains) == 0 { - return nil, nil - } - - gasPrices, err := ccipReader.GasPrices(ctx, chains) - if err != nil { - return nil, fmt.Errorf("get gas prices: %w", err) - } - - if len(gasPrices) != len(chains) { - return nil, fmt.Errorf("internal critical error gas prices length mismatch: got %d, want %d", - len(gasPrices), len(chains)) - } - - gasPricesGwei := make([]cciptypes.GasPriceChain, 0, len(chains)) - for i, chain := range chains { - gasPricesGwei = append(gasPricesGwei, cciptypes.NewGasPriceChain(gasPrices[i].Int, chain)) - } - - return gasPricesGwei, nil -} - -// newMsgsConsensus comes in consensus on the observed messages for each source chain. Generates one merkle root -// for each source chain based on the consensus on the messages. -func newMsgsConsensus( - lggr logger.Logger, - maxSeqNums []cciptypes.SeqNumChain, - observations []cciptypes.CommitPluginObservation, - fChainCfg map[cciptypes.ChainSelector]int, -) ([]cciptypes.MerkleRootChain, error) { - maxSeqNumsPerChain := make(map[cciptypes.ChainSelector]cciptypes.SeqNum) - for _, seqNumChain := range maxSeqNums { - maxSeqNumsPerChain[seqNumChain.ChainSel] = seqNumChain.SeqNum - } - - // Gather all messages from all observations. - msgsFromObservations := make([]cciptypes.CCIPMsgBaseDetails, 0) - for _, obs := range observations { - msgsFromObservations = append(msgsFromObservations, obs.NewMsgs...) - } - lggr.Debugw("total observed messages across all followers", "msgs", len(msgsFromObservations)) - - // Filter out messages less than or equal to the max sequence numbers. - msgsFromObservations = slicelib.Filter(msgsFromObservations, func(msg cciptypes.CCIPMsgBaseDetails) bool { - maxSeqNum, ok := maxSeqNumsPerChain[msg.SourceChain] - if !ok { - return false - } - return msg.SeqNum > maxSeqNum - }) - lggr.Debugw("observed messages after filtering", "msgs", len(msgsFromObservations)) - - // Group messages by source chain. - sourceChains, groupedMsgs := slicelib.GroupBy( - msgsFromObservations, - func(msg cciptypes.CCIPMsgBaseDetails) cciptypes.ChainSelector { return msg.SourceChain }, - ) - - // Come to consensus on the observed messages by source chain. - consensusBySourceChain := make(map[cciptypes.ChainSelector]observedMsgsConsensus) - for _, sourceChain := range sourceChains { // note: we iterate using sourceChains slice for deterministic order. - observedMsgs, ok := groupedMsgs[sourceChain] - if !ok { - lggr.Panicw("source chain not found in grouped messages", "sourceChain", sourceChain) - } - - msgsConsensus, err := newMsgsConsensusForChain(lggr, sourceChain, observedMsgs, fChainCfg) - if err != nil { - return nil, fmt.Errorf("calculate observed msgs consensus: %w", err) - } - - if msgsConsensus.isEmpty() { - lggr.Debugw("no consensus on observed messages", "sourceChain", sourceChain) - continue - } - consensusBySourceChain[sourceChain] = msgsConsensus - lggr.Debugw("observed messages consensus", "sourceChain", sourceChain, "consensus", msgsConsensus) - } - - merkleRoots := make([]cciptypes.MerkleRootChain, 0) - for sourceChain, consensus := range consensusBySourceChain { - merkleRoots = append( - merkleRoots, - cciptypes.NewMerkleRootChain(sourceChain, consensus.seqNumRange, consensus.merkleRoot), - ) - } - - sort.Slice(merkleRoots, func(i, j int) bool { return merkleRoots[i].ChainSel < merkleRoots[j].ChainSel }) - return merkleRoots, nil -} - -// Given a list of observed msgs -// - Keep the messages that were observed by at least 2f_chain+1 followers. -// - Starting from the first message (min seq num), keep adding the messages to the merkle tree until a gap is found. -func newMsgsConsensusForChain( - lggr logger.Logger, - chainSel cciptypes.ChainSelector, - observedMsgs []cciptypes.CCIPMsgBaseDetails, - fChainCfg map[cciptypes.ChainSelector]int, -) (observedMsgsConsensus, error) { - fChain, ok := fChainCfg[chainSel] - if !ok { - return observedMsgsConsensus{}, fmt.Errorf("fchain not found for chain %d", chainSel) - } - lggr.Debugw("observed messages consensus", - "chain", chainSel, "fChain", fChain, "observedMsgs", len(observedMsgs)) - - // First come to consensus about the (sequence number, msg hash) pairs. - // For each sequence number consider the Hash with the most votes. - msgSeqNumToHashCounts := make(map[cciptypes.SeqNum]map[string]int) // seqNum -> msgHash -> count - for _, msg := range observedMsgs { - if _, exists := msgSeqNumToHashCounts[msg.SeqNum]; !exists { - msgSeqNumToHashCounts[msg.SeqNum] = make(map[string]int) - } - msgSeqNumToHashCounts[msg.SeqNum][msg.MsgHash.String()]++ - } - lggr.Debugw("observed message counts", "chain", chainSel, "msgSeqNumToHashCounts", msgSeqNumToHashCounts) - - msgObservationsCount := make(map[cciptypes.SeqNum]int) - msgSeqNumToHash := make(map[cciptypes.SeqNum]cciptypes.Bytes32) - for seqNum, hashCounts := range msgSeqNumToHashCounts { - if len(hashCounts) == 0 { - lggr.Fatalw("hash counts should never be empty", "seqNum", seqNum) - continue - } - - // Find the MsgHash with the most votes for each sequence number. - hashesSlice := make([]string, 0, len(hashCounts)) - for h := range hashCounts { - hashesSlice = append(hashesSlice, h) - } - // determinism in case we have the same count for different hashes - sort.Slice(hashesSlice, func(i, j int) bool { return hashesSlice[i] < hashesSlice[j] }) - - maxCnt := hashCounts[hashesSlice[0]] - mostVotedHash := hashesSlice[0] - for _, h := range hashesSlice[1:] { - cnt := hashCounts[h] - if cnt > maxCnt { - maxCnt = cnt - mostVotedHash = h - } - } - - msgObservationsCount[seqNum] = maxCnt - hashBytes, err := cciptypes.NewBytes32FromString(mostVotedHash) - if err != nil { - return observedMsgsConsensus{}, fmt.Errorf("critical issue converting hash '%s' to bytes32: %w", - mostVotedHash, err) - } - msgSeqNumToHash[seqNum] = hashBytes - } - lggr.Debugw("observed message consensus", "chain", chainSel, "msgSeqNumToHash", msgSeqNumToHash) - - // Filter out msgs not observed by at least 2f_chain+1 followers. - msgSeqNumsQuorum := mapset.NewSet[cciptypes.SeqNum]() - for seqNum, count := range msgObservationsCount { - if count >= 2*fChain+1 { - msgSeqNumsQuorum.Add(seqNum) - } - } - if msgSeqNumsQuorum.Cardinality() == 0 { - return observedMsgsConsensus{}, nil - } - - // Come to consensus on the observed messages sequence numbers range. - msgSeqNumsQuorumSlice := msgSeqNumsQuorum.ToSlice() - sort.Slice(msgSeqNumsQuorumSlice, func(i, j int) bool { return msgSeqNumsQuorumSlice[i] < msgSeqNumsQuorumSlice[j] }) - seqNumConsensusRange := cciptypes.NewSeqNumRange(msgSeqNumsQuorumSlice[0], msgSeqNumsQuorumSlice[0]) - for _, seqNum := range msgSeqNumsQuorumSlice[1:] { - if seqNum != seqNumConsensusRange.End()+1 { - break // Found a gap in the sequence numbers. - } - seqNumConsensusRange.SetEnd(seqNum) - } - - treeLeaves := make([][32]byte, 0) - for seqNum := seqNumConsensusRange.Start(); seqNum <= seqNumConsensusRange.End(); seqNum++ { - msgHash, ok := msgSeqNumToHash[seqNum] - if !ok { - return observedMsgsConsensus{}, fmt.Errorf("msg hash not found for seq num %d", seqNum) - } - treeLeaves = append(treeLeaves, msgHash) - } - - lggr.Debugw("constructing merkle tree", "chain", chainSel, "treeLeaves", len(treeLeaves)) - tree, err := merklemulti.NewTree(hashutil.NewKeccak(), treeLeaves) - if err != nil { - return observedMsgsConsensus{}, fmt.Errorf("construct merkle tree from %d leaves: %w", len(treeLeaves), err) - } - - return observedMsgsConsensus{ - seqNumRange: seqNumConsensusRange, - merkleRoot: tree.Root(), - }, nil -} - -// maxSeqNumsConsensus groups the observed max seq nums across all followers per chain. -// Orders the sequence numbers and selects the one at the index of destination chain fChain. -// -// For example: -// -// seqNums: [1, 1, 1, 10, 10, 10, 10, 10, 10] -// fChain: 4 -// result: 10 -// -// Selecting seqNums[fChain] ensures: -// - At least one honest node has seen this value, so adversary cannot bias the value lower which would cause reverts -// - If an honest oracle reports sorted_min[f] which happens to be stale i.e. that oracle has a delayed view -// of the chain, then the report will revert onchain but still succeed upon retry -// - We minimize the risk of naturally hitting the error condition minSeqNum > maxSeqNum due to oracles -// delayed views of the chain (would be an issue with taking sorted_mins[-f]) -func maxSeqNumsConsensus( - lggr logger.Logger, fChain int, observations []cciptypes.CommitPluginObservation, -) []cciptypes.SeqNumChain { - observedSeqNumsPerChain := make(map[cciptypes.ChainSelector][]cciptypes.SeqNum) - for _, obs := range observations { - for _, maxSeqNum := range obs.MaxSeqNums { - if _, exists := observedSeqNumsPerChain[maxSeqNum.ChainSel]; !exists { - observedSeqNumsPerChain[maxSeqNum.ChainSel] = make([]cciptypes.SeqNum, 0) - } - observedSeqNumsPerChain[maxSeqNum.ChainSel] = - append(observedSeqNumsPerChain[maxSeqNum.ChainSel], maxSeqNum.SeqNum) - } - } - - seqNums := make([]cciptypes.SeqNumChain, 0, len(observedSeqNumsPerChain)) - for ch, observedSeqNums := range observedSeqNumsPerChain { - if len(observedSeqNums) < 2*fChain+1 { - lggr.Warnw("not enough observations for chain", "chain", ch, "observedSeqNums", observedSeqNums) - continue - } - - sort.Slice(observedSeqNums, func(i, j int) bool { return observedSeqNums[i] < observedSeqNums[j] }) - seqNums = append(seqNums, cciptypes.NewSeqNumChain(ch, observedSeqNums[fChain])) - } - - sort.Slice(seqNums, func(i, j int) bool { return seqNums[i].ChainSel < seqNums[j].ChainSel }) - return seqNums -} - -// tokenPricesConsensus returns the median price for tokens that have at least 2f_chain+1 observations. -func tokenPricesConsensus( - observations []cciptypes.CommitPluginObservation, - fChain int, -) ([]cciptypes.TokenPrice, error) { - pricesPerToken := make(map[types.Account][]cciptypes.BigInt) - for _, obs := range observations { - for _, price := range obs.TokenPrices { - if _, exists := pricesPerToken[price.TokenID]; !exists { - pricesPerToken[price.TokenID] = make([]cciptypes.BigInt, 0) - } - pricesPerToken[price.TokenID] = append(pricesPerToken[price.TokenID], price.Price) - } - } - - // Keep the median - consensusPrices := make([]cciptypes.TokenPrice, 0) - for token, prices := range pricesPerToken { - if len(prices) < 2*fChain+1 { - continue - } - consensusPrices = append(consensusPrices, cciptypes.NewTokenPrice(token, slicelib.BigIntSortedMiddle(prices).Int)) - } - - sort.Slice(consensusPrices, func(i, j int) bool { return consensusPrices[i].TokenID < consensusPrices[j].TokenID }) - return consensusPrices, nil -} - -func gasPricesConsensus( - lggr logger.Logger, observations []cciptypes.CommitPluginObservation, fChain int, -) []cciptypes.GasPriceChain { - // Group the observed gas prices by chain. - gasPricePerChain := make(map[cciptypes.ChainSelector][]cciptypes.BigInt) - for _, obs := range observations { - for _, gasPrice := range obs.GasPrices { - if _, exists := gasPricePerChain[gasPrice.ChainSel]; !exists { - gasPricePerChain[gasPrice.ChainSel] = make([]cciptypes.BigInt, 0) - } - gasPricePerChain[gasPrice.ChainSel] = append(gasPricePerChain[gasPrice.ChainSel], gasPrice.GasPrice) - } - } - - // Keep the median - consensusGasPrices := make([]cciptypes.GasPriceChain, 0) - for chain, gasPrices := range gasPricePerChain { - if len(gasPrices) < 2*fChain+1 { - lggr.Warnw("not enough gas price observations", "chain", chain, "gasPrices", gasPrices) - continue - } - - consensusGasPrices = append( - consensusGasPrices, - cciptypes.NewGasPriceChain(slicelib.BigIntSortedMiddle(gasPrices).Int, chain), - ) - } - - sort.Slice( - consensusGasPrices, - func(i, j int) bool { return consensusGasPrices[i].ChainSel < consensusGasPrices[j].ChainSel }, - ) - return consensusGasPrices -} - -// fChainConsensus comes to consensus on the plugin config based on the observations. -// We cannot trust the state of a single follower, so we need to come to consensus on the config. -func fChainConsensus( - observations []cciptypes.CommitPluginObservation, // observations from all followers -) map[cciptypes.ChainSelector]int { - // Come to consensus on fChain. - // Use the fChain observed by most followers for each chain. - fChainCounts := make(map[cciptypes.ChainSelector]map[int]int) // {chain: {fChain: count}} - for _, obs := range observations { - for chain, fChain := range obs.FChain { - if _, exists := fChainCounts[chain]; !exists { - fChainCounts[chain] = make(map[int]int) - } - fChainCounts[chain][fChain]++ - } - } - consensusFChain := make(map[cciptypes.ChainSelector]int) - for chain, counts := range fChainCounts { - maxCount := 0 - for fChain, count := range counts { - if count > maxCount { - maxCount = count - consensusFChain[chain] = fChain - } - } - } - - return consensusFChain -} - -// validateObservedSequenceNumbers checks if the sequence numbers of the provided messages are unique for each chain and -// that they match the observed max sequence numbers. -func validateObservedSequenceNumbers(msgs []cciptypes.CCIPMsgBaseDetails, maxSeqNums []cciptypes.SeqNumChain) error { - // If the observer did not include sequence numbers it means that it's not a destination chain reader. - // In that case we cannot do any msg sequence number validations. - if len(maxSeqNums) == 0 { - return nil - } - - // MaxSeqNums must be unique for each chain. - maxSeqNumsMap := make(map[cciptypes.ChainSelector]cciptypes.SeqNum) - for _, maxSeqNum := range maxSeqNums { - if _, exists := maxSeqNumsMap[maxSeqNum.ChainSel]; exists { - return fmt.Errorf("duplicate max sequence number for chain %d", maxSeqNum.ChainSel) - } - maxSeqNumsMap[maxSeqNum.ChainSel] = maxSeqNum.SeqNum - } - - seqNums := make(map[cciptypes.ChainSelector]mapset.Set[cciptypes.SeqNum], len(msgs)) - hashes := mapset.NewSet[string]() - for _, msg := range msgs { - if msg.MsgHash.IsEmpty() { - return fmt.Errorf("observed msg hash must not be empty") - } - - if _, exists := seqNums[msg.SourceChain]; !exists { - seqNums[msg.SourceChain] = mapset.NewSet[cciptypes.SeqNum]() - } - - // The same sequence number must not appear more than once for the same chain and must be valid. - if seqNums[msg.SourceChain].Contains(msg.SeqNum) { - return fmt.Errorf("duplicate sequence number %d for chain %d", msg.SeqNum, msg.SourceChain) - } - seqNums[msg.SourceChain].Add(msg.SeqNum) - - // The observed msg hash cannot appear twice for different msgs. - if hashes.Contains(msg.MsgHash.String()) { - return fmt.Errorf("duplicate msg hash %s", msg.MsgHash.String()) - } - hashes.Add(msg.MsgHash.String()) - - // The observed msg sequence number cannot be less than or equal to the max observed sequence number. - maxSeqNum, exists := maxSeqNumsMap[msg.SourceChain] - if !exists { - return fmt.Errorf("max sequence number observation not found for chain %d", msg.SourceChain) - } - if msg.SeqNum <= maxSeqNum { - return fmt.Errorf("max sequence number %d must be greater than observed sequence number %d for chain %d", - maxSeqNum, msg.SeqNum, msg.SourceChain) - } - } - - return nil -} - -// validateObserverReadingEligibility checks if the observer is eligible to observe the messages it observed. -func validateObserverReadingEligibility( - msgs []cciptypes.CCIPMsgBaseDetails, - seqNums []cciptypes.SeqNumChain, - nodeSupportedChains mapset.Set[cciptypes.ChainSelector], - destChain cciptypes.ChainSelector, -) error { - - if len(seqNums) > 0 && !nodeSupportedChains.Contains(destChain) { - return fmt.Errorf("observer must be a writer if it observes sequence numbers") - } - - if len(msgs) == 0 { - return nil - } - - for _, msg := range msgs { - // Observer must be able to read the chain that the message is coming from. - if !nodeSupportedChains.Contains(msg.SourceChain) { - return fmt.Errorf("observer not allowed to read chain %d", msg.SourceChain) - } - } - - return nil -} - -func validateObservedTokenPrices(tokenPrices []cciptypes.TokenPrice) error { - tokensWithPrice := mapset.NewSet[types.Account]() - for _, t := range tokenPrices { - if tokensWithPrice.Contains(t.TokenID) { - return fmt.Errorf("duplicate token price for token: %s", t.TokenID) - } - tokensWithPrice.Add(t.TokenID) - - if t.Price.IsEmpty() { - return fmt.Errorf("token price must not be empty") - } - } - - return nil -} - -func validateObservedGasPrices(gasPrices []cciptypes.GasPriceChain) error { - // Duplicate gas prices must not appear for the same chain and must not be empty. - gasPriceChains := mapset.NewSet[cciptypes.ChainSelector]() - for _, g := range gasPrices { - if gasPriceChains.Contains(g.ChainSel) { - return fmt.Errorf("duplicate gas price for chain %d", g.ChainSel) - } - gasPriceChains.Add(g.ChainSel) - if g.GasPrice.IsEmpty() { - return fmt.Errorf("gas price must not be empty") - } - } - - return nil -} - -type observedMsgsConsensus struct { - seqNumRange cciptypes.SeqNumRange - merkleRoot [32]byte -} - -func (o observedMsgsConsensus) isEmpty() bool { - return o.seqNumRange.Start() == 0 && o.seqNumRange.End() == 0 && o.merkleRoot == [32]byte{} -} diff --git a/core/services/ocr3/plugins/ccip/commit/plugin_functions_test.go b/core/services/ocr3/plugins/ccip/commit/plugin_functions_test.go deleted file mode 100644 index 31785fbbbf..0000000000 --- a/core/services/ocr3/plugins/ccip/commit/plugin_functions_test.go +++ /dev/null @@ -1,1190 +0,0 @@ -package commit - -import ( - "context" - "math/big" - "slices" - "strconv" - "testing" - "time" - - mapset "github.com/deckarep/golang-set/v2" - libocrtypes "github.com/smartcontractkit/libocr/ragep2p/types" - - "github.com/smartcontractkit/ccipocr3/internal/libs/slicelib" - "github.com/smartcontractkit/ccipocr3/internal/mocks" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" -) - -func Test_observeMaxSeqNumsPerChain(t *testing.T) { - testCases := []struct { - name string - prevOutcome cciptypes.CommitPluginOutcome - onChainSeqNums map[cciptypes.ChainSelector]cciptypes.SeqNum - readChains []cciptypes.ChainSelector - destChain cciptypes.ChainSelector - expErr bool - expSeqNumsInSync bool - expMaxSeqNums []cciptypes.SeqNumChain - }{ - { - name: "report on chain seq num and can read dest", - onChainSeqNums: map[cciptypes.ChainSelector]cciptypes.SeqNum{ - 1: 10, - 2: 20, - }, - readChains: []cciptypes.ChainSelector{1, 2, 3}, - destChain: 3, - expErr: false, - expMaxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - {ChainSel: 2, SeqNum: 20}, - }, - }, - { - name: "cannot read dest", - prevOutcome: cciptypes.CommitPluginOutcome{ - MaxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 11}, // for chain 1 previous outcome is higher than on-chain state - {ChainSel: 2, SeqNum: 19}, // for chain 2 previous outcome is behind on-chain state - }, - }, - onChainSeqNums: map[cciptypes.ChainSelector]cciptypes.SeqNum{ - 1: 10, - 2: 20, - }, - readChains: []cciptypes.ChainSelector{1, 2}, - destChain: 3, - expErr: false, - expMaxSeqNums: []cciptypes.SeqNumChain{}, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - ctx := context.Background() - mockReader := mocks.NewCCIPReader() - knownSourceChains := slicelib.Filter( - tc.readChains, - func(ch cciptypes.ChainSelector) bool { return ch != tc.destChain }, - ) - lggr := logger.Test(t) - - onChainSeqNums := make([]cciptypes.SeqNum, 0) - for _, chain := range knownSourceChains { - if v, ok := tc.onChainSeqNums[chain]; !ok { - t.Fatalf("invalid test case missing on chain seq num expectation for %d", chain) - } else { - onChainSeqNums = append(onChainSeqNums, v) - } - } - mockReader.On("NextSeqNum", ctx, knownSourceChains).Return(onChainSeqNums, nil) - - seqNums, err := observeLatestCommittedSeqNums( - ctx, - lggr, - mockReader, - mapset.NewSet(tc.readChains...), - tc.destChain, - knownSourceChains, - ) - - if tc.expErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - assert.Equal(t, tc.expMaxSeqNums, seqNums) - }) - } -} - -func Test_observeNewMsgs(t *testing.T) { - testCases := []struct { - name string - maxSeqNumsPerChain []cciptypes.SeqNumChain - readChains []cciptypes.ChainSelector - destChain cciptypes.ChainSelector - msgScanBatchSize int - newMsgs map[cciptypes.ChainSelector][]cciptypes.CCIPMsg - expMsgs []cciptypes.CCIPMsg - expErr bool - }{ - { - name: "no new messages", - maxSeqNumsPerChain: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - {ChainSel: 2, SeqNum: 20}, - }, - readChains: []cciptypes.ChainSelector{1, 2}, - msgScanBatchSize: 256, - newMsgs: map[cciptypes.ChainSelector][]cciptypes.CCIPMsg{ - 1: {}, - 2: {}, - }, - expMsgs: []cciptypes.CCIPMsg{}, - expErr: false, - }, - { - name: "new messages", - maxSeqNumsPerChain: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - {ChainSel: 2, SeqNum: 20}, - }, - readChains: []cciptypes.ChainSelector{1, 2}, - msgScanBatchSize: 256, - newMsgs: map[cciptypes.ChainSelector][]cciptypes.CCIPMsg{ - 1: { - {CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ID: "1", SourceChain: 1, SeqNum: 11}}, - }, - 2: { - {CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ID: "2", SourceChain: 2, SeqNum: 21}}, - {CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ID: "3", SourceChain: 2, SeqNum: 22}}, - }, - }, - expMsgs: []cciptypes.CCIPMsg{ - {CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ID: "1", SourceChain: 1, SeqNum: 11}}, - {CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ID: "2", SourceChain: 2, SeqNum: 21}}, - {CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ID: "3", SourceChain: 2, SeqNum: 22}}, - }, - expErr: false, - }, - { - name: "new messages but one chain is not readable", - maxSeqNumsPerChain: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - {ChainSel: 2, SeqNum: 20}, - }, - readChains: []cciptypes.ChainSelector{2}, - msgScanBatchSize: 256, - newMsgs: map[cciptypes.ChainSelector][]cciptypes.CCIPMsg{ - 2: { - {CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ID: "2", SourceChain: 2, SeqNum: 21}}, - {CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ID: "3", SourceChain: 2, SeqNum: 22}}, - }, - }, - expMsgs: []cciptypes.CCIPMsg{ - {CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ID: "2", SourceChain: 2, SeqNum: 21}}, - {CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ID: "3", SourceChain: 2, SeqNum: 22}}, - }, - expErr: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - ctx := context.Background() - mockReader := mocks.NewCCIPReader() - msgHasher := mocks.NewMessageHasher() - for i := range tc.expMsgs { // make sure the hashes are populated - h, err := msgHasher.Hash(ctx, tc.expMsgs[i]) - assert.NoError(t, err) - tc.expMsgs[i].MsgHash = h - } - - lggr := logger.Test(t) - - for _, seqNumChain := range tc.maxSeqNumsPerChain { - if slices.Contains(tc.readChains, seqNumChain.ChainSel) { - mockReader.On( - "MsgsBetweenSeqNums", - ctx, - seqNumChain.ChainSel, - cciptypes.NewSeqNumRange(seqNumChain.SeqNum+1, seqNumChain.SeqNum+cciptypes.SeqNum(1+tc.msgScanBatchSize)), - ).Return(tc.newMsgs[seqNumChain.ChainSel], nil) - } - } - - msgs, err := observeNewMsgs( - ctx, - lggr, - mockReader, - msgHasher, - mapset.NewSet(tc.readChains...), - tc.maxSeqNumsPerChain, - tc.msgScanBatchSize, - ) - if tc.expErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - assert.Equal(t, tc.expMsgs, msgs) - mockReader.AssertExpectations(t) - }) - } -} - -func Benchmark_observeNewMsgs(b *testing.B) { - const ( - numChains = 5 - readerDelayMS = 100 - newMsgsPerChain = 256 - ) - - readChains := make([]cciptypes.ChainSelector, numChains) - maxSeqNumsPerChain := make([]cciptypes.SeqNumChain, numChains) - for i := 0; i < numChains; i++ { - readChains[i] = cciptypes.ChainSelector(i + 1) - maxSeqNumsPerChain[i] = cciptypes.SeqNumChain{ChainSel: cciptypes.ChainSelector(i + 1), SeqNum: cciptypes.SeqNum(1)} - } - - for i := 0; i < b.N; i++ { - ctx := context.Background() - lggr, _ := logger.New() - ccipReader := mocks.NewCCIPReader() - msgHasher := mocks.NewMessageHasher() - - expNewMsgs := make([]cciptypes.CCIPMsg, 0, newMsgsPerChain*numChains) - for _, seqNumChain := range maxSeqNumsPerChain { - newMsgs := make([]cciptypes.CCIPMsg, 0, newMsgsPerChain) - for msgSeqNum := 1; msgSeqNum <= newMsgsPerChain; msgSeqNum++ { - newMsgs = append(newMsgs, cciptypes.CCIPMsg{ - CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ - ID: strconv.Itoa(msgSeqNum), - SourceChain: seqNumChain.ChainSel, - SeqNum: cciptypes.SeqNum(msgSeqNum), - }, - }) - } - - ccipReader.On( - "MsgsBetweenSeqNums", - ctx, - []cciptypes.ChainSelector{seqNumChain.ChainSel}, - cciptypes.NewSeqNumRange( - seqNumChain.SeqNum+1, - seqNumChain.SeqNum+cciptypes.SeqNum(1+newMsgsPerChain), - ), - ).Run(func(args mock.Arguments) { - time.Sleep(time.Duration(readerDelayMS) * time.Millisecond) - }).Return(newMsgs, nil) - expNewMsgs = append(expNewMsgs, newMsgs...) - } - - msgs, err := observeNewMsgs( - ctx, - lggr, - ccipReader, - msgHasher, - mapset.NewSet(readChains...), - maxSeqNumsPerChain, - newMsgsPerChain, - ) - assert.NoError(b, err) - assert.Equal(b, expNewMsgs, msgs) - - // (old) sequential: 509.345 ms/op (numChains * readerDelayMS) - // (current) parallel: 102.543 ms/op (readerDelayMS) - } -} - -func Test_observeTokenPrices(t *testing.T) { - ctx := context.Background() - - t.Run("happy path", func(t *testing.T) { - priceReader := mocks.NewTokenPricesReader() - tokens := []types.Account{"0x1", "0x2", "0x3"} - mockPrices := []*big.Int{big.NewInt(10), big.NewInt(20), big.NewInt(30)} - priceReader.On("GetTokenPricesUSD", ctx, tokens).Return(mockPrices, nil) - prices, err := observeTokenPrices(ctx, priceReader, tokens) - assert.NoError(t, err) - assert.Equal(t, []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(10)), - cciptypes.NewTokenPrice("0x2", big.NewInt(20)), - cciptypes.NewTokenPrice("0x3", big.NewInt(30)), - }, prices) - }) - - t.Run("price reader internal issue", func(t *testing.T) { - priceReader := mocks.NewTokenPricesReader() - tokens := []types.Account{"0x1", "0x2", "0x3"} - mockPrices := []*big.Int{big.NewInt(10), big.NewInt(20)} // returned two prices for three tokens - priceReader.On("GetTokenPricesUSD", ctx, tokens).Return(mockPrices, nil) - _, err := observeTokenPrices(ctx, priceReader, tokens) - assert.Error(t, err) - }) - -} - -func Test_observeGasPrices(t *testing.T) { - ctx := context.Background() - - t.Run("happy path", func(t *testing.T) { - mockReader := mocks.NewCCIPReader() - chains := []cciptypes.ChainSelector{1, 2, 3} - mockGasPrices := []cciptypes.BigInt{ - cciptypes.NewBigIntFromInt64(10), - cciptypes.NewBigIntFromInt64(20), - cciptypes.NewBigIntFromInt64(30), - } - mockReader.On("GasPrices", ctx, chains).Return(mockGasPrices, nil) - gasPrices, err := observeGasPrices(ctx, mockReader, chains) - assert.NoError(t, err) - assert.Equal(t, []cciptypes.GasPriceChain{ - cciptypes.NewGasPriceChain(mockGasPrices[0].Int, chains[0]), - cciptypes.NewGasPriceChain(mockGasPrices[1].Int, chains[1]), - cciptypes.NewGasPriceChain(mockGasPrices[2].Int, chains[2]), - }, gasPrices) - }) - - t.Run("gas reader internal issue", func(t *testing.T) { - mockReader := mocks.NewCCIPReader() - chains := []cciptypes.ChainSelector{1, 2, 3} - mockGasPrices := []cciptypes.BigInt{ - cciptypes.NewBigIntFromInt64(10), - cciptypes.NewBigIntFromInt64(20), - } // return 2 prices for 3 chains - mockReader.On("GasPrices", ctx, chains).Return(mockGasPrices, nil) - _, err := observeGasPrices(ctx, mockReader, chains) - assert.Error(t, err) - }) -} - -func Test_validateObservedSequenceNumbers(t *testing.T) { - testCases := []struct { - name string - msgs []cciptypes.CCIPMsgBaseDetails - maxSeqNums []cciptypes.SeqNumChain - expErr bool - }{ - { - name: "empty", - msgs: nil, - maxSeqNums: nil, - expErr: false, - }, - { - name: "dup seq num observation", - msgs: nil, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 1, SeqNum: 10}, - }, - expErr: true, - }, - { - name: "seq nums ok", - msgs: nil, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - {ChainSel: 2, SeqNum: 20}, - }, - expErr: false, - }, - { - name: "dup msg seq num", - msgs: []cciptypes.CCIPMsgBaseDetails{ - {ID: "1", SourceChain: 1, SeqNum: 12}, - {ID: "1", SourceChain: 1, SeqNum: 13}, - {ID: "1", SourceChain: 1, SeqNum: 14}, - {ID: "1", SourceChain: 1, SeqNum: 13}, // dup - }, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - {ChainSel: 2, SeqNum: 20}, - }, - expErr: true, - }, - { - name: "msg seq nums ok", - msgs: []cciptypes.CCIPMsgBaseDetails{ - {MsgHash: cciptypes.Bytes32{1}, ID: "1", SourceChain: 1, SeqNum: 12}, - {MsgHash: cciptypes.Bytes32{2}, ID: "1", SourceChain: 1, SeqNum: 13}, - {MsgHash: cciptypes.Bytes32{3}, ID: "1", SourceChain: 1, SeqNum: 14}, - {MsgHash: cciptypes.Bytes32{4}, ID: "1", SourceChain: 2, SeqNum: 21}, - }, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - {ChainSel: 2, SeqNum: 20}, - }, - expErr: false, - }, - { - name: "msg seq nums does not match observed max seq num", - msgs: []cciptypes.CCIPMsgBaseDetails{ - {ID: "1", SourceChain: 1, SeqNum: 12}, - {ID: "1", SourceChain: 1, SeqNum: 13}, - {ID: "1", SourceChain: 1, SeqNum: 10}, // max seq num is already 10 - {ID: "1", SourceChain: 2, SeqNum: 21}, - }, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - {ChainSel: 2, SeqNum: 20}, - }, - expErr: true, - }, - { - name: "max seq num not found", - msgs: []cciptypes.CCIPMsgBaseDetails{ - {ID: "1", SourceChain: 1, SeqNum: 12}, - {ID: "1", SourceChain: 1, SeqNum: 13}, - {ID: "1", SourceChain: 1, SeqNum: 14}, - {ID: "1", SourceChain: 2, SeqNum: 21}, // max seq num not reported - }, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - }, - expErr: true, - }, - { - name: "msg hashes ok", - msgs: []cciptypes.CCIPMsgBaseDetails{ - {MsgHash: cciptypes.Bytes32{123}, ID: "1", SourceChain: 1, SeqNum: 12}, - {MsgHash: cciptypes.Bytes32{99}, ID: "1", SourceChain: 1, SeqNum: 13}, - {MsgHash: cciptypes.Bytes32{12}, ID: "1", SourceChain: 300, SeqNum: 23}, - }, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 300, SeqNum: 22}, - }, - expErr: false, - }, - { - name: "dup msg hashes", - msgs: []cciptypes.CCIPMsgBaseDetails{ - {MsgHash: cciptypes.Bytes32{123}, ID: "1", SourceChain: 1, SeqNum: 12}, - {MsgHash: cciptypes.Bytes32{99}, ID: "1", SourceChain: 1, SeqNum: 13}, - {MsgHash: cciptypes.Bytes32{123}, ID: "1", SourceChain: 300, SeqNum: 23}, // dup hash - }, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 300, SeqNum: 22}, - }, - expErr: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := validateObservedSequenceNumbers(tc.msgs, tc.maxSeqNums) - if tc.expErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - }) - } -} - -func Test_validateObserverReadingEligibility(t *testing.T) { - testCases := []struct { - name string - observer libocrtypes.PeerID - msgs []cciptypes.CCIPMsgBaseDetails - seqNums []cciptypes.SeqNumChain - nodeSupportedChains mapset.Set[cciptypes.ChainSelector] - destChain cciptypes.ChainSelector - expErr bool - }{ - { - name: "observer can read all chains", - observer: libocrtypes.PeerID{10}, - msgs: []cciptypes.CCIPMsgBaseDetails{ - {ID: "1", SourceChain: 1, SeqNum: 12}, - {ID: "3", SourceChain: 2, SeqNum: 12}, - {ID: "1", SourceChain: 3, SeqNum: 12}, - {ID: "2", SourceChain: 3, SeqNum: 12}, - }, - nodeSupportedChains: mapset.NewSet[cciptypes.ChainSelector](1, 2, 3), - destChain: 1, - expErr: false, - }, - { - name: "observer is a writer so can observe seq nums", - observer: libocrtypes.PeerID{10}, - msgs: []cciptypes.CCIPMsgBaseDetails{}, - seqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 12}, - }, - nodeSupportedChains: mapset.NewSet[cciptypes.ChainSelector](1, 3), - destChain: 1, - expErr: false, - }, - { - name: "observer is not a writer so cannot observe seq nums", - observer: libocrtypes.PeerID{10}, - msgs: []cciptypes.CCIPMsgBaseDetails{}, - seqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 12}, - }, - nodeSupportedChains: mapset.NewSet[cciptypes.ChainSelector](3), - destChain: 1, - expErr: true, - }, - { - name: "observer cfg not found", - observer: libocrtypes.PeerID{10}, - msgs: []cciptypes.CCIPMsgBaseDetails{ - {ID: "1", SourceChain: 1, SeqNum: 12}, - {ID: "3", SourceChain: 2, SeqNum: 12}, - {ID: "1", SourceChain: 3, SeqNum: 12}, - {ID: "2", SourceChain: 3, SeqNum: 12}, - }, - nodeSupportedChains: mapset.NewSet[cciptypes.ChainSelector](1, 3), // observer 10 not found - destChain: 1, - expErr: true, - }, - { - name: "no msgs", - observer: libocrtypes.PeerID{10}, - msgs: []cciptypes.CCIPMsgBaseDetails{}, - nodeSupportedChains: mapset.NewSet[cciptypes.ChainSelector](1, 3), - expErr: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := validateObserverReadingEligibility(tc.msgs, tc.seqNums, tc.nodeSupportedChains, tc.destChain) - if tc.expErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - }) - } -} - -func Test_validateObservedTokenPrices(t *testing.T) { - testCases := []struct { - name string - tokenPrices []cciptypes.TokenPrice - expErr bool - }{ - { - name: "empty is valid", - tokenPrices: []cciptypes.TokenPrice{}, - expErr: false, - }, - { - name: "all valid", - tokenPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(1)), - cciptypes.NewTokenPrice("0x2", big.NewInt(1)), - cciptypes.NewTokenPrice("0x3", big.NewInt(1)), - cciptypes.NewTokenPrice("0xa", big.NewInt(1)), - }, - expErr: false, - }, - { - name: "dup price", - tokenPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(1)), - cciptypes.NewTokenPrice("0x2", big.NewInt(1)), - cciptypes.NewTokenPrice("0x1", big.NewInt(1)), // dup - cciptypes.NewTokenPrice("0xa", big.NewInt(1)), - }, - expErr: true, - }, - { - name: "nil price", - tokenPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(1)), - cciptypes.NewTokenPrice("0x2", big.NewInt(1)), - cciptypes.NewTokenPrice("0x3", nil), // nil price - cciptypes.NewTokenPrice("0xa", big.NewInt(1)), - }, - expErr: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := validateObservedTokenPrices(tc.tokenPrices) - if tc.expErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - }) - - } -} - -func Test_validateObservedGasPrices(t *testing.T) { - testCases := []struct { - name string - gasPrices []cciptypes.GasPriceChain - expErr bool - }{ - { - name: "empty is valid", - gasPrices: []cciptypes.GasPriceChain{}, - expErr: false, - }, - { - name: "all valid", - gasPrices: []cciptypes.GasPriceChain{ - cciptypes.NewGasPriceChain(big.NewInt(10), 1), - cciptypes.NewGasPriceChain(big.NewInt(20), 2), - cciptypes.NewGasPriceChain(big.NewInt(1312), 3), - }, - expErr: false, - }, - { - name: "duplicate gas price", - gasPrices: []cciptypes.GasPriceChain{ - cciptypes.NewGasPriceChain(big.NewInt(10), 1), - cciptypes.NewGasPriceChain(big.NewInt(20), 2), - cciptypes.NewGasPriceChain(big.NewInt(1312), 1), // notice we already have a gas price for chain 1 - }, - expErr: true, - }, - { - name: "empty gas price", - gasPrices: []cciptypes.GasPriceChain{ - cciptypes.NewGasPriceChain(big.NewInt(10), 1), - cciptypes.NewGasPriceChain(big.NewInt(20), 2), - cciptypes.NewGasPriceChain(nil, 3), // nil - }, - expErr: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := validateObservedGasPrices(tc.gasPrices) - if tc.expErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - }) - } -} - -func Test_newMsgsConsensusForChain(t *testing.T) { - testCases := []struct { - name string - maxSeqNums []cciptypes.SeqNumChain - observations []cciptypes.CommitPluginObservation - expMerkleRoots []cciptypes.MerkleRootChain - fChain map[cciptypes.ChainSelector]int - expErr bool - }{ - { - name: "empty", - maxSeqNums: []cciptypes.SeqNumChain{}, - observations: nil, - expMerkleRoots: []cciptypes.MerkleRootChain{}, - expErr: false, - }, - { - name: "one message but not reaching 2fChain+1 observations", - fChain: map[cciptypes.ChainSelector]int{ - 1: 2, - }, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - }, - observations: []cciptypes.CommitPluginObservation{ - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - }, - expMerkleRoots: []cciptypes.MerkleRootChain{}, - expErr: false, - }, - { - name: "one message reaching 2fChain+1 observations", - fChain: map[cciptypes.ChainSelector]int{ - 1: 2, - }, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - }, - observations: []cciptypes.CommitPluginObservation{ - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - }, - expMerkleRoots: []cciptypes.MerkleRootChain{ - { - ChainSel: 1, - SeqNumsRange: cciptypes.NewSeqNumRange(11, 11), - }, - }, - expErr: false, - }, - { - name: "multiple messages all of them reaching 2fChain+1 observations", - fChain: map[cciptypes.ChainSelector]int{ - 1: 2, - }, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - }, - observations: []cciptypes.CommitPluginObservation{ - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 12}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 12}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 12}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 12}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 12}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - }, - expMerkleRoots: []cciptypes.MerkleRootChain{ - { - ChainSel: 1, - SeqNumsRange: cciptypes.NewSeqNumRange(11, 13), - }, - }, - expErr: false, - }, - { - name: "one message sequence number is lower than consensus max seq num", - fChain: map[cciptypes.ChainSelector]int{ - 1: 2, - }, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - }, - observations: []cciptypes.CommitPluginObservation{ - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 10}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 10}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 10}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 10}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 10}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 12}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 12}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 12}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 12}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 12}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - }, - expMerkleRoots: []cciptypes.MerkleRootChain{ - { - ChainSel: 1, - SeqNumsRange: cciptypes.NewSeqNumRange(12, 13), - }, - }, - expErr: false, - }, - { - name: "multiple messages some of them not reaching 2fChain+1 observations", - fChain: map[cciptypes.ChainSelector]int{ - 1: 2, - }, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - }, - observations: []cciptypes.CommitPluginObservation{ - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 12}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 12}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 13}}}, - }, - expMerkleRoots: []cciptypes.MerkleRootChain{ - { - ChainSel: 1, - SeqNumsRange: cciptypes.NewSeqNumRange(11, 11), // we stop at 11 because there is a gap for going to 13 - }, - }, - expErr: false, - }, - { - name: "multiple messages on different chains", - fChain: map[cciptypes.ChainSelector]int{ - 1: 2, - 2: 1, - }, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - {ChainSel: 2, SeqNum: 20}, - }, - observations: []cciptypes.CommitPluginObservation{ - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 2, SeqNum: 21}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 2, SeqNum: 21}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 2, SeqNum: 21}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "4", SourceChain: 2, SeqNum: 22}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "4", SourceChain: 2, SeqNum: 22}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "4", SourceChain: 2, SeqNum: 22}}}, - }, - expMerkleRoots: []cciptypes.MerkleRootChain{ - { - ChainSel: 1, - SeqNumsRange: cciptypes.NewSeqNumRange(11, 11), // we stop at 11 because there is a gap for going to 13 - }, - { - ChainSel: 2, - SeqNumsRange: cciptypes.NewSeqNumRange(21, 22), // we stop at 11 because there is a gap for going to 13 - }, - }, - expErr: false, - }, - { - name: "one message seq num with multiple reported ids", - fChain: map[cciptypes.ChainSelector]int{ - 1: 2, - }, - maxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 1, SeqNum: 10}, - }, - observations: []cciptypes.CommitPluginObservation{ - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "1", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "10", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "10", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "111", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "111", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "3", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 11}}}, - {NewMsgs: []cciptypes.CCIPMsgBaseDetails{{ID: "2", SourceChain: 1, SeqNum: 11}}}, - }, - expMerkleRoots: []cciptypes.MerkleRootChain{ - { - ChainSel: 1, - SeqNumsRange: cciptypes.NewSeqNumRange(11, 11), - }, - }, - expErr: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - lggr := logger.Test(t) - merkleRoots, err := newMsgsConsensus(lggr, tc.maxSeqNums, tc.observations, tc.fChain) - if tc.expErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - assert.Equal(t, len(tc.expMerkleRoots), len(merkleRoots)) - for i, exp := range tc.expMerkleRoots { - assert.Equal(t, exp.ChainSel, merkleRoots[i].ChainSel) - assert.Equal(t, exp.SeqNumsRange, merkleRoots[i].SeqNumsRange) - } - }) - } -} - -func Test_maxSeqNumsConsensus(t *testing.T) { - testCases := []struct { - name string - observations []cciptypes.CommitPluginObservation - fChain int - expSeqNums []cciptypes.SeqNumChain - }{ - { - name: "empty observations", - observations: []cciptypes.CommitPluginObservation{}, - fChain: 2, - expSeqNums: []cciptypes.SeqNumChain{}, - }, - { - name: "one chain all followers agree", - observations: []cciptypes.CommitPluginObservation{ - { - MaxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - }, - }, - }, - fChain: 2, - expSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 2, SeqNum: 20}, - }, - }, - { - name: "one chain all followers agree but not enough observations", - observations: []cciptypes.CommitPluginObservation{ - { - MaxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - }, - }, - }, - fChain: 3, - expSeqNums: []cciptypes.SeqNumChain{}, - }, - { - name: "one chain 3 followers not in sync, 4 in sync", - observations: []cciptypes.CommitPluginObservation{ - { - MaxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 19}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 19}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 19}, - {ChainSel: 2, SeqNum: 20}, - }, - }, - }, - fChain: 3, - expSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 2, SeqNum: 20}, - }, - }, - { - name: "two chains", - observations: []cciptypes.CommitPluginObservation{ - { - MaxSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 2, SeqNum: 20}, - - {ChainSel: 3, SeqNum: 30}, - {ChainSel: 3, SeqNum: 30}, - {ChainSel: 3, SeqNum: 30}, - {ChainSel: 3, SeqNum: 30}, - {ChainSel: 3, SeqNum: 30}, - }, - }, - }, - fChain: 2, - expSeqNums: []cciptypes.SeqNumChain{ - {ChainSel: 2, SeqNum: 20}, - {ChainSel: 3, SeqNum: 30}, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - lggr := logger.Test(t) - seqNums := maxSeqNumsConsensus(lggr, tc.fChain, tc.observations) - assert.Equal(t, tc.expSeqNums, seqNums) - }) - } -} - -func Test_tokenPricesConsensus(t *testing.T) { - testCases := []struct { - name string - observations []cciptypes.CommitPluginObservation - fChain int - expPrices []cciptypes.TokenPrice - expErr bool - }{ - { - name: "empty", - observations: make([]cciptypes.CommitPluginObservation, 0), - fChain: 2, - expPrices: make([]cciptypes.TokenPrice, 0), - expErr: false, - }, - { - name: "happy flow", - observations: []cciptypes.CommitPluginObservation{ - { - TokenPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(10)), - cciptypes.NewTokenPrice("0x2", big.NewInt(20)), - }, - }, - { - TokenPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(11)), - cciptypes.NewTokenPrice("0x2", big.NewInt(21)), - }, - }, - { - TokenPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(11)), - cciptypes.NewTokenPrice("0x2", big.NewInt(21)), - }, - }, - { - TokenPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(10)), - cciptypes.NewTokenPrice("0x2", big.NewInt(21)), - }, - }, - { - TokenPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(11)), - cciptypes.NewTokenPrice("0x2", big.NewInt(20)), - }, - }, - }, - fChain: 2, - expPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(11)), - cciptypes.NewTokenPrice("0x2", big.NewInt(21)), - }, - expErr: false, - }, - { - name: "not enough observations for some token", - observations: []cciptypes.CommitPluginObservation{ - { - TokenPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x2", big.NewInt(20)), - }, - }, - { - TokenPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(11)), - cciptypes.NewTokenPrice("0x2", big.NewInt(21)), - }, - }, - { - TokenPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(11)), - cciptypes.NewTokenPrice("0x2", big.NewInt(21)), - }, - }, - { - TokenPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(10)), - cciptypes.NewTokenPrice("0x2", big.NewInt(21)), - }, - }, - { - TokenPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x1", big.NewInt(10)), - cciptypes.NewTokenPrice("0x2", big.NewInt(20)), - }, - }, - }, - fChain: 2, - expPrices: []cciptypes.TokenPrice{ - cciptypes.NewTokenPrice("0x2", big.NewInt(21)), - }, - expErr: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - prices, err := tokenPricesConsensus(tc.observations, tc.fChain) - if tc.expErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - assert.Equal(t, tc.expPrices, prices) - }) - } -} - -func Test_gasPricesConsensus(t *testing.T) { - testCases := []struct { - name string - observations []cciptypes.CommitPluginObservation - fChain int - expPrices []cciptypes.GasPriceChain - }{ - { - name: "empty", - observations: make([]cciptypes.CommitPluginObservation, 0), - fChain: 2, - expPrices: make([]cciptypes.GasPriceChain, 0), - }, - { - name: "one chain happy path", - observations: []cciptypes.CommitPluginObservation{ - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(20), 1)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(10), 1)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(10), 1)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(11), 1)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(10), 1)}}, - }, - fChain: 2, - expPrices: []cciptypes.GasPriceChain{ - cciptypes.NewGasPriceChain(big.NewInt(10), 1), - }, - }, - { - name: "one chain no consensus", - observations: []cciptypes.CommitPluginObservation{ - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(20), 1)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(10), 1)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(10), 1)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(11), 1)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(10), 1)}}, - }, - fChain: 3, // notice fChain is 3, means we need at least 2*3+1=7 observations - expPrices: []cciptypes.GasPriceChain{}, - }, - { - name: "two chains determinism check", - observations: []cciptypes.CommitPluginObservation{ - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(20), 1)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(10), 1)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(10), 1)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(11), 1)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(10), 1)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(200), 10)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(100), 10)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(100), 10)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(110), 10)}}, - {GasPrices: []cciptypes.GasPriceChain{cciptypes.NewGasPriceChain(big.NewInt(100), 10)}}, - }, - fChain: 2, - expPrices: []cciptypes.GasPriceChain{ - cciptypes.NewGasPriceChain(big.NewInt(10), 1), - cciptypes.NewGasPriceChain(big.NewInt(100), 10), - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - lggr := logger.Test(t) - prices := gasPricesConsensus(lggr, tc.observations, tc.fChain) - assert.Equal(t, tc.expPrices, prices) - }) - } -} diff --git a/core/services/ocr3/plugins/ccip/execute/factory.go b/core/services/ocr3/plugins/ccip/execute/factory.go deleted file mode 100644 index a75139d12e..0000000000 --- a/core/services/ocr3/plugins/ccip/execute/factory.go +++ /dev/null @@ -1,79 +0,0 @@ -package execute - -import ( - "context" - - "google.golang.org/grpc" - - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" - "github.com/smartcontractkit/chainlink-common/pkg/types/core" -) - -// PluginFactoryConstructor implements common OCR3ReportingPluginClient and is used for initializing a plugin factory -// and a validation service. -type PluginFactoryConstructor struct{} - -func NewPluginFactoryConstructor() *PluginFactoryConstructor { - return &PluginFactoryConstructor{} -} -func (p PluginFactoryConstructor) NewReportingPluginFactory( - ctx context.Context, - config core.ReportingPluginServiceConfig, - grpcProvider grpc.ClientConnInterface, - pipelineRunner core.PipelineRunnerService, - telemetry core.TelemetryService, - errorLog core.ErrorLog, - capRegistry core.CapabilitiesRegistry, - keyValueStore core.KeyValueStore, - relayerSet core.RelayerSet, -) (core.OCR3ReportingPluginFactory, error) { - return NewPluginFactory(), nil -} - -func (p PluginFactoryConstructor) NewValidationService(ctx context.Context) (core.ValidationService, error) { - panic("implement me") -} - -// PluginFactory implements common ReportingPluginFactory and is used for (re-)initializing commit plugin instances. -type PluginFactory struct{} - -func NewPluginFactory() *PluginFactory { - return &PluginFactory{} -} - -func (p PluginFactory) NewReportingPlugin( - config ocr3types.ReportingPluginConfig, -) (ocr3types.ReportingPlugin[[]byte], ocr3types.ReportingPluginInfo, error) { - return NewPlugin( - context.Background(), - config, - cciptypes.ExecutePluginConfig{}, - nil, - ), ocr3types.ReportingPluginInfo{}, nil -} - -func (p PluginFactory) Name() string { - panic("implement me") -} - -func (p PluginFactory) Start(ctx context.Context) error { - panic("implement me") -} - -func (p PluginFactory) Close() error { - panic("implement me") -} - -func (p PluginFactory) Ready() error { - panic("implement me") -} - -func (p PluginFactory) HealthReport() map[string]error { - panic("implement me") -} - -// Interface compatibility checks. -var _ core.OCR3ReportingPluginClient = &PluginFactoryConstructor{} -var _ core.OCR3ReportingPluginFactory = &PluginFactory{} diff --git a/core/services/ocr3/plugins/ccip/execute/internal/validation/reports.go b/core/services/ocr3/plugins/ccip/execute/internal/validation/reports.go deleted file mode 100644 index 61114a0232..0000000000 --- a/core/services/ocr3/plugins/ccip/execute/internal/validation/reports.go +++ /dev/null @@ -1,58 +0,0 @@ -package validation - -import ( - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" -) - -type counter[T any] struct { - data T - count int -} - -// MinObservationFilter provides a way to ensure a minimum number of observations for -// some piece of data have occurred. It maintains an internal cache and provides a list -// of valid or invalid data points. -type MinObservationFilter[T any] interface { - Add(data T) error - GetValid() ([]T, error) -} - -// minObservationValidator is a helper object to validate reports for a single chain. -// It keeps track of all reports and determines if they observations are consistent -// with one another and whether they meet the required fChain threshold. -type minObservationValidator[T any] struct { - minObservation int - cache map[cciptypes.Bytes32]*counter[T] - idFunc func(T) [32]byte -} - -// NewMinObservationValidator constructs a concrete MinObservationFilter object. The -// supplied idFunc is used to generate a uniqueID for the type being observed. -func NewMinObservationValidator[T any](min int, idFunc func(T) [32]byte) MinObservationFilter[T] { - return &minObservationValidator[T]{ - minObservation: min, - cache: make(map[cciptypes.Bytes32]*counter[T]), - idFunc: idFunc, - } -} - -func (cv *minObservationValidator[T]) Add(data T) error { - id := cv.idFunc(data) - if _, ok := cv.cache[id]; ok { - cv.cache[id].count++ - } else { - cv.cache[id] = &counter[T]{data: data, count: 1} - } - return nil -} - -func (cv *minObservationValidator[T]) GetValid() ([]T, error) { - var validated []T - for _, rc := range cv.cache { - if rc.count >= cv.minObservation { - rc := rc - validated = append(validated, rc.data) - } - } - return validated, nil -} diff --git a/core/services/ocr3/plugins/ccip/execute/internal/validation/reports_test.go b/core/services/ocr3/plugins/ccip/execute/internal/validation/reports_test.go deleted file mode 100644 index 73db4bcef9..0000000000 --- a/core/services/ocr3/plugins/ccip/execute/internal/validation/reports_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package validation - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "golang.org/x/crypto/sha3" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" -) - -func Test_CommitReportValidator_ExecutePluginCommitData(t *testing.T) { - tests := []struct { - name string - min int - reports []cciptypes.ExecutePluginCommitData - valid []cciptypes.ExecutePluginCommitData - wantErr assert.ErrorAssertionFunc - }{ - { - name: "empty", - valid: nil, - wantErr: assert.NoError, - }, - { - name: "single report, enough observations", - min: 1, - reports: []cciptypes.ExecutePluginCommitData{ - {MerkleRoot: [32]byte{1}}, - }, - valid: []cciptypes.ExecutePluginCommitData{ - {MerkleRoot: [32]byte{1}}, - }, - wantErr: assert.NoError, - }, - { - name: "single report, not enough observations", - min: 2, - reports: []cciptypes.ExecutePluginCommitData{ - {MerkleRoot: [32]byte{1}}, - }, - valid: nil, - wantErr: assert.NoError, - }, - { - name: "multiple reports, partial observations", - min: 2, - reports: []cciptypes.ExecutePluginCommitData{ - {MerkleRoot: [32]byte{3}}, - {MerkleRoot: [32]byte{1}}, - {MerkleRoot: [32]byte{2}}, - {MerkleRoot: [32]byte{1}}, - {MerkleRoot: [32]byte{2}}, - }, - valid: []cciptypes.ExecutePluginCommitData{ - {MerkleRoot: [32]byte{1}}, - {MerkleRoot: [32]byte{2}}, - }, - wantErr: assert.NoError, - }, - { - name: "multiple reports for same root", - min: 2, - reports: []cciptypes.ExecutePluginCommitData{ - {MerkleRoot: [32]byte{1}, BlockNum: 1}, - {MerkleRoot: [32]byte{1}, BlockNum: 2}, - {MerkleRoot: [32]byte{1}, BlockNum: 3}, - {MerkleRoot: [32]byte{1}, BlockNum: 4}, - {MerkleRoot: [32]byte{1}, BlockNum: 1}, - }, - valid: []cciptypes.ExecutePluginCommitData{ - {MerkleRoot: [32]byte{1}, BlockNum: 1}, - }, - wantErr: assert.NoError, - }, - { - name: "different executed messages same root", - min: 2, - reports: []cciptypes.ExecutePluginCommitData{ - {MerkleRoot: [32]byte{1}, ExecutedMessages: []cciptypes.SeqNum{1, 2}}, - {MerkleRoot: [32]byte{1}, ExecutedMessages: []cciptypes.SeqNum{2, 3}}, - {MerkleRoot: [32]byte{1}, ExecutedMessages: []cciptypes.SeqNum{3, 4}}, - {MerkleRoot: [32]byte{1}, ExecutedMessages: []cciptypes.SeqNum{4, 5}}, - {MerkleRoot: [32]byte{1}, ExecutedMessages: []cciptypes.SeqNum{5, 6}}, - {MerkleRoot: [32]byte{1}, ExecutedMessages: []cciptypes.SeqNum{1, 2}}, - }, - valid: []cciptypes.ExecutePluginCommitData{ - {MerkleRoot: [32]byte{1}, ExecutedMessages: []cciptypes.SeqNum{1, 2}}, - }, - wantErr: assert.NoError, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - // Initialize the minObservationValidator - idFunc := func(data cciptypes.ExecutePluginCommitData) [32]byte { - return sha3.Sum256([]byte(fmt.Sprintf("%v", data))) - } - validator := NewMinObservationValidator[cciptypes.ExecutePluginCommitData](tt.min, idFunc) - for _, report := range tt.reports { - err := validator.Add(report) - require.NoError(t, err) - } - - // Test the results - got, err := validator.GetValid() - if !tt.wantErr(t, err, "GetValid()") { - return - } - if !assert.ElementsMatch(t, got, tt.valid) { - t.Errorf("GetValid() = %v, valid %v", got, tt.valid) - } - }) - } -} - -func Test_CommitReportValidator_Generics(t *testing.T) { - type Generic struct { - number int - } - - // Initialize the minObservationValidator - idFunc := func(data Generic) [32]byte { - return sha3.Sum256([]byte(fmt.Sprintf("%v", data))) - } - validator := NewMinObservationValidator[Generic](2, idFunc) - - wantValue := Generic{number: 1} - otherValue := Generic{number: 2} - - err := validator.Add(wantValue) - require.NoError(t, err) - err = validator.Add(wantValue) - require.NoError(t, err) - err = validator.Add(otherValue) - require.NoError(t, err) - - // Test the results - - wantValid := []Generic{wantValue} - got, err := validator.GetValid() - require.NoError(t, err) - if !assert.ElementsMatch(t, got, wantValid) { - t.Errorf("GetValid() = %v, valid %v", got, wantValid) - } -} diff --git a/core/services/ocr3/plugins/ccip/execute/plugin.go b/core/services/ocr3/plugins/ccip/execute/plugin.go deleted file mode 100644 index cb252d0cf6..0000000000 --- a/core/services/ocr3/plugins/ccip/execute/plugin.go +++ /dev/null @@ -1,275 +0,0 @@ -package execute - -import ( - "context" - "fmt" - "slices" - "sort" - "sync/atomic" - "time" - - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" -) - -// Plugin implements the main ocr3 plugin logic. -type Plugin struct { - reportingCfg ocr3types.ReportingPluginConfig - cfg cciptypes.ExecutePluginConfig - ccipReader cciptypes.CCIPReader - - //commitRootsCache cache.CommitsRootsCache - lastReportTS *atomic.Int64 -} - -func NewPlugin( - _ context.Context, - reportingCfg ocr3types.ReportingPluginConfig, - cfg cciptypes.ExecutePluginConfig, - ccipReader cciptypes.CCIPReader, -) *Plugin { - lastReportTS := &atomic.Int64{} - lastReportTS.Store(time.Now().Add(-cfg.MessageVisibilityInterval).UnixMilli()) - - return &Plugin{ - reportingCfg: reportingCfg, - cfg: cfg, - ccipReader: ccipReader, - lastReportTS: lastReportTS, - } -} - -func (p *Plugin) Query(ctx context.Context, outctx ocr3types.OutcomeContext) (types.Query, error) { - return types.Query{}, nil -} - -func getPendingExecutedReports( - ctx context.Context, ccipReader cciptypes.CCIPReader, dest cciptypes.ChainSelector, ts time.Time, -) (cciptypes.ExecutePluginCommitObservations, time.Time, error) { - latestReportTS := time.Time{} - commitReports, err := ccipReader.CommitReportsGTETimestamp(ctx, dest, ts, 1000) - if err != nil { - return nil, time.Time{}, err - } - // TODO: this could be more efficient. reports is also traversed in 'filterOutExecutedMessages' function. - for _, report := range commitReports { - if report.Timestamp.After(latestReportTS) { - latestReportTS = report.Timestamp - } - } - - // TODO: this could be more efficient. commitReports is also traversed in 'groupByChainSelector'. - for _, report := range commitReports { - if report.Timestamp.After(latestReportTS) { - latestReportTS = report.Timestamp - } - } - - groupedCommits := groupByChainSelector(commitReports) - - // Remove fully executed reports. - for selector, reports := range groupedCommits { - if len(reports) == 0 { - continue - } - - ranges, err := computeRanges(reports) - if err != nil { - return nil, time.Time{}, err - } - - var executedMessages []cciptypes.SeqNumRange - for _, seqRange := range ranges { - executedMessagesForRange, err2 := ccipReader.ExecutedMessageRanges(ctx, selector, dest, seqRange) - if err2 != nil { - return nil, time.Time{}, err2 - } - executedMessages = append(executedMessages, executedMessagesForRange...) - } - - // Remove fully executed reports. - groupedCommits[selector], err = filterOutExecutedMessages(reports, executedMessages) - if err != nil { - return nil, time.Time{}, err - } - } - - return groupedCommits, latestReportTS, nil -} - -// Observation collects data across two phases which happen in separate rounds. -// These phases happen continuously so that except for the first round, every -// subsequent round can have a new execution report. -// -// Phase 1: Gather commit reports from the destination chain and determine -// which messages are required to build a valid execution report. -// -// Phase 2: Gather messages from the source chains and build the execution -// report. -func (p *Plugin) Observation( - ctx context.Context, outctx ocr3types.OutcomeContext, _ types.Query, -) (types.Observation, error) { - previousOutcome, err := cciptypes.DecodeExecutePluginOutcome(outctx.PreviousOutcome) - if err != nil { - return types.Observation{}, err - } - - // Phase 1: Gather commit reports from the destination chain and determine which messages are required to build a - // valid execution report. - ownConfig := p.cfg.ObserverInfo[p.reportingCfg.OracleID] - var groupedCommits cciptypes.ExecutePluginCommitObservations - if slices.Contains(ownConfig.Reads, p.cfg.DestChain) { - var latestReportTS time.Time - groupedCommits, latestReportTS, err = - getPendingExecutedReports(ctx, p.ccipReader, p.cfg.DestChain, time.UnixMilli(p.lastReportTS.Load())) - if err != nil { - return types.Observation{}, err - } - // Update timestamp to the last report. - p.lastReportTS.Store(latestReportTS.UnixMilli()) - - // TODO: truncate grouped commits to a maximum observation size. - // Cache everything which is not executed. - } - - // Phase 2: Gather messages from the source chains and build the execution report. - messages := make(cciptypes.ExecutePluginMessageObservations) - if len(previousOutcome.PendingCommitReports) == 0 { - fmt.Println("TODO: No reports to execute. This is expected after a cold start.") - // No reports to execute. - // This is expected after a cold start. - } else { - commitReportCache := make(map[cciptypes.ChainSelector][]cciptypes.ExecutePluginCommitDataWithMessages) - for _, report := range previousOutcome.PendingCommitReports { - commitReportCache[report.SourceChain] = append(commitReportCache[report.SourceChain], report) - } - - for selector, reports := range commitReportCache { - if len(reports) == 0 { - continue - } - - ranges, err := computeRanges(reports) - if err != nil { - return types.Observation{}, err - } - - // Read messages for each range. - for _, seqRange := range ranges { - msgs, err := p.ccipReader.MsgsBetweenSeqNums(ctx, selector, seqRange) - if err != nil { - return nil, err - } - for _, msg := range msgs { - messages[selector][msg.SeqNum] = msg - } - } - } - } - - // TODO: Fire off messages for an attestation check service. - - return cciptypes.NewExecutePluginObservation(groupedCommits, messages).Encode() -} - -func (p *Plugin) ValidateObservation( - outctx ocr3types.OutcomeContext, query types.Query, ao types.AttributedObservation, -) error { - decodedObservation, err := cciptypes.DecodeExecutePluginObservation(ao.Observation) - if err != nil { - return fmt.Errorf("decode observation: %w", err) - } - - err = validateObserverReadingEligibility(p.reportingCfg.OracleID, p.cfg.ObserverInfo, decodedObservation.Messages) - if err != nil { - return fmt.Errorf("validate observer reading eligibility: %w", err) - } - - if err := validateObservedSequenceNumbers(decodedObservation.CommitReports); err != nil { - return fmt.Errorf("validate observed sequence numbers: %w", err) - } - - return nil -} - -func (p *Plugin) ObservationQuorum(outctx ocr3types.OutcomeContext, query types.Query) (ocr3types.Quorum, error) { - // TODO: should we use f+1 (or less) instead of 2f+1 because it is not needed for security? - return ocr3types.QuorumFPlusOne, nil -} - -func (p *Plugin) Outcome( - outctx ocr3types.OutcomeContext, query types.Query, aos []types.AttributedObservation, -) (ocr3types.Outcome, error) { - decodedObservations, err := decodeAttributedObservations(aos) - if err != nil { - return ocr3types.Outcome{}, err - - } - if len(decodedObservations) < p.reportingCfg.F { - return ocr3types.Outcome{}, fmt.Errorf("below F threshold") - } - - mergedCommitObservations, err := mergeCommitObservations(decodedObservations, p.cfg.FChain) - if err != nil { - return ocr3types.Outcome{}, err - } - - mergedMessageObservations, err := mergeMessageObservations(decodedObservations, p.cfg.FChain) - if err != nil { - return ocr3types.Outcome{}, err - } - - observation := cciptypes.NewExecutePluginObservation( - mergedCommitObservations, - mergedMessageObservations) - - // flatten commit reports and sort by timestamp. - var reports []cciptypes.ExecutePluginCommitDataWithMessages - for _, report := range observation.CommitReports { - reports = append(reports, report...) - } - sort.Slice(reports, func(i, j int) bool { - return reports[i].Timestamp.Before(reports[j].Timestamp) - }) - - // add messages to their reports. - for _, report := range reports { - report.Messages = nil - for i := report.SequenceNumberRange.Start(); i <= report.SequenceNumberRange.End(); i++ { - if msg, ok := observation.Messages[report.SourceChain][i]; ok { - report.Messages = append(report.Messages, msg) - } - } - } - - // TODO: select reports and messages for the final exec report. - // TODO: may only need the proofs for the final exec report rather than the report and the messages. - - return cciptypes.NewExecutePluginOutcome(reports).Encode() -} - -func (p *Plugin) Reports(seqNr uint64, outcome ocr3types.Outcome) ([]ocr3types.ReportWithInfo[[]byte], error) { - - panic("implement me") -} - -func (p *Plugin) ShouldAcceptAttestedReport( - ctx context.Context, u uint64, r ocr3types.ReportWithInfo[[]byte], -) (bool, error) { - panic("implement me") -} - -func (p *Plugin) ShouldTransmitAcceptedReport( - ctx context.Context, u uint64, r ocr3types.ReportWithInfo[[]byte], -) (bool, error) { - panic("implement me") -} - -func (p *Plugin) Close() error { - panic("implement me") -} - -// Interface compatibility checks. -var _ ocr3types.ReportingPlugin[[]byte] = &Plugin{} diff --git a/core/services/ocr3/plugins/ccip/execute/plugin_functions.go b/core/services/ocr3/plugins/ccip/execute/plugin_functions.go deleted file mode 100644 index 529938939a..0000000000 --- a/core/services/ocr3/plugins/ccip/execute/plugin_functions.go +++ /dev/null @@ -1,317 +0,0 @@ -package execute - -import ( - "errors" - "fmt" - "sort" - - mapset "github.com/deckarep/golang-set/v2" - "golang.org/x/crypto/sha3" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" - "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - "github.com/smartcontractkit/ccipocr3/execute/internal/validation" -) - -// validateObserverReadingEligibility checks if the observer is eligible to observe the messages it observed. -func validateObserverReadingEligibility( - observer commontypes.OracleID, - observerCfg map[commontypes.OracleID]cciptypes.ObserverInfo, - observedMsgs cciptypes.ExecutePluginMessageObservations, -) error { - observerInfo, exists := observerCfg[observer] - if !exists { - return fmt.Errorf("observer not found in config") - } - - observerReadChains := mapset.NewSet(observerInfo.Reads...) - - for chainSel, msgs := range observedMsgs { - if len(msgs) == 0 { - continue - } - - if !observerReadChains.Contains(chainSel) { - return fmt.Errorf("observer not allowed to read from chain %d", chainSel) - } - } - - return nil -} - -// validateObservedSequenceNumbers checks if the sequence numbers of the provided messages are unique for each chain -// and that they match the observed max sequence numbers. -func validateObservedSequenceNumbers( - observedData map[cciptypes.ChainSelector][]cciptypes.ExecutePluginCommitDataWithMessages, -) error { - for _, commitData := range observedData { - // observed commitData must not contain duplicates - - observedMerkleRoots := mapset.NewSet[string]() - observedRanges := make([]cciptypes.SeqNumRange, 0) - - for _, data := range commitData { - rootStr := data.MerkleRoot.String() - if observedMerkleRoots.Contains(rootStr) { - return fmt.Errorf("duplicate merkle root %s observed", rootStr) - } - observedMerkleRoots.Add(rootStr) - - for _, rng := range observedRanges { - if rng.Overlaps(data.SequenceNumberRange) { - return fmt.Errorf("sequence number range %v overlaps with %v", data.SequenceNumberRange, rng) - } - } - observedRanges = append(observedRanges, data.SequenceNumberRange) - - // Executed sequence numbers should belong in the observed range. - for _, seqNum := range data.ExecutedMessages { - if !data.SequenceNumberRange.Contains(seqNum) { - return fmt.Errorf("executed message %d not in observed range %v", seqNum, data.SequenceNumberRange) - } - } - } - } - - return nil -} - -var errOverlappingRanges = errors.New("overlapping sequence numbers in reports") - -// computeRanges takes a slice of reports and computes the smallest number of contiguous ranges -// that cover all the sequence numbers in the reports. -// Note: reports need all messages to create a proof even if some are already executed. -func computeRanges(reports []cciptypes.ExecutePluginCommitDataWithMessages) ([]cciptypes.SeqNumRange, error) { - var ranges []cciptypes.SeqNumRange - - if len(reports) == 0 { - return nil, nil - } - - var seqRange cciptypes.SeqNumRange - for i, report := range reports { - if i == 0 { - // initialize - seqRange = cciptypes.NewSeqNumRange(report.SequenceNumberRange.Start(), report.SequenceNumberRange.End()) - } else if seqRange.End()+1 == report.SequenceNumberRange.Start() { - // extend the contiguous range - seqRange.SetEnd(report.SequenceNumberRange.End()) - } else if report.SequenceNumberRange.Start() < seqRange.End() { - return nil, errOverlappingRanges - } else { - ranges = append(ranges, seqRange) - - // Reset the range. - seqRange = cciptypes.NewSeqNumRange(report.SequenceNumberRange.Start(), report.SequenceNumberRange.End()) - } - } - // add final range - ranges = append(ranges, seqRange) - - return ranges, nil -} - -func groupByChainSelector(reports []cciptypes.CommitPluginReportWithMeta) cciptypes.ExecutePluginCommitObservations { - commitReportCache := make(map[cciptypes.ChainSelector][]cciptypes.ExecutePluginCommitDataWithMessages) - for _, report := range reports { - for _, singleReport := range report.Report.MerkleRoots { - commitReportCache[singleReport.ChainSel] = append(commitReportCache[singleReport.ChainSel], - cciptypes.ExecutePluginCommitDataWithMessages{ - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SourceChain: singleReport.ChainSel, - Timestamp: report.Timestamp, - BlockNum: report.BlockNum, - MerkleRoot: singleReport.MerkleRoot, - SequenceNumberRange: singleReport.SeqNumsRange, - ExecutedMessages: nil, - }}) - } - } - return commitReportCache -} - -// filterOutExecutedMessages returns a new reports slice with fully executed messages removed. -// Unordered inputs are supported. -func filterOutExecutedMessages( - reports []cciptypes.ExecutePluginCommitDataWithMessages, executedMessages []cciptypes.SeqNumRange, -) ([]cciptypes.ExecutePluginCommitDataWithMessages, error) { - sort.Slice(reports, func(i, j int) bool { - return reports[i].SequenceNumberRange.Start() < reports[j].SequenceNumberRange.Start() - }) - - // If none are executed, return the (sorted) input. - if len(executedMessages) == 0 { - return reports, nil - } - - sort.Slice(executedMessages, func(i, j int) bool { - return executedMessages[i].Start() < executedMessages[j].Start() - }) - - // Make sure they do not overlap - previousMax := cciptypes.SeqNum(0) - for _, seqRange := range executedMessages { - if seqRange.Start() < previousMax { - return nil, errOverlappingRanges - } - previousMax = seqRange.End() - } - - var filtered []cciptypes.ExecutePluginCommitDataWithMessages - - reportIdx := 0 - for _, executed := range executedMessages { - for i := reportIdx; i < len(reports); i++ { - reportRange := reports[i].SequenceNumberRange - if executed.End() < reportRange.Start() { - // need to go to the next set of executed messages. - break - } - - if executed.End() < reportRange.Start() { - // add report that has non-executed messages. - reportIdx++ - filtered = append(filtered, reports[i]) - continue - } - - if reportRange.Start() >= executed.Start() && reportRange.End() <= executed.End() { - // skip fully executed report. - reportIdx++ - continue - } - - s := executed.Start() - if reportRange.Start() > executed.Start() { - s = reportRange.Start() - } - for ; s <= executed.End(); s++ { - // This range runs into the next report. - if s > reports[i].SequenceNumberRange.End() { - reportIdx++ - filtered = append(filtered, reports[i]) - break - } - reports[i].ExecutedMessages = append(reports[i].ExecutedMessages, s) - } - } - } - - // Add any remaining reports that were not fully executed. - for i := reportIdx; i < len(reports); i++ { - filtered = append(filtered, reports[i]) - } - - return filtered, nil -} - -type decodedAttributedObservation struct { - Observation cciptypes.ExecutePluginObservation - Observer commontypes.OracleID -} - -func decodeAttributedObservations(aos []types.AttributedObservation) ([]decodedAttributedObservation, error) { - decoded := make([]decodedAttributedObservation, len(aos)) - for i, ao := range aos { - observation, err := cciptypes.DecodeExecutePluginObservation(ao.Observation) - if err != nil { - return nil, err - } - decoded[i] = decodedAttributedObservation{ - Observation: observation, - Observer: ao.Observer, - } - } - return decoded, nil -} - -func mergeMessageObservations( - aos []decodedAttributedObservation, fChain map[cciptypes.ChainSelector]int, -) (cciptypes.ExecutePluginMessageObservations, error) { - // Create a validator for each chain - validators := make(map[cciptypes.ChainSelector]validation.MinObservationFilter[cciptypes.CCIPMsg]) - idFunc := func(data cciptypes.CCIPMsg) [32]byte { - return sha3.Sum256([]byte(fmt.Sprintf("%v", data))) - } - for selector, f := range fChain { - validators[selector] = validation.NewMinObservationValidator[cciptypes.CCIPMsg](f+1, idFunc) - } - - // Add messages to the validator for each chain selector. - for _, ao := range aos { - for selector, messages := range ao.Observation.Messages { - validator, ok := validators[selector] - if !ok { - return cciptypes.ExecutePluginMessageObservations{}, fmt.Errorf("no validator for chain %d", selector) - } - // Add reports - for _, msg := range messages { - if err := validator.Add(msg); err != nil { - return cciptypes.ExecutePluginMessageObservations{}, err - } - } - } - } - - results := make(cciptypes.ExecutePluginMessageObservations) - for selector, validator := range validators { - msgs, err := validator.GetValid() - if err != nil { - return cciptypes.ExecutePluginMessageObservations{}, err - } - if _, ok := results[selector]; !ok { - results[selector] = make(map[cciptypes.SeqNum]cciptypes.CCIPMsg) - } - for _, msg := range msgs { - results[selector][msg.SeqNum] = msg - } - } - - return results, nil -} - -// mergeCommitObservations merges all observations which reach the fChain threshold into a single result. -// Any observations, or subsets of observations, which do not reach the threshold are ignored. -func mergeCommitObservations( - aos []decodedAttributedObservation, fChain map[cciptypes.ChainSelector]int, -) (cciptypes.ExecutePluginCommitObservations, error) { - // Create a validator for each chain - validators := - make(map[cciptypes.ChainSelector]validation.MinObservationFilter[cciptypes.ExecutePluginCommitDataWithMessages]) - idFunc := func(data cciptypes.ExecutePluginCommitDataWithMessages) [32]byte { - return sha3.Sum256([]byte(fmt.Sprintf("%v", data))) - } - for selector, f := range fChain { - validators[selector] = - validation.NewMinObservationValidator[cciptypes.ExecutePluginCommitDataWithMessages](f+1, idFunc) - } - - // Add reports to the validator for each chain selector. - for _, ao := range aos { - for selector, commitReports := range ao.Observation.CommitReports { - validator, ok := validators[selector] - if !ok { - return cciptypes.ExecutePluginCommitObservations{}, fmt.Errorf("no validator for chain %d", selector) - } - // Add reports - for _, commitReport := range commitReports { - if err := validator.Add(commitReport); err != nil { - return cciptypes.ExecutePluginCommitObservations{}, err - } - } - } - } - - results := make(cciptypes.ExecutePluginCommitObservations) - for selector, validator := range validators { - var err error - results[selector], err = validator.GetValid() - if err != nil { - return cciptypes.ExecutePluginCommitObservations{}, err - } - } - - return results, nil -} diff --git a/core/services/ocr3/plugins/ccip/execute/plugin_functions_test.go b/core/services/ocr3/plugins/ccip/execute/plugin_functions_test.go deleted file mode 100644 index 01400bda5f..0000000000 --- a/core/services/ocr3/plugins/ccip/execute/plugin_functions_test.go +++ /dev/null @@ -1,815 +0,0 @@ -package execute - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/smartcontractkit/libocr/commontypes" - - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" -) - -func Test_validateObserverReadingEligibility(t *testing.T) { - tests := []struct { - name string - observer commontypes.OracleID - observerCfg map[commontypes.OracleID]cciptypes.ObserverInfo - observedMsgs cciptypes.ExecutePluginMessageObservations - expErr string - }{ - { - name: "ValidObserverAndMessages", - observer: commontypes.OracleID(1), - observerCfg: map[commontypes.OracleID]cciptypes.ObserverInfo{ - 1: {Reads: []cciptypes.ChainSelector{1, 2}}, - }, - observedMsgs: cciptypes.ExecutePluginMessageObservations{ - 1: {1: {}, 2: {}}, - 2: {}, - }, - }, - { - name: "ObserverNotFound", - observer: commontypes.OracleID(1), - observerCfg: map[commontypes.OracleID]cciptypes.ObserverInfo{ - 2: {Reads: []cciptypes.ChainSelector{1, 2}}, - }, - observedMsgs: cciptypes.ExecutePluginMessageObservations{ - 1: {1: {}, 2: {}}, - }, - expErr: "observer not found in config", - }, - { - name: "ObserverNotAllowedToReadChain", - observer: commontypes.OracleID(1), - observerCfg: map[commontypes.OracleID]cciptypes.ObserverInfo{ - 1: {Reads: []cciptypes.ChainSelector{1}}, - }, - observedMsgs: cciptypes.ExecutePluginMessageObservations{ - 2: {1: {}}, - }, - expErr: "observer not allowed to read from chain 2", - }, - { - name: "NoMessagesObserved", - observer: commontypes.OracleID(1), - observerCfg: map[commontypes.OracleID]cciptypes.ObserverInfo{ - 1: {Reads: []cciptypes.ChainSelector{1, 2}}, - }, - observedMsgs: cciptypes.ExecutePluginMessageObservations{}, - }, - { - name: "EmptyMessagesInChain", - observer: commontypes.OracleID(1), - observerCfg: map[commontypes.OracleID]cciptypes.ObserverInfo{ - 1: {Reads: []cciptypes.ChainSelector{1, 2}}, - }, - observedMsgs: cciptypes.ExecutePluginMessageObservations{ - 1: {}, - 2: {1: {}, 2: {}}, - }, - }, - { - name: "AllMessagesEmpty", - observer: commontypes.OracleID(1), - observerCfg: map[commontypes.OracleID]cciptypes.ObserverInfo{ - 1: {Reads: []cciptypes.ChainSelector{1, 2}}, - }, - observedMsgs: cciptypes.ExecutePluginMessageObservations{ - 1: {}, - 2: {}, - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - err := validateObserverReadingEligibility(tc.observer, tc.observerCfg, tc.observedMsgs) - if len(tc.expErr) != 0 { - assert.Error(t, err) - assert.ErrorContains(t, err, tc.expErr) - return - } - assert.NoError(t, err) - }) - } -} - -func Test_validateObservedSequenceNumbers(t *testing.T) { - testCases := []struct { - name string - observedData map[cciptypes.ChainSelector][]cciptypes.ExecutePluginCommitDataWithMessages - expErr bool - }{ - { - name: "ValidData", - observedData: map[cciptypes.ChainSelector][]cciptypes.ExecutePluginCommitDataWithMessages{ - 1: { - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - MerkleRoot: cciptypes.Bytes32{1}, - SequenceNumberRange: cciptypes.SeqNumRange{1, 10}, - ExecutedMessages: []cciptypes.SeqNum{1, 2, 3}, - }, - }, - }, - 2: { - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - MerkleRoot: cciptypes.Bytes32{2}, - SequenceNumberRange: cciptypes.SeqNumRange{11, 20}, - ExecutedMessages: []cciptypes.SeqNum{11, 12, 13}, - }, - }, - }, - }, - }, - { - name: "DuplicateMerkleRoot", - observedData: map[cciptypes.ChainSelector][]cciptypes.ExecutePluginCommitDataWithMessages{ - 1: { - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - MerkleRoot: cciptypes.Bytes32{1}, - SequenceNumberRange: cciptypes.SeqNumRange{1, 10}, - ExecutedMessages: []cciptypes.SeqNum{1, 2, 3}, - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - MerkleRoot: cciptypes.Bytes32{1}, - SequenceNumberRange: cciptypes.SeqNumRange{11, 20}, - ExecutedMessages: []cciptypes.SeqNum{11, 12, 13}, - }, - }, - }, - }, - expErr: true, - }, - { - name: "OverlappingSequenceNumberRange", - observedData: map[cciptypes.ChainSelector][]cciptypes.ExecutePluginCommitDataWithMessages{ - 1: { - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - MerkleRoot: cciptypes.Bytes32{1}, - SequenceNumberRange: cciptypes.SeqNumRange{1, 10}, - ExecutedMessages: []cciptypes.SeqNum{1, 2, 3}, - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - MerkleRoot: cciptypes.Bytes32{2}, - SequenceNumberRange: cciptypes.SeqNumRange{5, 15}, - ExecutedMessages: []cciptypes.SeqNum{6, 7, 8}, - }, - }, - }, - }, - expErr: true, - }, - { - name: "ExecutedMessageOutsideObservedRange", - observedData: map[cciptypes.ChainSelector][]cciptypes.ExecutePluginCommitDataWithMessages{ - 1: { - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - MerkleRoot: cciptypes.Bytes32{1}, - SequenceNumberRange: cciptypes.SeqNumRange{1, 10}, - ExecutedMessages: []cciptypes.SeqNum{1, 2, 11}, - }, - }, - }, - }, - expErr: true, - }, - { - name: "NoCommitData", - observedData: map[cciptypes.ChainSelector][]cciptypes.ExecutePluginCommitDataWithMessages{ - 1: {}, - }, - }, - { - name: "EmptyObservedData", - observedData: map[cciptypes.ChainSelector][]cciptypes.ExecutePluginCommitDataWithMessages{}, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := validateObservedSequenceNumbers(tc.observedData) - if tc.expErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - }) - } -} - -func Test_computeRanges(t *testing.T) { - type args struct { - reports []cciptypes.ExecutePluginCommitDataWithMessages - } - - tests := []struct { - name string - args args - want []cciptypes.SeqNumRange - err error - }{ - { - name: "empty", - args: args{reports: []cciptypes.ExecutePluginCommitDataWithMessages{}}, - want: nil, - }, - { - name: "overlapping ranges", - args: args{reports: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(15, 25), - }, - }, - }, - }, - err: errOverlappingRanges, - }, - { - name: "simple ranges collapsed", - args: args{reports: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(21, 40), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(41, 60), - }, - }, - }, - }, - want: []cciptypes.SeqNumRange{{10, 60}}, - }, - { - name: "non-contiguous ranges", - args: args{reports: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60)}, - }, - }, - }, - want: []cciptypes.SeqNumRange{{10, 20}, {30, 40}, {50, 60}}, - }, - { - name: "contiguous and non-contiguous ranges", - args: args{reports: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(21, 40), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60), - }, - }, - }, - }, - want: []cciptypes.SeqNumRange{{10, 40}, {50, 60}}, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - got, err := computeRanges(tt.args.reports) - if tt.err != nil { - assert.ErrorIs(t, err, tt.err) - } else { - assert.NoError(t, err) - assert.Equal(t, tt.want, got) - } - }) - } -} - -func Test_groupByChainSelector(t *testing.T) { - type args struct { - reports []cciptypes.CommitPluginReportWithMeta - } - tests := []struct { - name string - args args - want cciptypes.ExecutePluginCommitObservations - }{ - { - name: "empty", - args: args{reports: []cciptypes.CommitPluginReportWithMeta{}}, - want: cciptypes.ExecutePluginCommitObservations{}, - }, - { - name: "reports", - args: args{reports: []cciptypes.CommitPluginReportWithMeta{{ - Report: cciptypes.CommitPluginReport{ - MerkleRoots: []cciptypes.MerkleRootChain{ - {ChainSel: 1, SeqNumsRange: cciptypes.NewSeqNumRange(10, 20), MerkleRoot: cciptypes.Bytes32{1}}, - {ChainSel: 2, SeqNumsRange: cciptypes.NewSeqNumRange(30, 40), MerkleRoot: cciptypes.Bytes32{2}}, - }}}}}, - want: cciptypes.ExecutePluginCommitObservations{ - 1: { - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SourceChain: 1, - MerkleRoot: cciptypes.Bytes32{1}, - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - ExecutedMessages: nil, - }, - }, - }, - 2: { - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SourceChain: 2, - MerkleRoot: cciptypes.Bytes32{2}, - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40), - ExecutedMessages: nil, - }, - }, - }, - }, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - assert.Equalf(t, tt.want, groupByChainSelector(tt.args.reports), "groupByChainSelector(%v)", tt.args.reports) - }) - } -} - -func Test_filterOutFullyExecutedMessages(t *testing.T) { - type args struct { - reports []cciptypes.ExecutePluginCommitDataWithMessages - executedMessages []cciptypes.SeqNumRange - } - tests := []struct { - name string - args args - want []cciptypes.ExecutePluginCommitDataWithMessages - wantErr assert.ErrorAssertionFunc - }{ - { - name: "empty", - args: args{ - reports: nil, - executedMessages: nil, - }, - want: nil, - wantErr: assert.NoError, - }, - { - name: "empty2", - args: args{ - reports: []cciptypes.ExecutePluginCommitDataWithMessages{}, - executedMessages: nil, - }, - want: []cciptypes.ExecutePluginCommitDataWithMessages{}, - wantErr: assert.NoError, - }, - { - name: "no executed messages", - args: args{ - reports: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60), - }, - }, - }, - executedMessages: nil, - }, - want: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20)}}, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40)}}, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60)}}, - }, - wantErr: assert.NoError, - }, - { - name: "executed messages", - args: args{ - reports: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20)}}, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40)}}, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60)}}, - }, - executedMessages: []cciptypes.SeqNumRange{ - cciptypes.NewSeqNumRange(0, 100), - }, - }, - want: nil, - wantErr: assert.NoError, - }, - { - name: "2 partially executed", - args: args{ - reports: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20)}, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40)}, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60)}, - }, - }, - executedMessages: []cciptypes.SeqNumRange{ - cciptypes.NewSeqNumRange(15, 35), - }, - }, - want: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - ExecutedMessages: []cciptypes.SeqNum{15, 16, 17, 18, 19, 20}, - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40), - ExecutedMessages: []cciptypes.SeqNum{30, 31, 32, 33, 34, 35}, - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60), - }, - }, - }, - wantErr: assert.NoError, - }, - { - name: "2 partially executed 1 fully executed", - args: args{ - reports: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60), - }, - }, - }, - executedMessages: []cciptypes.SeqNumRange{ - cciptypes.NewSeqNumRange(15, 55), - }, - }, - want: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - ExecutedMessages: []cciptypes.SeqNum{15, 16, 17, 18, 19, 20}, - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60), - ExecutedMessages: []cciptypes.SeqNum{50, 51, 52, 53, 54, 55}, - }, - }, - }, - wantErr: assert.NoError, - }, - { - name: "first report executed", - args: args{ - reports: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60), - }, - }, - }, - executedMessages: []cciptypes.SeqNumRange{ - cciptypes.NewSeqNumRange(10, 20), - }, - }, - want: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60), - }, - }, - }, - wantErr: assert.NoError, - }, - { - name: "last report executed", - args: args{ - reports: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60), - }, - }, - }, - executedMessages: []cciptypes.SeqNumRange{ - cciptypes.NewSeqNumRange(50, 60), - }, - }, - want: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40), - }, - }, - }, - wantErr: assert.NoError, - }, - { - name: "sort-report", - args: args{ - reports: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - }, - }, - }, - executedMessages: nil, - }, - want: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60), - }, - }, - }, - wantErr: assert.NoError, - }, - { - name: "sort-executed", - args: args{ - reports: []cciptypes.ExecutePluginCommitDataWithMessages{ - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(10, 20), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(30, 40), - }, - }, - { - ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SequenceNumberRange: cciptypes.NewSeqNumRange(50, 60), - }, - }, - }, - executedMessages: []cciptypes.SeqNumRange{ - cciptypes.NewSeqNumRange(50, 60), - cciptypes.NewSeqNumRange(10, 20), - cciptypes.NewSeqNumRange(30, 40), - }, - }, - want: nil, - wantErr: assert.NoError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := filterOutExecutedMessages(tt.args.reports, tt.args.executedMessages) - if !tt.wantErr(t, err, fmt.Sprintf("filterOutExecutedMessages(%v, %v)", tt.args.reports, tt.args.executedMessages)) { - return - } - assert.Equalf(t, tt.want, got, "filterOutExecutedMessages(%v, %v)", tt.args.reports, tt.args.executedMessages) - }) - } -} - -func Test_decodeAttributedObservations(t *testing.T) { - mustEncode := func(obs cciptypes.ExecutePluginObservation) []byte { - enc, err := obs.Encode() - if err != nil { - t.Fatal("Unable to encode") - } - return enc - } - tests := []struct { - name string - args []types.AttributedObservation - want []decodedAttributedObservation - wantErr assert.ErrorAssertionFunc - }{ - // TODO: Add test cases. - { - name: "empty", - args: nil, - want: []decodedAttributedObservation{}, - wantErr: assert.NoError, - }, - { - name: "one observation", - args: []types.AttributedObservation{ - { - Observer: commontypes.OracleID(1), - Observation: mustEncode(cciptypes.ExecutePluginObservation{ - CommitReports: cciptypes.ExecutePluginCommitObservations{ - 1: {{ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{MerkleRoot: cciptypes.Bytes32{1}}}}, - }, - }), - }, - }, - want: []decodedAttributedObservation{ - { - Observer: commontypes.OracleID(1), - Observation: cciptypes.ExecutePluginObservation{ - CommitReports: cciptypes.ExecutePluginCommitObservations{ - 1: {{ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{MerkleRoot: cciptypes.Bytes32{1}}}}, - }, - }, - }, - }, - wantErr: assert.NoError, - }, - { - name: "multiple observations", - args: []types.AttributedObservation{ - { - Observer: commontypes.OracleID(1), - Observation: mustEncode(cciptypes.ExecutePluginObservation{ - CommitReports: cciptypes.ExecutePluginCommitObservations{ - 1: {{ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{MerkleRoot: cciptypes.Bytes32{1}}}}, - }, - }), - }, - { - Observer: commontypes.OracleID(2), - Observation: mustEncode(cciptypes.ExecutePluginObservation{ - CommitReports: cciptypes.ExecutePluginCommitObservations{ - 2: {{ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{MerkleRoot: cciptypes.Bytes32{2}}}}, - }, - }), - }, - }, - want: []decodedAttributedObservation{ - { - Observer: commontypes.OracleID(1), - Observation: cciptypes.ExecutePluginObservation{ - CommitReports: cciptypes.ExecutePluginCommitObservations{ - 1: {{ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{MerkleRoot: cciptypes.Bytes32{1}}}}, - }, - }, - }, - { - Observer: commontypes.OracleID(2), - Observation: cciptypes.ExecutePluginObservation{ - CommitReports: cciptypes.ExecutePluginCommitObservations{ - 2: {{ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{MerkleRoot: cciptypes.Bytes32{2}}}}, - }, - }, - }, - }, - wantErr: assert.NoError, - }, - { - name: "invalid observation", - args: []types.AttributedObservation{ - { - Observer: commontypes.OracleID(1), - Observation: []byte("invalid"), - }, - }, - want: nil, - wantErr: assert.Error, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := decodeAttributedObservations(tt.args) - if !tt.wantErr(t, err, fmt.Sprintf("decodeAttributedObservations(%v)", tt.args)) { - return - } - assert.Equalf(t, tt.want, got, "decodeAttributedObservations(%v)", tt.args) - }) - } -} diff --git a/core/services/ocr3/plugins/ccip/execute/plugin_test.go b/core/services/ocr3/plugins/ccip/execute/plugin_test.go deleted file mode 100644 index 64fd4c69ec..0000000000 --- a/core/services/ocr3/plugins/ccip/execute/plugin_test.go +++ /dev/null @@ -1,168 +0,0 @@ -package execute - -import ( - "context" - "encoding/json" - "math" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - - "github.com/smartcontractkit/ccipocr3/internal/mocks" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" -) - -func TestSomethingCool(t *testing.T) { - - foo := map[cciptypes.ChainSelector]int{ - cciptypes.ChainSelector(1): 1, - cciptypes.ChainSelector(math.MaxUint64): 1, - } - - js, _ := json.Marshal(foo) - t.Log(string(js)) - - b := []byte(`{"1":1,"18446744073709551615":1}`) - var bar map[cciptypes.ChainSelector]int - assert.NoError(t, json.Unmarshal(b, &bar)) - t.Log(bar) -} - -func Test_getPendingExecutedReports(t *testing.T) { - tests := []struct { - name string - reports []cciptypes.CommitPluginReportWithMeta - ranges map[cciptypes.ChainSelector][]cciptypes.SeqNumRange - want cciptypes.ExecutePluginCommitObservations - want1 time.Time - wantErr assert.ErrorAssertionFunc - }{ - // TODO: Add test cases. - { - name: "empty", - reports: nil, - ranges: nil, - want: cciptypes.ExecutePluginCommitObservations{}, - want1: time.Time{}, - wantErr: assert.NoError, - }, - { - name: "single non-executed report", - reports: []cciptypes.CommitPluginReportWithMeta{ - { - BlockNum: 999, - Timestamp: time.UnixMilli(10101010101), - Report: cciptypes.CommitPluginReport{ - MerkleRoots: []cciptypes.MerkleRootChain{ - { - ChainSel: 1, - SeqNumsRange: cciptypes.NewSeqNumRange(1, 10), - }, - }, - }, - }, - }, - ranges: map[cciptypes.ChainSelector][]cciptypes.SeqNumRange{ - 1: nil, - }, - want: cciptypes.ExecutePluginCommitObservations{ - 1: []cciptypes.ExecutePluginCommitDataWithMessages{ - {ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SourceChain: 1, - SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10), - ExecutedMessages: nil, - Timestamp: time.UnixMilli(10101010101), - BlockNum: 999, - }}, - }, - }, - want1: time.UnixMilli(10101010101), - wantErr: assert.NoError, - }, - { - name: "single half-executed report", - reports: []cciptypes.CommitPluginReportWithMeta{ - { - BlockNum: 999, - Timestamp: time.UnixMilli(10101010101), - Report: cciptypes.CommitPluginReport{ - MerkleRoots: []cciptypes.MerkleRootChain{ - { - ChainSel: 1, - SeqNumsRange: cciptypes.NewSeqNumRange(1, 10), - }, - }, - }, - }, - }, - ranges: map[cciptypes.ChainSelector][]cciptypes.SeqNumRange{ - 1: { - cciptypes.NewSeqNumRange(1, 3), - cciptypes.NewSeqNumRange(7, 8), - }, - }, - want: cciptypes.ExecutePluginCommitObservations{ - 1: []cciptypes.ExecutePluginCommitDataWithMessages{ - {ExecutePluginCommitData: cciptypes.ExecutePluginCommitData{ - SourceChain: 1, - SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10), - Timestamp: time.UnixMilli(10101010101), - BlockNum: 999, - ExecutedMessages: []cciptypes.SeqNum{1, 2, 3, 7, 8}, - }}, - }, - }, - want1: time.UnixMilli(10101010101), - wantErr: assert.NoError, - }, - { - name: "last timestamp", - reports: []cciptypes.CommitPluginReportWithMeta{ - { - BlockNum: 999, - Timestamp: time.UnixMilli(10101010101), - Report: cciptypes.CommitPluginReport{}, - }, - { - BlockNum: 999, - Timestamp: time.UnixMilli(9999999999999999), - Report: cciptypes.CommitPluginReport{}, - }, - }, - ranges: map[cciptypes.ChainSelector][]cciptypes.SeqNumRange{}, - want: cciptypes.ExecutePluginCommitObservations{}, - want1: time.UnixMilli(9999999999999999), - wantErr: assert.NoError, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - mockReader := mocks.NewCCIPReader() - mockReader.On( - "CommitReportsGTETimestamp", mock.Anything, mock.Anything, mock.Anything, mock.Anything, - ).Return(tt.reports, nil) - for k, v := range tt.ranges { - mockReader.On("ExecutedMessageRanges", mock.Anything, k, mock.Anything, mock.Anything).Return(v, nil) - } - - // CCIP Reader mocks: - // once: - // CommitReportsGTETimestamp(ctx, dest, ts, 1000) -> ([]cciptypes.CommitPluginReportWithMeta, error) - // for each chain selector: - // ExecutedMessageRanges(ctx, selector, dest, seqRange) -> ([]cciptypes.SeqNumRange, error) - - got, got1, err := getPendingExecutedReports(context.Background(), mockReader, 123, time.Now()) - if !tt.wantErr(t, err, "getPendingExecutedReports(...)") { - return - } - assert.Equalf(t, tt.want, got, "getPendingExecutedReports(...)") - assert.Equalf(t, tt.want1, got1, "getPendingExecutedReports(...)") - }) - } -} diff --git a/core/services/ocr3/plugins/ccip/go.mod b/core/services/ocr3/plugins/ccip/go.mod deleted file mode 100644 index b497e58564..0000000000 --- a/core/services/ocr3/plugins/ccip/go.mod +++ /dev/null @@ -1,50 +0,0 @@ -module github.com/smartcontractkit/ccipocr3 - -go 1.21.7 - -require ( - github.com/deckarep/golang-set/v2 v2.6.0 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625074419-c278d083facf - github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c - github.com/stretchr/testify v1.9.0 - golang.org/x/crypto v0.24.0 - golang.org/x/sync v0.7.0 - google.golang.org/grpc v1.64.0 -) - -require ( - github.com/bahlo/generic-list-go v0.2.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/buger/jsonparser v1.1.1 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/invopop/jsonschema v0.12.0 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mr-tron/base58 v1.2.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.54.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect - github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect - github.com/shopspring/decimal v1.4.0 // indirect - github.com/stretchr/objx v0.5.2 // indirect - github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect - gonum.org/v1/gonum v0.15.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect - google.golang.org/protobuf v1.34.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) - -// replicating the replace directive on cosmos SDK -replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 diff --git a/core/services/ocr3/plugins/ccip/go.sum b/core/services/ocr3/plugins/ccip/go.sum deleted file mode 100644 index c71295378b..0000000000 --- a/core/services/ocr3/plugins/ccip/go.sum +++ /dev/null @@ -1,98 +0,0 @@ -github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= -github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= -github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= -github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= -github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -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.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8= -github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= -github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= -github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625074419-c278d083facf h1:d9AS/K8RSVG64USb20N/U7RaPOsYPcmuLGJq7iE+caM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240625074419-c278d083facf/go.mod h1:L32xvCpk84Nglit64OhySPMP1tM3TTBK7Tw0qZl7Sd4= -github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c h1:lIyMbTaF2H0Q71vkwZHX/Ew4KF2BxiKhqEXwF8rn+KI= -github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c/go.mod h1:fb1ZDVXACvu4frX3APHZaEBp0xi1DIm34DcA0CwTsZM= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= -github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= -gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -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/core/services/ocr3/plugins/ccip/internal/libs/slicelib/bigint.go b/core/services/ocr3/plugins/ccip/internal/libs/slicelib/bigint.go deleted file mode 100644 index fd24676b92..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/libs/slicelib/bigint.go +++ /dev/null @@ -1,26 +0,0 @@ -package slicelib - -import ( - "sort" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" -) - -// BigIntSortedMiddle returns the middle number after sorting the provided numbers. -// nil is returned if the provided slice is empty. -// If length of the provided slice is even, the right-hand-side value of the middle 2 numbers is returned. -// The objective of this function is to always pick within the range of values reported by honest nodes -// when we have 2f+1 values. -func BigIntSortedMiddle(vals []cciptypes.BigInt) cciptypes.BigInt { - if len(vals) == 0 { - return cciptypes.BigInt{} - } - - valsCopy := make([]cciptypes.BigInt, len(vals)) - copy(valsCopy[:], vals[:]) - - sort.Slice(valsCopy, func(i, j int) bool { - return (valsCopy[i].Int).Cmp(valsCopy[j].Int) < 0 - }) - return valsCopy[len(valsCopy)/2] -} diff --git a/core/services/ocr3/plugins/ccip/internal/libs/slicelib/bigint_test.go b/core/services/ocr3/plugins/ccip/internal/libs/slicelib/bigint_test.go deleted file mode 100644 index 29ce5f2068..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/libs/slicelib/bigint_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package slicelib - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" -) - -func TestBigIntSortedMiddle(t *testing.T) { - tests := []struct { - name string - vals []cciptypes.BigInt - want cciptypes.BigInt - }{ - { - name: "base case", - vals: []cciptypes.BigInt{ - cciptypes.NewBigIntFromInt64(1), - cciptypes.NewBigIntFromInt64(2), - cciptypes.NewBigIntFromInt64(4), - cciptypes.NewBigIntFromInt64(5), - }, - want: cciptypes.NewBigIntFromInt64(4), - }, - { - name: "not sorted", - vals: []cciptypes.BigInt{ - cciptypes.NewBigIntFromInt64(100), - cciptypes.NewBigIntFromInt64(50), - cciptypes.NewBigIntFromInt64(30), - cciptypes.NewBigIntFromInt64(110), - }, - want: cciptypes.NewBigIntFromInt64(100), - }, - { - name: "empty slice", - vals: []cciptypes.BigInt{}, - want: cciptypes.BigInt{}, - }, - { - name: "one item", - vals: []cciptypes.BigInt{ - cciptypes.NewBigIntFromInt64(123), - }, - want: cciptypes.NewBigIntFromInt64(123), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equalf(t, tt.want, BigIntSortedMiddle(tt.vals), "BigIntSortedMiddle(%v)", tt.vals) - }) - } -} diff --git a/core/services/ocr3/plugins/ccip/internal/libs/slicelib/generic.go b/core/services/ocr3/plugins/ccip/internal/libs/slicelib/generic.go deleted file mode 100644 index 3080bd89e5..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/libs/slicelib/generic.go +++ /dev/null @@ -1,43 +0,0 @@ -package slicelib - -// GroupBy groups a slice based on a specific item property. The returned groups slice is deterministic. -func GroupBy[T any, K comparable](items []T, prop func(T) K) ([]K, map[K][]T) { - groups := make([]K, 0) - grouped := make(map[K][]T) - for _, item := range items { - k := prop(item) - if _, exists := grouped[k]; !exists { - groups = append(groups, k) - } - grouped[k] = append(grouped[k], item) - } - return groups, grouped -} - -// CountUnique counts the unique items of the provided slice. -func CountUnique[T comparable](items []T) int { - m := make(map[T]struct{}) - for _, item := range items { - m[item] = struct{}{} - } - return len(m) -} - -// Flatten flattens a slice of slices into a single slice. -func Flatten[T any](slices [][]T) []T { - res := make([]T, 0) - for _, s := range slices { - res = append(res, s...) - } - return res -} - -func Filter[T any](slice []T, valid func(T) bool) []T { - res := make([]T, 0, len(slice)) - for _, item := range slice { - if valid(item) { - res = append(res, item) - } - } - return res -} diff --git a/core/services/ocr3/plugins/ccip/internal/libs/slicelib/generic_test.go b/core/services/ocr3/plugins/ccip/internal/libs/slicelib/generic_test.go deleted file mode 100644 index 906af817cc..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/libs/slicelib/generic_test.go +++ /dev/null @@ -1,179 +0,0 @@ -package slicelib - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestGroupBy(t *testing.T) { - type person struct { - id string - name string - age int - } - - testCases := []struct { - name string - items []person - expGroupNames []string - expGroups map[string][]person - }{ - { - name: "empty slice", - items: []person{}, - expGroupNames: []string{}, - expGroups: map[string][]person{}, - }, - { - name: "no duplicate", - items: []person{ - {id: "2", name: "Bob", age: 25}, - {id: "1", name: "Alice", age: 23}, - {id: "3", name: "Charlie", age: 22}, - {id: "4", name: "Dim", age: 13}, - }, - expGroupNames: []string{"2", "1", "3", "4"}, // should be deterministic - expGroups: map[string][]person{ - "1": {{id: "1", name: "Alice", age: 23}}, - "2": {{id: "2", name: "Bob", age: 25}}, - "3": {{id: "3", name: "Charlie", age: 22}}, - "4": {{id: "4", name: "Dim", age: 13}}, - }, - }, - { - name: "with duplicate", - items: []person{ - {id: "1", name: "Alice", age: 23}, - {id: "1", name: "Bob", age: 25}, - {id: "3", name: "Charlie", age: 22}, - }, - expGroupNames: []string{"1", "3"}, - expGroups: map[string][]person{ - "1": {{id: "1", name: "Alice", age: 23}, {id: "1", name: "Bob", age: 25}}, - "3": {{id: "3", name: "Charlie", age: 22}}, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - keys, groups := GroupBy(tc.items, func(p person) string { return p.id }) - assert.Equal(t, tc.expGroupNames, keys) - assert.Equal(t, len(tc.expGroups), len(groups)) - for _, k := range keys { - assert.Equal(t, tc.expGroups[k], groups[k]) - } - }) - } -} - -func TestCountUnique(t *testing.T) { - testCases := []struct { - name string - items []string - expCount int - }{ - { - name: "empty slice", - items: []string{}, - expCount: 0, - }, - { - name: "no duplicate", - items: []string{"a", "b", "c"}, - expCount: 3, - }, - { - name: "with duplicate", - items: []string{"a", "a", "b", "c", "b"}, - expCount: 3, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - assert.Equal(t, tc.expCount, CountUnique(tc.items)) - }) - } -} - -func TestFlatten(t *testing.T) { - testCases := []struct { - name string - slices [][]int - expFlatten []int - }{ - { - name: "empty slice", - slices: [][]int{}, - expFlatten: []int{}, - }, - { - name: "no duplicate", - slices: [][]int{{1, 2}, {3, 4}, {5, 6}}, - expFlatten: []int{1, 2, 3, 4, 5, 6}, - }, - { - name: "with duplicate", - slices: [][]int{{1, 2}, {1, 2}, {3, 4}, {5, 6}}, - expFlatten: []int{1, 2, 1, 2, 3, 4, 5, 6}, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - assert.Equal(t, tc.expFlatten, Flatten(tc.slices)) - }) - } -} - -func TestFilter(t *testing.T) { - type person struct { - id string - name string - age int - } - - testCases := []struct { - name string - items []person - valid func(person) bool - expResults []person - }{ - { - name: "empty slice", - items: []person{}, - valid: func(p person) bool { return p.age > 20 }, - expResults: []person{}, - }, - { - name: "no valid item", - items: []person{ - {id: "1", name: "Alice", age: 18}, - {id: "2", name: "Bob", age: 20}, - {id: "3", name: "Charlie", age: 19}, - }, - valid: func(p person) bool { return p.age > 20 }, - expResults: []person{}, - }, - { - name: "with valid item", - items: []person{ - {id: "1", name: "Alice", age: 18}, - {id: "2", name: "Bob", age: 25}, - {id: "3", name: "Charlie", age: 19}, - }, - valid: func(p person) bool { return p.age > 20 }, - expResults: []person{ - {id: "2", name: "Bob", age: 25}, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - assert.Equal(t, tc.expResults, Filter(tc.items, tc.valid)) - }) - } -} diff --git a/core/services/ocr3/plugins/ccip/internal/libs/testhelpers/ocr3runner.go b/core/services/ocr3/plugins/ccip/internal/libs/testhelpers/ocr3runner.go deleted file mode 100644 index 4a1aec2d48..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/libs/testhelpers/ocr3runner.go +++ /dev/null @@ -1,201 +0,0 @@ -package testhelpers - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "errors" - "fmt" - "math/rand" - - "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - "github.com/smartcontractkit/ccipocr3/internal/libs/slicelib" -) - -var ( - ErrQuery = errors.New("error in query phase") - ErrObservation = errors.New("error in observation phase") - ErrValidateObservation = errors.New("error in validate observation phase") - ErrOutcome = errors.New("error in outcome phase") - ErrEmptyOutcome = errors.New("outcome is empty") - ErrReports = errors.New("error in reports phase") - ErrShouldAcceptAttestedReport = errors.New("error in should accept attested report phase") - ErrShouldTransmitAcceptedReport = errors.New("error in should transmit accepted report phase") -) - -// OCR3Runner is a simple runner for OCR3. -// -// TODO: move to a shared repository. -type OCR3Runner[RI any] struct { - nodes []ocr3types.ReportingPlugin[RI] - nodeIDs []commontypes.OracleID - round int - previousOutcome ocr3types.Outcome -} - -func NewOCR3Runner[RI any]( - nodes []ocr3types.ReportingPlugin[RI], nodeIDs []commontypes.OracleID, initialOutcome ocr3types.Outcome, -) *OCR3Runner[RI] { - return &OCR3Runner[RI]{ - nodes: nodes, - nodeIDs: nodeIDs, - round: 0, - previousOutcome: initialOutcome, - } -} - -// RunRound will run some basic steps of an OCR3 flow. -// This is not a full OCR3 round but only the bare minimum. -func (r *OCR3Runner[RI]) RunRound(ctx context.Context) (result RoundResult[RI], err error) { - r.round++ - seqNr := uint64(r.round) - - leaderNode := r.selectLeader() - - outcomeCtx := ocr3types.OutcomeContext{SeqNr: seqNr, PreviousOutcome: r.previousOutcome} - - q, err := leaderNode.Query(ctx, outcomeCtx) - if err != nil { - return RoundResult[RI]{}, fmt.Errorf("%w: %w", err, ErrQuery) - } - - attributedObservations := make([]types.AttributedObservation, len(r.nodes)) - for i, n := range r.nodes { - obs, err2 := n.Observation(ctx, outcomeCtx, q) - if err2 != nil { - return RoundResult[RI]{}, fmt.Errorf("%w: %w", err2, ErrObservation) - } - - attrObs := types.AttributedObservation{Observation: obs, Observer: r.nodeIDs[i]} - err = leaderNode.ValidateObservation(outcomeCtx, q, attrObs) - if err != nil { - return RoundResult[RI]{}, fmt.Errorf("%w: %w", err, ErrValidateObservation) - } - - attributedObservations[i] = attrObs - } - - outcomes := make([]ocr3types.Outcome, len(r.nodes)) - for i, n := range r.nodes { - outcome, err2 := n.Outcome(outcomeCtx, q, attributedObservations) - if err2 != nil { - return RoundResult[RI]{}, fmt.Errorf("%w: %w", err2, ErrOutcome) - } - if len(outcome) == 0 { - return RoundResult[RI]{}, ErrEmptyOutcome - } - - outcomes[i] = outcome - } - - // check that all the outcomes are the same. - if countUniqueOutcomes(outcomes) > 1 { - return RoundResult[RI]{}, fmt.Errorf("outcomes are not equal") - } - - r.previousOutcome = outcomes[0] - - allReports := make([][]ocr3types.ReportWithInfo[RI], len(r.nodes)) - for i, n := range r.nodes { - reportsWithInfo, err2 := n.Reports(seqNr, outcomes[0]) - if err2 != nil { - return RoundResult[RI]{}, fmt.Errorf("%w: %w", err2, ErrReports) - } - - allReports[i] = reportsWithInfo - } - - // check that all the reports are the same. - if countUniqueReports(slicelib.Flatten(allReports)) > 1 { - return RoundResult[RI]{}, fmt.Errorf("reports are not equal") - } - - transmitted := make([]ocr3types.ReportWithInfo[RI], 0) - notAccepted := make([]ocr3types.ReportWithInfo[RI], 0) - notTransmitted := make([]ocr3types.ReportWithInfo[RI], 0) - - for _, report := range allReports[0] { - allShouldAccept := make([]bool, len(r.nodes)) - for i, n := range r.nodes { - shouldAccept, err2 := n.ShouldAcceptAttestedReport(ctx, seqNr, report) - if err2 != nil { - return RoundResult[RI]{}, fmt.Errorf("%w: %w", err2, ErrShouldAcceptAttestedReport) - } - - allShouldAccept[i] = shouldAccept - } - if slicelib.CountUnique(allShouldAccept) > 1 { - return RoundResult[RI]{}, fmt.Errorf("should accept attested report from all oracles is not equal") - } - - if !allShouldAccept[0] { - notAccepted = append(notAccepted, report) - continue - } - - allShouldTransmit := make([]bool, len(r.nodes)) - for i, n := range r.nodes { - shouldTransmit, err2 := n.ShouldTransmitAcceptedReport(ctx, seqNr, report) - if err2 != nil { - return RoundResult[RI]{}, fmt.Errorf("%w: %w", err2, ErrShouldTransmitAcceptedReport) - } - - allShouldTransmit[i] = shouldTransmit - } - if slicelib.CountUnique(allShouldTransmit) > 1 { - return RoundResult[RI]{}, fmt.Errorf("should transmit accepted report from all oracles is not equal") - } - - if !allShouldTransmit[0] { - notTransmitted = append(notTransmitted, report) - continue - } - - transmitted = append(transmitted, report) - } - - return RoundResult[RI]{ - Transmitted: transmitted, - NotAccepted: notAccepted, - NotTransmitted: notTransmitted, - Outcome: outcomes[0], - }, nil -} - -func (r *OCR3Runner[RI]) selectLeader() ocr3types.ReportingPlugin[RI] { - numNodes := len(r.nodes) - if numNodes == 0 { - return nil - } - return r.nodes[rand.Intn(numNodes)] -} - -type RoundResult[RI any] struct { - Transmitted []ocr3types.ReportWithInfo[RI] - NotAccepted []ocr3types.ReportWithInfo[RI] - NotTransmitted []ocr3types.ReportWithInfo[RI] - Outcome []byte -} - -func countUniqueOutcomes(outcomes []ocr3types.Outcome) int { - flattenedHashes := make([]string, 0, len(outcomes)) - for _, o := range outcomes { - h := sha256.New() - h.Write(o) - flattenedHashes = append(flattenedHashes, hex.EncodeToString(h.Sum(nil))) - } - return slicelib.CountUnique(flattenedHashes) -} - -func countUniqueReports[RI any](reports []ocr3types.ReportWithInfo[RI]) int { - flattenedHashes := make([]string, 0, len(reports)) - for _, report := range reports { - h := sha256.New() - h.Write(report.Report) - flattenedHashes = append(flattenedHashes, hex.EncodeToString(h.Sum(nil))) - } - return slicelib.CountUnique(flattenedHashes) -} diff --git a/core/services/ocr3/plugins/ccip/internal/mocks/ccipreader.go b/core/services/ocr3/plugins/ccip/internal/mocks/ccipreader.go deleted file mode 100644 index 8da37ec710..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/mocks/ccipreader.go +++ /dev/null @@ -1,60 +0,0 @@ -package mocks - -import ( - "context" - "time" - - "github.com/stretchr/testify/mock" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" -) - -type CCIPReader struct { - *mock.Mock -} - -func NewCCIPReader() *CCIPReader { - return &CCIPReader{ - Mock: &mock.Mock{}, - } -} - -func (r CCIPReader) CommitReportsGTETimestamp( - ctx context.Context, dest cciptypes.ChainSelector, ts time.Time, limit int, -) ([]cciptypes.CommitPluginReportWithMeta, error) { - args := r.Called(ctx, dest, ts, limit) - return args.Get(0).([]cciptypes.CommitPluginReportWithMeta), args.Error(1) -} - -func (r CCIPReader) ExecutedMessageRanges( - ctx context.Context, source, dest cciptypes.ChainSelector, seqNumRange cciptypes.SeqNumRange, -) ([]cciptypes.SeqNumRange, error) { - args := r.Called(ctx, source, dest, seqNumRange) - return args.Get(0).([]cciptypes.SeqNumRange), args.Error(1) -} - -func (r CCIPReader) MsgsBetweenSeqNums( - ctx context.Context, chain cciptypes.ChainSelector, seqNumRange cciptypes.SeqNumRange, -) ([]cciptypes.CCIPMsg, error) { - args := r.Called(ctx, chain, seqNumRange) - return args.Get(0).([]cciptypes.CCIPMsg), args.Error(1) -} - -func (r CCIPReader) NextSeqNum(ctx context.Context, chains []cciptypes.ChainSelector) ( - seqNum []cciptypes.SeqNum, err error) { - args := r.Called(ctx, chains) - return args.Get(0).([]cciptypes.SeqNum), args.Error(1) -} - -func (r CCIPReader) GasPrices(ctx context.Context, chains []cciptypes.ChainSelector) ([]cciptypes.BigInt, error) { - args := r.Called(ctx, chains) - return args.Get(0).([]cciptypes.BigInt), args.Error(1) -} - -func (r CCIPReader) Close(ctx context.Context) error { - args := r.Called(ctx) - return args.Error(0) -} - -// Interface compatibility check. -var _ cciptypes.CCIPReader = (*CCIPReader)(nil) diff --git a/core/services/ocr3/plugins/ccip/internal/mocks/contract_reader.go b/core/services/ocr3/plugins/ccip/internal/mocks/contract_reader.go deleted file mode 100644 index 25185805b9..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/mocks/contract_reader.go +++ /dev/null @@ -1,67 +0,0 @@ -package mocks - -import ( - "context" - - "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink-common/pkg/types/query" - "github.com/stretchr/testify/mock" -) - -type ContractReaderMock struct { - *mock.Mock -} - -func NewContractReaderMock() *ContractReaderMock { - return &ContractReaderMock{ - Mock: &mock.Mock{}, - } -} - -// GetLatestValue Returns given configs at initialization -func (cr *ContractReaderMock) GetLatestValue( - ctx context.Context, contractName, method string, params, returnVal any, -) error { - args := cr.Called(ctx, contractName, method, params, returnVal) - return args.Error(0) -} - -func (cr *ContractReaderMock) Bind(ctx context.Context, bindings []types.BoundContract) error { - args := cr.Called(ctx, bindings) - return args.Error(0) -} - -func (cr *ContractReaderMock) QueryKey( - ctx context.Context, - contractName string, - filter query.KeyFilter, - limitAndSort query.LimitAndSort, - sequenceDataType any, -) ([]types.Sequence, error) { - args := cr.Called(ctx, contractName, filter, limitAndSort, sequenceDataType) - return args.Get(0).([]types.Sequence), args.Error(1) -} - -func (cr *ContractReaderMock) Start(ctx context.Context) error { - args := cr.Called(ctx) - return args.Error(0) -} - -func (cr *ContractReaderMock) Close() error { - args := cr.Called() - return args.Error(0) -} - -func (cr *ContractReaderMock) Ready() error { - args := cr.Called() - return args.Error(0) -} - -func (cr *ContractReaderMock) HealthReport() map[string]error { - args := cr.Called() - return args.Get(0).(map[string]error) -} - -func (cr *ContractReaderMock) Name() string { - return "ContractReaderMock" -} diff --git a/core/services/ocr3/plugins/ccip/internal/mocks/messagehasher.go b/core/services/ocr3/plugins/ccip/internal/mocks/messagehasher.go deleted file mode 100644 index 5a85e25f4c..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/mocks/messagehasher.go +++ /dev/null @@ -1,20 +0,0 @@ -package mocks - -import ( - "context" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" -) - -type MessageHasher struct{} - -func NewMessageHasher() *MessageHasher { - return &MessageHasher{} -} - -func (m *MessageHasher) Hash(ctx context.Context, msg cciptypes.CCIPMsg) (cciptypes.Bytes32, error) { - // simply return the msg id as bytes32 - var b32 [32]byte - copy(b32[:], msg.ID) - return b32, nil -} diff --git a/core/services/ocr3/plugins/ccip/internal/mocks/pricesreader.go b/core/services/ocr3/plugins/ccip/internal/mocks/pricesreader.go deleted file mode 100644 index 9d1f4c5f75..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/mocks/pricesreader.go +++ /dev/null @@ -1,29 +0,0 @@ -package mocks - -import ( - "context" - "math/big" - - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/stretchr/testify/mock" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" -) - -type TokenPricesReader struct { - *mock.Mock -} - -func NewTokenPricesReader() *TokenPricesReader { - return &TokenPricesReader{ - Mock: &mock.Mock{}, - } -} - -func (t TokenPricesReader) GetTokenPricesUSD(ctx context.Context, tokens []ocrtypes.Account) ([]*big.Int, error) { - args := t.Called(ctx, tokens) - return args.Get(0).([]*big.Int), args.Error(1) -} - -// Interface compatibility check. -var _ cciptypes.TokenPricesReader = (*TokenPricesReader)(nil) diff --git a/core/services/ocr3/plugins/ccip/internal/mocks/reportcodec.go b/core/services/ocr3/plugins/ccip/internal/mocks/reportcodec.go deleted file mode 100644 index b29c93603c..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/mocks/reportcodec.go +++ /dev/null @@ -1,24 +0,0 @@ -package mocks - -import ( - "context" - "encoding/json" - - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" -) - -type CommitPluginJSONReportCodec struct{} - -func NewCommitPluginJSONReportCodec() *CommitPluginJSONReportCodec { - return &CommitPluginJSONReportCodec{} -} - -func (c CommitPluginJSONReportCodec) Encode(ctx context.Context, report cciptypes.CommitPluginReport) ([]byte, error) { - return json.Marshal(report) -} - -func (c CommitPluginJSONReportCodec) Decode(ctx context.Context, bytes []byte) (cciptypes.CommitPluginReport, error) { - report := cciptypes.CommitPluginReport{} - err := json.Unmarshal(bytes, &report) - return report, err -} diff --git a/core/services/ocr3/plugins/ccip/internal/reader/ccip.go b/core/services/ocr3/plugins/ccip/internal/reader/ccip.go deleted file mode 100644 index cdf8d046bc..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/reader/ccip.go +++ /dev/null @@ -1,194 +0,0 @@ -package reader - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/smartcontractkit/chainlink-common/pkg/types" - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" - "github.com/smartcontractkit/chainlink-common/pkg/types/query" - "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" - "golang.org/x/sync/errgroup" -) - -var ( - ErrContractReaderNotFound = errors.New("contract reader not found") - ErrContractWriterNotFound = errors.New("contract writer not found") -) - -// TODO: unit test the implementation when the actual contract reader and writer interfaces are finalized and mocks -// can be generated. -type CCIPChainReader struct { - contractReaders map[cciptypes.ChainSelector]types.ContractReader - contractWriters map[cciptypes.ChainSelector]types.ChainWriter - destChain cciptypes.ChainSelector -} - -func NewCCIPChainReader( - contractReaders map[cciptypes.ChainSelector]types.ContractReader, - contractWriters map[cciptypes.ChainSelector]types.ChainWriter, - destChain cciptypes.ChainSelector, -) *CCIPChainReader { - return &CCIPChainReader{ - contractReaders: contractReaders, - contractWriters: contractWriters, - destChain: destChain, - } -} - -func (r *CCIPChainReader) CommitReportsGTETimestamp( - ctx context.Context, dest cciptypes.ChainSelector, ts time.Time, limit int, -) ([]cciptypes.CommitPluginReportWithMeta, error) { - if err := r.validateReaderExistence(dest); err != nil { - return nil, err - } - panic("implement me") -} - -func (r *CCIPChainReader) ExecutedMessageRanges( - ctx context.Context, source, dest cciptypes.ChainSelector, seqNumRange cciptypes.SeqNumRange, -) ([]cciptypes.SeqNumRange, error) { - if err := r.validateReaderExistence(source, dest); err != nil { - return nil, err - } - panic("implement me") -} - -func (r *CCIPChainReader) MsgsBetweenSeqNums( - ctx context.Context, chain cciptypes.ChainSelector, seqNumRange cciptypes.SeqNumRange, -) ([]cciptypes.CCIPMsg, error) { - if err := r.validateReaderExistence(chain); err != nil { - return nil, err - } - - const ( - contractName = "OnRamp" - eventName = "CCIPSendRequested" - eventAttributeName = "SequenceNumber" - ) - - seq, err := r.contractReaders[chain].QueryKey( - ctx, - contractName, - query.KeyFilter{ - Key: eventName, - Expressions: []query.Expression{ - { - Primitive: &primitives.Comparator{ - Name: eventAttributeName, - ValueComparators: []primitives.ValueComparator{ - { - Value: seqNumRange.Start().String(), - Operator: primitives.Gte, - }, - { - Value: seqNumRange.End().String(), - Operator: primitives.Lte, - }, - }, - }, - BoolExpression: query.BoolExpression{}, - }, - }, - }, - query.LimitAndSort{ - SortBy: []query.SortBy{ - query.NewSortByTimestamp(query.Asc), - }, - Limit: query.Limit{ - Count: uint64(seqNumRange.End() - seqNumRange.Start() + 1), - }, - }, - &cciptypes.CCIPMsg{}, - ) - if err != nil { - return nil, fmt.Errorf("failed to query onRamp: %w", err) - } - - msgs := make([]cciptypes.CCIPMsg, 0) - for _, item := range seq { - msg, ok := item.Data.(cciptypes.CCIPMsg) - if !ok { - return nil, fmt.Errorf("failed to cast %v to CCIPMsg", item.Data) - } - msgs = append(msgs, msg) - } - - return msgs, nil -} - -func (r *CCIPChainReader) NextSeqNum( - ctx context.Context, chains []cciptypes.ChainSelector, -) ([]cciptypes.SeqNum, error) { - if err := r.validateReaderExistence(r.destChain); err != nil { - return nil, err - } - - const ( - contractName = "OffRamp" - funcName = "getExpectedNextSequenceNumbers" - ) - - seqNums := make([]cciptypes.SeqNum, 0) - err := r.contractReaders[r.destChain].GetLatestValue( - ctx, - contractName, - funcName, - map[string]any{ - "chains": chains, - }, - &seqNums, - ) - return seqNums, err -} - -func (r *CCIPChainReader) GasPrices(ctx context.Context, chains []cciptypes.ChainSelector) ([]cciptypes.BigInt, error) { - if err := r.validateWriterExistence(chains...); err != nil { - return nil, err - } - - eg := new(errgroup.Group) - gasPrices := make([]cciptypes.BigInt, len(chains)) - for i, chain := range chains { - i, chain := i, chain - eg.Go(func() error { - gasPrice, err := r.contractWriters[chain].GetFeeComponents(ctx) - if err != nil { - return fmt.Errorf("failed to get gas price: %w", err) - } - gasPrices[i] = cciptypes.NewBigInt(gasPrice.ExecutionFee) - return nil - }) - } - - if err := eg.Wait(); err != nil { - return nil, err - } - return gasPrices, nil -} - -func (r *CCIPChainReader) Close(ctx context.Context) error { - return nil -} - -func (r *CCIPChainReader) validateReaderExistence(chains ...cciptypes.ChainSelector) error { - for _, ch := range chains { - _, exists := r.contractReaders[ch] - if !exists { - return fmt.Errorf("chain %d: %w", ch, ErrContractReaderNotFound) - } - } - return nil -} - -func (r *CCIPChainReader) validateWriterExistence(chains ...cciptypes.ChainSelector) error { - for _, ch := range chains { - _, exists := r.contractWriters[ch] - if !exists { - return fmt.Errorf("chain %d: %w", ch, ErrContractWriterNotFound) - } - } - return nil -} diff --git a/core/services/ocr3/plugins/ccip/internal/reader/home_chain.go b/core/services/ocr3/plugins/ccip/internal/reader/home_chain.go deleted file mode 100644 index ff2409250a..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/reader/home_chain.go +++ /dev/null @@ -1,326 +0,0 @@ -package reader - -import ( - "context" - "fmt" - "sync" - "time" - - mapset "github.com/deckarep/golang-set/v2" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink-common/pkg/types" - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" - libocrtypes "github.com/smartcontractkit/libocr/ragep2p/types" -) - -type HomeChain interface { - GetChainConfig(chainSelector cciptypes.ChainSelector) (ChainConfig, error) - GetAllChainConfigs() (map[cciptypes.ChainSelector]ChainConfig, error) - // GetSupportedChainsForPeer Gets all chain selectors that the peerID can read/write from/to - GetSupportedChainsForPeer(id libocrtypes.PeerID) (mapset.Set[cciptypes.ChainSelector], error) - // GetKnownCCIPChains Gets all chain selectors that are known to CCIP - GetKnownCCIPChains() (mapset.Set[cciptypes.ChainSelector], error) - // GetFChain Gets the FChain value for each chain - GetFChain() (map[cciptypes.ChainSelector]int, error) - // GetOCRConfigs Gets the OCR3Configs for a given donID and pluginType - GetOCRConfigs(ctx context.Context, donID uint32, pluginType uint8) ([]OCR3ConfigWithMeta, error) - services.Service -} - -type state struct { - // gets updated by the polling loop - chainConfigs map[cciptypes.ChainSelector]ChainConfig - // mapping between each node's peerID and the chains it supports. derived from chainConfigs - nodeSupportedChains map[libocrtypes.PeerID]mapset.Set[cciptypes.ChainSelector] - // set of chains that are known to CCIP, derived from chainConfigs - knownSourceChains mapset.Set[cciptypes.ChainSelector] - // map of chain to FChain value, derived from chainConfigs - fChain map[cciptypes.ChainSelector]int -} - -type homeChainPoller struct { - stopCh services.StopChan - sync services.StateMachine - homeChainReader types.ContractReader - lggr logger.Logger - mutex *sync.RWMutex - state state - failedPolls uint - // How frequently the poller fetches the chain configs - pollingDuration time.Duration -} - -const MaxFailedPolls = 10 - -func NewHomeChainConfigPoller( - homeChainReader types.ContractReader, - lggr logger.Logger, - pollingInterval time.Duration, -) HomeChain { - return &homeChainPoller{ - stopCh: make(chan struct{}), - homeChainReader: homeChainReader, - state: state{}, - mutex: &sync.RWMutex{}, - failedPolls: 0, - lggr: lggr, - pollingDuration: pollingInterval, - } -} - -func (r *homeChainPoller) Start(ctx context.Context) error { - err := r.fetchAndSetConfigs(ctx) - if err != nil { - // Just log, don't return error as we want to keep polling - r.lggr.Errorw("Initial fetch of on-chain configs failed", "err", err) - } - r.lggr.Infow("Start Polling ChainConfig") - return r.sync.StartOnce(r.Name(), func() error { - go r.poll() - return nil - }) -} - -func (r *homeChainPoller) poll() { - ctx, cancel := r.stopCh.NewCtx() - defer cancel() - ticker := time.NewTicker(r.pollingDuration) - defer ticker.Stop() - for { - select { - case <-ctx.Done(): - r.mutex.Lock() - r.failedPolls = 0 - r.mutex.Unlock() - return - case <-ticker.C: - if err := r.fetchAndSetConfigs(ctx); err != nil { - r.mutex.Lock() - r.failedPolls++ - r.mutex.Unlock() - r.lggr.Errorw("fetching and setting configs failed", "failedPolls", r.failedPolls, "err", err) - } - } - } -} - -func (r *homeChainPoller) fetchAndSetConfigs(ctx context.Context) error { - var chainConfigInfos []ChainConfigInfo - err := r.homeChainReader.GetLatestValue( - ctx, "CCIPCapabilityConfiguration", "getAllChainConfigs", nil, &chainConfigInfos, - ) - if err != nil { - r.lggr.Errorw("fetching on-chain configs failed", "err", err) - return err - } - if len(chainConfigInfos) == 0 { - // That's a legitimate case if there are no chain configs on chain yet - r.lggr.Warnw("no on chain configs found") - return nil - } - homeChainConfigs, err := convertOnChainConfigToHomeChainConfig(chainConfigInfos) - if err != nil { - r.lggr.Errorw("error converting OnChainConfigs to ChainConfig", "err", err) - return err - } - r.lggr.Infow("Setting ChainConfig") - r.setState(homeChainConfigs) - return nil -} - -func (r *homeChainPoller) setState(chainConfigs map[cciptypes.ChainSelector]ChainConfig) { - r.mutex.Lock() - defer r.mutex.Unlock() - s := &r.state - s.chainConfigs = chainConfigs - s.nodeSupportedChains = createNodesSupportedChains(chainConfigs) - s.knownSourceChains = createKnownChains(chainConfigs) - s.fChain = createFChain(chainConfigs) -} - -func (r *homeChainPoller) GetChainConfig(chainSelector cciptypes.ChainSelector) (ChainConfig, error) { - r.mutex.RLock() - defer r.mutex.RUnlock() - s := r.state - if chainConfig, ok := s.chainConfigs[chainSelector]; ok { - return chainConfig, nil - } - return ChainConfig{}, fmt.Errorf("chain config not found for chain %v", chainSelector) -} - -func (r *homeChainPoller) GetAllChainConfigs() (map[cciptypes.ChainSelector]ChainConfig, error) { - r.mutex.RLock() - defer r.mutex.RUnlock() - return r.state.chainConfigs, nil -} - -func (r *homeChainPoller) GetSupportedChainsForPeer( - id libocrtypes.PeerID, -) (mapset.Set[cciptypes.ChainSelector], error) { - r.mutex.RLock() - defer r.mutex.RUnlock() - s := r.state - if _, ok := s.nodeSupportedChains[id]; !ok { - // empty set to denote no chains supported - return mapset.NewSet[cciptypes.ChainSelector](), nil - } - return s.nodeSupportedChains[id], nil -} - -func (r *homeChainPoller) GetKnownCCIPChains() (mapset.Set[cciptypes.ChainSelector], error) { - r.mutex.RLock() - defer r.mutex.RUnlock() - knownSourceChains := mapset.NewSet[cciptypes.ChainSelector]() - for chain := range r.state.chainConfigs { - knownSourceChains.Add(chain) - } - - return knownSourceChains, nil -} - -func (r *homeChainPoller) GetFChain() (map[cciptypes.ChainSelector]int, error) { - r.mutex.RLock() - defer r.mutex.RUnlock() - return r.state.fChain, nil -} - -func (r *homeChainPoller) GetOCRConfigs( - ctx context.Context, donID uint32, pluginType uint8, -) ([]OCR3ConfigWithMeta, error) { - var ocrConfigs []OCR3ConfigWithMeta - err := r.homeChainReader.GetLatestValue(ctx, "CCIPCapabilityConfiguration", "getOCRConfig", map[string]any{ - "donId": donID, - "pluginType": pluginType, - }, &ocrConfigs) - if err != nil { - return nil, fmt.Errorf("error fetching OCR configs: %w", err) - } - - return ocrConfigs, nil -} - -func (r *homeChainPoller) Close() error { - return r.sync.StopOnce(r.Name(), func() error { - close(r.stopCh) - return nil - }) -} - -func (r *homeChainPoller) Ready() error { - r.mutex.RLock() - defer r.mutex.RUnlock() - return r.sync.Ready() -} - -func (r *homeChainPoller) HealthReport() map[string]error { - r.mutex.RLock() - defer r.mutex.RUnlock() - if r.failedPolls >= MaxFailedPolls { - r.sync.SvcErrBuffer.Append(fmt.Errorf("polling failed %d times in a row", MaxFailedPolls)) - } - return map[string]error{r.Name(): r.sync.Healthy()} -} - -func (r *homeChainPoller) Name() string { - return "homeChainPoller" -} - -func createFChain(chainConfigs map[cciptypes.ChainSelector]ChainConfig) map[cciptypes.ChainSelector]int { - fChain := map[cciptypes.ChainSelector]int{} - for chain, config := range chainConfigs { - fChain[chain] = config.FChain - } - return fChain -} - -func createKnownChains(chainConfigs map[cciptypes.ChainSelector]ChainConfig) mapset.Set[cciptypes.ChainSelector] { - knownChains := mapset.NewSet[cciptypes.ChainSelector]() - for chain := range chainConfigs { - knownChains.Add(chain) - } - return knownChains -} - -func createNodesSupportedChains( - chainConfigs map[cciptypes.ChainSelector]ChainConfig, -) map[libocrtypes.PeerID]mapset.Set[cciptypes.ChainSelector] { - nodeSupportedChains := map[libocrtypes.PeerID]mapset.Set[cciptypes.ChainSelector]{} - for chainSelector, config := range chainConfigs { - for _, p2pID := range config.SupportedNodes.ToSlice() { - if _, ok := nodeSupportedChains[p2pID]; !ok { - nodeSupportedChains[p2pID] = mapset.NewSet[cciptypes.ChainSelector]() - } - //add chain to SupportedChains - nodeSupportedChains[p2pID].Add(chainSelector) - } - } - return nodeSupportedChains -} - -func convertOnChainConfigToHomeChainConfig( - capabilityConfigs []ChainConfigInfo, -) (map[cciptypes.ChainSelector]ChainConfig, error) { - chainConfigs := make(map[cciptypes.ChainSelector]ChainConfig) - for _, capabilityConfig := range capabilityConfigs { - chainSelector := capabilityConfig.ChainSelector - config := capabilityConfig.ChainConfig - - chainConfigs[chainSelector] = ChainConfig{ - FChain: int(config.FChain), - SupportedNodes: mapset.NewSet(config.Readers...), - } - } - return chainConfigs, nil -} - -// HomeChainConfigMapper This is a 1-1 mapping between the config that we get from the contract to make -// se/deserializing easier -type HomeChainConfigMapper struct { - Readers []libocrtypes.PeerID `json:"readers"` - FChain uint8 `json:"fChain"` - Config []byte `json:"config"` -} - -// ChainConfigInfo This is a 1-1 mapping between the config that we get from the contract to make -// se/deserializing easier -type ChainConfigInfo struct { - // nolint:lll // don't split up the long url - // Calling function https://github.com/smartcontractkit/ccip/blob/330c5e98f624cfb10108c92fe1e00ced6d345a99/contracts/src/v0.8/ccip/capability/CCIPCapabilityConfiguration.sol#L140 - ChainSelector cciptypes.ChainSelector `json:"chainSelector"` - ChainConfig HomeChainConfigMapper `json:"chainConfig"` -} - -// ChainConfig will live on the home chain and will be used to update chain configuration like F value and supported -// nodes dynamically. -type ChainConfig struct { - // FChain defines the FChain value for the chain. FChain is used while forming consensus based on the observations. - FChain int `json:"fChain"` - // SupportedNodes is a map of PeerIDs to SupportedChains. - SupportedNodes mapset.Set[libocrtypes.PeerID] `json:"supportedNodes"` - // Config is the chain specific configuration. - Config []byte `json:"config"` -} - -// OCR3Config mirrors CCIPCapabilityConfiguration.sol's OCR3Config struct -type OCR3Config struct { - PluginType uint8 `json:"pluginType"` - ChainSelector cciptypes.ChainSelector `json:"chainSelector"` - F uint8 `json:"F"` - OffchainConfigVersion uint64 `json:"offchainConfigVersion"` - OfframpAddress []byte `json:"offrampAddress"` - BootstrapP2PIds [][32]byte `json:"bootstrapP2PIds"` - P2PIds [][32]byte `json:"p2pIds"` - Signers [][]byte `json:"signers"` - Transmitters [][]byte `json:"transmitters"` - OffchainConfig []byte `json:"offchainConfig"` -} - -// OCR3ConfigWithmeta mirrors CCIPCapabilityConfiguration.sol's OCR3ConfigWithMeta struct -type OCR3ConfigWithMeta struct { - Config OCR3Config `json:"config"` - ConfigCount uint64 `json:"configCount"` - ConfigDigest [32]byte `json:"configDigest"` -} - -var _ HomeChain = (*homeChainPoller)(nil) diff --git a/core/services/ocr3/plugins/ccip/internal/reader/home_chain_test.go b/core/services/ocr3/plugins/ccip/internal/reader/home_chain_test.go deleted file mode 100644 index f08f44bf7f..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/reader/home_chain_test.go +++ /dev/null @@ -1,201 +0,0 @@ -package reader - -import ( - "context" - "fmt" - "testing" - "time" - - mapset "github.com/deckarep/golang-set/v2" - libocrtypes "github.com/smartcontractkit/libocr/ragep2p/types" - - "github.com/smartcontractkit/ccipocr3/internal/mocks" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" - "github.com/smartcontractkit/libocr/commontypes" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -var ( - chainA = cciptypes.ChainSelector(1) - chainB = cciptypes.ChainSelector(2) - chainC = cciptypes.ChainSelector(3) - oracleAId = commontypes.OracleID(1) - p2pOracleAId = libocrtypes.PeerID{byte(oracleAId)} - oracleBId = commontypes.OracleID(2) - p2pOracleBId = libocrtypes.PeerID{byte(oracleBId)} - oracleCId = commontypes.OracleID(3) - p2pOracleCId = libocrtypes.PeerID{byte(oracleCId)} -) - -func TestHomeChainConfigPoller_HealthReport(t *testing.T) { - homeChainReader := mocks.NewContractReaderMock() - homeChainReader.On( - "GetLatestValue", - mock.Anything, - "CCIPCapabilityConfiguration", - "getAllChainConfigs", - mock.Anything, - mock.Anything).Return(fmt.Errorf("error")) - - var ( - tickTime = 10 * time.Millisecond - totalSleepTime = 11 * tickTime - ) - - configPoller := NewHomeChainConfigPoller( - homeChainReader, - logger.Test(t), - tickTime, - ) - _ = configPoller.Start(context.Background()) - - // Initially it's healthy - healthy := configPoller.HealthReport() - assert.Equal(t, map[string]error{configPoller.Name(): error(nil)}, healthy) - - // After one second it will try polling 10 times and fail - time.Sleep(totalSleepTime) - - errors := configPoller.HealthReport() - - err := configPoller.Close() - time.Sleep(tickTime) - assert.NoError(t, err) - assert.Equal(t, 1, len(errors)) - assert.Errorf(t, errors[configPoller.Name()], "polling failed %d times in a row", MaxFailedPolls) -} - -func Test_PollingWorking(t *testing.T) { - onChainConfigs := []ChainConfigInfo{ - { - ChainSelector: chainA, - ChainConfig: HomeChainConfigMapper{ - FChain: 1, - Readers: []libocrtypes.PeerID{ - p2pOracleAId, - p2pOracleBId, - p2pOracleCId, - }, - Config: []byte{0}, - }, - }, - { - ChainSelector: chainB, - ChainConfig: HomeChainConfigMapper{ - FChain: 2, - Readers: []libocrtypes.PeerID{ - p2pOracleAId, - p2pOracleBId, - }, - Config: []byte{0}, - }, - }, - { - ChainSelector: chainC, - ChainConfig: HomeChainConfigMapper{ - FChain: 3, - Readers: []libocrtypes.PeerID{ - p2pOracleCId, - }, - Config: []byte{0}, - }, - }, - } - homeChainConfig := map[cciptypes.ChainSelector]ChainConfig{ - chainA: { - FChain: 1, - SupportedNodes: mapset.NewSet(p2pOracleAId, p2pOracleBId, p2pOracleCId), - }, - chainB: { - FChain: 2, - SupportedNodes: mapset.NewSet(p2pOracleAId, p2pOracleBId), - }, - chainC: { - FChain: 3, - SupportedNodes: mapset.NewSet(p2pOracleCId), - }, - } - - homeChainReader := mocks.NewContractReaderMock() - homeChainReader.On( - "GetLatestValue", mock.Anything, "CCIPCapabilityConfiguration", "getAllChainConfigs", mock.Anything, mock.Anything, - ).Run( - func(args mock.Arguments) { - arg := args.Get(4).(*[]ChainConfigInfo) - *arg = onChainConfigs - }).Return(nil) - - var ( - tickTime = 20 * time.Millisecond - totalSleepTime = (tickTime * 2) + (10 * time.Millisecond) - expNumCalls = int(totalSleepTime/tickTime) + 1 // +1 for the initial call - ) - - configPoller := NewHomeChainConfigPoller( - homeChainReader, - logger.Test(t), - tickTime, - ) - - ctx := context.Background() - err := configPoller.Start(ctx) - assert.NoError(t, err) - time.Sleep(totalSleepTime) - err = configPoller.Close() - assert.NoError(t, err) - - // called 3 times, once when it's started, and 2 times when it's polling - homeChainReader.AssertNumberOfCalls(t, "GetLatestValue", expNumCalls) - - configs, err := configPoller.GetAllChainConfigs() - assert.NoError(t, err) - assert.Equal(t, homeChainConfig, configs) -} - -func Test_HomeChainPoller_GetOCRConfig(t *testing.T) { - donID := uint32(1) - pluginType := uint8(1) // execution - homeChainReader := mocks.NewContractReaderMock() - homeChainReader.On( - "GetLatestValue", - mock.Anything, - "CCIPCapabilityConfiguration", - "getOCRConfig", - map[string]any{ - "donId": donID, - "pluginType": pluginType, - }, - mock.AnythingOfType("*[]reader.OCR3ConfigWithMeta"), - ).Return(nil).Run(func(args mock.Arguments) { - arg := args.Get(4).(*[]OCR3ConfigWithMeta) - *arg = append(*arg, OCR3ConfigWithMeta{ - ConfigCount: 1, - Config: OCR3Config{ - PluginType: pluginType, - ChainSelector: 1, - F: 1, - OfframpAddress: []byte("offramp"), - }, - }) - }) - defer homeChainReader.AssertExpectations(t) - - configPoller := NewHomeChainConfigPoller( - homeChainReader, - logger.Test(t), - 10*time.Millisecond, - ) - - configs, err := configPoller.GetOCRConfigs(context.Background(), donID, pluginType) - require.NoError(t, err) - require.Len(t, configs, 1) - require.Equal(t, uint8(1), configs[0].Config.PluginType) - require.Equal(t, cciptypes.ChainSelector(1), configs[0].Config.ChainSelector) - require.Equal(t, uint8(1), configs[0].Config.F) - require.Equal(t, []byte("offramp"), configs[0].Config.OfframpAddress) -} diff --git a/core/services/ocr3/plugins/ccip/internal/reader/onchain_prices_reader.go b/core/services/ocr3/plugins/ccip/internal/reader/onchain_prices_reader.go deleted file mode 100644 index 6c747bf48b..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/reader/onchain_prices_reader.go +++ /dev/null @@ -1,70 +0,0 @@ -package reader - -import ( - "context" - "fmt" - "math/big" - - commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" - ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "golang.org/x/sync/errgroup" -) - -type TokenPriceConfig struct { - // This is mainly used for inputTokens on testnet to give them a price - StaticPrices map[ocr2types.Account]big.Int `json:"staticPrices"` -} - -type OnchainTokenPricesReader struct { - TokenPriceConfig TokenPriceConfig - // Reader for the chain that will have the token prices on-chain - ContractReader commontypes.ContractReader -} - -func NewOnchainTokenPricesReader( - tokenPriceConfig TokenPriceConfig, contractReader commontypes.ContractReader, -) *OnchainTokenPricesReader { - return &OnchainTokenPricesReader{ - TokenPriceConfig: tokenPriceConfig, - ContractReader: contractReader, - } -} - -func (pr *OnchainTokenPricesReader) GetTokenPricesUSD( - ctx context.Context, tokens []ocr2types.Account, -) ([]*big.Int, error) { - const ( - contractName = "PriceAggregator" - functionName = "getTokenPrice" - ) - prices := make([]*big.Int, len(tokens)) - eg := new(errgroup.Group) - for idx, token := range tokens { - idx := idx - token := token - eg.Go(func() error { - price := new(big.Int) - if staticPrice, exists := pr.TokenPriceConfig.StaticPrices[token]; exists { - price.Set(&staticPrice) - } else { - if err := pr.ContractReader.GetLatestValue(ctx, contractName, functionName, token, price); err != nil { - return fmt.Errorf("failed to get token price for %s: %w", token, err) - } - } - prices[idx] = price - return nil - }) - } - - if err := eg.Wait(); err != nil { - return nil, fmt.Errorf("failed to get all token prices successfully: %w", err) - } - - for _, price := range prices { - if price == nil { - return nil, fmt.Errorf("failed to get all token prices successfully, some prices are nil") - } - } - - return prices, nil -} diff --git a/core/services/ocr3/plugins/ccip/internal/reader/onchain_prices_reader_test.go b/core/services/ocr3/plugins/ccip/internal/reader/onchain_prices_reader_test.go deleted file mode 100644 index f57183134d..0000000000 --- a/core/services/ocr3/plugins/ccip/internal/reader/onchain_prices_reader_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package reader - -import ( - "context" - "fmt" - "math/big" - "testing" - - "github.com/smartcontractkit/ccipocr3/internal/mocks" - - ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -const ( - EthAcc = ocr2types.Account("ETH") - OpAcc = ocr2types.Account("OP") - ArbAcc = ocr2types.Account("ARB") -) - -var ( - EthPrice = big.NewInt(100) - OpPrice = big.NewInt(10) - ArbPrice = big.NewInt(1) -) - -func TestOnchainTokenPricesReader_GetTokenPricesUSD(t *testing.T) { - testCases := []struct { - name string - staticPrices map[ocr2types.Account]big.Int - inputTokens []ocr2types.Account - mockPrices map[ocr2types.Account]*big.Int - want []*big.Int - errorAccounts []ocr2types.Account - wantErr bool - }{ - { - name: "Static price only", - staticPrices: map[ocr2types.Account]big.Int{EthAcc: *EthPrice, OpAcc: *OpPrice}, - inputTokens: []ocr2types.Account{EthAcc, OpAcc}, - mockPrices: map[ocr2types.Account]*big.Int{}, - want: []*big.Int{EthPrice, OpPrice}, - }, - { - name: "On-chain price only", - staticPrices: map[ocr2types.Account]big.Int{}, - inputTokens: []ocr2types.Account{ArbAcc, OpAcc, EthAcc}, - mockPrices: map[ocr2types.Account]*big.Int{OpAcc: OpPrice, ArbAcc: ArbPrice, EthAcc: EthPrice}, - want: []*big.Int{ArbPrice, OpPrice, EthPrice}, - }, - { - name: "Mix of static price and onchain price", - staticPrices: map[ocr2types.Account]big.Int{EthAcc: *EthPrice}, - inputTokens: []ocr2types.Account{EthAcc, OpAcc, ArbAcc}, - mockPrices: map[ocr2types.Account]*big.Int{ArbAcc: ArbPrice, OpAcc: OpPrice}, - want: []*big.Int{EthPrice, OpPrice, ArbPrice}, - }, - { - name: "Missing price should error", - staticPrices: map[ocr2types.Account]big.Int{}, - inputTokens: []ocr2types.Account{ArbAcc, OpAcc, EthAcc}, - mockPrices: map[ocr2types.Account]*big.Int{OpAcc: OpPrice, ArbAcc: ArbPrice}, - errorAccounts: []ocr2types.Account{EthAcc}, - want: nil, - wantErr: true, - }, - } - - for _, tc := range testCases { - contractReader := createMockReader(tc.mockPrices, tc.errorAccounts) - tokenPricesReader := OnchainTokenPricesReader{ - TokenPriceConfig: TokenPriceConfig{StaticPrices: tc.staticPrices}, - ContractReader: contractReader, - } - t.Run(tc.name, func(t *testing.T) { - ctx := context.Background() - result, err := tokenPricesReader.GetTokenPricesUSD(ctx, tc.inputTokens) - - if tc.wantErr { - require.Error(t, err) - return - } - - require.NoError(t, err) - require.Equal(t, tc.want, result) - }) - } - -} - -func createMockReader( - mockPrices map[ocr2types.Account]*big.Int, errorAccounts []ocr2types.Account, -) *mocks.ContractReaderMock { - reader := mocks.NewContractReaderMock() - for _, acc := range errorAccounts { - acc := acc - reader.On( - "GetLatestValue", mock.Anything, "PriceAggregator", "getTokenPrice", acc, mock.Anything, - ).Return(fmt.Errorf("error")) - } - for acc, price := range mockPrices { - acc := acc - price := price - reader.On("GetLatestValue", mock.Anything, "PriceAggregator", "getTokenPrice", acc, mock.Anything).Run( - func(args mock.Arguments) { - arg := args.Get(4).(*big.Int) - arg.Set(price) - }).Return(nil) - } - return reader -} diff --git a/core/services/ocr3/plugins/ccip/pkg/reader/home_chain.go b/core/services/ocr3/plugins/ccip/pkg/reader/home_chain.go deleted file mode 100644 index 7d56122952..0000000000 --- a/core/services/ocr3/plugins/ccip/pkg/reader/home_chain.go +++ /dev/null @@ -1,24 +0,0 @@ -package reader - -import ( - "time" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/types" - - reader_internal "github.com/smartcontractkit/ccipocr3/internal/reader" -) - -type HomeChain = reader_internal.HomeChain - -type ChainConfig = reader_internal.ChainConfig - -type ChainConfigInfo = reader_internal.ChainConfigInfo - -func NewHomeChainReader( - homeChainReader types.ContractReader, - lggr logger.Logger, - pollingInterval time.Duration, -) HomeChain { - return reader_internal.NewHomeChainConfigPoller(homeChainReader, lggr, pollingInterval) -} diff --git a/core/services/ocr3/plugins/ccip/spec/commit_plugin.py b/core/services/ocr3/plugins/ccip/spec/commit_plugin.py deleted file mode 100644 index 5698650153..0000000000 --- a/core/services/ocr3/plugins/ccip/spec/commit_plugin.py +++ /dev/null @@ -1,243 +0,0 @@ -# -# High-level Python specification for the CCIP OCR3 Commit Plugin. -# -# This specification aims to provide a clear and comprehensive understanding -# of the plugin's functionality. It is highly recommended for engineers working on CCIP -# to familiarize themselves with this specification prior to reading the -# corresponding Go implementation. -# -# NOTE: Even though the specification is written in a high-level programming language, it's purpose -# is not to be executed. It is meant to be just a reference for the Go implementation. -# -from dataclasses import dataclass -from typing import List, Dict - -ChainSelector = int - -@dataclass -class Interval: - min: int - max: int - -@dataclass -class Message: - seq_nr: int - message_id: bytes # a unique message identifier computed on the source chain - message_hash: bytes # hash of message body computed on the destination chain and used on merkle tree - # TODO: - -@dataclass -class Commit: - interval: Interval - root: bytes - -@dataclass -class CommitOutcome: - latest_committed_seq_nums: Dict[ChainSelector, int] - commits: Dict[ChainSelector, Commit] - token_prices: Dict[str, int] - gas_prices: Dict[ChainSelector, int] - -@dataclass -class CommitObservation: - latest_committed_seq_nums: Dict[ChainSelector, int] - new_msgs: Dict[ChainSelector, List[Message]] - token_prices: Dict[str, int] - gas_prices: Dict[ChainSelector, int] - f_chain: Dict[ChainSelector, int] - -@dataclass -class CommitConfig: - oracle: int # our own observer - dest_chain: ChainSelector - f_chain: Dict[ChainSelector, int] - # oracleIndex -> supported chains - oracle_info: Dict[int, Dict[ChainSelector, bool]] - priced_tokens: List[str] - -class CommitPlugin: - def __init__(self): - self.cfg = CommitConfig( - oracle=1, - dest_chain=10, - f_chain={1: 2, 2: 3, 10: 3}, - oracle_info={ - 0: {1: True, 2: True, 10: True}, - # TODO: other oracles - }, - # TODO: will likely need aggregator address as well to - # actually get the price. - priced_tokens=["tokenA", "tokenB"], - ) - self.keep_cfg_in_sync() - - def get_token_prices(self): - # Read token prices which are required for the destination chain. - # We only read them if we have the capability to read from the price chain (e.g. arbitrum) - pass - - def get_gas_prices(self): - # Read all gas prices for the chains we support. - pass - - def query(self): - pass - - def observation(self, previous_outcome: CommitOutcome) -> CommitObservation: - # max_committed_seq_nr={sourceChainA: 10, sourceChainB: 20,...} - # Provided by the nodes that can read from the destination on the previous round. - # Observe msgs for our supported chains since the prev outcome. - new_msgs = {} - for (chain, seq_num) in previous_outcome.latest_committed_seq_nums: - if chain in self.cfg.oracle_info[self.cfg.oracle]: - msgs = self.onRamp(chain).get_msgs(chain, start=seq_num+1, limit=256) - for msg in msgs: - msg.message_hash = msg.compute_hash() - new_msgs[chain] = msgs - - # Observe token prices. {token: price} - token_prices = self.get_token_prices() - - # Observe gas prices. {chain: gasPrice} - # TODO: Should be a way to combine the loops over support chains for gas prices and new messages. - gas_prices = self.get_gas_prices() - - # Observe fChain for each chain. {chain: f_chain} - # We observe this because configuration changes may be detected at different times by different nodes. - # We always use the configuration which is seen by a majority of nodes. - f_chain = self.cfg.f_chain - - # If we support the destination chain, then we contribute an observation of the max committed seq nums. - # We use these in outcome to filter out messages that have already been committed. - latest_committed_seq_nums = {} - if self.cfg.dest_chain in self.cfg.oracle_info[self.cfg.oracle]: - latest_committed_seq_nums = self.offRamp.latest_committed_seq_nums() - - return CommitObservation(latest_committed_seq_nums, new_msgs, token_prices, gas_prices, f_chain) - - - def validate_observation(self, attributed_observation): - observation = attributed_observation.observation - oracle = attributed_observation.oracle - - # Only accept dest observations from nodes that support the dest chain - if observation.latest_committed_seq_nums is not None: - assert self.cfg.dest_chain in self.cfg.oracle_info[oracle] - - # Only accept source observations from nodes which support those sources. - msg_ids = set() - msg_hashes = set() - for (chain, msgs) in observation.new_msgs.items(): - assert(chain in self.cfg.oracle_info[oracle]) - # Don't allow duplicates of (chain, seqNr), (id) and (hash). Required to prevent double counting. - assert(len(msgs) == len(set([msg.seq_num for msg in msgs]))) - for msg in msgs: - assert msg.message_id not in msg_ids - assert msg.message_hash not in msg_hashes - msg_ids.add(msg.message_id) - msg_hashes.add(msg.message_hash) - - def observation_quorum(self): - return "2F+1" - - def outcome(self, observations: List[CommitObservation])->CommitOutcome: - f_chain = consensus_f_chain(observations) - latest_committed_seq_nums = consensus_latest_committed_seq_nums(observations, f_chain) - - # all_msgs contains all messages from all observations, grouped by source chain - all_msgs = [observation.new_msgs for observation in observations].group_by_source_chain() - - commits = {} # { chain: (root, min_seq_num, max_seq_num) } - for (chain, msgs) in all_msgs: - # Keep only msgs with seq nums greater than the consensus max commited seq nums. - # Note right after a report has been submitted, we'll expect those same messages - # to appear in the next observation, because the message observations are built - # on the previous max committed seq nums. - msgs = [msg for msg in msgs if msg.seq_num > latest_committed_seq_nums[chain]] - - msgs_by_seq_num = msgs.group_by_seq_num() # { 423: [0x1, 0x1, 0x2] } - # 2 nodes say that msg hash is 0x1 and 1 node says it's 0x2 - # if different hashes have the same number of votes, we select the - # hash with the lowest lexicographic order - - msg_hashes = { seq_num: elem_most_occurrences(hashes) for (seq_num, hashes) in msgs_by_seq_num.items() } - for (seq_num, hash) in msg_hashes.items(): # require at least 2f+1 observations of the voted hash - assert(msgs_by_seq_num[seq_num].count(hash) >= 2*f_chain[chain]+1) - - msgs_for_tree = [] # [ (seq_num, hash) ] - for (seq_num, hash) in msg_hashes.ordered_by_seq_num(): - if len(msgs_for_tree) > 0 and msgs_for_tree[-1].seq_num+1 != seq_num: - break # gap in sequence numbers, stop here - msgs_for_tree.append((seq_num, hash)) - - commits[chain] = Commit(root=build_merkle_tree(msgs_for_tree), interval=Interval(min=msgs_for_tree[0].seq_num, max=msgs_for_tree[-1].seq_num)) - - # TODO: we only want to put token/gas prices onchain - # on a regular cadence unless huge deviation. - token_prices = { tk: median(prices) for (tk, prices) in observations.group_token_prices_by_token() } - gas_prices = { chain: median(prices) for (chain, prices) in observations.group_gas_prices_by_chain() } - - return CommitOutcome(latest_committed_seq_nums=latest_committed_seq_nums, commits=commits, token_price=token_prices, gas_prices=gas_prices) - - def reports(self, outcome): - report = report_from_outcome(outcome) - encoded = report.chain_encode() # abi_encode for evm chains - return [encoded] - - def should_accept(self, report): - if len(report) == 0 or self.validate_report(report): - return False - - def should_transmit(self, report): - if not self.is_writer(): - return False - - if len(report) == 0 or not self.validate_report(report): - return False - - on_chain_seq_nums = self.offRamp.get_sequence_numbers() - for (chain, tree) in report.trees(): - if not (on_chain_seq_nums[chain]+1 == tree.min_seq_num): - return False - - return True - - def validate_report(self, report): - pass - - def keep_cfg_in_sync(self): - # Polling the configuration on the on-chain contract. - # When the config is updated on-chain, updates the plugin's local copy to the most recent version. - pass - -def consensus_f_chain(observations): - f_chain_votes = observations["f_chain"].group_by_chain() # { chainA: [1, 1, 16, 16, 16, 16] } - return { ch: elem_most_occurrences(fs) for (ch, fs) in f_chain_votes.items() } # { chainA: 16 } - -def consensus_latest_committed_seq_nums(observations, f_chains): - all_latest_committed_seq_nums = {} - for observation in observations: - for (chain, seq_num) in observation.latest_committed_seq_nums.items(): - if chain not in all_latest_committed_seq_nums: - all_latest_committed_seq_nums[chain] = [] - all_latest_committed_seq_nums[chain].append(seq_num) - - latest_committed_seq_nums_consensus = {} - # { chainA: [4, 5, 5, 5, 5, 6, 6] } - for (chain, latest_committed_seq_nums) in all_latest_committed_seq_nums.items(): - if len(latest_committed_seq_nums) >= 2*f_chains[chain]+1: - # 2f+1 = 2*5+1 = 11 - latest_committed_seq_nums_consensus[chain] = sorted(latest_committed_seq_nums)[f_chains[chain]]# with f=4 { chainA: 5 } - return latest_committed_seq_nums_consensus - -def elem_most_occurrences(lst): - pass - -def build_merkle_tree(messages): - pass - -def median(lst): - pass - -def report_from_outcome(outcome: CommitOutcome)->bytes: - pass diff --git a/core/services/ocr3/plugins/ccip_integration_tests/chainreader/chainreader_test.go b/core/services/ocr3/plugins/ccip_integration_tests/chainreader/chainreader_test.go index caff62c795..d0bedf48a9 100644 --- a/core/services/ocr3/plugins/ccip_integration_tests/chainreader/chainreader_test.go +++ b/core/services/ocr3/plugins/ccip_integration_tests/chainreader/chainreader_test.go @@ -18,6 +18,8 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/assert" + "github.com/smartcontractkit/chainlink-common/pkg/codec" types2 "github.com/smartcontractkit/chainlink-common/pkg/types" query2 "github.com/smartcontractkit/chainlink-common/pkg/types/query" @@ -28,7 +30,6 @@ import ( logger2 "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" - "github.com/stretchr/testify/assert" ) const chainID = 1337 @@ -77,6 +78,9 @@ func TestChainReader(t *testing.T) { cfg := evmtypes.ChainReaderConfig{ Contracts: map[string]evmtypes.ChainContractReader{ ContractNameAlias: { + ContractPollingFilter: evmtypes.ContractPollingFilter{ + GenericEventNames: []string{EventNameAlias}, + }, ContractABI: ChainreaderMetaData.ABI, Configs: map[string]*evmtypes.ChainReaderDefinition{ EventNameAlias: { diff --git a/core/services/ocr3/plugins/ccip_integration_tests/home_chain/home_chain_test.go b/core/services/ocr3/plugins/ccip_integration_tests/home_chain/home_chain_test.go new file mode 100644 index 0000000000..ab8629f8c1 --- /dev/null +++ b/core/services/ocr3/plugins/ccip_integration_tests/home_chain/home_chain_test.go @@ -0,0 +1,89 @@ +package home_chain + +import ( + "testing" + "time" + + mapset "github.com/deckarep/golang-set/v2" + "github.com/onsi/gomega" + + libocrtypes "github.com/smartcontractkit/libocr/ragep2p/types" + + ccipreader "github.com/smartcontractkit/chainlink-ccip/pkg/reader" + cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" + capcfg "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + it "github.com/smartcontractkit/chainlink/v2/core/services/ocr3/plugins/ccip_integration_tests" + + "github.com/stretchr/testify/require" +) + +func TestHomeChainReader(t *testing.T) { + ctx := testutils.Context(t) + lggr := logger.TestLogger(t) + uni := it.NewTestUniverse(ctx, t, lggr) + // We need 3*f + 1 p2pIDs to have enough nodes to bootstrap + var arr []int64 + n := int(it.FChainA*3 + 1) + for i := 0; i <= n; i++ { + arr = append(arr, int64(i)) + } + p2pIDs := it.P2pIDsFromInts(arr) + uni.AddCapability(p2pIDs) + //==============================Apply configs to Capability Contract================================= + chainAConf := it.SetupConfigInfo(it.ChainA, p2pIDs, it.FChainA, []byte("ChainA")) + chainBConf := it.SetupConfigInfo(it.ChainB, p2pIDs[1:], it.FChainB, []byte("ChainB")) + chainCConf := it.SetupConfigInfo(it.ChainC, p2pIDs[2:], it.FChainC, []byte("ChainC")) + inputConfig := []capcfg.CCIPConfigTypesChainConfigInfo{ + chainAConf, + chainBConf, + chainCConf, + } + _, err := uni.CcipCfg.ApplyChainConfigUpdates(uni.Transactor, nil, inputConfig) + require.NoError(t, err) + uni.Backend.Commit() + //================================Setup HomeChainReader=============================== + + pollDuration := time.Second + homeChain := uni.HomeChainReader + + gomega.NewWithT(t).Eventually(func() bool { + configs, _ := homeChain.GetAllChainConfigs() + return configs != nil + }, testutils.WaitTimeout(t), pollDuration*5).Should(gomega.BeTrue()) + + t.Logf("homchain reader is ready") + //================================Test HomeChain Reader=============================== + expectedChainConfigs := map[cciptypes.ChainSelector]ccipreader.ChainConfig{} + for _, c := range inputConfig { + expectedChainConfigs[cciptypes.ChainSelector(c.ChainSelector)] = ccipreader.ChainConfig{ + FChain: int(c.ChainConfig.FChain), + SupportedNodes: toPeerIDs(c.ChainConfig.Readers), + } + } + configs, err := homeChain.GetAllChainConfigs() + require.NoError(t, err) + require.Equal(t, expectedChainConfigs, configs) + //=================================Remove ChainC from OnChainConfig========================================= + _, err = uni.CcipCfg.ApplyChainConfigUpdates(uni.Transactor, []uint64{it.ChainC}, nil) + require.NoError(t, err) + uni.Backend.Commit() + time.Sleep(pollDuration * 5) // Wait for the chain reader to update + configs, err = homeChain.GetAllChainConfigs() + require.NoError(t, err) + delete(expectedChainConfigs, cciptypes.ChainSelector(it.ChainC)) + require.Equal(t, expectedChainConfigs, configs) + //================================Close HomeChain Reader=============================== + //require.NoError(t, homeChain.Close()) + //require.NoError(t, uni.LogPoller.Close()) + t.Logf("homchain reader successfully closed") +} + +func toPeerIDs(readers [][32]byte) mapset.Set[libocrtypes.PeerID] { + peerIDs := mapset.NewSet[libocrtypes.PeerID]() + for _, r := range readers { + peerIDs.Add(r) + } + return peerIDs +} diff --git a/core/services/ocr3/plugins/ccip_integration_tests/integration_helpers.go b/core/services/ocr3/plugins/ccip_integration_tests/integration_helpers.go new file mode 100644 index 0000000000..b7e81cd2c9 --- /dev/null +++ b/core/services/ocr3/plugins/ccip_integration_tests/integration_helpers.go @@ -0,0 +1,291 @@ +package ccip_integration_tests + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + + ccipreader "github.com/smartcontractkit/chainlink-ccip/pkg/reader" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ocr3_config_encoder" + kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + cctypes "github.com/smartcontractkit/chainlink/v2/core/services/ccipcapability/types" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" + + "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + evmrelaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +const chainID = 1337 + +func NewReader(t *testing.T, logPoller logpoller.LogPoller, client client.Client, address common.Address, chainReaderConfig evmrelaytypes.ChainReaderConfig, contractName string) types.ContractReader { + cr, err := evm.NewChainReaderService(testutils.Context(t), logger.TestLogger(t), logPoller, client, chainReaderConfig) + require.NoError(t, err) + err = cr.Bind(testutils.Context(t), []types.BoundContract{ + { + Address: address.String(), + Name: contractName, + Pending: false, + }, + }) + require.NoError(t, err) + require.NoError(t, cr.Start(testutils.Context(t))) + for { + if err := cr.Ready(); err == nil { + break + } + } + + return cr +} + +const ( + ChainA uint64 = 1 + FChainA uint8 = 1 + + ChainB uint64 = 2 + FChainB uint8 = 2 + + ChainC uint64 = 3 + FChainC uint8 = 3 + + CcipCapabilityLabelledName = "ccip" + CcipCapabilityVersion = "v1.0" +) + +type TestUniverse struct { + Transactor *bind.TransactOpts + Backend *backends.SimulatedBackend + CapReg *kcr.CapabilitiesRegistry + CcipCfg *ccip_config.CCIPConfig + TestingT *testing.T + LogPoller logpoller.LogPoller + SimClient client.Client + HomeChainReader ccipreader.HomeChain +} + +func NewTestUniverse(ctx context.Context, t *testing.T, lggr logger.Logger) TestUniverse { + transactor := testutils.MustNewSimTransactor(t) + backend := backends.NewSimulatedBackend(core.GenesisAlloc{ + transactor.From: {Balance: assets.Ether(1000).ToInt()}, + }, 30e6) + + crAddress, _, _, err := kcr.DeployCapabilitiesRegistry(transactor, backend) + require.NoError(t, err) + backend.Commit() + + capReg, err := kcr.NewCapabilitiesRegistry(crAddress, backend) + require.NoError(t, err) + + ccAddress, _, _, err := ccip_config.DeployCCIPConfig(transactor, backend, crAddress) + require.NoError(t, err) + backend.Commit() + + cc, err := ccip_config.NewCCIPConfig(ccAddress, backend) + require.NoError(t, err) + + db := pgtest.NewSqlxDB(t) + lpOpts := logpoller.Opts{ + PollPeriod: time.Millisecond, + FinalityDepth: 0, + BackfillBatchSize: 10, + RpcBatchSize: 10, + KeepFinalizedBlocksDepth: 100000, + } + cl := client.NewSimulatedBackendClient(t, backend, big.NewInt(chainID)) + lp := logpoller.NewLogPoller(logpoller.NewORM(big.NewInt(chainID), db, lggr), cl, logger.NullLogger, lpOpts) + require.NoError(t, lp.Start(ctx)) + t.Cleanup(func() { require.NoError(t, lp.Close()) }) + + hcr := NewHomeChainReader(t, lp, cl, ccAddress) + return TestUniverse{ + Transactor: transactor, + Backend: backend, + CapReg: capReg, + CcipCfg: cc, + TestingT: t, + SimClient: cl, + LogPoller: lp, + HomeChainReader: hcr, + } +} + +func (t TestUniverse) NewContractReader(ctx context.Context, cfg []byte) (types.ContractReader, error) { + var config evmrelaytypes.ChainReaderConfig + err := json.Unmarshal(cfg, &config) + require.NoError(t.TestingT, err) + return evm.NewChainReaderService(ctx, logger.TestLogger(t.TestingT), t.LogPoller, t.SimClient, config) +} + +func P2pIDsFromInts(ints []int64) [][32]byte { + var p2pIDs [][32]byte + for _, i := range ints { + p2pID := p2pkey.MustNewV2XXXTestingOnly(big.NewInt(i)).PeerID() + p2pIDs = append(p2pIDs, p2pID) + } + return p2pIDs +} + +func (t *TestUniverse) AddCapability(p2pIDs [][32]byte) { + _, err := t.CapReg.AddCapabilities(t.Transactor, []kcr.CapabilitiesRegistryCapability{ + { + LabelledName: CcipCapabilityLabelledName, + Version: CcipCapabilityVersion, + CapabilityType: 0, + ResponseType: 0, + ConfigurationContract: t.CcipCfg.Address(), + }, + }) + require.NoError(t.TestingT, err, "failed to add capability to registry") + t.Backend.Commit() + + ccipCapabilityID, err := t.CapReg.GetHashedCapabilityId(nil, CcipCapabilityLabelledName, CcipCapabilityVersion) + require.NoError(t.TestingT, err) + + for i := 0; i < len(p2pIDs); i++ { + _, err = t.CapReg.AddNodeOperators(t.Transactor, []kcr.CapabilitiesRegistryNodeOperator{ + { + Admin: t.Transactor.From, + Name: fmt.Sprintf("nop-%d", i), + }, + }) + require.NoError(t.TestingT, err) + t.Backend.Commit() + + // get the node operator id from the event + it, err := t.CapReg.FilterNodeOperatorAdded(nil, nil, nil) + require.NoError(t.TestingT, err) + var nodeOperatorID uint32 + for it.Next() { + if it.Event.Name == fmt.Sprintf("nop-%d", i) { + nodeOperatorID = it.Event.NodeOperatorId + break + } + } + require.NotZero(t.TestingT, nodeOperatorID) + + _, err = t.CapReg.AddNodes(t.Transactor, []kcr.CapabilitiesRegistryNodeParams{ + { + NodeOperatorId: nodeOperatorID, + Signer: testutils.Random32Byte(), + P2pId: p2pIDs[i], + HashedCapabilityIds: [][32]byte{ccipCapabilityID}, + }, + }) + require.NoError(t.TestingT, err) + t.Backend.Commit() + + // verify that the node was added successfully + nodeInfo, err := t.CapReg.GetNode(nil, p2pIDs[i]) + require.NoError(t.TestingT, err) + + require.Equal(t.TestingT, nodeOperatorID, nodeInfo.NodeOperatorId) + require.Equal(t.TestingT, p2pIDs[i][:], nodeInfo.P2pId[:]) + } +} + +func NewHomeChainReader(t *testing.T, logPoller logpoller.LogPoller, client client.Client, ccAddress common.Address) ccipreader.HomeChain { + cfg := evmrelaytypes.ChainReaderConfig{ + Contracts: map[string]evmrelaytypes.ChainContractReader{ + "CCIPConfig": { + ContractABI: ccip_config.CCIPConfigMetaData.ABI, + Configs: map[string]*evmrelaytypes.ChainReaderDefinition{ + "getAllChainConfigs": { + ChainSpecificName: "getAllChainConfigs", + }, + "getOCRConfig": { + ChainSpecificName: "getOCRConfig", + }, + }, + }, + }, + } + + cr := NewReader(t, logPoller, client, ccAddress, cfg, "CCIPConfig") + + hcr := ccipreader.NewHomeChainReader(cr, logger.TestLogger(t), 500*time.Millisecond) + require.NoError(t, hcr.Start(testutils.Context(t))) + t.Cleanup(func() { require.NoError(t, hcr.Close()) }) + + return hcr +} + +func (t *TestUniverse) AddDONToRegistry( + ccipCapabilityID [32]byte, + chainSelector uint64, + f uint8, + bootstrapP2PID [32]byte, + p2pIDs [][32]byte, +) { + tabi, err := ocr3_config_encoder.IOCR3ConfigEncoderMetaData.GetAbi() + require.NoError(t.TestingT, err) + + var ( + signers [][]byte + transmitters [][]byte + ) + for range p2pIDs { + signers = append(signers, testutils.NewAddress().Bytes()) + transmitters = append(transmitters, testutils.NewAddress().Bytes()) + } + + var ocr3Configs []ocr3_config_encoder.CCIPConfigTypesOCR3Config + for _, pluginType := range []cctypes.PluginType{cctypes.PluginTypeCCIPCommit, cctypes.PluginTypeCCIPExec} { + ocr3Configs = append(ocr3Configs, ocr3_config_encoder.CCIPConfigTypesOCR3Config{ + PluginType: uint8(pluginType), + ChainSelector: chainSelector, + F: f, + OffchainConfigVersion: 30, + OfframpAddress: testutils.NewAddress().Bytes(), + BootstrapP2PIds: [][32]byte{bootstrapP2PID}, + P2pIds: p2pIDs, + Signers: signers, + Transmitters: transmitters, + OffchainConfig: []byte("offchain config"), + }) + } + + encodedCall, err := tabi.Pack("exposeOCR3Config", ocr3Configs) + require.NoError(t.TestingT, err) + + // Trim first four bytes to remove function selector. + encodedConfigs := encodedCall[4:] + + _, err = t.CapReg.AddDON(t.Transactor, p2pIDs, []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: ccipCapabilityID, + Config: encodedConfigs, + }, + }, false, false, f) + require.NoError(t.TestingT, err) + t.Backend.Commit() +} + +func SetupConfigInfo(chainSelector uint64, readers [][32]byte, fChain uint8, cfg []byte) ccip_config.CCIPConfigTypesChainConfigInfo { + return ccip_config.CCIPConfigTypesChainConfigInfo{ + ChainSelector: chainSelector, + ChainConfig: ccip_config.CCIPConfigTypesChainConfig{ + Readers: readers, + FChain: fChain, + Config: cfg, + }, + } +} diff --git a/core/services/ocr3/plugins/ccipevm/commitcodec.go b/core/services/ocr3/plugins/ccipevm/commitcodec.go index f6a5ea93c5..928cecd0a4 100644 --- a/core/services/ocr3/plugins/ccipevm/commitcodec.go +++ b/core/services/ocr3/plugins/ccipevm/commitcodec.go @@ -2,22 +2,137 @@ package ccipevm import ( "context" + "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ) -var _ cciptypes.CommitPluginCodec = (*CommitPluginCodec)(nil) - -type CommitPluginCodec struct{} +// CommitPluginCodecV1 is a codec for encoding and decoding commit plugin reports. +// Compatible with: +// - "EVM2EVMMultiOffRamp 1.6.0-dev" +type CommitPluginCodecV1 struct { + commitReportAcceptedEventInputs abi.Arguments +} -func NewCommitPluginCodec() *CommitPluginCodec { - return &CommitPluginCodec{} +func NewCommitPluginCodecV1() *CommitPluginCodecV1 { + abiParsed, err := abi.JSON(strings.NewReader(evm_2_evm_multi_offramp.EVM2EVMMultiOffRampABI)) + if err != nil { + panic(fmt.Errorf("parse multi offramp abi: %s", err)) + } + eventInputs := abihelpers.MustGetEventInputs("CommitReportAccepted", abiParsed) + return &CommitPluginCodecV1{commitReportAcceptedEventInputs: eventInputs} } -func (c *CommitPluginCodec) Encode(ctx context.Context, report cciptypes.CommitPluginReport) ([]byte, error) { - panic("implement me") +func (c *CommitPluginCodecV1) Encode(ctx context.Context, report cciptypes.CommitPluginReport) ([]byte, error) { + merkleRoots := make([]evm_2_evm_multi_offramp.EVM2EVMMultiOffRampMerkleRoot, 0, len(report.MerkleRoots)) + for _, root := range report.MerkleRoots { + merkleRoots = append(merkleRoots, evm_2_evm_multi_offramp.EVM2EVMMultiOffRampMerkleRoot{ + SourceChainSelector: uint64(root.ChainSel), + Interval: evm_2_evm_multi_offramp.EVM2EVMMultiOffRampInterval{ + Min: uint64(root.SeqNumsRange.Start()), + Max: uint64(root.SeqNumsRange.End()), + }, + MerkleRoot: root.MerkleRoot, + }) + } + + tokenPriceUpdates := make([]evm_2_evm_multi_offramp.InternalTokenPriceUpdate, 0, len(report.PriceUpdates.TokenPriceUpdates)) + for _, update := range report.PriceUpdates.TokenPriceUpdates { + if !common.IsHexAddress(string(update.TokenID)) { + return nil, fmt.Errorf("invalid token address: %s", update.TokenID) + } + if update.Price.IsEmpty() { + return nil, fmt.Errorf("empty price for token: %s", update.TokenID) + } + tokenPriceUpdates = append(tokenPriceUpdates, evm_2_evm_multi_offramp.InternalTokenPriceUpdate{ + SourceToken: common.HexToAddress(string(update.TokenID)), + UsdPerToken: update.Price.Int, + }) + } + + gasPriceUpdates := make([]evm_2_evm_multi_offramp.InternalGasPriceUpdate, 0, len(report.PriceUpdates.GasPriceUpdates)) + for _, update := range report.PriceUpdates.GasPriceUpdates { + if update.GasPrice.IsEmpty() { + return nil, fmt.Errorf("empty gas price for chain: %d", update.ChainSel) + } + + gasPriceUpdates = append(gasPriceUpdates, evm_2_evm_multi_offramp.InternalGasPriceUpdate{ + DestChainSelector: uint64(update.ChainSel), + UsdPerUnitGas: update.GasPrice.Int, + }) + } + + evmReport := evm_2_evm_multi_offramp.EVM2EVMMultiOffRampCommitReport{ + PriceUpdates: evm_2_evm_multi_offramp.InternalPriceUpdates{ + TokenPriceUpdates: tokenPriceUpdates, + GasPriceUpdates: gasPriceUpdates, + }, + MerkleRoots: merkleRoots, + } + + return c.commitReportAcceptedEventInputs.PackValues([]interface{}{evmReport}) } -func (c *CommitPluginCodec) Decode(ctx context.Context, bytes []byte) (cciptypes.CommitPluginReport, error) { - panic("implement me") +func (c *CommitPluginCodecV1) Decode(ctx context.Context, bytes []byte) (cciptypes.CommitPluginReport, error) { + unpacked, err := c.commitReportAcceptedEventInputs.Unpack(bytes) + if err != nil { + return cciptypes.CommitPluginReport{}, err + } + if len(unpacked) != 1 { + return cciptypes.CommitPluginReport{}, fmt.Errorf("expected 1 argument, got %d", len(unpacked)) + } + + commitReportRaw := abi.ConvertType(unpacked[0], new(evm_2_evm_multi_offramp.EVM2EVMMultiOffRampCommitReport)) + commitReport, is := commitReportRaw.(*evm_2_evm_multi_offramp.EVM2EVMMultiOffRampCommitReport) + if !is { + return cciptypes.CommitPluginReport{}, + fmt.Errorf("expected EVM2EVMMultiOffRampCommitReport, got %T", unpacked[0]) + } + + merkleRoots := make([]cciptypes.MerkleRootChain, 0, len(commitReport.MerkleRoots)) + for _, root := range commitReport.MerkleRoots { + merkleRoots = append(merkleRoots, cciptypes.MerkleRootChain{ + ChainSel: cciptypes.ChainSelector(root.SourceChainSelector), + SeqNumsRange: cciptypes.NewSeqNumRange( + cciptypes.SeqNum(root.Interval.Min), + cciptypes.SeqNum(root.Interval.Max), + ), + MerkleRoot: root.MerkleRoot, + }) + } + + tokenPriceUpdates := make([]cciptypes.TokenPrice, 0, len(commitReport.PriceUpdates.TokenPriceUpdates)) + for _, update := range commitReport.PriceUpdates.TokenPriceUpdates { + tokenPriceUpdates = append(tokenPriceUpdates, cciptypes.TokenPrice{ + TokenID: types.Account(update.SourceToken.String()), + Price: cciptypes.NewBigInt(big.NewInt(0).Set(update.UsdPerToken)), + }) + } + + gasPriceUpdates := make([]cciptypes.GasPriceChain, 0, len(commitReport.PriceUpdates.GasPriceUpdates)) + for _, update := range commitReport.PriceUpdates.GasPriceUpdates { + gasPriceUpdates = append(gasPriceUpdates, cciptypes.GasPriceChain{ + GasPrice: cciptypes.NewBigInt(big.NewInt(0).Set(update.UsdPerUnitGas)), + ChainSel: cciptypes.ChainSelector(update.DestChainSelector), + }) + } + + return cciptypes.CommitPluginReport{ + MerkleRoots: merkleRoots, + PriceUpdates: cciptypes.PriceUpdates{ + TokenPriceUpdates: tokenPriceUpdates, + GasPriceUpdates: gasPriceUpdates, + }, + }, nil } + +// Ensure CommitPluginCodec implements the CommitPluginCodec interface +var _ cciptypes.CommitPluginCodec = (*CommitPluginCodecV1)(nil) diff --git a/core/services/ocr3/plugins/ccipevm/commitcodec_test.go b/core/services/ocr3/plugins/ccipevm/commitcodec_test.go new file mode 100644 index 0000000000..dffc9ff55e --- /dev/null +++ b/core/services/ocr3/plugins/ccipevm/commitcodec_test.go @@ -0,0 +1,135 @@ +package ccipevm + +import ( + "math/big" + "math/rand" + "testing" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" +) + +var randomReport = func() cciptypes.CommitPluginReport { + return cciptypes.CommitPluginReport{ + MerkleRoots: []cciptypes.MerkleRootChain{ + { + ChainSel: cciptypes.ChainSelector(rand.Uint64()), + SeqNumsRange: cciptypes.NewSeqNumRange( + cciptypes.SeqNum(rand.Uint64()), + cciptypes.SeqNum(rand.Uint64()), + ), + MerkleRoot: utils.RandomBytes32(), + }, + { + ChainSel: cciptypes.ChainSelector(rand.Uint64()), + SeqNumsRange: cciptypes.NewSeqNumRange( + cciptypes.SeqNum(rand.Uint64()), + cciptypes.SeqNum(rand.Uint64()), + ), + MerkleRoot: utils.RandomBytes32(), + }, + }, + PriceUpdates: cciptypes.PriceUpdates{ + TokenPriceUpdates: []cciptypes.TokenPrice{ + { + TokenID: types.Account(utils.RandomAddress().String()), + Price: cciptypes.NewBigInt(utils.RandUint256()), + }, + }, + GasPriceUpdates: []cciptypes.GasPriceChain{ + {GasPrice: cciptypes.NewBigInt(utils.RandUint256()), ChainSel: cciptypes.ChainSelector(rand.Uint64())}, + {GasPrice: cciptypes.NewBigInt(utils.RandUint256()), ChainSel: cciptypes.ChainSelector(rand.Uint64())}, + {GasPrice: cciptypes.NewBigInt(utils.RandUint256()), ChainSel: cciptypes.ChainSelector(rand.Uint64())}, + }, + }, + } +} + +func TestCommitPluginCodec(t *testing.T) { + testCases := []struct { + name string + report func(report cciptypes.CommitPluginReport) cciptypes.CommitPluginReport + expErr bool + }{ + { + name: "base report", + report: func(report cciptypes.CommitPluginReport) cciptypes.CommitPluginReport { + return report + }, + }, + { + name: "empty token address", + report: func(report cciptypes.CommitPluginReport) cciptypes.CommitPluginReport { + report.PriceUpdates.TokenPriceUpdates[0].TokenID = "" + return report + }, + expErr: true, + }, + { + name: "empty merkle root", + report: func(report cciptypes.CommitPluginReport) cciptypes.CommitPluginReport { + report.MerkleRoots[0].MerkleRoot = cciptypes.Bytes32{} + return report + }, + }, + { + name: "zero token price", + report: func(report cciptypes.CommitPluginReport) cciptypes.CommitPluginReport { + report.PriceUpdates.TokenPriceUpdates[0].Price = cciptypes.NewBigInt(big.NewInt(0)) + return report + }, + }, + { + name: "zero gas price", + report: func(report cciptypes.CommitPluginReport) cciptypes.CommitPluginReport { + report.PriceUpdates.GasPriceUpdates[0].GasPrice = cciptypes.NewBigInt(big.NewInt(0)) + return report + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + report := tc.report(randomReport()) + commitCodec := NewCommitPluginCodecV1() + ctx := testutils.Context(t) + encodedReport, err := commitCodec.Encode(ctx, report) + if tc.expErr { + assert.Error(t, err) + return + } + require.NoError(t, err) + decodedReport, err := commitCodec.Decode(ctx, encodedReport) + require.NoError(t, err) + require.Equal(t, report, decodedReport) + }) + } +} + +func BenchmarkCommitPluginCodec_Encode(b *testing.B) { + commitCodec := NewCommitPluginCodecV1() + ctx := testutils.Context(b) + + rep := randomReport() + for i := 0; i < b.N; i++ { + _, err := commitCodec.Encode(ctx, rep) + require.NoError(b, err) + } +} + +func BenchmarkCommitPluginCodec_Decode(b *testing.B) { + commitCodec := NewCommitPluginCodecV1() + ctx := testutils.Context(b) + encodedReport, err := commitCodec.Encode(ctx, randomReport()) + require.NoError(b, err) + + for i := 0; i < b.N; i++ { + _, err := commitCodec.Decode(ctx, encodedReport) + require.NoError(b, err) + } +} diff --git a/core/services/ocr3/plugins/ccipevm/msghasher.go b/core/services/ocr3/plugins/ccipevm/msghasher.go index 0b68ba4675..948775a7be 100644 --- a/core/services/ocr3/plugins/ccipevm/msghasher.go +++ b/core/services/ocr3/plugins/ccipevm/msghasher.go @@ -2,107 +2,132 @@ package ccipevm import ( "context" + "encoding/hex" "fmt" - "math/big" "strings" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" - "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_multi_onramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/message_hasher" +) + +var ( + // bytes32 internal constant LEAF_DOMAIN_SEPARATOR = 0x0000000000000000000000000000000000000000000000000000000000000000; + leafDomainSeparator = [32]byte{} + + // bytes32 internal constant ANY_2_EVM_MESSAGE_HASH = keccak256("Any2EVMMessageHashV1"); + ANY_2_EVM_MESSAGE_HASH = utils.Keccak256Fixed([]byte("Any2EVMMessageHashV1")) + + messageHasherABI = types.MustGetABI(message_hasher.MessageHasherABI) ) // MessageHasherV1 implements the MessageHasher interface. // Compatible with: // - "EVM2EVMMultiOnRamp 1.6.0-dev" type MessageHasherV1 struct { - metaDataHash [32]byte - leafDomainSeparator [32]byte - - // ABIs and types for encoding the message data similar to on-chain implementation: - // https://github.com/smartcontractkit/ccip/blob/54ee4f13143d3e414627b6a0b9f71d5dfade76c5/contracts/src/v0.8/ccip/libraries/Internal.sol#L135 - bytesArrayType abi.Type - tokensAbi abi.ABI - fixedSizeValuesAbi abi.ABI - packedValuesAbi abi.ABI + // TODO: move these to CCIPMsg instead? + destChainSelector cciptypes.ChainSelector + onrampAddress []byte } -func NewMessageHasherV1(metaDataHash [32]byte) *MessageHasherV1 { - bytesArray, err := abi.NewType("bytes[]", "bytes[]", nil) - if err != nil { - panic(fmt.Sprintf("failed to create bytes[] type: %v", err)) - } - +func NewMessageHasherV1( + onrampAddress []byte, + destChainSelector cciptypes.ChainSelector, +) *MessageHasherV1 { return &MessageHasherV1{ - metaDataHash: metaDataHash, - leafDomainSeparator: [32]byte{}, - - bytesArrayType: bytesArray, - tokensAbi: mustParseInputsAbi(`[{"components": [{"name":"token","type":"address"}, - {"name":"amount","type":"uint256"}], "type":"tuple[]"}]`), - fixedSizeValuesAbi: mustParseInputsAbi(`[{"name": "sender", "type":"address"}, - {"name": "receiver", "type":"address"}, - {"name": "sequenceNumber", "type":"uint64"}, - {"name": "gasLimit", "type":"uint256"}, - {"name": "strict", "type":"bool"}, - {"name": "nonce", "type":"uint64"}, - {"name": "feeToken","type": "address"}, - {"name": "feeTokenAmount","type": "uint256"}]`), - packedValuesAbi: mustParseInputsAbi(`[{"name": "leafDomainSeparator","type":"bytes32"}, - {"name": "metadataHash", "type":"bytes32"}, - {"name": "fixedSizeValuesHash", "type":"bytes32"}, - {"name": "dataHash", "type":"bytes32"}, - {"name": "tokenAmountsHash", "type":"bytes32"}, - {"name": "sourceTokenDataHash", "type":"bytes32"}]`), + destChainSelector: destChainSelector, + onrampAddress: onrampAddress, } } +// Hash implements the MessageHasher interface. +// It constructs all of the inputs to the final keccak256 hash in Internal._hash(Any2EVMRampMessage). +// The main structure of the hash is as follows: +/* + keccak256( + leafDomainSeparator, + keccak256(any_2_evm_message_hash, header.sourceChainSelector, header.destinationChainSelector, onRamp), + keccak256(fixedSizeMessageFields), + keccak256(messageData), + keccak256(encodedTokenAmounts), + keccak256(encodedSourceTokenData), + ) +*/ func (h *MessageHasherV1) Hash(_ context.Context, msg cciptypes.CCIPMsg) (cciptypes.Bytes32, error) { - type tokenAmount struct { - Token common.Address - Amount *big.Int - } - tokenAmounts := make([]tokenAmount, len(msg.TokenAmounts)) + tokenAmounts := make([]evm_2_evm_multi_onramp.ClientEVMTokenAmount, len(msg.TokenAmounts)) for i, ta := range msg.TokenAmounts { - tokenAmounts[i] = tokenAmount{ + tokenAmounts[i] = evm_2_evm_multi_onramp.ClientEVMTokenAmount{ Token: common.HexToAddress(string(ta.Token)), Amount: ta.Amount, } } - encodedTokens, err := h.abiEncode(h.tokensAbi, tokenAmounts) + encodedTokens, err := h.abiEncode("encodeTokenAmountsHashPreimage", tokenAmounts) if err != nil { return [32]byte{}, fmt.Errorf("abi encode token amounts: %w", err) } - encodedSourceTokenData, err := abi.Arguments{abi.Argument{Type: h.bytesArrayType}}. - PackValues([]interface{}{msg.SourceTokenData}) + encodedSourceTokenData, err := h.abiEncode("encodeSourceTokenDataHashPreimage", msg.SourceTokenData) if err != nil { return [32]byte{}, fmt.Errorf("pack source token data: %w", err) } - packedFixedSizeValues, err := h.abiEncode( - h.fixedSizeValuesAbi, - common.HexToAddress(string(msg.Sender)), + metaDataHashInput, err := h.abiEncode( + "encodeMetadataHashPreimage", + ANY_2_EVM_MESSAGE_HASH, + uint64(msg.SourceChain), + uint64(h.destChainSelector), + h.onrampAddress, + ) + if err != nil { + return [32]byte{}, fmt.Errorf("abi encode metadata hash input: %w", err) + } + + var msgID [32]byte + decoded, err := hex.DecodeString(msg.ID) + if err != nil { + return [32]byte{}, fmt.Errorf("decode message ID: %w", err) + } + if len(decoded) != 32 { + return [32]byte{}, fmt.Errorf("message ID must be 32 bytes") + } + copy(msgID[:], decoded) + + // NOTE: msg.Sender is not necessarily an EVM address since this is Any2EVM. + // Accordingly, sender is defined as "bytes" in the onchain message definition + // rather than "address". + // However, its not clear how best to translate from Sender being a string representation + // to bytes. For now, we assume that the string is hex encoded, but ideally Sender would + // just be a byte array in the CCIPMsg struct that represents a sender encoded in the + // source chain family encoding scheme. + decodedSender, err := hex.DecodeString( + strings.TrimPrefix(string(msg.Sender), "0x"), + ) + if err != nil { + return [32]byte{}, fmt.Errorf("decode sender '%s': %w", msg.Sender, err) + } + fixedSizeFieldsEncoded, err := h.abiEncode( + "encodeFixedSizeFieldsHashPreimage", + msgID, + decodedSender, common.HexToAddress(string(msg.Receiver)), uint64(msg.SeqNum), msg.ChainFeeLimit.Int, - msg.Strict, msg.Nonce, - common.HexToAddress(string(msg.FeeToken)), - msg.FeeTokenAmount.Int, ) if err != nil { return [32]byte{}, fmt.Errorf("abi encode fixed size values: %w", err) } - fixedSizeValuesHash := utils.Keccak256Fixed(packedFixedSizeValues) packedValues, err := h.abiEncode( - h.packedValuesAbi, - h.leafDomainSeparator, - h.metaDataHash, - fixedSizeValuesHash, + "encodeFinalHashPreimage", + leafDomainSeparator, + utils.Keccak256Fixed(metaDataHashInput), + utils.Keccak256Fixed(fixedSizeFieldsEncoded), utils.Keccak256Fixed(msg.Data), utils.Keccak256Fixed(encodedTokens), utils.Keccak256Fixed(encodedSourceTokenData), @@ -114,22 +139,14 @@ func (h *MessageHasherV1) Hash(_ context.Context, msg cciptypes.CCIPMsg) (ccipty return utils.Keccak256Fixed(packedValues), nil } -func (h *MessageHasherV1) abiEncode(theAbi abi.ABI, values ...interface{}) ([]byte, error) { - res, err := theAbi.Pack("method", values...) +func (h *MessageHasherV1) abiEncode(method string, values ...interface{}) ([]byte, error) { + res, err := messageHasherABI.Pack(method, values...) if err != nil { return nil, err } + // trim the method selector. return res[4:], nil } -func mustParseInputsAbi(s string) abi.ABI { - inDef := fmt.Sprintf(`[{ "name" : "method", "type": "function", "inputs": %s}]`, s) - inAbi, err := abi.JSON(strings.NewReader(inDef)) - if err != nil { - panic(fmt.Errorf("failed to create %s ABI: %v", s, err)) - } - return inAbi -} - // Interface compliance check var _ cciptypes.MessageHasher = (*MessageHasherV1)(nil) diff --git a/core/services/ocr3/plugins/ccipevm/msghasher_test.go b/core/services/ocr3/plugins/ccipevm/msghasher_test.go index 3a49d78f9a..0bdf2c04b9 100644 --- a/core/services/ocr3/plugins/ccipevm/msghasher_test.go +++ b/core/services/ocr3/plugins/ccipevm/msghasher_test.go @@ -1,6 +1,7 @@ package ccipevm import ( + "context" cryptorand "crypto/rand" "encoding/hex" "fmt" @@ -13,42 +14,85 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/crypto" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/message_hasher" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestMessageHasher_e2e(t *testing.T) { - // Deploy messageHasher contract ctx := testutils.Context(t) d := testSetup(t) + // low budget "fuzz" test. + // TODO: should actually write a real fuzz test. + for i := 0; i < 5; i++ { + testHasher(ctx, t, d) + } +} + +func testHasher(ctx context.Context, t *testing.T, d *testSetupData) { + destChainSelector := rand.Uint64() + onRampAddress := testutils.NewAddress().Bytes() + ccipMsg := createCCIPMsg(t) + + evmTokenAmounts := make([]message_hasher.ClientEVMTokenAmount, 0, len(ccipMsg.TokenAmounts)) + for _, ta := range ccipMsg.TokenAmounts { + evmTokenAmounts = append(evmTokenAmounts, message_hasher.ClientEVMTokenAmount{ + Token: common.HexToAddress(string(ta.Token)), + Amount: ta.Amount, + }) + } + evmMsg := message_hasher.InternalAny2EVMRampMessage{ + Header: message_hasher.InternalRampMessageHeader{ + MessageId: mustMessageID(t, ccipMsg.ID), + SourceChainSelector: uint64(ccipMsg.SourceChain), + DestChainSelector: destChainSelector, + SequenceNumber: uint64(ccipMsg.SeqNum), + Nonce: ccipMsg.Nonce, + }, + Sender: common.HexToAddress(string(ccipMsg.Sender)).Bytes(), + Receiver: common.HexToAddress(string(ccipMsg.Receiver)), + GasLimit: ccipMsg.ChainFeeLimit.Int, + Data: ccipMsg.Data, + TokenAmounts: evmTokenAmounts, + SourceTokenData: ccipMsg.SourceTokenData, + } + + expectedHash, err := d.contract.Hash(&bind.CallOpts{Context: ctx}, evmMsg, onRampAddress) + require.NoError(t, err) + + evmMsgHasher := NewMessageHasherV1(onRampAddress, cciptypes.ChainSelector(destChainSelector)) + actualHash, err := evmMsgHasher.Hash(ctx, ccipMsg) + require.NoError(t, err) + + require.Equal(t, fmt.Sprintf("%x", expectedHash), strings.TrimPrefix(actualHash.String(), "0x")) +} + +// TODO: fix this once messageID is part of CCIPMsg +func createCCIPMsg(t *testing.T) cciptypes.CCIPMsg { // Setup random msg data - metadataHash := utils.RandomBytes32() + messageID := utils.RandomBytes32() sourceTokenData := make([]byte, rand.Intn(2048)) _, err := cryptorand.Read(sourceTokenData) - assert.NoError(t, err) + require.NoError(t, err) sourceChain := rand.Uint64() seqNum := rand.Uint64() chainFeeLimit := rand.Uint64() nonce := rand.Uint64() - strict := rand.Intn(2) == 1 - feeTokenAmount := rand.Uint64() - data := make([]byte, rand.Intn(2048)) - _, err = cryptorand.Read(data) - assert.NoError(t, err) + messageData := make([]byte, rand.Intn(2048)) + _, err = cryptorand.Read(messageData) + require.NoError(t, err) sourceTokenDatas := make([][]byte, rand.Intn(10)) for i := range sourceTokenDatas { @@ -63,53 +107,34 @@ func TestMessageHasher_e2e(t *testing.T) { Amount: big.NewInt(0).SetUint64(rand.Uint64()), }) } - ccipMsg := cciptypes.CCIPMsg{ + return cciptypes.CCIPMsg{ CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ + ID: hex.EncodeToString(messageID[:]), SourceChain: cciptypes.ChainSelector(sourceChain), SeqNum: cciptypes.SeqNum(seqNum), }, - ChainFeeLimit: cciptypes.NewBigInt(big.NewInt(0).SetUint64(chainFeeLimit)), - Nonce: nonce, - Sender: types.Account(utils.RandomAddress().String()), - Receiver: types.Account(utils.RandomAddress().String()), - Strict: strict, - FeeToken: types.Account(utils.RandomAddress().String()), - FeeTokenAmount: cciptypes.NewBigInt(big.NewInt(0).SetUint64(feeTokenAmount)), - Data: data, + ChainFeeLimit: cciptypes.NewBigInt(big.NewInt(0).SetUint64(chainFeeLimit)), + Nonce: nonce, + Sender: types.Account(utils.RandomAddress().String()), + Receiver: types.Account(utils.RandomAddress().String()), + // TODO: remove this field if not needed, not used by the hasher + // Strict: strict, + // NOTE: not used by the hasher + // FeeToken: types.Account(utils.RandomAddress().String()), + // FeeTokenAmount: cciptypes.NewBigInt(big.NewInt(0).SetUint64(feeTokenAmount)), + Data: messageData, TokenAmounts: tokenAmounts, SourceTokenData: sourceTokenDatas, } +} - evmTokenAmounts := make([]message_hasher.ClientEVMTokenAmount, 0, len(ccipMsg.TokenAmounts)) - for _, ta := range ccipMsg.TokenAmounts { - evmTokenAmounts = append(evmTokenAmounts, message_hasher.ClientEVMTokenAmount{ - Token: common.HexToAddress(string(ta.Token)), - Amount: ta.Amount, - }) - } - evmMsg := message_hasher.InternalEVM2EVMMessage{ - SourceChainSelector: uint64(ccipMsg.SourceChain), - Sender: common.HexToAddress(string(ccipMsg.Sender)), - Receiver: common.HexToAddress(string(ccipMsg.Receiver)), - SequenceNumber: uint64(ccipMsg.SeqNum), - GasLimit: ccipMsg.ChainFeeLimit.Int, - Strict: ccipMsg.Strict, - Nonce: ccipMsg.Nonce, - FeeToken: common.HexToAddress(string(ccipMsg.FeeToken)), - FeeTokenAmount: ccipMsg.FeeTokenAmount.Int, - Data: ccipMsg.Data, - TokenAmounts: evmTokenAmounts, - SourceTokenData: ccipMsg.SourceTokenData, - } - - h, err := d.contract.Hash(&bind.CallOpts{Context: ctx}, evmMsg, metadataHash) - assert.NoError(t, err) - - evmMsgHasher := NewMessageHasherV1(metadataHash) - h2, err := evmMsgHasher.Hash(ctx, ccipMsg) - assert.NoError(t, err) - - assert.Equal(t, fmt.Sprintf("%x", h), strings.TrimPrefix(h2.String(), "0x")) +func mustMessageID(t *testing.T, msgIDHex string) [32]byte { + msgID, err := hex.DecodeString(msgIDHex) + require.NoError(t, err) + require.Len(t, msgID, 32) + var msgID32 [32]byte + copy(msgID32[:], msgID) + return msgID32 } type testSetupData struct { @@ -119,211 +144,25 @@ type testSetupData struct { auth *bind.TransactOpts } -const chainID = 1337 - func testSetup(t *testing.T) *testSetupData { - // Generate a new key pair for the simulated account - privateKey, err := crypto.GenerateKey() - assert.NoError(t, err) - // Set up the genesis account with balance - blnc, ok := big.NewInt(0).SetString("999999999999999999999999999999999999", 10) - assert.True(t, ok) - alloc := map[common.Address]core.GenesisAccount{crypto.PubkeyToAddress(privateKey.PublicKey): {Balance: blnc}} - simulatedBackend := backends.NewSimulatedBackend(alloc, 0) - // Create a transactor - - auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(chainID)) - assert.NoError(t, err) - auth.GasLimit = uint64(0) + transactor := testutils.MustNewSimTransactor(t) + simulatedBackend := backends.NewSimulatedBackend(core.GenesisAlloc{ + transactor.From: {Balance: assets.Ether(1000).ToInt()}, + }, 30e6) // Deploy the contract - address, _, _, err := message_hasher.DeployMessageHasher(auth, simulatedBackend) - assert.NoError(t, err) + address, _, _, err := message_hasher.DeployMessageHasher(transactor, simulatedBackend) + require.NoError(t, err) simulatedBackend.Commit() // Setup contract client contract, err := message_hasher.NewMessageHasher(address, simulatedBackend) - assert.NoError(t, err) + require.NoError(t, err) return &testSetupData{ contractAddr: address, contract: contract, sb: simulatedBackend, - auth: auth, - } -} - -func TestMessageHasher_Hash(t *testing.T) { - ctx := testutils.Context(t) - - largeNumber, ok := big.NewInt(0).SetString("1000000000000000000", 10) // 1e18 - require.True(t, ok) - - msgData, err := hex.DecodeString("64617461") - require.NoError(t, err) - - sourceTokenData1, err := hex.DecodeString("000000000000000000000000000000000000000000000000000000000000002000" + - "000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000" + - "0000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000" + - "0000000000000000000000000000000000000000000200000000000000000000000009e7218a11a2cda657ae50bd9cc5f953174aa" + - "e2a50000000000000000000000000000000000000000000000000000000000000020000000000000000000000000e4eebe19216af8" + - "6b9a996f53bd80b8365f832be80000000000000000000000000000000000000000000000000000000000000000") - require.NoError(t, err) - - sourceTokenData2, err := hex.DecodeString("000000000000000000000000000000000000000000000000000000000000002" + - "00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000" + - "00000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000" + - "000000000000000000000000000000000000000000000020000000000000000000000000e2c2bb2f43b91f65b5519708e34031039" + - "4c72d8f0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000bd2f7046d10" + - "59abfe5316b48f050684a4676710f0000000000000000000000000000000000000000000000000000000000000000") - require.NoError(t, err) - - // metadataHash used in this test is copied from on-chain tests - // keccak256(abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, i_chainSelector, destChainSelector, address(this))) - metadataHash := [32]byte{39, 130, 244, 70, 94, 31, 113, 169, 251, 136, 123, 6, 255, 77, 50, 91, 73, - 144, 94, 70, 13, 16, 47, 1, 171, 201, 40, 185, 144, 12, 103, 129} - - testCases := []struct { - name string - msg cciptypes.CCIPMsg - exp string - expErr bool - }{ - { - name: "empty msg", - msg: cciptypes.CCIPMsg{ - ChainFeeLimit: cciptypes.NewBigIntFromInt64(0), - FeeTokenAmount: cciptypes.NewBigIntFromInt64(0), - }, - exp: "0x3682d9965b91efa44c6274446a362dca2ea526bf3858bf54d52ec56be716f6be", - expErr: false, - }, - { - name: "base msg", - msg: cciptypes.CCIPMsg{ - CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ - SourceChain: 1, - SeqNum: 1, - }, - ChainFeeLimit: cciptypes.NewBigIntFromInt64(400000), - Nonce: 1, - Sender: "0x00007e64E1fB0C487F25dd6D3601ff6aF8d32e4e", - Receiver: "0x00007e64E1fB0C487F25dd6D3601ff6aF8d32e4e", - Strict: false, - FeeToken: "0xcE4ec7b524851E51d5C55eeFbBb8E58E8Ce2515F", - FeeTokenAmount: cciptypes.NewBigIntFromInt64(1234567890), - Data: []byte{}, - TokenAmounts: []cciptypes.TokenAmount{}, - SourceTokenData: [][]byte{}, - Metadata: cciptypes.CCIPMsgMetadata{}, - }, - exp: "0x23bf76c493e9bf58346b7cac0e9f357f5879f3d673819e5f27fc443cf9c907b9", - expErr: false, - }, - { - name: "full msg", - msg: cciptypes.CCIPMsg{ - CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ - SourceChain: 1, - SeqNum: 1, - }, - ChainFeeLimit: cciptypes.NewBigIntFromInt64(400000), - Nonce: 1, - Sender: "0x00007e64E1fB0C487F25dd6D3601ff6aF8d32e4e", - Receiver: "0x00007e64E1fB0C487F25dd6D3601ff6aF8d32e4e", - Strict: false, - FeeToken: "0xcE4ec7b524851E51d5C55eeFbBb8E58E8Ce2515F", - FeeTokenAmount: cciptypes.NewBigIntFromInt64(1234567890), - Data: msgData, - TokenAmounts: []cciptypes.TokenAmount{ - { - Token: "0xcE4ec7b524851E51d5C55eeFbBb8E58E8Ce2515F", - Amount: largeNumber, - }, - }, - SourceTokenData: [][]byte{sourceTokenData1}, - Metadata: cciptypes.CCIPMsgMetadata{}, - }, - exp: "0xe04ade4e6a1121155ca1e89f17c9df6c9236fdcfdf38b97594594d0540345d60", - expErr: false, - }, - { - name: "full msg 2 - two source token data items", - msg: cciptypes.CCIPMsg{ - CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ - SourceChain: 1, - SeqNum: 1, - }, - ChainFeeLimit: cciptypes.NewBigIntFromInt64(400000), - Nonce: 1, - Sender: "0x00007e64E1fB0C487F25dd6D3601ff6aF8d32e4e", - Receiver: "0x00007e64E1fB0C487F25dd6D3601ff6aF8d32e4e", - Strict: false, - FeeToken: "0xcE4ec7b524851E51d5C55eeFbBb8E58E8Ce2515F", - FeeTokenAmount: cciptypes.NewBigIntFromInt64(1234567890), - Data: msgData, - TokenAmounts: []cciptypes.TokenAmount{ - { - Token: "0xcE4ec7b524851E51d5C55eeFbBb8E58E8Ce2515F", - Amount: largeNumber, - }, - { - Token: "0x3c78e47de47B765dcEE2F30F31B3CF5F10B42d1F", - Amount: largeNumber, - }, - }, - SourceTokenData: [][]byte{sourceTokenData1, sourceTokenData2}, - Metadata: cciptypes.CCIPMsgMetadata{}, - }, - exp: "0x30123234e5d9e0cd94610e83be2e0128167b9ab072e8e9450f1f7704b9901589", - expErr: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - m := NewMessageHasherV1(metadataHash) - hash, err := m.Hash(ctx, tc.msg) - if tc.expErr { - assert.Error(t, err) - return - } - - assert.NoError(t, err) - assert.Equal(t, tc.exp, hash.String()) - }) - } -} - -func BenchmarkMessageHasher_Hash(b *testing.B) { - ctx := testutils.Context(b) - msg := cciptypes.CCIPMsg{ - CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{ - SourceChain: 1, - SeqNum: 1, - }, - ChainFeeLimit: cciptypes.NewBigIntFromInt64(400000), - Nonce: 1, - Sender: types.Account(utils.RandomAddress().String()), - Receiver: types.Account(utils.RandomAddress().String()), - Strict: false, - FeeToken: types.Account(utils.RandomAddress().String()), - FeeTokenAmount: cciptypes.NewBigIntFromInt64(1234567890), - Data: make([]byte, 2048), - TokenAmounts: []cciptypes.TokenAmount{ - { - Token: types.Account(utils.RandomAddress().String()), - Amount: utils.RandUint256(), - }, - }, - SourceTokenData: [][]byte{make([]byte, 2048)}, - } - metadataHash := utils.RandomBytes32() - - m := NewMessageHasherV1(metadataHash) - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := m.Hash(ctx, msg) - require.NoError(b, err) + auth: transactor, } } diff --git a/go.md b/go.md index dda7192c1a..eeff43e365 100644 --- a/go.md +++ b/go.md @@ -28,6 +28,8 @@ flowchart LR click chain-selectors href "https://github.com/smartcontractkit/chain-selectors" chainlink/v2 --> chainlink-automation click chainlink-automation href "https://github.com/smartcontractkit/chainlink-automation" + chainlink/v2 --> chainlink-ccip + click chainlink-ccip href "https://github.com/smartcontractkit/chainlink-ccip" chainlink/v2 --> chainlink-common click chainlink-common href "https://github.com/smartcontractkit/chainlink-common" chainlink/v2 --> chainlink-cosmos @@ -52,6 +54,8 @@ flowchart LR click wsrpc href "https://github.com/smartcontractkit/wsrpc" chainlink-automation --> chainlink-common chainlink-automation --> libocr + chainlink-ccip --> chainlink-common + chainlink-ccip --> libocr chainlink-common --> libocr chainlink-cosmos --> chainlink-common chainlink-cosmos --> libocr diff --git a/go.mod b/go.mod index 2e683ad3ee..89da04f5b0 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/cometbft/cometbft v0.37.5 github.com/cosmos/cosmos-sdk v0.47.11 github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e - github.com/deckarep/golang-set/v2 v2.3.0 + github.com/deckarep/golang-set/v2 v2.6.0 github.com/dominikbraun/graph v0.23.0 github.com/esote/minmaxheap v1.0.0 github.com/ethereum/go-ethereum v1.13.8 @@ -74,6 +74,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chain-selectors v1.0.17 github.com/smartcontractkit/chainlink-automation v1.0.4 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20240702134133-a6d2f429671f github.com/smartcontractkit/chainlink-common v0.1.7-0.20240708041125-a5403d128ee4 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 diff --git a/go.sum b/go.sum index dff82c986c..cbf872b997 100644 --- a/go.sum +++ b/go.sum @@ -284,8 +284,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g= -github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= @@ -1060,6 +1060,8 @@ github.com/smartcontractkit/chain-selectors v1.0.17 h1:otOlYUnutS8oQBEAi9RLQICqZ github.com/smartcontractkit/chain-selectors v1.0.17/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-20240702134133-a6d2f429671f h1:ohB9VgjJ+cvfOSzVEykZqpB/wGlzhzy3IjtdG3ZiO9E= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240702134133-a6d2f429671f/go.mod h1:vy2vEF1K61khqGcbOCrHBKFPvSQW0O1eg19Sv74Xq/4= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240708041125-a5403d128ee4 h1:NrNZFMNvUJpaeRIWO6Zdd1uvXYriM0l8jd//rBWbw/8= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240708041125-a5403d128ee4/go.mod h1:L32xvCpk84Nglit64OhySPMP1tM3TTBK7Tw0qZl7Sd4= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141 h1:TMOoYaeSDkkI3jkCH7lKHOZaLkeDuxFTNC+XblD6M0M= diff --git a/integration-tests/ccip-tests/actions/ccip_helpers.go b/integration-tests/ccip-tests/actions/ccip_helpers.go index 88cd28876c..e2b9b70871 100644 --- a/integration-tests/ccip-tests/actions/ccip_helpers.go +++ b/integration-tests/ccip-tests/actions/ccip_helpers.go @@ -397,15 +397,9 @@ func (ccipModule *CCIPCommon) ApproveTokens() error { } } - allowance, err := token.Allowance(ccipModule.ChainClient.GetDefaultWallet().Address(), ccipModule.Router.Address()) + err := token.Approve(ccipModule.ChainClient.GetDefaultWallet(), ccipModule.Router.Address(), ApprovedAmountToRouter) if err != nil { - return fmt.Errorf("failed to get allowance for token %s: %w", token.ContractAddress.Hex(), err) - } - if allowance.Cmp(ApprovedAmountToRouter) < 0 { - err := token.Approve(ccipModule.ChainClient.GetDefaultWallet(), ccipModule.Router.Address(), ApprovedAmountToRouter) - if err != nil { - return fmt.Errorf("failed to approve token %s: %w", token.ContractAddress.Hex(), err) - } + return fmt.Errorf("failed to approve token %s: %w", token.ContractAddress.Hex(), err) } if token.ContractAddress == ccipModule.FeeToken.EthAddress { isApproved = true @@ -1564,23 +1558,43 @@ func (sourceCCIP *SourceCCIPModule) UpdateBalance( func (sourceCCIP *SourceCCIPModule) AssertSendRequestedLogFinalized( lggr *zerolog.Logger, txHash common.Hash, + sendReqData []*contracts.SendReqEventData, prevEventAt time.Time, reqStats []*testreporters.RequestStat, ) (time.Time, uint64, error) { + if len(sendReqData) != len(reqStats) { + return time.Time{}, 0, fmt.Errorf("sendReqData and reqStats length mismatch") + } + var gasUsed uint64 + receipt, err := sourceCCIP.Common.ChainClient.GetTxReceipt(txHash) + if err == nil { + gasUsed = receipt.GasUsed + } lggr.Info().Msg("Waiting for CCIPSendRequested event log to be finalized") finalizedBlockNum, finalizedAt, err := sourceCCIP.Common.ChainClient.WaitForFinalizedTx(txHash) if err != nil || finalizedBlockNum == nil { - for _, stat := range reqStats { - stat.UpdateState(lggr, stat.SeqNum, testreporters.SourceLogFinalized, time.Since(prevEventAt), testreporters.Failure) + for i, stat := range reqStats { + stat.UpdateState(lggr, stat.SeqNum, testreporters.SourceLogFinalized, time.Since(prevEventAt), testreporters.Failure, &testreporters.TransactionStats{ + MsgID: fmt.Sprintf("0x%x", sendReqData[i].MessageId[:]), + Fee: sendReqData[i].Fee.String(), + NoOfTokensSent: sendReqData[i].NoOfTokens, + MessageBytesLength: int64(sendReqData[i].DataLength), + TxHash: txHash.Hex(), + }) } return time.Time{}, 0, fmt.Errorf("error waiting for CCIPSendRequested event log to be finalized - %w", err) } - for _, stat := range reqStats { + for i, stat := range reqStats { stat.UpdateState(lggr, stat.SeqNum, testreporters.SourceLogFinalized, finalizedAt.Sub(prevEventAt), testreporters.Success, - testreporters.TransactionStats{ - TxHash: txHash.Hex(), - FinalizedByBlock: finalizedBlockNum.String(), - FinalizedAt: finalizedAt.String(), + &testreporters.TransactionStats{ + MsgID: fmt.Sprintf("0x%x", sendReqData[i].MessageId[:]), + Fee: sendReqData[i].Fee.String(), + GasUsed: gasUsed, + NoOfTokensSent: sendReqData[i].NoOfTokens, + MessageBytesLength: int64(sendReqData[i].DataLength), + TxHash: txHash.Hex(), + FinalizedByBlock: finalizedBlockNum.String(), + FinalizedAt: finalizedAt.String(), }) } return finalizedAt, finalizedBlockNum.Uint64(), nil @@ -1639,13 +1653,7 @@ func (sourceCCIP *SourceCCIPModule) AssertEventCCIPSendRequested( Str("MsgID", fmt.Sprintf("0x%x", sendRequestedEvent.MessageId[:])). Logger()) // prevEventAt is the time when the message was successful, this should be same as the time when the event was emitted - reqStat[i].UpdateState(lggr, seqNum, testreporters.CCIPSendRe, 0, testreporters.Success, - testreporters.TransactionStats{ - MsgID: fmt.Sprintf("0x%x", sendRequestedEvent.MessageId[:]), - TxHash: "", - NoOfTokensSent: sendRequestedEvent.NoOfTokens, - MessageBytesLength: int64(sendRequestedEvent.DataLength), - }) + reqStat[i].UpdateState(lggr, seqNum, testreporters.CCIPSendRe, 0, testreporters.Success, nil) } var err error if len(sendRequestedEvents) == 0 { @@ -1659,7 +1667,10 @@ func (sourceCCIP *SourceCCIPModule) AssertEventCCIPSendRequested( if sourceCCIP.Common.IsConnectionRestoredRecently != nil && !sourceCCIP.Common.IsConnectionRestoredRecently.Load() { if resetTimer > 2 { for _, stat := range reqStat { - stat.UpdateState(lggr, 0, testreporters.CCIPSendRe, time.Since(prevEventAt), testreporters.Failure) + stat.UpdateState(lggr, 0, testreporters.CCIPSendRe, time.Since(prevEventAt), testreporters.Failure, + &testreporters.TransactionStats{ + TxHash: txHash, + }) } return nil, time.Now(), fmt.Errorf("possible RPC issue - CCIPSendRequested event is not found for tx %s", txHash) } @@ -1669,7 +1680,10 @@ func (sourceCCIP *SourceCCIPModule) AssertEventCCIPSendRequested( continue } for _, stat := range reqStat { - stat.UpdateState(lggr, 0, testreporters.CCIPSendRe, time.Since(prevEventAt), testreporters.Failure) + stat.UpdateState(lggr, 0, testreporters.CCIPSendRe, time.Since(prevEventAt), testreporters.Failure, + &testreporters.TransactionStats{ + TxHash: txHash, + }) } return nil, time.Now(), fmt.Errorf("CCIPSendRequested event is not found for tx %s", txHash) } @@ -2292,7 +2306,7 @@ func (destCCIP *DestCCIPModule) AssertEventExecutionStateChanged( lggr.Info().Int64("seqNum", int64(seqNum)).Uint8("ExecutionState", e.State).Msg("ExecutionStateChanged event received") reqStat.UpdateState(lggr, seqNum, testreporters.ExecStateChanged, receivedAt.Sub(timeNow), testreporters.Success, - testreporters.TransactionStats{ + &testreporters.TransactionStats{ TxHash: vLogs.TxHash.Hex(), MsgID: fmt.Sprintf("0x%x", e.MessageId[:]), GasUsed: gasUsed, @@ -2300,7 +2314,7 @@ func (destCCIP *DestCCIPModule) AssertEventExecutionStateChanged( ) return e.State, nil } - reqStat.UpdateState(lggr, seqNum, testreporters.ExecStateChanged, time.Since(timeNow), testreporters.Failure) + reqStat.UpdateState(lggr, seqNum, testreporters.ExecStateChanged, time.Since(timeNow), testreporters.Failure, nil) return e.State, fmt.Errorf("ExecutionStateChanged event state - expected %d actual - %d with data %x for seq num %v for lane %d-->%d", execState, testhelpers.MessageExecutionState(e.State), e.ReturnData, seqNum, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) } @@ -2310,7 +2324,7 @@ func (destCCIP *DestCCIPModule) AssertEventExecutionStateChanged( if destCCIP.Common.IsConnectionRestoredRecently != nil && !destCCIP.Common.IsConnectionRestoredRecently.Load() { // if timer already has been reset 2 times we fail with warning if resetTimer > 2 { - reqStat.UpdateState(lggr, seqNum, testreporters.ExecStateChanged, time.Since(timeNow), testreporters.Failure) + reqStat.UpdateState(lggr, seqNum, testreporters.ExecStateChanged, time.Since(timeNow), testreporters.Failure, nil) return 0, fmt.Errorf("possible RPC issues - ExecutionStateChanged event not found for seq num %d for lane %d-->%d", seqNum, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) } @@ -2319,7 +2333,7 @@ func (destCCIP *DestCCIPModule) AssertEventExecutionStateChanged( lggr.Info().Int("count of reset", resetTimer).Msg("Resetting timer to validate ExecutionStateChanged event") continue } - reqStat.UpdateState(lggr, seqNum, testreporters.ExecStateChanged, time.Since(timeNow), testreporters.Failure) + reqStat.UpdateState(lggr, seqNum, testreporters.ExecStateChanged, time.Since(timeNow), testreporters.Failure, nil) return 0, fmt.Errorf("ExecutionStateChanged event not found for seq num %d for lane %d-->%d", seqNum, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) } @@ -2377,7 +2391,7 @@ func (destCCIP *DestCCIPModule) AssertEventReportAccepted( gasUsed = receipt.GasUsed } reqStat.UpdateState(lggr, seqNum, testreporters.Commit, totalTime, testreporters.Success, - testreporters.TransactionStats{ + &testreporters.TransactionStats{ GasUsed: gasUsed, TxHash: reportAccepted.Raw.TxHash.String(), CommitRoot: fmt.Sprintf("%x", reportAccepted.MerkleRoot), @@ -2389,7 +2403,7 @@ func (destCCIP *DestCCIPModule) AssertEventReportAccepted( // if there is connection issue reset the context : if destCCIP.Common.IsConnectionRestoredRecently != nil && !destCCIP.Common.IsConnectionRestoredRecently.Load() { if resetTimerCount > 2 { - reqStat.UpdateState(lggr, seqNum, testreporters.Commit, time.Since(prevEventAt), testreporters.Failure) + reqStat.UpdateState(lggr, seqNum, testreporters.Commit, time.Since(prevEventAt), testreporters.Failure, nil) return nil, time.Now().UTC(), fmt.Errorf("possible RPC issue - ReportAccepted is not found for seq num %d lane %d-->%d", seqNum, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) } @@ -2398,7 +2412,7 @@ func (destCCIP *DestCCIPModule) AssertEventReportAccepted( lggr.Info().Int("count of reset", resetTimerCount).Msg("Resetting timer to validate ReportAccepted event") continue } - reqStat.UpdateState(lggr, seqNum, testreporters.Commit, time.Since(prevEventAt), testreporters.Failure) + reqStat.UpdateState(lggr, seqNum, testreporters.Commit, time.Since(prevEventAt), testreporters.Failure, nil) return nil, time.Now().UTC(), fmt.Errorf("ReportAccepted is not found for seq num %d lane %d-->%d", seqNum, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) } @@ -2471,7 +2485,7 @@ func (destCCIP *DestCCIPModule) AssertReportBlessed( gasUsed = receipt.GasUsed } reqStat.UpdateState(lggr, seqNum, testreporters.ReportBlessed, receivedAt.Sub(prevEventAt), testreporters.Success, - testreporters.TransactionStats{ + &testreporters.TransactionStats{ GasUsed: gasUsed, TxHash: vLogs.TxHash.String(), CommitRoot: fmt.Sprintf("%x", CommitReport.MerkleRoot), @@ -2483,7 +2497,7 @@ func (destCCIP *DestCCIPModule) AssertReportBlessed( // if there is connection issue reset the context : if destCCIP.Common.IsConnectionRestoredRecently != nil && !destCCIP.Common.IsConnectionRestoredRecently.Load() { if resetTimerCount > 2 { - reqStat.UpdateState(lggr, seqNum, testreporters.ReportBlessed, time.Since(prevEventAt), testreporters.Failure) + reqStat.UpdateState(lggr, seqNum, testreporters.ReportBlessed, time.Since(prevEventAt), testreporters.Failure, nil) return time.Now().UTC(), fmt.Errorf("possible RPC issue - ReportBlessed is not found for interval min - %d max - %d lane %d-->%d", CommitReport.Min, CommitReport.Max, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) } @@ -2492,7 +2506,7 @@ func (destCCIP *DestCCIPModule) AssertReportBlessed( lggr.Info().Int("count of reset", resetTimerCount).Msg("Resetting timer to validate ReportBlessed event") continue } - reqStat.UpdateState(lggr, seqNum, testreporters.ReportBlessed, time.Since(prevEventAt), testreporters.Failure) + reqStat.UpdateState(lggr, seqNum, testreporters.ReportBlessed, time.Since(prevEventAt), testreporters.Failure, nil) return time.Now().UTC(), fmt.Errorf("ReportBlessed is not found for interval min - %d max - %d lane %d-->%d", CommitReport.Min, CommitReport.Max, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) } @@ -2531,7 +2545,7 @@ func (destCCIP *DestCCIPModule) AssertSeqNumberExecuted( // if there is connection issue reset the context : if destCCIP.Common.IsConnectionRestoredRecently != nil && !destCCIP.Common.IsConnectionRestoredRecently.Load() { if resetTimerCount > 2 { - reqStat.UpdateState(lggr, seqNumberBefore, testreporters.Commit, time.Since(timeNow), testreporters.Failure) + reqStat.UpdateState(lggr, seqNumberBefore, testreporters.Commit, time.Since(timeNow), testreporters.Failure, nil) return fmt.Errorf("possible RPC issue - sequence number is not increased for seq num %d lane %d-->%d", seqNumberBefore, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) } @@ -2540,7 +2554,7 @@ func (destCCIP *DestCCIPModule) AssertSeqNumberExecuted( lggr.Info().Int("count of reset", resetTimerCount).Msg("Resetting timer to validate seqnumber increase in commit store") continue } - reqStat.UpdateState(lggr, seqNumberBefore, testreporters.Commit, time.Since(timeNow), testreporters.Failure) + reqStat.UpdateState(lggr, seqNumberBefore, testreporters.Commit, time.Since(timeNow), testreporters.Failure, nil) return fmt.Errorf("sequence number is not increased for seq num %d lane %d-->%d", seqNumberBefore, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) } @@ -2739,7 +2753,7 @@ func (lane *CCIPLane) AddToSentReqs(txHash common.Hash, reqStats []*testreporter request, rcpt, err := CCIPRequestFromTxHash(txHash, lane.Source.Common.ChainClient) if err != nil { for _, stat := range reqStats { - stat.UpdateState(lane.Logger, 0, testreporters.TX, 0, testreporters.Failure) + stat.UpdateState(lane.Logger, 0, testreporters.TX, 0, testreporters.Failure, nil) } return rcpt, fmt.Errorf("could not get request from tx hash %s: %w", txHash.Hex(), err) } @@ -2771,7 +2785,7 @@ func (lane *CCIPLane) Multicall(noOfRequests int, multiSendAddr common.Address) return fmt.Errorf("failed getting the chain selector: %w", err) } var reqStats []*testreporters.RequestStat - var txstats []testreporters.TransactionStats + var txstats []*testreporters.TransactionStats for i := 1; i <= noOfRequests; i++ { // form the message for transfer msg := genericMsg @@ -2807,7 +2821,7 @@ func (lane *CCIPLane) Multicall(noOfRequests int, multiSendAddr common.Address) } } stat := testreporters.NewCCIPRequestStats(int64(lane.NumberOfReq+i), lane.SourceNetworkName, lane.DestNetworkName) - txstats = append(txstats, testreporters.TransactionStats{ + txstats = append(txstats, &testreporters.TransactionStats{ Fee: fee.String(), NoOfTokensSent: len(msg.TokenAmounts), MessageBytesLength: int64(len(msg.Data)), @@ -2828,7 +2842,7 @@ func (lane *CCIPLane) Multicall(noOfRequests int, multiSendAddr common.Address) if err != nil { // update the stats as failure for all the requests in the multicall tx for _, stat := range reqStats { - stat.UpdateState(lane.Logger, 0, testreporters.TX, 0, testreporters.Failure) + stat.UpdateState(lane.Logger, 0, testreporters.TX, 0, testreporters.Failure, nil) } return fmt.Errorf("failed to send the multicall: %w", err) } @@ -2859,12 +2873,12 @@ func (lane *CCIPLane) SendRequests(noOfRequests int, gasLimit *big.Int) error { gasLimit, ) if err != nil { - stat.UpdateState(lane.Logger, 0, testreporters.TX, txConfirmationDur, testreporters.Failure) + stat.UpdateState(lane.Logger, 0, testreporters.TX, txConfirmationDur, testreporters.Failure, nil) return fmt.Errorf("could not send request: %w", err) } err = lane.Source.Common.ChainClient.WaitForEvents() if err != nil { - stat.UpdateState(lane.Logger, 0, testreporters.TX, txConfirmationDur, testreporters.Failure) + stat.UpdateState(lane.Logger, 0, testreporters.TX, txConfirmationDur, testreporters.Failure, nil) return fmt.Errorf("could not send request: %w", err) } @@ -2874,22 +2888,11 @@ func (lane *CCIPLane) SendRequests(noOfRequests int, gasLimit *big.Int) error { noOfTokens++ } } - rcpt, err := lane.AddToSentReqs(txHash, []*testreporters.RequestStat{stat}) + _, err = lane.AddToSentReqs(txHash, []*testreporters.RequestStat{stat}) if err != nil { return err } - var gasUsed uint64 - if rcpt != nil { - gasUsed = rcpt.GasUsed - } - stat.UpdateState(lane.Logger, 0, - testreporters.TX, txConfirmationDur, testreporters.Success, testreporters.TransactionStats{ - Fee: fee.String(), - GasUsed: gasUsed, - TxHash: rcpt.TxHash.Hex(), - NoOfTokensSent: noOfTokens, - MessageBytesLength: lane.Source.MsgDataLength, - }) + stat.UpdateState(lane.Logger, 0, testreporters.TX, txConfirmationDur, testreporters.Success, nil) lane.TotalFee = bigmath.Add(lane.TotalFee, fee) } @@ -3093,7 +3096,6 @@ func ExpectPhaseToFail(phase testreporters.Phase, phaseSpecificOptions ...PhaseS // If not, just pass in nil. func (lane *CCIPLane) ValidateRequests(validationOptionFuncs ...ValidationOptionFunc) { var opts validationOptions - require.LessOrEqual(lane.Test, len(validationOptionFuncs), 1, "only one validation option function can be passed in to ValidateRequests") for _, f := range validationOptionFuncs { if f != nil { f(lane.Logger, &opts) @@ -3144,13 +3146,13 @@ func (lane *CCIPLane) ValidateRequestByTxHash(txHash common.Hash, opts validatio return phaseErr } - sourceLogFinalizedAt, _, err := lane.Source.AssertSendRequestedLogFinalized(lane.Logger, txHash, ccipSendReqGenAt, reqStats) + sourceLogFinalizedAt, _, err := lane.Source.AssertSendRequestedLogFinalized(lane.Logger, txHash, msgLogs, ccipSendReqGenAt, reqStats) if shouldReturn, phaseErr := isPhaseValid(lane.Logger, testreporters.SourceLogFinalized, opts, err); shouldReturn { return phaseErr } for _, msgLog := range msgLogs { seqNumber := msgLog.SequenceNumber - lane.Logger = ptr.Ptr(lane.Logger.With().Str("msgId ", fmt.Sprintf("0x%x", msgLog.MessageId[:])).Logger()) + lane.Logger = ptr.Ptr(lane.Logger.With().Str("msgId", fmt.Sprintf("0x%x", msgLog.MessageId[:])).Logger()) var reqStat *testreporters.RequestStat for _, stat := range reqStats { if stat.SeqNum == seqNumber { @@ -3332,6 +3334,7 @@ func (lane *CCIPLane) StartEventWatchers() error { DataLength: len(e.Message.Data), NoOfTokens: len(e.Message.TokenAmounts), Raw: e.Raw, + Fee: e.Message.FeeTokenAmount, })) } else { lane.Source.CCIPSendRequestedWatcher.Store(e.Raw.TxHash.Hex(), []*contracts.SendReqEventData{ @@ -3341,6 +3344,7 @@ func (lane *CCIPLane) StartEventWatchers() error { DataLength: len(e.Message.Data), NoOfTokens: len(e.Message.TokenAmounts), Raw: e.Raw, + Fee: e.Message.FeeTokenAmount, }, }) } diff --git a/integration-tests/ccip-tests/contracts/contract_models.go b/integration-tests/ccip-tests/contracts/contract_models.go index 08a88be569..7ab92a673c 100644 --- a/integration-tests/ccip-tests/contracts/contract_models.go +++ b/integration-tests/ccip-tests/contracts/contract_models.go @@ -271,9 +271,6 @@ func (token *ERC20Token) Approve(onBehalf *blockchain.EthereumWallet, spender st if err != nil { return fmt.Errorf("failed to get balance of onBehalf: %w", err) } - if onBehalfBalance.Cmp(amount) < 0 { - return fmt.Errorf("onBehalf '%s' does not have enough balance to approve", onBehalf.Address()) - } currentAllowance, err := token.Allowance(onBehalf.Address(), spender) if err != nil { return fmt.Errorf("failed to get current allowance for '%s' on behalf of '%s': %w", spender, onBehalf.Address(), err) @@ -1500,6 +1497,7 @@ type SendReqEventData struct { DataLength int NoOfTokens int Raw types.Log + Fee *big.Int } type OnRampWrapper struct { diff --git a/integration-tests/ccip-tests/load/ccip_loadgen.go b/integration-tests/ccip-tests/load/ccip_loadgen.go index 4e142c9afe..ab74dd4509 100644 --- a/integration-tests/ccip-tests/load/ccip_loadgen.go +++ b/integration-tests/ccip-tests/load/ccip_loadgen.go @@ -11,7 +11,6 @@ import ( "time" "github.com/AlekSi/pointer" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/rs/zerolog" @@ -258,78 +257,25 @@ func (c *CCIPE2ELoad) Call(_ *wasp.Generator) *wasp.Response { } startTime := time.Now().UTC() if feeToken != common.HexToAddress("0x0") { - sendTx, err = sourceCCIP.Common.Router.CCIPSend(destChainSelector, msg, nil) + sendTx, err = sourceCCIP.Common.Router.CCIPSendAndProcessTx(destChainSelector, msg, nil) } else { // add a bit buffer to fee - sendTx, err = sourceCCIP.Common.Router.CCIPSend(destChainSelector, msg, new(big.Int).Add(big.NewInt(1e5), fee)) + sendTx, err = sourceCCIP.Common.Router.CCIPSendAndProcessTx(destChainSelector, msg, new(big.Int).Add(big.NewInt(1e5), fee)) } if err != nil { - stats.UpdateState(&lggr, 0, testreporters.TX, time.Since(startTime), testreporters.Failure) + stats.UpdateState(&lggr, 0, testreporters.TX, time.Since(startTime), testreporters.Failure, nil) res.Error = fmt.Sprintf("ccip-send tx error %s for reqNo %d", err.Error(), msgSerialNo) res.Data = stats.StatusByPhase res.Failed = true return res } - err = sourceCCIP.Common.ChainClient.MarkTxAsSentOnL2(sendTx) - - if err != nil { - stats.UpdateState(&lggr, 0, testreporters.TX, time.Since(startTime), testreporters.Failure) - res.Error = fmt.Sprintf("reqNo %d failed to mark tx as sent on L2 %s", msgSerialNo, err.Error()) - res.Data = stats.StatusByPhase - res.Failed = true - return res - } - txConfirmationTime := time.Now().UTC() // wait for the tx to be mined, timeout is set to 10 minutes lggr.Info().Str("tx", sendTx.Hash().Hex()).Msg("waiting for tx to be mined") - ctx, cancel := context.WithTimeout(context.Background(), sourceCCIP.Common.ChainClient.GetNetworkConfig().Timeout.Duration) - defer cancel() - rcpt, err := bind.WaitMined(ctx, sourceCCIP.Common.ChainClient.DeployBackend(), sendTx) - if err != nil { - res.Error = fmt.Sprintf("ccip-send request tx not mined, err=%s", err.Error()) - res.Failed = true - res.Data = stats.StatusByPhase - return res - } - if rcpt == nil { - res.Error = "ccip-send request tx not mined, receipt is nil" - res.Failed = true - res.Data = stats.StatusByPhase - return res - } - hdr, err := c.Lane.Source.Common.ChainClient.HeaderByNumber(context.Background(), rcpt.BlockNumber) - if err == nil && hdr != nil { - txConfirmationTime = hdr.Timestamp - } lggr = lggr.With().Str("Msg Tx", sendTx.Hash().String()).Logger() - if rcpt.Status != types.ReceiptStatusSuccessful { - stats.UpdateState(&lggr, 0, testreporters.TX, txConfirmationTime.Sub(startTime), testreporters.Failure, - testreporters.TransactionStats{ - Fee: fee.String(), - GasUsed: rcpt.GasUsed, - TxHash: sendTx.Hash().Hex(), - NoOfTokensSent: len(msg.TokenAmounts), - MessageBytesLength: int64(len(msg.Data)), - }) - errReason, v, err := c.Lane.Source.Common.ChainClient.RevertReasonFromTx(rcpt.TxHash, router.RouterABI) - if err != nil { - errReason = "could not decode" - } - res.Error = fmt.Sprintf("ccip-send request receipt is not successful, errReason=%s, args =%v", errReason, v) - res.Failed = true - res.Data = stats.StatusByPhase - return res - } - stats.UpdateState(&lggr, 0, testreporters.TX, txConfirmationTime.Sub(startTime), testreporters.Success, - testreporters.TransactionStats{ - Fee: fee.String(), - GasUsed: rcpt.GasUsed, - TxHash: sendTx.Hash().Hex(), - NoOfTokensSent: len(msg.TokenAmounts), - MessageBytesLength: int64(len(msg.Data)), - }) + + stats.UpdateState(&lggr, 0, testreporters.TX, txConfirmationTime.Sub(startTime), testreporters.Success, nil) err = c.Validate(lggr, sendTx, txConfirmationTime, []*testreporters.RequestStat{stats}) if err != nil { res.Error = err.Error() @@ -356,19 +302,23 @@ func (c *CCIPE2ELoad) Validate(lggr zerolog.Logger, sendTx *types.Transaction, t if c.Lane.Source.Common.ChainClient.GetNetworkConfig().FinalityDepth == 0 && lstFinalizedBlock != 0 && lstFinalizedBlock > msgLogs[0].Raw.BlockNumber { sourceLogFinalizedAt = c.LastFinalizedTimestamp.Load() - for _, stat := range stats { + for i, stat := range stats { stat.UpdateState(&lggr, stat.SeqNum, testreporters.SourceLogFinalized, sourceLogFinalizedAt.Sub(sourceLogTime), testreporters.Success, - testreporters.TransactionStats{ - TxHash: msgLogs[0].Raw.TxHash.Hex(), - FinalizedByBlock: strconv.FormatUint(lstFinalizedBlock, 10), - FinalizedAt: sourceLogFinalizedAt.String(), + &testreporters.TransactionStats{ + TxHash: msgLogs[i].Raw.TxHash.Hex(), + FinalizedByBlock: strconv.FormatUint(lstFinalizedBlock, 10), + FinalizedAt: sourceLogFinalizedAt.String(), + Fee: msgLogs[i].Fee.String(), + NoOfTokensSent: msgLogs[i].NoOfTokens, + MessageBytesLength: int64(msgLogs[i].DataLength), + MsgID: fmt.Sprintf("0x%x", msgLogs[i].MessageId[:]), }) } } else { var finalizingBlock uint64 sourceLogFinalizedAt, finalizingBlock, err = c.Lane.Source.AssertSendRequestedLogFinalized( - &lggr, msgLogs[0].Raw.TxHash, sourceLogTime, stats) + &lggr, msgLogs[0].Raw.TxHash, msgLogs, sourceLogTime, stats) if err != nil { return err } diff --git a/integration-tests/ccip-tests/load/ccip_multicall_loadgen.go b/integration-tests/ccip-tests/load/ccip_multicall_loadgen.go index b0824ac9b1..ad3960dee2 100644 --- a/integration-tests/ccip-tests/load/ccip_multicall_loadgen.go +++ b/integration-tests/ccip-tests/load/ccip_multicall_loadgen.go @@ -1,13 +1,11 @@ package load import ( - "context" "fmt" "math/big" "testing" "time" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/prometheus/common/model" "github.com/rs/zerolog" @@ -178,33 +176,14 @@ func (m *CCIPMultiCallLoadGenerator) Call(_ *wasp.Generator) *wasp.Response { lggr := m.logger.With().Str("Msg Tx", sendTx.Hash().String()).Logger() txConfirmationTime := time.Now().UTC() - rcpt, err1 := bind.WaitMined(context.Background(), m.client.DeployBackend(), sendTx) - if err1 == nil { - hdr, err1 := m.client.HeaderByNumber(context.Background(), rcpt.BlockNumber) - if err1 == nil { - txConfirmationTime = hdr.Timestamp - } - } - var gasUsed uint64 - if rcpt != nil { - gasUsed = rcpt.GasUsed - } for _, rValues := range returnValuesByDest { if len(rValues.Stats) != len(rValues.Msgs) { res.Error = fmt.Sprintf("number of stats %d and msgs %d should be same", len(rValues.Stats), len(rValues.Msgs)) res.Failed = true return res } - for i, stat := range rValues.Stats { - msg := rValues.Msgs[i] - stat.UpdateState(&lggr, 0, testreporters.TX, startTime.Sub(txConfirmationTime), testreporters.Success, - testreporters.TransactionStats{ - Fee: msg.Fee.String(), - GasUsed: gasUsed, - TxHash: sendTx.Hash().Hex(), - NoOfTokensSent: len(msg.Msg.TokenAmounts), - MessageBytesLength: int64(len(msg.Msg.Data)), - }) + for _, stat := range rValues.Stats { + stat.UpdateState(&lggr, 0, testreporters.TX, startTime.Sub(txConfirmationTime), testreporters.Success, nil) } } diff --git a/integration-tests/ccip-tests/smoke/ccip_test.go b/integration-tests/ccip-tests/smoke/ccip_test.go index 9ba2b9caaa..a9de3abe85 100644 --- a/integration-tests/ccip-tests/smoke/ccip_test.go +++ b/integration-tests/ccip-tests/smoke/ccip_test.go @@ -531,6 +531,7 @@ func TestSmokeCCIPOnRampLimits(t *testing.T) { Msg("Limited token transfer failed on source chain (a good thing in this context)") // Set a high price for the tokens to more easily trigger aggregate rate limits + // Aggregate rate limits are based on USD price of the tokens err = src.Common.PriceRegistry.UpdatePrices([]contracts.InternalTokenPriceUpdate{ { SourceToken: aggRateToken.ContractAddress, @@ -614,6 +615,136 @@ func TestSmokeCCIPOffRampAggRateLimit(t *testing.T) { testOffRampRateLimits(t, aggRateLimited) } +func TestSmokeCCIPTokenPoolRateLimits(t *testing.T) { + t.Parallel() + + log := logging.GetTestLogger(t) + TestCfg := testsetups.NewCCIPTestConfig(t, log, testconfig.Smoke, testsetups.WithNoTokensPerMessage(4), testsetups.WithTokensPerChain(4)) + require.False(t, pointer.GetBool(TestCfg.TestGroupInput.ExistingDeployment), + "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, + }) + require.NoError(t, err, "Required contract versions not met") + + setUpOutput := testsetups.CCIPDefaultTestSetUp(t, &log, "smoke-ccip", nil, TestCfg) + if len(setUpOutput.Lanes) == 0 { + return + } + t.Cleanup(func() { + require.NoError(t, setUpOutput.TearDown()) + }) + + var tests []testDefinition + for _, lane := range setUpOutput.Lanes { + tests = append(tests, testDefinition{ + testName: fmt.Sprintf("Network %s to network %s", + lane.ForwardLane.SourceNetworkName, lane.ForwardLane.DestNetworkName), + lane: lane.ForwardLane, + }) + } + + var ( + capacityLimit = big.NewInt(1e16) + overCapacityAmount = new(big.Int).Add(capacityLimit, big.NewInt(1)) + + // token without any limits + freeTokenIndex = 0 + // token with rate limits + limitedTokenIndex = 1 + ) + + for _, tc := range tests { + t.Run(fmt.Sprintf("%s - Token Pool Rate Limits", tc.testName), func(t *testing.T) { + tc.lane.Test = t + src := tc.lane.Source + dest := tc.lane.Dest + require.GreaterOrEqual(t, len(src.Common.BridgeTokens), 2, "At least two bridge tokens needed for test") + require.GreaterOrEqual(t, len(src.Common.BridgeTokenPools), 2, "At least two bridge token pools needed for test") + require.GreaterOrEqual(t, len(dest.Common.BridgeTokens), 2, "At least two bridge tokens needed for test") + require.GreaterOrEqual(t, len(dest.Common.BridgeTokenPools), 2, "At least two bridge token pools needed for test") + addLiquidity(t, src.Common, new(big.Int).Mul(capacityLimit, big.NewInt(20))) + addLiquidity(t, dest.Common, new(big.Int).Mul(capacityLimit, big.NewInt(20))) + + var ( + freeToken = src.Common.BridgeTokens[freeTokenIndex] + limitedToken = src.Common.BridgeTokens[limitedTokenIndex] + limitedTokenPool = src.Common.BridgeTokenPools[limitedTokenIndex] + ) + tc.lane.Logger.Info(). + Str("Free Token", freeToken.ContractAddress.Hex()). + Str("Limited Token", limitedToken.ContractAddress.Hex()). + Msg("Tokens for rate limit testing") + err := tc.lane.DisableAllRateLimiting() // Make sure this is pure + require.NoError(t, err, "Error disabling rate limits") + + // Check capacity limits + err = limitedTokenPool.SetRemoteChainRateLimits(src.DestChainSelector, token_pool.RateLimiterConfig{ + IsEnabled: true, + Capacity: capacityLimit, + Rate: new(big.Int).Sub(capacityLimit, big.NewInt(1)), // Set as high rate as possible to avoid it getting in the way + }) + require.NoError(t, err, "Error setting token pool rate limit") + err = src.Common.ChainClient.WaitForEvents() + require.NoError(t, err, "Error waiting for events") + + // Send all tokens under their limits and ensure they succeed + src.TransferAmount[freeTokenIndex] = overCapacityAmount + src.TransferAmount[limitedTokenIndex] = big.NewInt(1) + tc.lane.RecordStateBeforeTransfer() + err = tc.lane.SendRequests(1, big.NewInt(actions.DefaultDestinationGasLimit)) + require.NoError(t, err) + tc.lane.ValidateRequests() + + // Send limited token over capacity and ensure it fails + src.TransferAmount[freeTokenIndex] = big.NewInt(0) + src.TransferAmount[limitedTokenIndex] = overCapacityAmount + failedTx, _, _, err := tc.lane.Source.SendRequest(tc.lane.Dest.ReceiverDapp.EthAddress, big.NewInt(actions.DefaultDestinationGasLimit)) + require.Error(t, err, "Limited token transfer should immediately revert") + errReason, _, err := src.Common.ChainClient.RevertReasonFromTx(failedTx, lock_release_token_pool.LockReleaseTokenPoolABI) + require.NoError(t, err) + require.Equal(t, "TokenMaxCapacityExceeded", errReason, "Expected token capacity error") + tc.lane.Logger. + Info(). + Str("Token", limitedToken.ContractAddress.Hex()). + Msg("Limited token transfer failed on source chain (a good thing in this context)") + + // Check rate limit + err = limitedTokenPool.SetRemoteChainRateLimits(src.DestChainSelector, token_pool.RateLimiterConfig{ + IsEnabled: true, + Capacity: new(big.Int).Mul(capacityLimit, big.NewInt(2)), // Set a high capacity to avoid it getting in the way + Rate: big.NewInt(1), + }) + require.NoError(t, err, "Error setting token pool rate limit") + err = src.Common.ChainClient.WaitForEvents() + require.NoError(t, err, "Error waiting for events") + + // Send all tokens under their limits and ensure they succeed + src.TransferAmount[freeTokenIndex] = overCapacityAmount + src.TransferAmount[limitedTokenIndex] = capacityLimit + tc.lane.RecordStateBeforeTransfer() + err = tc.lane.SendRequests(1, big.NewInt(actions.DefaultDestinationGasLimit)) + require.NoError(t, err) + tc.lane.ValidateRequests() + + // Send limited token over rate limit and ensure it fails + src.TransferAmount[freeTokenIndex] = big.NewInt(0) + src.TransferAmount[limitedTokenIndex] = capacityLimit + failedTx, _, _, err = tc.lane.Source.SendRequest(tc.lane.Dest.ReceiverDapp.EthAddress, big.NewInt(actions.DefaultDestinationGasLimit)) + require.Error(t, err, "Limited token transfer should immediately revert") + errReason, _, err = src.Common.ChainClient.RevertReasonFromTx(failedTx, lock_release_token_pool.LockReleaseTokenPoolABI) + require.NoError(t, err) + require.Equal(t, "TokenRateLimitReached", errReason, "Expected rate limit reached error") + tc.lane.Logger. + Info(). + Str("Token", limitedToken.ContractAddress.Hex()). + Msg("Limited token transfer failed on source chain (a good thing in this context)") + }) + } +} + func TestSmokeCCIPMulticall(t *testing.T) { t.Parallel() log := logging.GetTestLogger(t) diff --git a/integration-tests/ccip-tests/testconfig/README.md b/integration-tests/ccip-tests/testconfig/README.md index 6da17d1125..a402c218d0 100644 --- a/integration-tests/ccip-tests/testconfig/README.md +++ b/integration-tests/ccip-tests/testconfig/README.md @@ -528,7 +528,7 @@ Specifies the number of routers to be set up for each network. ### CCIP.Groups.[testgroup].MaxNoOfLanes Specifies the maximum number of lanes to be set up between networks. If this values is not set, the test will set up lanes between all possible pairs of networks mentioned in `selected_networks` in [CCIP.Env.Networks](#ccipenvnetworksselectednetworks). -For example, if `selected_networks = ['SIMULATED_1', 'SIMULATED_2', 'SIMULATED_3']`, and `MaxNoOfLanes` is set to 3, it denotes that the test will randomly select 3 lanes between all possible pairs `SIMULATED_1`, `SIMULATED_2`, and `SIMULATED_3` for the test run. +For example, if `selected_networks = ['SIMULATED_1', 'SIMULATED_2', 'SIMULATED_3']`, and `MaxNoOfLanes` is set to 2, it denotes that the test will select the first 2 lanes between all possible pairs `SIMULATED_1`, `SIMULATED_2`, and `SIMULATED_3` for the test run. ### CCIP.Groups.[testgroup].ChaosDuration Specifies the duration for which the chaos experiment is to be run. This is only valid if the test type is 'chaos'. diff --git a/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress-2024-06-27/baseline.toml b/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress-2024-06-27/baseline.toml new file mode 100644 index 0000000000..9bcdbff61d --- /dev/null +++ b/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress-2024-06-27/baseline.toml @@ -0,0 +1,188 @@ +## Baseline performance test on simulated environment (with chaos) +## 40 chains / 400 lanes +## historyDepth 200 / finalityDepth 200 +## block_time = 1s +## throughput 1msg / 5s +## 20% Token, 60% DataWithToken, 15% Regular size msgs, 5% Large msgs +## +## make test_load_ccip testimage=amazonaws.com/chainlink-ccip-tests:ccip-develop \ +## testname=TestLoadCCIPStableRequestTriggeringWithNetworkChaos \ +## override_toml=./testconfig/tomls/baseline.toml \ +## secret_toml=./testconfig/tomls/secrets.toml + +[CCIP] +[CCIP.ContractVersions] +PriceRegistry = '1.2.0' +OffRamp = '1.2.0' +OnRamp = '1.2.0' +TokenPool = '1.4.0' +CommitStore = '1.2.0' + +[CCIP.Env] +TTL = '8h' + +[CCIP.Env.Network] +selected_networks= ['PRIVATE-CHAIN-1', 'PRIVATE-CHAIN-2'] + +[CCIP.Env.Network.EVMNetworks.PRIVATE-CHAIN-1] +evm_name = 'private-chain-1' +evm_chain_id = 2337 +evm_urls = ['wss://ignore-this-url.com'] +evm_http_urls = ['https://ignore-this-url.com'] +evm_keys = ['59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'] +evm_simulated = true +client_implementation = 'Ethereum' +evm_chainlink_transaction_limit = 5000 +evm_transaction_timeout = '3m' +evm_minimum_confirmations = 1 +evm_gas_estimation_buffer = 1000 +evm_supports_eip1559 = true +evm_default_gas_limit = 6000000 +evm_finality_depth = 200 + +[CCIP.Env.Network.EVMNetworks.PRIVATE-CHAIN-2] +evm_name = 'private-chain-2' +evm_chain_id = 1337 +evm_urls = ['wss://ignore-this-url.com'] +evm_http_urls = ['https://ignore-this-url.com'] +evm_keys = ['ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'] +evm_simulated = true +client_implementation = 'Ethereum' +evm_chainlink_transaction_limit = 5000 +evm_transaction_timeout = '3m' +evm_minimum_confirmations = 1 +evm_gas_estimation_buffer = 1000 +evm_supports_eip1559 = true +evm_default_gas_limit = 6000000 +evm_finality_depth = 200 + +[CCIP.Env.Network.AnvilConfigs.PRIVATE-CHAIN-1] +block_time = 1 +# +[CCIP.Env.Network.AnvilConfigs.PRIVATE-CHAIN-2] +block_time = 1 + +[CCIP.Env.NewCLCluster] +NoOfNodes = 17 +NodeMemory = '10Gi' +NodeCPU = '6' +DBMemory = '16Gi' +DBCPU = '4' +DBStorageClass = 'gp3' +PromPgExporter = true +DBCapacity = '50Gi' +IsStateful = true +DBArgs = ['shared_buffers=4096MB', 'effective_cache_size=8192MB', 'work_mem=128MB'] + +[CCIP.Env.NewCLCluster.Common] +BaseConfigTOML = """ +[Feature] +LogPoller = true +CCIP = true + +[Log] +Level = 'debug' +JSONConsole = true + +[Log.File] +MaxSize = '0b' + +[WebServer] +AllowOrigins = '*' +HTTPPort = 6688 +SecureCookies = false +HTTPWriteTimeout = '1m' + +[WebServer.RateLimit] +Authenticated = 2000 +Unauthenticated = 1000 + +[WebServer.TLS] +HTTPSPort = 0 + +[Database] +MaxIdleConns = 20 +MaxOpenConns = 30 +MigrateOnStartup = true + +[OCR2] +Enabled = true +DefaultTransactionQueueDepth = 0 + +[OCR] +Enabled = false +DefaultTransactionQueueDepth = 0 + +[P2P] +[P2P.V2] +Enabled = true +ListenAddresses = ['0.0.0.0:6690'] +AnnounceAddresses = ['0.0.0.0:6690'] +DeltaDial = '500ms' +DeltaReconcile = '5s' +""" + +CommonChainConfigTOML = """ +[HeadTracker] +HistoryDepth = 200 + +[GasEstimator] +PriceMax = '200 gwei' +LimitDefault = 6000000 +FeeCapDefault = '200 gwei' +""" + +[CCIP.Groups] +[CCIP.Groups.load] +KeepEnvAlive = true +NoOfCommitNodes = 16 +PhaseTimeout = '40m' +NodeFunding = 1000.0 +NoOfRoutersPerPair = 2 +NoOfNetworks = 40 +MaxNoOfLanes = 400 + +[CCIP.Groups.load.OffRampConfig] +BatchGasLimit = 11000000 + +[CCIP.Groups.load.TokenConfig] +TimeoutForPriceUpdate = '15m' +NoOfTokensPerChain = 60 +NoOfTokensWithDynamicPrice = 15 +DynamicPriceUpdateInterval ='15s' +CCIPOwnerTokens = true + +[CCIP.Groups.load.LoadProfile] +TestDuration = '4h' +TimeUnit = '5s' +RequestPerUnitTime = [1] +OptimizeSpace = true +NetworkChaosDelay = '100ms' + +# to represent 20%, 60%, 15%, 5% of the total messages +[CCIP.Groups.load.LoadProfile.MsgProfile] +Frequencies = [4,12,3,1] + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'Token' +DestGasLimit = 0 +DataLength = 0 +NoOfTokens = 5 +AmountPerToken = 1 + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'DataWithToken' +DestGasLimit = 500000 +DataLength = 5000 +NoOfTokens = 5 +AmountPerToken = 1 + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'Data' +DestGasLimit = 800000 +DataLength = 10000 + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'Data' +DestGasLimit = 2500000 +DataLength = 10000 diff --git a/integration-tests/ccip-tests/testconfig/override/prod-testnet.toml b/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress-2024-06-27/prod-testnet.toml similarity index 99% rename from integration-tests/ccip-tests/testconfig/override/prod-testnet.toml rename to integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress-2024-06-27/prod-testnet.toml index 0dd220b7af..f8321584c8 100644 --- a/integration-tests/ccip-tests/testconfig/override/prod-testnet.toml +++ b/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress-2024-06-27/prod-testnet.toml @@ -919,7 +919,8 @@ selected_networks = [ [CCIP.Groups.load] NetworkPairs = [ - 'AVALANCHE_FUJI,SEPOLIA' + 'AVALANCHE_FUJI,SEPOLIA', + 'OPTIMISM_SEPOLIA,BASE_SEPOLIA' ] BiDirectionalLane = true @@ -930,8 +931,8 @@ NoOfTokensPerChain = 1 [CCIP.Groups.load.LoadProfile] RequestPerUnitTime = [1] -TimeUnit = '30s' -TestDuration = '5m' +TimeUnit = '5s' +TestDuration = '1h' TestRunName = 'v2.12.0-ccip1.4.16-load' # to represent 20%, 60%, 15%, 5% of the total messages diff --git a/integration-tests/ccip-tests/testconfig/tomls/scalability.toml b/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress-2024-06-27/sample-scalability.toml similarity index 100% rename from integration-tests/ccip-tests/testconfig/tomls/scalability.toml rename to integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress-2024-06-27/sample-scalability.toml diff --git a/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress-2024-06-27/tier-a.toml b/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress-2024-06-27/tier-a.toml new file mode 100644 index 0000000000..b7595906e6 --- /dev/null +++ b/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress-2024-06-27/tier-a.toml @@ -0,0 +1,226 @@ +## Baseline performance test on simulated environment (with chaos) +## 40 chains / 400 lanes +## historyDepth 200 / finalityDepth 200 +## block_time = 1s +## throughput 1msg / 5s +## 20% Token, 60% DataWithToken, 15% Regular size msgs, 5% Large msgs +## +## make test_load_ccip testimage=795953128386.dkr.ecr.us-west-2.amazonaws.com/chainlink-ccip-tests:ccip-develop \ +## testname=TestLoadCCIPStableRequestTriggeringWithNetworkChaos \ +## override_toml=./testconfig/tomls/baseline.toml \ +## secret_toml=./testconfig/tomls/secrets.toml + +[CCIP] +[CCIP.ContractVersions] +PriceRegistry = '1.2.0' +OffRamp = '1.2.0' +OnRamp = '1.2.0' +TokenPool = '1.4.0' +CommitStore = '1.2.0' + +[CCIP.Env] +TTL = '8h' + +[CCIP.Env.Network] +selected_networks= ['PRIVATE-CHAIN-1', 'SLOW-CHAIN-1', 'SLOW-CHAIN-2', 'SLOW-CHAIN-3'] + +[CCIP.Env.Network.EVMNetworks.PRIVATE-CHAIN-1] +evm_name = 'private-chain-1' +evm_chain_id = 2337 +evm_urls = ['wss://ignore-this-url.com'] +evm_http_urls = ['https://ignore-this-url.com'] +evm_keys = ['59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'] +evm_simulated = true +client_implementation = 'Ethereum' +evm_chainlink_transaction_limit = 5000 +evm_transaction_timeout = '3m' +evm_minimum_confirmations = 1 +evm_gas_estimation_buffer = 1000 +evm_supports_eip1559 = true +evm_default_gas_limit = 6000000 +evm_finality_depth = 1 + +[CCIP.Env.Network.EVMNetworks.SLOW-CHAIN-1] +evm_name = 'slow-chain-1' +evm_chain_id = 90000001 +evm_urls = ['wss://ignore-this-url.com'] +evm_http_urls = ['https://ignore-this-url.com'] +evm_keys = ['ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'] +evm_simulated = true +client_implementation = 'Ethereum' +evm_chainlink_transaction_limit = 5000 +evm_transaction_timeout = '3m' +evm_minimum_confirmations = 1 +evm_gas_estimation_buffer = 1000 +evm_supports_eip1559 = true +evm_default_gas_limit = 6000000 +evm_finality_depth = 1 + +[CCIP.Env.Network.EVMNetworks.SLOW-CHAIN-2] +evm_name = 'slow-chain-2' +evm_chain_id = 90000002 +evm_urls = ['wss://ignore-this-url.com'] +evm_http_urls = ['https://ignore-this-url.com'] +evm_keys = ['ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'] +evm_simulated = true +client_implementation = 'Ethereum' +evm_chainlink_transaction_limit = 5000 +evm_transaction_timeout = '3m' +evm_minimum_confirmations = 1 +evm_gas_estimation_buffer = 1000 +evm_supports_eip1559 = true +evm_default_gas_limit = 6000000 +evm_finality_depth = 1 + +[CCIP.Env.Network.EVMNetworks.SLOW-CHAIN-3] +evm_name = 'slow-chain-3' +evm_chain_id = 1337 +evm_urls = ['wss://ignore-this-url.com'] +evm_http_urls = ['https://ignore-this-url.com'] +evm_keys = ['ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'] +evm_simulated = true +client_implementation = 'Ethereum' +evm_chainlink_transaction_limit = 5000 +evm_transaction_timeout = '3m' +evm_minimum_confirmations = 1 +evm_gas_estimation_buffer = 1000 +evm_supports_eip1559 = true +evm_default_gas_limit = 6000000 +evm_finality_depth = 1 + +[CCIP.Env.Network.AnvilConfigs.PRIVATE-CHAIN-1] +block_time = 1 +# +[CCIP.Env.Network.AnvilConfigs.SLOW-CHAIN-1] +block_time = 12 + +[CCIP.Env.Network.AnvilConfigs.SLOW-CHAIN-2] +block_time = 12 + +[CCIP.Env.Network.AnvilConfigs.SLOW-CHAIN-3] +block_time = 12 + +[CCIP.Env.NewCLCluster] +NoOfNodes = 17 +NodeMemory = '10Gi' +NodeCPU = '6' +DBMemory = '16Gi' +DBCPU = '4' +DBStorageClass = 'gp3' +PromPgExporter = true +DBCapacity = '50Gi' +IsStateful = true +DBArgs = ['shared_buffers=4096MB', 'effective_cache_size=8192MB', 'work_mem=128MB'] + +[CCIP.Env.NewCLCluster.Common] +BaseConfigTOML = """ +[Feature] +LogPoller = true +CCIP = true + +[Log] +Level = 'debug' +JSONConsole = true + +[Log.File] +MaxSize = '0b' + +[WebServer] +AllowOrigins = '*' +HTTPPort = 6688 +SecureCookies = false +HTTPWriteTimeout = '1m' + +[WebServer.RateLimit] +Authenticated = 2000 +Unauthenticated = 1000 + +[WebServer.TLS] +HTTPSPort = 0 + +[Database] +MaxIdleConns = 20 +MaxOpenConns = 30 +MigrateOnStartup = true + +[OCR2] +Enabled = true +DefaultTransactionQueueDepth = 0 + +[OCR] +Enabled = false +DefaultTransactionQueueDepth = 0 + +[P2P] +[P2P.V2] +Enabled = true +ListenAddresses = ['0.0.0.0:6690'] +AnnounceAddresses = ['0.0.0.0:6690'] +DeltaDial = '500ms' +DeltaReconcile = '5s' +""" + +#CommonChainConfigTOML = """ +#[HeadTracker] +#HistoryDepth = 200 +# +#[GasEstimator] +#PriceMax = '200 gwei' +#LimitDefault = 6000000 +#FeeCapDefault = '200 gwei' +#""" + +[CCIP.Groups] +[CCIP.Groups.load] +KeepEnvAlive = true +NoOfCommitNodes = 16 +PhaseTimeout = '40m' +NodeFunding = 1000.0 +NoOfRoutersPerPair = 2 +NoOfNetworks = 40 +MaxNoOfLanes = 400 + +[CCIP.Groups.load.OffRampConfig] +BatchGasLimit = 11000000 + +[CCIP.Groups.load.TokenConfig] +TimeoutForPriceUpdate = '15m' +NoOfTokensPerChain = 60 +NoOfTokensWithDynamicPrice = 15 +DynamicPriceUpdateInterval ='15s' +CCIPOwnerTokens = true + +[CCIP.Groups.load.LoadProfile] +TestDuration = '4h' +TimeUnit = '5s' +RequestPerUnitTime = [1] +OptimizeSpace = true +NetworkChaosDelay = '100ms' + +# to represent 20%, 60%, 15%, 5% of the total messages +[CCIP.Groups.load.LoadProfile.MsgProfile] +Frequencies = [4,12,3,1] + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'Token' +DestGasLimit = 0 +DataLength = 0 +NoOfTokens = 5 +AmountPerToken = 1 + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'DataWithToken' +DestGasLimit = 500000 +DataLength = 5000 +NoOfTokens = 5 +AmountPerToken = 1 + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'Data' +DestGasLimit = 800000 +DataLength = 10000 + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'Data' +DestGasLimit = 2500000 +DataLength = 10000 \ No newline at end of file diff --git a/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress-2024-06-27/tier-b.toml b/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress-2024-06-27/tier-b.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration-tests/ccip-tests/testreporters/ccip.go b/integration-tests/ccip-tests/testreporters/ccip.go index 8341735687..b567f6a629 100644 --- a/integration-tests/ccip-tests/testreporters/ccip.go +++ b/integration-tests/ccip-tests/testreporters/ccip.go @@ -57,10 +57,10 @@ type TransactionStats struct { } type PhaseStat struct { - SeqNum uint64 `json:"seq_num,omitempty"` - Duration float64 `json:"duration,omitempty"` - Status Status `json:"success"` - SendTransactionStats TransactionStats `json:"ccip_send_data,omitempty"` + SeqNum uint64 `json:"seq_num,omitempty"` + Duration float64 `json:"duration,omitempty"` + Status Status `json:"success"` + SendTransactionStats *TransactionStats `json:"ccip_send_data,omitempty"` } type RequestStat struct { @@ -77,19 +77,17 @@ func (stat *RequestStat) UpdateState( step Phase, duration time.Duration, state Status, - sendTransactionStats ...TransactionStats, + sendTransactionStats *TransactionStats, ) { durationInSec := duration.Seconds() stat.SeqNum = seqNum phaseDetails := PhaseStat{ - SeqNum: seqNum, - Duration: durationInSec, - Status: state, + SeqNum: seqNum, + Duration: durationInSec, + Status: state, + SendTransactionStats: sendTransactionStats, } - if len(sendTransactionStats) > 0 { - phaseDetails.SendTransactionStats = sendTransactionStats[0] - } - stat.StatusByPhase[step] = phaseDetails + event := lggr.Info() if seqNum != 0 { event.Uint64("seq num", seqNum) @@ -100,12 +98,17 @@ func (stat *RequestStat) UpdateState( SeqNum: seqNum, Status: state, } + stat.StatusByPhase[step] = phaseDetails lggr.Info(). Str(fmt.Sprint(E2E), string(state)). Msgf("reqNo %d", stat.ReqNo) event.Str(string(step), string(state)).Msgf("reqNo %d", stat.ReqNo) } else { event.Str(string(step), string(Success)).Msgf("reqNo %d", stat.ReqNo) + // we don't want to save phase details for TX and CCIPSendRe to avoid redundancy if these phases are successful + if step != TX && step != CCIPSendRe { + stat.StatusByPhase[step] = phaseDetails + } if step == Commit || step == ReportBlessed || step == ExecStateChanged { stat.StatusByPhase[E2E] = PhaseStat{ SeqNum: seqNum, diff --git a/integration-tests/ccip-tests/testsetups/ccip.go b/integration-tests/ccip-tests/testsetups/ccip.go index 9a892a667a..39e52164bc 100644 --- a/integration-tests/ccip-tests/testsetups/ccip.go +++ b/integration-tests/ccip-tests/testsetups/ccip.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "math/big" - "math/rand" "os" "regexp" "strings" @@ -223,8 +222,13 @@ func (c *CCIPTestConfig) SetNetworkPairs(lggr zerolog.Logger) error { actualNoOfNetworks := len(c.SelectedNetworks) n := c.SelectedNetworks[0] var chainIDs []int64 + existingChainIDs := make(map[uint64]struct{}) + for _, net := range c.SelectedNetworks { + existingChainIDs[uint64(net.ChainID)] = struct{}{} + } for _, id := range chainselectors.TestChainIds() { - if id == 2337 { + // if the chain id already exists in the already provided selected networks, skip it + if _, exists := existingChainIDs[id]; exists { continue } chainIDs = append(chainIDs, int64(id)) @@ -281,11 +285,8 @@ func (c *CCIPTestConfig) SetNetworkPairs(lggr zerolog.Logger) error { c.AddPairToNetworkList(c.SelectedNetworks[0], c.SelectedNetworks[1]) } - // if the number of lanes is lesser than the number of network pairs, choose a random subset of network pairs + // if the number of lanes is lesser than the number of network pairs, choose first c.TestGroupInput.MaxNoOfLanes pairs if c.TestGroupInput.MaxNoOfLanes > 0 && c.TestGroupInput.MaxNoOfLanes < len(c.NetworkPairs) { - rand.Shuffle(len(c.NetworkPairs), func(i, j int) { - c.NetworkPairs[i], c.NetworkPairs[j] = c.NetworkPairs[j], c.NetworkPairs[i] - }) c.NetworkPairs = c.NetworkPairs[:c.TestGroupInput.MaxNoOfLanes] } diff --git a/integration-tests/go.mod b/integration-tests/go.mod index d6325fc83c..ffa2fdfbe6 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -154,7 +154,7 @@ require ( github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/deckarep/golang-set/v2 v2.3.0 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index ad0d8c8f70..1596055046 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -382,8 +382,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g= -github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index df681afc7a..93975f66af 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -122,7 +122,7 @@ require ( github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/deckarep/golang-set/v2 v2.3.0 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 2ec9121364..73d60a7de1 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -376,8 +376,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g= -github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=