From 68e00b3abf566a222ff6775b0fb2575ff73f735e Mon Sep 17 00:00:00 2001 From: Matthew Pendrey Date: Tue, 4 Jun 2024 19:06:47 +0100 Subject: [PATCH 01/15] move the step output to a value type, fixes nil pointer exception bug in step persistence code (#13416) --- .changeset/kind-cobras-hope.md | 5 ++++ core/services/workflows/engine.go | 4 ++-- core/services/workflows/engine_test.go | 4 ++-- core/services/workflows/state.go | 2 +- core/services/workflows/state_test.go | 24 +++++++++---------- core/services/workflows/store/models.go | 2 +- core/services/workflows/store/store_db.go | 17 ++++--------- .../services/workflows/store/store_db_test.go | 4 ++-- 8 files changed, 29 insertions(+), 33 deletions(-) create mode 100644 .changeset/kind-cobras-hope.md diff --git a/.changeset/kind-cobras-hope.md b/.changeset/kind-cobras-hope.md new file mode 100644 index 00000000000..deb4f5aeea1 --- /dev/null +++ b/.changeset/kind-cobras-hope.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#internal fix for workflow step persistence diff --git a/core/services/workflows/engine.go b/core/services/workflows/engine.go index 6f616235632..2672ea3fb6f 100644 --- a/core/services/workflows/engine.go +++ b/core/services/workflows/engine.go @@ -413,7 +413,7 @@ func (e *Engine) startExecution(ctx context.Context, executionID string, event v ec := &store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ workflows.KeywordTrigger: { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: event, }, Status: store.StatusCompleted, @@ -562,7 +562,7 @@ func (e *Engine) workerForStepRequest(ctx context.Context, msg stepRequest) { l.Debugw("executing on a step event") stepState := &store.WorkflowExecutionStep{ - Outputs: &store.StepOutput{}, + Outputs: store.StepOutput{}, ExecutionID: msg.state.ExecutionID, Ref: msg.stepRef, } diff --git a/core/services/workflows/engine_test.go b/core/services/workflows/engine_test.go index 808e0b2555d..a25568953a8 100644 --- a/core/services/workflows/engine_test.go +++ b/core/services/workflows/engine_test.go @@ -544,7 +544,7 @@ func TestEngine_ResumesPendingExecutions(t *testing.T) { ec := &store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ workflows.KeywordTrigger: { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: resp, }, Status: store.StatusCompleted, @@ -599,7 +599,7 @@ func TestEngine_TimesOutOldExecutions(t *testing.T) { ec := &store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ workflows.KeywordTrigger: { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: resp, }, Status: store.StatusCompleted, diff --git a/core/services/workflows/state.go b/core/services/workflows/state.go index 218022eae36..6fc61af3954 100644 --- a/core/services/workflows/state.go +++ b/core/services/workflows/state.go @@ -29,7 +29,7 @@ func copyState(es store.WorkflowExecution) store.WorkflowExecution { Ref: step.Ref, Status: step.Status, - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Err: step.Outputs.Err, Value: copiedov, }, diff --git a/core/services/workflows/state_test.go b/core/services/workflows/state_test.go index ccd6cd5004d..a9829a97c74 100644 --- a/core/services/workflows/state_test.go +++ b/core/services/workflows/state_test.go @@ -38,7 +38,7 @@ func TestInterpolateKey(t *testing.T) { state: store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ "evm_median": { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: values.NewString(""), }, }, @@ -68,7 +68,7 @@ func TestInterpolateKey(t *testing.T) { state: store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ "evm_median": { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: values.NewString(""), }, }, @@ -82,7 +82,7 @@ func TestInterpolateKey(t *testing.T) { state: store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ "evm_median": { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Err: errors.New("catastrophic error"), }, }, @@ -96,7 +96,7 @@ func TestInterpolateKey(t *testing.T) { state: store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ "evm_median": { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: val, }, }, @@ -110,7 +110,7 @@ func TestInterpolateKey(t *testing.T) { state: store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ "evm_median": { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: val, }, }, @@ -124,7 +124,7 @@ func TestInterpolateKey(t *testing.T) { state: store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ "evm_median": { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: val, }, }, @@ -138,7 +138,7 @@ func TestInterpolateKey(t *testing.T) { state: store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ "evm_median": { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: val, }, }, @@ -152,7 +152,7 @@ func TestInterpolateKey(t *testing.T) { state: store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ "evm_median": { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: val, }, }, @@ -166,7 +166,7 @@ func TestInterpolateKey(t *testing.T) { state: store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ "evm_median": { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: val, }, }, @@ -180,7 +180,7 @@ func TestInterpolateKey(t *testing.T) { state: store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ "evm_median": { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: val, }, }, @@ -222,7 +222,7 @@ func TestInterpolateInputsFromState(t *testing.T) { state: store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ "evm_median": { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: values.NewString(""), }, }, @@ -242,7 +242,7 @@ func TestInterpolateInputsFromState(t *testing.T) { state: store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ "evm_median": { - Outputs: &store.StepOutput{ + Outputs: store.StepOutput{ Value: values.NewString(""), }, }, diff --git a/core/services/workflows/store/models.go b/core/services/workflows/store/models.go index 29a1df154de..27604543ede 100644 --- a/core/services/workflows/store/models.go +++ b/core/services/workflows/store/models.go @@ -24,7 +24,7 @@ type WorkflowExecutionStep struct { Status string Inputs *values.Map - Outputs *StepOutput + Outputs StepOutput UpdatedAt *time.Time } diff --git a/core/services/workflows/store/store_db.go b/core/services/workflows/store/store_db.go index 73acece5b18..e9204efd7b1 100644 --- a/core/services/workflows/store/store_db.go +++ b/core/services/workflows/store/store_db.go @@ -147,20 +147,15 @@ func stepToState(step workflowStepRow) (*WorkflowExecutionStep, error) { outputs = values.FromProto(vProto) } - var so *StepOutput - if outputErr != nil || outputs != nil { - so = &StepOutput{ - Err: outputErr, - Value: outputs, - } - } - return &WorkflowExecutionStep{ ExecutionID: step.WorkflowExecutionID, Ref: step.Ref, Status: step.Status, Inputs: inputs, - Outputs: so, + Outputs: StepOutput{ + Err: outputErr, + Value: outputs, + }, }, nil } @@ -182,10 +177,6 @@ func stateToStep(state *WorkflowExecutionStep) (workflowStepRow, error) { Inputs: inpb, } - if state.Outputs == nil { - return wsr, nil - } - if state.Outputs.Value != nil { p := values.Proto(state.Outputs.Value) ob, err := proto.Marshal(p) diff --git a/core/services/workflows/store/store_db_test.go b/core/services/workflows/store/store_db_test.go index e41f4857363..e30eda1bfc6 100644 --- a/core/services/workflows/store/store_db_test.go +++ b/core/services/workflows/store/store_db_test.go @@ -153,7 +153,7 @@ func Test_StoreDB_UpdateStep(t *testing.T) { require.NoError(t, err) stepOne.Inputs = nm - stepOne.Outputs = &StepOutput{Err: errors.New("some error")} + stepOne.Outputs = StepOutput{Err: errors.New("some error")} es, err = store.UpsertStep(tests.Context(t), stepOne) require.NoError(t, err) @@ -161,7 +161,7 @@ func Test_StoreDB_UpdateStep(t *testing.T) { gotStep := es.Steps[stepOne.Ref] assert.Equal(t, stepOne, gotStep) - stepTwo.Outputs = &StepOutput{Value: nm} + stepTwo.Outputs = StepOutput{Value: nm} es, err = store.UpsertStep(tests.Context(t), stepTwo) require.NoError(t, err) From 4bff2c1e63bdf795638905101a36a2d21e249b98 Mon Sep 17 00:00:00 2001 From: Tate Date: Tue, 4 Jun 2024 12:43:02 -0600 Subject: [PATCH 02/15] Make slack message urls repo agnostic (#13398) --- .github/workflows/ci-core.yml | 4 ++-- .github/workflows/integration-tests-publish.yml | 2 +- .github/workflows/integration-tests.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index be7145ab0e3..ee44c1bc46f 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -75,7 +75,7 @@ jobs: SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} with: channel-id: "#team-core" - slack-message: "golangci-lint failed: ${{ job.html_url }}\n${{ format('https://github.com/smartcontractkit/chainlink/actions/runs/{0}', github.run_id) }}" + slack-message: "golangci-lint failed: \n${{ format('https://github.com/{0}/actions/runs/{1}', github.repository, github.run_id) }}" core: env: @@ -209,7 +209,7 @@ jobs: SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} with: channel-id: "#topic-data-races" - slack-message: "Race tests failed: ${{ job.html_url }}\n${{ format('https://github.com/smartcontractkit/chainlink/actions/runs/{0}', github.run_id) }}" + slack-message: "Race tests failed: \n${{ format('https://github.com/{0}/actions/runs/{1}', github.repository, github.run_id) }}" - name: Collect Path Output id: collect-path-output env: diff --git a/.github/workflows/integration-tests-publish.yml b/.github/workflows/integration-tests-publish.yml index e730a404532..f3eac97ffb5 100644 --- a/.github/workflows/integration-tests-publish.yml +++ b/.github/workflows/integration-tests-publish.yml @@ -54,7 +54,7 @@ jobs: SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} with: channel-id: "#team-test-tooling-internal" - slack-message: ":x: :mild-panic-intensifies: Publish Integration Test Image failed: ${{ job.html_url }}\n${{ format('https://github.com/smartcontractkit/chainlink/actions/runs/{0}', github.run_id) }}" + slack-message: ":x: :mild-panic-intensifies: Publish Integration Test Image failed: \n${{ format('https://github.com/{0}/actions/runs/{1}', github.repository, github.run_id) }}" build-chainlink-image: environment: integration # Only run this build for workflow_dispatch diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 8e7df49650c..c0465791ae5 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -967,7 +967,7 @@ jobs: SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} with: channel-id: "#team-test-tooling-internal" - slack-message: ":x: :mild-panic-intensifies: Node Migration Tests Failed: ${{ job.html_url }}\n${{ format('https://github.com/smartcontractkit/chainlink/actions/runs/{0}', github.run_id) }}" + slack-message: ":x: :mild-panic-intensifies: Node Migration Tests Failed: \n${{ format('https://github.com/{0}/actions/runs/{1}', github.repository, github.run_id) }}" ## Solana Section get_solana_sha: From f68e2b3f969e19eae896bdcfa0196caac7acbae8 Mon Sep 17 00:00:00 2001 From: Tate Date: Tue, 4 Jun 2024 12:43:29 -0600 Subject: [PATCH 03/15] Remove skipped tests from ci pipeline (#13399) --- integration-tests/smoke/log_poller_test.go | 8 ++++---- integration-tests/smoke/log_poller_test.go_test_list.json | 6 ------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/integration-tests/smoke/log_poller_test.go b/integration-tests/smoke/log_poller_test.go index 4ddffab30a6..1f77b3a6250 100644 --- a/integration-tests/smoke/log_poller_test.go +++ b/integration-tests/smoke/log_poller_test.go @@ -46,13 +46,13 @@ func TestLogPollerFewFiltersFinalityTag(t *testing.T) { // consistency test with no network disruptions with approximate emission of 1000-1100 logs per second for ~110-120 seconds // 900 filters are registered -func TestLogPollerManyFiltersFixedDepth(t *testing.T) { - t.Skip("Execute manually, when needed as it runs for a long time") +func XTestLogPollerManyFiltersFixedDepth(t *testing.T) { + t.Skip("Execute manually, when needed as it runs for a long time, remove the X from the test name to run it") executeBasicLogPollerTest(t, test_env.DefaultChainlinkNodeLogScannerSettings) } -func TestLogPollerManyFiltersFinalityTag(t *testing.T) { - t.Skip("Execute manually, when needed as it runs for a long time") +func XTestLogPollerManyFiltersFinalityTag(t *testing.T) { + t.Skip("Execute manually, when needed as it runs for a long time, remove the X from the test name to run it") executeBasicLogPollerTest(t, test_env.DefaultChainlinkNodeLogScannerSettings) } diff --git a/integration-tests/smoke/log_poller_test.go_test_list.json b/integration-tests/smoke/log_poller_test.go_test_list.json index 96939c5133b..7d94617c544 100644 --- a/integration-tests/smoke/log_poller_test.go_test_list.json +++ b/integration-tests/smoke/log_poller_test.go_test_list.json @@ -23,12 +23,6 @@ }, { "name": "TestLogPollerReplayFinalityTag" - }, - { - "name": "TestLogPollerManyFiltersFixedDepth" - }, - { - "name": "TestLogPollerManyFiltersFinalityTag" } ] } \ No newline at end of file From 9a7a3488e93fe5f7699f4fdce9cb9e3f4ce4da16 Mon Sep 17 00:00:00 2001 From: Tate Date: Tue, 4 Jun 2024 12:49:28 -0600 Subject: [PATCH 04/15] [TT-1236] Concurrency Issue Triage (#13372) * Change concurrency group name to see if that fixes cancel issues in github * make concurrency group even more unique * log concurrency group * put sha in front in case truncation is happening don't cancel in merge groups * concurrency needs to be unique in the way that the same pr will only run one of this workflow at a time in the pull_request phase as well as be separate in the merge group phase without stepping on each other yet be able to step on themselves in their own respective phases * log more out to see more options * little more refining * cleanup --- .github/workflows/integration-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index c0465791ae5..d57ac9c26e2 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -27,9 +27,9 @@ on: type: string # Only run 1 of this workflow at a time per PR -# concurrency: -# group: integration-tests-chainlink-${{ github.sha }}-${{ github.run_id }}-${{ inputs.distinct_run_name }} -# cancel-in-progress: true +concurrency: + group: ${{ github.ref }}-${{ github.repository }}-${{ github.event_name }}--e2e-tests-${{ inputs.distinct_run_name }} + cancel-in-progress: true env: # for run-test variables and environment From 59c74da271d57736575b03c0e2985f2a162003ad Mon Sep 17 00:00:00 2001 From: Domino Valdano Date: Tue, 4 Jun 2024 18:40:40 -0700 Subject: [PATCH 05/15] Update TestSpawner_CreateJobDeleteJob (#13390) * Unregister filters for both Keepers 2.1 & 2.0 jobs Also: fix log msg typo * Replace ocr2vrf job with ocr2keeper job in TestSpawner_CreateJobDeleteJob test * Append filter names instead of overwriting --- core/services/job/helpers_test.go | 78 ++++++++++++------------------- core/services/job/spawner_test.go | 20 ++++---- core/services/ocr2/delegate.go | 9 +++- 3 files changed, 48 insertions(+), 59 deletions(-) diff --git a/core/services/job/helpers_test.go b/core/services/job/helpers_test.go index 7120fe4200c..d69056ae819 100644 --- a/core/services/job/helpers_test.go +++ b/core/services/job/helpers_test.go @@ -1,8 +1,6 @@ package job_test import ( - "crypto/rand" - "encoding/hex" "fmt" "math/big" "testing" @@ -10,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" + "github.com/hashicorp/consul/sdk/freeport" "github.com/lib/pq" "github.com/pelletier/go-toml" "github.com/stretchr/testify/require" @@ -49,31 +48,30 @@ observationSource = """ %s """ ` - ocr2vrfJobSpecTemplate = ` -type = "offchainreporting2" -schemaVersion = 1 -name = "ocr2 vrf spec" -maxTaskDuration = "10s" -contractID = "%s" -ocrKeyBundleID = "%s" -relay = "evm" -pluginType = "ocr2vrf" -transmitterID = "%s" -forwardingAllowed = %t + + ocr2Keeper21JobSpecTemplate = ` +type = "offchainreporting2" +pluginType = "ocr2automation" +relay = "evm" +name = "ocr2keeper" +schemaVersion = 1 +contractID = "%s" +contractConfigTrackerPollInterval = "15s" +ocrKeyBundleID = "%s" +transmitterID = "%s" +p2pv2Bootstrappers = [ +"%s" +] [relayConfig] -chainID = %d -fromBlock = %d -sendingKeys = [%s] +chainID = %d [pluginConfig] -dkgEncryptionPublicKey = "%s" -dkgSigningPublicKey = "%s" -dkgKeyID = "%s" -dkgContractAddress = "%s" - -vrfCoordinatorAddress = "%s" -linkEthFeedAddress = "%s" +maxServiceWorkers = 100 +cacheEvictionInterval = "1s" +mercuryCredentialName = "%s" +contractVersion = "v2.1" +useBufferV1 = %v ` voterTurnoutDataSourceTemplate = ` // data source 1 @@ -268,38 +266,20 @@ func makeOCRJobSpecFromToml(t *testing.T, jobSpecToml string) *job.Job { return &jb } -func makeOCR2VRFJobSpec(t testing.TB, ks keystore.Master, transmitter common.Address, chainID *big.Int, fromBlock uint64) *job.Job { +func makeOCR2Keeper21JobSpec(t testing.TB, ks keystore.Master, transmitter common.Address, chainID *big.Int) *job.Job { t.Helper() ctx := testutils.Context(t) - useForwarders := false - _, beacon := cltest.MustInsertRandomKey(t, ks.Eth()) - _, coordinator := cltest.MustInsertRandomKey(t, ks.Eth()) - _, feed := cltest.MustInsertRandomKey(t, ks.Eth()) - _, dkg := cltest.MustInsertRandomKey(t, ks.Eth()) - sendingKeys := fmt.Sprintf(`"%s"`, transmitter) + bootstrapNodePort := freeport.GetOne(t) + bootstrapPeerID := "peerId" + kb, _ := ks.OCR2().Create(ctx, chaintype.EVM) + _, registry := cltest.MustInsertRandomKey(t, ks.Eth()) - vrfKey := make([]byte, 32) - _, err := rand.Read(vrfKey) - require.NoError(t, err) + ocr2Keeper21Job := fmt.Sprintf(ocr2Keeper21JobSpecTemplate, registry.String(), kb.ID(), transmitter, + fmt.Sprintf("%s127.0.0.1:%d", bootstrapPeerID, bootstrapNodePort), chainID, "mercury cred", false) - ocr2vrfJob := fmt.Sprintf(ocr2vrfJobSpecTemplate, - beacon.String(), - kb.ID(), - "", - useForwarders, - chainID, - fromBlock, - sendingKeys, - ks.DKGEncrypt(), - ks.DKGSign(), - hex.EncodeToString(vrfKey[:]), - dkg.String(), - coordinator.String(), - feed.String(), - ) - jobSpec := makeOCR2JobSpecFromToml(t, ocr2vrfJob) + jobSpec := makeOCR2JobSpecFromToml(t, ocr2Keeper21Job) return jobSpec } diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index 8ed08a1cb8a..4abb81eda3a 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" + "github.com/smartcontractkit/chainlink/v2/core/capabilities" "github.com/smartcontractkit/chainlink/v2/core/bridges" @@ -291,7 +292,8 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { relayExtenders := evmtest.NewChainRelayExtenders(t, testopts) assert.Equal(t, relayExtenders.Len(), 1) legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - chain := evmtest.MustGetDefaultChain(t, legacyChains) + chain, err := legacyChains.Get("0") + require.NoError(t, err) evmRelayer, err := evmrelayer.NewRelayer(lggr, chain, evmrelayer.RelayerOpts{ DS: db, @@ -305,7 +307,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { r: evmRelayer, } - jobOCR2VRF := makeOCR2VRFJobSpec(t, keyStore, address, chain.ID(), 2) + jobOCR2Keeper := makeOCR2Keeper21JobSpec(t, keyStore, address, chain.ID()) orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db), keyStore) mailMon := servicetest.Run(t, mailboxtest.NewMonitor(t)) @@ -315,26 +317,26 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { d := ocr2.NewDelegate(nil, orm, nil, nil, nil, nil, nil, monitoringEndpoint, legacyChains, lggr, ocr2DelegateConfig, keyStore.OCR2(), keyStore.DKGSign(), keyStore.DKGEncrypt(), ethKeyStore, testRelayGetter, mailMon, capabilities.NewRegistry(lggr)) - delegateOCR2 := &delegate{jobOCR2VRF.Type, []job.ServiceCtx{}, 0, nil, d} + delegateOCR2 := &delegate{jobOCR2Keeper.Type, []job.ServiceCtx{}, 0, nil, d} spawner := job.NewSpawner(orm, config.Database(), noopChecker{}, map[job.Type]job.Delegate{ - jobOCR2VRF.Type: delegateOCR2, + jobOCR2Keeper.Type: delegateOCR2, }, lggr, nil) ctx := testutils.Context(t) - err = spawner.CreateJob(ctx, nil, jobOCR2VRF) + err = spawner.CreateJob(ctx, nil, jobOCR2Keeper) require.NoError(t, err) - jobSpecID := jobOCR2VRF.ID - delegateOCR2.jobID = jobOCR2VRF.ID + jobSpecID := jobOCR2Keeper.ID + delegateOCR2.jobID = jobOCR2Keeper.ID lp.On("UnregisterFilter", mock.Anything, mock.Anything).Return(nil).Run(func(args mock.Arguments) { - lggr.Debugf("Got here, with args %v", args) + lggr.Debugf("UnregisterFilter called with args %v", args) }) err = spawner.DeleteJob(ctx, nil, jobSpecID) require.NoError(t, err) - lp.AssertNumberOfCalls(t, "UnregisterFilter", 3) + lp.AssertNumberOfCalls(t, "UnregisterFilter", 6) lp.On("Close").Return(nil).Once() spawner.Close() diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 1c70195dd43..2c253d592c2 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -303,7 +303,7 @@ func (d *Delegate) cleanupEVM(ctx context.Context, jb job.Job, relayID types.Rel spec := jb.OCR2OracleSpec chain, err := d.legacyChains.Get(relayID.ChainID) if err != nil { - d.lggr.Error("cleanupEVM: failed to chain get chain %s", "err", relayID.ChainID, err) + d.lggr.Errorw("cleanupEVM: failed to get chain id", "chainId", relayID.ChainID, "err", err) return nil } lp := chain.LogPoller() @@ -316,10 +316,17 @@ func (d *Delegate) cleanupEVM(ctx context.Context, jb job.Job, relayID types.Rel d.lggr.Errorw("failed to derive ocr2vrf filter names from spec", "err", err, "spec", spec) } case types.OCR2Keeper: + // Not worth the effort to validate and parse the job spec config to figure out whether this is v2.0 or v2.1, + // simpler and faster to just Unregister them both filters, err = ocr2keeper.FilterNamesFromSpec20(spec) if err != nil { d.lggr.Errorw("failed to derive ocr2keeper filter names from spec", "err", err, "spec", spec) } + filters21, err2 := ocr2keeper.FilterNamesFromSpec21(spec) + if err2 != nil { + d.lggr.Errorw("failed to derive ocr2keeper filter names from spec", "err", err, "spec", spec) + } + filters = append(filters, filters21...) default: return nil } From f18e1d92ad935f7006fa8a60870160a3d09d291e Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Wed, 5 Jun 2024 10:51:18 +0200 Subject: [PATCH 06/15] fix docker env for live networks (#13409) --- integration-tests/docker/test_env/test_env_builder.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index 098b273d9bb..20a551cd905 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -521,6 +521,11 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { // Start Chainlink Nodes if b.clNodesCount > 0 { + // needed for live networks + if len(b.te.EVMNetworks) == 0 { + b.te.EVMNetworks = append(b.te.EVMNetworks, &networkConfig) + } + dereferrencedEvms := make([]blockchain.EVMNetwork, 0) for _, en := range b.te.EVMNetworks { network := *en @@ -559,7 +564,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { b.defaultNodeCsaKeys = nodeCsaKeys } - if len(b.privateEthereumNetworks) > 0 && b.clNodesCount > 0 && b.ETHFunds != nil { + if b.clNodesCount > 0 && b.ETHFunds != nil { if b.hasEVMClient { b.te.ParallelTransactions(true) defer b.te.ParallelTransactions(false) From 3959091d4f3925b64cb6b0b55b7f7c72a4f924b9 Mon Sep 17 00:00:00 2001 From: Bolek <1416262+bolekk@users.noreply.github.com> Date: Wed, 5 Jun 2024 08:09:30 -0700 Subject: [PATCH 07/15] [KS-241] Update metadata passed to Forwarder and Receiver (#13389) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [KS-241] Update metadata passed to Forwarder and Receiver * Update gethwrappers * Fix tests * Update gethwrappers --------- Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com> Co-authored-by: Blaž Hrastnik --- .changeset/twenty-zoos-do.md | 5 + contracts/.changeset/rich-crabs-smoke.md | 5 + .../v0.8/keystone/KeystoneFeedsConsumer.sol | 100 +++++++++++++++--- .../src/v0.8/keystone/KeystoneForwarder.sol | 91 +++++++++------- .../v0.8/keystone/interfaces/IReceiver.sol | 2 +- .../test/KeystoneForwarderBaseTest.t.sol | 1 + .../test/KeystoneForwarder_ReportTest.t.sol | 40 ++++--- .../src/v0.8/keystone/test/mocks/Receiver.sol | 6 +- core/capabilities/targets/write_target.go | 11 +- .../keystone/generated/forwarder/forwarder.go | 41 +++---- ...rapper-dependency-versions-do-not-edit.txt | 2 +- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/services/relay/evm/cap_encoder.go | 62 +++++++---- core/services/relay/evm/cap_encoder_test.go | 51 ++++----- core/services/relay/evm/write_target_test.go | 4 + core/services/workflows/engine.go | 2 +- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- integration-tests/load/go.mod | 2 +- integration-tests/load/go.sum | 4 +- 23 files changed, 290 insertions(+), 157 deletions(-) create mode 100644 .changeset/twenty-zoos-do.md create mode 100644 contracts/.changeset/rich-crabs-smoke.md diff --git a/.changeset/twenty-zoos-do.md b/.changeset/twenty-zoos-do.md new file mode 100644 index 00000000000..e8d999c4c7d --- /dev/null +++ b/.changeset/twenty-zoos-do.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#internal Update metadata passed to Forwarder and Receiver diff --git a/contracts/.changeset/rich-crabs-smoke.md b/contracts/.changeset/rich-crabs-smoke.md new file mode 100644 index 00000000000..fc33cf7d4e0 --- /dev/null +++ b/contracts/.changeset/rich-crabs-smoke.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +#internal Keystone Forwarder and Feeds Consumer diff --git a/contracts/src/v0.8/keystone/KeystoneFeedsConsumer.sol b/contracts/src/v0.8/keystone/KeystoneFeedsConsumer.sol index 36058d2b11a..1044c70d492 100644 --- a/contracts/src/v0.8/keystone/KeystoneFeedsConsumer.sol +++ b/contracts/src/v0.8/keystone/KeystoneFeedsConsumer.sol @@ -2,27 +2,101 @@ pragma solidity ^0.8.19; import {IReceiver} from "./interfaces/IReceiver.sol"; +import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; -contract KeystoneFeedsConsumer is IReceiver { - event MessageReceived(bytes32 indexed workflowId, address indexed workflowOwner, uint256 nReports); - event FeedReceived(bytes32 indexed feedId, uint256 price, uint64 timestamp); +contract KeystoneFeedsConsumer is IReceiver, ConfirmedOwner { + event FeedReceived(bytes32 indexed feedId, int192 price, uint32 timestamp); - constructor() {} + error UnauthorizedSender(address sender); + error UnauthorizedWorkflowOwner(address workflowOwner); + error UnauthorizedWorkflowName(bytes10 workflowName); - struct FeedReport { - bytes32 FeedID; - uint256 Price; - uint64 Timestamp; + constructor() ConfirmedOwner(msg.sender) {} + + struct ReceivedFeedReport { + bytes32 FeedId; + int192 Price; + uint32 Timestamp; + } + + struct StoredFeedReport { + int192 Price; + uint32 Timestamp; + } + + mapping(bytes32 feedId => StoredFeedReport feedReport) internal s_feedReports; + address[] internal s_allowedSendersList; + mapping(address => bool) internal s_allowedSenders; + address[] internal s_allowedWorkflowOwnersList; + mapping(address => bool) internal s_allowedWorkflowOwners; + bytes10[] internal s_allowedWorkflowNamesList; + mapping(bytes10 => bool) internal s_allowedWorkflowNames; + + function setConfig( + address[] calldata _allowedSendersList, + address[] calldata _allowedWorkflowOwnersList, + bytes10[] calldata _allowedWorkflowNamesList + ) external onlyOwner { + for (uint32 i = 0; i < s_allowedSendersList.length; i++) { + s_allowedSenders[s_allowedSendersList[i]] = false; + } + for (uint32 i = 0; i < _allowedSendersList.length; i++) { + s_allowedSenders[_allowedSendersList[i]] = true; + } + s_allowedSendersList = _allowedSendersList; + for (uint32 i = 0; i < s_allowedWorkflowOwnersList.length; i++) { + s_allowedWorkflowOwners[s_allowedWorkflowOwnersList[i]] = false; + } + for (uint32 i = 0; i < _allowedWorkflowOwnersList.length; i++) { + s_allowedWorkflowOwners[_allowedWorkflowOwnersList[i]] = true; + } + s_allowedWorkflowOwnersList = _allowedWorkflowOwnersList; + for (uint32 i = 0; i < s_allowedWorkflowNamesList.length; i++) { + s_allowedWorkflowNames[s_allowedWorkflowNamesList[i]] = false; + } + for (uint32 i = 0; i < _allowedWorkflowNamesList.length; i++) { + s_allowedWorkflowNames[_allowedWorkflowNamesList[i]] = true; + } + s_allowedWorkflowNamesList = _allowedWorkflowNamesList; } - function onReport(bytes32 workflowId, address workflowOwner, bytes calldata rawReport) external { - // TODO: validate sender and workflowOwner + function onReport(bytes calldata metadata, bytes calldata rawReport) external { + if (s_allowedSenders[msg.sender] == false) { + revert UnauthorizedSender(msg.sender); + } + + (bytes10 workflowName, address workflowOwner) = _getInfo(metadata); + if (s_allowedWorkflowNames[workflowName] == false) { + revert UnauthorizedWorkflowName(workflowName); + } + if (s_allowedWorkflowOwners[workflowOwner] == false) { + revert UnauthorizedWorkflowOwner(workflowOwner); + } - FeedReport[] memory feeds = abi.decode(rawReport, (FeedReport[])); + ReceivedFeedReport[] memory feeds = abi.decode(rawReport, (ReceivedFeedReport[])); for (uint32 i = 0; i < feeds.length; i++) { - emit FeedReceived(feeds[i].FeedID, feeds[i].Price, feeds[i].Timestamp); + s_feedReports[feeds[i].FeedId] = StoredFeedReport(feeds[i].Price, feeds[i].Timestamp); + emit FeedReceived(feeds[i].FeedId, feeds[i].Price, feeds[i].Timestamp); } + } + + // solhint-disable-next-line chainlink-solidity/explicit-returns + function _getInfo(bytes memory metadata) internal pure returns (bytes10 workflowName, address workflowOwner) { + // (first 32 bytes contain length of the byte array) + // workflow_cid // offset 32, size 32 + // workflow_name // offset 64, size 10 + // workflow_owner // offset 74, size 20 + // report_name // offset 94, size 2 + assembly { + // shift right by 22 bytes to get the actual value + workflowName := shr(mul(22, 8), mload(add(metadata, 64))) + // shift right by 12 bytes to get the actual value + workflowOwner := shr(mul(12, 8), mload(add(metadata, 74))) + } + } - emit MessageReceived(workflowId, workflowOwner, feeds.length); + function getPrice(bytes32 feedId) external view returns (int192, uint32) { + StoredFeedReport memory report = s_feedReports[feedId]; + return (report.Price, report.Timestamp); } } diff --git a/contracts/src/v0.8/keystone/KeystoneForwarder.sol b/contracts/src/v0.8/keystone/KeystoneForwarder.sol index 3bdfd10fe8a..a54c3686f0c 100644 --- a/contracts/src/v0.8/keystone/KeystoneForwarder.sol +++ b/contracts/src/v0.8/keystone/KeystoneForwarder.sol @@ -17,6 +17,9 @@ contract KeystoneForwarder is IForwarder, ConfirmedOwner, TypeAndVersionInterfac /// REPORT_METADATA_LENGTH, which is the minimum length of a report. error InvalidReport(); + /// @notice This error is returned when the metadata version is not supported. + error InvalidVersion(uint8 version); + /// @notice This error is thrown whenever trying to set a config with a fault /// tolerance of 0. error FaultToleranceMustBePositive(); @@ -58,9 +61,9 @@ contract KeystoneForwarder is IForwarder, ConfirmedOwner, TypeAndVersionInterfac /// @param signature The signature that was invalid error InvalidSignature(bytes signature); - /// @notice This error is thrown whenever a report has already been processed. - /// @param reportId The ID of the report that was already processed - error ReportAlreadyProcessed(bytes32 reportId); + /// @notice This error is thrown whenever a message has already been processed. + /// @param messageId The ID of the message that was already processed + error AlreadyProcessed(bytes32 messageId); bool internal s_reentrancyGuard; // guard against reentrancy @@ -83,22 +86,15 @@ contract KeystoneForwarder is IForwarder, ConfirmedOwner, TypeAndVersionInterfac /// @notice Emitted when a report is processed /// @param receiver The address of the receiver contract - /// @param workflowOwner The address of the workflow owner /// @param workflowExecutionId The ID of the workflow execution /// @param result The result of the attempted delivery. True if successful. - event ReportProcessed( - address indexed receiver, - address indexed workflowOwner, - bytes32 indexed workflowExecutionId, - bool result - ); + event ReportProcessed(address indexed receiver, bytes32 indexed workflowExecutionId, bool result); constructor() ConfirmedOwner(msg.sender) {} uint256 internal constant MAX_ORACLES = 31; - // 32 bytes for workflowId, 4 bytes for donId, 32 bytes for - // workflowExecutionId, 20 bytes for workflowOwner - uint256 internal constant REPORT_METADATA_LENGTH = 88; + uint256 internal constant METADATA_LENGTH = 109; + uint256 internal constant FORWARDER_METADATA_LENGTH = 45; uint256 internal constant SIGNATURE_LENGTH = 65; function setConfig(uint32 donId, uint8 f, address[] calldata signers) external onlyOwner { @@ -133,17 +129,19 @@ contract KeystoneForwarder is IForwarder, ConfirmedOwner, TypeAndVersionInterfac bytes calldata reportContext, bytes[] calldata signatures ) external nonReentrant { - if (rawReport.length < REPORT_METADATA_LENGTH) { + if (rawReport.length < METADATA_LENGTH) { revert InvalidReport(); } - (bytes32 workflowId, uint32 donId, bytes32 workflowExecutionId, address workflowOwner) = _getMetadata(rawReport); + (bytes32 workflowExecutionId, uint32 donId /* uint32 donConfigVersion */, , bytes2 reportId) = _getMetadata( + rawReport + ); // f can never be 0, so this means the config doesn't actually exist if (s_configs[donId].f == 0) revert InvalidDonId(donId); - bytes32 reportId = _reportId(receiverAddress, workflowExecutionId); - if (s_reports[reportId].transmitter != address(0)) revert ReportAlreadyProcessed(reportId); + bytes32 combinedId = _combinedId(receiverAddress, workflowExecutionId, reportId); + if (s_reports[combinedId].transmitter != address(0)) revert AlreadyProcessed(combinedId); if (s_configs[donId].f + 1 != signatures.length) revert InvalidSignatureCount(s_configs[donId].f + 1, signatures.length); @@ -169,28 +167,37 @@ contract KeystoneForwarder is IForwarder, ConfirmedOwner, TypeAndVersionInterfac } bool success; - try IReceiver(receiverAddress).onReport(workflowId, workflowOwner, rawReport[REPORT_METADATA_LENGTH:]) { + try + IReceiver(receiverAddress).onReport( + rawReport[FORWARDER_METADATA_LENGTH:METADATA_LENGTH], + rawReport[METADATA_LENGTH:] + ) + { success = true; } catch { // Do nothing, success is already false } - s_reports[reportId] = DeliveryStatus(msg.sender, success); - - emit ReportProcessed(receiverAddress, workflowOwner, workflowExecutionId, success); + s_reports[combinedId] = DeliveryStatus(msg.sender, success); + emit ReportProcessed(receiverAddress, workflowExecutionId, success); } - function _reportId(address receiver, bytes32 workflowExecutionId) internal pure returns (bytes32) { + function _combinedId(address receiver, bytes32 workflowExecutionId, bytes2 reportId) internal pure returns (bytes32) { // TODO: gas savings: could we just use a bytes key and avoid another keccak256 call - return keccak256(bytes.concat(bytes20(uint160(receiver)), workflowExecutionId)); + return keccak256(bytes.concat(bytes20(uint160(receiver)), workflowExecutionId, reportId)); } // get transmitter of a given report or 0x0 if it wasn't transmitted yet - function getTransmitter(address receiver, bytes32 workflowExecutionId) external view returns (address) { - bytes32 reportId = _reportId(receiver, workflowExecutionId); - return s_reports[reportId].transmitter; + function getTransmitter( + address receiver, + bytes32 workflowExecutionId, + bytes2 reportId + ) external view returns (address) { + bytes32 combinedId = _combinedId(receiver, workflowExecutionId, reportId); + return s_reports[combinedId].transmitter; } + // solhint-disable-next-line chainlink-solidity/explicit-returns function _splitSignature(bytes memory sig) internal pure returns (bytes32 r, bytes32 s, uint8 v) { if (sig.length != SIGNATURE_LENGTH) revert InvalidSignature(sig); @@ -213,20 +220,30 @@ contract KeystoneForwarder is IForwarder, ConfirmedOwner, TypeAndVersionInterfac } } + // solhint-disable-next-line chainlink-solidity/explicit-returns function _getMetadata( bytes memory rawReport - ) internal pure returns (bytes32 workflowId, uint32 donId, bytes32 workflowExecutionId, address workflowOwner) { + ) internal pure returns (bytes32 workflowExecutionId, uint32 donId, uint32 donConfigVersion, bytes2 reportId) { + // (first 32 bytes of memory contain length of the report) + // version // offset 32, size 1 + // workflow_execution_id // offset 33, size 32 + // timestamp // offset 65, size 4 + // don_id // offset 69, size 4 + // don_config_version, // offset 73, size 4 + // workflow_cid // offset 77, size 32 + // workflow_name // offset 109, size 10 + // workflow_owner // offset 119, size 20 + // report_name // offset 139, size 2 + if (uint8(rawReport[0]) != 1) { + revert InvalidVersion(uint8(rawReport[0])); + } assembly { - // skip first 32 bytes, contains length of the report - // first 32 bytes is the workflowId - workflowId := mload(add(rawReport, 32)) - // next 4 bytes is donId. We shift right by 28 bytes to get the actual value - donId := shr(mul(28, 8), mload(add(rawReport, 64))) - // next 32 bytes is the workflowExecutionId - workflowExecutionId := mload(add(rawReport, 68)) - // next 20 bytes is the workflowOwner. We shift right by 12 bytes to get - // the actual value - workflowOwner := shr(mul(12, 8), mload(add(rawReport, 100))) + workflowExecutionId := mload(add(rawReport, 33)) + // shift right by 28 bytes to get the actual value + donId := shr(mul(28, 8), mload(add(rawReport, 69))) + // shift right by 28 bytes to get the actual value + donConfigVersion := shr(mul(28, 8), mload(add(rawReport, 73))) + reportId := mload(add(rawReport, 139)) } } diff --git a/contracts/src/v0.8/keystone/interfaces/IReceiver.sol b/contracts/src/v0.8/keystone/interfaces/IReceiver.sol index 96a68ec97f2..f58c2da7ae1 100644 --- a/contracts/src/v0.8/keystone/interfaces/IReceiver.sol +++ b/contracts/src/v0.8/keystone/interfaces/IReceiver.sol @@ -3,5 +3,5 @@ pragma solidity ^0.8.19; /// @title IReceiver - receives keystone reports interface IReceiver { - function onReport(bytes32 workflowId, address workflowOwner, bytes calldata report) external; + function onReport(bytes calldata metadata, bytes calldata report) external; } diff --git a/contracts/src/v0.8/keystone/test/KeystoneForwarderBaseTest.t.sol b/contracts/src/v0.8/keystone/test/KeystoneForwarderBaseTest.t.sol index d2801abb5af..16b85f79fdb 100644 --- a/contracts/src/v0.8/keystone/test/KeystoneForwarderBaseTest.t.sol +++ b/contracts/src/v0.8/keystone/test/KeystoneForwarderBaseTest.t.sol @@ -11,6 +11,7 @@ contract BaseTest is Test { uint256 internal constant MAX_ORACLES = 31; uint32 internal DON_ID = 0x01020304; uint8 internal F = 1; + uint32 internal CONFIG_VERSION = 1; struct Signer { uint256 mockPrivateKey; diff --git a/contracts/src/v0.8/keystone/test/KeystoneForwarder_ReportTest.t.sol b/contracts/src/v0.8/keystone/test/KeystoneForwarder_ReportTest.t.sol index bb209e4cf98..236851fde14 100644 --- a/contracts/src/v0.8/keystone/test/KeystoneForwarder_ReportTest.t.sol +++ b/contracts/src/v0.8/keystone/test/KeystoneForwarder_ReportTest.t.sol @@ -5,19 +5,20 @@ import {BaseTest} from "./KeystoneForwarderBaseTest.t.sol"; import {KeystoneForwarder} from "../KeystoneForwarder.sol"; contract KeystoneForwarder_ReportTest is BaseTest { - event MessageReceived(bytes32 indexed workflowId, address indexed workflowOwner, bytes[] mercuryReports); - event ReportProcessed( - address indexed receiver, - address indexed workflowOwner, - bytes32 indexed workflowExecutionId, - bool result - ); + event MessageReceived(bytes metadata, bytes[] mercuryReports); + event ReportProcessed(address indexed receiver, bytes32 indexed workflowExecutionId, bool result); + uint8 internal version = 1; + uint32 internal timestamp = 0; bytes32 internal workflowId = hex"6d795f6964000000000000000000000000000000000000000000000000000000"; + bytes10 internal workflowName = hex"000000000000DEADBEEF"; address internal workflowOwner = address(51); bytes32 internal executionId = hex"6d795f657865637574696f6e5f69640000000000000000000000000000000000"; + bytes2 internal reportId = hex"0001"; bytes[] internal mercuryReports = new bytes[](2); bytes internal rawReports; + bytes internal header; + bytes internal metadata; bytes internal report; bytes internal reportContext = new bytes(96); uint256 internal requiredSignaturesNum = F + 1; @@ -32,7 +33,9 @@ contract KeystoneForwarder_ReportTest is BaseTest { mercuryReports[1] = hex"aabbccdd"; rawReports = abi.encode(mercuryReports); - report = abi.encodePacked(workflowId, DON_ID, executionId, workflowOwner, rawReports); + metadata = abi.encodePacked(workflowId, workflowName, workflowOwner, reportId); + header = abi.encodePacked(version, executionId, timestamp, DON_ID, CONFIG_VERSION, metadata); + report = abi.encodePacked(header, rawReports); for (uint256 i = 0; i < requiredSignaturesNum; i++) { (uint8 v, bytes32 r, bytes32 s) = vm.sign( @@ -48,10 +51,15 @@ contract KeystoneForwarder_ReportTest is BaseTest { function test_RevertWhen_ReportHasIncorrectDON() public { uint32 invalidDONId = 111; bytes memory reportWithInvalidDONId = abi.encodePacked( - workflowId, - invalidDONId, + version, executionId, + timestamp, + invalidDONId, + CONFIG_VERSION, + workflowId, + workflowName, workflowOwner, + reportId, rawReports ); @@ -112,11 +120,11 @@ contract KeystoneForwarder_ReportTest is BaseTest { s_forwarder.report(address(s_receiver), report, reportContext, signatures); } - function test_RevertWhen_ReportAlreadyProcessed() public { + function test_RevertWhen_AlreadyProcessed() public { s_forwarder.report(address(s_receiver), report, reportContext, signatures); - bytes32 reportId = keccak256(bytes.concat(bytes20(uint160(address(s_receiver))), executionId)); + bytes32 combinedId = keccak256(bytes.concat(bytes20(uint160(address(s_receiver))), executionId, reportId)); - vm.expectRevert(abi.encodeWithSelector(KeystoneForwarder.ReportAlreadyProcessed.selector, reportId)); + vm.expectRevert(abi.encodeWithSelector(KeystoneForwarder.AlreadyProcessed.selector, combinedId)); s_forwarder.report(address(s_receiver), report, reportContext, signatures); } @@ -125,15 +133,15 @@ contract KeystoneForwarder_ReportTest is BaseTest { // bytes memory report = hex"6d795f6964000000000000000000000000000000000000000000000000000000010203046d795f657865637574696f6e5f696400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000301020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004aabbccdd00000000000000000000000000000000000000000000000000000000"; vm.expectEmit(address(s_receiver)); - emit MessageReceived(workflowId, workflowOwner, mercuryReports); + emit MessageReceived(metadata, mercuryReports); vm.expectEmit(address(s_forwarder)); - emit ReportProcessed(address(s_receiver), workflowOwner, executionId, true); + emit ReportProcessed(address(s_receiver), executionId, true); s_forwarder.report(address(s_receiver), report, reportContext, signatures); // validate transmitter was recorded - address transmitter = s_forwarder.getTransmitter(address(s_receiver), executionId); + address transmitter = s_forwarder.getTransmitter(address(s_receiver), executionId, reportId); assertEq(transmitter, TRANSMITTER, "transmitter mismatch"); } } diff --git a/contracts/src/v0.8/keystone/test/mocks/Receiver.sol b/contracts/src/v0.8/keystone/test/mocks/Receiver.sol index 5d2f646445b..25e8755641b 100644 --- a/contracts/src/v0.8/keystone/test/mocks/Receiver.sol +++ b/contracts/src/v0.8/keystone/test/mocks/Receiver.sol @@ -4,13 +4,13 @@ pragma solidity ^0.8.19; import {IReceiver} from "../../interfaces/IReceiver.sol"; contract Receiver is IReceiver { - event MessageReceived(bytes32 indexed workflowId, address indexed workflowOwner, bytes[] mercuryReports); + event MessageReceived(bytes metadata, bytes[] mercuryReports); constructor() {} - function onReport(bytes32 workflowId, address workflowOwner, bytes calldata rawReport) external { + function onReport(bytes calldata metadata, bytes calldata rawReport) external { // parse actual report bytes[] memory mercuryReports = abi.decode(rawReport, (bytes[])); - emit MessageReceived(workflowId, workflowOwner, mercuryReports); + emit MessageReceived(metadata, mercuryReports); } } diff --git a/core/capabilities/targets/write_target.go b/core/capabilities/targets/write_target.go index be5c779bf05..9eea8451fd9 100644 --- a/core/capabilities/targets/write_target.go +++ b/core/capabilities/targets/write_target.go @@ -10,6 +10,7 @@ import ( "github.com/google/uuid" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" + "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/types" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/values" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -19,6 +20,7 @@ var ( _ capabilities.ActionCapability = &WriteTarget{} ) +// required field of target's config in the workflow spec const signedReportField = "signed_report" type WriteTarget struct { @@ -84,11 +86,7 @@ func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.Capabi return nil, fmt.Errorf("missing required field %s", signedReportField) } - var inputs struct { - Report []byte - Context []byte - Signatures [][]byte - } + inputs := types.SignedReport{} if err = signedReport.UnwrapTo(&inputs); err != nil { return nil, err } @@ -110,9 +108,11 @@ func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.Capabi queryInputs := struct { Receiver string WorkflowExecutionID []byte + ReportId []byte }{ Receiver: reqConfig.Address, WorkflowExecutionID: rawExecutionID, + ReportId: inputs.ID, } var transmitter common.Address if err = cap.cr.GetLatestValue(ctx, "forwarder", "getTransmitter", queryInputs, &transmitter); err != nil { @@ -149,6 +149,7 @@ func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.Capabi if req.Signatures == nil { req.Signatures = make([][]byte, 0) } + cap.lggr.Debugw("Transaction raw report", "report", hex.EncodeToString(req.RawReport)) meta := commontypes.TxMeta{WorkflowExecutionID: &request.Metadata.WorkflowExecutionID} value := big.NewInt(0) diff --git a/core/gethwrappers/keystone/generated/forwarder/forwarder.go b/core/gethwrappers/keystone/generated/forwarder/forwarder.go index 951ef047364..6fd963d7886 100644 --- a/core/gethwrappers/keystone/generated/forwarder/forwarder.go +++ b/core/gethwrappers/keystone/generated/forwarder/forwarder.go @@ -31,8 +31,8 @@ var ( ) var KeystoneForwarderMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxSigners\",\"type\":\"uint256\"}],\"name\":\"ExcessSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FaultToleranceMustBePositive\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSigners\",\"type\":\"uint256\"}],\"name\":\"InsufficientSigners\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"InvalidDonId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"received\",\"type\":\"uint256\"}],\"name\":\"InvalidSignatureCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"reportId\",\"type\":\"bytes32\"}],\"name\":\"ReportAlreadyProcessed\",\"type\":\"error\"},{\"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\":\"receiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"workflowOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"result\",\"type\":\"bool\"}],\"name\":\"ReportProcessed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"}],\"name\":\"getTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiverAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"reportContext\",\"type\":\"bytes\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"setConfig\",\"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\":\"pure\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61152c806101576000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c8063390d0b151161005b578063390d0b15146100f257806379ba50971461012a5780638da5cb5b14610132578063f2fde38b1461015057600080fd5b80631128956514610082578063134a46f014610097578063181f5a77146100aa575b600080fd5b610095610090366004611128565b610163565b005b6100956100a53660046111d3565b6108b5565b604080518082018252601781527f4b657973746f6e65466f7277617264657220312e302e30000000000000000000602082015290516100e991906112ab565b60405180910390f35b6101056101003660046112c5565b610c16565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100e9565b610095610ca7565b60005473ffffffffffffffffffffffffffffffffffffffff16610105565b61009561015e3660046112ef565b610da4565b60015474010000000000000000000000000000000000000000900460ff16156101b8576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790556058851015610232576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806000806102778a8a8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610db892505050565b63ffffffff8316600090815260026020526040812054949850929650909450925060ff90911690036102e2576040517fea1b312900000000000000000000000000000000000000000000000000000000815263ffffffff841660048201526024015b60405180910390fd5b604080517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608e901b1660208083019190915260348083018690528351808403909101815260549092018352815191810191909120600081815260039092529190205473ffffffffffffffffffffffffffffffffffffffff1615610396576040517f1aac3d29000000000000000000000000000000000000000000000000000000008152600481018290526024016102d9565b63ffffffff841660009081526002602052604090205486906103bc9060ff166001611339565b60ff16146104275763ffffffff84166000908152600260205260409020546103e89060ff166001611339565b6040517fd6022e8e00000000000000000000000000000000000000000000000000000000815260ff9091166004820152602481018790526044016102d9565b60008b8b604051610439929190611352565b604051908190038120610452918c908c90602001611362565b604051602081830303815290604052805190602001209050610472610fb5565b6000805b898110156106e05760008060006104e48e8e868181106104985761049861137c565b90506020028101906104aa91906113ab565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610ddd92505050565b9194509250905060006001886104fb84601b611339565b6040805160008152602081018083529390935260ff90911690820152606081018690526080810185905260a0016020604051602081039080840390855afa15801561054a573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015163ffffffff8f1660009081526002602081815284832073ffffffffffffffffffffffffffffffffffffffff851684529091019052918220549850925060ff881690039050610605576040517fbf18af4300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016102d9565b610610600187611410565b955060008760ff8816601f81106106295761062961137c565b602002015173ffffffffffffffffffffffffffffffffffffffff1614610693576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016102d9565b80878760ff16601f81106106a9576106a961137c565b73ffffffffffffffffffffffffffffffffffffffff9092166020929092020152506106d992508391506114299050565b9050610476565b5050505060008c73ffffffffffffffffffffffffffffffffffffffff1663ff5a027087858f8f605890809261071793929190611461565b6040518563ffffffff1660e01b8152600401610736949392919061148b565b600060405180830381600087803b15801561075057600080fd5b505af1925050508015610761575060015b1561076a575060015b60405180604001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020018215158152506003600084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160000160146101000a81548160ff021916908315150217905550905050838373ffffffffffffffffffffffffffffffffffffffff168e73ffffffffffffffffffffffffffffffffffffffff167fdae8e752043eb5fc7e4a6eced57ceaf159548b630125ece9ffc41cfc952c208184604051610876911515815260200190565b60405180910390a45050600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690555050505050505050505050565b6108bd610e3d565b8260ff166000036108fa576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f81111561093f576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101829052601f60248201526044016102d9565b61094a8360036114fc565b60ff1681116109a8578061095f8460036114fc565b61096a906001611339565b6040517f9dd9e6d8000000000000000000000000000000000000000000000000000000008152600481019290925260ff1660248201526044016102d9565b60005b63ffffffff8516600090815260026020526040902060010154811015610a495763ffffffff851660009081526002602052604081206001018054839081106109f5576109f561137c565b600091825260208083209091015463ffffffff891683526002808352604080852073ffffffffffffffffffffffffffffffffffffffff9093168552910190915281205550610a4281611429565b90506109ab565b5063ffffffff84166000908152600260205260409020610a6d906001018383610fd4565b5060005b81811015610bcb576000838383818110610a8d57610a8d61137c565b9050602002016020810190610aa291906112ef565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff86168552909201905290205490915015610b2d576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016102d9565b610b38826001611339565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff909616808552868401835290842060ff959095169094559081526001938401805494850181558252902090910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055610bc481611429565b9050610a71565b50505063ffffffff91909116600090815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055565b600080610c7884846040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b1660208201526034810182905260009060540160405160208183030381529060405280519060200120905092915050565b60009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff169150505b92915050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d28576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016102d9565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610dac610e3d565b610db581610ec0565b50565b602081015160408201516044830151606490930151919360e09190911c929160601c90565b60008060006041845114610e1f57836040517f2adfdc300000000000000000000000000000000000000000000000000000000081526004016102d991906112ab565b50505060208101516040820151606090920151909260009190911a90565b60005473ffffffffffffffffffffffffffffffffffffffff163314610ebe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016102d9565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610f3f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016102d9565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b604051806103e00160405280601f906020820280368337509192915050565b82805482825590600052602060002090810192821561104c579160200282015b8281111561104c5781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190610ff4565b5061105892915061105c565b5090565b5b80821115611058576000815560010161105d565b803573ffffffffffffffffffffffffffffffffffffffff8116811461109557600080fd5b919050565b60008083601f8401126110ac57600080fd5b50813567ffffffffffffffff8111156110c457600080fd5b6020830191508360208285010111156110dc57600080fd5b9250929050565b60008083601f8401126110f557600080fd5b50813567ffffffffffffffff81111561110d57600080fd5b6020830191508360208260051b85010111156110dc57600080fd5b60008060008060008060006080888a03121561114357600080fd5b61114c88611071565b9650602088013567ffffffffffffffff8082111561116957600080fd5b6111758b838c0161109a565b909850965060408a013591508082111561118e57600080fd5b61119a8b838c0161109a565b909650945060608a01359150808211156111b357600080fd5b506111c08a828b016110e3565b989b979a50959850939692959293505050565b600080600080606085870312156111e957600080fd5b843563ffffffff811681146111fd57600080fd5b9350602085013560ff8116811461121357600080fd5b9250604085013567ffffffffffffffff81111561122f57600080fd5b61123b878288016110e3565b95989497509550505050565b6000815180845260005b8181101561126d57602081850181015186830182015201611251565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006112be6020830184611247565b9392505050565b600080604083850312156112d857600080fd5b6112e183611071565b946020939093013593505050565b60006020828403121561130157600080fd5b6112be82611071565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff8181168382160190811115610ca157610ca161130a565b8183823760009101908152919050565b838152818360208301376000910160200190815292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126113e057600080fd5b83018035915067ffffffffffffffff8211156113fb57600080fd5b6020019150368190038213156110dc57600080fd5b60ff8281168282160390811115610ca157610ca161130a565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361145a5761145a61130a565b5060010190565b6000808585111561147157600080fd5b8386111561147e57600080fd5b5050820193919092039150565b84815273ffffffffffffffffffffffffffffffffffffffff8416602082015260606040820152816060820152818360808301376000818301608090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01601019392505050565b60ff81811683821602908116908181146115185761151861130a565b509291505056fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"}],\"name\":\"AlreadyProcessed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxSigners\",\"type\":\"uint256\"}],\"name\":\"ExcessSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FaultToleranceMustBePositive\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSigners\",\"type\":\"uint256\"}],\"name\":\"InsufficientSigners\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"InvalidDonId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"received\",\"type\":\"uint256\"}],\"name\":\"InvalidSignatureCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"InvalidVersion\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"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\":\"receiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"result\",\"type\":\"bool\"}],\"name\":\"ReportProcessed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes2\",\"name\":\"reportId\",\"type\":\"bytes2\"}],\"name\":\"getTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiverAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"reportContext\",\"type\":\"bytes\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"setConfig\",\"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\":\"pure\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6115ec806101576000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806379ba50971161005b57806379ba5097146100f25780638864b864146100fa5780638da5cb5b146101d3578063f2fde38b146101f157600080fd5b80631128956514610082578063134a46f014610097578063181f5a77146100aa575b600080fd5b61009561009036600461119d565b610204565b005b6100956100a5366004611248565b61093e565b604080518082018252601781527f4b657973746f6e65466f7277617264657220312e302e30000000000000000000602082015290516100e99190611320565b60405180910390f35b610095610c9f565b6101ae61010836600461133a565b6040805160609490941b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660208086019190915260348501939093527fffff000000000000000000000000000000000000000000000000000000000000919091166054840152805160368185030181526056909301815282519282019290922060009081526003909152205473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100e9565b60005473ffffffffffffffffffffffffffffffffffffffff166101ae565b6100956101ff36600461139f565b610d9c565b60015474010000000000000000000000000000000000000000900460ff1615610259576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055606d8510156102d3576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600061031789898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610db092505050565b63ffffffff831660009081526002602052604081205494975092955093505060ff9091169003610380576040517fea1b312900000000000000000000000000000000000000000000000000000000815263ffffffff831660048201526024015b60405180910390fd5b604080517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608d901b16602080830191909152603482018690527fffff000000000000000000000000000000000000000000000000000000000000841660548301528251808303603601815260569092018352815191810191909120600081815260039092529190205473ffffffffffffffffffffffffffffffffffffffff161561045b576040517f1a20d3e600000000000000000000000000000000000000000000000000000000815260048101829052602401610377565b63ffffffff831660009081526002602052604090205485906104819060ff1660016113e9565b60ff16146104ec5763ffffffff83166000908152600260205260409020546104ad9060ff1660016113e9565b6040517fd6022e8e00000000000000000000000000000000000000000000000000000000815260ff909116600482015260248101869052604401610377565b60008a8a6040516104fe929190611408565b604051908190038120610517918b908b90602001611418565b60405160208183030381529060405280519060200120905061053761102a565b6000805b888110156107a55760008060006105a98d8d8681811061055d5761055d611432565b905060200281019061056f9190611461565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e5292505050565b9194509250905060006001886105c084601b6113e9565b6040805160008152602081018083529390935260ff90911690820152606081018690526080810185905260a0016020604051602081039080840390855afa15801561060f573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015163ffffffff8e1660009081526002602081815284832073ffffffffffffffffffffffffffffffffffffffff851684529091019052918220549850925060ff8816900390506106ca576040517fbf18af4300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610377565b6106d56001876114c6565b955060008760ff8816601f81106106ee576106ee611432565b602002015173ffffffffffffffffffffffffffffffffffffffff1614610758576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610377565b80878760ff16601f811061076e5761076e611432565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201525061079e92508391506114df9050565b905061053b565b5050505060008b73ffffffffffffffffffffffffffffffffffffffff1663805f21328c8c602d90606d926107db93929190611517565b8e8e606d9080926107ee93929190611517565b6040518563ffffffff1660e01b815260040161080d949392919061158a565b600060405180830381600087803b15801561082757600080fd5b505af1925050508015610838575060015b15610841575060015b604080518082018252338152821515602080830191825260008681526003909152839020915182549151151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00000000000000000000000000000000000000000090921673ffffffffffffffffffffffffffffffffffffffff9182161791909117909155905186918e16907fbe015fd2fd7c1a00158e111095c794ae7030eb413d2a0990e5b78d3114df1d499061090090851515815260200190565b60405180910390a35050600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16905550505050505050505050565b610946610eb2565b8260ff16600003610983576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f8111156109c8576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101829052601f6024820152604401610377565b6109d38360036115bc565b60ff168111610a3157806109e88460036115bc565b6109f39060016113e9565b6040517f9dd9e6d8000000000000000000000000000000000000000000000000000000008152600481019290925260ff166024820152604401610377565b60005b63ffffffff8516600090815260026020526040902060010154811015610ad25763ffffffff85166000908152600260205260408120600101805483908110610a7e57610a7e611432565b600091825260208083209091015463ffffffff891683526002808352604080852073ffffffffffffffffffffffffffffffffffffffff9093168552910190915281205550610acb816114df565b9050610a34565b5063ffffffff84166000908152600260205260409020610af6906001018383611049565b5060005b81811015610c54576000838383818110610b1657610b16611432565b9050602002016020810190610b2b919061139f565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff86168552909201905290205490915015610bb6576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610377565b610bc18260016113e9565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff909616808552868401835290842060ff959095169094559081526001938401805494850181558252902090910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055610c4d816114df565b9050610afa565b50505063ffffffff91909116600090815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d20576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610377565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610da4610eb2565b610dad81610f35565b50565b60008060008084600081518110610dc957610dc9611432565b60209101015160f81c600114610e2a5784600081518110610dec57610dec611432565b01602001516040517f7207be2000000000000000000000000000000000000000000000000000000000815260f89190911c6004820152602401610377565b50505050602181015160458201516049830151608b90930151919360e091821c9390911c9190565b60008060006041845114610e9457836040517f2adfdc300000000000000000000000000000000000000000000000000000000081526004016103779190611320565b50505060208101516040820151606090920151909260009190911a90565b60005473ffffffffffffffffffffffffffffffffffffffff163314610f33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610377565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610fb4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610377565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b604051806103e00160405280601f906020820280368337509192915050565b8280548282559060005260206000209081019282156110c1579160200282015b828111156110c15781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190611069565b506110cd9291506110d1565b5090565b5b808211156110cd57600081556001016110d2565b803573ffffffffffffffffffffffffffffffffffffffff8116811461110a57600080fd5b919050565b60008083601f84011261112157600080fd5b50813567ffffffffffffffff81111561113957600080fd5b60208301915083602082850101111561115157600080fd5b9250929050565b60008083601f84011261116a57600080fd5b50813567ffffffffffffffff81111561118257600080fd5b6020830191508360208260051b850101111561115157600080fd5b60008060008060008060006080888a0312156111b857600080fd5b6111c1886110e6565b9650602088013567ffffffffffffffff808211156111de57600080fd5b6111ea8b838c0161110f565b909850965060408a013591508082111561120357600080fd5b61120f8b838c0161110f565b909650945060608a013591508082111561122857600080fd5b506112358a828b01611158565b989b979a50959850939692959293505050565b6000806000806060858703121561125e57600080fd5b843563ffffffff8116811461127257600080fd5b9350602085013560ff8116811461128857600080fd5b9250604085013567ffffffffffffffff8111156112a457600080fd5b6112b087828801611158565b95989497509550505050565b6000815180845260005b818110156112e2576020818501810151868301820152016112c6565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b60208152600061133360208301846112bc565b9392505050565b60008060006060848603121561134f57600080fd5b611358846110e6565b92506020840135915060408401357fffff0000000000000000000000000000000000000000000000000000000000008116811461139457600080fd5b809150509250925092565b6000602082840312156113b157600080fd5b611333826110e6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff8181168382160190811115611402576114026113ba565b92915050565b8183823760009101908152919050565b838152818360208301376000910160200190815292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261149657600080fd5b83018035915067ffffffffffffffff8211156114b157600080fd5b60200191503681900382131561115157600080fd5b60ff8281168282160390811115611402576114026113ba565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611510576115106113ba565b5060010190565b6000808585111561152757600080fd5b8386111561153457600080fd5b5050820193919092039150565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60408152600061159e604083018688611541565b82810360208401526115b1818587611541565b979650505050505050565b60ff81811683821602908116908181146115d8576115d86113ba565b509291505056fea164736f6c6343000813000a", } var KeystoneForwarderABI = KeystoneForwarderMetaData.ABI @@ -171,9 +171,9 @@ func (_KeystoneForwarder *KeystoneForwarderTransactorRaw) Transact(opts *bind.Tr return _KeystoneForwarder.Contract.contract.Transact(opts, method, params...) } -func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) { +func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) { var out []interface{} - err := _KeystoneForwarder.contract.Call(opts, &out, "getTransmitter", receiver, workflowExecutionId) + err := _KeystoneForwarder.contract.Call(opts, &out, "getTransmitter", receiver, workflowExecutionId, reportId) if err != nil { return *new(common.Address), err @@ -185,12 +185,12 @@ func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmitter(opts *bind.Cal } -func (_KeystoneForwarder *KeystoneForwarderSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) { - return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId) +func (_KeystoneForwarder *KeystoneForwarderSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) { + return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId, reportId) } -func (_KeystoneForwarder *KeystoneForwarderCallerSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) { - return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId) +func (_KeystoneForwarder *KeystoneForwarderCallerSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) { + return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId, reportId) } func (_KeystoneForwarder *KeystoneForwarderCaller) Owner(opts *bind.CallOpts) (common.Address, error) { @@ -619,50 +619,41 @@ func (it *KeystoneForwarderReportProcessedIterator) Close() error { type KeystoneForwarderReportProcessed struct { Receiver common.Address - WorkflowOwner common.Address WorkflowExecutionId [32]byte Result bool Raw types.Log } -func (_KeystoneForwarder *KeystoneForwarderFilterer) FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) { +func (_KeystoneForwarder *KeystoneForwarderFilterer) FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) { var receiverRule []interface{} for _, receiverItem := range receiver { receiverRule = append(receiverRule, receiverItem) } - var workflowOwnerRule []interface{} - for _, workflowOwnerItem := range workflowOwner { - workflowOwnerRule = append(workflowOwnerRule, workflowOwnerItem) - } var workflowExecutionIdRule []interface{} for _, workflowExecutionIdItem := range workflowExecutionId { workflowExecutionIdRule = append(workflowExecutionIdRule, workflowExecutionIdItem) } - logs, sub, err := _KeystoneForwarder.contract.FilterLogs(opts, "ReportProcessed", receiverRule, workflowOwnerRule, workflowExecutionIdRule) + logs, sub, err := _KeystoneForwarder.contract.FilterLogs(opts, "ReportProcessed", receiverRule, workflowExecutionIdRule) if err != nil { return nil, err } return &KeystoneForwarderReportProcessedIterator{contract: _KeystoneForwarder.contract, event: "ReportProcessed", logs: logs, sub: sub}, nil } -func (_KeystoneForwarder *KeystoneForwarderFilterer) WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) { +func (_KeystoneForwarder *KeystoneForwarderFilterer) WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) { var receiverRule []interface{} for _, receiverItem := range receiver { receiverRule = append(receiverRule, receiverItem) } - var workflowOwnerRule []interface{} - for _, workflowOwnerItem := range workflowOwner { - workflowOwnerRule = append(workflowOwnerRule, workflowOwnerItem) - } var workflowExecutionIdRule []interface{} for _, workflowExecutionIdItem := range workflowExecutionId { workflowExecutionIdRule = append(workflowExecutionIdRule, workflowExecutionIdItem) } - logs, sub, err := _KeystoneForwarder.contract.WatchLogs(opts, "ReportProcessed", receiverRule, workflowOwnerRule, workflowExecutionIdRule) + logs, sub, err := _KeystoneForwarder.contract.WatchLogs(opts, "ReportProcessed", receiverRule, workflowExecutionIdRule) if err != nil { return nil, err } @@ -726,7 +717,7 @@ func (KeystoneForwarderOwnershipTransferred) Topic() common.Hash { } func (KeystoneForwarderReportProcessed) Topic() common.Hash { - return common.HexToHash("0xdae8e752043eb5fc7e4a6eced57ceaf159548b630125ece9ffc41cfc952c2081") + return common.HexToHash("0xbe015fd2fd7c1a00158e111095c794ae7030eb413d2a0990e5b78d3114df1d49") } func (_KeystoneForwarder *KeystoneForwarder) Address() common.Address { @@ -734,7 +725,7 @@ func (_KeystoneForwarder *KeystoneForwarder) Address() common.Address { } type KeystoneForwarderInterface interface { - GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) + GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) Owner(opts *bind.CallOpts) (common.Address, error) @@ -760,9 +751,9 @@ type KeystoneForwarderInterface interface { ParseOwnershipTransferred(log types.Log) (*KeystoneForwarderOwnershipTransferred, error) - FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) + FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) - WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) + WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) ParseReportProcessed(log types.Log) (*KeystoneForwarderReportProcessed, error) diff --git a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt index bedaa8320eb..9167c638fbb 100644 --- a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ GETH_VERSION: 1.13.8 -forwarder: ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.bin 7de386c8c4f6cc82ee5d57c35725c522bc3ee0276356b3dce19e1735e70f17b2 +forwarder: ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.bin 892c6ced16576bebd887eb581147c02139853d5143a0c9b77704efefd4ab7ec7 keystone_capability_registry: ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.abi ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.bin 0a79d0eba13fd4a4b83d7618bb181c21c42222f3cc6c5a90a09302f685555033 ocr3_capability: ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.abi ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.bin 9dcbdf55bd5729ba266148da3f17733eb592c871c2108ccca546618628fd9ad2 diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 69ddfb58278..c32e2a9f128 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -21,7 +21,7 @@ require ( github.com/prometheus/client_golang v1.17.0 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240603100528-9e4ad2c80034 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20240222010609-cd67d123c772 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 9745a09531e..06ad66caeaf 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1185,8 +1185,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240603100528-9e4ad2c80034 h1:y2AKnwEybyhr7LEvBRn0RoLBABuckvB6S9gQJMEDrgU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240603100528-9e4ad2c80034/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 h1:3Rl4N7u9RRYmXY96ZLoaHVGONXZ8lL4Kc027dFjD46g= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= diff --git a/core/services/relay/evm/cap_encoder.go b/core/services/relay/evm/cap_encoder.go index e9e9c8d54d8..55cb34a90ac 100644 --- a/core/services/relay/evm/cap_encoder.go +++ b/core/services/relay/evm/cap_encoder.go @@ -2,6 +2,7 @@ package evm import ( "context" + "encoding/binary" "encoding/hex" "encoding/json" "fmt" @@ -16,7 +17,6 @@ import ( const ( abiConfigFieldName = "abi" encoderName = "user" - idLen = 32 ) type capEncoder struct { @@ -84,41 +84,65 @@ func (c *capEncoder) Encode(ctx context.Context, input values.Map) ([]byte, erro } func prependMetadataFields(meta consensustypes.Metadata, userPayload []byte) ([]byte, error) { - // TODO: use all 7 fields from Metadata struct - result := []byte{} - workflowID, err := decodeID(meta.WorkflowID, idLen) - if err != nil { + var err error + var result []byte + + // 1. Version (1 byte) + if meta.Version > 255 { + return nil, fmt.Errorf("version must be between 0 and 255") + } + result = append(result, byte(meta.Version)) + + // 2. Execution ID (32 bytes) + if result, err = decodeAndAppend(meta.ExecutionID, 32, result, "ExecutionID"); err != nil { return nil, err } - result = append(result, workflowID...) - donID, err := decodeID(meta.DONID, 4) - if err != nil { + // 3. Timestamp (4 bytes) + tsBytes := make([]byte, 4) + binary.BigEndian.PutUint32(tsBytes, meta.Timestamp) + result = append(result, tsBytes...) + + // 4. DON ID (4 bytes) + if result, err = decodeAndAppend(meta.DONID, 4, result, "DONID"); err != nil { return nil, err } - result = append(result, donID...) - executionID, err := decodeID(meta.ExecutionID, idLen) - if err != nil { + // 5. DON config version (4 bytes) + cfgVersionBytes := make([]byte, 4) + binary.BigEndian.PutUint32(cfgVersionBytes, meta.DONConfigVersion) + result = append(result, cfgVersionBytes...) + + // 5. Workflow ID / spec hash (32 bytes) + if result, err = decodeAndAppend(meta.WorkflowID, 32, result, "WorkflowID"); err != nil { return nil, err } - result = append(result, executionID...) - workflowOwner, err := decodeID(meta.WorkflowOwner, 20) - if err != nil { + // 6. Workflow Name (10 bytes) + if result, err = decodeAndAppend(meta.WorkflowName, 10, result, "WorkflowName"); err != nil { return nil, err } - result = append(result, workflowOwner...) + + // 7. Workflow Owner (20 bytes) + if result, err = decodeAndAppend(meta.WorkflowOwner, 20, result, "WorkflowOwner"); err != nil { + return nil, err + } + + // 8. Report ID (2 bytes) + if result, err = decodeAndAppend(meta.ReportID, 2, result, "ReportID"); err != nil { + return nil, err + } + return append(result, userPayload...), nil } -func decodeID(id string, expectedLen int) ([]byte, error) { +func decodeAndAppend(id string, expectedLen int, prevResult []byte, logName string) ([]byte, error) { b, err := hex.DecodeString(id) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to hex-decode %s (%s): %w", logName, id, err) } if len(b) != expectedLen { - return nil, fmt.Errorf("incorrect length for id %s, expected %d bytes, got %d", id, expectedLen, len(b)) + return nil, fmt.Errorf("incorrect length for id %s (%s), expected %d bytes, got %d", logName, id, expectedLen, len(b)) } - return b, nil + return append(prevResult, b...), nil } diff --git a/core/services/relay/evm/cap_encoder_test.go b/core/services/relay/evm/cap_encoder_test.go index 7c441491064..b56d3828c42 100644 --- a/core/services/relay/evm/cap_encoder_test.go +++ b/core/services/relay/evm/cap_encoder_test.go @@ -18,10 +18,16 @@ var ( reportA = []byte{0x01, 0x02, 0x03} reportB = []byte{0xaa, 0xbb, 0xcc, 0xdd} - workflowID = "15c631d295ef5e32deb99a10ee6804bc4af1385568f9b3363f6552ac6dbb2cef" - donID = "00010203" - executionID = "8d4e66421db647dd916d3ec28d56188c8d7dae5f808e03d03339ed2562f13bb0" - workflowOwnerID = "0000000000000000000000000000000000000000" + workflowID = "15c631d295ef5e32deb99a10ee6804bc4af1385568f9b3363f6552ac6dbb2cef" + workflowName = "aabbccddeeaabbccddee" + donID = "00010203" + executionID = "8d4e66421db647dd916d3ec28d56188c8d7dae5f808e03d03339ed2562f13bb0" + workflowOwnerID = "0000000000000000000000000000000000000000" + reportID = "9988" + timestampInt = uint32(1234567890) + timestampHex = "499602d2" + configVersionInt = uint32(1) + configVersionHex = "00000001" invalidID = "not_valid" wrongLength = "8d4e66" @@ -48,10 +54,7 @@ func TestEVMEncoder_SingleField(t *testing.T) { expected := // start of the outer tuple - workflowID + - donID + - executionID + - workflowOwnerID + + getHexMetadata() + // start of the inner tuple (user_fields) "0000000000000000000000000000000000000000000000000000000000000020" + // offset of Full_reports array "0000000000000000000000000000000000000000000000000000000000000002" + // length of Full_reports array @@ -87,10 +90,7 @@ func TestEVMEncoder_TwoFields(t *testing.T) { expected := // start of the outer tuple - workflowID + - donID + - executionID + - workflowOwnerID + + getHexMetadata() + // start of the inner tuple (user_fields) "0000000000000000000000000000000000000000000000000000000000000040" + // offset of Prices array "00000000000000000000000000000000000000000000000000000000000000a0" + // offset of Timestamps array @@ -128,10 +128,7 @@ func TestEVMEncoder_Tuple(t *testing.T) { expected := // start of the outer tuple - workflowID + - donID + - executionID + - workflowOwnerID + + getHexMetadata() + // start of the inner tuple (user_fields) "0000000000000000000000000000000000000000000000000000000000000020" + // offset of Elem tuple "0000000000000000000000000000000000000000000000000000000000000040" + // offset of Prices array @@ -176,10 +173,7 @@ func TestEVMEncoder_ListOfTuples(t *testing.T) { expected := // start of the outer tuple - workflowID + - donID + - executionID + - workflowOwnerID + + getHexMetadata() + // start of the inner tuple (user_fields) "0000000000000000000000000000000000000000000000000000000000000020" + // offset of Elem list "0000000000000000000000000000000000000000000000000000000000000002" + // length of Elem list @@ -222,11 +216,20 @@ func TestEVMEncoder_InvalidIDs(t *testing.T) { assert.ErrorContains(t, err, "incorrect length for id") } +func getHexMetadata() string { + return "01" + executionID + timestampHex + donID + configVersionHex + workflowID + workflowName + workflowOwnerID + reportID +} + func getMetadata(cid string) consensustypes.Metadata { return consensustypes.Metadata{ - WorkflowID: cid, - DONID: donID, - ExecutionID: executionID, - WorkflowOwner: workflowOwnerID, + Version: 1, + ExecutionID: executionID, + Timestamp: timestampInt, + DONID: donID, + DONConfigVersion: configVersionInt, + WorkflowID: cid, + WorkflowName: workflowName, + WorkflowOwner: workflowOwnerID, + ReportID: reportID, } } diff --git a/core/services/relay/evm/write_target_test.go b/core/services/relay/evm/write_target_test.go index 76060dce990..4abbf16cd3b 100644 --- a/core/services/relay/evm/write_target_test.go +++ b/core/services/relay/evm/write_target_test.go @@ -99,6 +99,8 @@ func TestEvmWrite(t *testing.T) { "signed_report": map[string]any{ "report": []byte{1, 2, 3}, "signatures": [][]byte{}, + "context": []byte{4, 5}, + "id": []byte{9, 9}, }, }) require.NoError(t, err) @@ -193,6 +195,8 @@ func TestEvmWrite(t *testing.T) { "signed_report": map[string]any{ "report": []byte{1, 2, 3}, "signatures": [][]byte{}, + "context": []byte{4, 5}, + "id": []byte{9, 9}, }, }) require.NoError(t, err) diff --git a/core/services/workflows/engine.go b/core/services/workflows/engine.go index 2672ea3fb6f..9fb2a6beacd 100644 --- a/core/services/workflows/engine.go +++ b/core/services/workflows/engine.go @@ -794,7 +794,7 @@ func NewEngine(cfg Config) (engine *Engine, err error) { workflow.id = cfg.WorkflowID workflow.owner = cfg.WorkflowOwner - workflow.name = cfg.WorkflowName + workflow.name = hex.EncodeToString([]byte(cfg.WorkflowName)) // Instantiate semaphore to put a limit on the number of workers newWorkerCh := make(chan struct{}, cfg.MaxWorkerLimit) diff --git a/go.mod b/go.mod index f9f8cc6000e..309ecf16f01 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chain-selectors v1.0.10 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240603100528-9e4ad2c80034 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 diff --git a/go.sum b/go.sum index 05c8c9ffe3c..f1b2762c452 100644 --- a/go.sum +++ b/go.sum @@ -1171,8 +1171,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240603100528-9e4ad2c80034 h1:y2AKnwEybyhr7LEvBRn0RoLBABuckvB6S9gQJMEDrgU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240603100528-9e4ad2c80034/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 h1:3Rl4N7u9RRYmXY96ZLoaHVGONXZ8lL4Kc027dFjD46g= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 76fb1264b5a..968c6f04d02 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -27,7 +27,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240603100528-9e4ad2c80034 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 github.com/smartcontractkit/chainlink-testing-framework v1.28.17 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 53462f9be60..e3a5c0a8e80 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1512,8 +1512,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240603100528-9e4ad2c80034 h1:y2AKnwEybyhr7LEvBRn0RoLBABuckvB6S9gQJMEDrgU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240603100528-9e4ad2c80034/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 h1:3Rl4N7u9RRYmXY96ZLoaHVGONXZ8lL4Kc027dFjD46g= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 0163e4acd04..f12267228f3 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -16,7 +16,7 @@ require ( github.com/rs/zerolog v1.30.0 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240603100528-9e4ad2c80034 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 github.com/smartcontractkit/chainlink-testing-framework v1.28.17 github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20240214231432-4ad5eb95178c github.com/smartcontractkit/chainlink/v2 v2.9.0-beta0.0.20240216210048-da02459ddad8 diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 1fe677a4843..4dc25c5b838 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1502,8 +1502,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240603100528-9e4ad2c80034 h1:y2AKnwEybyhr7LEvBRn0RoLBABuckvB6S9gQJMEDrgU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240603100528-9e4ad2c80034/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 h1:3Rl4N7u9RRYmXY96ZLoaHVGONXZ8lL4Kc027dFjD46g= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= From 2cc94eb1d0c1e4113865c89deeff7ae66ec15693 Mon Sep 17 00:00:00 2001 From: krehermann Date: Wed, 5 Jun 2024 09:30:37 -0600 Subject: [PATCH 08/15] update workflow name and id validation (#13424) * update workflow name and id validation * fix tests * fix comments --- core/services/feeds/service_test.go | 2 +- core/services/job/models.go | 36 +++++++--- core/services/job/models_test.go | 86 +++++++++++++++++++++++- core/services/workflows/delegate_test.go | 16 ++++- core/web/jobs_controller_test.go | 2 +- 5 files changed, 126 insertions(+), 16 deletions(-) diff --git a/core/services/feeds/service_test.go b/core/services/feeds/service_test.go index ea25fb05756..09c710fff9e 100644 --- a/core/services/feeds/service_test.go +++ b/core/services/feeds/service_test.go @@ -650,7 +650,7 @@ func Test_Service_ProposeJob(t *testing.T) { // variables for workflow spec wfID = "15c631d295ef5e32deb99a10ee6804bc4af1385568f9b3363f6552ac6dbb2cef" wfOwner = "00000000000000000000000000000000000000aa" - wfName = "my-workflow" + wfName = "myworkflow" // len 10 specYaml = ` triggers: - id: "a-trigger" diff --git a/core/services/job/models.go b/core/services/job/models.go index 9601df2e02d..4c6190a0b8e 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -2,6 +2,7 @@ package job import ( "database/sql/driver" + "encoding/hex" "encoding/json" "fmt" "strconv" @@ -841,31 +842,46 @@ type LiquidityBalancerSpec struct { } type WorkflowSpec struct { - ID int32 `toml:"-"` - WorkflowID string `toml:"workflowId"` + ID int32 `toml:"-"` + // TODO it may be possible to compute the workflow id from the hash(yaml, owner, name) and remove this field + WorkflowID string `toml:"workflowId"` // globally unique identifier for the workflow, specified by the user Workflow string `toml:"workflow"` - WorkflowOwner string `toml:"workflowOwner"` - WorkflowName string `toml:"workflowName"` + WorkflowOwner string `toml:"workflowOwner"` // hex string representation of 20 bytes + WorkflowName string `toml:"workflowName"` // 10 byte plain text name CreatedAt time.Time `toml:"-"` UpdatedAt time.Time `toml:"-"` } +var ( + ErrInvalidWorkflowID = errors.New("invalid workflow id") + ErrInvalidWorkflowOwner = errors.New("invalid workflow owner") + ErrInvalidWorkflowName = errors.New("invalid workflow name") +) + const ( - workflowIDLen = 64 - workflowOwnerLen = 40 + workflowIDLen = 64 // conveniently the same length as a sha256 hash + // owner and name are constrained the onchain representation in [github.com/smartcontractkit/chainlink-common/blob/main/pkg/capabilities/consensus/ocr3/types/Metadata] + workflowOwnerLen = 40 // hex string representation of 20 bytes + workflowNameLen = 10 // plain text name ) +// Validate checks the length of the workflow id, owner and name +// that latter two are constrained by the onchain representation in [github.com/smartcontractkit/chainlink-common/blob/main/pkg/capabilities/consensus/ocr3/types/Metadata] func (w *WorkflowSpec) Validate() error { if len(w.WorkflowID) != workflowIDLen { - return fmt.Errorf("incorrect length for id %s: expected %d, got %d", w.WorkflowID, workflowIDLen, len(w.WorkflowID)) + return fmt.Errorf("%w: incorrect length for id %s: expected %d, got %d", ErrInvalidWorkflowID, w.WorkflowID, workflowIDLen, len(w.WorkflowID)) } + _, err := hex.DecodeString(w.WorkflowOwner) + if err != nil { + return fmt.Errorf("%w: expected hex encoding got %s: %w", ErrInvalidWorkflowOwner, w.WorkflowOwner, err) + } if len(w.WorkflowOwner) != workflowOwnerLen { - return fmt.Errorf("incorrect length for owner %s: expected %d, got %d", w.WorkflowOwner, workflowOwnerLen, len(w.WorkflowOwner)) + return fmt.Errorf("%w: incorrect length for owner %s: expected %d, got %d", ErrInvalidWorkflowOwner, w.WorkflowOwner, workflowOwnerLen, len(w.WorkflowOwner)) } - if w.WorkflowName == "" { - return fmt.Errorf("workflow name is required") + if len(w.WorkflowName) != workflowNameLen { + return fmt.Errorf("%w: incorrect length for name %s: expected %d, got %d", ErrInvalidWorkflowName, w.WorkflowName, workflowNameLen, len(w.WorkflowName)) } return nil diff --git a/core/services/job/models_test.go b/core/services/job/models_test.go index 04bbbdee0f0..39ac0f83c1f 100644 --- a/core/services/job/models_test.go +++ b/core/services/job/models_test.go @@ -7,12 +7,14 @@ import ( "time" "github.com/pelletier/go-toml/v2" - "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/codec" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/guregu/null.v4" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) @@ -268,3 +270,83 @@ func TestOCR2OracleSpec(t *testing.T) { }) }) } + +func TestWorkflowSpec_Validate(t *testing.T) { + type fields struct { + ID int32 + WorkflowID string + Workflow string + WorkflowOwner string + WorkflowName string + CreatedAt time.Time + UpdatedAt time.Time + } + tests := []struct { + name string + fields fields + expectedErr error + }{ + { + name: "valid", + fields: fields{ + WorkflowID: "15c631d295ef5e32deb99a10ee6804bc4af1385568f9b3363f6552ac6dbb2cef", + WorkflowOwner: "00000000000000000000000000000000000000aa", + WorkflowName: "ten bytes!", + }, + }, + + { + name: "not hex owner", + fields: fields{ + WorkflowID: "15c631d295ef5e32deb99a10ee6804bc4af1385568f9b3363f6552ac6dbb2cef", + WorkflowOwner: "00000000000000000000000000000000000000az", + WorkflowName: "ten bytes!", + }, + expectedErr: ErrInvalidWorkflowOwner, + }, + + { + name: "not len 40 owner", + fields: fields{ + WorkflowID: "15c631d295ef5e32deb99a10ee6804bc4af1385568f9b3363f6552ac6dbb2cef", + WorkflowOwner: "0000000000", + WorkflowName: "ten bytes!", + }, + expectedErr: ErrInvalidWorkflowOwner, + }, + + { + name: "not len 10 name", + fields: fields{ + WorkflowID: "15c631d295ef5e32deb99a10ee6804bc4af1385568f9b3363f6552ac6dbb2cef", + WorkflowOwner: "00000000000000000000000000000000000000aa", + WorkflowName: "not ten bytes!", + }, + expectedErr: ErrInvalidWorkflowName, + }, + { + name: "not len 64 id", + fields: fields{ + WorkflowID: "15c631d295ef5e32deb99a10ee6804bc4af1385568f9b3363f", + WorkflowOwner: "00000000000000000000000000000000000000aa", + WorkflowName: "ten bytes!", + }, + expectedErr: ErrInvalidWorkflowID, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &WorkflowSpec{ + ID: tt.fields.ID, + WorkflowID: tt.fields.WorkflowID, + Workflow: tt.fields.Workflow, + WorkflowOwner: tt.fields.WorkflowOwner, + WorkflowName: tt.fields.WorkflowName, + CreatedAt: tt.fields.CreatedAt, + UpdatedAt: tt.fields.UpdatedAt, + } + err := w.Validate() + assert.ErrorIs(t, err, tt.expectedErr) + }) + } +} diff --git a/core/services/workflows/delegate_test.go b/core/services/workflows/delegate_test.go index d87e6d68466..f7e106796e6 100644 --- a/core/services/workflows/delegate_test.go +++ b/core/services/workflows/delegate_test.go @@ -23,7 +23,7 @@ type = "workflow" schemaVersion = 1 workflowId = "15c631d295ef5e32deb99a10ee6804bc4af1385568f9b3363f6552ac6dbb2cef" workflowOwner = "00000000000000000000000000000000000000aa" -workflowName = "test" +workflowName = "ten bytes!" `, true, }, @@ -43,12 +43,24 @@ schemaVersion = 1 false, }, { - "missing name", + "invalid name length", ` type = "workflow" schemaVersion = 1 workflowId = "15c631d295ef5e32deb99a10ee6804bc4af1385568f9b3363f6552ac6dbb2cef" workflowOwner = "00000000000000000000000000000000000000aa" +workflowName = "not ten bytes" +`, + false, + }, + { + "not a hex owner", + ` +type = "workflow" +schemaVersion = 1 +workflowId = "15c631d295ef5e32deb99a10ee6804bc4af1385568f9b3363f6552ac6dbb2cef" +workflowOwner = "00000000000000000000000000000000000000aZ" +workflowName = "0123456789" `, false, }, diff --git a/core/web/jobs_controller_test.go b/core/web/jobs_controller_test.go index 359f9ba8b1c..0eadcab108c 100644 --- a/core/web/jobs_controller_test.go +++ b/core/web/jobs_controller_test.go @@ -394,7 +394,7 @@ func TestJobController_Create_HappyPath(t *testing.T) { tomlTemplate: func(_ string) string { id := "15c631d295ef5e32deb99a10ee6804bc4af1385568f9b3363f6552ac6dbb2cef" owner := "00000000000000000000000000000000000000aa" - name := "my-test-workflow" + name := "myworkflow" // 10 bytes workflow := ` triggers: - id: "mercury-trigger" From 2fa39b60e13af275708510cc4238af3995ae1bc3 Mon Sep 17 00:00:00 2001 From: Rodrigo Soares <38868277+rodrigombsoares@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:21:54 -0300 Subject: [PATCH 09/15] Add besu as officially supported (#13387) --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index f216e1d5875..6d246a6c461 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ Ethereum node versions currently tested and supported: - [Parity/Openethereum](https://github.com/openethereum/openethereum) (NOTE: Parity is deprecated and support for this client may be removed in future) - [Geth](https://github.com/ethereum/go-ethereum/releases) +- [Besu](https://github.com/hyperledger/besu) [Supported but broken] These clients are supported by Chainlink, but have bugs that prevent Chainlink from working reliably on these execution clients. @@ -71,11 +72,6 @@ These clients are supported by Chainlink, but have bugs that prevent Chainlink f - [Nethermind](https://github.com/NethermindEth/nethermind) Blocking issues: - ~https://github.com/NethermindEth/nethermind/issues/4384~ -- [Besu](https://github.com/hyperledger/besu) - Blocking issues: - - https://github.com/hyperledger/besu/issues/4212 - - ~https://github.com/hyperledger/besu/issues/4192~ - - ~https://github.com/hyperledger/besu/issues/4114~ - [Erigon](https://github.com/ledgerwatch/erigon) Blocking issues: - https://github.com/ledgerwatch/erigon/discussions/4946 From daf8cf6cfe1c0b8d2a70fb476a4e815af5025ebe Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Wed, 5 Jun 2024 19:41:31 +0300 Subject: [PATCH 10/15] Decouple headtracker tests (#13397) * Add first version of evm utils * Remove unused context util * Add WSServer tests * Add NewLegacyTransaction test * Update NewTestChainScopedConfig to apply correct defaults * Decouple config tests * Decouple headtracker test types from core * Move testutils * Update paths * Update paths * Add default config validation * Fix assert import * Rename test method * Use common multierror * Fixes * Add changeset * Update dependencies * Fix tests * Clean up * Add changeset * Fix lint --- .changeset/five-lizards-smile.md | 5 + core/chains/evm/headtracker/config.go | 20 - .../evm/headtracker/head_broadcaster_test.go | 60 ++- core/chains/evm/headtracker/head_listener.go | 4 +- .../evm/headtracker/head_listener_test.go | 73 ++- core/chains/evm/headtracker/head_saver.go | 7 +- .../chains/evm/headtracker/head_saver_test.go | 16 +- core/chains/evm/headtracker/head_tracker.go | 5 +- .../evm/headtracker/head_tracker_test.go | 468 +++++++++++------- core/chains/evm/headtracker/heads_test.go | 18 +- core/chains/evm/headtracker/mocks/config.go | 82 --- .../evm/headtracker/mocks/head_trackable.go | 23 + core/chains/evm/headtracker/orm_test.go | 49 +- core/chains/evm/log/helpers_test.go | 13 +- core/chains/evm/log/integration_test.go | 27 +- core/chains/evm/testutils/client.go | 69 +++ core/chains/evm/testutils/evmtypes.go | 10 + core/internal/cltest/cltest.go | 25 - core/internal/cltest/mocks.go | 16 - core/internal/features/features_test.go | 9 +- core/internal/testutils/evmtest/evmtest.go | 69 --- 21 files changed, 525 insertions(+), 543 deletions(-) create mode 100644 .changeset/five-lizards-smile.md delete mode 100644 core/chains/evm/headtracker/config.go delete mode 100644 core/chains/evm/headtracker/mocks/config.go create mode 100644 core/chains/evm/headtracker/mocks/head_trackable.go diff --git a/.changeset/five-lizards-smile.md b/.changeset/five-lizards-smile.md new file mode 100644 index 00000000000..ca18921e861 --- /dev/null +++ b/.changeset/five-lizards-smile.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +Remove multiple core dependencies from evm head tracker tests #internal" diff --git a/core/chains/evm/headtracker/config.go b/core/chains/evm/headtracker/config.go deleted file mode 100644 index 85fe084470d..00000000000 --- a/core/chains/evm/headtracker/config.go +++ /dev/null @@ -1,20 +0,0 @@ -package headtracker - -import ( - "time" - - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" -) - -//go:generate mockery --quiet --name Config --output ./mocks/ --case=underscore - -// Config represents a subset of options needed by head tracker -type Config interface { - BlockEmissionIdleWarningThreshold() time.Duration - FinalityDepth() uint32 - FinalityTagEnabled() bool -} - -type HeadTrackerConfig interface { - config.HeadTracker -} diff --git a/core/chains/evm/headtracker/head_broadcaster_test.go b/core/chains/evm/headtracker/head_broadcaster_test.go index ee9e460b16c..2da41de8774 100644 --- a/core/chains/evm/headtracker/head_broadcaster_test.go +++ b/core/chains/evm/headtracker/head_broadcaster_test.go @@ -14,30 +14,29 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" commonhtrk "github.com/smartcontractkit/chainlink/v2/common/headtracker" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" ) func waitHeadBroadcasterToStart(t *testing.T, hb types.HeadBroadcaster) { t.Helper() - subscriber := &cltest.MockHeadTrackable{} + subscriber := &mocks.MockHeadTrackable{} _, unsubscribe := hb.Subscribe(subscriber) defer unsubscribe() - hb.BroadcastNewLongestChain(cltest.Head(1)) + hb.BroadcastNewLongestChain(testutils.Head(1)) g := gomega.NewWithT(t) g.Eventually(subscriber.OnNewLongestChainCount).Should(gomega.Equal(int32(1))) } @@ -46,15 +45,14 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { t.Parallel() g := gomega.NewWithT(t) - cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].HeadTracker.SamplingInterval = &commonconfig.Duration{} + evmCfg := testutils.NewTestChainScopedConfig(t, func(c *toml.EVMConfig) { + c.HeadTracker.SamplingInterval = &commonconfig.Duration{} }) - evmCfg := evmtest.NewChainScopedConfig(t, cfg) db := pgtest.NewSqlxDB(t) logger := logger.Test(t) sub := commonmocks.NewSubscription(t) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) chchHeaders := make(chan chan<- *evmtypes.Head, 1) ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). @@ -63,13 +61,13 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { }). Return(sub, nil) // 2 for initial and 2 for backfill - ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(cltest.Head(1), nil).Times(4) + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(1), nil).Times(4) sub.On("Unsubscribe").Return() sub.On("Err").Return(nil) - checker1 := &cltest.MockHeadTrackable{} - checker2 := &cltest.MockHeadTrackable{} + checker1 := &mocks.MockHeadTrackable{} + checker2 := &mocks.MockHeadTrackable{} orm := headtracker.NewORM(*ethClient.ConfiguredChainID(), db) hs := headtracker.NewHeadSaver(logger, orm, evmCfg.EVM(), evmCfg.EVM().HeadTracker()) @@ -85,7 +83,7 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { assert.Equal(t, (*evmtypes.Head)(nil), latest1) headers := <-chchHeaders - h := evmtypes.Head{Number: 1, Hash: utils.NewHash(), ParentHash: utils.NewHash(), EVMChainID: big.New(&cltest.FixtureChainID)} + h := evmtypes.Head{Number: 1, Hash: utils.NewHash(), ParentHash: utils.NewHash(), EVMChainID: big.New(testutils.FixtureChainID)} headers <- &h g.Eventually(checker1.OnNewLongestChainCount).Should(gomega.Equal(int32(1))) @@ -96,7 +94,7 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { unsubscribe1() - headers <- &evmtypes.Head{Number: 2, Hash: utils.NewHash(), ParentHash: h.Hash, EVMChainID: big.New(&cltest.FixtureChainID)} + headers <- &evmtypes.Head{Number: 2, Hash: utils.NewHash(), ParentHash: h.Hash, EVMChainID: big.New(testutils.FixtureChainID)} g.Eventually(checker2.OnNewLongestChainCount).Should(gomega.Equal(int32(1))) } @@ -107,35 +105,35 @@ func TestHeadBroadcaster_BroadcastNewLongestChain(t *testing.T) { lggr := logger.Test(t) broadcaster := headtracker.NewHeadBroadcaster(lggr) - err := broadcaster.Start(testutils.Context(t)) + err := broadcaster.Start(tests.Context(t)) require.NoError(t, err) waitHeadBroadcasterToStart(t, broadcaster) - subscriber1 := &cltest.MockHeadTrackable{} - subscriber2 := &cltest.MockHeadTrackable{} + subscriber1 := &mocks.MockHeadTrackable{} + subscriber2 := &mocks.MockHeadTrackable{} _, unsubscribe1 := broadcaster.Subscribe(subscriber1) _, unsubscribe2 := broadcaster.Subscribe(subscriber2) - broadcaster.BroadcastNewLongestChain(cltest.Head(1)) + broadcaster.BroadcastNewLongestChain(testutils.Head(1)) g.Eventually(subscriber1.OnNewLongestChainCount).Should(gomega.Equal(int32(1))) unsubscribe1() - broadcaster.BroadcastNewLongestChain(cltest.Head(2)) + broadcaster.BroadcastNewLongestChain(testutils.Head(2)) g.Eventually(subscriber2.OnNewLongestChainCount).Should(gomega.Equal(int32(2))) unsubscribe2() - subscriber3 := &cltest.MockHeadTrackable{} + subscriber3 := &mocks.MockHeadTrackable{} _, unsubscribe3 := broadcaster.Subscribe(subscriber3) - broadcaster.BroadcastNewLongestChain(cltest.Head(1)) + broadcaster.BroadcastNewLongestChain(testutils.Head(1)) g.Eventually(subscriber3.OnNewLongestChainCount).Should(gomega.Equal(int32(1))) unsubscribe3() // no subscribers - shall do nothing - broadcaster.BroadcastNewLongestChain(cltest.Head(0)) + broadcaster.BroadcastNewLongestChain(testutils.Head(0)) err = broadcaster.Close() require.NoError(t, err) @@ -149,21 +147,21 @@ func TestHeadBroadcaster_TrackableCallbackTimeout(t *testing.T) { lggr := logger.Test(t) broadcaster := headtracker.NewHeadBroadcaster(lggr) - err := broadcaster.Start(testutils.Context(t)) + err := broadcaster.Start(tests.Context(t)) require.NoError(t, err) waitHeadBroadcasterToStart(t, broadcaster) - slowAwaiter := cltest.NewAwaiter() - fastAwaiter := cltest.NewAwaiter() + slowAwaiter := testutils.NewAwaiter() + fastAwaiter := testutils.NewAwaiter() slow := &sleepySubscriber{awaiter: slowAwaiter, delay: commonhtrk.TrackableCallbackTimeout * 2} fast := &sleepySubscriber{awaiter: fastAwaiter, delay: commonhtrk.TrackableCallbackTimeout / 2} _, unsubscribe1 := broadcaster.Subscribe(slow) _, unsubscribe2 := broadcaster.Subscribe(fast) - broadcaster.BroadcastNewLongestChain(cltest.Head(1)) - slowAwaiter.AwaitOrFail(t, testutils.WaitTimeout(t)) - fastAwaiter.AwaitOrFail(t, testutils.WaitTimeout(t)) + broadcaster.BroadcastNewLongestChain(testutils.Head(1)) + slowAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) + fastAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) require.True(t, slow.contextDone) require.False(t, fast.contextDone) @@ -176,7 +174,7 @@ func TestHeadBroadcaster_TrackableCallbackTimeout(t *testing.T) { } type sleepySubscriber struct { - awaiter cltest.Awaiter + awaiter testutils.Awaiter delay time.Duration contextDone bool } diff --git a/core/chains/evm/headtracker/head_listener.go b/core/chains/evm/headtracker/head_listener.go index 964d686e803..04535a34868 100644 --- a/core/chains/evm/headtracker/head_listener.go +++ b/core/chains/evm/headtracker/head_listener.go @@ -8,6 +8,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/headtracker" + + htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) @@ -17,7 +19,7 @@ type headListener = headtracker.HeadListener[*evmtypes.Head, common.Hash] func NewHeadListener( lggr logger.Logger, ethClient evmclient.Client, - config Config, chStop chan struct{}, + config htrktypes.Config, chStop chan struct{}, ) headListener { return headtracker.NewHeadListener[ *evmtypes.Head, diff --git a/core/chains/evm/headtracker/head_listener_test.go b/core/chains/evm/headtracker/head_listener_test.go index 4e7efb5e809..2e1a9c81d5e 100644 --- a/core/chains/evm/headtracker/head_listener_test.go +++ b/core/chains/evm/headtracker/head_listener_test.go @@ -14,14 +14,13 @@ import ( commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" - "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" ) func Test_HeadListener_HappyPath(t *testing.T) { @@ -37,12 +36,10 @@ func Test_HeadListener_HappyPath(t *testing.T) { // - ethClient methods are invoked lggr := logger.Test(t) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - // no need to test head timeouts here - c.EVM[0].NoNewHeadsThreshold = &commonconfig.Duration{} + ethClient := testutils.NewEthClientMockWithDefaultChain(t) + evmcfg := testutils.NewTestChainScopedConfig(t, func(c *toml.EVMConfig) { + c.NoNewHeadsThreshold = &commonconfig.Duration{} }) - evmcfg := evmtest.NewChainScopedConfig(t, cfg) chStop := make(chan struct{}) hl := headtracker.NewHeadListener(lggr, ethClient, evmcfg.EVM(), chStop) @@ -52,8 +49,8 @@ func Test_HeadListener_HappyPath(t *testing.T) { return nil } - subscribeAwaiter := cltest.NewAwaiter() - unsubscribeAwaiter := cltest.NewAwaiter() + subscribeAwaiter := testutils.NewAwaiter() + unsubscribeAwaiter := testutils.NewAwaiter() var chHeads chan<- *evmtypes.Head var chErr = make(chan error) var chSubErr <-chan error = chErr @@ -69,18 +66,18 @@ func Test_HeadListener_HappyPath(t *testing.T) { close(chErr) }) - doneAwaiter := cltest.NewAwaiter() + doneAwaiter := testutils.NewAwaiter() done := func() { doneAwaiter.ItHappened() } go hl.ListenForNewHeads(handler, done) - subscribeAwaiter.AwaitOrFail(t, testutils.WaitTimeout(t)) - require.Eventually(t, hl.Connected, testutils.WaitTimeout(t), testutils.TestInterval) + subscribeAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) + require.Eventually(t, hl.Connected, tests.WaitTimeout(t), tests.TestInterval) - chHeads <- cltest.Head(0) - chHeads <- cltest.Head(1) - chHeads <- cltest.Head(2) + chHeads <- testutils.Head(0) + chHeads <- testutils.Head(1) + chHeads <- testutils.Head(2) require.True(t, hl.ReceivingHeads()) @@ -99,22 +96,21 @@ func Test_HeadListener_NotReceivingHeads(t *testing.T) { // - do not send any heads within BlockEmissionIdleWarningThreshold and check ReceivingHeads() is false lggr := logger.Test(t) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) - cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].NoNewHeadsThreshold = commonconfig.MustNewDuration(time.Second) + evmcfg := testutils.NewTestChainScopedConfig(t, func(c *toml.EVMConfig) { + c.NoNewHeadsThreshold = commonconfig.MustNewDuration(time.Second) }) - evmcfg := evmtest.NewChainScopedConfig(t, cfg) chStop := make(chan struct{}) hl := headtracker.NewHeadListener(lggr, ethClient, evmcfg.EVM(), chStop) - firstHeadAwaiter := cltest.NewAwaiter() + firstHeadAwaiter := testutils.NewAwaiter() handler := func(context.Context, *evmtypes.Head) error { firstHeadAwaiter.ItHappened() return nil } - subscribeAwaiter := cltest.NewAwaiter() + subscribeAwaiter := testutils.NewAwaiter() var chHeads chan<- *evmtypes.Head var chErr = make(chan error) var chSubErr <-chan error = chErr @@ -129,15 +125,15 @@ func Test_HeadListener_NotReceivingHeads(t *testing.T) { close(chErr) }) - doneAwaiter := cltest.NewAwaiter() + doneAwaiter := testutils.NewAwaiter() done := func() { doneAwaiter.ItHappened() } go hl.ListenForNewHeads(handler, done) - subscribeAwaiter.AwaitOrFail(t, testutils.WaitTimeout(t)) + subscribeAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) - chHeads <- cltest.Head(0) + chHeads <- testutils.Head(0) firstHeadAwaiter.AwaitOrFail(t) require.True(t, hl.ReceivingHeads()) @@ -152,7 +148,7 @@ func Test_HeadListener_NotReceivingHeads(t *testing.T) { func Test_HeadListener_SubscriptionErr(t *testing.T) { t.Parallel() - tests := []struct { + cases := []struct { name string err error closeErr bool @@ -162,13 +158,12 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { {"close Err channel", nil, true}, } - for _, test := range tests { + for _, test := range cases { test := test t.Run(test.name, func(t *testing.T) { l := logger.Test(t) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - cfg := configtest.NewGeneralConfig(t, nil) - evmcfg := evmtest.NewChainScopedConfig(t, cfg) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) + evmcfg := testutils.NewTestChainScopedConfig(t, nil) chStop := make(chan struct{}) hl := headtracker.NewHeadListener(l, ethClient, evmcfg.EVM(), chStop) @@ -177,7 +172,7 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { hnhCalled <- header return nil } - doneAwaiter := cltest.NewAwaiter() + doneAwaiter := testutils.NewAwaiter() done := doneAwaiter.ItHappened chSubErrTest := make(chan error) @@ -187,7 +182,7 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { // initially and once again after exactly one head has been received sub.On("Err").Return(chSubErr).Twice() - subscribeAwaiter := cltest.NewAwaiter() + subscribeAwaiter := testutils.NewAwaiter() var headsCh chan<- *evmtypes.Head // Initial subscribe ethClient.On("SubscribeNewHead", mock.Anything, mock.AnythingOfType("chan<- *types.Head")).Return(sub, nil).Once().Run(func(args mock.Arguments) { @@ -199,8 +194,8 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { }() // Put a head on the channel to ensure we test all code paths - subscribeAwaiter.AwaitOrFail(t, testutils.WaitTimeout(t)) - head := cltest.Head(0) + subscribeAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) + head := testutils.Head(0) headsCh <- head h := <-hnhCalled @@ -219,7 +214,7 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { var chSubErr2 <-chan error = chSubErrTest2 sub2 := commonmocks.NewSubscription(t) sub2.On("Err").Return(chSubErr2) - subscribeAwaiter2 := cltest.NewAwaiter() + subscribeAwaiter2 := testutils.NewAwaiter() var headsCh2 chan<- *evmtypes.Head ethClient.On("SubscribeNewHead", mock.Anything, mock.AnythingOfType("chan<- *types.Head")).Return(sub2, nil).Once().Run(func(args mock.Arguments) { @@ -235,9 +230,9 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { } // Wait for it to resubscribe - subscribeAwaiter2.AwaitOrFail(t, testutils.WaitTimeout(t)) + subscribeAwaiter2.AwaitOrFail(t, tests.WaitTimeout(t)) - head2 := cltest.Head(1) + head2 := testutils.Head(1) headsCh2 <- head2 h2 := <-hnhCalled diff --git a/core/chains/evm/headtracker/head_saver.go b/core/chains/evm/headtracker/head_saver.go index 8913d2dec21..320e88a19bc 100644 --- a/core/chains/evm/headtracker/head_saver.go +++ b/core/chains/evm/headtracker/head_saver.go @@ -9,21 +9,22 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/headtracker" + commontypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) type headSaver struct { orm ORM - config Config - htConfig HeadTrackerConfig + config commontypes.Config + htConfig commontypes.HeadTrackerConfig logger logger.Logger heads Heads } var _ headtracker.HeadSaver[*evmtypes.Head, common.Hash] = (*headSaver)(nil) -func NewHeadSaver(lggr logger.Logger, orm ORM, config Config, htConfig HeadTrackerConfig) httypes.HeadSaver { +func NewHeadSaver(lggr logger.Logger, orm ORM, config commontypes.Config, htConfig commontypes.HeadTrackerConfig) httypes.HeadSaver { return &headSaver{ orm: orm, config: config, diff --git a/core/chains/evm/headtracker/head_saver_test.go b/core/chains/evm/headtracker/head_saver_test.go index 7f10cad2080..9be9f838d08 100644 --- a/core/chains/evm/headtracker/head_saver_test.go +++ b/core/chains/evm/headtracker/head_saver_test.go @@ -9,14 +9,14 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" ) @@ -69,7 +69,7 @@ func configureSaver(t *testing.T, opts saverOpts) (httypes.HeadSaver, headtracke db := pgtest.NewSqlxDB(t) lggr := logger.Test(t) htCfg := &config{finalityDepth: uint32(1)} - orm := headtracker.NewORM(cltest.FixtureChainID, db) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) saver := headtracker.NewHeadSaver(lggr, orm, htCfg, opts.headTrackerConfig) return saver, orm } @@ -79,11 +79,11 @@ func TestHeadSaver_Save(t *testing.T) { saver, _ := configureSaver(t, saverOpts{}) - head := cltest.Head(1) - err := saver.Save(testutils.Context(t), head) + head := testutils.Head(1) + err := saver.Save(tests.Context(t), head) require.NoError(t, err) - latest, err := saver.LatestHeadFromDB(testutils.Context(t)) + latest, err := saver.LatestHeadFromDB(tests.Context(t)) require.NoError(t, err) require.Equal(t, int64(1), latest.Number) @@ -123,7 +123,7 @@ func TestHeadSaver_Load(t *testing.T) { allHeads := []*evmtypes.Head{h0, h1, h2, h2Uncle, h3, h4, h5} for _, h := range allHeads { - err := orm.IdempotentInsertHead(testutils.Context(t), h) + err := orm.IdempotentInsertHead(tests.Context(t), h) require.NoError(t, err) } @@ -136,7 +136,7 @@ func TestHeadSaver_Load(t *testing.T) { } // load all from [h5-historyDepth, h5] - latestHead, err := saver.Load(testutils.Context(t), h5.BlockNumber()) + latestHead, err := saver.Load(tests.Context(t), h5.BlockNumber()) require.NoError(t, err) // verify latest head loaded from db verifyLatestHead(latestHead) diff --git a/core/chains/evm/headtracker/head_tracker.go b/core/chains/evm/headtracker/head_tracker.go index 414dba23833..357c4dae99a 100644 --- a/core/chains/evm/headtracker/head_tracker.go +++ b/core/chains/evm/headtracker/head_tracker.go @@ -12,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/common/headtracker" + commontypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -20,8 +21,8 @@ import ( func NewHeadTracker( lggr logger.Logger, ethClient evmclient.Client, - config Config, - htConfig HeadTrackerConfig, + config commontypes.Config, + htConfig commontypes.HeadTrackerConfig, headBroadcaster httypes.HeadBroadcaster, headSaver httypes.HeadSaver, mailMon *mailbox.Monitor, diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index b0d9a50da5e..4da8d27c552 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ethereum/go-ethereum" - gethCommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common" gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/onsi/gomega" "github.com/stretchr/testify/assert" @@ -20,8 +20,6 @@ import ( "go.uber.org/zap/zaptest/observer" "golang.org/x/exp/maps" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/jmoiron/sqlx" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" @@ -29,21 +27,22 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" htmocks "github.com/smartcontractkit/chainlink/v2/common/headtracker/mocks" + commontypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/mocks" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" ) func firstHead(t *testing.T, db *sqlx.DB) (h evmtypes.Head) { @@ -57,18 +56,18 @@ func TestHeadTracker_New(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(cltest.Head(0), nil) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) + ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(testutils.Head(0), nil) // finalized - ethClient.On("HeadByNumber", mock.Anything, big.NewInt(0)).Return(cltest.Head(0), nil) + ethClient.On("HeadByNumber", mock.Anything, big.NewInt(0)).Return(testutils.Head(0), nil) - orm := headtracker.NewORM(cltest.FixtureChainID, db) - assert.Nil(t, orm.IdempotentInsertHead(testutils.Context(t), cltest.Head(1))) - last := cltest.Head(16) - assert.Nil(t, orm.IdempotentInsertHead(testutils.Context(t), last)) - assert.Nil(t, orm.IdempotentInsertHead(testutils.Context(t), cltest.Head(10))) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) + assert.Nil(t, orm.IdempotentInsertHead(tests.Context(t), testutils.Head(1))) + last := testutils.Head(16) + assert.Nil(t, orm.IdempotentInsertHead(tests.Context(t), last)) + assert.Nil(t, orm.IdempotentInsertHead(tests.Context(t), testutils.Head(10))) - evmcfg := cltest.NewTestChainScopedConfig(t) + evmcfg := testutils.NewTestChainScopedConfig(t, nil) ht := createHeadTracker(t, ethClient, evmcfg.EVM(), evmcfg.EVM().HeadTracker(), orm) ht.Start(t) @@ -81,31 +80,30 @@ func TestHeadTracker_MarkFinalized_MarksAndTrimsTable(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - gCfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, _ *chainlink.Secrets) { - c.EVM[0].HeadTracker.HistoryDepth = ptr[uint32](100) + config := testutils.NewTestChainScopedConfig(t, func(c *toml.EVMConfig) { + c.HeadTracker.HistoryDepth = ptr[uint32](100) }) - config := evmtest.NewChainScopedConfig(t, gCfg) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - orm := headtracker.NewORM(cltest.FixtureChainID, db) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) for idx := 0; idx < 200; idx++ { - assert.Nil(t, orm.IdempotentInsertHead(testutils.Context(t), cltest.Head(idx))) + assert.Nil(t, orm.IdempotentInsertHead(tests.Context(t), testutils.Head(idx))) } - latest := cltest.Head(201) - assert.Nil(t, orm.IdempotentInsertHead(testutils.Context(t), latest)) + latest := testutils.Head(201) + assert.Nil(t, orm.IdempotentInsertHead(tests.Context(t), latest)) ht := createHeadTracker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm) - _, err := ht.headSaver.Load(testutils.Context(t), latest.Number) + _, err := ht.headSaver.Load(tests.Context(t), latest.Number) require.NoError(t, err) - require.NoError(t, ht.headSaver.MarkFinalized(testutils.Context(t), latest)) + require.NoError(t, ht.headSaver.MarkFinalized(tests.Context(t), latest)) assert.Equal(t, big.NewInt(201), ht.headSaver.LatestChain().ToInt()) firstHead := firstHead(t, db) assert.Equal(t, big.NewInt(101), firstHead.ToInt()) - lastHead, err := orm.LatestHead(testutils.Context(t)) + lastHead, err := orm.LatestHead(tests.Context(t)) require.NoError(t, err) assert.Equal(t, int64(201), lastHead.Number) } @@ -113,30 +111,30 @@ func TestHeadTracker_MarkFinalized_MarksAndTrimsTable(t *testing.T) { func TestHeadTracker_Get(t *testing.T) { t.Parallel() - start := cltest.Head(5) + start := testutils.Head(5) - tests := []struct { + cases := []struct { name string initial *evmtypes.Head toSave *evmtypes.Head want *big.Int }{ - {"greater", start, cltest.Head(6), big.NewInt(6)}, - {"less than", start, cltest.Head(1), big.NewInt(5)}, - {"zero", start, cltest.Head(0), big.NewInt(5)}, + {"greater", start, testutils.Head(6), big.NewInt(6)}, + {"less than", start, testutils.Head(1), big.NewInt(5)}, + {"zero", start, testutils.Head(0), big.NewInt(5)}, {"nil", start, nil, big.NewInt(5)}, {"nil no initial", nil, nil, big.NewInt(0)}, } - for _, test := range tests { + for _, test := range cases { t.Run(test.name, func(t *testing.T) { db := pgtest.NewSqlxDB(t) - config := cltest.NewTestChainScopedConfig(t) - orm := headtracker.NewORM(cltest.FixtureChainID, db) + config := testutils.NewTestChainScopedConfig(t, nil) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) chStarted := make(chan struct{}) - mockEth := &evmtest.MockEth{ + mockEth := &testutils.MockEth{ EthClient: ethClient, } ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). @@ -148,23 +146,23 @@ func TestHeadTracker_Get(t *testing.T) { }, func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) - ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(cltest.Head(0), nil) + ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(testutils.Head(0), nil) fnCall := ethClient.On("HeadByNumber", mock.Anything, mock.Anything) fnCall.RunFn = func(args mock.Arguments) { num := args.Get(1).(*big.Int) - fnCall.ReturnArguments = mock.Arguments{cltest.Head(num.Int64()), nil} + fnCall.ReturnArguments = mock.Arguments{testutils.Head(num.Int64()), nil} } if test.initial != nil { - assert.Nil(t, orm.IdempotentInsertHead(testutils.Context(t), test.initial)) + assert.Nil(t, orm.IdempotentInsertHead(tests.Context(t), test.initial)) } ht := createHeadTracker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm) ht.Start(t) if test.toSave != nil { - err := ht.headSaver.Save(testutils.Context(t), test.toSave) + err := ht.headSaver.Save(tests.Context(t), test.toSave) assert.NoError(t, err) } @@ -177,18 +175,18 @@ func TestHeadTracker_Start_NewHeads(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - config := cltest.NewTestChainScopedConfig(t) - orm := headtracker.NewORM(cltest.FixtureChainID, db) + config := testutils.NewTestChainScopedConfig(t, nil) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) chStarted := make(chan struct{}) - mockEth := &evmtest.MockEth{EthClient: ethClient} + mockEth := &testutils.MockEth{EthClient: ethClient} sub := mockEth.NewSub(t) // for initial load - ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(cltest.Head(0), nil).Once() - ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(cltest.Head(0), nil).Once() + ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(testutils.Head(0), nil).Once() + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil).Once() // for backfill - ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(cltest.Head(0), nil).Maybe() + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil).Maybe() ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). Run(func(mock.Arguments) { close(chStarted) @@ -213,38 +211,37 @@ func TestHeadTracker_Start(t *testing.T) { } newHeadTracker := func(t *testing.T, opts opts) *headTrackerUniverse { db := pgtest.NewSqlxDB(t) - gCfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, _ *chainlink.Secrets) { + config := testutils.NewTestChainScopedConfig(t, func(c *toml.EVMConfig) { if opts.FinalityTagEnable != nil { - c.EVM[0].FinalityTagEnabled = opts.FinalityTagEnable + c.FinalityTagEnabled = opts.FinalityTagEnable } - c.EVM[0].HeadTracker.HistoryDepth = ptr[uint32](historyDepth) - c.EVM[0].FinalityDepth = ptr[uint32](finalityDepth) + c.HeadTracker.HistoryDepth = ptr[uint32](historyDepth) + c.FinalityDepth = ptr[uint32](finalityDepth) if opts.MaxAllowedFinalityDepth != nil { - c.EVM[0].HeadTracker.MaxAllowedFinalityDepth = opts.MaxAllowedFinalityDepth + c.HeadTracker.MaxAllowedFinalityDepth = opts.MaxAllowedFinalityDepth } if opts.FinalityTagBypass != nil { - c.EVM[0].HeadTracker.FinalityTagBypass = opts.FinalityTagBypass + c.HeadTracker.FinalityTagBypass = opts.FinalityTagBypass } }) - config := evmtest.NewChainScopedConfig(t, gCfg) - orm := headtracker.NewORM(cltest.FixtureChainID, db) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) return createHeadTracker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm) } t.Run("Fail start if context was canceled", func(t *testing.T) { - ctx, cancel := context.WithCancel(testutils.Context(t)) + ctx, cancel := context.WithCancel(tests.Context(t)) ht := newHeadTracker(t, opts{}) ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Run(func(args mock.Arguments) { cancel() - }).Return(cltest.Head(0), context.Canceled) + }).Return(testutils.Head(0), context.Canceled) err := ht.headTracker.Start(ctx) require.ErrorIs(t, err, context.Canceled) }) t.Run("Starts even if failed to get initialHead", func(t *testing.T) { ht := newHeadTracker(t, opts{}) - ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(cltest.Head(0), errors.New("failed to get init head")) + ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(testutils.Head(0), errors.New("failed to get init head")) ht.Start(t) tests.AssertLogEventually(t, ht.observer, "Error handling initial head") }) @@ -256,7 +253,7 @@ func TestHeadTracker_Start(t *testing.T) { }) t.Run("Starts even if fails to get finalizedHead", func(t *testing.T) { ht := newHeadTracker(t, opts{FinalityTagEnable: ptr(true), FinalityTagBypass: ptr(false)}) - head := cltest.Head(1000) + head := testutils.Head(1000) ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(head, nil).Once() ht.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, errors.New("failed to load latest finalized")).Once() ht.Start(t) @@ -264,7 +261,7 @@ func TestHeadTracker_Start(t *testing.T) { }) t.Run("Starts even if latest finalizedHead is nil", func(t *testing.T) { ht := newHeadTracker(t, opts{FinalityTagEnable: ptr(true), FinalityTagBypass: ptr(false)}) - head := cltest.Head(1000) + head := testutils.Head(1000) ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(head, nil).Once() ht.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, nil).Once() ht.ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, errors.New("failed to connect")).Maybe() @@ -273,9 +270,9 @@ func TestHeadTracker_Start(t *testing.T) { }) t.Run("Logs error if finality gap is too big", func(t *testing.T) { ht := newHeadTracker(t, opts{FinalityTagEnable: ptr(true), FinalityTagBypass: ptr(false), MaxAllowedFinalityDepth: ptr(uint32(10))}) - head := cltest.Head(1000) + head := testutils.Head(1000) ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(head, nil).Once() - ht.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(cltest.Head(989), nil).Once() + ht.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(testutils.Head(989), nil).Once() ht.ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, errors.New("failed to connect")).Maybe() ht.Start(t) tests.AssertEventually(t, func() bool { @@ -286,12 +283,12 @@ func TestHeadTracker_Start(t *testing.T) { }) }) t.Run("Happy path (finality tag)", func(t *testing.T) { - head := cltest.Head(1000) + head := testutils.Head(1000) ht := newHeadTracker(t, opts{FinalityTagEnable: ptr(true), FinalityTagBypass: ptr(false)}) - ctx := testutils.Context(t) - require.NoError(t, ht.orm.IdempotentInsertHead(ctx, cltest.Head(799))) + ctx := tests.Context(t) + require.NoError(t, ht.orm.IdempotentInsertHead(ctx, testutils.Head(799))) ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(head, nil).Once() - finalizedHead := cltest.Head(800) + finalizedHead := testutils.Head(800) // on start ht.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(finalizedHead, nil).Once() // on backfill @@ -301,13 +298,13 @@ func TestHeadTracker_Start(t *testing.T) { tests.AssertLogEventually(t, ht.observer, "Loaded chain from DB") }) happyPathFD := func(t *testing.T, opts opts) { - head := cltest.Head(1000) + head := testutils.Head(1000) ht := newHeadTracker(t, opts) ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(head, nil).Once() - finalizedHead := cltest.Head(head.Number - finalityDepth) + finalizedHead := testutils.Head(head.Number - finalityDepth) ht.ethClient.On("HeadByNumber", mock.Anything, big.NewInt(finalizedHead.Number)).Return(finalizedHead, nil).Once() - ctx := testutils.Context(t) - require.NoError(t, ht.orm.IdempotentInsertHead(ctx, cltest.Head(finalizedHead.Number-1))) + ctx := tests.Context(t) + require.NoError(t, ht.orm.IdempotentInsertHead(ctx, testutils.Head(finalizedHead.Number-1))) // on backfill ht.ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(nil, errors.New("backfill call to finalized failed")).Maybe() ht.ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, errors.New("failed to connect")).Maybe() @@ -343,33 +340,33 @@ func TestHeadTracker_CallsHeadTrackableCallbacks(t *testing.T) { g := gomega.NewWithT(t) db := pgtest.NewSqlxDB(t) - config := cltest.NewTestChainScopedConfig(t) - orm := headtracker.NewORM(cltest.FixtureChainID, db) + config := testutils.NewTestChainScopedConfig(t, nil) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) - chchHeaders := make(chan evmtest.RawSub[*evmtypes.Head], 1) - mockEth := &evmtest.MockEth{EthClient: ethClient} + chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) + mockEth := &testutils.MockEth{EthClient: ethClient} ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). Return( func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { sub := mockEth.NewSub(t) - chchHeaders <- evmtest.NewRawSub(ch, sub.Err()) + chchHeaders <- testutils.NewRawSub(ch, sub.Err()) return sub }, func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) - ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(cltest.Head(0), nil) - ethClient.On("HeadByHash", mock.Anything, mock.Anything).Return(cltest.Head(0), nil).Maybe() + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil) + ethClient.On("HeadByHash", mock.Anything, mock.Anything).Return(testutils.Head(0), nil).Maybe() - checker := &cltest.MockHeadTrackable{} + checker := &mocks.MockHeadTrackable{} ht := createHeadTrackerWithChecker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm, checker) ht.Start(t) assert.Equal(t, int32(0), checker.OnNewLongestChainCount()) headers := <-chchHeaders - headers.TrySend(&evmtypes.Head{Number: 1, Hash: utils.NewHash(), EVMChainID: ubig.New(&cltest.FixtureChainID)}) + headers.TrySend(&evmtypes.Head{Number: 1, Hash: utils.NewHash(), EVMChainID: ubig.New(testutils.FixtureChainID)}) g.Eventually(checker.OnNewLongestChainCount).Should(gomega.Equal(int32(1))) ht.Stop(t) @@ -381,11 +378,11 @@ func TestHeadTracker_ReconnectOnError(t *testing.T) { g := gomega.NewWithT(t) db := pgtest.NewSqlxDB(t) - config := cltest.NewTestChainScopedConfig(t) - orm := headtracker.NewORM(cltest.FixtureChainID, db) + config := testutils.NewTestChainScopedConfig(t, nil) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - mockEth := &evmtest.MockEth{EthClient: ethClient} + ethClient := testutils.NewEthClientMockWithDefaultChain(t) + mockEth := &testutils.MockEth{EthClient: ethClient} ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). Return( func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { return mockEth.NewSub(t) }, @@ -397,9 +394,8 @@ func TestHeadTracker_ReconnectOnError(t *testing.T) { func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { return mockEth.NewSub(t) }, func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) - ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(cltest.Head(0), nil) - - checker := &cltest.MockHeadTrackable{} + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil) + checker := &mocks.MockHeadTrackable{} ht := createHeadTrackerWithChecker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm, checker) // connect @@ -408,7 +404,7 @@ func TestHeadTracker_ReconnectOnError(t *testing.T) { // trigger reconnect loop mockEth.SubsErr(errors.New("test error to force reconnect")) - g.Eventually(checker.OnNewLongestChainCount).Should(gomega.Equal(int32(1))) + g.Eventually(checker.OnNewLongestChainCount, 5*time.Second, tests.TestInterval).Should(gomega.Equal(int32(1))) } func TestHeadTracker_ResubscribeOnSubscriptionError(t *testing.T) { @@ -416,26 +412,26 @@ func TestHeadTracker_ResubscribeOnSubscriptionError(t *testing.T) { g := gomega.NewWithT(t) db := pgtest.NewSqlxDB(t) - config := cltest.NewTestChainScopedConfig(t) - orm := headtracker.NewORM(cltest.FixtureChainID, db) + config := testutils.NewTestChainScopedConfig(t, nil) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) - chchHeaders := make(chan evmtest.RawSub[*evmtypes.Head], 1) - mockEth := &evmtest.MockEth{EthClient: ethClient} + chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) + mockEth := &testutils.MockEth{EthClient: ethClient} ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). Return( func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { sub := mockEth.NewSub(t) - chchHeaders <- evmtest.NewRawSub(ch, sub.Err()) + chchHeaders <- testutils.NewRawSub(ch, sub.Err()) return sub }, func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) - ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(cltest.Head(0), nil) - ethClient.On("HeadByHash", mock.Anything, mock.Anything).Return(cltest.Head(0), nil).Maybe() + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil) + ethClient.On("HeadByHash", mock.Anything, mock.Anything).Return(testutils.Head(0), nil).Maybe() - checker := &cltest.MockHeadTrackable{} + checker := &mocks.MockHeadTrackable{} ht := createHeadTrackerWithChecker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm, checker) ht.Start(t) @@ -443,37 +439,37 @@ func TestHeadTracker_ResubscribeOnSubscriptionError(t *testing.T) { headers := <-chchHeaders go func() { - headers.TrySend(cltest.Head(1)) + headers.TrySend(testutils.Head(1)) }() g.Eventually(func() bool { report := ht.headTracker.HealthReport() return !slices.ContainsFunc(maps.Values(report), func(e error) bool { return e != nil }) - }, 5*time.Second, testutils.TestInterval).Should(gomega.Equal(true)) + }, 5*time.Second, tests.TestInterval).Should(gomega.Equal(true)) // trigger reconnect loop headers.CloseCh() // wait for full disconnect and a new subscription - g.Eventually(checker.OnNewLongestChainCount, 5*time.Second, testutils.TestInterval).Should(gomega.Equal(int32(1))) + g.Eventually(checker.OnNewLongestChainCount, 5*time.Second, tests.TestInterval).Should(gomega.Equal(int32(1))) } func TestHeadTracker_Start_LoadsLatestChain(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - config := cltest.NewTestChainScopedConfig(t) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + config := testutils.NewTestChainScopedConfig(t, nil) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) heads := []*evmtypes.Head{ - cltest.Head(0), - cltest.Head(1), - cltest.Head(2), - cltest.Head(3), + testutils.Head(0), + testutils.Head(1), + testutils.Head(2), + testutils.Head(3), } - var parentHash gethCommon.Hash + var parentHash common.Hash for i := 0; i < len(heads); i++ { - if parentHash != (gethCommon.Hash{}) { + if parentHash != (common.Hash{}) { heads[i].ParentHash = parentHash } parentHash = heads[i].Hash @@ -484,23 +480,23 @@ func TestHeadTracker_Start_LoadsLatestChain(t *testing.T) { ethClient.On("HeadByHash", mock.Anything, heads[1].Hash).Return(heads[1], nil).Maybe() ethClient.On("HeadByHash", mock.Anything, heads[0].Hash).Return(heads[0], nil).Maybe() - chchHeaders := make(chan evmtest.RawSub[*evmtypes.Head], 1) - mockEth := &evmtest.MockEth{EthClient: ethClient} + chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) + mockEth := &testutils.MockEth{EthClient: ethClient} ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). Return( func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { sub := mockEth.NewSub(t) - chchHeaders <- evmtest.NewRawSub(ch, sub.Err()) + chchHeaders <- testutils.NewRawSub(ch, sub.Err()) return sub }, func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) - orm := headtracker.NewORM(cltest.FixtureChainID, db) - trackable := &cltest.MockHeadTrackable{} + orm := headtracker.NewORM(*testutils.FixtureChainID, db) + trackable := &mocks.MockHeadTrackable{} ht := createHeadTrackerWithChecker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm, trackable) - require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), heads[2])) + require.NoError(t, orm.IdempotentInsertHead(tests.Context(t), heads[2])) ht.Start(t) @@ -508,16 +504,16 @@ func TestHeadTracker_Start_LoadsLatestChain(t *testing.T) { headers := <-chchHeaders go func() { - headers.TrySend(cltest.Head(1)) + headers.TrySend(testutils.Head(1)) }() gomega.NewWithT(t).Eventually(func() bool { report := ht.headTracker.HealthReport() services.CopyHealth(report, ht.headBroadcaster.HealthReport()) return !slices.ContainsFunc(maps.Values(report), func(e error) bool { return e != nil }) - }, 5*time.Second, testutils.TestInterval).Should(gomega.Equal(true)) + }, 5*time.Second, tests.TestInterval).Should(gomega.Equal(true)) - h, err := orm.LatestHead(testutils.Context(t)) + h, err := orm.LatestHead(tests.Context(t)) require.NoError(t, err) require.NotNil(t, h) assert.Equal(t, h.Number, int64(3)) @@ -528,34 +524,33 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingEnabled(t *testing.T) db := pgtest.NewSqlxDB(t) - config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].FinalityDepth = ptr[uint32](50) + config := testutils.NewTestChainScopedConfig(t, func(c *toml.EVMConfig) { + c.FinalityDepth = ptr[uint32](50) // Need to set the buffer to something large since we inject a lot of heads at once and otherwise they will be dropped - c.EVM[0].HeadTracker.MaxBufferSize = ptr[uint32](100) - c.EVM[0].HeadTracker.SamplingInterval = commonconfig.MustNewDuration(2500 * time.Millisecond) + c.HeadTracker.MaxBufferSize = ptr[uint32](100) + c.HeadTracker.SamplingInterval = commonconfig.MustNewDuration(2500 * time.Millisecond) }) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) - checker := htmocks.NewHeadTrackable[*evmtypes.Head, gethCommon.Hash](t) - orm := headtracker.NewORM(*evmtest.MustGetDefaultChainID(t, config.EVMConfigs()), db) - csCfg := evmtest.NewChainScopedConfig(t, config) - ht := createHeadTrackerWithChecker(t, ethClient, csCfg.EVM(), csCfg.EVM().HeadTracker(), orm, checker) + checker := htmocks.NewHeadTrackable[*evmtypes.Head, common.Hash](t) + orm := headtracker.NewORM(*config.EVM().ChainID(), db) + ht := createHeadTrackerWithChecker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm, checker) - chchHeaders := make(chan evmtest.RawSub[*evmtypes.Head], 1) - mockEth := &evmtest.MockEth{EthClient: ethClient} + chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) + mockEth := &testutils.MockEth{EthClient: ethClient} ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). Return( func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { sub := mockEth.NewSub(t) - chchHeaders <- evmtest.NewRawSub(ch, sub.Err()) + chchHeaders <- testutils.NewRawSub(ch, sub.Err()) return sub }, func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) // --------------------- - blocks := cltest.NewBlocks(t, 10) + blocks := NewBlocks(t, 10) head0 := blocks.Head(0) // Initial query @@ -564,7 +559,7 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingEnabled(t *testing.T) ethClient.On("HeadByNumber", mock.Anything, big.NewInt(0)).Return(head0, nil) ht.Start(t) - headSeq := cltest.NewHeadBuffer(t) + headSeq := NewHeadBuffer(t) headSeq.Append(blocks.Head(0)) headSeq.Append(blocks.Head(1)) @@ -585,7 +580,7 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingEnabled(t *testing.T) headSeq.Append(blocksForked.Head(4)) headSeq.Append(blocksForked.Head(5)) // Now the new chain is longer - lastLongestChainAwaiter := cltest.NewAwaiter() + lastLongestChainAwaiter := testutils.NewAwaiter() // the callback is only called for head number 5 because of head sampling checker.On("OnNewLongestChain", mock.Anything, mock.Anything). @@ -619,14 +614,14 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingEnabled(t *testing.T) // This grotesque construction is the only way to do dynamic return values using // the mock package. We need dynamic returns because we're simulating reorgs. - latestHeadByHash := make(map[gethCommon.Hash]*evmtypes.Head) + latestHeadByHash := make(map[common.Hash]*evmtypes.Head) latestHeadByHashMu := new(sync.Mutex) fnCall := ethClient.On("HeadByHash", mock.Anything, mock.Anything).Maybe() fnCall.RunFn = func(args mock.Arguments) { latestHeadByHashMu.Lock() defer latestHeadByHashMu.Unlock() - hash := args.Get(1).(gethCommon.Hash) + hash := args.Get(1).(common.Hash) head := latestHeadByHash[hash] fnCall.ReturnArguments = mock.Arguments{head, nil} } @@ -638,8 +633,8 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingEnabled(t *testing.T) headers.TrySend(h) } - // default 10s may not be sufficient, so using testutils.WaitTimeout(t) - lastLongestChainAwaiter.AwaitOrFail(t, testutils.WaitTimeout(t)) + // default 10s may not be sufficient, so using tests.WaitTimeout(t) + lastLongestChainAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) ht.Stop(t) assert.Equal(t, int64(5), ht.headSaver.LatestChain().Number) @@ -657,34 +652,33 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingDisabled(t *testing.T db := pgtest.NewSqlxDB(t) - config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].FinalityDepth = ptr[uint32](50) + config := testutils.NewTestChainScopedConfig(t, func(c *toml.EVMConfig) { + c.FinalityDepth = ptr[uint32](50) // Need to set the buffer to something large since we inject a lot of heads at once and otherwise they will be dropped - c.EVM[0].HeadTracker.MaxBufferSize = ptr[uint32](100) - c.EVM[0].HeadTracker.SamplingInterval = commonconfig.MustNewDuration(0) + c.HeadTracker.MaxBufferSize = ptr[uint32](100) + c.HeadTracker.SamplingInterval = commonconfig.MustNewDuration(0) }) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) - checker := htmocks.NewHeadTrackable[*evmtypes.Head, gethCommon.Hash](t) - orm := headtracker.NewORM(cltest.FixtureChainID, db) - evmcfg := evmtest.NewChainScopedConfig(t, config) - ht := createHeadTrackerWithChecker(t, ethClient, evmcfg.EVM(), evmcfg.EVM().HeadTracker(), orm, checker) + checker := htmocks.NewHeadTrackable[*evmtypes.Head, common.Hash](t) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) + ht := createHeadTrackerWithChecker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm, checker) - chchHeaders := make(chan evmtest.RawSub[*evmtypes.Head], 1) - mockEth := &evmtest.MockEth{EthClient: ethClient} + chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) + mockEth := &testutils.MockEth{EthClient: ethClient} ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). Return( func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { sub := mockEth.NewSub(t) - chchHeaders <- evmtest.NewRawSub(ch, sub.Err()) + chchHeaders <- testutils.NewRawSub(ch, sub.Err()) return sub }, func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) // --------------------- - blocks := cltest.NewBlocks(t, 10) + blocks := NewBlocks(t, 10) head0 := blocks.Head(0) // evmtypes.Head{Number: 0, Hash: utils.NewHash(), ParentHash: utils.NewHash(), Timestamp: time.Unix(0, 0)} // Initial query @@ -692,7 +686,7 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingDisabled(t *testing.T // backfill ethClient.On("HeadByNumber", mock.Anything, big.NewInt(0)).Return(head0, nil) - headSeq := cltest.NewHeadBuffer(t) + headSeq := NewHeadBuffer(t) headSeq.Append(blocks.Head(0)) headSeq.Append(blocks.Head(1)) @@ -713,7 +707,7 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingDisabled(t *testing.T headSeq.Append(blocksForked.Head(4)) headSeq.Append(blocksForked.Head(5)) // Now the new chain is longer - lastLongestChainAwaiter := cltest.NewAwaiter() + lastLongestChainAwaiter := testutils.NewAwaiter() checker.On("OnNewLongestChain", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { @@ -776,14 +770,14 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingDisabled(t *testing.T // This grotesque construction is the only way to do dynamic return values using // the mock package. We need dynamic returns because we're simulating reorgs. - latestHeadByHash := make(map[gethCommon.Hash]*evmtypes.Head) + latestHeadByHash := make(map[common.Hash]*evmtypes.Head) latestHeadByHashMu := new(sync.Mutex) fnCall := ethClient.On("HeadByHash", mock.Anything, mock.Anything).Maybe() fnCall.RunFn = func(args mock.Arguments) { latestHeadByHashMu.Lock() defer latestHeadByHashMu.Unlock() - hash := args.Get(1).(gethCommon.Hash) + hash := args.Get(1).(common.Hash) head := latestHeadByHash[hash] fnCall.ReturnArguments = mock.Arguments{head, nil} } @@ -793,11 +787,11 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingDisabled(t *testing.T latestHeadByHash[h.Hash] = h latestHeadByHashMu.Unlock() headers.TrySend(h) - time.Sleep(testutils.TestInterval) + time.Sleep(tests.TestInterval) } - // default 10s may not be sufficient, so using testutils.WaitTimeout(t) - lastLongestChainAwaiter.AwaitOrFail(t, testutils.WaitTimeout(t)) + // default 10s may not be sufficient, so using tests.WaitTimeout(t) + lastLongestChainAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) ht.Stop(t) assert.Equal(t, int64(5), ht.headSaver.LatestChain().Number) @@ -827,12 +821,12 @@ func TestHeadTracker_Backfill(t *testing.T) { gethHead0 := &gethTypes.Header{ Number: big.NewInt(0), - ParentHash: gethCommon.BigToHash(big.NewInt(0)), + ParentHash: common.BigToHash(big.NewInt(0)), Time: now, } - head0 := evmtypes.NewHead(gethHead0.Number, utils.NewHash(), gethHead0.ParentHash, gethHead0.Time, ubig.New(&cltest.FixtureChainID)) + head0 := evmtypes.NewHead(gethHead0.Number, utils.NewHash(), gethHead0.ParentHash, gethHead0.Time, ubig.New(testutils.FixtureChainID)) - h1 := *cltest.Head(1) + h1 := *testutils.Head(1) h1.ParentHash = head0.Hash gethHead8 := &gethTypes.Header{ @@ -840,9 +834,9 @@ func TestHeadTracker_Backfill(t *testing.T) { ParentHash: utils.NewHash(), Time: now, } - head8 := evmtypes.NewHead(gethHead8.Number, utils.NewHash(), gethHead8.ParentHash, gethHead8.Time, ubig.New(&cltest.FixtureChainID)) + head8 := evmtypes.NewHead(gethHead8.Number, utils.NewHash(), gethHead8.ParentHash, gethHead8.Time, ubig.New(testutils.FixtureChainID)) - h9 := *cltest.Head(9) + h9 := *testutils.Head(9) h9.ParentHash = head8.Hash gethHead10 := &gethTypes.Header{ @@ -850,24 +844,24 @@ func TestHeadTracker_Backfill(t *testing.T) { ParentHash: h9.Hash, Time: now, } - head10 := evmtypes.NewHead(gethHead10.Number, utils.NewHash(), gethHead10.ParentHash, gethHead10.Time, ubig.New(&cltest.FixtureChainID)) + head10 := evmtypes.NewHead(gethHead10.Number, utils.NewHash(), gethHead10.ParentHash, gethHead10.Time, ubig.New(testutils.FixtureChainID)) - h11 := *cltest.Head(11) + h11 := *testutils.Head(11) h11.ParentHash = head10.Hash - h12 := *cltest.Head(12) + h12 := *testutils.Head(12) h12.ParentHash = h11.Hash - h13 := *cltest.Head(13) + h13 := *testutils.Head(13) h13.ParentHash = h12.Hash - h14Orphaned := *cltest.Head(14) + h14Orphaned := *testutils.Head(14) h14Orphaned.ParentHash = h13.Hash - h14 := *cltest.Head(14) + h14 := *testutils.Head(14) h14.ParentHash = h13.Hash - h15 := *cltest.Head(15) + h15 := *testutils.Head(15) h15.ParentHash = h14.Hash heads := []evmtypes.Head{ @@ -880,24 +874,22 @@ func TestHeadTracker_Backfill(t *testing.T) { h15, } - ctx := testutils.Context(t) + ctx := tests.Context(t) type opts struct { Heads []evmtypes.Head } newHeadTrackerUniverse := func(t *testing.T, opts opts) *headTrackerUniverse { - cfg := configtest.NewGeneralConfig(t, nil) - - evmcfg := evmtest.NewChainScopedConfig(t, cfg) + evmcfg := testutils.NewTestChainScopedConfig(t, nil) db := pgtest.NewSqlxDB(t) - orm := headtracker.NewORM(cltest.FixtureChainID, db) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) for i := range opts.Heads { - require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), &opts.Heads[i])) + require.NoError(t, orm.IdempotentInsertHead(tests.Context(t), &opts.Heads[i])) } - ethClient := evmtest.NewEthClientMock(t) - ethClient.On("ConfiguredChainID", mock.Anything).Return(evmtest.MustGetDefaultChainID(t, cfg.EVMConfigs()), nil) + ethClient := testutils.NewEthClientMock(t) + ethClient.On("ConfiguredChainID", mock.Anything).Return(evmcfg.EVM().ChainID(), nil) ht := createHeadTracker(t, ethClient, evmcfg.EVM(), evmcfg.EVM().HeadTracker(), orm) - _, err := ht.headSaver.Load(testutils.Context(t), 0) + _, err := ht.headSaver.Load(tests.Context(t), 0) require.NoError(t, err) return ht } @@ -954,7 +946,7 @@ func TestHeadTracker_Backfill(t *testing.T) { require.NotNil(t, h.Parent.Parent.Parent) assert.Equal(t, int64(9), h.Parent.Parent.Parent.Number) - writtenHead, err := htu.orm.HeadByHash(testutils.Context(t), head10.Hash) + writtenHead, err := htu.orm.HeadByHash(tests.Context(t), head10.Hash) require.NoError(t, err) assert.Equal(t, int64(10), writtenHead.Number) }) @@ -1055,9 +1047,7 @@ func TestHeadTracker_Backfill(t *testing.T) { // BenchmarkHeadTracker_Backfill - benchmarks HeadTracker's Backfill with focus on efficiency after initial // backfill on start up func BenchmarkHeadTracker_Backfill(b *testing.B) { - cfg := configtest.NewGeneralConfig(b, nil) - - evmcfg := evmtest.NewChainScopedConfig(b, cfg) + evmcfg := testutils.NewTestChainScopedConfig(b, nil) db := pgtest.NewSqlxDB(b) chainID := big.NewInt(evmclient.NullClientChainID) orm := headtracker.NewORM(*chainID, db) @@ -1065,8 +1055,8 @@ func BenchmarkHeadTracker_Backfill(b *testing.B) { ethClient.On("ConfiguredChainID").Return(chainID) ht := createHeadTracker(b, ethClient, evmcfg.EVM(), evmcfg.EVM().HeadTracker(), orm) ctx := tests.Context(b) - makeHash := func(n int64) gethCommon.Hash { - return gethCommon.BigToHash(big.NewInt(n)) + makeHash := func(n int64) common.Hash { + return common.BigToHash(big.NewInt(n)) } const finalityDepth = 12000 // observed value on Arbitrum makeBlock := func(n int64) *evmtypes.Head { @@ -1074,7 +1064,7 @@ func BenchmarkHeadTracker_Backfill(b *testing.B) { } latest := makeBlock(finalityDepth) finalized := makeBlock(1) - ethClient.On("HeadByHash", mock.Anything, mock.Anything).Return(func(_ context.Context, hash gethCommon.Hash) (*evmtypes.Head, error) { + ethClient.On("HeadByHash", mock.Anything, mock.Anything).Return(func(_ context.Context, hash common.Hash) (*evmtypes.Head, error) { number := hash.Big().Int64() return makeBlock(number), nil }) @@ -1091,7 +1081,7 @@ func BenchmarkHeadTracker_Backfill(b *testing.B) { } } -func createHeadTracker(t testing.TB, ethClient *evmclimocks.Client, config headtracker.Config, htConfig headtracker.HeadTrackerConfig, orm headtracker.ORM) *headTrackerUniverse { +func createHeadTracker(t testing.TB, ethClient *evmclimocks.Client, config commontypes.Config, htConfig commontypes.HeadTrackerConfig, orm headtracker.ORM) *headTrackerUniverse { lggr, ob := logger.TestObserved(t, zap.DebugLevel) hb := headtracker.NewHeadBroadcaster(lggr) hs := headtracker.NewHeadSaver(lggr, orm, config, htConfig) @@ -1108,7 +1098,7 @@ func createHeadTracker(t testing.TB, ethClient *evmclimocks.Client, config headt } } -func createHeadTrackerWithChecker(t *testing.T, ethClient *evmclimocks.Client, config headtracker.Config, htConfig headtracker.HeadTrackerConfig, orm headtracker.ORM, checker httypes.HeadTrackable) *headTrackerUniverse { +func createHeadTrackerWithChecker(t *testing.T, ethClient *evmclimocks.Client, config commontypes.Config, htConfig commontypes.HeadTrackerConfig, orm headtracker.ORM, checker httypes.HeadTrackable) *headTrackerUniverse { lggr, ob := logger.TestObserved(t, zap.DebugLevel) hb := headtracker.NewHeadBroadcaster(lggr) hs := headtracker.NewHeadSaver(lggr, orm, config, htConfig) @@ -1146,7 +1136,7 @@ func (u *headTrackerUniverse) Backfill(ctx context.Context, head, finalizedHead func (u *headTrackerUniverse) Start(t *testing.T) { u.mu.Lock() defer u.mu.Unlock() - ctx := testutils.Context(t) + ctx := tests.Context(t) require.NoError(t, u.headBroadcaster.Start(ctx)) require.NoError(t, u.headTracker.Start(ctx)) require.NoError(t, u.mailMon.Start(ctx)) @@ -1155,7 +1145,7 @@ func (u *headTrackerUniverse) Start(t *testing.T) { g.Eventually(func() bool { report := u.headBroadcaster.HealthReport() return !slices.ContainsFunc(maps.Values(report), func(e error) bool { return e != nil }) - }, 5*time.Second, testutils.TestInterval).Should(gomega.Equal(true)) + }, 5*time.Second, tests.TestInterval).Should(gomega.Equal(true)) t.Cleanup(func() { u.Stop(t) @@ -1175,3 +1165,99 @@ func (u *headTrackerUniverse) Stop(t *testing.T) { } func ptr[T any](t T) *T { return &t } + +// headBuffer - stores heads in sequence, with increasing timestamps +type headBuffer struct { + t *testing.T + Heads []*evmtypes.Head +} + +func NewHeadBuffer(t *testing.T) *headBuffer { + return &headBuffer{ + t: t, + Heads: make([]*evmtypes.Head, 0), + } +} + +func (hb *headBuffer) Append(head *evmtypes.Head) { + cloned := &evmtypes.Head{ + Number: head.Number, + Hash: head.Hash, + ParentHash: head.ParentHash, + Parent: head.Parent, + Timestamp: time.Unix(int64(len(hb.Heads)), 0), + EVMChainID: head.EVMChainID, + } + hb.Heads = append(hb.Heads, cloned) +} + +type blocks struct { + t *testing.T + Hashes []common.Hash + mHashes map[int64]common.Hash + Heads map[int64]*evmtypes.Head +} + +func (b *blocks) Head(number uint64) *evmtypes.Head { + return b.Heads[int64(number)] +} + +func NewBlocks(t *testing.T, numHashes int) *blocks { + hashes := make([]common.Hash, 0) + heads := make(map[int64]*evmtypes.Head) + for i := int64(0); i < int64(numHashes); i++ { + hash := testutils.NewHash() + hashes = append(hashes, hash) + + heads[i] = &evmtypes.Head{Hash: hash, Number: i, Timestamp: time.Unix(i, 0), EVMChainID: ubig.New(testutils.FixtureChainID)} + if i > 0 { + parent := heads[i-1] + heads[i].Parent = parent + heads[i].ParentHash = parent.Hash + } + } + + hashesMap := make(map[int64]common.Hash) + for i := 0; i < len(hashes); i++ { + hashesMap[int64(i)] = hashes[i] + } + + return &blocks{ + t: t, + Hashes: hashes, + mHashes: hashesMap, + Heads: heads, + } +} + +func (b *blocks) ForkAt(t *testing.T, blockNum int64, numHashes int) *blocks { + forked := NewBlocks(t, len(b.Heads)+numHashes) + if _, exists := forked.Heads[blockNum]; !exists { + t.Fatalf("Not enough length for block num: %v", blockNum) + } + + for i := int64(0); i < blockNum; i++ { + forked.Heads[i] = b.Heads[i] + } + + forked.Heads[blockNum].ParentHash = b.Heads[blockNum].ParentHash + forked.Heads[blockNum].Parent = b.Heads[blockNum].Parent + return forked +} + +func (b *blocks) NewHead(number uint64) *evmtypes.Head { + parentNumber := number - 1 + parent, ok := b.Heads[int64(parentNumber)] + if !ok { + b.t.Fatalf("Can't find parent block at index: %v", parentNumber) + } + head := &evmtypes.Head{ + Number: parent.Number + 1, + Hash: testutils.NewHash(), + ParentHash: parent.Hash, + Parent: parent, + Timestamp: time.Unix(parent.Number+1, 0), + EVMChainID: ubig.New(testutils.FixtureChainID), + } + return head +} diff --git a/core/chains/evm/headtracker/heads_test.go b/core/chains/evm/headtracker/heads_test.go index 2f468e0f541..6c02c528ba2 100644 --- a/core/chains/evm/headtracker/heads_test.go +++ b/core/chains/evm/headtracker/heads_test.go @@ -10,28 +10,28 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" ) func TestHeads_LatestHead(t *testing.T) { t.Parallel() heads := headtracker.NewHeads() - heads.AddHeads(cltest.Head(100), cltest.Head(200), cltest.Head(300)) + heads.AddHeads(testutils.Head(100), testutils.Head(200), testutils.Head(300)) latest := heads.LatestHead() require.NotNil(t, latest) require.Equal(t, int64(300), latest.Number) - heads.AddHeads(cltest.Head(250)) + heads.AddHeads(testutils.Head(250)) latest = heads.LatestHead() require.NotNil(t, latest) require.Equal(t, int64(300), latest.Number) - heads.AddHeads(cltest.Head(400)) + heads.AddHeads(testutils.Head(400)) latest = heads.LatestHead() require.NotNil(t, latest) require.Equal(t, int64(400), latest.Number) @@ -41,9 +41,9 @@ func TestHeads_HeadByHash(t *testing.T) { t.Parallel() var testHeads = []*evmtypes.Head{ - cltest.Head(100), - cltest.Head(200), - cltest.Head(300), + testutils.Head(100), + testutils.Head(200), + testutils.Head(300), } heads := headtracker.NewHeads() heads.AddHeads(testHeads...) @@ -62,10 +62,10 @@ func TestHeads_Count(t *testing.T) { heads := headtracker.NewHeads() require.Zero(t, heads.Count()) - heads.AddHeads(cltest.Head(100), cltest.Head(200), cltest.Head(300)) + heads.AddHeads(testutils.Head(100), testutils.Head(200), testutils.Head(300)) require.Equal(t, 3, heads.Count()) - heads.AddHeads(cltest.Head(400)) + heads.AddHeads(testutils.Head(400)) require.Equal(t, 4, heads.Count()) } diff --git a/core/chains/evm/headtracker/mocks/config.go b/core/chains/evm/headtracker/mocks/config.go deleted file mode 100644 index fea7d4629b5..00000000000 --- a/core/chains/evm/headtracker/mocks/config.go +++ /dev/null @@ -1,82 +0,0 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. - -package mocks - -import ( - time "time" - - mock "github.com/stretchr/testify/mock" -) - -// Config is an autogenerated mock type for the Config type -type Config struct { - mock.Mock -} - -// BlockEmissionIdleWarningThreshold provides a mock function with given fields: -func (_m *Config) BlockEmissionIdleWarningThreshold() time.Duration { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for BlockEmissionIdleWarningThreshold") - } - - var r0 time.Duration - if rf, ok := ret.Get(0).(func() time.Duration); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(time.Duration) - } - - return r0 -} - -// FinalityDepth provides a mock function with given fields: -func (_m *Config) FinalityDepth() uint32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for FinalityDepth") - } - - var r0 uint32 - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - return r0 -} - -// FinalityTagEnabled provides a mock function with given fields: -func (_m *Config) FinalityTagEnabled() bool { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for FinalityTagEnabled") - } - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// NewConfig creates a new instance of Config. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewConfig(t interface { - mock.TestingT - Cleanup(func()) -}) *Config { - mock := &Config{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/chains/evm/headtracker/mocks/head_trackable.go b/core/chains/evm/headtracker/mocks/head_trackable.go new file mode 100644 index 00000000000..b74e0d34ff4 --- /dev/null +++ b/core/chains/evm/headtracker/mocks/head_trackable.go @@ -0,0 +1,23 @@ +package mocks + +import ( + "context" + "sync/atomic" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" +) + +// MockHeadTrackable allows you to mock HeadTrackable +type MockHeadTrackable struct { + onNewHeadCount atomic.Int32 +} + +// OnNewLongestChain increases the OnNewLongestChainCount count by one +func (m *MockHeadTrackable) OnNewLongestChain(context.Context, *types.Head) { + m.onNewHeadCount.Add(1) +} + +// OnNewLongestChainCount returns the count of new heads, safely. +func (m *MockHeadTrackable) OnNewLongestChainCount() int32 { + return m.onNewHeadCount.Load() +} diff --git a/core/chains/evm/headtracker/orm_test.go b/core/chains/evm/headtracker/orm_test.go index ba164511c19..225722e27e1 100644 --- a/core/chains/evm/headtracker/orm_test.go +++ b/core/chains/evm/headtracker/orm_test.go @@ -7,9 +7,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" ) @@ -17,22 +18,22 @@ func TestORM_IdempotentInsertHead(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - orm := headtracker.NewORM(cltest.FixtureChainID, db) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) // Returns nil when inserting first head - head := cltest.Head(0) - require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), head)) + head := testutils.Head(0) + require.NoError(t, orm.IdempotentInsertHead(tests.Context(t), head)) // Head is inserted - foundHead, err := orm.LatestHead(testutils.Context(t)) + foundHead, err := orm.LatestHead(tests.Context(t)) require.NoError(t, err) assert.Equal(t, head.Hash, foundHead.Hash) // Returns nil when inserting same head again - require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), head)) + require.NoError(t, orm.IdempotentInsertHead(tests.Context(t), head)) // Head is still inserted - foundHead, err = orm.LatestHead(testutils.Context(t)) + foundHead, err = orm.LatestHead(tests.Context(t)) require.NoError(t, err) assert.Equal(t, head.Hash, foundHead.Hash) } @@ -41,20 +42,20 @@ func TestORM_TrimOldHeads(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - orm := headtracker.NewORM(cltest.FixtureChainID, db) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) for i := 0; i < 10; i++ { - head := cltest.Head(i) - require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), head)) + head := testutils.Head(i) + require.NoError(t, orm.IdempotentInsertHead(tests.Context(t), head)) } - uncleHead := cltest.Head(5) - require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), uncleHead)) + uncleHead := testutils.Head(5) + require.NoError(t, orm.IdempotentInsertHead(tests.Context(t), uncleHead)) - err := orm.TrimOldHeads(testutils.Context(t), 5) + err := orm.TrimOldHeads(tests.Context(t), 5) require.NoError(t, err) - heads, err := orm.LatestHeads(testutils.Context(t), 0) + heads, err := orm.LatestHeads(tests.Context(t), 0) require.NoError(t, err) // uncle block was loaded too @@ -68,18 +69,18 @@ func TestORM_HeadByHash(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - orm := headtracker.NewORM(cltest.FixtureChainID, db) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) var hash common.Hash for i := 0; i < 10; i++ { - head := cltest.Head(i) + head := testutils.Head(i) if i == 5 { hash = head.Hash } - require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), head)) + require.NoError(t, orm.IdempotentInsertHead(tests.Context(t), head)) } - head, err := orm.HeadByHash(testutils.Context(t), hash) + head, err := orm.HeadByHash(tests.Context(t), hash) require.NoError(t, err) require.Equal(t, hash, head.Hash) require.Equal(t, int64(5), head.Number) @@ -89,10 +90,10 @@ func TestORM_HeadByHash_NotFound(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - orm := headtracker.NewORM(cltest.FixtureChainID, db) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) - hash := cltest.Head(123).Hash - head, err := orm.HeadByHash(testutils.Context(t), hash) + hash := testutils.Head(123).Hash + head, err := orm.HeadByHash(tests.Context(t), hash) require.Nil(t, head) require.NoError(t, err) @@ -102,9 +103,9 @@ func TestORM_LatestHeads_NoRows(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - orm := headtracker.NewORM(cltest.FixtureChainID, db) + orm := headtracker.NewORM(*testutils.FixtureChainID, db) - heads, err := orm.LatestHeads(testutils.Context(t), 100) + heads, err := orm.LatestHeads(tests.Context(t), 100) require.Zero(t, len(heads)) require.NoError(t, err) diff --git a/core/chains/evm/log/helpers_test.go b/core/chains/evm/log/helpers_test.go index 7f216e8545e..df0d54680cd 100644 --- a/core/chains/evm/log/helpers_test.go +++ b/core/chains/evm/log/helpers_test.go @@ -29,6 +29,7 @@ import ( evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" + evmtestutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/config" @@ -48,12 +49,12 @@ type broadcasterHelper struct { t *testing.T lb log.BroadcasterInTest db *sqlx.DB - mockEth *evmtest.MockEth + mockEth *evmtestutils.MockEth globalConfig config.AppConfig config evmconfig.ChainScopedConfig // each received channel corresponds to one eth subscription - chchRawLogs chan evmtest.RawSub[types.Log] + chchRawLogs chan evmtestutils.RawSub[types.Log] toUnsubscribe []func() pipelineHelper cltest.JobPipelineV2TestHelper } @@ -69,7 +70,7 @@ func newBroadcasterHelper(t *testing.T, blockHeight int64, timesSubscribe int, f FilterLogsResult: filterLogsResult, } - chchRawLogs := make(chan evmtest.RawSub[types.Log], timesSubscribe) + chchRawLogs := make(chan evmtestutils.RawSub[types.Log], timesSubscribe) mockEth := newMockEthClient(t, chchRawLogs, blockHeight, expectedCalls) helper := newBroadcasterHelperWithEthClient(t, mockEth.EthClient, nil, overridesFn) helper.chchRawLogs = chchRawLogs @@ -361,15 +362,15 @@ type mockEthClientExpectedCalls struct { FilterLogsResult []types.Log } -func newMockEthClient(t *testing.T, chchRawLogs chan<- evmtest.RawSub[types.Log], blockHeight int64, expectedCalls mockEthClientExpectedCalls) *evmtest.MockEth { +func newMockEthClient(t *testing.T, chchRawLogs chan<- evmtestutils.RawSub[types.Log], blockHeight int64, expectedCalls mockEthClientExpectedCalls) *evmtestutils.MockEth { ethClient := evmclimocks.NewClient(t) - mockEth := &evmtest.MockEth{EthClient: ethClient} + mockEth := &evmtestutils.MockEth{EthClient: ethClient} mockEth.EthClient.On("ConfiguredChainID", mock.Anything).Return(&cltest.FixtureChainID) mockEth.EthClient.On("SubscribeFilterLogs", mock.Anything, mock.Anything, mock.Anything). Return( func(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) ethereum.Subscription { sub := mockEth.NewSub(t) - chchRawLogs <- evmtest.NewRawSub(ch, sub.Err()) + chchRawLogs <- evmtestutils.NewRawSub(ch, sub.Err()) return sub }, func(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) error { diff --git a/core/chains/evm/log/integration_test.go b/core/chains/evm/log/integration_test.go index 6c06be93dbe..6e63d5ec774 100644 --- a/core/chains/evm/log/integration_test.go +++ b/core/chains/evm/log/integration_test.go @@ -23,6 +23,7 @@ import ( evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" + evmtestutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" evmtypes "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/generated" @@ -81,7 +82,7 @@ func TestBroadcaster_ResubscribesOnAddOrRemoveContract(t *testing.T) { FilterLogs: backfillTimes, } - chchRawLogs := make(chan evmtest.RawSub[types.Log], backfillTimes) + chchRawLogs := make(chan evmtestutils.RawSub[types.Log], backfillTimes) mockEth := newMockEthClient(t, chchRawLogs, blockHeight, expectedCalls) helper := newBroadcasterHelperWithEthClient(t, mockEth.EthClient, cltest.Head(lastStoredBlockHeight), nil) helper.mockEth = mockEth @@ -147,7 +148,7 @@ func TestBroadcaster_BackfillOnNodeStartAndOnReplay(t *testing.T) { FilterLogs: 2, } - chchRawLogs := make(chan evmtest.RawSub[types.Log], backfillTimes) + chchRawLogs := make(chan evmtestutils.RawSub[types.Log], backfillTimes) mockEth := newMockEthClient(t, chchRawLogs, blockHeight, expectedCalls) helper := newBroadcasterHelperWithEthClient(t, mockEth.EthClient, cltest.Head(lastStoredBlockHeight), nil) helper.mockEth = mockEth @@ -204,7 +205,7 @@ func TestBroadcaster_ReplaysLogs(t *testing.T) { blocks.LogOnBlockNum(7, contract.Address()), } - mockEth := newMockEthClient(t, make(chan evmtest.RawSub[types.Log], 4), blockHeight, mockEthClientExpectedCalls{ + mockEth := newMockEthClient(t, make(chan evmtestutils.RawSub[types.Log], 4), blockHeight, mockEthClientExpectedCalls{ FilterLogs: 4, FilterLogsResult: sentLogs, }) @@ -409,7 +410,7 @@ func TestBroadcaster_ShallowBackfillOnNodeStart(t *testing.T) { FilterLogs: backfillTimes, } - chchRawLogs := make(chan evmtest.RawSub[types.Log], backfillTimes) + chchRawLogs := make(chan evmtestutils.RawSub[types.Log], backfillTimes) mockEth := newMockEthClient(t, chchRawLogs, blockHeight, expectedCalls) helper := newBroadcasterHelperWithEthClient(t, mockEth.EthClient, cltest.Head(lastStoredBlockHeight), func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].BlockBackfillSkip = ptr(true) @@ -459,7 +460,7 @@ func TestBroadcaster_BackfillInBatches(t *testing.T) { FilterLogs: expectedBatches, } - chchRawLogs := make(chan evmtest.RawSub[types.Log], backfillTimes) + chchRawLogs := make(chan evmtestutils.RawSub[types.Log], backfillTimes) mockEth := newMockEthClient(t, chchRawLogs, blockHeight, expectedCalls) helper := newBroadcasterHelperWithEthClient(t, mockEth.EthClient, cltest.Head(lastStoredBlockHeight), func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].LogBackfillBatchSize = ptr(uint32(batchSize)) @@ -532,7 +533,7 @@ func TestBroadcaster_BackfillALargeNumberOfLogs(t *testing.T) { FilterLogsResult: backfilledLogs, } - chchRawLogs := make(chan evmtest.RawSub[types.Log], backfillTimes) + chchRawLogs := make(chan evmtestutils.RawSub[types.Log], backfillTimes) mockEth := newMockEthClient(t, chchRawLogs, blockHeight, expectedCalls) helper := newBroadcasterHelperWithEthClient(t, mockEth.EthClient, cltest.Head(lastStoredBlockHeight), func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].LogBackfillBatchSize = ptr(batchSize) @@ -1012,8 +1013,8 @@ func TestBroadcaster_Register_ResubscribesToMostRecentlySeenBlock(t *testing.T) contract1 = newMockContract(t) contract2 = newMockContract(t) ) - mockEth := &evmtest.MockEth{EthClient: ethClient} - chchRawLogs := make(chan evmtest.RawSub[types.Log], backfillTimes) + mockEth := &evmtestutils.MockEth{EthClient: ethClient} + chchRawLogs := make(chan evmtestutils.RawSub[types.Log], backfillTimes) chStarted := make(chan struct{}) ethClient.On("ConfiguredChainID", mock.Anything).Return(&cltest.FixtureChainID) ethClient.On("SubscribeFilterLogs", mock.Anything, mock.Anything, mock.Anything). @@ -1021,7 +1022,7 @@ func TestBroadcaster_Register_ResubscribesToMostRecentlySeenBlock(t *testing.T) func(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) ethereum.Subscription { defer close(chStarted) sub := mockEth.NewSub(t) - chchRawLogs <- evmtest.NewRawSub(ch, sub.Err()) + chchRawLogs <- evmtestutils.NewRawSub(ch, sub.Err()) return sub }, func(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) error { @@ -1034,7 +1035,7 @@ func TestBroadcaster_Register_ResubscribesToMostRecentlySeenBlock(t *testing.T) Return( func(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) ethereum.Subscription { sub := mockEth.NewSub(t) - chchRawLogs <- evmtest.NewRawSub(ch, sub.Err()) + chchRawLogs <- evmtestutils.NewRawSub(ch, sub.Err()) return sub }, func(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) error { @@ -1530,14 +1531,14 @@ func TestBroadcaster_BroadcastsWithZeroConfirmations(t *testing.T) { gm := gomega.NewWithT(t) ethClient := evmclimocks.NewClient(t) - mockEth := &evmtest.MockEth{EthClient: ethClient} + mockEth := &evmtestutils.MockEth{EthClient: ethClient} ethClient.On("ConfiguredChainID").Return(big.NewInt(0)).Maybe() - logsChCh := make(chan evmtest.RawSub[types.Log]) + logsChCh := make(chan evmtestutils.RawSub[types.Log]) ethClient.On("SubscribeFilterLogs", mock.Anything, mock.Anything, mock.Anything). Return( func(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) ethereum.Subscription { sub := mockEth.NewSub(t) - logsChCh <- evmtest.NewRawSub(ch, sub.Err()) + logsChCh <- evmtestutils.NewRawSub(ch, sub.Err()) return sub }, func(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) error { diff --git a/core/chains/evm/testutils/client.go b/core/chains/evm/testutils/client.go index 31ad23eeb81..89c97b01e6d 100644 --- a/core/chains/evm/testutils/client.go +++ b/core/chains/evm/testutils/client.go @@ -7,14 +7,18 @@ import ( "net/http/httptest" "net/url" "sync" + "sync/atomic" "testing" "time" + "github.com/ethereum/go-ethereum" "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" evmclmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" ) @@ -196,3 +200,68 @@ func (ts *testWSServer) newWSHandler(chainID *big.Int, callback JSONRPCHandler) } } } + +type MockEth struct { + EthClient *evmclmocks.Client + CheckFilterLogs func(int64, int64) + + subsMu sync.RWMutex + subs []*commonmocks.Subscription + errChs []chan error + subscribeCalls atomic.Int32 + unsubscribeCalls atomic.Int32 +} + +func (m *MockEth) SubscribeCallCount() int32 { + return m.subscribeCalls.Load() +} + +func (m *MockEth) UnsubscribeCallCount() int32 { + return m.unsubscribeCalls.Load() +} + +func (m *MockEth) NewSub(t *testing.T) ethereum.Subscription { + m.subscribeCalls.Add(1) + sub := commonmocks.NewSubscription(t) + errCh := make(chan error) + sub.On("Err"). + Return(func() <-chan error { return errCh }).Maybe() + sub.On("Unsubscribe"). + Run(func(mock.Arguments) { + m.unsubscribeCalls.Add(1) + close(errCh) + }).Return().Maybe() + m.subsMu.Lock() + m.subs = append(m.subs, sub) + m.errChs = append(m.errChs, errCh) + m.subsMu.Unlock() + return sub +} + +func (m *MockEth) SubsErr(err error) { + m.subsMu.Lock() + defer m.subsMu.Unlock() + for _, errCh := range m.errChs { + errCh <- err + } +} + +type RawSub[T any] struct { + ch chan<- T + err <-chan error +} + +func NewRawSub[T any](ch chan<- T, err <-chan error) RawSub[T] { + return RawSub[T]{ch: ch, err: err} +} + +func (r *RawSub[T]) CloseCh() { + close(r.ch) +} + +func (r *RawSub[T]) TrySend(t T) { + select { + case <-r.err: + case r.ch <- t: + } +} diff --git a/core/chains/evm/testutils/evmtypes.go b/core/chains/evm/testutils/evmtypes.go index 5f9ae83d1b7..eda284284ba 100644 --- a/core/chains/evm/testutils/evmtypes.go +++ b/core/chains/evm/testutils/evmtypes.go @@ -35,6 +35,16 @@ func NewAddress() common.Address { return common.BytesToAddress(randomBytes(20)) } +// NewHash return random Keccak256 +func NewHash() common.Hash { + b := make([]byte, 32) + _, err := rand.Read(b) + if err != nil { + panic(err) + } + return common.BytesToHash(b) +} + func randomBytes(n int) []byte { b := make([]byte, n) _, err := rand.Read(b) diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 34bae098ef8..5212b29fce3 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -1419,31 +1419,6 @@ func NewBlocks(t *testing.T, numHashes int) *Blocks { } } -// HeadBuffer - stores heads in sequence, with increasing timestamps -type HeadBuffer struct { - t *testing.T - Heads []*evmtypes.Head -} - -func NewHeadBuffer(t *testing.T) *HeadBuffer { - return &HeadBuffer{ - t: t, - Heads: make([]*evmtypes.Head, 0), - } -} - -func (hb *HeadBuffer) Append(head *evmtypes.Head) { - cloned := &evmtypes.Head{ - Number: head.Number, - Hash: head.Hash, - ParentHash: head.ParentHash, - Parent: head.Parent, - Timestamp: time.Unix(int64(len(hb.Heads)), 0), - EVMChainID: head.EVMChainID, - } - hb.Heads = append(hb.Heads, cloned) -} - type HeadTrackableFunc func(context.Context, *evmtypes.Head) func (fn HeadTrackableFunc) OnNewLongestChain(ctx context.Context, head *evmtypes.Head) { diff --git a/core/internal/cltest/mocks.go b/core/internal/cltest/mocks.go index 2d7c0afc20a..9e0ee2f3f20 100644 --- a/core/internal/cltest/mocks.go +++ b/core/internal/cltest/mocks.go @@ -7,7 +7,6 @@ import ( "net/http" "net/http/httptest" "sync" - "sync/atomic" "testing" "time" @@ -272,21 +271,6 @@ type MockCronEntry struct { Function func() } -// MockHeadTrackable allows you to mock HeadTrackable -type MockHeadTrackable struct { - onNewHeadCount atomic.Int32 -} - -// OnNewLongestChain increases the OnNewLongestChainCount count by one -func (m *MockHeadTrackable) OnNewLongestChain(context.Context, *evmtypes.Head) { - m.onNewHeadCount.Add(1) -} - -// OnNewLongestChainCount returns the count of new heads, safely. -func (m *MockHeadTrackable) OnNewLongestChainCount() int32 { - return m.onNewHeadCount.Load() -} - // NeverSleeper is a struct that never sleeps type NeverSleeper struct{} diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index 26e7d5eae56..046f21b7f7d 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -46,6 +46,7 @@ import ( "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/forwarders" + evmtestutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" @@ -1263,7 +1264,7 @@ func TestIntegration_BlockHistoryEstimator(t *testing.T) { ethClient := cltest.NewEthMocks(t) ethClient.On("ConfiguredChainID").Return(big.NewInt(client.NullClientChainID)).Maybe() - chchNewHeads := make(chan evmtest.RawSub[*evmtypes.Head], 1) + chchNewHeads := make(chan evmtestutils.RawSub[*evmtypes.Head], 1) db := pgtest.NewSqlxDB(t) kst := cltest.NewKeyStore(t, db) @@ -1292,12 +1293,12 @@ func TestIntegration_BlockHistoryEstimator(t *testing.T) { h41 := evmtypes.Head{Hash: b41.Hash, ParentHash: h40.Hash, Number: 41, EVMChainID: evmChainID} h42 := evmtypes.Head{Hash: b42.Hash, ParentHash: h41.Hash, Number: 42, EVMChainID: evmChainID} - mockEth := &evmtest.MockEth{EthClient: ethClient} + mockEth := &evmtestutils.MockEth{EthClient: ethClient} ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). Return( func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { sub := mockEth.NewSub(t) - chchNewHeads <- evmtest.NewRawSub(ch, sub.Err()) + chchNewHeads <- evmtestutils.NewRawSub(ch, sub.Err()) return sub }, func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, @@ -1329,7 +1330,7 @@ func TestIntegration_BlockHistoryEstimator(t *testing.T) { for _, re := range cc.Slice() { servicetest.Run(t, re) } - var newHeads evmtest.RawSub[*evmtypes.Head] + var newHeads evmtestutils.RawSub[*evmtypes.Head] select { case newHeads = <-chchNewHeads: case <-time.After(10 * time.Second): diff --git a/core/internal/testutils/evmtest/evmtest.go b/core/internal/testutils/evmtest/evmtest.go index d435252089a..40f5eee994f 100644 --- a/core/internal/testutils/evmtest/evmtest.go +++ b/core/internal/testutils/evmtest/evmtest.go @@ -5,12 +5,9 @@ import ( "math/big" "slices" "sync" - "sync/atomic" "testing" - "github.com/ethereum/go-ethereum" "github.com/pelletier/go-toml/v2" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" @@ -20,7 +17,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox/mailboxtest" - commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -302,68 +298,3 @@ func NewEthClientMockWithDefaultChain(t *testing.T) *evmclimocks.Client { c.On("IsL2").Return(false).Maybe() return c } - -type MockEth struct { - EthClient *evmclimocks.Client - CheckFilterLogs func(int64, int64) - - subsMu sync.RWMutex - subs []*commonmocks.Subscription - errChs []chan error - subscribeCalls atomic.Int32 - unsubscribeCalls atomic.Int32 -} - -func (m *MockEth) SubscribeCallCount() int32 { - return m.subscribeCalls.Load() -} - -func (m *MockEth) UnsubscribeCallCount() int32 { - return m.unsubscribeCalls.Load() -} - -func (m *MockEth) NewSub(t *testing.T) ethereum.Subscription { - m.subscribeCalls.Add(1) - sub := commonmocks.NewSubscription(t) - errCh := make(chan error) - sub.On("Err"). - Return(func() <-chan error { return errCh }).Maybe() - sub.On("Unsubscribe"). - Run(func(mock.Arguments) { - m.unsubscribeCalls.Add(1) - close(errCh) - }).Return().Maybe() - m.subsMu.Lock() - m.subs = append(m.subs, sub) - m.errChs = append(m.errChs, errCh) - m.subsMu.Unlock() - return sub -} - -func (m *MockEth) SubsErr(err error) { - m.subsMu.Lock() - defer m.subsMu.Unlock() - for _, errCh := range m.errChs { - errCh <- err - } -} - -type RawSub[T any] struct { - ch chan<- T - err <-chan error -} - -func NewRawSub[T any](ch chan<- T, err <-chan error) RawSub[T] { - return RawSub[T]{ch: ch, err: err} -} - -func (r *RawSub[T]) CloseCh() { - close(r.ch) -} - -func (r *RawSub[T]) TrySend(t T) { - select { - case <-r.err: - case r.ch <- t: - } -} From b521061438b8d25b094db0deeb73cb7b5afa43d8 Mon Sep 17 00:00:00 2001 From: Aaron Lu <50029043+aalu1418@users.noreply.github.com> Date: Wed, 5 Jun 2024 12:00:16 -0600 Subject: [PATCH 11/15] add blockhistory estimator config (#13348) --- core/config/docs/chains-solana.toml | 2 ++ core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- core/services/chainlink/config_test.go | 2 ++ core/services/chainlink/testdata/config-full.toml | 1 + .../chainlink/testdata/config-multi-chain-effective.toml | 2 ++ core/web/resolver/testdata/config-full.toml | 1 + .../resolver/testdata/config-multi-chain-effective.toml | 2 ++ core/web/solana_chains_controller_test.go | 1 + docs/CONFIG.md | 7 +++++++ go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- integration-tests/load/go.mod | 2 +- integration-tests/load/go.sum | 4 ++-- 16 files changed, 30 insertions(+), 12 deletions(-) diff --git a/core/config/docs/chains-solana.toml b/core/config/docs/chains-solana.toml index d0aae876b43..9376445061a 100644 --- a/core/config/docs/chains-solana.toml +++ b/core/config/docs/chains-solana.toml @@ -34,6 +34,8 @@ ComputeUnitPriceMin = 0 # Default ComputeUnitPriceDefault = 0 # Default # FeeBumpPeriod is the amount of time before a tx is retried with a fee bump FeeBumpPeriod = '3s' # Default +# BlockHistoryPollPeriod is the rate to poll for blocks in the block history fee estimator +BlockHistoryPollPeriod = '5s' # Default [[Solana.Nodes]] # Name is a unique (per-chain) identifier for this node. diff --git a/core/scripts/go.mod b/core/scripts/go.mod index c32e2a9f128..6efa07574bf 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -259,7 +259,7 @@ require ( github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240603183542-58ab0f61506b // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240605170242-555ff582f36a // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240531021326-99118e47f696 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 06ad66caeaf..986fa9e7f40 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1193,8 +1193,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h1:MD80ZRCTvxxJ8PBmhtrKoTnky8cVNYrCrIBLVRbrOM0= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240603183542-58ab0f61506b h1:pJQ3K0kUdOUICIOZBLAN+MaEfloG2t6Nc1+ve057pYc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240603183542-58ab0f61506b/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240605170242-555ff582f36a h1:/QIQciKjhZy+iBB74WG4pP88O2FwS1KZtSXO5COTTgI= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240605170242-555ff582f36a/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240531021326-99118e47f696 h1:h1E87+z+JcUEfvbJVF56SnZA/YUFE5ewUE61MaR/Ewg= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240531021326-99118e47f696/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= github.com/smartcontractkit/chainlink-vrf v0.0.0-20240222010609-cd67d123c772 h1:LQmRsrzzaYYN3wEU1l5tWiccznhvbyGnu2N+wHSXZAo= diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index c2b2e288039..20c2dd8d57e 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -657,6 +657,7 @@ func TestConfig_Marshal(t *testing.T) { ComputeUnitPriceMin: ptr[uint64](10), ComputeUnitPriceDefault: ptr[uint64](100), FeeBumpPeriod: commoncfg.MustNewDuration(time.Minute), + BlockHistoryPollPeriod: commoncfg.MustNewDuration(time.Minute), }, Nodes: []*solcfg.Node{ {Name: ptr("primary"), URL: commoncfg.MustParseURL("http://solana.web")}, @@ -1143,6 +1144,7 @@ ComputeUnitPriceMax = 1000 ComputeUnitPriceMin = 10 ComputeUnitPriceDefault = 100 FeeBumpPeriod = '1m0s' +BlockHistoryPollPeriod = '1m0s' [[Solana.Nodes]] Name = 'primary' diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 4e61353543e..103f068c8e8 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -441,6 +441,7 @@ ComputeUnitPriceMax = 1000 ComputeUnitPriceMin = 10 ComputeUnitPriceDefault = 100 FeeBumpPeriod = '1m0s' +BlockHistoryPollPeriod = '1m0s' [[Solana.Nodes]] Name = 'primary' diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 9cf4d4a5d43..05dfe21a5d8 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -579,6 +579,7 @@ ComputeUnitPriceMax = 1000 ComputeUnitPriceMin = 0 ComputeUnitPriceDefault = 0 FeeBumpPeriod = '3s' +BlockHistoryPollPeriod = '5s' [[Solana.Nodes]] Name = 'primary' @@ -601,6 +602,7 @@ ComputeUnitPriceMax = 1000 ComputeUnitPriceMin = 0 ComputeUnitPriceDefault = 0 FeeBumpPeriod = '3s' +BlockHistoryPollPeriod = '5s' [[Solana.Nodes]] Name = 'secondary' diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index ea8c16d8741..7e1b2291106 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -440,6 +440,7 @@ ComputeUnitPriceMax = 1000000 ComputeUnitPriceMin = 0 ComputeUnitPriceDefault = 0 FeeBumpPeriod = '3s' +BlockHistoryPollPeriod = '5s' [[Solana.Nodes]] Name = 'primary' diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 9cf4d4a5d43..05dfe21a5d8 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -579,6 +579,7 @@ ComputeUnitPriceMax = 1000 ComputeUnitPriceMin = 0 ComputeUnitPriceDefault = 0 FeeBumpPeriod = '3s' +BlockHistoryPollPeriod = '5s' [[Solana.Nodes]] Name = 'primary' @@ -601,6 +602,7 @@ ComputeUnitPriceMax = 1000 ComputeUnitPriceMin = 0 ComputeUnitPriceDefault = 0 FeeBumpPeriod = '3s' +BlockHistoryPollPeriod = '5s' [[Solana.Nodes]] Name = 'secondary' diff --git a/core/web/solana_chains_controller_test.go b/core/web/solana_chains_controller_test.go index a2ac904b783..048a3790b1a 100644 --- a/core/web/solana_chains_controller_test.go +++ b/core/web/solana_chains_controller_test.go @@ -57,6 +57,7 @@ ComputeUnitPriceMax = 1000 ComputeUnitPriceMin = 0 ComputeUnitPriceDefault = 0 FeeBumpPeriod = '3s' +BlockHistoryPollPeriod = '5s' Nodes = [] `, } diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 6c94b1d1f62..07a26246d4e 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -8157,6 +8157,7 @@ ComputeUnitPriceMax = 1000 # Default ComputeUnitPriceMin = 0 # Default ComputeUnitPriceDefault = 0 # Default FeeBumpPeriod = '3s' # Default +BlockHistoryPollPeriod = '5s' # Default ``` @@ -8263,6 +8264,12 @@ FeeBumpPeriod = '3s' # Default ``` FeeBumpPeriod is the amount of time before a tx is retried with a fee bump +### BlockHistoryPollPeriod +```toml +BlockHistoryPollPeriod = '5s' # Default +``` +BlockHistoryPollPeriod is the rate to poll for blocks in the block history fee estimator + ## Solana.Nodes ```toml [[Solana.Nodes]] diff --git a/go.mod b/go.mod index 309ecf16f01..a85094b9952 100644 --- a/go.mod +++ b/go.mod @@ -76,7 +76,7 @@ require ( github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240603183542-58ab0f61506b + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240605170242-555ff582f36a github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240531021326-99118e47f696 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c diff --git a/go.sum b/go.sum index f1b2762c452..4ea255f8270 100644 --- a/go.sum +++ b/go.sum @@ -1179,8 +1179,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h1:MD80ZRCTvxxJ8PBmhtrKoTnky8cVNYrCrIBLVRbrOM0= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240603183542-58ab0f61506b h1:pJQ3K0kUdOUICIOZBLAN+MaEfloG2t6Nc1+ve057pYc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240603183542-58ab0f61506b/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240605170242-555ff582f36a h1:/QIQciKjhZy+iBB74WG4pP88O2FwS1KZtSXO5COTTgI= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240605170242-555ff582f36a/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240531021326-99118e47f696 h1:h1E87+z+JcUEfvbJVF56SnZA/YUFE5ewUE61MaR/Ewg= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240531021326-99118e47f696/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 968c6f04d02..c272b0a615d 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -378,7 +378,7 @@ require ( github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240603183542-58ab0f61506b // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240605170242-555ff582f36a // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240531021326-99118e47f696 // indirect github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index e3a5c0a8e80..1ec43c7be45 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1520,8 +1520,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h1:MD80ZRCTvxxJ8PBmhtrKoTnky8cVNYrCrIBLVRbrOM0= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240603183542-58ab0f61506b h1:pJQ3K0kUdOUICIOZBLAN+MaEfloG2t6Nc1+ve057pYc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240603183542-58ab0f61506b/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240605170242-555ff582f36a h1:/QIQciKjhZy+iBB74WG4pP88O2FwS1KZtSXO5COTTgI= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240605170242-555ff582f36a/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240531021326-99118e47f696 h1:h1E87+z+JcUEfvbJVF56SnZA/YUFE5ewUE61MaR/Ewg= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240531021326-99118e47f696/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= github.com/smartcontractkit/chainlink-testing-framework v1.28.17 h1:zezoeiG3GUGW1T2+genS/HD1BvRJwC3rqFnFTFNB9aY= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index f12267228f3..81d7167fc54 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -367,7 +367,7 @@ require ( github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240603183542-58ab0f61506b // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240605170242-555ff582f36a // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240531021326-99118e47f696 // indirect github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 // indirect github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 4dc25c5b838..5292cba860f 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1510,8 +1510,8 @@ github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 h1:MD80ZRCTvxxJ8PBmhtrKoTnky8cVNYrCrIBLVRbrOM0= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917/go.mod h1:jwVxhctE6BgLOSSsVq9wbREpZ8Ev34H+UBxeUhESZRs= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240603183542-58ab0f61506b h1:pJQ3K0kUdOUICIOZBLAN+MaEfloG2t6Nc1+ve057pYc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240603183542-58ab0f61506b/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240605170242-555ff582f36a h1:/QIQciKjhZy+iBB74WG4pP88O2FwS1KZtSXO5COTTgI= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240605170242-555ff582f36a/go.mod h1:QqcZSwLgEIn7YraAIRmomnBMAuVFephiHrIWVlkWbFI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240531021326-99118e47f696 h1:h1E87+z+JcUEfvbJVF56SnZA/YUFE5ewUE61MaR/Ewg= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240531021326-99118e47f696/go.mod h1:OiWUTrrpSLLTMh7FINWjEh6mmDJCVPaC4yEsDCVaWdU= github.com/smartcontractkit/chainlink-testing-framework v1.28.17 h1:zezoeiG3GUGW1T2+genS/HD1BvRJwC3rqFnFTFNB9aY= From 5293b70c8cd56fe5190fd3739ff5b58f58f22d20 Mon Sep 17 00:00:00 2001 From: frank zhu Date: Wed, 5 Jun 2024 12:47:15 -0700 Subject: [PATCH 12/15] fix: slack-notify conditional for ci-core for race tests failures (#13432) --- .github/workflows/ci-core.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index ee44c1bc46f..9f6fbd8fae4 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -170,7 +170,7 @@ jobs: env: OUTPUT_FILE: ./output.txt USE_TEE: false - CL_DATABASE_URL: ${{ env.DB_URL }} + CL_DATABASE_URL: ${{ env.DB_URL }} run: ./tools/bin/${{ matrix.type.cmd }} ./... - name: Print Filtered Test Results if: ${{ failure() && matrix.type.cmd == 'go_core_tests' && needs.filter.outputs.changes == 'true' }} @@ -187,6 +187,8 @@ jobs: else echo "post_to_slack=false" >> $GITHUB_OUTPUT fi + echo "github.event_name: ${{ github.event_name }}" + echo "github.ref: ${{ github.ref }}" - name: Print postgres logs if: ${{ always() && needs.filter.outputs.changes == 'true' }} run: docker compose logs postgres | tee ../../../postgres_logs.txt @@ -203,7 +205,7 @@ jobs: ./coverage.txt ./postgres_logs.txt - name: Notify Slack - if: ${{ failure() && steps.print-races.outputs.post_to_slack == 'true' && matrix.type.cmd == 'go_core_race_tests' && (github.event_name == 'merge_group' || github.event.branch == 'develop') && needs.filter.outputs.changes == 'true' }} + if: ${{ failure() && steps.print-races.outputs.post_to_slack == 'true' && matrix.type.cmd == 'go_core_race_tests' && (github.event_name == 'merge_group' || github.ref == 'refs/heads/develop') && needs.filter.outputs.changes == 'true' }} uses: slackapi/slack-github-action@6c661ce58804a1a20f6dc5fbee7f0381b469e001 # v1.25.0 env: SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} From 58420d6a67a47db16e65e9ca859633df5d6c9a81 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 5 Jun 2024 15:05:34 -0500 Subject: [PATCH 13/15] bump chainlink-common for SLOW QUERY log updates and custom thresholds (#13358) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- core/services/pipeline/runner.go | 15 +++++++++++++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- integration-tests/load/go.mod | 2 +- integration-tests/load/go.sum | 4 ++-- 9 files changed, 25 insertions(+), 14 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 6efa07574bf..f8faa82fabe 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -21,7 +21,7 @@ require ( github.com/prometheus/client_golang v1.17.0 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605181819-316b5eb82110 github.com/smartcontractkit/chainlink-vrf v0.0.0-20240222010609-cd67d123c772 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 986fa9e7f40..fd3bb134503 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1185,8 +1185,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 h1:3Rl4N7u9RRYmXY96ZLoaHVGONXZ8lL4Kc027dFjD46g= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605181819-316b5eb82110 h1:skCp4kAmd0H+/sqiCzpwXKxkSWpf1NsdIUuN73nuz/Y= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605181819-316b5eb82110/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= diff --git a/core/services/pipeline/runner.go b/core/services/pipeline/runner.go index 708a2b07636..e816e96dad8 100644 --- a/core/services/pipeline/runner.go +++ b/core/services/pipeline/runner.go @@ -219,8 +219,18 @@ func (r *runner) OnRunFinished(fn func(*Run)) { r.runFinished = fn } -// github.com/smartcontractkit/libocr/offchainreporting2plus/internal/protocol.ReportingPluginTimeoutWarningGracePeriod -var overtime = 100 * time.Millisecond +var ( + // github.com/smartcontractkit/libocr/offchainreporting2plus/internal/protocol.ReportingPluginTimeoutWarningGracePeriod + overtime = 100 * time.Millisecond + overtimeThresholds = sqlutil.LogThresholds{ + Warn: func(timeout time.Duration) time.Duration { + return timeout - (timeout / 5) // 80% + }, + Error: func(timeout time.Duration) time.Duration { + return timeout - (timeout / 10) // 90% + }, + } +) func init() { // undocumented escape hatch @@ -235,6 +245,7 @@ func init() { // overtimeContext returns a modified context for overtime work, since tasks are expected to keep running and return // results, even after context cancellation. func overtimeContext(ctx context.Context) (context.Context, context.CancelFunc) { + ctx = overtimeThresholds.ContextWithValue(ctx) if d, ok := ctx.Deadline(); ok { // extend deadline return context.WithDeadline(context.WithoutCancel(ctx), d.Add(overtime)) diff --git a/go.mod b/go.mod index a85094b9952..9f0c41b0b95 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chain-selectors v1.0.10 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605181819-316b5eb82110 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 diff --git a/go.sum b/go.sum index 4ea255f8270..e69d1d87f3b 100644 --- a/go.sum +++ b/go.sum @@ -1171,8 +1171,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 h1:3Rl4N7u9RRYmXY96ZLoaHVGONXZ8lL4Kc027dFjD46g= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605181819-316b5eb82110 h1:skCp4kAmd0H+/sqiCzpwXKxkSWpf1NsdIUuN73nuz/Y= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605181819-316b5eb82110/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index c272b0a615d..3906c252265 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -27,7 +27,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605181819-316b5eb82110 github.com/smartcontractkit/chainlink-testing-framework v1.28.17 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 1ec43c7be45..16994b03560 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1512,8 +1512,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 h1:3Rl4N7u9RRYmXY96ZLoaHVGONXZ8lL4Kc027dFjD46g= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605181819-316b5eb82110 h1:skCp4kAmd0H+/sqiCzpwXKxkSWpf1NsdIUuN73nuz/Y= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605181819-316b5eb82110/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 81d7167fc54..833ac48d453 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -16,7 +16,7 @@ require ( github.com/rs/zerolog v1.30.0 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605181819-316b5eb82110 github.com/smartcontractkit/chainlink-testing-framework v1.28.17 github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20240214231432-4ad5eb95178c github.com/smartcontractkit/chainlink/v2 v2.9.0-beta0.0.20240216210048-da02459ddad8 diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 5292cba860f..15c58ac5f41 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1502,8 +1502,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1 h1:3Rl4N7u9RRYmXY96ZLoaHVGONXZ8lL4Kc027dFjD46g= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605021851-ddaad4797fe1/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605181819-316b5eb82110 h1:skCp4kAmd0H+/sqiCzpwXKxkSWpf1NsdIUuN73nuz/Y= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240605181819-316b5eb82110/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= From 72c34c02517994e230cdf9ecc128aa955f015a2c Mon Sep 17 00:00:00 2001 From: Ryan Tinianov Date: Wed, 5 Jun 2024 16:18:08 -0400 Subject: [PATCH 14/15] Name unnamed return values Fi where i is the index of the returned parameter. If that name is taken, append _X until it's not taken (#13433) --- core/services/relay/evm/types/codec_entry.go | 14 ++++++- .../relay/evm/types/codec_entry_test.go | 37 +++++++++++++++++-- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/core/services/relay/evm/types/codec_entry.go b/core/services/relay/evm/types/codec_entry.go index a63460b0897..b42bf9c0b08 100644 --- a/core/services/relay/evm/types/codec_entry.go +++ b/core/services/relay/evm/types/codec_entry.go @@ -133,13 +133,23 @@ func (entry *codecEntry) Init() (err error) { if err != nil { return err } + allowRename := false if len(arg.Name) == 0 { - return fmt.Errorf("%w: empty field names are not supported for multiple returns", commontypes.ErrInvalidType) + arg.Name = fmt.Sprintf("F%d", i) + allowRename = true } name := strings.ToUpper(arg.Name[:1]) + arg.Name[1:] if seenNames[name] { - return fmt.Errorf("%w: duplicate field name %s, after ToCamelCase", commontypes.ErrInvalidConfig, name) + if !allowRename { + return fmt.Errorf("%w: duplicate field name %s, after ToCamelCase", commontypes.ErrInvalidConfig, name) + } + for { + name = name + "_X" + if !seenNames[name] { + break + } + } } seenNames[name] = true native[i] = reflect.StructField{Name: name, Type: nativeArg} diff --git a/core/services/relay/evm/types/codec_entry_test.go b/core/services/relay/evm/types/codec_entry_test.go index d4a0dd8edea..da1d4cc22a5 100644 --- a/core/services/relay/evm/types/codec_entry_test.go +++ b/core/services/relay/evm/types/codec_entry_test.go @@ -209,11 +209,40 @@ func TestCodecEntry(t *testing.T) { assertHaveSameStructureAndNames(t, iNative.Type(), entry.CheckedType()) }) - t.Run("Multiple unnamed parameters are not supported", func(t *testing.T) { - anyType, err := abi.NewType("int16[3]", "", []abi.ArgumentMarshaling{}) + t.Run("Unnamed parameters are named after their locations", func(t *testing.T) { + // use different types to make sure that the right fields have the right types. + anyType1, err := abi.NewType("int64", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + anyType2, err := abi.NewType("int32", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + entry := NewCodecEntry(abi.Arguments{{Name: "", Type: anyType1}, {Name: "", Type: anyType2}}, nil, nil) + assert.NoError(t, entry.Init()) + ct := entry.CheckedType() + require.Equal(t, 2, ct.NumField()) + f0 := ct.Field(0) + assert.Equal(t, "F0", f0.Name) + assert.Equal(t, reflect.TypeOf((*int64)(nil)), f0.Type) + f1 := ct.Field(1) + assert.Equal(t, "F1", f1.Name) + assert.Equal(t, reflect.TypeOf((*int32)(nil)), f1.Type) + }) + + t.Run("Unnamed parameters adds _Xes at the end if their location name is taken", func(t *testing.T) { + // use different types to make sure that the right fields have the right types. + anyType1, err := abi.NewType("int64", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + anyType2, err := abi.NewType("int32", "", []abi.ArgumentMarshaling{}) require.NoError(t, err) - entry := NewCodecEntry(abi.Arguments{{Name: "", Type: anyType}, {Name: "", Type: anyType}}, nil, nil) - assert.True(t, errors.Is(entry.Init(), commontypes.ErrInvalidType)) + entry := NewCodecEntry(abi.Arguments{{Name: "F1", Type: anyType1}, {Name: "", Type: anyType2}}, nil, nil) + assert.NoError(t, entry.Init()) + ct := entry.CheckedType() + require.Equal(t, 2, ct.NumField()) + f0 := ct.Field(0) + assert.Equal(t, "F1", f0.Name) + assert.Equal(t, reflect.TypeOf((*int64)(nil)), f0.Type) + f1 := ct.Field(1) + assert.Equal(t, "F1_X", f1.Name) + assert.Equal(t, reflect.TypeOf((*int32)(nil)), f1.Type) }) t.Run("Multiple abi arguments with the same name returns an error", func(t *testing.T) { From 9e0dfbaae31e36d160ed49606bb648aa6d59c6a3 Mon Sep 17 00:00:00 2001 From: Njegos Railic Date: Wed, 5 Jun 2024 22:41:48 +0200 Subject: [PATCH 15/15] Bumping setup-github-token to version 0.2.1 (#13418) --- .github/workflows/solidity-wrappers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/solidity-wrappers.yml b/.github/workflows/solidity-wrappers.yml index 2622a29842f..bbd7ac0c670 100644 --- a/.github/workflows/solidity-wrappers.yml +++ b/.github/workflows/solidity-wrappers.yml @@ -55,7 +55,7 @@ jobs: working-directory: ./contracts - name: Assume role capable of dispatching action - uses: smartcontractkit/.github/actions/setup-github-token@9e7cc0779934cae4a9028b8588c9adb64d8ce68c # setup-github-token@0.1.0 + uses: smartcontractkit/.github/actions/setup-github-token@ef78fa97bf3c77de6563db1175422703e9e6674f # setup-github-token@0.2.1 id: get-gh-token with: aws-role-arn: ${{ secrets.AWS_OIDC_CHAINLINK_CI_AUTO_PR_TOKEN_ISSUER_ROLE_ARN }}