From 55d4d9c88667065f56257a71c133d3b700690097 Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Fri, 17 Nov 2023 09:10:37 +0400 Subject: [PATCH] [AUTO-7258] Setup log trigger load test using wasp (#11267) * add simple log upkeep counter contract * WIP- add automationv2_1 load test * update contract * working state * add config * try GHA * disable reads inside loadgen * updates * add specs * fix linkfunds * detach runner, add loglevel config * increase geth capacity * fix batching - test end FilterLogs * fix batching * fix log filter context and batching * increase upkeep funding, fix event count calc * increase GasLimitPerReport to 10.3M * Decrease PerformLockoutWindow to 80k * reduce sleep time - log filter batch * use ConcurrentEVMClients * increase RR CPU and Mem * add pyroscope, add slack notifications * add tag and image to test config * fix dashbord URL * contracts prettier * run lint * add new load test workflow * fix AddNetworksConfig * undo changes to benchmark test workflow * add load/automationv2_1 to build test image steps * lint --- .github/actions/build-test-image/action.yml | 2 +- .../workflows/automation-benchmark-tests.yml | 3 +- .github/workflows/automation-load-tests.yml | 105 +++ CODEOWNERS | 1 + .../native_solc_compile_all_automation | 1 + .../testhelpers/SimpleLogUpkeepCounter.sol | 45 ++ .../simple_log_upkeep_counter_wrapper.go | 504 ++++++++++++++ ...rapper-dependency-versions-do-not-edit.txt | 1 + core/gethwrappers/go_generate.go | 1 + .../contracts/contract_deployer.go | 21 + .../contracts/ethereum_keeper_contracts.go | 27 + .../automationv2_1/automationv2_1_test.go | 621 ++++++++++++++++++ integration-tests/load/automationv2_1/gun.go | 43 ++ .../load/automationv2_1/helpers.go | 71 ++ .../testreporters/keeper_benchmark.go | 6 +- 15 files changed, 1447 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/automation-load-tests.yml create mode 100644 contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol create mode 100644 core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go create mode 100644 integration-tests/load/automationv2_1/automationv2_1_test.go create mode 100644 integration-tests/load/automationv2_1/gun.go create mode 100644 integration-tests/load/automationv2_1/helpers.go diff --git a/.github/actions/build-test-image/action.yml b/.github/actions/build-test-image/action.yml index 252b292d031..4b1dce6ee15 100644 --- a/.github/actions/build-test-image/action.yml +++ b/.github/actions/build-test-image/action.yml @@ -15,7 +15,7 @@ inputs: required: false suites: description: The test suites to build into the image - default: chaos migration performance reorg smoke soak benchmark + default: chaos migration performance reorg smoke soak benchmark load/automationv2_1 required: false base_image_tag: description: The test base image version to use, if not provided it will use the version from the ./integration-tests/go.mod file diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index a4338d642bc..0fff36f8df4 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -6,7 +6,7 @@ on: description: Chainlink image version to use required: true type: string - default: 2.5.0 + default: 2.6.0 chainlinkImage: description: Chainlink image repo to use required: true @@ -108,6 +108,7 @@ jobs: QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + suites: benchmark load/automationv2_1 chaos reorg - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 env: diff --git a/.github/workflows/automation-load-tests.yml b/.github/workflows/automation-load-tests.yml new file mode 100644 index 00000000000..eebd87322cf --- /dev/null +++ b/.github/workflows/automation-load-tests.yml @@ -0,0 +1,105 @@ +name: Automation Load Test +on: + workflow_dispatch: + inputs: + chainlinkVersion: + description: Chainlink image version to use + required: true + type: string + default: 2.6.0 + chainlinkImage: + description: Chainlink image repo to use + required: true + type: string + default: public.ecr.aws/chainlink/chainlink + network: + description: Network to run tests on + required: true + type: choice + options: + - SIMULATED + TestInputs: + description: TestInputs + required: false + type: string + slackMemberID: + description: Notifies test results (Not your @) + required: true + default: U02Q14G80TY + type: string + +jobs: + automation_load: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + name: ${{ inputs.network }} Automation Load Test + runs-on: ubuntu20.04-16cores-64GB + env: + SELECTED_NETWORKS: ${{ inputs.network }} + SLACK_API_KEY: ${{ secrets.QA_SLACK_API_KEY }} + SLACK_CHANNEL: C03KJ5S7KEK + TEST_INPUTS: ${{ inputs.TestInputs }} + CHAINLINK_ENV_USER: ${{ github.actor }} + REF_NAME: ${{ github.head_ref || github.ref_name }} + steps: + - name: Setup Push Tag + shell: bash + run: | + echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY + echo "\`${{ inputs.chainlinkVersion }}\`" >>$GITHUB_STEP_SUMMARY + echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY + echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY + + - name: Add mask + run: | + SLACK_USER=$(jq -r '.inputs.slackMemberID' $GITHUB_EVENT_PATH) + echo ::add-mask::$SLACK_USER + echo SLACK_USER=$SLACK_USER >> $GITHUB_ENV + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ env.REF_NAME }} + - name: Build Test Image + uses: ./.github/actions/build-test-image + with: + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + suites: benchmark load/automationv2_1 chaos reorg + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + env: + RR_CPU: 4000m + RR_MEM: 4Gi + DETACH_RUNNER: true + TEST_SUITE: automationv2_1 + TEST_ARGS: -test.timeout 720h + ENV_JOB_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-tests:${{ github.sha }} + INTERNAL_DOCKER_REPO: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: cd integration-tests && go test -timeout 1h -v -run TestLogTrigger ./load/automationv2_1 -count=1 + test_download_vendor_packages_command: make gomod + cl_repo: ${{ inputs.chainlinkImage }} + cl_image_tag: ${{ inputs.chainlinkVersion }} + token: ${{ secrets.GITHUB_TOKEN }} + should_cleanup: false + go_mod_path: ./integration-tests/go.mod + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Collect Metrics + if: always() + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 + with: + basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} + this-job-name: ${{ inputs.network }} Automation Load Test + test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' + continue-on-error: true diff --git a/CODEOWNERS b/CODEOWNERS index 8ec42ed6dd4..5f33e68e514 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -104,6 +104,7 @@ core/scripts/gateway @bolekk @pinebit /.github/workflows/performance-tests.yml @smartcontractkit/test-tooling-team /.github/workflows/automation-ondemand-tests.yml @smartcontractkit/keepers /.github/workflows/automation-benchmark-tests.yml @smartcontractkit/keepers +/.github/workflows/automation-load-tests.yml @smartcontractkit/keepers /core/chainlink.Dockerfile @smartcontractkit/prodsec-public diff --git a/contracts/scripts/native_solc_compile_all_automation b/contracts/scripts/native_solc_compile_all_automation index 1c54d677135..ddf6c2c8bfa 100755 --- a/contracts/scripts/native_solc_compile_all_automation +++ b/contracts/scripts/native_solc_compile_all_automation @@ -41,6 +41,7 @@ compileContract automation/v2_0/KeeperRegistryLogic2_0.sol compileContract automation/UpkeepTranscoder.sol compileContract automation/mocks/MockAggregatorProxy.sol compileContract automation/testhelpers/LogUpkeepCounter.sol +compileContract automation/testhelpers/SimpleLogUpkeepCounter.sol compileContract automation/mocks/KeeperRegistrar1_2Mock.sol compileContract automation/mocks/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.sol diff --git a/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol b/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol new file mode 100644 index 00000000000..563c1354b66 --- /dev/null +++ b/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.6; + +import {ILogAutomation, Log} from "../interfaces/ILogAutomation.sol"; + +contract SimpleLogUpkeepCounter is ILogAutomation { + event PerformingUpkeep( + address indexed from, + uint256 initialBlock, + uint256 lastBlock, + uint256 previousBlock, + uint256 counter, + uint256 timeToPerform + ); + + uint256 public lastBlock; + uint256 public previousPerformBlock; + uint256 public initialBlock; + uint256 public counter; + uint256 public timeToPerform; + + constructor() { + previousPerformBlock = 0; + lastBlock = block.number; + initialBlock = 0; + counter = 0; + } + + function checkLog(Log calldata log, bytes memory) external view override returns (bool, bytes memory) { + return (true, abi.encode(log)); + } + + function performUpkeep(bytes calldata performData) external override { + if (initialBlock == 0) { + initialBlock = block.number; + } + lastBlock = block.number; + counter = counter + 1; + previousPerformBlock = lastBlock; + Log memory log = abi.decode(performData, (Log)); + timeToPerform = block.timestamp - log.timestamp; + emit PerformingUpkeep(tx.origin, initialBlock, lastBlock, previousPerformBlock, counter, timeToPerform); + } +} diff --git a/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go b/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go new file mode 100644 index 00000000000..bb28c8bd6c4 --- /dev/null +++ b/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go @@ -0,0 +1,504 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package simple_log_upkeep_counter_wrapper + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type Log struct { + Index *big.Int + Timestamp *big.Int + TxHash [32]byte + BlockNumber *big.Int + BlockHash [32]byte + Source common.Address + Topics [][32]byte + Data []byte +} + +var SimpleLogUpkeepCounterMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"initialBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lastBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"counter\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"timeToPerform\",\"type\":\"uint256\"}],\"name\":\"PerformingUpkeep\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"topics\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structLog\",\"name\":\"log\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"checkLog\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"counter\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"performUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"previousPerformBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"timeToPerform\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5060006001819055438155600281905560035561088a806100326000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806361bc221a1161005b57806361bc221a146100d4578063806b984f146100dd578063917d895f146100e6578063c6066f0d146100ef57600080fd5b80632cb158641461008257806340691db41461009e5780634585e33b146100bf575b600080fd5b61008b60025481565b6040519081526020015b60405180910390f35b6100b16100ac366004610384565b6100f8565b60405161009592919061055e565b6100d26100cd366004610312565b61012a565b005b61008b60035481565b61008b60005481565b61008b60015481565b61008b60045481565b6000606060018460405160200161010f91906105db565b604051602081830303815290604052915091505b9250929050565b60025461013657436002555b436000556003546101489060016107f0565b6003556000805460015561015e828401846103f1565b90508060200151426101709190610808565b6004819055600254600054600154600354604080519485526020850193909352918301526060820152608081019190915232907f4874b8dd61a40fe23599b4360a9a824d7081742fca9f555bcee3d389c4f4bd659060a00160405180910390a2505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101f957600080fd5b919050565b600082601f83011261020f57600080fd5b8135602067ffffffffffffffff82111561022b5761022b61084e565b8160051b61023a8282016106d6565b83815282810190868401838801850189101561025557600080fd5b600093505b8584101561027857803583526001939093019291840191840161025a565b50979650505050505050565b600082601f83011261029557600080fd5b813567ffffffffffffffff8111156102af576102af61084e565b6102e060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016106d6565b8181528460208386010111156102f557600080fd5b816020850160208301376000918101602001919091529392505050565b6000806020838503121561032557600080fd5b823567ffffffffffffffff8082111561033d57600080fd5b818501915085601f83011261035157600080fd5b81358181111561036057600080fd5b86602082850101111561037257600080fd5b60209290920196919550909350505050565b6000806040838503121561039757600080fd5b823567ffffffffffffffff808211156103af57600080fd5b9084019061010082870312156103c457600080fd5b909250602084013590808211156103da57600080fd5b506103e785828601610284565b9150509250929050565b60006020828403121561040357600080fd5b813567ffffffffffffffff8082111561041b57600080fd5b90830190610100828603121561043057600080fd5b6104386106ac565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015261047060a084016101d5565b60a082015260c08301358281111561048757600080fd5b610493878286016101fe565b60c08301525060e0830135828111156104ab57600080fd5b6104b787828601610284565b60e08301525095945050505050565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156104f857600080fd5b8260051b8083602087013760009401602001938452509192915050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b821515815260006020604081840152835180604085015260005b8181101561059457858101830151858201606001528201610578565b818111156105a6576000606083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201606001949350505050565b6020815281356020820152602082013560408201526040820135606082015260608201356080820152608082013560a082015273ffffffffffffffffffffffffffffffffffffffff61062f60a084016101d5565b1660c0820152600061064460c0840184610725565b6101008060e086015261065c610120860183856104c6565b925061066b60e087018761078c565b92507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086850301828701526106a1848483610515565b979650505050505050565b604051610100810167ffffffffffffffff811182821017156106d0576106d061084e565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561071d5761071d61084e565b604052919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261075a57600080fd5b830160208101925035905067ffffffffffffffff81111561077a57600080fd5b8060051b360383131561012357600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126107c157600080fd5b830160208101925035905067ffffffffffffffff8111156107e157600080fd5b80360383131561012357600080fd5b600082198211156108035761080361081f565b500190565b60008282101561081a5761081a61081f565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a", +} + +var SimpleLogUpkeepCounterABI = SimpleLogUpkeepCounterMetaData.ABI + +var SimpleLogUpkeepCounterBin = SimpleLogUpkeepCounterMetaData.Bin + +func DeploySimpleLogUpkeepCounter(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SimpleLogUpkeepCounter, error) { + parsed, err := SimpleLogUpkeepCounterMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(SimpleLogUpkeepCounterBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &SimpleLogUpkeepCounter{address: address, abi: *parsed, SimpleLogUpkeepCounterCaller: SimpleLogUpkeepCounterCaller{contract: contract}, SimpleLogUpkeepCounterTransactor: SimpleLogUpkeepCounterTransactor{contract: contract}, SimpleLogUpkeepCounterFilterer: SimpleLogUpkeepCounterFilterer{contract: contract}}, nil +} + +type SimpleLogUpkeepCounter struct { + address common.Address + abi abi.ABI + SimpleLogUpkeepCounterCaller + SimpleLogUpkeepCounterTransactor + SimpleLogUpkeepCounterFilterer +} + +type SimpleLogUpkeepCounterCaller struct { + contract *bind.BoundContract +} + +type SimpleLogUpkeepCounterTransactor struct { + contract *bind.BoundContract +} + +type SimpleLogUpkeepCounterFilterer struct { + contract *bind.BoundContract +} + +type SimpleLogUpkeepCounterSession struct { + Contract *SimpleLogUpkeepCounter + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type SimpleLogUpkeepCounterCallerSession struct { + Contract *SimpleLogUpkeepCounterCaller + CallOpts bind.CallOpts +} + +type SimpleLogUpkeepCounterTransactorSession struct { + Contract *SimpleLogUpkeepCounterTransactor + TransactOpts bind.TransactOpts +} + +type SimpleLogUpkeepCounterRaw struct { + Contract *SimpleLogUpkeepCounter +} + +type SimpleLogUpkeepCounterCallerRaw struct { + Contract *SimpleLogUpkeepCounterCaller +} + +type SimpleLogUpkeepCounterTransactorRaw struct { + Contract *SimpleLogUpkeepCounterTransactor +} + +func NewSimpleLogUpkeepCounter(address common.Address, backend bind.ContractBackend) (*SimpleLogUpkeepCounter, error) { + abi, err := abi.JSON(strings.NewReader(SimpleLogUpkeepCounterABI)) + if err != nil { + return nil, err + } + contract, err := bindSimpleLogUpkeepCounter(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &SimpleLogUpkeepCounter{address: address, abi: abi, SimpleLogUpkeepCounterCaller: SimpleLogUpkeepCounterCaller{contract: contract}, SimpleLogUpkeepCounterTransactor: SimpleLogUpkeepCounterTransactor{contract: contract}, SimpleLogUpkeepCounterFilterer: SimpleLogUpkeepCounterFilterer{contract: contract}}, nil +} + +func NewSimpleLogUpkeepCounterCaller(address common.Address, caller bind.ContractCaller) (*SimpleLogUpkeepCounterCaller, error) { + contract, err := bindSimpleLogUpkeepCounter(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &SimpleLogUpkeepCounterCaller{contract: contract}, nil +} + +func NewSimpleLogUpkeepCounterTransactor(address common.Address, transactor bind.ContractTransactor) (*SimpleLogUpkeepCounterTransactor, error) { + contract, err := bindSimpleLogUpkeepCounter(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &SimpleLogUpkeepCounterTransactor{contract: contract}, nil +} + +func NewSimpleLogUpkeepCounterFilterer(address common.Address, filterer bind.ContractFilterer) (*SimpleLogUpkeepCounterFilterer, error) { + contract, err := bindSimpleLogUpkeepCounter(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &SimpleLogUpkeepCounterFilterer{contract: contract}, nil +} + +func bindSimpleLogUpkeepCounter(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := SimpleLogUpkeepCounterMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _SimpleLogUpkeepCounter.Contract.SimpleLogUpkeepCounterCaller.contract.Call(opts, result, method, params...) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.SimpleLogUpkeepCounterTransactor.contract.Transfer(opts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.SimpleLogUpkeepCounterTransactor.contract.Transact(opts, method, params...) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _SimpleLogUpkeepCounter.Contract.contract.Call(opts, result, method, params...) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.contract.Transfer(opts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.contract.Transact(opts, method, params...) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) CheckLog(opts *bind.CallOpts, log Log, arg1 []byte) (bool, []byte, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "checkLog", log, arg1) + + if err != nil { + return *new(bool), *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + out1 := *abi.ConvertType(out[1], new([]byte)).(*[]byte) + + return out0, out1, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) CheckLog(log Log, arg1 []byte) (bool, []byte, error) { + return _SimpleLogUpkeepCounter.Contract.CheckLog(&_SimpleLogUpkeepCounter.CallOpts, log, arg1) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) CheckLog(log Log, arg1 []byte) (bool, []byte, error) { + return _SimpleLogUpkeepCounter.Contract.CheckLog(&_SimpleLogUpkeepCounter.CallOpts, log, arg1) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) Counter(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "counter") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) Counter() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.Counter(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) Counter() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.Counter(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) InitialBlock(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "initialBlock") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) InitialBlock() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.InitialBlock(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) InitialBlock() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.InitialBlock(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) LastBlock(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "lastBlock") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) LastBlock() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.LastBlock(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) LastBlock() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.LastBlock(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) PreviousPerformBlock(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "previousPerformBlock") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) PreviousPerformBlock() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.PreviousPerformBlock(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) PreviousPerformBlock() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.PreviousPerformBlock(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) TimeToPerform(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "timeToPerform") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) TimeToPerform() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.TimeToPerform(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) TimeToPerform() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.TimeToPerform(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterTransactor) PerformUpkeep(opts *bind.TransactOpts, performData []byte) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.contract.Transact(opts, "performUpkeep", performData) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) PerformUpkeep(performData []byte) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.PerformUpkeep(&_SimpleLogUpkeepCounter.TransactOpts, performData) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterTransactorSession) PerformUpkeep(performData []byte) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.PerformUpkeep(&_SimpleLogUpkeepCounter.TransactOpts, performData) +} + +type SimpleLogUpkeepCounterPerformingUpkeepIterator struct { + Event *SimpleLogUpkeepCounterPerformingUpkeep + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *SimpleLogUpkeepCounterPerformingUpkeepIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(SimpleLogUpkeepCounterPerformingUpkeep) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(SimpleLogUpkeepCounterPerformingUpkeep) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *SimpleLogUpkeepCounterPerformingUpkeepIterator) Error() error { + return it.fail +} + +func (it *SimpleLogUpkeepCounterPerformingUpkeepIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type SimpleLogUpkeepCounterPerformingUpkeep struct { + From common.Address + InitialBlock *big.Int + LastBlock *big.Int + PreviousBlock *big.Int + Counter *big.Int + TimeToPerform *big.Int + Raw types.Log +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterFilterer) FilterPerformingUpkeep(opts *bind.FilterOpts, from []common.Address) (*SimpleLogUpkeepCounterPerformingUpkeepIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + + logs, sub, err := _SimpleLogUpkeepCounter.contract.FilterLogs(opts, "PerformingUpkeep", fromRule) + if err != nil { + return nil, err + } + return &SimpleLogUpkeepCounterPerformingUpkeepIterator{contract: _SimpleLogUpkeepCounter.contract, event: "PerformingUpkeep", logs: logs, sub: sub}, nil +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterFilterer) WatchPerformingUpkeep(opts *bind.WatchOpts, sink chan<- *SimpleLogUpkeepCounterPerformingUpkeep, from []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + + logs, sub, err := _SimpleLogUpkeepCounter.contract.WatchLogs(opts, "PerformingUpkeep", fromRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(SimpleLogUpkeepCounterPerformingUpkeep) + if err := _SimpleLogUpkeepCounter.contract.UnpackLog(event, "PerformingUpkeep", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterFilterer) ParsePerformingUpkeep(log types.Log) (*SimpleLogUpkeepCounterPerformingUpkeep, error) { + event := new(SimpleLogUpkeepCounterPerformingUpkeep) + if err := _SimpleLogUpkeepCounter.contract.UnpackLog(event, "PerformingUpkeep", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounter) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _SimpleLogUpkeepCounter.abi.Events["PerformingUpkeep"].ID: + return _SimpleLogUpkeepCounter.ParsePerformingUpkeep(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (SimpleLogUpkeepCounterPerformingUpkeep) Topic() common.Hash { + return common.HexToHash("0x4874b8dd61a40fe23599b4360a9a824d7081742fca9f555bcee3d389c4f4bd65") +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounter) Address() common.Address { + return _SimpleLogUpkeepCounter.address +} + +type SimpleLogUpkeepCounterInterface interface { + CheckLog(opts *bind.CallOpts, log Log, arg1 []byte) (bool, []byte, error) + + Counter(opts *bind.CallOpts) (*big.Int, error) + + InitialBlock(opts *bind.CallOpts) (*big.Int, error) + + LastBlock(opts *bind.CallOpts) (*big.Int, error) + + PreviousPerformBlock(opts *bind.CallOpts) (*big.Int, error) + + TimeToPerform(opts *bind.CallOpts) (*big.Int, error) + + PerformUpkeep(opts *bind.TransactOpts, performData []byte) (*types.Transaction, error) + + FilterPerformingUpkeep(opts *bind.FilterOpts, from []common.Address) (*SimpleLogUpkeepCounterPerformingUpkeepIterator, error) + + WatchPerformingUpkeep(opts *bind.WatchOpts, sink chan<- *SimpleLogUpkeepCounterPerformingUpkeep, from []common.Address) (event.Subscription, error) + + ParsePerformingUpkeep(log types.Log) (*SimpleLogUpkeepCounterPerformingUpkeep, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 6482c01cf88..6efc75fce98 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -50,6 +50,7 @@ operator_factory: ../../contracts/solc/v0.8.19/OperatorFactory/OperatorFactory.a operator_wrapper: ../../contracts/solc/v0.8.19/Operator/Operator.abi ../../contracts/solc/v0.8.19/Operator/Operator.bin d7abd0e67f30a3a4c9c04c896124391306fa364fcf579fa6df04dbf912b48568 oracle_wrapper: ../../contracts/solc/v0.6/Oracle/Oracle.abi ../../contracts/solc/v0.6/Oracle/Oracle.bin 7af2fbac22a6e8c2847e8e685a5400cac5101d72ddf5365213beb79e4dede43a perform_data_checker_wrapper: ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.abi ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.bin 48d8309c2117c29a24e1155917ab0b780956b2cd6a8a39ef06ae66a7f6d94f73 +simple_log_upkeep_counter_wrapper: ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.abi ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.bin 0a7a0cc4da7dc2a3d0a0c36c746b1adc044af5cad1838367356a0604f3255a01 solidity_vrf_consumer_interface: ../../contracts/solc/v0.6/VRFConsumer/VRFConsumer.abi ../../contracts/solc/v0.6/VRFConsumer/VRFConsumer.bin ecc99378aa798014de9db42b2eb81320778b0663dbe208008dad75ccdc1d4366 solidity_vrf_consumer_interface_v08: ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.abi ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.bin b14f9136b15e3dc9d6154d5700f3ed4cf88ddc4f70f20c3bb57fc46050904c8f solidity_vrf_coordinator_interface: ../../contracts/solc/v0.6/VRFCoordinator/VRFCoordinator.abi ../../contracts/solc/v0.6/VRFCoordinator/VRFCoordinator.bin a23d3c395156804788c7f6fbda2994e8f7184304c0f0c9f2c4ddeaf073d346d2 diff --git a/core/gethwrappers/go_generate.go b/core/gethwrappers/go_generate.go index 3965c159080..07e4fa9b8f3 100644 --- a/core/gethwrappers/go_generate.go +++ b/core/gethwrappers/go_generate.go @@ -59,6 +59,7 @@ package gethwrappers //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.abi ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.bin AutomationUtils automation_utils_2_1 //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationForwarderLogic/AutomationForwarderLogic.abi ../../contracts/solc/v0.8.16/AutomationForwarderLogic/AutomationForwarderLogic.bin AutomationForwarderLogic automation_forwarder_logic //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.abi ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.bin LogUpkeepCounter log_upkeep_counter_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.abi ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.bin SimpleLogUpkeepCounter simple_log_upkeep_counter_wrapper //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.abi ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.bin LogTriggeredStreamsLookup log_triggered_streams_lookup_wrapper //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/DummyProtocol/DummyProtocol.abi ../../contracts/solc/v0.8.16/DummyProtocol/DummyProtocol.bin DummyProtocol dummy_protocol_wrapper diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index 45195d327ee..000fe7b2b81 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -55,6 +55,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_factory" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/oracle_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/perform_data_checker_wrapper" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_upkeep_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/test_api_consumer_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/upkeep_counter_wrapper" @@ -92,6 +93,7 @@ type ContractDeployer interface { LoadKeeperRegistry(address common.Address, registryVersion eth_contracts.KeeperRegistryVersion) (KeeperRegistry, error) DeployKeeperConsumer(updateInterval *big.Int) (KeeperConsumer, error) DeployAutomationLogTriggerConsumer(testInterval *big.Int) (KeeperConsumer, error) + DeployAutomationSimpleLogTriggerConsumer() (KeeperConsumer, error) DeployAutomationStreamsLookupUpkeepConsumer(testRange *big.Int, interval *big.Int, useArbBlock bool, staging bool, verify bool) (KeeperConsumer, error) DeployAutomationLogTriggeredStreamsLookupUpkeepConsumer() (KeeperConsumer, error) DeployKeeperConsumerPerformance( @@ -1292,6 +1294,25 @@ func (e *EthereumContractDeployer) DeployAutomationLogTriggerConsumer(testInterv }, err } +func (e *EthereumContractDeployer) DeployAutomationSimpleLogTriggerConsumer() (KeeperConsumer, error) { + address, _, instance, err := e.client.DeployContract("SimpleLogUpkeepCounter", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return simple_log_upkeep_counter_wrapper.DeploySimpleLogUpkeepCounter( + auth, backend, + ) + }) + if err != nil { + return nil, err + } + return &EthereumAutomationSimpleLogCounterConsumer{ + client: e.client, + consumer: instance.(*simple_log_upkeep_counter_wrapper.SimpleLogUpkeepCounter), + address: address, + }, err +} + func (e *EthereumContractDeployer) DeployAutomationStreamsLookupUpkeepConsumer(testRange *big.Int, interval *big.Int, useArbBlock bool, staging bool, verify bool) (KeeperConsumer, error) { address, _, instance, err := e.client.DeployContract("StreamsLookupUpkeep", func( auth *bind.TransactOpts, diff --git a/integration-tests/contracts/ethereum_keeper_contracts.go b/integration-tests/contracts/ethereum_keeper_contracts.go index 2c0250e7454..7519b5de4cf 100644 --- a/integration-tests/contracts/ethereum_keeper_contracts.go +++ b/integration-tests/contracts/ethereum_keeper_contracts.go @@ -35,6 +35,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_triggered_streams_lookup_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_upkeep_counter_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/perform_data_checker_wrapper" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_upkeep_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/upkeep_perform_counter_restrictive_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/upkeep_transcoder" @@ -1821,6 +1822,32 @@ func (v *EthereumAutomationLogCounterConsumer) Counter(ctx context.Context) (*bi return cnt, nil } +type EthereumAutomationSimpleLogCounterConsumer struct { + client blockchain.EVMClient + consumer *simple_log_upkeep_counter_wrapper.SimpleLogUpkeepCounter + address *common.Address +} + +func (v *EthereumAutomationSimpleLogCounterConsumer) Address() string { + return v.address.Hex() +} + +func (v *EthereumAutomationSimpleLogCounterConsumer) Start() error { + return nil +} + +func (v *EthereumAutomationSimpleLogCounterConsumer) Counter(ctx context.Context) (*big.Int, error) { + opts := &bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + } + cnt, err := v.consumer.Counter(opts) + if err != nil { + return nil, err + } + return cnt, nil +} + // EthereumKeeperConsumerPerformance represents a more complicated keeper consumer contract, one intended only for // performance tests. type EthereumKeeperConsumerPerformance struct { diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go new file mode 100644 index 00000000000..dfef099c175 --- /dev/null +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -0,0 +1,621 @@ +package automationv2_1 + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + "os" + "strconv" + "strings" + "testing" + "time" + + geth "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/slack-go/slack" + ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" + ocr2keepers30config "github.com/smartcontractkit/ocr2keepers/pkg/v3/config" + "github.com/smartcontractkit/wasp" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + contractseth "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "github.com/smartcontractkit/chainlink/integration-tests/testreporters" + registrar21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_registrar_wrapper2_1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" +) + +const ( + StartupWaitTime = 30 * time.Second + StopWaitTime = 60 * time.Second +) + +var ( + baseTOML = `[Feature] +LogPoller = true + +[OCR2] +Enabled = true + +[P2P] +[P2P.V2] +Enabled = true +AnnounceAddresses = ["0.0.0.0:6690"] +ListenAddresses = ["0.0.0.0:6690"]` + + minimumNodeSpec = map[string]interface{}{ + "resources": map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": "2000m", + "memory": "4Gi", + }, + "limits": map[string]interface{}{ + "cpu": "2000m", + "memory": "4Gi", + }, + }, + } + + minimumDbSpec = map[string]interface{}{ + "resources": map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": "1000m", + "memory": "1Gi", + }, + "limits": map[string]interface{}{ + "cpu": "1000m", + "memory": "1Gi", + }, + }, + "stateful": true, + "capacity": "5Gi", + } + + recNodeSpec = map[string]interface{}{ + "resources": map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": "4000m", + "memory": "8Gi", + }, + "limits": map[string]interface{}{ + "cpu": "4000m", + "memory": "8Gi", + }, + }, + } + + recDbSpec = map[string]interface{}{ + "resources": map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": "2000m", + "memory": "2Gi", + }, + "limits": map[string]interface{}{ + "cpu": "2000m", + "memory": "2Gi", + }, + }, + "stateful": true, + "capacity": "10Gi", + } +) + +var ( + numberofNodes, _ = strconv.Atoi(getEnv("NUMBEROFNODES", "6")) // Number of nodes in the DON + numberOfUpkeeps, _ = strconv.Atoi(getEnv("NUMBEROFUPKEEPS", "100")) // Number of log triggered upkeeps + duration, _ = strconv.Atoi(getEnv("DURATION", "900")) // Test duration in seconds + blockTime, _ = strconv.Atoi(getEnv("BLOCKTIME", "1")) // Block time in seconds for geth simulated dev network + numberOfEvents, _ = strconv.Atoi(getEnv("NUMBEROFEVENTS", "1")) // Number of events to emit per trigger + specType = getEnv("SPECTYPE", "minimum") // minimum, recommended, local specs for the test + logLevel = getEnv("LOGLEVEL", "info") // log level for the chainlink nodes + pyroscope, _ = strconv.ParseBool(getEnv("PYROSCOPE", "false")) // enable pyroscope for the chainlink nodes +) + +func TestLogTrigger(t *testing.T) { + l := logging.GetTestLogger(t) + + l.Info().Msg("Starting automation v2.1 log trigger load test") + l.Info().Str("TEST_INPUTS", os.Getenv("TEST_INPUTS")).Int("Number of Nodes", numberofNodes). + Int("Number of Upkeeps", numberOfUpkeeps). + Int("Duration", duration). + Int("Block Time", blockTime). + Int("Number of Events", numberOfEvents). + Str("Spec Type", specType). + Str("Log Level", logLevel). + Str("Image", os.Getenv(config.EnvVarCLImage)). + Str("Tag", os.Getenv(config.EnvVarCLTag)). + Msg("Test Config") + + testConfig := fmt.Sprintf("Number of Nodes: %d\nNumber of Upkeeps: %d\nDuration: %d\nBlock Time: %d\n"+ + "Number of Events: %d\nSpec Type: %s\nLog Level: %s\nImage: %s\nTag: %s\n", numberofNodes, numberOfUpkeeps, duration, + blockTime, numberOfEvents, specType, logLevel, os.Getenv(config.EnvVarCLImage), os.Getenv(config.EnvVarCLTag)) + + testNetwork := networks.MustGetSelectedNetworksFromEnv()[0] + testType := "load" + loadDuration := time.Duration(duration) * time.Second + automationDefaultLinkFunds := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(int64(10000))) //10000 LINK + automationDefaultUpkeepGasLimit := uint32(1_000_000) + + registrySettings := &contracts.KeeperRegistrySettings{ + PaymentPremiumPPB: uint32(0), + FlatFeeMicroLINK: uint32(40_000), + BlockCountPerTurn: big.NewInt(100), + CheckGasLimit: uint32(45_000_000), //45M + StalenessSeconds: big.NewInt(90_000), + GasCeilingMultiplier: uint16(2), + MaxPerformGas: uint32(5_000_000), + MinUpkeepSpend: big.NewInt(0), + FallbackGasPrice: big.NewInt(2e11), + FallbackLinkPrice: big.NewInt(2e18), + MaxCheckDataSize: uint32(5_000), + MaxPerformDataSize: uint32(5_000), + RegistryVersion: contractseth.RegistryVersion_2_1, + } + + testEnvironment := environment.New(&environment.Config{ + TTL: time.Hour * 24, // 1 day, + NamespacePrefix: fmt.Sprintf( + "automation-%s-%s", + testType, + strings.ReplaceAll(strings.ToLower(testNetwork.Name), " ", "-"), + ), + Test: t, + PreventPodEviction: true, + }) + + if testEnvironment.WillUseRemoteRunner() { + key := "TEST_INPUTS" + err := os.Setenv(fmt.Sprintf("TEST_%s", key), os.Getenv(key)) + require.NoError(t, err, "failed to set the environment variable TEST_INPUTS for remote runner") + + key = config.EnvVarPyroscopeServer + err = os.Setenv(fmt.Sprintf("TEST_%s", key), os.Getenv(key)) + require.NoError(t, err, "failed to set the environment variable PYROSCOPE_SERVER for remote runner") + + key = config.EnvVarPyroscopeKey + err = os.Setenv(fmt.Sprintf("TEST_%s", key), os.Getenv(key)) + require.NoError(t, err, "failed to set the environment variable PYROSCOPE_KEY for remote runner") + + key = "GRAFANA_DASHBOARD_URL" + err = os.Setenv(fmt.Sprintf("TEST_%s", key), getEnv(key, "")) + require.NoError(t, err, "failed to set the environment variable GRAFANA_DASHBOARD_URL for remote runner") + } + + testEnvironment. + AddHelm(ethereum.New(ðereum.Props{ + NetworkName: testNetwork.Name, + Simulated: testNetwork.Simulated, + WsURLs: testNetwork.URLs, + Values: map[string]interface{}{ + "resources": map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": "4000m", + "memory": "4Gi", + }, + "limits": map[string]interface{}{ + "cpu": "8000m", + "memory": "8Gi", + }, + }, + "geth": map[string]interface{}{ + "blocktime": blockTime, + "capacity": "10Gi", + }, + }, + })) + + err := testEnvironment.Run() + require.NoError(t, err, "Error launching test environment") + + if testEnvironment.WillUseRemoteRunner() { + return + } + + var ( + nodeSpec = minimumNodeSpec + dbSpec = minimumDbSpec + ) + + switch specType { + case "recommended": + nodeSpec = recNodeSpec + dbSpec = recDbSpec + case "local": + nodeSpec = map[string]interface{}{} + dbSpec = map[string]interface{}{"stateful": true} + default: + // minimum: + + } + + if !pyroscope { + err = os.Setenv(config.EnvVarPyroscopeServer, "") + require.NoError(t, err, "Error setting pyroscope server env var") + } + + err = os.Setenv(config.EnvVarPyroscopeEnvironment, testEnvironment.Cfg.Namespace) + require.NoError(t, err, "Error setting pyroscope environment env var") + + for i := 0; i < numberofNodes+1; i++ { // +1 for the OCR boot node + var nodeTOML string + if i == 1 || i == 3 { + nodeTOML = fmt.Sprintf("%s\n\n[Log]\nLevel = \"%s\"", baseTOML, logLevel) + } else { + nodeTOML = fmt.Sprintf("%s\n\n[Log]\nLevel = \"info\"", baseTOML) + } + nodeTOML = networks.AddNetworksConfig(nodeTOML, testNetwork) + testEnvironment.AddHelm(chainlink.New(i, map[string]any{ + "toml": nodeTOML, + "chainlink": nodeSpec, + "db": dbSpec, + })) + } + + err = testEnvironment.Run() + require.NoError(t, err, "Error running chainlink DON") + + chainClient, err := blockchain.NewEVMClient(testNetwork, testEnvironment, l) + require.NoError(t, err, "Error building chain client") + + contractDeployer, err := contracts.NewContractDeployer(chainClient, l) + require.NoError(t, err, "Error building contract deployer") + + chainlinkNodes, err := client.ConnectChainlinkNodes(testEnvironment) + require.NoError(t, err, "Error connecting to chainlink nodes") + + chainClient.ParallelTransactions(true) + + linkToken, err := contractDeployer.DeployLinkTokenContract() + require.NoError(t, err, "Error deploying link token contract") + + err = chainClient.WaitForEvents() + require.NoError(t, err, "Failed waiting for contracts to deploy") + + registry, registrar := actions.DeployAutoOCRRegistryAndRegistrar( + t, contractseth.RegistryVersion_2_1, *registrySettings, linkToken, contractDeployer, chainClient, + ) + + err = actions.FundChainlinkNodesAddress(chainlinkNodes[1:], chainClient, big.NewFloat(100), 0) + require.NoError(t, err, "Error funding chainlink nodes") + + actions.CreateOCRKeeperJobs( + t, + chainlinkNodes, + registry.Address(), + chainClient.GetChainID().Int64(), + 0, + contractseth.RegistryVersion_2_1, + ) + + S, oracleIdentities, err := actions.GetOracleIdentities(chainlinkNodes) + require.NoError(t, err, "Error getting oracle identities") + offC, err := json.Marshal(ocr2keepers30config.OffchainConfig{ + TargetProbability: "0.999", + TargetInRounds: 1, + PerformLockoutWindow: 80_000, // Copied from arbitrum mainnet prod value + GasLimitPerReport: 10_300_000, + GasOverheadPerUpkeep: 300_000, + MinConfirmations: 0, + MaxUpkeepBatchSize: 10, + }) + require.NoError(t, err, "Error marshalling offchain config") + + signerOnchainPublicKeys, transmitterAccounts, f, _, offchainConfigVersion, offchainConfig, err := ocr3.ContractSetConfigArgsForTests( + 10*time.Second, // deltaProgress time.Duration, + 15*time.Second, // deltaResend time.Duration, + 500*time.Millisecond, // deltaInitial time.Duration, + 1000*time.Millisecond, // deltaRound time.Duration, + 200*time.Millisecond, // deltaGrace time.Duration, + 300*time.Millisecond, // deltaCertifiedCommitRequest time.Duration + 15*time.Second, // deltaStage time.Duration, + 24, // rMax uint64, + S, // s []int, + oracleIdentities, // oracles []OracleIdentityExtra, + offC, // reportingPluginConfig []byte, + 20*time.Millisecond, // maxDurationQuery time.Duration, + 20*time.Millisecond, // maxDurationObservation time.Duration, // good to here + 1200*time.Millisecond, // maxDurationShouldAcceptAttestedReport time.Duration, + 20*time.Millisecond, // maxDurationShouldTransmitAcceptedReport time.Duration, + 1, // f int, + nil, // onchainConfig []byte, + ) + require.NoError(t, err, "Error setting OCR config vars") + + var signers []common.Address + for _, signer := range signerOnchainPublicKeys { + require.Equal(t, 20, len(signer), "OnChainPublicKey '%v' has wrong length for address", signer) + signers = append(signers, common.BytesToAddress(signer)) + } + + var transmitters []common.Address + for _, transmitter := range transmitterAccounts { + require.True(t, common.IsHexAddress(string(transmitter)), "TransmitAccount '%s' is not a valid Ethereum address", string(transmitter)) + transmitters = append(transmitters, common.HexToAddress(string(transmitter))) + } + + onchainConfig, err := registrySettings.EncodeOnChainConfig(registrar.Address(), common.HexToAddress(chainClient.GetDefaultWallet().Address())) + require.NoError(t, err, "Error encoding onchain config") + l.Info().Msg("Done building OCR config") + ocrConfig := contracts.OCRv2Config{ + Signers: signers, + Transmitters: transmitters, + F: f, + OnchainConfig: onchainConfig, + OffchainConfigVersion: offchainConfigVersion, + OffchainConfig: offchainConfig, + } + + err = registry.SetConfig(*registrySettings, ocrConfig) + require.NoError(t, err, "Error setting registry config") + + consumerContracts := make([]contracts.KeeperConsumer, 0) + triggerContracts := make([]contracts.LogEmitter, 0) + + utilsABI, err := automation_utils_2_1.AutomationUtilsMetaData.GetAbi() + require.NoError(t, err, "Error getting automation utils abi") + registrarABI, err := registrar21.AutomationRegistrarMetaData.GetAbi() + require.NoError(t, err, "Error getting automation registrar abi") + emitterABI, err := log_emitter.LogEmitterMetaData.GetAbi() + require.NoError(t, err, "Error getting log emitter abi") + consumerABI, err := simple_log_upkeep_counter_wrapper.SimpleLogUpkeepCounterMetaData.GetAbi() + require.NoError(t, err, "Error getting consumer abi") + + var bytes0 = [32]byte{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + } + registrationTxHashes := make([]common.Hash, 0) + upkeepIds := make([]*big.Int, 0) + + for i := 0; i < numberOfUpkeeps; i++ { + consumerContract, err := contractDeployer.DeployAutomationSimpleLogTriggerConsumer() + require.NoError(t, err, "Error deploying automation consumer contract") + consumerContracts = append(consumerContracts, consumerContract) + l.Debug(). + Str("Contract Address", consumerContract.Address()). + Int("Number", i+1). + Int("Out Of", numberOfUpkeeps). + Msg("Deployed Automation Log Trigger Consumer Contract") + + cEVMClient, err := blockchain.ConcurrentEVMClient(testNetwork, testEnvironment, chainClient, l) + require.NoError(t, err, "Error building concurrent chain client") + + cContractDeployer, err := contracts.NewContractDeployer(cEVMClient, l) + require.NoError(t, err, "Error building concurrent contract deployer") + + triggerContract, err := cContractDeployer.DeployLogEmitterContract() + require.NoError(t, err, "Error deploying log emitter contract") + triggerContracts = append(triggerContracts, triggerContract) + l.Debug(). + Str("Contract Address", triggerContract.Address().Hex()). + Int("Number", i+1). + Int("Out Of", numberOfUpkeeps). + Msg("Deployed Automation Log Trigger Emitter Contract") + } + + err = chainClient.WaitForEvents() + require.NoError(t, err, "Failed waiting for contracts to deploy") + + for i, consumerContract := range consumerContracts { + logTriggerConfigStruct := automation_utils_2_1.LogTriggerConfig{ + ContractAddress: triggerContracts[i].Address(), + FilterSelector: 0, + Topic0: emitterABI.Events["Log1"].ID, + Topic1: bytes0, + Topic2: bytes0, + Topic3: bytes0, + } + encodedLogTriggerConfig, err := utilsABI.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) + require.NoError(t, err, "Error encoding log trigger config") + l.Debug().Bytes("Encoded Log Trigger Config", encodedLogTriggerConfig).Msg("Encoded Log Trigger Config") + + registrationRequest, err := registrarABI.Pack( + "register", + fmt.Sprintf("LogTriggerUpkeep-%d", i), + []byte("test@mail.com"), + common.HexToAddress(consumerContract.Address()), + automationDefaultUpkeepGasLimit, + common.HexToAddress(chainClient.GetDefaultWallet().Address()), + uint8(1), + []byte("0"), + encodedLogTriggerConfig, + []byte("0"), + automationDefaultLinkFunds, + common.HexToAddress(chainClient.GetDefaultWallet().Address()), + ) + require.NoError(t, err, "Error encoding upkeep registration request") + tx, err := linkToken.TransferAndCall(registrar.Address(), automationDefaultLinkFunds, registrationRequest) + require.NoError(t, err, "Error sending upkeep registration request") + registrationTxHashes = append(registrationTxHashes, tx.Hash()) + } + + err = chainClient.WaitForEvents() + require.NoError(t, err, "Failed waiting for upkeeps to be registered") + + for _, txHash := range registrationTxHashes { + receipt, err := chainClient.GetTxReceipt(txHash) + require.NoError(t, err, "Registration tx should be completed") + var upkeepId *big.Int + for _, rawLog := range receipt.Logs { + parsedUpkeepId, err := registry.ParseUpkeepIdFromRegisteredLog(rawLog) + if err == nil { + upkeepId = parsedUpkeepId + break + } + } + require.NotNil(t, upkeepId, "Upkeep ID should be found after registration") + l.Debug(). + Str("TxHash", txHash.String()). + Str("Upkeep ID", upkeepId.String()). + Msg("Found upkeepId in tx hash") + upkeepIds = append(upkeepIds, upkeepId) + } + l.Info().Msg("Successfully registered all Automation Consumer Contracts") + l.Info().Interface("Upkeep IDs", upkeepIds).Msg("Upkeep IDs") + l.Info().Str("STARTUP_WAIT_TIME", StartupWaitTime.String()).Msg("Waiting for plugin to start") + time.Sleep(StartupWaitTime) + + startBlock, err := chainClient.LatestBlockNumber(context.Background()) + require.NoError(t, err, "Error getting latest block number") + + p := wasp.NewProfile() + + for i, triggerContract := range triggerContracts { + g, err := wasp.NewGenerator(&wasp.Config{ + T: t, + LoadType: wasp.RPS, + GenName: fmt.Sprintf("log_trigger_gen_%s", triggerContract.Address().String()), + CallTimeout: time.Second * 10, + Schedule: wasp.Plain( + 1, + loadDuration, + ), + Gun: NewLogTriggerUser( + triggerContract, + consumerContracts[i], + l, + numberOfEvents, + ), + CallResultBufLen: 1000000, + }) + p.Add(g, err) + } + + l.Info().Msg("Starting load generators") + startTime := time.Now() + err = sendSlackNotification("Started", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), + strconv.FormatInt(startTime.UnixMilli(), 10), "now", + []slack.Block{extraBlockWithText("\bTest Config\b\n```" + testConfig + "```")}) + if err != nil { + l.Error().Err(err).Msg("Error sending slack notification") + } + _, err = p.Run(true) + require.NoError(t, err, "Error running load generators") + + l.Info().Msg("Finished load generators") + l.Info().Str("STOP_WAIT_TIME", StopWaitTime.String()).Msg("Waiting for upkeeps to be performed") + time.Sleep(StopWaitTime) + l.Info().Msg("Finished waiting 60s for upkeeps to be performed") + endTime := time.Now() + testDuration := endTime.Sub(startTime) + l.Info().Str("Duration", testDuration.String()).Msg("Test Duration") + endBlock, err := chainClient.LatestBlockNumber(context.Background()) + require.NoError(t, err, "Error getting latest block number") + l.Info().Uint64("Starting Block", startBlock).Uint64("Ending Block", endBlock).Msg("Test Block Range") + + upkeepDelays := make([][]int64, 0) + var numberOfEventsEmitted int + var batchSize = 500 + + for _, gen := range p.Generators { + numberOfEventsEmitted += len(gen.GetData().OKData.Data) + } + numberOfEventsEmitted = numberOfEventsEmitted * numberOfEvents + l.Info().Int("Number of Events Emitted", numberOfEventsEmitted).Msg("Number of Events Emitted") + + if endBlock-startBlock < uint64(batchSize) { + batchSize = int(endBlock - startBlock) + } + + for cIter, consumerContract := range consumerContracts { + var ( + logs []types.Log + address = common.HexToAddress(consumerContract.Address()) + timeout = 5 * time.Second + ) + for fromBlock := startBlock; fromBlock < endBlock; fromBlock += uint64(batchSize) + 1 { + var ( + filterQuery = geth.FilterQuery{ + Addresses: []common.Address{address}, + FromBlock: big.NewInt(0).SetUint64(fromBlock), + ToBlock: big.NewInt(0).SetUint64(fromBlock + uint64(batchSize)), + Topics: [][]common.Hash{{consumerABI.Events["PerformingUpkeep"].ID}}, + } + ) + ctx, cancel := context.WithTimeout(context.Background(), timeout) + logsInBatch, err := chainClient.FilterLogs(ctx, filterQuery) + cancel() + if err != nil { + l.Error().Err(err). + Interface("FilterQuery", filterQuery). + Str("Contract Address", consumerContract.Address()). + Str("Timeout", timeout.String()). + Msg("Error getting logs") + } + logs = append(logs, logsInBatch...) + time.Sleep(time.Millisecond * 500) + } + + if len(logs) > 0 { + delay := make([]int64, 0) + for _, log := range logs { + eventDetails, err := consumerABI.EventByID(log.Topics[0]) + require.NoError(t, err, "Error getting event details") + consumer, err := simple_log_upkeep_counter_wrapper.NewSimpleLogUpkeepCounter( + address, chainClient.Backend(), + ) + require.NoError(t, err, "Error getting consumer contract") + if eventDetails.Name == "PerformingUpkeep" { + parsedLog, err := consumer.ParsePerformingUpkeep(log) + require.NoError(t, err, "Error parsing log") + delay = append(delay, parsedLog.TimeToPerform.Int64()) + } + } + upkeepDelays = append(upkeepDelays, delay) + } + if (cIter+1)%batchSize == 0 { + time.Sleep(time.Millisecond * 500) + } + } + + l.Info().Interface("Upkeep Delays", upkeepDelays).Msg("Upkeep Delays") + + var allUpkeepDelays []int64 + + for _, upkeepDelay := range upkeepDelays { + allUpkeepDelays = append(allUpkeepDelays, upkeepDelay...) + } + + avg, median, ninetyPct, ninetyNinePct, maximum := testreporters.IntListStats(allUpkeepDelays) + l.Info(). + Float64("Average", avg).Int64("Median", median). + Int64("90th Percentile", ninetyPct).Int64("99th Percentile", ninetyNinePct). + Int64("Max", maximum).Msg("Upkeep Delays in seconds") + + l.Info(). + Int("Total Perform Count", len(allUpkeepDelays)). + Int("Total Events Emitted", numberOfEventsEmitted). + Int("Total Events Missed", numberOfEventsEmitted-len(allUpkeepDelays)). + Msg("Test completed") + + testReport := fmt.Sprintf("Upkeep Delays in seconds\nAverage: %f\nMedian: %d\n90th Percentile: %d\n"+ + "99th Percentile: %d\nMax: %d\nTotal Perform Count: %d\n\nTotal Events Emitted: %d\nTotal Events Missed: %d\nTest Duration: %s\n", + avg, median, ninetyPct, ninetyNinePct, maximum, len(allUpkeepDelays), numberOfEventsEmitted, + numberOfEventsEmitted-len(allUpkeepDelays), testDuration.String()) + + err = sendSlackNotification("Finished", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), + strconv.FormatInt(startTime.UnixMilli(), 10), strconv.FormatInt(endTime.UnixMilli(), 10), + []slack.Block{extraBlockWithText("\bTest Report\b\n```" + testReport + "```")}) + if err != nil { + l.Error().Err(err).Msg("Error sending slack notification") + } + + t.Cleanup(func() { + if err = actions.TeardownRemoteSuite(t, testEnvironment.Cfg.Namespace, chainlinkNodes, nil, chainClient); err != nil { + l.Error().Err(err).Msg("Error when tearing down remote suite") + } + }) + +} diff --git a/integration-tests/load/automationv2_1/gun.go b/integration-tests/load/automationv2_1/gun.go new file mode 100644 index 00000000000..a2863a6064b --- /dev/null +++ b/integration-tests/load/automationv2_1/gun.go @@ -0,0 +1,43 @@ +package automationv2_1 + +import ( + "github.com/rs/zerolog" + "github.com/smartcontractkit/wasp" + + "github.com/smartcontractkit/chainlink/integration-tests/contracts" +) + +type LogTriggerGun struct { + triggerContract contracts.LogEmitter + upkeepContract contracts.KeeperConsumer + logger zerolog.Logger + numberOfEvents int +} + +func NewLogTriggerUser( + triggerContract contracts.LogEmitter, + upkeepContract contracts.KeeperConsumer, + logger zerolog.Logger, + numberOfEvents int, +) *LogTriggerGun { + return &LogTriggerGun{ + triggerContract: triggerContract, + upkeepContract: upkeepContract, + logger: logger, + numberOfEvents: numberOfEvents, + } +} + +func (m *LogTriggerGun) Call(_ *wasp.Generator) *wasp.CallResult { + m.logger.Debug().Str("Trigger address", m.triggerContract.Address().String()).Msg("Triggering upkeep") + payload := make([]int, 0) + for i := 0; i < m.numberOfEvents; i++ { + payload = append(payload, 1) + } + _, err := m.triggerContract.EmitLogInts(payload) + if err != nil { + return &wasp.CallResult{Error: err.Error(), Failed: true} + } + + return &wasp.CallResult{} +} diff --git a/integration-tests/load/automationv2_1/helpers.go b/integration-tests/load/automationv2_1/helpers.go new file mode 100644 index 00000000000..3c08199a9cf --- /dev/null +++ b/integration-tests/load/automationv2_1/helpers.go @@ -0,0 +1,71 @@ +package automationv2_1 + +import ( + "fmt" + "os" + "strings" + + "github.com/rs/zerolog" + "github.com/slack-go/slack" + + "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" + reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" + "github.com/smartcontractkit/chainlink/integration-tests/testreporters" +) + +func getEnv(key, fallback string) string { + if inputs, ok := os.LookupEnv("TEST_INPUTS"); ok { + values := strings.Split(inputs, ",") + for _, value := range values { + if strings.Contains(value, key) { + return strings.Split(value, "=")[1] + } + } + } + return fallback +} + +func extraBlockWithText(text string) slack.Block { + return slack.NewSectionBlock(slack.NewTextBlockObject( + "mrkdwn", text, false, false), nil, nil) +} + +func sendSlackNotification(header string, l zerolog.Logger, namespace string, numberOfNodes, + startingTime string, endingTime string, extraBlocks []slack.Block) error { + slackClient := slack.New(reportModel.SlackAPIKey) + + headerText := ":chainlink-keepers: Automation Load Test " + header + " :white_check_mark:" + + formattedDashboardUrl := fmt.Sprintf("%s?orgId=1&from=%s&to=%s&var-namespace=%s&var-number_of_nodes=%s", testreporters.DashboardUrl, startingTime, endingTime, namespace, numberOfNodes) + l.Info().Str("Dashboard", formattedDashboardUrl).Msg("Dashboard URL") + + pyroscopeServer := os.Getenv(config.EnvVarPyroscopeServer) + pyroscopeEnvironment := os.Getenv(config.EnvVarPyroscopeEnvironment) + + formattedPyroscopeUrl := fmt.Sprintf("%s/?query=chainlink-node.cpu{Environment=\"%s\"}&from=%s&to=%s", pyroscopeServer, pyroscopeEnvironment, startingTime, endingTime) + + var notificationBlocks []slack.Block + + notificationBlocks = append(notificationBlocks, + slack.NewHeaderBlock(slack.NewTextBlockObject("plain_text", headerText, true, false))) + notificationBlocks = append(notificationBlocks, + slack.NewContextBlock("context_block", slack.NewTextBlockObject("plain_text", namespace, false, false))) + notificationBlocks = append(notificationBlocks, slack.NewDividerBlock()) + if pyroscopeServer != "" { + l.Info().Str("Pyroscope", formattedPyroscopeUrl).Msg("Dashboard URL") + notificationBlocks = append(notificationBlocks, slack.NewSectionBlock(slack.NewTextBlockObject("mrkdwn", + fmt.Sprintf("<%s|Pyroscope>", + formattedPyroscopeUrl), false, true), nil, nil)) + } + notificationBlocks = append(notificationBlocks, slack.NewSectionBlock(slack.NewTextBlockObject("mrkdwn", + fmt.Sprintf("<%s|Test Dashboard> \nNotifying <@%s>", + formattedDashboardUrl, reportModel.SlackUserID), false, true), nil, nil)) + + if len(extraBlocks) > 0 { + notificationBlocks = append(notificationBlocks, extraBlocks...) + } + + ts, err := reportModel.SendSlackMessage(slackClient, slack.MsgOptionBlocks(notificationBlocks...)) + l.Info().Str("ts", ts).Msg("Sent Slack Message") + return err +} diff --git a/integration-tests/testreporters/keeper_benchmark.go b/integration-tests/testreporters/keeper_benchmark.go index e9f2eaad7c5..5db6cca3ec4 100644 --- a/integration-tests/testreporters/keeper_benchmark.go +++ b/integration-tests/testreporters/keeper_benchmark.go @@ -133,7 +133,7 @@ func (k *KeeperBenchmarkTestReporter) WriteReport(folderLocation string) error { if err != nil { return err } - avg, median, ninetyPct, ninetyNinePct, max := intListStats(allDelays) + avg, median, ninetyPct, ninetyNinePct, max := IntListStats(allDelays) err = keeperReportWriter.Write([]string{ fmt.Sprint(totalEligibleCount), fmt.Sprint(totalPerformed), @@ -183,7 +183,7 @@ func (k *KeeperBenchmarkTestReporter) WriteReport(folderLocation string) error { } for contractIndex, report := range k.Reports { - avg, median, ninetyPct, ninetyNinePct, max = intListStats(report.AllCheckDelays) + avg, median, ninetyPct, ninetyNinePct, max = IntListStats(report.AllCheckDelays) err = keeperReportWriter.Write([]string{ fmt.Sprint(contractIndex), report.RegistryAddress, @@ -307,7 +307,7 @@ func (k *KeeperBenchmarkTestReporter) SendSlackNotification(t *testing.T, slackC // intListStats helper calculates some statistics on an int list: avg, median, 90pct, 99pct, max // //nolint:revive -func intListStats(in []int64) (float64, int64, int64, int64, int64) { +func IntListStats(in []int64) (float64, int64, int64, int64, int64) { length := len(in) if length == 0 { return 0, 0, 0, 0, 0