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 b0ac72c1aa..8bf9542f7e 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 60bd3a9b85..54443aa4e2 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -119,115 +119,110 @@ E2E:test_E2E_3MessagesSuccess_gas() (gas: 1104821) 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: 191359) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 69836) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 12359) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRampAndPrevOffRamp_Revert() (gas: 64548) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRamp_Revert() (gas: 64551) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChain_Success() (gas: 77549) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ZeroOnRampAddress_Revert() (gas: 12258) -EVM2EVMMultiOffRamp_applySourceChainConfigUpdates:test_ZeroSourceChainSelector_Revert() (gas: 12199) -EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsDifferentChains_Success() (gas: 294969) -EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsSameChain_Success() (gas: 238328) -EVM2EVMMultiOffRamp_batchExecute:test_MultipleReportsSkipDuplicate_Success() (gas: 157487) -EVM2EVMMultiOffRamp_batchExecute:test_OutOfBoundsGasLimitsAccess_Revert() (gas: 188364) -EVM2EVMMultiOffRamp_batchExecute:test_SingleReport_Success() (gas: 147255) -EVM2EVMMultiOffRamp_batchExecute:test_Unhealthy_Revert() (gas: 527932) -EVM2EVMMultiOffRamp_batchExecute:test_ZeroReports_Revert() (gas: 10505) -EVM2EVMMultiOffRamp_ccipReceive:test_Reverts() (gas: 17210) -EVM2EVMMultiOffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 63695) +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: 6425913) -EVM2EVMMultiOffRamp_commit:test_NoConfig_Revert() (gas: 6009185) +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: 353558) -EVM2EVMMultiOffRamp_commit:test_ReportAndPriceUpdate_Success() (gas: 157810) -EVM2EVMMultiOffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 132726) +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: 222706) +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: 205601) -EVM2EVMMultiOffRamp_commit:test_WrongConfigWithoutSigners_Revert() (gas: 6420302) +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: 6024813) -EVM2EVMMultiOffRamp_constructor:test_SourceChainSelector_Revert() (gas: 103217) -EVM2EVMMultiOffRamp_constructor:test_ZeroChainSelector_Revert() (gas: 99988) -EVM2EVMMultiOffRamp_constructor:test_ZeroNonceManager_Revert() (gas: 97859) -EVM2EVMMultiOffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 103227) -EVM2EVMMultiOffRamp_constructor:test_ZeroRMNProxy_Revert() (gas: 97758) -EVM2EVMMultiOffRamp_constructor:test_ZeroTokenAdminRegistry_Revert() (gas: 97825) +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: 1546406) -EVM2EVMMultiOffRamp_execute:test_MultipleReportsWithPartialValidationFailures_Success() (gas: 341361) -EVM2EVMMultiOffRamp_execute:test_MultipleReports_Success() (gas: 258517) -EVM2EVMMultiOffRamp_execute:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6477785) -EVM2EVMMultiOffRamp_execute:test_NoConfig_Revert() (gas: 6060776) -EVM2EVMMultiOffRamp_execute:test_NonArray_Revert() (gas: 30048) -EVM2EVMMultiOffRamp_execute:test_SingleReport_Success() (gas: 164664) -EVM2EVMMultiOffRamp_execute:test_UnauthorizedTransmitter_Revert() (gas: 148612) -EVM2EVMMultiOffRamp_execute:test_WrongConfigWithSigners_Revert() (gas: 6839860) +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: 20655) -EVM2EVMMultiOffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 255569) -EVM2EVMMultiOffRamp_executeSingleMessage:test_NonContract_Success() (gas: 22806) -EVM2EVMMultiOffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 207958) -EVM2EVMMultiOffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 50888) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 50398) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() (gas: 235368) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidation_Revert() (gas: 91184) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 287803) -EVM2EVMMultiOffRamp_executeSingleMessage:test_executeSingleMessage_WithValidation_Success() (gas: 95288) -EVM2EVMMultiOffRamp_executeSingleReport:test_DisabledSourceChain_Revert() (gas: 37208) -EVM2EVMMultiOffRamp_executeSingleReport:test_EmptyReport_Revert() (gas: 23877) -EVM2EVMMultiOffRamp_executeSingleReport:test_InvalidMessageId_Revert() (gas: 41684) -EVM2EVMMultiOffRamp_executeSingleReport:test_InvalidSourcePoolAddress_Success() (gas: 458019) -EVM2EVMMultiOffRamp_executeSingleReport:test_ManualExecutionNotYetEnabled_Revert() (gas: 53460) -EVM2EVMMultiOffRamp_executeSingleReport:test_MismatchingOnRampAddress_Revert() (gas: 44527) -EVM2EVMMultiOffRamp_executeSingleReport:test_MismatchingSourceChainSelector_Revert() (gas: 41576) -EVM2EVMMultiOffRamp_executeSingleReport:test_NonExistingSourceChain_Revert() (gas: 37438) -EVM2EVMMultiOffRamp_executeSingleReport:test_ReceiverError_Success() (gas: 181595) -EVM2EVMMultiOffRamp_executeSingleReport:test_RetryFailedMessageWithoutManualExecution_Revert() (gas: 189957) -EVM2EVMMultiOffRamp_executeSingleReport:test_RootNotCommitted_Revert() (gas: 47044) -EVM2EVMMultiOffRamp_executeSingleReport:test_RouterYULCall_Revert() (gas: 434527) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain_Success() (gas: 248935) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered_Success() (gas: 174421) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageNoTokens_Success() (gas: 193347) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessageToNonCCIPReceiver_Success() (gas: 259840) -EVM2EVMMultiOffRamp_executeSingleReport:test_SingleMessagesNoTokensSuccess_gas() (gas: 127020) -EVM2EVMMultiOffRamp_executeSingleReport:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 394980) -EVM2EVMMultiOffRamp_executeSingleReport:test_SkippedIncorrectNonce_Success() (gas: 65861) -EVM2EVMMultiOffRamp_executeSingleReport:test_TokenDataMismatch_Revert() (gas: 80076) -EVM2EVMMultiOffRamp_executeSingleReport:test_TwoMessagesWithTokensAndGE_Success() (gas: 541116) -EVM2EVMMultiOffRamp_executeSingleReport:test_TwoMessagesWithTokensSuccess_gas() (gas: 475488) -EVM2EVMMultiOffRamp_executeSingleReport:test_UnexpectedTokenData_Revert() (gas: 37957) -EVM2EVMMultiOffRamp_executeSingleReport:test_UnhealthySingleChainCurse_Revert() (gas: 526295) -EVM2EVMMultiOffRamp_executeSingleReport:test_Unhealthy_Revert() (gas: 523674) -EVM2EVMMultiOffRamp_executeSingleReport:test_WithCurseOnAnotherSourceChain_Success() (gas: 492610) -EVM2EVMMultiOffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 126952) -EVM2EVMMultiOffRamp_executeSingleReport:test_execute_SkippedAlreadyExecutedMessage_Success() (gas: 155518) -EVM2EVMMultiOffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 3667544) -EVM2EVMMultiOffRamp_getExecutionState:test_GetDifferentChainExecutionState_Success() (gas: 118276) -EVM2EVMMultiOffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 87396) -EVM2EVMMultiOffRamp_manuallyExecute:test_ManualExecGasLimitMismatchSingleReport_Revert() (gas: 79283) -EVM2EVMMultiOffRamp_manuallyExecute:test_ManualExecInvalidGasLimit_Revert() (gas: 28601) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 163512) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_FailedTx_Revert() (gas: 207222) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_ForkedChain_Revert() (gas: 28130) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() (gas: 158903) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit_Success() (gas: 505081) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails() (gas: 2379173) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_Success() (gas: 209279) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 209853) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success() (gas: 664710) -EVM2EVMMultiOffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 297865) -EVM2EVMMultiOffRamp_metadataHash:test_MetadataHashChangesOnOnRampAddress_Success() (gas: 10995) -EVM2EVMMultiOffRamp_metadataHash:test_MetadataHashChangesOnSourceChain_Success() (gas: 11041) -EVM2EVMMultiOffRamp_metadataHash:test_MetadataHash_Success() (gas: 9141) +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) @@ -236,19 +231,19 @@ EVM2EVMMultiOffRamp_releaseOrMintTokens:test_releaseOrMintTokens_PoolDoesNotSupp 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: 215214) +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: 55563) -EVM2EVMMultiOffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 33573) -EVM2EVMMultiOffRamp_trialExecute:test_RateLimitError_Success() (gas: 243560) -EVM2EVMMultiOffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 252217) -EVM2EVMMultiOffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 306038) -EVM2EVMMultiOffRamp_trialExecute:test_trialExecute_Success() (gas: 285633) -EVM2EVMMultiOffRamp_verify:test_Blessed_Success() (gas: 176524) -EVM2EVMMultiOffRamp_verify:test_NotBlessedWrongChainSelector_Success() (gas: 178595) -EVM2EVMMultiOffRamp_verify:test_NotBlessed_Success() (gas: 141456) +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) @@ -642,18 +637,18 @@ 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: 1424394) +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: 251109) -NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 250287) -NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 303526) -NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 286447) -NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() (gas: 250490) -NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 238465) -NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 144101) +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) diff --git a/contracts/scripts/native_solc_compile_all_ccip b/contracts/scripts/native_solc_compile_all_ccip index b00071c9b3..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 )" 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 44df9df050..08c8086884 100644 --- a/contracts/src/v0.8/ccip/offRamp/EVM2EVMMultiOffRamp.sol +++ b/contracts/src/v0.8/ccip/offRamp/EVM2EVMMultiOffRamp.sol @@ -24,8 +24,8 @@ 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. @@ -51,7 +51,6 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { 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); @@ -59,6 +58,7 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { 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); @@ -89,20 +89,16 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { /// @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 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 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 @@ -345,14 +341,19 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { 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 @@ -363,14 +364,15 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { // 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 @@ -381,7 +383,7 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { originalState == Internal.MessageExecutionState.UNTOUCHED || originalState == Internal.MessageExecutionState.FAILURE ) - ) revert AlreadyExecuted(sourceChainSelector, message.sequenceNumber); + ) revert AlreadyExecuted(sourceChainSelector, message.header.sequenceNumber); if (manualExecution) { bool isOldCommitReport = @@ -400,7 +402,7 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { // 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); } } @@ -410,12 +412,10 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { // FAILURE -> FAILURE no nonce bump // FAILURE -> SUCCESS no nonce bump // UNTOUCHED messages MUST be executed in order always - if (message.nonce > 0 && originalState == Internal.MessageExecutionState.UNTOUCHED) { + 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.nonce, abi.encode(message.sender) - ) + !INonceManager(i_nonceManager).incrementInboundNonce(sourceChainSelector, message.header.nonce, message.sender) ) continue; } @@ -423,12 +423,12 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { // 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 @@ -439,26 +439,28 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { ) { // 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); } - 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) {} @@ -476,7 +478,7 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { 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. @@ -490,21 +492,27 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { /// 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)) { @@ -535,11 +543,6 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { 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 │ // ================================================================ @@ -728,21 +731,20 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { 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.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) { + } else if (keccak256(currentOnRamp) != keccak256(newOnRamp)) { revert InvalidStaticConfig(sourceChainSelector); } @@ -788,9 +790,11 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { /// @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, @@ -863,8 +867,12 @@ contract EVM2EVMMultiOffRamp is ITypeAndVersion, MultiOCR3Base { /// @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 f6e332701d..b780f4cfad 100644 --- a/contracts/src/v0.8/ccip/test/NonceManager.t.sol +++ b/contracts/src/v0.8/ccip/test/NonceManager.t.sol @@ -322,20 +322,28 @@ 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, ON_RAMP_ADDRESS_1 + 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, ON_RAMP_ADDRESS_2 + 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, ON_RAMP_ADDRESS_2 + 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); @@ -345,8 +353,9 @@ contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { 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(ON_RAMP_ADDRESS_3, address(0))); + 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 = @@ -374,12 +383,13 @@ contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { } function test_Upgraded_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( SOURCE_CHAIN_SELECTOR_1, - messages[0].sequenceNumber, - messages[0].messageId, + messages[0].header.sequenceNumber, + messages[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -388,23 +398,26 @@ contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { } function test_NoPrevOffRampForChain_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + 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(_generateSingleRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); + 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.EVM2EVMMessage[] memory messagesChain3 = + Internal.Any2EVMRampMessage[] 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, + messagesChain3[0].header.sequenceNumber, + messagesChain3[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); @@ -413,17 +426,19 @@ contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messagesChain3), new uint256[](0) ); assertEq( - startNonceChain3 + 1, - s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, abi.encode(messagesChain3[0].sender)) + startNonceChain3 + 1, s_inboundNonceManager.getInboundNonce(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); + 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(_generateSingleRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); + 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++; @@ -437,12 +452,13 @@ contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { } function test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_2, ON_RAMP_ADDRESS_2); + 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( - _generateSingleRampReportFromMessages(SOURCE_CHAIN_SELECTOR_2, messages), new uint256[](0) + _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_2, messages), new uint256[](0) ); // messages contains a single message - update for the next execution @@ -458,122 +474,190 @@ contract NonceManager_OffRampUpgrade is EVM2EVMMultiOffRampSetup { } function test_UpgradedNonceStartsAtV1Nonce_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + 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(_generateSingleRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); + 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)) ); - messages[0].nonce++; - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + 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, - messages[0].sequenceNumber, - messages[0].messageId, + messagesMultiRamp[0].header.sequenceNumber, + messagesMultiRamp[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); - s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp), new uint256[](0) + ); assertEq( - startNonce + 2, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)) + startNonce + 2, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp[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)); + 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, - messages[0].sequenceNumber, - messages[0].messageId, + messagesMultiRamp[0].header.sequenceNumber, + messagesMultiRamp[0].header.messageId, Internal.MessageExecutionState.SUCCESS, "" ); - s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp), new uint256[](0) + ); assertEq( - startNonce + 3, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)) + startNonce + 3, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp[0].sender) ); } function test_UpgradedNonceNewSenderStartsAtZero_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + 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) + ); - s_prevOffRamp.execute(_generateSingleRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); + Internal.Any2EVMRampMessage[] memory messagesMultiRamp = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - 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)); + 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, - messages[0].sequenceNumber, - messages[0].messageId, + 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, abi.encode(newSender)), 0); - s_offRamp.executeSingleReport(_generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); - assertEq(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(newSender)), 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.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); 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)); + 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, abi.encode(messages[0].sender)); + 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].nonce, abi.encode(newSender)); + 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, abi.encode(messages[0].sender))); + 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); - messages[0].nonce = 1; - messages[0].messageId = Internal._hash(messages[0], s_prevOffRamp.metadataHash()); + 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(_generateSingleRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new uint256[](0)); + s_prevOffRamp.execute( + _generateSingleLaneRampReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesSingleLane), new uint256[](0) + ); assertEq( - startNonce + 1, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)) + startNonce + 1, + s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messagesSingleLane[0].sender)) ); - messages[0].nonce = 2; - messages[0].messageId = - Internal._hash(messages[0], s_offRamp.metadataHash(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)); + 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].sequenceNumber, - messages[0].messageId, + 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, abi.encode(messages[0].sender)) + 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/e2e/MultiRampsEnd2End.sol b/contracts/src/v0.8/ccip/test/e2e/MultiRampsEnd2End.sol index d8cc6cc97c..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; @@ -89,12 +89,13 @@ contract MultiRampsE2E is EVM2EVMMultiOnRampSetup, EVM2EVMMultiOffRampSetup { sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR, isEnabled: true, - 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, - onRamp: address(s_onRamp2) + onRamp: abi.encode(address(s_onRamp2)) }); _setupMultipleOffRampsFromConfigs(sourceChainConfigs); @@ -108,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); @@ -124,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); @@ -139,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] }); @@ -176,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, "" ); @@ -185,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, "" ); @@ -194,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, "" ); @@ -215,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); @@ -242,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/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 edb27d1a74..c994cf5698 100644 --- a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRamp.t.sol +++ b/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRamp.t.sol @@ -49,28 +49,20 @@ contract EVM2EVMMultiOffRamp_constructor is EVM2EVMMultiOffRampSetup { new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](2); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - onRamp: ON_RAMP_ADDRESS_1 + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true }); sourceChainConfigs[1] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1 + 1, - isEnabled: true, - 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, - 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, - 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); @@ -148,8 +140,8 @@ contract EVM2EVMMultiOffRamp_constructor is EVM2EVMMultiOffRampSetup { new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - onRamp: address(0) + onRamp: new bytes(0), + isEnabled: true }); vm.expectRevert(EVM2EVMMultiOffRamp.ZeroAddressNotAllowed.selector); @@ -172,7 +164,7 @@ contract EVM2EVMMultiOffRamp_constructor is EVM2EVMMultiOffRampSetup { EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); sourceChainConfigs[0] = - EVM2EVMMultiOffRamp.SourceChainConfigArgs({sourceChainSelector: 0, isEnabled: true, onRamp: address(0)}); + EVM2EVMMultiOffRamp.SourceChainConfigArgs({sourceChainSelector: 0, onRamp: ON_RAMP_ADDRESS_1, isEnabled: true}); vm.expectRevert(EVM2EVMMultiOffRamp.ZeroChainSelectorNotAllowed.selector); @@ -326,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 @@ -414,114 +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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, abi.encode(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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, abi.encode(messagesChain2[0].sender)), 0); + assertGt(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, messagesChain2[0].sender), 0); // Other chain's nonce is unaffected - assertEq( - s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(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; @@ -529,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, @@ -550,50 +468,51 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { } 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 NonceManager.SkippedIncorrectNonce( - messages[0].sourceChainSelector, messages[0].nonce, abi.encode(messages[0].sender) + 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 NonceManager.SkippedIncorrectNonce(SOURCE_CHAIN_SELECTOR_1, messages[1].nonce, abi.encode(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); + function test__execute_SkippedAlreadyExecutedMessage_Success() public { + 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, "" ); @@ -601,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, "" ); @@ -624,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)); } @@ -632,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, "" ); @@ -652,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, "" ); @@ -671,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, "" ); @@ -689,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, "" ); @@ -702,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, "" ); @@ -720,8 +640,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, "" ); @@ -734,7 +654,7 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { } 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) { @@ -746,16 +666,15 @@ 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, "" ); @@ -769,7 +688,7 @@ 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) ); } @@ -781,7 +700,8 @@ contract EVM2EVMMultiOffRamp_executeSingleReport is EVM2EVMMultiOffRampSetup { 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), @@ -790,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, @@ -822,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)); } @@ -909,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) @@ -920,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) ); @@ -933,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) ); @@ -941,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); @@ -985,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) ) ); @@ -993,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; @@ -1001,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, @@ -1019,11 +944,25 @@ 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)); } + + 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, + interval: EVM2EVMMultiOffRamp.Interval(1, 2), + merkleRoot: merkleRoot + }); + + return EVM2EVMMultiOffRamp.CommitReport({ + priceUpdates: getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), + merkleRoots: roots + }); + } } contract EVM2EVMMultiOffRamp_executeSingleMessage is EVM2EVMMultiOffRampSetup { @@ -1034,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)); @@ -1049,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], @@ -1069,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)); @@ -1089,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)); @@ -1104,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); @@ -1114,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; @@ -1125,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)); @@ -1136,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, @@ -1154,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, @@ -1182,27 +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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(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); @@ -1214,41 +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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages1[0].sender)); + uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages1[0].sender); s_offRamp.batchExecute(reports, new uint256[][](2)); - assertGt( - s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(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); @@ -1260,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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages1[0].sender)); - uint64 nonceChain3 = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, abi.encode(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); @@ -1296,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); @@ -1304,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)); } @@ -1343,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); @@ -1369,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); @@ -1380,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, "" ); @@ -1392,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); @@ -1403,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, "" ); @@ -1417,14 +1355,13 @@ 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_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)) + messages[0].header.nonce - 1, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender) ); s_reverting_receiver.setRevert(true); @@ -1432,8 +1369,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.FAILURE, abi.encodeWithSelector( EVM2EVMMultiOffRamp.ReceiverError.selector, @@ -1447,26 +1384,24 @@ contract EVM2EVMMultiOffRamp_manuallyExecute is EVM2EVMMultiOffRampSetup { s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); assertEq( - messages[0].nonce, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)) + 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); @@ -1485,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, "" ); @@ -1498,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, "" ); @@ -1511,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, "" ); @@ -1532,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, @@ -1544,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, "" ); @@ -1555,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); @@ -1565,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, "" ); @@ -1575,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, "") ); @@ -1601,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, "" ); @@ -1612,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); @@ -1628,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); @@ -1659,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); @@ -1702,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); @@ -1717,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)); @@ -1733,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("")) @@ -1755,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({ @@ -1768,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); @@ -1784,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 ) ) ); @@ -1812,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, "" ); @@ -1834,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); @@ -1847,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, "" ); @@ -1883,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); @@ -1895,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, "" ); @@ -1915,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); @@ -1926,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, @@ -1943,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, @@ -1975,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); @@ -1987,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); @@ -2013,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); @@ -2041,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); @@ -2070,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]); @@ -2242,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); @@ -2266,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); @@ -2286,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); @@ -2300,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 @@ -2320,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)); @@ -2343,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)); @@ -2743,17 +2678,13 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - onRamp: ON_RAMP_ADDRESS_1 - }); - - EVM2EVMMultiOffRamp.SourceChainConfig memory expectedSourceChainConfig = EVM2EVMMultiOffRamp.SourceChainConfig({ - isEnabled: true, - minSeqNr: 1, 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); @@ -2770,19 +2701,15 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - 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, - 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); @@ -2806,18 +2733,18 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](3); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - 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, - 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, - onRamp: address(uint160(ON_RAMP_ADDRESS_1) + 42) + onRamp: abi.encode(ON_RAMP_ADDRESS_1, 2), + isEnabled: true }); EVM2EVMMultiOffRamp.SourceChainConfig[] memory expectedSourceChainConfigs = @@ -2826,8 +2753,7 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam expectedSourceChainConfigs[i] = EVM2EVMMultiOffRamp.SourceChainConfig({ isEnabled: sourceChainConfigs[i].isEnabled, minSeqNr: 1, - onRamp: sourceChainConfigs[i].onRamp, - metadataHash: s_offRamp.metadataHash(sourceChainConfigs[i].sourceChainSelector, sourceChainConfigs[i].onRamp) + onRamp: abi.encode(ON_RAMP_ADDRESS_1, i) }); vm.expectEmit(); @@ -2841,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); } } @@ -2858,29 +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, - 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) { + 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, - onRamp: sourceChainConfigArgs.onRamp, - metadataHash: s_offRamp.metadataHash(sourceChainConfigArgs.sourceChainSelector, sourceChainConfigArgs.onRamp) + onRamp: sourceChainConfigArgs.onRamp }); if (isNewChain) { @@ -2905,8 +2824,8 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - onRamp: address(0) + onRamp: new bytes(0), + isEnabled: true }); vm.expectRevert(EVM2EVMMultiOffRamp.ZeroAddressNotAllowed.selector); @@ -2917,7 +2836,7 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); sourceChainConfigs[0] = - EVM2EVMMultiOffRamp.SourceChainConfigArgs({sourceChainSelector: 0, isEnabled: true, onRamp: ON_RAMP_ADDRESS_1}); + EVM2EVMMultiOffRamp.SourceChainConfigArgs({sourceChainSelector: 0, onRamp: ON_RAMP_ADDRESS_1, isEnabled: true}); vm.expectRevert(EVM2EVMMultiOffRamp.ZeroChainSelectorNotAllowed.selector); s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); @@ -2928,30 +2847,13 @@ contract EVM2EVMMultiOffRamp_applySourceChainConfigUpdates is EVM2EVMMultiOffRam new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - 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_ReplaceExistingChainOnRampAndPrevOffRamp_Revert() public { - EVM2EVMMultiOffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = - new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - 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].onRamp = ON_RAMP_ADDRESS_2; vm.expectRevert(abi.encodeWithSelector(EVM2EVMMultiOffRamp.InvalidStaticConfig.selector, SOURCE_CHAIN_SELECTOR_1)); s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); 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 e5d882b10e..7355d6a072 100644 --- a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRampSetup.t.sol +++ b/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMMultiOffRampSetup.t.sol @@ -35,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); @@ -175,18 +175,18 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba new EVM2EVMMultiOffRamp.SourceChainConfigArgs[](3); sourceChainConfigs[0] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - isEnabled: true, - onRamp: ON_RAMP_ADDRESS_1 + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true }); sourceChainConfigs[1] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_2, - isEnabled: false, - onRamp: ON_RAMP_ADDRESS_2 + onRamp: ON_RAMP_ADDRESS_2, + isEnabled: false }); sourceChainConfigs[2] = EVM2EVMMultiOffRamp.SourceChainConfigArgs({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_3, - isEnabled: true, - onRamp: ON_RAMP_ADDRESS_3 + onRamp: ON_RAMP_ADDRESS_3, + isEnabled: true }); _setupMultipleOffRampsFromConfigs(sourceChainConfigs); } @@ -196,14 +196,12 @@ 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, @@ -243,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) @@ -262,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 @@ -272,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]; @@ -293,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. @@ -326,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; @@ -356,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); @@ -392,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) @@ -431,7 +412,6 @@ contract EVM2EVMMultiOffRampSetup is TokenSetup, PriceRegistrySetup, MultiOCR3Ba assertEq(config1.isEnabled, config2.isEnabled); assertEq(config1.minSeqNr, config2.minSeqNr); assertEq(config1.onRamp, config2.onRamp); - assertEq(config1.metadataHash, config2.metadataHash); } function _getDefaultSourceTokenData(Client.EVMTokenAmount[] memory srcTokenAmounts) 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 f5a77a4070..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,16 +69,15 @@ type EVM2EVMMultiOffRampMerkleRoot struct { } type EVM2EVMMultiOffRampSourceChainConfig struct { - IsEnabled bool - MinSeqNr uint64 - OnRamp common.Address - MetadataHash [32]byte + IsEnabled bool + MinSeqNr uint64 + OnRamp []byte } type EVM2EVMMultiOffRampSourceChainConfigArgs struct { SourceChainSelector uint64 IsEnabled bool - OnRamp common.Address + OnRamp []byte } type EVM2EVMMultiOffRampStaticConfig struct { @@ -93,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 @@ -127,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 @@ -155,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\":\"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\":\"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\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"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\"},{\"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\":\"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\"}],\"name\":\"getSourceChainConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"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\":\"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\":[{\"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: "0x6101206040523480156200001257600080fd5b506040516200641738038062006417833981016040819052620000359162000631565b33806000816200008c5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000bf57620000bf81620001ee565b5050466080525060208201516001600160a01b03161580620000ec575060408201516001600160a01b0316155b8062000103575060608201516001600160a01b0316155b1562000122576040516342bcdf7f60e11b815260040160405180910390fd5b81516001600160401b03166000036200014e5760405163c656089560e01b815260040160405180910390fd5b81516001600160401b0390811660a052602080840180516001600160a01b0390811660c05260408087018051831660e0526060808901805185166101005283518a519098168852945184169587019590955251821690850152905116908201527f683eb52ee924eb817377cfa8f41f238f4bb7a877da5267869dfffbad85f564d89060800160405180910390a1620001e68162000299565b5050620007b8565b336001600160a01b03821603620002485760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000083565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60005b815181101562000505576000828281518110620002bd57620002bd620007a2565b60200260200101519050600081600001519050806001600160401b0316600003620002fb5760405163c656089560e01b815260040160405180910390fd5b60408201516001600160a01b031662000327576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160401b03811660009081526007602052604090208054690100000000000000000090046001600160a01b03166200041957620003938284604001517f8acd72527118c8324937b1a42e02cd246697c3b633f1742f3cae11de233722b36200050960201b60201c565b60018201556040838101518254610100600160481b03196001600160a01b0390921669010000000000000000000291909116610100600160e81b031990911617610100178255516001600160401b03831681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a162000465565b60408301518154690100000000000000000090046001600160a01b03908116911614620004655760405163c39a620560e01b81526001600160401b038316600482015260240162000083565b6020830151815490151560ff199091161781556040516001600160401b038316907fb8f0c74385134334c728fcac437ec6c6397c9f2c1440532b0c44175a090b140190620004ee908490815460ff811615158252600881901c6001600160401b0316602083015260481c6001600160a01b03166040820152600190910154606082015260800190565b60405180910390a25050508060010190506200029c565b5050565b60a0805160408051602081018590526001600160401b0380881692820192909252911660608201526001600160a01b0384166080820152600091016040516020818303038152906040528051906020012090509392505050565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b03811182821017156200059e576200059e62000563565b60405290565b604051606081016001600160401b03811182821017156200059e576200059e62000563565b604051601f8201601f191681016001600160401b0381118282101715620005f457620005f462000563565b604052919050565b80516001600160401b03811681146200061457600080fd5b919050565b80516001600160a01b03811681146200061457600080fd5b60008082840360a08112156200064657600080fd5b60808112156200065557600080fd5b506200066062000579565b6200066b84620005fc565b815260206200067c81860162000619565b818301526040620006906040870162000619565b60408401526060620006a56060880162000619565b606085015260808701519395506001600160401b0380851115620006c857600080fd5b848801945088601f860112620006dd57600080fd5b845181811115620006f257620006f262000563565b62000702858260051b01620005c9565b8181528581019250606090910286018501908a8211156200072257600080fd5b958501955b81871015620007915783878c031215620007415760008081fd5b6200074b620005a4565b6200075688620005fc565b81528688015180151581146200076c5760008081fd5b818801526200077d88870162000619565b818701528352958301959185019162000727565b809750505050505050509250929050565b634e487b7160e01b600052603260045260246000fd5b60805160a05160c05160e05161010051615bf0620008276000396000818161023e0152612b1d01526000818161020f0152612ed60152600081816101e001528181611557015261160e0152600081816101b00152612df8015260008181611baa0152611bf60152615bf06000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806385572ffb116100d8578063d2a15d351161008c578063f52121a511610066578063f52121a514610662578063f716f99f14610675578063ff888fb11461068857600080fd5b8063d2a15d3514610556578063e9d68a8e14610569578063f2fde38b1461064f57600080fd5b80638e7da1af116100bd5780638e7da1af146104de578063c673e584146104f1578063ccd37ba31461051157600080fd5b806385572ffb146104b55780638da5cb5b146104c357600080fd5b8063403b2d631161012f5780635e36480c116101145780635e36480c146103785780637437ff9f1461039857806379ba5097146104ad57600080fd5b8063403b2d6314610352578063542625af1461036557600080fd5b80632d04ab76116101605780632d04ab761461030e578063311cd513146103235780633f4b04aa1461033657600080fd5b806306285c691461017c578063181f5a77146102c5575b600080fd5b61026e60408051608081018252600080825260208201819052918101829052606081019190915260405180608001604052807f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250905090565b6040516102bc9190815167ffffffffffffffff1681526020808301516001600160a01b0390811691830191909152604080840151821690830152606092830151169181019190915260800190565b60405180910390f35b6103016040518060400160405280601d81526020017f45564d3245564d4d756c74694f666652616d7020312e362e302d64657600000081525081565b6040516102bc9190613f13565b61032161031c366004613fbe565b6106ab565b005b610321610331366004614071565b610a71565b600a5460405167ffffffffffffffff90911681526020016102bc565b6103216103603660046141fb565b610ada565b610321610373366004614816565b610c97565b61038b610386366004614941565b610e3c565b6040516102bc919061499e565b6104446040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506040805160c0810182526004546001600160a01b03808216835263ffffffff74010000000000000000000000000000000000000000830481166020850152600160c01b8304811694840194909452600160e01b90910490921660608201526005548216608082015260065490911660a082015290565b6040516102bc9190600060c0820190506001600160a01b03808451168352602084015163ffffffff808216602086015280604087015116604086015280606087015116606086015250508060808501511660808401528060a08501511660a08401525092915050565b610321610e92565b6103216101773660046149ac565b6000546040516001600160a01b0390911681526020016102bc565b6103216104ec3660046149e7565b610f50565b6105046104ff366004614acf565b610f64565b6040516102bc9190614b2f565b61054861051f366004614ba4565b67ffffffffffffffff919091166000908152600960209081526040808320938352929052205490565b6040519081526020016102bc565b610321610564366004614bce565b6110c2565b610607610577366004614c43565b6040805160808101825260008082526020820181905291810182905260608101919091525067ffffffffffffffff9081166000908152600760209081526040918290208251608081018452815460ff81161515825261010081049095169281019290925269010000000000000000009093046001600160a01b031691810191909152600190910154606082015290565b6040516102bc919081511515815260208083015167ffffffffffffffff16908201526040808301516001600160a01b0316908201526060918201519181019190915260800190565b61032161065d366004614c5e565b61117c565b610321610670366004614c7b565b61118d565b610321610683366004614d47565b6114bf565b61069b610696366004614e92565b611501565b60405190151581526020016102bc565b60006106b98789018961501b565b805151519091501515806106d257508051602001515115155b156107d257600a5460208a01359067ffffffffffffffff8083169116101561079157600a805467ffffffffffffffff191667ffffffffffffffff831617905560065482516040517f3937306f0000000000000000000000000000000000000000000000000000000081526001600160a01b0390921691633937306f9161075a9160040161524d565b600060405180830381600087803b15801561077457600080fd5b505af1158015610788573d6000803e3d6000fd5b505050506107d0565b8160200151516000036107d0576040517f2261116700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b60005b8160200151518110156109ba576000826020015182815181106107fa576107fa615186565b60200260200101519050600081600001519050610816816115c2565b6000610821826116c4565b602084015151815491925067ffffffffffffffff90811661010090920416141580610863575060208084015190810151905167ffffffffffffffff9182169116115b156108ac57825160208401516040517feefb0cac0000000000000000000000000000000000000000000000000000000081526108a3929190600401615260565b60405180910390fd5b6040830151806108e8576040517f504570e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b835167ffffffffffffffff1660009081526009602090815260408083208484529091529020541561095b5783516040517f32cf0cbf00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602481018290526044016108a3565b602080850151015161096e9060016152ab565b825468ffffffffffffffff00191661010067ffffffffffffffff9283160217909255925116600090815260096020908152604080832094835293905291909120429055506001016107d5565b507f3a3950e13dd607cc37980db0ef14266c40d2bba9c01b2e44bfe549808883095d816040516109ea91906152d3565b60405180910390a1610a6660008a8a8a8a8a8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808e0282810182019093528d82529093508d92508c9182918501908490808284376000920191909152508b9250611724915050565b505050505050505050565b610ab1610a8082840184615370565b6040805160008082526020820190925290610aab565b6060815260200190600190039081610a965790505b50611a9b565b604080516000808252602082019092529050610ad4600185858585866000611724565b50505050565b610ae2611b4b565b60a08101516001600160a01b03161580610b04575080516001600160a01b0316155b15610b3b576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516004805460208085018051604080880180516060808b0180516001600160a01b039b8c167fffffffffffffffff000000000000000000000000000000000000000000000000909a168a177401000000000000000000000000000000000000000063ffffffff988916021777ffffffffffffffffffffffffffffffffffffffffffffffff16600160c01b948816949094026001600160e01b031693909317600160e01b93871693909302929092179098556080808b0180516005805473ffffffffffffffffffffffffffffffffffffffff19908116928e1692909217905560a0808e01805160068054909416908f161790925586519a8b5297518716988a0198909852925185169388019390935251909216958501959095525185169383019390935251909216908201527f0da37fd00459f4f5f0b8210d31525e4910ae674b8bab34b561d146bb45773a4c9060c00160405180910390a150565b610c9f611ba7565b815181518114610cdb576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610e2c576000848281518110610cfa57610cfa615186565b60200260200101519050600081602001515190506000858481518110610d2257610d22615186565b6020026020010151905080518214610d66576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b82811015610e1d576000828281518110610d8557610d85615186565b6020026020010151905080600014158015610dc0575084602001518281518110610db157610db1615186565b60200260200101516080015181105b15610e145784516040517fc8e9605100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff909116600482015260248101839052604481018290526064016108a3565b50600101610d69565b50505050806001019050610cde565b50610e378383611a9b565b505050565b6000610e4a600160046153a5565b6002610e576080856153ce565b67ffffffffffffffff16610e6b91906153f5565b610e758585611c28565b901c166003811115610e8957610e89614974565b90505b92915050565b6001546001600160a01b03163314610eec5760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016108a3565b600080543373ffffffffffffffffffffffffffffffffffffffff19808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610f58611b4b565b610f6181611c6f565b50565b610fa76040805160e081019091526000606082018181526080830182905260a0830182905260c08301919091528190815260200160608152602001606081525090565b60ff808316600090815260026020818152604092839020835160e081018552815460608201908152600183015480881660808401526101008104881660a0840152620100009004909616151560c08201529485529182018054845181840281018401909552808552929385830193909283018282801561105057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611032575b50505050508152602001600382018054806020026020016040519081016040528092919081815260200182805480156110b257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611094575b5050505050815250509050919050565b6110ca611b4b565b60005b81811015610e375760008383838181106110e9576110e9615186565b9050604002018036038101906110ff919061540c565b905061110e8160200151611501565b61117357805167ffffffffffffffff1660009081526009602090815260408083208285018051855290835281842093909355915191519182527f202f1139a3e334b6056064c0e9b19fd07e44a88d8f6e5ded571b24cf8c371f12910160405180910390a15b506001016110cd565b611184611b4b565b610f6181611f30565b3330146111c6576040517f371a732800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160008082526020820190925281611203565b60408051808201909152600080825260208201528152602001906001900390816111dc5790505b50610140840151519091501561126457611261836101400151846020015160405160200161124091906001600160a01b0391909116815260200190565b60408051601f19818403018152918152860151865161016088015187611fe6565b90505b60006112708483612093565b6005549091506001600160a01b0316801561135d576040517f08d450a10000000000000000000000000000000000000000000000000000000081526001600160a01b038216906308d450a1906112ca9085906004016154f3565b600060405180830381600087803b1580156112e457600080fd5b505af19250505080156112f5575060015b61135d573d808015611323576040519150601f19603f3d011682016040523d82523d6000602084013e611328565b606091505b50806040517f09c253250000000000000000000000000000000000000000000000000000000081526004016108a39190613f13565b6101208501515115801561137357506080850151155b8061138a575060408501516001600160a01b03163b155b806113ca575060408501516113c8906001600160a01b03167f85572ffb00000000000000000000000000000000000000000000000000000000612136565b155b156113d6575050505050565b60048054608087015160408089015190517f3cf9798300000000000000000000000000000000000000000000000000000000815260009485946001600160a01b031693633cf9798393611431938a9361138893929101615506565b6000604051808303816000875af1158015611450573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114789190810190615587565b5091509150816114b657806040517f0a8d6e8c0000000000000000000000000000000000000000000000000000000081526004016108a39190613f13565b50505050505050565b6114c7611b4b565b60005b81518110156114fd576114f58282815181106114e8576114e8615186565b6020026020010151612152565b6001016114ca565b5050565b6040805180820182523081526020810183815291517f4d61677100000000000000000000000000000000000000000000000000000000815290516001600160a01b039081166004830152915160248201526000917f00000000000000000000000000000000000000000000000000000000000000001690634d61677190604401602060405180830381865afa15801561159e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8c91906155e1565b6040517f2cbc26bb000000000000000000000000000000000000000000000000000000008152608082901b77ffffffffffffffff000000000000000000000000000000001660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632cbc26bb90602401602060405180830381865afa15801561165d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061168191906155e1565b15610f61576040517ffdbd6a7200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108a3565b67ffffffffffffffff81166000908152600760205260408120805460ff16610e8c576040517fed053c5900000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016108a3565b60ff878116600090815260026020908152604080832081516080810183528154815260019091015480861693820193909352610100830485169181019190915262010000909104909216151560608301528735906117838760a46155fe565b90508260600151156117cb57845161179c9060206153f5565b86516117a99060206153f5565b6117b49060a06155fe565b6117be91906155fe565b6117c890826155fe565b90505b36811461180d576040517f8e1192e1000000000000000000000000000000000000000000000000000000008152600481018290523660248201526044016108a3565b50815181146118555781516040517f93df584c0000000000000000000000000000000000000000000000000000000081526004810191909152602481018290526044016108a3565b61185d611ba7565b60ff808a16600090815260036020908152604080832033845282528083208151808301909252805480861683529394919390928401916101009091041660028111156118ab576118ab614974565b60028111156118bc576118bc614974565b90525090506002816020015160028111156118d9576118d9614974565b14801561192d5750600260008b60ff1660ff168152602001908152602001600020600301816000015160ff168154811061191557611915615186565b6000918252602090912001546001600160a01b031633145b611963576040517fda0f08e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50816060015115611a4557602082015161197e906001615611565b60ff168551146119ba576040517f71253a2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83518551146119f5576040517fa75d88af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008787604051611a0792919061562a565b604051908190038120611a1e918b9060200161563a565b604051602081830303815290604052805190602001209050611a438a8288888861245d565b505b6040805182815260208a81013567ffffffffffffffff169082015260ff8b16917f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef0910160405180910390a2505050505050505050565b8151600003611ad5576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160408051600080825260208201909252911591905b8451811015611b4457611b3c858281518110611b0a57611b0a615186565b602002602001015184611b3657858381518110611b2957611b29615186565b6020026020010151612674565b83612674565b600101611aec565b5050505050565b6000546001600160a01b03163314611ba55760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016108a3565b565b467f000000000000000000000000000000000000000000000000000000000000000014611ba5576040517f0f01ce850000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201524660248201526044016108a3565b67ffffffffffffffff8216600090815260086020526040812081611c4d60808561564e565b67ffffffffffffffff1681526020810191909152604001600020549392505050565b60005b81518110156114fd576000828281518110611c8f57611c8f615186565b602002602001015190506000816000015190508067ffffffffffffffff16600003611ce6576040517fc656089500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516001600160a01b0316611d2a576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811660009081526007602052604090208054690100000000000000000090046001600160a01b0316611e2c57611d8e8284604001517f8acd72527118c8324937b1a42e02cd246697c3b633f1742f3cae11de233722b3612df2565b6001820155604083810151825468ffffffffffffffff00196001600160a01b03909216690100000000000000000002919091167fffffff00000000000000000000000000000000000000000000000000000000ff909116176101001782555167ffffffffffffffff831681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a1611e90565b60408301518154690100000000000000000090046001600160a01b03908116911614611e90576040517fc39a620500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff831660048201526024016108a3565b6020830151815490151560ff1990911617815560405167ffffffffffffffff8316907fb8f0c74385134334c728fcac437ec6c6397c9f2c1440532b0c44175a090b140190611f1a908490815460ff811615158252600881901c67ffffffffffffffff16602083015260481c6001600160a01b03166040820152600190910154606082015260800190565b60405180910390a2505050806001019050611c72565b336001600160a01b03821603611f885760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016108a3565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b8560005b87518110156120885761206388828151811061200857612008615186565b60200260200101516020015188888888868151811061202957612029615186565b60200260200101518060200190518101906120449190615675565b88878151811061205657612056615186565b6020026020010151612e75565b82828151811061207557612075615186565b6020908102919091010152600101611fea565b509695505050505050565b6040805160a08101825260008082526020820152606091810182905281810182905260808101919091526040518060a001604052808461018001518152602001846000015167ffffffffffffffff168152602001846020015160405160200161210b91906001600160a01b0391909116815260200190565b6040516020818303038152906040528152602001846101200151815260200183815250905092915050565b6000612141836131ee565b8015610e895750610e89838361323a565b806040015160ff1660000361217d576000604051631b3fab5160e11b81526004016108a3919061572a565b60208082015160ff808216600090815260029093526040832060018101549293909283921690036121ce576060840151600182018054911515620100000262ff000019909216919091179055612223565b6060840151600182015460ff6201000090910416151590151514612223576040517f87f6037c00000000000000000000000000000000000000000000000000000000815260ff841660048201526024016108a3565b60a08401518051601f60ff82161115612252576001604051631b3fab5160e11b81526004016108a3919061572a565b6122b885856003018054806020026020016040519081016040528092919081815260200182805480156122ae57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612290575b50505050506132dc565b8560600151156123ca5761232685856002018054806020026020016040519081016040528092919081815260200182805480156122ae576020028201919060005260206000209081546001600160a01b031681526001909101906020018083116122905750505050506132dc565b608086015180516123409060028701906020840190613e21565b50805160018501805461ff00191661010060ff841690810291909117909155601f1015612383576002604051631b3fab5160e11b81526004016108a3919061572a565b6040880151612393906003615744565b60ff168160ff16116123bb576003604051631b3fab5160e11b81526004016108a3919061572a565b6123c787836001613345565b50505b6123d685836002613345565b81516123eb9060038601906020850190613e21565b5060408681015160018501805460ff191660ff8316179055875180865560a089015192517fab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f54793612444938a939260028b01929190615760565b60405180910390a1612455856134c5565b505050505050565b612465613e8f565b835160005b8181101561266a57600060018886846020811061248957612489615186565b61249691901a601b615611565b8985815181106124a8576124a8615186565b60200260200101518986815181106124c2576124c2615186565b602002602001015160405160008152602001604052604051612500949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015612522573d6000803e3d6000fd5b505060408051601f1981015160ff808e166000908152600360209081528582206001600160a01b0385168352815285822085870190965285548084168652939750909550929392840191610100900416600281111561258357612583614974565b600281111561259457612594614974565b90525090506001816020015160028111156125b1576125b1614974565b146125e8576040517fca31867a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051859060ff16601f81106125ff576125ff615186565b60200201511561263b576040517ff67bc7c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600185826000015160ff16601f811061265657612656615186565b91151560209092020152505060010161246a565b5050505050505050565b815161267f816115c2565b600061268a826116c4565b60208501515190915060008190036126cd576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b846040015151811461270b576040517f57e0e08300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008167ffffffffffffffff811115612726576127266140c5565b60405190808252806020026020018201604052801561274f578160200160208202803683370190505b50905060005b828110156128145760008760200151828151811061277557612775615186565b6020026020010151905061278d8186600101546134e1565b83838151811061279f5761279f615186565b6020026020010181815250508061018001518383815181106127c3576127c3615186565b60200260200101511461280b578061018001516040517f345039be0000000000000000000000000000000000000000000000000000000081526004016108a391815260200190565b50600101612755565b50600061282b858389606001518a6080015161363d565b905080600003612873576040517f7dd17a7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff861660048201526024016108a3565b8551151560005b84811015610a665760008960200151828151811061289a5761289a615186565b6020026020010151905060006128b4898360600151610e3c565b905060028160038111156128ca576128ca614974565b036129205760608201516040805167ffffffffffffffff808d16825290921660208301527f3b575419319662b2a6f5e2467d84521517a3382b908eb3d557bb3fdb0c50e23c910160405180910390a15050612dea565b600081600381111561293457612934614974565b14806129515750600381600381111561294f5761294f614974565b145b6129a15760608201516040517f25507e7f00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808c16600483015290911660248201526044016108a3565b8315612a825760045460009074010000000000000000000000000000000000000000900463ffffffff166129d587426153a5565b11905080806129f5575060038260038111156129f3576129f3614974565b145b612a37576040517fa9cfc86200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8b1660048201526024016108a3565b8a8481518110612a4957612a49615186565b6020026020010151600014612a7c578a8481518110612a6a57612a6a615186565b60200260200101518360800181815250505b50612ae7565b6000816003811115612a9657612a96614974565b14612ae75760608201516040517f3ef2a99c00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808c16600483015290911660248201526044016108a3565b60008260c0015167ffffffffffffffff16118015612b1657506000816003811115612b1457612b14614974565b145b15612bef577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663e0e03cae8a8460c001518560200151604051602001612b7491906001600160a01b0391909116815260200190565b6040516020818303038152906040526040518463ffffffff1660e01b8152600401612ba1939291906157e6565b6020604051808303816000875af1158015612bc0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612be491906155e1565b612bef575050612dea565b60008b604001518481518110612c0757612c07615186565b6020026020010151905080518361014001515114612c6b5760608301516040517f1cfe6d8b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808d16600483015290911660248201526044016108a3565b612c7b8a84606001516001613693565b600080612c88858461373b565b91509150612c9b8c866060015184613693565b868015612cb957506003826003811115612cb757612cb7614974565b145b8015612cd757506000846003811115612cd457612cd4614974565b14155b15612d1757846101800151816040517f2b11b8d90000000000000000000000000000000000000000000000000000000081526004016108a3929190615813565b6003826003811115612d2b57612d2b614974565b14158015612d4b57506002826003811115612d4857612d48614974565b14155b15612d8b5760608501516040517f926c5a3e0000000000000000000000000000000000000000000000000000000081526108a3918e91859060040161582c565b846101800151856060015167ffffffffffffffff168d67ffffffffffffffff167f8c324ce1367b83031769f6a813e3bb4c117aba2185789d66b98b791405be6df28585604051612ddc929190615852565b60405180910390a450505050505b60010161287a565b600081847f000000000000000000000000000000000000000000000000000000000000000085604051602001612e55949392919093845267ffffffffffffffff9283166020850152911660408301526001600160a01b0316606082015260800190565b6040516020818303038152906040528051906020012090505b9392505050565b60408051808201909152600080825260208201526000612e988460200151613986565b6040517fbbe4f6db0000000000000000000000000000000000000000000000000000000081526001600160a01b0380831660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063bbe4f6db90602401602060405180830381865afa158015612f1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f419190615872565b90506001600160a01b0381161580612f895750612f876001600160a01b0382167faff2afbf00000000000000000000000000000000000000000000000000000000612136565b155b15612fcb576040517fae9b4ce90000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016108a3565b600080613096633907753760e01b6040518061010001604052808d81526020018b67ffffffffffffffff1681526020018c6001600160a01b031681526020018e8152602001876001600160a01b031681526020018a6000015181526020018a60400151815260200189815250604051602401613047919061588f565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152600454859063ffffffff600160e01b9091041661138860846139c8565b5091509150816130bb578060405163e1cd550960e01b81526004016108a39190613f13565b80516020146131035780516040517f78ef80240000000000000000000000000000000000000000000000000000000081526020600482015260248101919091526044016108a3565b6000818060200190518101906131199190615966565b604080516001600160a01b038d16602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b03167fa9059cbb0000000000000000000000000000000000000000000000000000000017905260045491925061319c918790600160c01b900463ffffffff1661138860846139c8565b509093509150826131c2578160405163e1cd550960e01b81526004016108a39190613f13565b604080518082019091526001600160a01b03909516855260208501525091925050509695505050505050565b600061321a827f01ffc9a70000000000000000000000000000000000000000000000000000000061323a565b8015610e8c5750613233826001600160e01b031961323a565b1592915050565b604080516001600160e01b03198316602480830191909152825180830390910181526044909101909152602080820180516001600160e01b03167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d915060005190508280156132c5575060208210155b80156132d15750600081115b979650505050505050565b60005b8151811015610e375760ff83166000908152600360205260408120835190919084908490811061331157613311615186565b6020908102919091018101516001600160a01b03168252810191909152604001600020805461ffff191690556001016132df565b60005b82518160ff161015610ad4576000838260ff168151811061336b5761336b615186565b602002602001015190506000600281111561338857613388614974565b60ff80871660009081526003602090815260408083206001600160a01b038716845290915290205461010090041660028111156133c7576133c7614974565b146133e8576004604051631b3fab5160e11b81526004016108a3919061572a565b6001600160a01b038116613428576040517fd6c62c9b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052808360ff16815260200184600281111561344e5761344e614974565b905260ff80871660009081526003602090815260408083206001600160a01b0387168452825290912083518154931660ff198416811782559184015190929091839161ffff1916176101008360028111156134ab576134ab614974565b021790555090505050806134be9061597f565b9050613348565b60ff8116610f6157600a805467ffffffffffffffff1916905550565b60008060001b8284602001518560400151866060015187608001518860a001518960c001518a60e001518b61010001516040516020016135779897969594939291906001600160a01b039889168152968816602088015267ffffffffffffffff95861660408801526060870194909452911515608086015290921660a0840152921660c082015260e08101919091526101000190565b60405160208183030381529060405280519060200120856101200151805190602001208661014001516040516020016135b0919061599e565b604051602081830303815290604052805190602001208761016001516040516020016135dc9190615a53565b60408051601f198184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e0015b60405160208183030381529060405280519060200120905092915050565b60008061364b858585613aee565b905061365681611501565b61366457600091505061368b565b67ffffffffffffffff86166000908152600960209081526040808320938352929052205490505b949350505050565b600060026136a26080856153ce565b67ffffffffffffffff166136b691906153f5565b905060006136c48585611c28565b9050816136d3600160046153a5565b901b1916818360038111156136ea576136ea614974565b67ffffffffffffffff871660009081526008602052604081209190921b9290921791829161371960808861564e565b67ffffffffffffffff1681526020810191909152604001600020555050505050565b6040517ff52121a5000000000000000000000000000000000000000000000000000000008152600090606090309063f52121a59061377f9087908790600401615a66565b600060405180830381600087803b15801561379957600080fd5b505af19250505080156137aa575060015b61396a573d8080156137d8576040519150601f19603f3d011682016040523d82523d6000602084013e6137dd565b606091505b5060006137e982615bab565b90507f0a8d6e8c000000000000000000000000000000000000000000000000000000006001600160e01b031982161480613833575063e1cd550960e01b6001600160e01b03198216145b8061384e575063046b337b60e51b6001600160e01b03198216145b8061388257507f78ef8024000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b806138b657507f0c3b563c000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b806138ea57507fae9b4ce9000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b8061391e57507f09c25325000000000000000000000000000000000000000000000000000000006001600160e01b03198216145b1561392f575060039250905061397f565b856101800151826040517f2b11b8d90000000000000000000000000000000000000000000000000000000081526004016108a3929190615813565b50506040805160208101909152600081526002905b9250929050565b600081516020146139ac578160405163046b337b60e51b81526004016108a39190613f13565b610e8c828060200190518101906139c39190615966565b613d8c565b6000606060008361ffff1667ffffffffffffffff8111156139eb576139eb6140c5565b6040519080825280601f01601f191660200182016040528015613a15576020820181803683370190505b509150863b613a48577f0c3b563c0000000000000000000000000000000000000000000000000000000060005260046000fd5b5a85811015613a7b577fafa32a2c0000000000000000000000000000000000000000000000000000000060005260046000fd5b8590036040810481038710613ab4577f37c3be290000000000000000000000000000000000000000000000000000000060005260046000fd5b505a6000808a5160208c0160008c8cf193505a900390503d84811115613ad75750835b808352806000602085013e50955095509592505050565b8251825160009190818303613b2f576040517f11a6b26400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101018211801590613b4357506101018111155b613b60576040516309bde33960e01b815260040160405180910390fd5b60001982820101610100811115613b8a576040516309bde33960e01b815260040160405180910390fd5b80600003613bb75786600081518110613ba557613ba5615186565b60200260200101519350505050612e6e565b60008167ffffffffffffffff811115613bd257613bd26140c5565b604051908082528060200260200182016040528015613bfb578160200160208202803683370190505b50905060008080805b85811015613d255760006001821b8b811603613c5f5788851015613c48578c5160018601958e918110613c3957613c39615186565b60200260200101519050613c81565b8551600185019487918110613c3957613c39615186565b8b5160018401938d918110613c7657613c76615186565b602002602001015190505b600089861015613cb1578d5160018701968f918110613ca257613ca2615186565b60200260200101519050613cd3565b8651600186019588918110613cc857613cc8615186565b602002602001015190505b82851115613cf4576040516309bde33960e01b815260040160405180910390fd5b613cfe8282613de0565b878481518110613d1057613d10615186565b60209081029190910101525050600101613c04565b506001850382148015613d3757508683145b8015613d4257508581145b613d5f576040516309bde33960e01b815260040160405180910390fd5b836001860381518110613d7457613d74615186565b60200260200101519750505050505050509392505050565b60006001600160a01b03821180613da4575061040082105b15613ddc5760408051602081018490520160408051601f198184030181529082905263046b337b60e51b82526108a391600401613f13565b5090565b6000818310613df857613df38284613dfe565b610e89565b610e8983835b60408051600160208201529081018390526060810182905260009060800161361f565b828054828255906000526020600020908101928215613e83579160200282015b82811115613e83578251825473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909116178255602090920191600190910190613e41565b50613ddc929150613eae565b604051806103e00160405280601f906020820280368337509192915050565b5b80821115613ddc5760008155600101613eaf565b60005b83811015613ede578181015183820152602001613ec6565b50506000910152565b60008151808452613eff816020860160208601613ec3565b601f01601f19169290920160200192915050565b602081526000610e896020830184613ee7565b8060608101831015610e8c57600080fd5b60008083601f840112613f4957600080fd5b50813567ffffffffffffffff811115613f6157600080fd5b60208301915083602082850101111561397f57600080fd5b60008083601f840112613f8b57600080fd5b50813567ffffffffffffffff811115613fa357600080fd5b6020830191508360208260051b850101111561397f57600080fd5b60008060008060008060008060e0898b031215613fda57600080fd5b613fe48a8a613f26565b9750606089013567ffffffffffffffff8082111561400157600080fd5b61400d8c838d01613f37565b909950975060808b013591508082111561402657600080fd5b6140328c838d01613f79565b909750955060a08b013591508082111561404b57600080fd5b506140588b828c01613f79565b999c989b50969995989497949560c00135949350505050565b60008060006080848603121561408657600080fd5b6140908585613f26565b9250606084013567ffffffffffffffff8111156140ac57600080fd5b6140b886828701613f37565b9497909650939450505050565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff811182821017156140fe576140fe6140c5565b60405290565b6040805190810167ffffffffffffffff811182821017156140fe576140fe6140c5565b6040516101a0810167ffffffffffffffff811182821017156140fe576140fe6140c5565b60405160a0810167ffffffffffffffff811182821017156140fe576140fe6140c5565b6040516060810167ffffffffffffffff811182821017156140fe576140fe6140c5565b604051601f8201601f1916810167ffffffffffffffff811182821017156141ba576141ba6140c5565b604052919050565b6001600160a01b0381168114610f6157600080fd5b80356141e2816141c2565b919050565b803563ffffffff811681146141e257600080fd5b600060c0828403121561420d57600080fd5b6142156140db565b8235614220816141c2565b815261422e602084016141e7565b602082015261423f604084016141e7565b6040820152614250606084016141e7565b60608201526080830135614263816141c2565b608082015260a0830135614276816141c2565b60a08201529392505050565b600067ffffffffffffffff82111561429c5761429c6140c5565b5060051b60200190565b803567ffffffffffffffff811681146141e257600080fd5b8015158114610f6157600080fd5b80356141e2816142be565b600067ffffffffffffffff8211156142f1576142f16140c5565b50601f01601f191660200190565b600082601f83011261431057600080fd5b813561432361431e826142d7565b614191565b81815284602083860101111561433857600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f83011261436657600080fd5b8135602061437661431e83614282565b82815260069290921b8401810191818101908684111561439557600080fd5b8286015b8481101561208857604081890312156143b25760008081fd5b6143ba614104565b81356143c5816141c2565b81528185013585820152835291830191604001614399565b600082601f8301126143ee57600080fd5b813560206143fe61431e83614282565b82815260059290921b8401810191818101908684111561441d57600080fd5b8286015b8481101561208857803567ffffffffffffffff8111156144415760008081fd5b61444f8986838b01016142ff565b845250918301918301614421565b60006101a0828403121561447057600080fd5b614478614127565b9050614483826142a6565b8152614491602083016141d7565b60208201526144a2604083016141d7565b60408201526144b3606083016142a6565b6060820152608082013560808201526144ce60a083016142cc565b60a08201526144df60c083016142a6565b60c08201526144f060e083016141d7565b60e082015261010082810135908201526101208083013567ffffffffffffffff8082111561451d57600080fd5b614529868387016142ff565b8385015261014092508285013591508082111561454557600080fd5b61455186838701614355565b8385015261016092508285013591508082111561456d57600080fd5b5061457a858286016143dd565b82840152505061018080830135818301525092915050565b600082601f8301126145a357600080fd5b813560206145b361431e83614282565b82815260059290921b840181019181810190868411156145d257600080fd5b8286015b8481101561208857803567ffffffffffffffff8111156145f65760008081fd5b6146048986838b010161445d565b8452509183019183016145d6565b600082601f83011261462357600080fd5b8135602061463361431e83614282565b82815260059290921b8401810191818101908684111561465257600080fd5b8286015b8481101561208857803567ffffffffffffffff8111156146765760008081fd5b6146848986838b01016143dd565b845250918301918301614656565b600082601f8301126146a357600080fd5b813560206146b361431e83614282565b8083825260208201915060208460051b8701019350868411156146d557600080fd5b602086015b8481101561208857803583529183019183016146da565b600082601f83011261470257600080fd5b8135602061471261431e83614282565b82815260059290921b8401810191818101908684111561473157600080fd5b8286015b8481101561208857803567ffffffffffffffff808211156147565760008081fd5b9088019060a0828b03601f19018113156147705760008081fd5b61477861414b565b6147838885016142a6565b8152604080850135848111156147995760008081fd5b6147a78e8b83890101614592565b8a84015250606080860135858111156147c05760008081fd5b6147ce8f8c838a0101614612565b83850152506080915081860135858111156147e95760008081fd5b6147f78f8c838a0101614692565b9184019190915250919093013590830152508352918301918301614735565b600080604080848603121561482a57600080fd5b833567ffffffffffffffff8082111561484257600080fd5b61484e878388016146f1565b945060209150818601358181111561486557600080fd5b8601601f8101881361487657600080fd5b803561488461431e82614282565b81815260059190911b8201840190848101908a8311156148a357600080fd5b8584015b8381101561492f578035868111156148bf5760008081fd5b8501603f81018d136148d15760008081fd5b878101356148e161431e82614282565b81815260059190911b82018a0190898101908f8311156149015760008081fd5b928b01925b8284101561491f5783358252928a0192908a0190614906565b86525050509186019186016148a7565b50809750505050505050509250929050565b6000806040838503121561495457600080fd5b61495d836142a6565b915061496b602084016142a6565b90509250929050565b634e487b7160e01b600052602160045260246000fd5b6004811061499a5761499a614974565b9052565b60208101610e8c828461498a565b6000602082840312156149be57600080fd5b813567ffffffffffffffff8111156149d557600080fd5b820160a08185031215612e6e57600080fd5b600060208083850312156149fa57600080fd5b823567ffffffffffffffff811115614a1157600080fd5b8301601f81018513614a2257600080fd5b8035614a3061431e82614282565b81815260609182028301840191848201919088841115614a4f57600080fd5b938501935b83851015614ab25780858a031215614a6c5760008081fd5b614a7461416e565b614a7d866142a6565b815286860135614a8c816142be565b81880152604086810135614a9f816141c2565b9082015283529384019391850191614a54565b50979650505050505050565b803560ff811681146141e257600080fd5b600060208284031215614ae157600080fd5b610e8982614abe565b60008151808452602080850194506020840160005b83811015614b245781516001600160a01b031687529582019590820190600101614aff565b509495945050505050565b60208152600082518051602084015260ff602082015116604084015260ff604082015116606084015260608101511515608084015250602083015160c060a0840152614b7e60e0840182614aea565b90506040840151601f198483030160c0850152614b9b8282614aea565b95945050505050565b60008060408385031215614bb757600080fd5b614bc0836142a6565b946020939093013593505050565b60008060208385031215614be157600080fd5b823567ffffffffffffffff80821115614bf957600080fd5b818501915085601f830112614c0d57600080fd5b813581811115614c1c57600080fd5b8660208260061b8501011115614c3157600080fd5b60209290920196919550909350505050565b600060208284031215614c5557600080fd5b610e89826142a6565b600060208284031215614c7057600080fd5b8135612e6e816141c2565b60008060408385031215614c8e57600080fd5b823567ffffffffffffffff80821115614ca657600080fd5b614cb28683870161445d565b93506020850135915080821115614cc857600080fd5b50614cd5858286016143dd565b9150509250929050565b600082601f830112614cf057600080fd5b81356020614d0061431e83614282565b8083825260208201915060208460051b870101935086841115614d2257600080fd5b602086015b84811015612088578035614d3a816141c2565b8352918301918301614d27565b60006020808385031215614d5a57600080fd5b823567ffffffffffffffff80821115614d7257600080fd5b818501915085601f830112614d8657600080fd5b8135614d9461431e82614282565b81815260059190911b83018401908481019088831115614db357600080fd5b8585015b83811015614e8557803585811115614dce57600080fd5b860160c0818c03601f19011215614de55760008081fd5b614ded6140db565b8882013581526040614e00818401614abe565b8a8301526060614e11818501614abe565b8284015260809150614e248285016142cc565b9083015260a08381013589811115614e3c5760008081fd5b614e4a8f8d83880101614cdf565b838501525060c0840135915088821115614e645760008081fd5b614e728e8c84870101614cdf565b9083015250845250918601918601614db7565b5098975050505050505050565b600060208284031215614ea457600080fd5b5035919050565b80356001600160e01b03811681146141e257600080fd5b600082601f830112614ed357600080fd5b81356020614ee361431e83614282565b82815260069290921b84018101918181019086841115614f0257600080fd5b8286015b848110156120885760408189031215614f1f5760008081fd5b614f27614104565b614f30826142a6565b8152614f3d858301614eab565b81860152835291830191604001614f06565b600082601f830112614f6057600080fd5b81356020614f7061431e83614282565b82815260079290921b84018101918181019086841115614f8f57600080fd5b8286015b84811015612088578088036080811215614fad5760008081fd5b614fb561416e565b614fbe836142a6565b8152604080601f1984011215614fd45760008081fd5b614fdc614104565b9250614fe98785016142a6565b8352614ff68185016142a6565b8388015281870192909252606083013591810191909152835291830191608001614f93565b6000602080838503121561502e57600080fd5b823567ffffffffffffffff8082111561504657600080fd5b8185019150604080838803121561505c57600080fd5b615064614104565b83358381111561507357600080fd5b84016040818a03121561508557600080fd5b61508d614104565b81358581111561509c57600080fd5b8201601f81018b136150ad57600080fd5b80356150bb61431e82614282565b81815260069190911b8201890190898101908d8311156150da57600080fd5b928a01925b8284101561512a5787848f0312156150f75760008081fd5b6150ff614104565b843561510a816141c2565b8152615117858d01614eab565b818d0152825292870192908a01906150df565b84525050508187013593508484111561514257600080fd5b61514e8a858401614ec2565b818801528252508385013591508282111561516857600080fd5b61517488838601614f4f565b85820152809550505050505092915050565b634e487b7160e01b600052603260045260246000fd5b805160408084528151848201819052600092602091908201906060870190855b818110156151f357835180516001600160a01b031684528501516001600160e01b03168584015292840192918501916001016151bc565b50508583015187820388850152805180835290840192506000918401905b80831015614ab2578351805167ffffffffffffffff1683528501516001600160e01b031685830152928401926001929092019190850190615211565b602081526000610e89602083018461519c565b67ffffffffffffffff8316815260608101612e6e6020830184805167ffffffffffffffff908116835260209182015116910152565b634e487b7160e01b600052601160045260246000fd5b67ffffffffffffffff8181168382160190808211156152cc576152cc615295565b5092915050565b6000602080835260608451604080848701526152f2606087018361519c565b87850151878203601f19016040890152805180835290860193506000918601905b80831015614e8557845167ffffffffffffffff81511683528781015161535289850182805167ffffffffffffffff908116835260209182015116910152565b50840151828701529386019360019290920191608090910190615313565b60006020828403121561538257600080fd5b813567ffffffffffffffff81111561539957600080fd5b61368b848285016146f1565b81810381811115610e8c57610e8c615295565b634e487b7160e01b600052601260045260246000fd5b600067ffffffffffffffff808416806153e9576153e96153b8565b92169190910692915050565b8082028115828204841417610e8c57610e8c615295565b60006040828403121561541e57600080fd5b615426614104565b61542f836142a6565b8152602083013560208201528091505092915050565b60008151808452602080850194506020840160005b83811015614b2457815180516001600160a01b03168852602090810151908801526040870196509082019060010161545a565b8051825267ffffffffffffffff60208201511660208301526000604082015160a060408501526154c060a0850182613ee7565b9050606083015184820360608601526154d98282613ee7565b91505060808301518482036080860152614b9b8282615445565b602081526000610e89602083018461548d565b608081526000615519608083018761548d565b61ffff9590951660208301525060408101929092526001600160a01b0316606090910152919050565b600082601f83011261555357600080fd5b815161556161431e826142d7565b81815284602083860101111561557657600080fd5b61368b826020830160208701613ec3565b60008060006060848603121561559c57600080fd5b83516155a7816142be565b602085015190935067ffffffffffffffff8111156155c457600080fd5b6155d086828701615542565b925050604084015190509250925092565b6000602082840312156155f357600080fd5b8151612e6e816142be565b80820180821115610e8c57610e8c615295565b60ff8181168382160190811115610e8c57610e8c615295565b8183823760009101908152919050565b828152606082602083013760800192915050565b600067ffffffffffffffff80841680615669576156696153b8565b92169190910492915050565b60006020828403121561568757600080fd5b815167ffffffffffffffff8082111561569f57600080fd5b90830190606082860312156156b357600080fd5b6156bb61416e565b8251828111156156ca57600080fd5b6156d687828601615542565b8252506020830151828111156156eb57600080fd5b6156f787828601615542565b60208301525060408301518281111561570f57600080fd5b61571b87828601615542565b60408301525095945050505050565b602081016005831061573e5761573e614974565b91905290565b60ff81811683821602908116908181146152cc576152cc615295565b600060a0820160ff88168352602087602085015260a0604085015281875480845260c086019150886000526020600020935060005b818110156157ba5784546001600160a01b031683526001948501949284019201615795565b505084810360608601526157ce8188614aea565b935050505060ff831660808301529695505050505050565b600067ffffffffffffffff808616835280851660208401525060606040830152614b9b6060830184613ee7565b82815260406020820152600061368b6040830184613ee7565b67ffffffffffffffff8481168252831660208201526060810161368b604083018461498a565b61585c818461498a565b60406020820152600061368b6040830184613ee7565b60006020828403121561588457600080fd5b8151612e6e816141c2565b60208152600082516101008060208501526158ae610120850183613ee7565b915060208501516158cb604086018267ffffffffffffffff169052565b5060408501516001600160a01b03811660608601525060608501516080850152608085015161590560a08601826001600160a01b03169052565b5060a0850151601f19808685030160c08701526159228483613ee7565b935060c08701519150808685030160e087015261593f8483613ee7565b935060e087015191508086850301838701525061595c8382613ee7565b9695505050505050565b60006020828403121561597857600080fd5b5051919050565b600060ff821660ff810361599557615995615295565b60010192915050565b6020808252825182820181905260009190848201906040850190845b818110156159ed57835180516001600160a01b0316845260209081015190840152604083019385019392506001016159ba565b50909695505050505050565b60008282518085526020808601955060208260051b8401016020860160005b84811015615a4657601f19868403018952615a34838351613ee7565b98840198925090830190600101615a18565b5090979650505050505050565b602081526000610e8960208301846159f9565b60408152615a8160408201845167ffffffffffffffff169052565b60006020840151615a9d60608401826001600160a01b03169052565b5060408401516001600160a01b038116608084015250606084015167ffffffffffffffff811660a084015250608084015160c083015260a084015180151560e08401525060c0840151610100615afe8185018367ffffffffffffffff169052565b60e08601519150610120615b1c818601846001600160a01b03169052565b81870151925061014091508282860152808701519250506101a06101608181870152615b4c6101e0870185613ee7565b9350828801519250603f19610180818887030181890152615b6d8686615445565b9550828a01519450818887030184890152615b8886866159f9565b9550808a01516101c089015250505050508281036020840152614b9b81856159f9565b6000815160208301516001600160e01b031980821693506004831015615bdb5780818460040360031b1b83161693505b50505091905056fea164736f6c6343000818000a", + 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 @@ -583,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) } @@ -2220,7 +2221,7 @@ func (EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage) Topic() common.Hash { } func (EVM2EVMMultiOffRampSourceChainConfigSet) Topic() common.Hash { - return common.HexToHash("0xb8f0c74385134334c728fcac437ec6c6397c9f2c1440532b0c44175a090b1401") + return common.HexToHash("0x4f49973170c548fddd4a48341b75e131818913f38f44d47af57e8735eee588ba") } func (EVM2EVMMultiOffRampSourceChainSelectorAdded) Topic() common.Hash { @@ -2270,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) 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/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt index d2d6fbcc2a..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 @@ -9,14 +9,14 @@ ccip_config: ../../../contracts/solc/v0.8.24/CCIPConfig/CCIPConfig.abi ../../../ 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 035e69cf7dc9682f720022e2c7a89566a849163bf8a043e3e0aea2b19cce4aa7 -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 diff --git a/core/services/ccipcapability/launcher/integration_test.go b/core/services/ccipcapability/launcher/integration_test.go index 5c6adc4308..da04ef4cbc 100644 --- a/core/services/ccipcapability/launcher/integration_test.go +++ b/core/services/ccipcapability/launcher/integration_test.go @@ -1,304 +1,47 @@ package launcher 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" "github.com/onsi/gomega" "github.com/stretchr/testify/require" - ccipreader "github.com/smartcontractkit/chainlink-ccip/pkg/reader" - "github.com/smartcontractkit/chainlink-common/pkg/types" - - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "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" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" cctypes "github.com/smartcontractkit/chainlink/v2/core/services/ccipcapability/types" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" + it "github.com/smartcontractkit/chainlink/v2/core/services/ocr3/plugins/ccip_integration_tests" "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" - "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - evmrelaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) -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 - cc *ccip_config.CCIPConfig - testingT *testing.T - lp logpoller.LogPoller - simClient client.Client -} - -func newTestUniverse(t *testing.T) 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) - - return testUniverse{ - transactor: transactor, - backend: backend, - capReg: capReg, - cc: cc, - testingT: t, - } -} - -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.lp, t.simClient, config) -} - -func addCapabilities( - t *testing.T, - backend *backends.SimulatedBackend, - transactor *bind.TransactOpts, - capReg *kcr.CapabilitiesRegistry, - capConfAddress common.Address) [][32]byte { - // add the CCIP capability to the registry - _, err := capReg.AddCapabilities(transactor, []kcr.CapabilitiesRegistryCapability{ - { - LabelledName: ccipCapabilityLabelledName, - Version: ccipCapabilityVersion, - CapabilityType: 0, - ResponseType: 0, - ConfigurationContract: capConfAddress, - }, - }) - require.NoError(t, err, "failed to add capability to registry") - backend.Commit() - - ccipCapabilityID, err := capReg.GetHashedCapabilityId(nil, ccipCapabilityLabelledName, ccipCapabilityVersion) - require.NoError(t, err) - - // Add the p2p ids of the ccip nodes - var p2pIDs [][32]byte - for i := 0; i < 4; i++ { - p2pID := p2pkey.MustNewV2XXXTestingOnly(big.NewInt(int64(i + 1))).PeerID() - p2pIDs = append(p2pIDs, p2pID) - _, err = capReg.AddNodeOperators(transactor, []kcr.CapabilitiesRegistryNodeOperator{ - { - Admin: transactor.From, - Name: fmt.Sprintf("nop-%d", i), - }, - }) - require.NoError(t, err) - backend.Commit() - - // get the node operator id from the event - it, err := capReg.FilterNodeOperatorAdded(nil, nil, nil) - require.NoError(t, err) - var nodeOperatorID uint32 - for it.Next() { - if it.Event.Name == fmt.Sprintf("nop-%d", i) { - nodeOperatorID = it.Event.NodeOperatorId - break - } - } - require.NotZero(t, nodeOperatorID) - - _, err = capReg.AddNodes(transactor, []kcr.CapabilitiesRegistryNodeParams{ - { - NodeOperatorId: nodeOperatorID, - Signer: testutils.Random32Byte(), - P2pId: p2pID, - HashedCapabilityIds: [][32]byte{ccipCapabilityID}, - }, - }) - require.NoError(t, err) - backend.Commit() - - // verify that the node was added successfully - nodeInfo, err := capReg.GetNode(nil, p2pID) - require.NoError(t, err) - - require.Equal(t, nodeOperatorID, nodeInfo.NodeOperatorId) - require.Equal(t, p2pID[:], nodeInfo.P2pId[:]) - } - return p2pIDs -} - -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, - }, - } -} - -func newHomeChainReader(t *testing.T, logPoller logpoller.LogPoller, client client.Client, ccAddress common.Address) cctypes.HomeChainReader { - 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, err := evm.NewChainReaderService(testutils.Context(t), logger.TestLogger(t), logPoller, client, cfg) - require.NoError(t, err) - - err = cr.Bind(testutils.Context(t), []types.BoundContract{ - { - Address: ccAddress.String(), - Name: "CCIPConfig", - }, - }) - require.NoError(t, err) - require.NoError(t, cr.Start(testutils.Context(t))) - - hcr := ccipreader.NewHomeChainReader(cr, logger.TestLogger(t), time.Second) - require.NoError(t, hcr.Start(testutils.Context(t))) - - return hcr -} - -func addDONToRegistry(t *testing.T, - transactor *bind.TransactOpts, - ccipCapabilityID [32]byte, - chainSelector uint64, - f uint8, - capReg *kcr.CapabilitiesRegistry, - backend *backends.SimulatedBackend, - bootstrapP2PID [32]byte, - p2pIDs [][32]byte, -) { - tabi, err := ocr3_config_encoder.IOCR3ConfigEncoderMetaData.GetAbi() - require.NoError(t, 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, err) - - // Trim first four bytes to remove function selector. - encodedConfigs := encodedCall[4:] - - _, err = capReg.AddDON(transactor, p2pIDs, []kcr.CapabilitiesRegistryCapabilityConfiguration{ - { - CapabilityId: ccipCapabilityID, - Config: encodedConfigs, - }, - }, false, false, f) - require.NoError(t, err) - backend.Commit() -} - func TestIntegration_Launcher(t *testing.T) { ctx := testutils.Context(t) lggr := logger.TestLogger(t) - uni := newTestUniverse(t) - - db := pgtest.NewSqlxDB(t) - lpOpts := logpoller.Opts{ - PollPeriod: time.Millisecond, - FinalityDepth: 0, - BackfillBatchSize: 10, - RpcBatchSize: 10, - KeepFinalizedBlocksDepth: 100000, + 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)) } - cl := client.NewSimulatedBackendClient(t, uni.backend, big.NewInt(1337)) - lp := logpoller.NewLogPoller(logpoller.NewORM(big.NewInt(1337), db, lggr), cl, logger.NullLogger, lpOpts) - require.NoError(t, lp.Start(ctx)) - t.Cleanup(func() { require.NoError(t, lp.Close()) }) - - uni.lp = lp - uni.simClient = cl + p2pIDs := it.P2pIDsFromInts(arr) + uni.AddCapability(p2pIDs) - p2pIDs := addCapabilities(t, uni.backend, uni.transactor, uni.capReg, uni.cc.Address()) - - regSyncer, err := registrysyncer.New(lggr, uni, uni.capReg.Address().String()) + regSyncer, err := registrysyncer.New(lggr, uni, uni.CapReg.Address().String()) require.NoError(t, err) - hcr := newHomeChainReader(t, lp, cl, uni.cc.Address()) + hcr := uni.HomeChainReader launcher := New( - ccipCapabilityVersion, - ccipCapabilityLabelledName, + it.CcipCapabilityVersion, + it.CcipCapabilityLabelledName, p2pIDs[0], logger.TestLogger(t), hcr, &oracleCreatorPrints{ t: t, }, - 3*time.Second, + 1*time.Second, ) regSyncer.AddLauncher(launcher) @@ -307,32 +50,27 @@ func TestIntegration_Launcher(t *testing.T) { t.Cleanup(func() { require.NoError(t, regSyncer.Close()) }) t.Cleanup(func() { require.NoError(t, launcher.Close()) }) - chainAConf := setupConfigInfo(chainA, p2pIDs, fChainA, []byte("chainA")) - chainBConf := setupConfigInfo(chainB, p2pIDs[1:], fChainB, []byte("chainB")) - chainCConf := setupConfigInfo(chainC, p2pIDs[2:], fChainC, []byte("chainC")) + 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.cc.ApplyChainConfigUpdates(uni.transactor, nil, inputConfig) + _, err = uni.CcipCfg.ApplyChainConfigUpdates(uni.Transactor, nil, inputConfig) require.NoError(t, err) - uni.backend.Commit() + uni.Backend.Commit() - ccipCapabilityID, err := uni.capReg.GetHashedCapabilityId(nil, ccipCapabilityLabelledName, ccipCapabilityVersion) + ccipCapabilityID, err := uni.CapReg.GetHashedCapabilityId(nil, it.CcipCapabilityLabelledName, it.CcipCapabilityVersion) require.NoError(t, err) - addDONToRegistry( - t, - uni.transactor, + uni.AddDONToRegistry( ccipCapabilityID, - chainA, - fChainA, - uni.capReg, - uni.backend, - p2pIDs[1], // we're not bootstrapping - p2pIDs, - ) + it.ChainA, + it.FChainA, + p2pIDs[1], + p2pIDs) gomega.NewWithT(t).Eventually(func() bool { return len(launcher.runningDONIDs()) == 1 diff --git a/core/services/ccipcapability/launcher/launcher.go b/core/services/ccipcapability/launcher/launcher.go index fb46fe40c0..66aaef8cc0 100644 --- a/core/services/ccipcapability/launcher/launcher.go +++ b/core/services/ccipcapability/launcher/launcher.go @@ -13,6 +13,7 @@ import ( 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" @@ -31,7 +32,7 @@ func New( capabilityLabelledName string, p2pID ragep2ptypes.PeerID, lggr logger.Logger, - homeChainReader cctypes.HomeChainReader, + homeChainReader ccipreader.HomeChain, oracleCreator cctypes.OracleCreator, tickInterval time.Duration, ) *launcher { @@ -60,7 +61,7 @@ type launcher struct { capabilityLabelledName string p2pID ragep2ptypes.PeerID lggr logger.Logger - homeChainReader cctypes.HomeChainReader + homeChainReader ccipreader.HomeChain stopChan chan struct{} // latestState is the latest capability registry state received from the syncer. latestState registrysyncer.State @@ -284,7 +285,7 @@ func (l *launcher) processRemoved(removed map[registrysyncer.DonID]kcr.Capabilit func updateDON( lggr logger.Logger, p2pID ragep2ptypes.PeerID, - homeChainReader cctypes.HomeChainReader, + homeChainReader ccipreader.HomeChain, oracleCreator cctypes.OracleCreator, prevDeployment ccipDeployment, don kcr.CapabilitiesRegistryDONInfo, @@ -358,7 +359,7 @@ func createFutureBlueGreenDeployment( func createDON( lggr logger.Logger, p2pID ragep2ptypes.PeerID, - homeChainReader cctypes.HomeChainReader, + homeChainReader ccipreader.HomeChain, oracleCreator cctypes.OracleCreator, don kcr.CapabilitiesRegistryDONInfo, ) (*ccipDeployment, error) { diff --git a/core/services/ccipcapability/types/mocks/home_chain_reader.go b/core/services/ccipcapability/types/mocks/home_chain_reader.go index b2576324c8..a5a581a1d2 100644 --- a/core/services/ccipcapability/types/mocks/home_chain_reader.go +++ b/core/services/ccipcapability/types/mocks/home_chain_reader.go @@ -3,19 +3,67 @@ package mocks import ( "context" - mock "github.com/stretchr/testify/mock" + mapset "github.com/deckarep/golang-set/v2" + "github.com/stretchr/testify/mock" ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" - cctypes "github.com/smartcontractkit/chainlink/v2/core/services/ccipcapability/types" + cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" + + "github.com/smartcontractkit/libocr/ragep2p/types" ) -var _ cctypes.HomeChainReader = (*HomeChainReader)(nil) +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) diff --git a/core/services/ccipcapability/types/types.go b/core/services/ccipcapability/types/types.go index da99785896..50e4fb19f4 100644 --- a/core/services/ccipcapability/types/types.go +++ b/core/services/ccipcapability/types/types.go @@ -1,20 +1,12 @@ package types import ( - "context" - 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 -type HomeChainReader interface { - // GetOCRConfigs Gets the OCR3Configs for a given donID and pluginType - GetOCRConfigs(ctx context.Context, donID uint32, pluginType uint8) ([]ccipreaderpkg.OCR3ConfigWithMeta, error) - Ready() error -} - // PluginType represents the type of CCIP plugin. // It mirrors the OCRPluginType in Internal.sol. type PluginType uint8 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/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 7523a75cc9..fcea5e5957 100644 --- a/core/services/ocr2/plugins/liquiditymanager/graph/graph.go +++ b/core/services/ocr2/plugins/liquiditymanager/graph/graph.go @@ -14,6 +14,8 @@ type GraphWriter interface { Add(from, to Data) 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/writer.go b/core/services/ocr2/plugins/liquiditymanager/graph/writer.go index 1d0ee45d59..8671087b12 100644 --- a/core/services/ocr2/plugins/liquiditymanager/graph/writer.go +++ b/core/services/ocr2/plugins/liquiditymanager/graph/writer.go @@ -41,6 +41,27 @@ func (g *liquidityGraph) SetLiquidity(n models.NetworkSelector, liquidity *big.I return true } +func (g *liquidityGraph) SetTargetLiquidity(n models.NetworkSelector, target *big.Int) bool { + g.lock.Lock() + defer g.lock.Unlock() + + if !g.hasNetwork(n) { + return false + } + + prev := g.data[n] + g.data[n] = Data{ + Liquidity: prev.Liquidity, + TokenAddress: prev.TokenAddress, + LiquidityManagerAddress: prev.LiquidityManagerAddress, + ConfigDigest: prev.ConfigDigest, + NetworkSelector: prev.NetworkSelector, + MinimumLiquidity: prev.MinimumLiquidity, + TargetLiquidity: target, + } + return true +} + func (g *liquidityGraph) AddNetwork(n models.NetworkSelector, data Data) bool { g.lock.Lock() defer g.lock.Unlock() 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/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 c36f786bc9..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 @@ -78,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 index a84786328c..ab8629f8c1 100644 --- 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 @@ -1,92 +1,52 @@ package home_chain import ( - "fmt" - "math/big" "testing" "time" mapset "github.com/deckarep/golang-set/v2" - "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" "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" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" capcfg "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" - "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" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" - helpers "github.com/smartcontractkit/chainlink/v2/core/services/ocr3/plugins/ccip_integration_tests" + it "github.com/smartcontractkit/chainlink/v2/core/services/ocr3/plugins/ccip_integration_tests" "github.com/stretchr/testify/require" - - evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" -) - -const ( - chainA uint64 = 1 - fChainA uint8 = 1 - - chainB uint64 = 2 - fChainB uint8 = 2 - - chainC uint64 = 3 - fChainC uint8 = 3 ) func TestHomeChainReader(t *testing.T) { - // Initialize chainReader - cfg := evmtypes.ChainReaderConfig{ - Contracts: map[string]evmtypes.ChainContractReader{ - "CCIPConfig": { - ContractABI: capcfg.CCIPConfigMetaData.ABI, - Configs: map[string]*evmtypes.ChainReaderDefinition{ - "getAllChainConfigs": { - ChainSpecificName: "getAllChainConfigs", - }, - }, - }, - }, + 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)) } - //============================Setup Backend=================================== - transactor := testutils.MustNewSimTransactor(t) - backend := backends.NewSimulatedBackend(core.GenesisAlloc{ - transactor.From: {Balance: assets.Ether(1000).ToInt()}, - }, 30e6) - //==============================Setup Contracts - Add capabilities================================= - capRegAddress, capRegContract, err := prepareCapabilityRegistry(t, backend, transactor) - require.NoError(t, err) - capConfAddress, capConfContract, err := prepareCCIPCapabilityConfig(t, backend, transactor, capRegAddress) - require.NoError(t, err) - p2pIDS := addCapabilities(t, backend, transactor, capRegContract, capConfAddress) + p2pIDs := it.P2pIDsFromInts(arr) + uni.AddCapability(p2pIDs) //==============================Apply configs to Capability Contract================================= - chainAConf := setupConfigInfo(chainA, p2pIDS, fChainA, []byte("chainA")) - chainBConf := setupConfigInfo(chainB, p2pIDS[1:], fChainB, []byte("chainB")) - chainCConf := setupConfigInfo(chainC, p2pIDS[2:], fChainC, []byte("chainC")) + 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 = capConfContract.ApplyChainConfigUpdates(transactor, nil, inputConfig) + _, err := uni.CcipCfg.ApplyChainConfigUpdates(uni.Transactor, nil, inputConfig) require.NoError(t, err) - backend.Commit() + uni.Backend.Commit() //================================Setup HomeChainReader=============================== - ctx := testutils.Context(t) - testData := helpers.SetupReaderTestData(ctx, t, backend, capConfAddress, cfg, "CCIPConfig") - chainReader := testData.ChainReader - logPoller := testData.LogPoller - require.NoError(t, err) - pollDuration := 5 * time.Millisecond - homeChain := ccipreader.NewHomeChainReader(chainReader, logger.TestLogger(t), pollDuration) - require.NoError(t, homeChain.Start(ctx)) + + pollDuration := time.Second + homeChain := uni.HomeChainReader gomega.NewWithT(t).Eventually(func() bool { configs, _ := homeChain.GetAllChainConfigs() @@ -106,18 +66,17 @@ func TestHomeChainReader(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedChainConfigs, configs) //=================================Remove ChainC from OnChainConfig========================================= - _, err = capConfContract.ApplyChainConfigUpdates(transactor, []uint64{chainC}, nil) + _, err = uni.CcipCfg.ApplyChainConfigUpdates(uni.Transactor, []uint64{it.ChainC}, nil) require.NoError(t, err) - backend.Commit() + 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(chainC)) + delete(expectedChainConfigs, cciptypes.ChainSelector(it.ChainC)) require.Equal(t, expectedChainConfigs, configs) //================================Close HomeChain Reader=============================== - require.NoError(t, homeChain.Close()) - require.NoError(t, logPoller.Close()) - require.NoError(t, chainReader.Close()) + //require.NoError(t, homeChain.Close()) + //require.NoError(t, uni.LogPoller.Close()) t.Logf("homchain reader successfully closed") } @@ -128,107 +87,3 @@ func toPeerIDs(readers [][32]byte) mapset.Set[libocrtypes.PeerID] { } return peerIDs } - -func setupConfigInfo(chainSelector uint64, readers [][32]byte, fChain uint8, cfg []byte) capcfg.CCIPConfigTypesChainConfigInfo { - return capcfg.CCIPConfigTypesChainConfigInfo{ - ChainSelector: chainSelector, - ChainConfig: capcfg.CCIPConfigTypesChainConfig{ - Readers: readers, - FChain: fChain, - Config: cfg, - }, - } -} - -func prepareCCIPCapabilityConfig(t *testing.T, backend *backends.SimulatedBackend, transactor *bind.TransactOpts, capRegAddress common.Address) (common.Address, *capcfg.CCIPConfig, error) { - ccAddress, _, _, err := capcfg.DeployCCIPConfig(transactor, backend, capRegAddress) - require.NoError(t, err) - backend.Commit() - - contract, err := capcfg.NewCCIPConfig(ccAddress, backend) - require.NoError(t, err) - backend.Commit() - - return ccAddress, contract, nil -} - -func prepareCapabilityRegistry(t *testing.T, backend *backends.SimulatedBackend, transactor *bind.TransactOpts) (common.Address, *capabilities_registry.CapabilitiesRegistry, error) { - crAddress, _, _, err := capabilities_registry.DeployCapabilitiesRegistry(transactor, backend) - require.NoError(t, err) - backend.Commit() - - capReg, err := capabilities_registry.NewCapabilitiesRegistry(crAddress, backend) - require.NoError(t, err) - backend.Commit() - - return crAddress, capReg, nil -} - -func addCapabilities( - t *testing.T, - backend *backends.SimulatedBackend, - transactor *bind.TransactOpts, - capReg *capabilities_registry.CapabilitiesRegistry, - capConfAddress common.Address) [][32]byte { - // add the CCIP capability to the registry - _, err := capReg.AddCapabilities(transactor, []capabilities_registry.CapabilitiesRegistryCapability{ - { - LabelledName: "ccip", - Version: "v1.0", - CapabilityType: 0, - ResponseType: 0, - ConfigurationContract: capConfAddress, - }, - }) - require.NoError(t, err, "failed to add capability to registry") - backend.Commit() - - ccipCapabilityID, err := capReg.GetHashedCapabilityId(nil, "ccip", "v1.0") - require.NoError(t, err) - - // Add the p2p ids of the ccip nodes - var p2pIDs [][32]byte - for i := 0; i < 4; i++ { - p2pID := p2pkey.MustNewV2XXXTestingOnly(big.NewInt(int64(i + 1))).PeerID() - p2pIDs = append(p2pIDs, p2pID) - _, err = capReg.AddNodeOperators(transactor, []capabilities_registry.CapabilitiesRegistryNodeOperator{ - { - Admin: transactor.From, - Name: fmt.Sprintf("nop-%d", i), - }, - }) - require.NoError(t, err) - backend.Commit() - - // get the node operator id from the event - it, err := capReg.FilterNodeOperatorAdded(nil, nil, nil) - require.NoError(t, err) - var nodeOperatorID uint32 - for it.Next() { - if it.Event.Name == fmt.Sprintf("nop-%d", i) { - nodeOperatorID = it.Event.NodeOperatorId - break - } - } - require.NotZero(t, nodeOperatorID) - - _, err = capReg.AddNodes(transactor, []capabilities_registry.CapabilitiesRegistryNodeParams{ - { - NodeOperatorId: nodeOperatorID, - Signer: testutils.Random32Byte(), - P2pId: p2pID, - HashedCapabilityIds: [][32]byte{ccipCapabilityID}, - }, - }) - require.NoError(t, err) - backend.Commit() - - // verify that the node was added successfully - nodeInfo, err := capReg.GetNode(nil, p2pID) - require.NoError(t, err) - - require.Equal(t, nodeOperatorID, nodeInfo.NodeOperatorId) - require.Equal(t, p2pID[:], nodeInfo.P2pId[:]) - } - return p2pIDs -} diff --git a/core/services/ocr3/plugins/ccip_integration_tests/integration_helpers.go b/core/services/ocr3/plugins/ccip_integration_tests/integration_helpers.go index 9a30a7469f..b7e81cd2c9 100644 --- a/core/services/ocr3/plugins/ccip_integration_tests/integration_helpers.go +++ b/core/services/ocr3/plugins/ccip_integration_tests/integration_helpers.go @@ -2,14 +2,27 @@ 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" - types2 "github.com/smartcontractkit/chainlink-common/pkg/types" + 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" @@ -18,33 +31,15 @@ import ( "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" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" + evmrelaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) const chainID = 1337 -type TestSetupData struct { - LogPoller logpoller.LogPoller - ChainReader evm.ChainReaderService -} - -func SetupReaderTestData(ctx context.Context, t *testing.T, simulatedBackend *backends.SimulatedBackend, address common.Address, chainReaderConfig evmtypes.ChainReaderConfig, contractName string) TestSetupData { - lggr := logger.TestLogger(t) - db := pgtest.NewSqlxDB(t) - lpOpts := logpoller.Opts{ - PollPeriod: time.Millisecond, - FinalityDepth: 1, - BackfillBatchSize: 1, - RpcBatchSize: 1, - KeepFinalizedBlocksDepth: 10000, - } - cl := client.NewSimulatedBackendClient(t, simulatedBackend, big.NewInt(chainID)) - lp := logpoller.NewLogPoller(logpoller.NewORM(big.NewInt(chainID), db, lggr), cl, lggr, lpOpts) - require.NoError(t, lp.Start(ctx)) - - cr, err := evm.NewChainReaderService(ctx, lggr, lp, cl, chainReaderConfig) +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(ctx, []types2.BoundContract{ + err = cr.Bind(testutils.Context(t), []types.BoundContract{ { Address: address.String(), Name: contractName, @@ -52,14 +47,245 @@ func SetupReaderTestData(ctx context.Context, t *testing.T, simulatedBackend *ba }, }) require.NoError(t, err) - require.NoError(t, cr.Start(ctx)) + require.NoError(t, cr.Start(testutils.Context(t))) for { if err := cr.Ready(); err == nil { break } } - return TestSetupData{ - LogPoller: lp, - ChainReader: cr, + + 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/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/integration-tests/ccip-tests/actions/ccip_helpers.go b/integration-tests/ccip-tests/actions/ccip_helpers.go index d7b5396d02..6b59696abe 100644 --- a/integration-tests/ccip-tests/actions/ccip_helpers.go +++ b/integration-tests/ccip-tests/actions/ccip_helpers.go @@ -405,16 +405,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 { - allowanceApprovalDelta := new(big.Int).Sub(ApprovedAmountToRouter, allowance) - err := token.Approve(ccipModule.ChainClient.GetDefaultWallet(), ccipModule.Router.Address(), allowanceApprovalDelta) - 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 @@ -1582,23 +1575,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 @@ -1657,13 +1670,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 { @@ -1677,7 +1684,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) } @@ -1687,7 +1697,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) } @@ -2310,7 +2323,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, @@ -2318,7 +2331,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()) } @@ -2328,7 +2341,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()) } @@ -2337,7 +2350,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()) } @@ -2395,7 +2408,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), @@ -2407,7 +2420,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()) } @@ -2416,7 +2429,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()) } @@ -2489,7 +2502,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), @@ -2501,7 +2514,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()) } @@ -2510,7 +2523,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()) } @@ -2549,7 +2562,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()) } @@ -2558,7 +2571,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()) } @@ -2758,7 +2771,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) } @@ -2790,7 +2803,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 @@ -2826,7 +2839,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)), @@ -2847,7 +2860,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) } @@ -2878,12 +2891,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) } @@ -2893,22 +2906,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) } @@ -3112,7 +3114,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) @@ -3163,13 +3164,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 { @@ -3351,6 +3352,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{ @@ -3360,6 +3362,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 28dec6aedb..7ab92a673c 100644 --- a/integration-tests/ccip-tests/contracts/contract_models.go +++ b/integration-tests/ccip-tests/contracts/contract_models.go @@ -1497,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 index 1fb90de13b..9bcdbff61d 100644 --- 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 @@ -5,7 +5,7 @@ ## 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 \ +## 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 @@ -185,4 +185,4 @@ DataLength = 10000 [[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] MsgType = 'Data' DestGasLimit = 2500000 -DataLength = 10000 \ No newline at end of file +DataLength = 10000 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 index e69de29bb2..b7595906e6 100644 --- 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 @@ -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/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 0de777aae0..86da7fc1ae 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" @@ -229,8 +228,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)) @@ -287,11 +291,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] }