From 23f767fd3c02b1c047f072c843d4d8cfbac7e271 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:50:18 +0100 Subject: [PATCH 01/16] BCF-2854 Jfpc pipeline cache (#12094) * Add rough implementation for data source caching * Add JuelsPerFeeCoinCacheDuration validation * Make sonar happy * Set EATelemetry for dataSource in observe to reuse executeRun for cache * Fix races in inMemoryDataSourceCache * Fix JuelsPerFeeCoinCacheDuration validation * Shorten NewInMemoryDataSourceCache signature * Infer juels fee per coin caching from non-zero duration instead of bool * Change how jfpc cache handles cache updater cron errors * Change jfpc cache duration validation * Set default jfpc data source expiry duration to 5minutes if not set * Improve jfpc updateCache err handling to account for result errors * Make juels fee per coin get cached by default, and add cache tests * Make jfpc cache updater run on start * Increase jfpc data source timeout * Change log priority in cache updater * Fix test flakiness * Change log severity in jfpc cache updater * Add cfg option to disable jfpc caching * Remove ptr from inMemoryDataSourceCache mutex * Fix JuelsPerFeeCoinCacheDuration encoding type to use models.Interval * Add tests for validating juels per fee coin data source cache config * Add changelog * Fix typo in test * Fix log --- .../features/ocr2/features_ocr2_test.go | 2 + core/services/feeds/service_test.go | 3 + .../ocr2/plugins/median/config/config.go | 16 +- .../ocr2/plugins/median/config/config_test.go | 55 +++++-- core/services/ocr2/plugins/median/services.go | 14 +- core/services/ocrcommon/data_source.go | 138 +++++++++++++++--- core/services/ocrcommon/data_source_test.go | 49 +++++++ docs/CHANGELOG.md | 1 + 8 files changed, 242 insertions(+), 36 deletions(-) diff --git a/core/internal/features/ocr2/features_ocr2_test.go b/core/internal/features/ocr2/features_ocr2_test.go index e089970951b..72d70720c65 100644 --- a/core/internal/features/ocr2/features_ocr2_test.go +++ b/core/internal/features/ocr2/features_ocr2_test.go @@ -487,6 +487,7 @@ juelsPerFeeCoinSource = """ answer1 [type=median index=0]; """ +juelsPerFeeCoinCacheDuration = "1m" `, ocrContractAddress, kbs[i].ID(), transmitters[i], fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i, blockBeforeConfig.Number().Int64(), chainReaderSpec, fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i)) require.NoError(t, err) err = apps[i].AddJobV2(testutils.Context(t), &ocrJob) @@ -839,6 +840,7 @@ juelsPerFeeCoinSource = """ answer1 [type=median index=0]; """ +juelsPerFeeCoinCacheDuration = "1m" `, ocrContractAddress, kbs[i].ID(), transmitters[i], fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i, fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i)) require.NoError(t, err) err = apps[i].AddJobV2(testutils.Context(t), &ocrJob) diff --git a/core/services/feeds/service_test.go b/core/services/feeds/service_test.go index d822cd9787d..be3620efac5 100644 --- a/core/services/feeds/service_test.go +++ b/core/services/feeds/service_test.go @@ -124,6 +124,7 @@ ds1_multiply [type=multiply times=1.23]; ds1 -> ds1_parse -> ds1_multiply -> answer1; answer1 [type=median index=0]; """ +juelsPerFeeCoinCacheDuration = "1m" ` const BootstrapTestSpecTemplate = ` type = "bootstrap" @@ -2186,6 +2187,7 @@ ds1_multiply [type=multiply times=1.23]; ds1 -> ds1_parse -> ds1_multiply -> answer1; answer1 [type=median index=0]; """ +juelsPerFeeCoinCacheDuration = "30s" ` defn2 = ` name = 'LINK / ETH | version 3 | contract 0x0000000000000000000000000000000000000000' @@ -2215,6 +2217,7 @@ ds1_multiply [type=multiply times=1.23]; ds1 -> ds1_parse -> ds1_multiply -> answer1; answer1 [type=median index=0]; """ +juelsPerFeeCoinCacheDuration = "20m" ` jp = &feeds.JobProposal{ diff --git a/core/services/ocr2/plugins/median/config/config.go b/core/services/ocr2/plugins/median/config/config.go index c86c75f7200..218f7491094 100644 --- a/core/services/ocr2/plugins/median/config/config.go +++ b/core/services/ocr2/plugins/median/config/config.go @@ -4,14 +4,19 @@ package config import ( + "time" + "github.com/pkg/errors" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/store/models" ) // The PluginConfig struct contains the custom arguments needed for the Median plugin. type PluginConfig struct { - JuelsPerFeeCoinPipeline string `json:"juelsPerFeeCoinSource"` + JuelsPerFeeCoinPipeline string `json:"juelsPerFeeCoinSource"` + JuelsPerFeeCoinCacheDuration models.Interval `json:"juelsPerFeeCoinCacheDuration"` + JuelsPerFeeCoinCacheDisabled bool `json:"juelsPerFeeCoinCacheDisabled"` } // ValidatePluginConfig validates the arguments for the Median plugin. @@ -20,5 +25,14 @@ func ValidatePluginConfig(config PluginConfig) error { return errors.Wrap(err, "invalid juelsPerFeeCoinSource pipeline") } + // unset duration defaults later + if config.JuelsPerFeeCoinCacheDuration != 0 { + if config.JuelsPerFeeCoinCacheDuration.Duration() < time.Second*30 { + return errors.Errorf("juelsPerFeeCoinSource cache duration: %s is below 30 second minimum", config.JuelsPerFeeCoinCacheDuration.Duration().String()) + } else if config.JuelsPerFeeCoinCacheDuration.Duration() > time.Minute*20 { + return errors.Errorf("juelsPerFeeCoinSource cache duration: %s is above 20 minute maximum", config.JuelsPerFeeCoinCacheDuration.Duration().String()) + } + } + return nil } diff --git a/core/services/ocr2/plugins/median/config/config_test.go b/core/services/ocr2/plugins/median/config/config_test.go index 6e137992af5..e0dcd4f9203 100644 --- a/core/services/ocr2/plugins/median/config/config_test.go +++ b/core/services/ocr2/plugins/median/config/config_test.go @@ -1,22 +1,55 @@ package config import ( + "fmt" "testing" + "time" "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func TestValidatePluginConfig(t *testing.T) { - for _, s := range []struct { - name string - pipeline string - }{ - {"empty", ""}, - {"blank", " "}, - {"foo", "foo"}, - } { - t.Run(s.name, func(t *testing.T) { - assert.Error(t, ValidatePluginConfig(PluginConfig{JuelsPerFeeCoinPipeline: s.pipeline})) - }) + type testCase struct { + name string + pipeline string + cacheDuration models.Interval + expectedError error } + + t.Run("pipeline validation", func(t *testing.T) { + for _, tc := range []testCase{ + {"empty pipeline", "", models.Interval(time.Minute), fmt.Errorf("invalid juelsPerFeeCoinSource pipeline: empty pipeline")}, + {"blank pipeline", " ", models.Interval(time.Minute), fmt.Errorf("invalid juelsPerFeeCoinSource pipeline: empty pipeline")}, + {"foo pipeline", "foo", models.Interval(time.Minute), fmt.Errorf("invalid juelsPerFeeCoinSource pipeline: UnmarshalTaskFromMap: unknown task type: \"\"")}, + } { + t.Run(tc.name, func(t *testing.T) { + assert.EqualError(t, ValidatePluginConfig(PluginConfig{JuelsPerFeeCoinPipeline: tc.pipeline}), tc.expectedError.Error()) + }) + } + }) + + t.Run("cache duration validation", func(t *testing.T) { + for _, tc := range []testCase{ + {"cache duration below minimum", `ds1 [type=bridge name=voter_turnout];`, models.Interval(time.Second * 29), fmt.Errorf("juelsPerFeeCoinSource cache duration: 29s is below 30 second minimum")}, + {"cache duration above maximum", `ds1 [type=bridge name=voter_turnout];`, models.Interval(time.Minute*20 + time.Second), fmt.Errorf("juelsPerFeeCoinSource cache duration: 20m1s is above 20 minute maximum")}, + } { + t.Run(tc.name, func(t *testing.T) { + assert.EqualError(t, ValidatePluginConfig(PluginConfig{JuelsPerFeeCoinPipeline: tc.pipeline, JuelsPerFeeCoinCacheDuration: tc.cacheDuration}), tc.expectedError.Error()) + }) + } + }) + + t.Run("valid values", func(t *testing.T) { + for _, s := range []testCase{ + {"valid 0 cache duration and valid pipeline", `ds1 [type=bridge name=voter_turnout];`, 0, nil}, + {"valid duration and valid pipeline", `ds1 [type=bridge name=voter_turnout];`, models.Interval(time.Second * 30), nil}, + {"valid duration and valid pipeline", `ds1 [type=bridge name=voter_turnout];`, models.Interval(time.Minute * 20), nil}, + } { + t.Run(s.name, func(t *testing.T) { + assert.Nil(t, ValidatePluginConfig(PluginConfig{JuelsPerFeeCoinPipeline: s.pipeline})) + }) + } + }) } diff --git a/core/services/ocr2/plugins/median/services.go b/core/services/ocr2/plugins/median/services.go index 2a874ff1756..74690565e94 100644 --- a/core/services/ocr2/plugins/median/services.go +++ b/core/services/ocr2/plugins/median/services.go @@ -113,18 +113,26 @@ func NewMedianServices(ctx context.Context, } } - dataSource, juelsPerFeeCoinSource := ocrcommon.NewDataSourceV2(pipelineRunner, + dataSource := ocrcommon.NewDataSourceV2(pipelineRunner, jb, *jb.PipelineSpec, lggr, runSaver, - chEnhancedTelem, - ), ocrcommon.NewInMemoryDataSource(pipelineRunner, jb, pipeline.Spec{ + chEnhancedTelem) + + juelsPerFeeCoinSource := ocrcommon.NewInMemoryDataSource(pipelineRunner, jb, pipeline.Spec{ ID: jb.ID, DotDagSource: pluginConfig.JuelsPerFeeCoinPipeline, CreatedAt: time.Now(), }, lggr) + if !pluginConfig.JuelsPerFeeCoinCacheDisabled { + lggr.Infof("juelsPerFeeCoin data source caching is enabled") + if juelsPerFeeCoinSource, err = ocrcommon.NewInMemoryDataSourceCache(juelsPerFeeCoinSource, pluginConfig.JuelsPerFeeCoinCacheDuration.Duration()); err != nil { + return nil, err + } + } + if cmdName := env.MedianPlugin.Cmd.Get(); cmdName != "" { // use unique logger names so we can use it to register a loop medianLggr := lggr.Named("Median").Named(spec.ContractID).Named(spec.GetID()) diff --git a/core/services/ocrcommon/data_source.go b/core/services/ocrcommon/data_source.go index 706376fbfd1..3dd7e4eebc0 100644 --- a/core/services/ocrcommon/data_source.go +++ b/core/services/ocrcommon/data_source.go @@ -2,6 +2,7 @@ package ocrcommon import ( "context" + errjoin "errors" "math/big" "sync" "time" @@ -98,8 +99,42 @@ func NewInMemoryDataSource(pr pipeline.Runner, jb job.Job, spec pipeline.Spec, l } } +const defaultInMemoryCacheDuration = time.Minute * 5 + +func NewInMemoryDataSourceCache(ds median.DataSource, cacheExpiryDuration time.Duration) (median.DataSource, error) { + inMemoryDS, ok := ds.(*inMemoryDataSource) + if !ok { + return nil, errors.Errorf("unsupported data source type: %T, only inMemoryDataSource supported", ds) + } + + if cacheExpiryDuration == 0 { + cacheExpiryDuration = defaultInMemoryCacheDuration + } + + dsCache := &inMemoryDataSourceCache{ + cacheExpiration: cacheExpiryDuration, + inMemoryDataSource: inMemoryDS, + } + go func() { dsCache.updater() }() + return dsCache, nil +} + var _ ocr1types.DataSource = (*dataSource)(nil) +func setEATelemetry(ds *inMemoryDataSource, finalResult pipeline.FinalResult, trrs pipeline.TaskRunResults, timestamp ObservationTimestamp) { + promSetFinalResultMetrics(ds, &finalResult) + promSetBridgeParseMetrics(ds, &trrs) + if ShouldCollectEnhancedTelemetry(&ds.jb) { + EnqueueEnhancedTelem(ds.chEnhancedTelemetry, EnhancedTelemetryData{ + TaskRunResults: trrs, + FinalResults: finalResult, + RepTimestamp: timestamp, + }) + } else { + ds.lggr.Infow("Enhanced telemetry is disabled for job", "job", ds.jb.Name) + } +} + func (ds *inMemoryDataSource) updateAnswer(a *big.Int) { ds.mu.Lock() defer ds.mu.Unlock() @@ -117,7 +152,7 @@ func (ds *inMemoryDataSource) currentAnswer() (*big.Int, *big.Int) { // The context passed in here has a timeout of (ObservationTimeout + ObservationGracePeriod). // Upon context cancellation, its expected that we return any usable values within ObservationGracePeriod. -func (ds *inMemoryDataSource) executeRun(ctx context.Context, timestamp ObservationTimestamp) (*pipeline.Run, pipeline.FinalResult, error) { +func (ds *inMemoryDataSource) executeRun(ctx context.Context) (*pipeline.Run, pipeline.TaskRunResults, error) { md, err := bridges.MarshalBridgeMetaData(ds.currentAnswer()) if err != nil { ds.lggr.Warnw("unable to attach metadata for run", "err", err) @@ -136,23 +171,10 @@ func (ds *inMemoryDataSource) executeRun(ctx context.Context, timestamp Observat run, trrs, err := ds.pipelineRunner.ExecuteRun(ctx, ds.spec, vars, ds.lggr) if err != nil { - return nil, pipeline.FinalResult{}, errors.Wrapf(err, "error executing run for spec ID %v", ds.spec.ID) - } - finalResult := trrs.FinalResult(ds.lggr) - promSetBridgeParseMetrics(ds, &trrs) - promSetFinalResultMetrics(ds, &finalResult) - - if ShouldCollectEnhancedTelemetry(&ds.jb) { - EnqueueEnhancedTelem(ds.chEnhancedTelemetry, EnhancedTelemetryData{ - TaskRunResults: trrs, - FinalResults: finalResult, - RepTimestamp: timestamp, - }) - } else { - ds.lggr.Infow("Enhanced telemetry is disabled for job", "job", ds.jb.Name) + return nil, pipeline.TaskRunResults{}, errors.Wrapf(err, "error executing run for spec ID %v", ds.spec.ID) } - return run, finalResult, err + return run, trrs, err } // parse uses the FinalResult into a big.Int and stores it in the bridge metadata @@ -176,19 +198,90 @@ func (ds *inMemoryDataSource) parse(finalResult pipeline.FinalResult) (*big.Int, // Observe without saving to DB func (ds *inMemoryDataSource) Observe(ctx context.Context, timestamp ocr2types.ReportTimestamp) (*big.Int, error) { - _, finalResult, err := ds.executeRun(ctx, ObservationTimestamp{ + _, trrs, err := ds.executeRun(ctx) + if err != nil { + return nil, err + } + + finalResult := trrs.FinalResult(ds.lggr) + setEATelemetry(ds, finalResult, trrs, ObservationTimestamp{ Round: timestamp.Round, Epoch: timestamp.Epoch, ConfigDigest: timestamp.ConfigDigest.Hex(), }) - if err != nil { - return nil, err - } + return ds.parse(finalResult) } +// inMemoryDataSourceCache is a time based cache wrapper for inMemoryDataSource. +// If cache update is overdue Observe defaults to standard inMemoryDataSource behaviour. +type inMemoryDataSourceCache struct { + *inMemoryDataSource + cacheExpiration time.Duration + mu sync.RWMutex + latestUpdateErr error + latestTrrs pipeline.TaskRunResults + latestResult pipeline.FinalResult +} + +// updater periodically updates data source cache. +func (ds *inMemoryDataSourceCache) updater() { + ticker := time.NewTicker(ds.cacheExpiration) + for ; true; <-ticker.C { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + if err := ds.updateCache(ctx); err != nil { + ds.lggr.Infow("failed to update cache", "err", err) + } + cancel() + } +} + +func (ds *inMemoryDataSourceCache) updateCache(ctx context.Context) error { + ds.mu.Lock() + defer ds.mu.Unlock() + _, ds.latestTrrs, ds.latestUpdateErr = ds.executeRun(ctx) + if ds.latestUpdateErr != nil { + return errors.Wrapf(ds.latestUpdateErr, "error executing run for spec ID %v", ds.spec.ID) + } else if ds.latestTrrs.FinalResult(ds.lggr).HasErrors() { + ds.latestUpdateErr = errjoin.Join(ds.latestTrrs.FinalResult(ds.lggr).AllErrors...) + return errors.Wrapf(ds.latestUpdateErr, "error executing run for spec ID %v", ds.spec.ID) + } + + ds.latestResult = ds.latestTrrs.FinalResult(ds.lggr) + return nil +} + +func (ds *inMemoryDataSourceCache) get(ctx context.Context) (pipeline.FinalResult, pipeline.TaskRunResults) { + ds.mu.RLock() + // updater didn't error, so we know that the latestResult is fresh + if ds.latestUpdateErr == nil { + defer ds.mu.RUnlock() + return ds.latestResult, ds.latestTrrs + } + ds.mu.RUnlock() + + if err := ds.updateCache(ctx); err != nil { + ds.lggr.Errorw("failed to update cache, returning stale result now", "err", err) + } + + ds.mu.RLock() + defer ds.mu.RUnlock() + return ds.latestResult, ds.latestTrrs +} + +func (ds *inMemoryDataSourceCache) Observe(ctx context.Context, timestamp ocr2types.ReportTimestamp) (*big.Int, error) { + latestResult, latestTrrs := ds.get(ctx) + setEATelemetry(ds.inMemoryDataSource, latestResult, latestTrrs, ObservationTimestamp{ + Round: timestamp.Round, + Epoch: timestamp.Epoch, + ConfigDigest: timestamp.ConfigDigest.Hex(), + }) + + return ds.parse(latestResult) +} + func (ds *dataSourceBase) observe(ctx context.Context, timestamp ObservationTimestamp) (*big.Int, error) { - run, finalResult, err := ds.inMemoryDataSource.executeRun(ctx, timestamp) + run, trrs, err := ds.inMemoryDataSource.executeRun(ctx) if err != nil { return nil, err } @@ -201,6 +294,9 @@ func (ds *dataSourceBase) observe(ctx context.Context, timestamp ObservationTime // a db write block that. ds.saver.Save(run) + finalResult := trrs.FinalResult(ds.lggr) + setEATelemetry(&ds.inMemoryDataSource, finalResult, trrs, timestamp) + return ds.inMemoryDataSource.parse(finalResult) } diff --git a/core/services/ocrcommon/data_source_test.go b/core/services/ocrcommon/data_source_test.go index 3ffc6b31e8c..2e4e07973a0 100644 --- a/core/services/ocrcommon/data_source_test.go +++ b/core/services/ocrcommon/data_source_test.go @@ -1,8 +1,10 @@ package ocrcommon_test import ( + "fmt" "math/big" "testing" + "time" promtestutil "github.com/prometheus/client_golang/prometheus/testutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" @@ -44,6 +46,53 @@ func Test_InMemoryDataSource(t *testing.T) { assert.Equal(t, mockValue, val.String()) // returns expected value after pipeline run } +func Test_CachedInMemoryDataSourceErrHandling(t *testing.T) { + runner := pipelinemocks.NewRunner(t) + + ds := ocrcommon.NewInMemoryDataSource(runner, job.Job{}, pipeline.Spec{}, logger.TestLogger(t)) + dsCache, err := ocrcommon.NewInMemoryDataSourceCache(ds, time.Second*2) + require.NoError(t, err) + + changeResultValue := func(value string, returnErr, once bool) { + result := pipeline.Result{ + Value: value, + Error: nil, + } + if returnErr { + result.Error = assert.AnError + } + + call := runner.On("ExecuteRun", mock.Anything, mock.AnythingOfType("pipeline.Spec"), mock.Anything, mock.Anything). + Return(&pipeline.Run{}, pipeline.TaskRunResults{ + { + Result: result, + Task: &pipeline.HTTPTask{}, + }, + }, nil) + // last mock can't be Once or test will panic because there are logs after it finished + if once { + call.Once() + } + } + + mockVal := int64(1) + // Test if Observe notices that cache updater failed and can refresh the cache on its own + // 1. Set initial value + changeResultValue(fmt.Sprint(mockVal), false, true) + time.Sleep(time.Millisecond * 100) + val, err := dsCache.Observe(testutils.Context(t), types.ReportTimestamp{}) + require.NoError(t, err) + assert.Equal(t, mockVal, val.Int64()) + // 2. Set values again, but make it error in updater + changeResultValue(fmt.Sprint(mockVal+1), true, true) + time.Sleep(time.Second*2 + time.Millisecond*100) + // 3. Set value in between updates and call Observe (shouldn't flake because of huge wait time) + changeResultValue(fmt.Sprint(mockVal+2), false, false) + val, err = dsCache.Observe(testutils.Context(t), types.ReportTimestamp{}) + require.NoError(t, err) + assert.Equal(t, mockVal+2, val.Int64()) +} + func Test_InMemoryDataSourceWithProm(t *testing.T) { runner := pipelinemocks.NewRunner(t) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index b7c37ecb348..0d1a5cc365e 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Gas bumping logic to the `SuggestedPriceEstimator`. The bumping mechanism for this estimator refetches the price from the RPC and adds a buffer on top using the greater of `BumpPercent` and `BumpMin`. - Add preliminary support for "llo" job type (Data Streams V1) - Add `LogPrunePageSize` parameter to the EVM configuration. This parameter controls the number of logs removed during prune phase in LogPoller. Default value is 0, which deletes all logs at once - exactly how it used to work, so it doesn't require any changes on the product's side. +- Add Juels Fee Per Coin data source caching for OCR2 Feeds. Cache is time based and is turned on by default with default cache refresh of 5 minutes. Cache can be configured through pluginconfig using "juelsPerFeeCoinCacheDuration" and "juelsPerFeeCoinCacheDisabled" tags. Duration tag accepts values between "30s" and "20m" with default of "0s" that is overridden on cache startup to 5 minutes. ### Fixed From d50c3790f71d7d57bc5a4a151c7eb59119916ca5 Mon Sep 17 00:00:00 2001 From: frank zhu Date: Tue, 27 Feb 2024 10:56:15 -0800 Subject: [PATCH 02/16] chore: update otel-collector version (#12191) * update open-telemetry chart version * update otel-collector version to 0.95.0 * otel-telemetry 0.82.0 chart version --- charts/chainlink-cluster/Chart.lock | 6 +++--- charts/chainlink-cluster/Chart.yaml | 4 ++-- charts/chainlink-cluster/values.yaml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/charts/chainlink-cluster/Chart.lock b/charts/chainlink-cluster/Chart.lock index ec87bb6d24e..38e7f09705e 100644 --- a/charts/chainlink-cluster/Chart.lock +++ b/charts/chainlink-cluster/Chart.lock @@ -4,12 +4,12 @@ dependencies: version: 5.14.0 - name: opentelemetry-collector repository: https://open-telemetry.github.io/opentelemetry-helm-charts - version: 0.81.2 + version: 0.82.0 - name: tempo repository: https://grafana.github.io/helm-charts version: 1.7.2 - name: grafana repository: https://grafana.github.io/helm-charts version: 7.3.2 -digest: sha256:fb13a8a7b490fd1e388a6b082e50d65a39f44a28030d66559d4640c687a70e20 -generated: "2024-02-23T15:22:09.284013-06:00" +digest: sha256:37722063f68689c42ac1d6549ddfae4756370c1659b8ac1c0d7da8916c6fad3d +generated: "2024-02-27T11:04:29.920915-06:00" diff --git a/charts/chainlink-cluster/Chart.yaml b/charts/chainlink-cluster/Chart.yaml index dc6f390f8a8..fa45ea9bd77 100644 --- a/charts/chainlink-cluster/Chart.yaml +++ b/charts/chainlink-cluster/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: chainlink-cluster description: Chainlink nodes cluster -version: 0.3.0 +version: 0.4.0 appVersion: "2.6.0" dependencies: - name: mockserver @@ -9,7 +9,7 @@ dependencies: repository: "https://www.mock-server.com" condition: mockserver.enabled - name: opentelemetry-collector - version: "0.81.2" + version: "0.82.0" repository: "https://open-telemetry.github.io/opentelemetry-helm-charts" condition: opentelemetry-collector.enabled - name: tempo diff --git a/charts/chainlink-cluster/values.yaml b/charts/chainlink-cluster/values.yaml index 2bca739bfdf..303cec04213 100644 --- a/charts/chainlink-cluster/values.yaml +++ b/charts/chainlink-cluster/values.yaml @@ -197,7 +197,7 @@ opentelemetry-collector: mode: deployment image: repository: otel/opentelemetry-collector - tag: 0.81.2 + tag: 0.95.0 command: name: otelcol extraVolumes: From 2d9342e452bf4f57a82d0b33b9dce48a5ec8e1d8 Mon Sep 17 00:00:00 2001 From: Cedric Date: Tue, 27 Feb 2024 19:23:32 +0000 Subject: [PATCH 03/16] [KS-59] Start capability service (#12171) - Start capability service - Bump golangci-lint version - Add fatal logging to OCR3 capability start --- GNUmakefile | 2 +- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- 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 ++-- plugins/cmd/chainlink-ocr3-capability/main.go | 12 +++++++++++- 10 files changed, 24 insertions(+), 14 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 8cbd0953ee7..c68e13f8ae6 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -145,7 +145,7 @@ config-docs: ## Generate core node configuration documentation .PHONY: golangci-lint golangci-lint: ## Run golangci-lint for all issues. [ -d "./golangci-lint" ] || mkdir ./golangci-lint && \ - docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:v1.55.2 golangci-lint run --max-issues-per-linter 0 --max-same-issues 0 > ./golangci-lint/$(shell date +%Y-%m-%d_%H:%M:%S).txt + docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:v1.56.2 golangci-lint run --max-issues-per-linter 0 --max-same-issues 0 > ./golangci-lint/$(shell date +%Y-%m-%d_%H:%M:%S).txt GORELEASER_CONFIG ?= .goreleaser.yaml diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 91697eb5f9f..5039897bec8 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -20,7 +20,7 @@ require ( github.com/pelletier/go-toml/v2 v2.1.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240215150045-fe2ba71b2f0a diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 7a3f6b2f0c2..066bc9592a5 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1173,8 +1173,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.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c h1:ielGD+tVCB+irZ+nDt5VDTYJauJI88tirkLLaHWLaTs= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e h1:3fA2bobIvvfB7Ia8XQhpJRAXlm+Fbw7NZaCw4GaclIk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= diff --git a/go.mod b/go.mod index 1e0c09b06bf..6dab12267db 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,7 @@ require ( github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chain-selectors v1.0.10 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 diff --git a/go.sum b/go.sum index 13d684fd34d..e04289334ee 100644 --- a/go.sum +++ b/go.sum @@ -1168,8 +1168,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.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c h1:ielGD+tVCB+irZ+nDt5VDTYJauJI88tirkLLaHWLaTs= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e h1:3fA2bobIvvfB7Ia8XQhpJRAXlm+Fbw7NZaCw4GaclIk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= 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 cf3f065d812..caec4f79373 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -23,7 +23,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e github.com/smartcontractkit/chainlink-testing-framework v1.24.1 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 05717c79210..0340d6b31ac 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1507,8 +1507,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.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c h1:ielGD+tVCB+irZ+nDt5VDTYJauJI88tirkLLaHWLaTs= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e h1:3fA2bobIvvfB7Ia8XQhpJRAXlm+Fbw7NZaCw4GaclIk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= 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 016d8c5c91a..ab78012d982 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -15,7 +15,7 @@ require ( github.com/rs/zerolog v1.30.0 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e github.com/smartcontractkit/chainlink-testing-framework v1.24.1 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 803c56b8d71..a6517124c67 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1490,8 +1490,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.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c h1:ielGD+tVCB+irZ+nDt5VDTYJauJI88tirkLLaHWLaTs= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e h1:3fA2bobIvvfB7Ia8XQhpJRAXlm+Fbw7NZaCw4GaclIk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= diff --git a/plugins/cmd/chainlink-ocr3-capability/main.go b/plugins/cmd/chainlink-ocr3-capability/main.go index ac8bb6454f3..85767554a1c 100644 --- a/plugins/cmd/chainlink-ocr3-capability/main.go +++ b/plugins/cmd/chainlink-ocr3-capability/main.go @@ -1,6 +1,8 @@ package main import ( + "context" + "github.com/hashicorp/go-plugin" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3" @@ -19,7 +21,15 @@ func main() { s := loop.MustNewStartedServer(loggerName) defer s.Stop() - p := ocr3.NewOCR3(s.Logger, evm.NewEVMEncoder) + c := ocr3.Config{ + Logger: s.Logger, + EncoderFactory: evm.NewEVMEncoder, + } + p := ocr3.NewOCR3(c) + if err := p.Start(context.Background()); err != nil { + s.Logger.Fatal("Failed to start OCR3 capability", err) + } + defer s.Logger.ErrorIfFn(p.Close, "Failed to close") s.MustRegister(p) From 17287df41385b684e1521eafdae3987d6178939c Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com> Date: Tue, 27 Feb 2024 21:25:42 +0100 Subject: [PATCH 04/16] BCI-2450: Health check for MultiNode to ensure that node is not in syncing state (#11765) * syncing state for multinode * fix evm tests * zombie check for syncing node * Update common/client/node_fsm.go Co-authored-by: Sam * make IsSyncing check optional * regen config docs * remove merge artefact * add description to the test * fix config docs * fix RPC client mock generation * regen mocks --------- Co-authored-by: Sam --- common/client/mock_node_client_test.go | 28 ++ common/client/mock_rpc_test.go | 28 ++ common/client/node.go | 67 ++- common/client/node_fsm.go | 63 ++- common/client/node_fsm_test.go | 44 +- common/client/node_lifecycle.go | 110 +++-- common/client/node_lifecycle_test.go | 417 +++++++++++++++++- common/client/node_test.go | 6 + common/client/types.go | 2 + core/chains/evm/client/chain_client.go | 8 +- core/chains/evm/client/chain_client_test.go | 4 +- core/chains/evm/client/helpers_test.go | 20 +- core/chains/evm/client/mocks/rpc_client.go | 118 +++-- core/chains/evm/client/rpc_client.go | 35 +- .../evm/config/chain_scoped_node_pool.go | 4 + core/chains/evm/config/config.go | 1 + core/chains/evm/config/config_test.go | 2 + core/chains/evm/config/toml/config.go | 4 + .../evm/config/toml/defaults/fallback.toml | 1 + core/chains/legacyevm/chain.go | 8 +- core/config/docs/chains-evm.toml | 6 + core/internal/testutils/testutils.go | 2 + core/services/chainlink/config_test.go | 2 + .../chainlink/testdata/config-full.toml | 1 + .../config-multi-chain-effective.toml | 3 + core/web/resolver/testdata/config-full.toml | 1 + .../config-multi-chain-effective.toml | 3 + docs/CHANGELOG.md | 1 + docs/CONFIG.md | 62 +++ .../disk-based-logging-disabled.txtar | 1 + .../validate/disk-based-logging-no-dir.txtar | 1 + .../node/validate/disk-based-logging.txtar | 1 + testdata/scripts/node/validate/invalid.txtar | 1 + testdata/scripts/node/validate/valid.txtar | 1 + 34 files changed, 908 insertions(+), 148 deletions(-) diff --git a/common/client/mock_node_client_test.go b/common/client/mock_node_client_test.go index 661ad68ede5..d143ebb88a5 100644 --- a/common/client/mock_node_client_test.go +++ b/common/client/mock_node_client_test.go @@ -116,6 +116,34 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) DisconnectAll() { _m.Called() } +// IsSyncing provides a mock function with given fields: ctx +func (_m *mockNodeClient[CHAIN_ID, HEAD]) IsSyncing(ctx context.Context) (bool, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for IsSyncing") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) bool); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // SetAliveLoopSub provides a mock function with given fields: _a0 func (_m *mockNodeClient[CHAIN_ID, HEAD]) SetAliveLoopSub(_a0 types.Subscription) { _m.Called(_a0) diff --git a/common/client/mock_rpc_test.go b/common/client/mock_rpc_test.go index 465445b8092..60e0cb4b421 100644 --- a/common/client/mock_rpc_test.go +++ b/common/client/mock_rpc_test.go @@ -366,6 +366,34 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS return r0, r1 } +// IsSyncing provides a mock function with given fields: ctx +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) IsSyncing(ctx context.Context) (bool, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for IsSyncing") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) bool); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // LINKBalance provides a mock function with given fields: ctx, accountAddress, linkAddress func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (*assets.Link, error) { ret := _m.Called(ctx, accountAddress, linkAddress) diff --git a/common/client/node.go b/common/client/node.go index ce144bbca86..ee9ff5eb1c0 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -42,6 +42,7 @@ type NodeConfig interface { PollInterval() time.Duration SelectionMode() string SyncThreshold() uint32 + NodeIsSyncingEnabled() bool } //go:generate mockery --quiet --name Node --structname mockNode --filename "mock_node_test.go" --inpackage --case=underscore @@ -224,23 +225,14 @@ func (n *node[CHAIN_ID, HEAD, RPC]) start(startCtx context.Context) { } n.setState(nodeStateDialed) - if err := n.verify(startCtx); errors.Is(err, errInvalidChainID) { - n.lfcLog.Errorw("Verify failed: Node has the wrong chain ID", "err", err) - n.declareInvalidChainID() - return - } else if err != nil { - n.lfcLog.Errorw(fmt.Sprintf("Verify failed: %v", err), "err", err) - n.declareUnreachable() - return - } - - n.declareAlive() + state := n.verifyConn(startCtx, n.lfcLog) + n.declareState(state) } -// verify checks that all connections to eth nodes match the given chain ID +// verifyChainID checks that connection to the node matches the given chain ID // Not thread-safe -// Pure verify: does not mutate node "state" field. -func (n *node[CHAIN_ID, HEAD, RPC]) verify(callerCtx context.Context) (err error) { +// Pure verifyChainID: does not mutate node "state" field. +func (n *node[CHAIN_ID, HEAD, RPC]) verifyChainID(callerCtx context.Context, lggr logger.Logger) nodeState { promPoolRPCNodeVerifies.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc() promFailed := func() { promPoolRPCNodeVerifiesFailed.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc() @@ -248,29 +240,68 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verify(callerCtx context.Context) (err error st := n.State() switch st { - case nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID: + case nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID, nodeStateSyncing: default: panic(fmt.Sprintf("cannot verify node in state %v", st)) } var chainID CHAIN_ID + var err error if chainID, err = n.rpc.ChainID(callerCtx); err != nil { promFailed() - return fmt.Errorf("failed to verify chain ID for node %s: %w", n.name, err) + lggr.Errorw("Failed to verify chain ID for node", "err", err, "nodeState", n.State()) + return nodeStateUnreachable } else if chainID.String() != n.chainID.String() { promFailed() - return fmt.Errorf( + err = fmt.Errorf( "rpc ChainID doesn't match local chain ID: RPC ID=%s, local ID=%s, node name=%s: %w", chainID.String(), n.chainID.String(), n.name, errInvalidChainID, ) + lggr.Errorw("Failed to verify RPC node; remote endpoint returned the wrong chain ID", "err", err, "nodeState", n.State()) + return nodeStateInvalidChainID } promPoolRPCNodeVerifiesSuccess.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc() - return nil + return nodeStateAlive +} + +// createVerifiedConn - establishes new connection with the RPC and verifies that it's valid: chainID matches, and it's not syncing. +// Returns desired state if one of the verifications fails. Otherwise, returns nodeStateAlive. +func (n *node[CHAIN_ID, HEAD, RPC]) createVerifiedConn(ctx context.Context, lggr logger.Logger) nodeState { + if err := n.rpc.Dial(ctx); err != nil { + n.lfcLog.Errorw("Dial failed: Node is unreachable", "err", err, "nodeState", n.State()) + return nodeStateUnreachable + } + + return n.verifyConn(ctx, lggr) +} + +// verifyConn - verifies that current connection is valid: chainID matches, and it's not syncing. +// Returns desired state if one of the verifications fails. Otherwise, returns nodeStateAlive. +func (n *node[CHAIN_ID, HEAD, RPC]) verifyConn(ctx context.Context, lggr logger.Logger) nodeState { + state := n.verifyChainID(ctx, lggr) + if state != nodeStateAlive { + return state + } + + if n.nodePoolCfg.NodeIsSyncingEnabled() { + isSyncing, err := n.rpc.IsSyncing(ctx) + if err != nil { + lggr.Errorw("Unexpected error while verifying RPC node synchronization status", "err", err, "nodeState", n.State()) + return nodeStateUnreachable + } + + if isSyncing { + lggr.Errorw("Verification failed: Node is syncing", "nodeState", n.State()) + return nodeStateSyncing + } + } + + return nodeStateAlive } // disconnectAll disconnects all clients connected to the node diff --git a/common/client/node_fsm.go b/common/client/node_fsm.go index 74a5814f773..4cb020893b5 100644 --- a/common/client/node_fsm.go +++ b/common/client/node_fsm.go @@ -33,6 +33,10 @@ var ( Name: "pool_rpc_node_num_transitions_to_unusable", Help: transitionString(nodeStateUnusable), }, []string{"chainID", "nodeName"}) + promPoolRPCNodeTransitionsToSyncing = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_num_transitions_to_syncing", + Help: transitionString(nodeStateSyncing), + }, []string{"chainID", "nodeName"}) ) // nodeState represents the current state of the node @@ -57,6 +61,8 @@ func (n nodeState) String() string { return "OutOfSync" case nodeStateClosed: return "Closed" + case nodeStateSyncing: + return "Syncing" default: return fmt.Sprintf("nodeState(%d)", n) } @@ -87,6 +93,11 @@ const ( nodeStateUnusable // nodeStateClosed is after the connection has been closed and the node is at the end of its lifecycle nodeStateClosed + // nodeStateSyncing is a node that is actively back-filling blockchain. Usually, it's a newly set up node that is + // still syncing the chain. The main difference from `nodeStateOutOfSync` is that it represents state relative + // to other primary nodes configured in the MultiNode. In contrast, `nodeStateSyncing` represents the internal state of + // the node (RPC). + nodeStateSyncing // nodeStateLen tracks the number of states nodeStateLen ) @@ -144,7 +155,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToAlive(fn func()) { return } switch n.state { - case nodeStateDialed, nodeStateInvalidChainID: + case nodeStateDialed, nodeStateInvalidChainID, nodeStateSyncing: n.state = nodeStateAlive default: panic(transitionFail(n.state, nodeStateAlive)) @@ -171,7 +182,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToInSync(fn func()) { return } switch n.state { - case nodeStateOutOfSync: + case nodeStateOutOfSync, nodeStateSyncing: n.state = nodeStateAlive default: panic(transitionFail(n.state, nodeStateAlive)) @@ -222,7 +233,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToUnreachable(fn func()) { return } switch n.state { - case nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID: + case nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID, nodeStateSyncing: n.disconnectAll() n.state = nodeStateUnreachable default: @@ -231,6 +242,21 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToUnreachable(fn func()) { fn() } +func (n *node[CHAIN_ID, HEAD, RPC]) declareState(state nodeState) { + switch state { + case nodeStateInvalidChainID: + n.declareInvalidChainID() + case nodeStateUnreachable: + n.declareUnreachable() + case nodeStateSyncing: + n.declareSyncing() + case nodeStateAlive: + n.declareAlive() + default: + panic(fmt.Sprintf("%#v state declaration is not implemented", state)) + } +} + func (n *node[CHAIN_ID, HEAD, RPC]) declareInvalidChainID() { n.transitionToInvalidChainID(func() { n.lfcLog.Errorw("RPC Node has the wrong chain ID", "nodeState", n.state) @@ -247,7 +273,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToInvalidChainID(fn func()) { return } switch n.state { - case nodeStateDialed, nodeStateOutOfSync: + case nodeStateDialed, nodeStateOutOfSync, nodeStateSyncing: n.disconnectAll() n.state = nodeStateInvalidChainID default: @@ -256,6 +282,35 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToInvalidChainID(fn func()) { fn() } +func (n *node[CHAIN_ID, HEAD, RPC]) declareSyncing() { + n.transitionToSyncing(func() { + n.lfcLog.Errorw("RPC Node is syncing", "nodeState", n.state) + n.wg.Add(1) + go n.syncingLoop() + }) +} + +func (n *node[CHAIN_ID, HEAD, RPC]) transitionToSyncing(fn func()) { + promPoolRPCNodeTransitionsToSyncing.WithLabelValues(n.chainID.String(), n.name).Inc() + n.stateMu.Lock() + defer n.stateMu.Unlock() + if n.state == nodeStateClosed { + return + } + switch n.state { + case nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID: + n.disconnectAll() + n.state = nodeStateSyncing + default: + panic(transitionFail(n.state, nodeStateSyncing)) + } + + if !n.nodePoolCfg.NodeIsSyncingEnabled() { + panic("unexpected transition to nodeStateSyncing, while it's disabled") + } + fn() +} + func transitionString(state nodeState) string { return fmt.Sprintf("Total number of times node has transitioned to %s", state) } diff --git a/common/client/node_fsm_test.go b/common/client/node_fsm_test.go index 87e90846699..36cee65e09e 100644 --- a/common/client/node_fsm_test.go +++ b/common/client/node_fsm_test.go @@ -2,6 +2,7 @@ package client import ( "slices" + "strconv" "testing" "github.com/stretchr/testify/assert" @@ -23,15 +24,11 @@ func (fm *fnMock) AssertCalled(t *testing.T) { assert.Greater(t, fm.calls, 0) } -func newTestTransitionNode(t *testing.T, rpc *mockNodeClient[types.ID, Head]) testNode { - return newTestNode(t, testNodeOpts{rpc: rpc}) -} - func TestUnit_Node_StateTransitions(t *testing.T) { t.Parallel() t.Run("setState", func(t *testing.T) { - n := newTestTransitionNode(t, nil) + n := newTestNode(t, testNodeOpts{rpc: nil, config: testNodeConfig{nodeIsSyncingEnabled: true}}) assert.Equal(t, nodeStateUndialed, n.State()) n.setState(nodeStateAlive) assert.Equal(t, nodeStateAlive, n.State()) @@ -41,14 +38,14 @@ func TestUnit_Node_StateTransitions(t *testing.T) { t.Run("transitionToAlive", func(t *testing.T) { const destinationState = nodeStateAlive - allowedStates := []nodeState{nodeStateDialed, nodeStateInvalidChainID} + allowedStates := []nodeState{nodeStateDialed, nodeStateInvalidChainID, nodeStateSyncing} rpc := newMockNodeClient[types.ID, Head](t) testTransition(t, rpc, testNode.transitionToAlive, destinationState, allowedStates...) }) t.Run("transitionToInSync", func(t *testing.T) { const destinationState = nodeStateAlive - allowedStates := []nodeState{nodeStateOutOfSync} + allowedStates := []nodeState{nodeStateOutOfSync, nodeStateSyncing} rpc := newMockNodeClient[types.ID, Head](t) testTransition(t, rpc, testNode.transitionToInSync, destinationState, allowedStates...) }) @@ -61,22 +58,40 @@ func TestUnit_Node_StateTransitions(t *testing.T) { }) t.Run("transitionToUnreachable", func(t *testing.T) { const destinationState = nodeStateUnreachable - allowedStates := []nodeState{nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID} + allowedStates := []nodeState{nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID, nodeStateSyncing} rpc := newMockNodeClient[types.ID, Head](t) rpc.On("DisconnectAll").Times(len(allowedStates)) testTransition(t, rpc, testNode.transitionToUnreachable, destinationState, allowedStates...) }) t.Run("transitionToInvalidChain", func(t *testing.T) { const destinationState = nodeStateInvalidChainID - allowedStates := []nodeState{nodeStateDialed, nodeStateOutOfSync} + allowedStates := []nodeState{nodeStateDialed, nodeStateOutOfSync, nodeStateSyncing} rpc := newMockNodeClient[types.ID, Head](t) rpc.On("DisconnectAll").Times(len(allowedStates)) testTransition(t, rpc, testNode.transitionToInvalidChainID, destinationState, allowedStates...) }) + t.Run("transitionToSyncing", func(t *testing.T) { + const destinationState = nodeStateSyncing + allowedStates := []nodeState{nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID} + rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("DisconnectAll").Times(len(allowedStates)) + testTransition(t, rpc, testNode.transitionToSyncing, destinationState, allowedStates...) + }) + t.Run("transitionToSyncing panics if nodeIsSyncing is disabled", func(t *testing.T) { + rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("DisconnectAll").Once() + node := newTestNode(t, testNodeOpts{rpc: rpc}) + node.setState(nodeStateDialed) + fn := new(fnMock) + defer fn.AssertNotCalled(t) + assert.PanicsWithValue(t, "unexpected transition to nodeStateSyncing, while it's disabled", func() { + node.transitionToSyncing(fn.Fn) + }) + }) } func testTransition(t *testing.T, rpc *mockNodeClient[types.ID, Head], transition func(node testNode, fn func()), destinationState nodeState, allowedStates ...nodeState) { - node := newTestTransitionNode(t, rpc) + node := newTestNode(t, testNodeOpts{rpc: rpc, config: testNodeConfig{nodeIsSyncingEnabled: true}}) for _, allowedState := range allowedStates { m := new(fnMock) node.setState(allowedState) @@ -106,3 +121,12 @@ func testTransition(t *testing.T, rpc *mockNodeClient[types.ID, Head], transitio } } + +func TestNodeState_String(t *testing.T) { + t.Run("Ensure all states are meaningful when converted to string", func(t *testing.T) { + for _, ns := range allNodeStates { + // ensure that string representation is not nodeState(%d) + assert.NotContains(t, ns.String(), strconv.FormatInt(int64(ns), 10), "Expected node state to have readable name") + } + }) +} diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index af8f27d498d..20902277480 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -2,7 +2,6 @@ package client import ( "context" - "errors" "fmt" "math" "math/big" @@ -282,16 +281,9 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td lggr.Debugw("Trying to revive out-of-sync RPC node", "nodeState", n.State()) // Need to redial since out-of-sync nodes are automatically disconnected - if err := n.rpc.Dial(n.nodeCtx); err != nil { - lggr.Errorw("Failed to dial out-of-sync RPC node", "nodeState", n.State()) - n.declareUnreachable() - return - } - - // Manually re-verify since out-of-sync nodes are automatically disconnected - if err := n.verify(n.nodeCtx); err != nil { - lggr.Errorw(fmt.Sprintf("Failed to verify out-of-sync RPC node: %v", err), "err", err) - n.declareInvalidChainID() + state := n.createVerifiedConn(n.nodeCtx, lggr) + if state != nodeStateAlive { + n.declareState(state) return } @@ -377,21 +369,18 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { n.setState(nodeStateDialed) - err = n.verify(n.nodeCtx) - - if errors.Is(err, errInvalidChainID) { - lggr.Errorw("Failed to redial RPC node; remote endpoint returned the wrong chain ID", "err", err) - n.declareInvalidChainID() - return - } else if err != nil { - lggr.Errorw(fmt.Sprintf("Failed to redial RPC node; verify failed: %v", err), "err", err) - n.declareUnreachable() + state := n.verifyConn(n.nodeCtx, lggr) + switch state { + case nodeStateUnreachable: + n.setState(nodeStateUnreachable) + continue + case nodeStateAlive: + lggr.Infow(fmt.Sprintf("Successfully redialled and verified RPC node %s. Node was offline for %s", n.String(), time.Since(unreachableAt)), "nodeState", n.State()) + fallthrough + default: + n.declareState(state) return } - - lggr.Infow(fmt.Sprintf("Successfully redialled and verified RPC node %s. Node was offline for %s", n.String(), time.Since(unreachableAt)), "nodeState", n.State()) - n.declareAlive() - return } } } @@ -414,6 +403,14 @@ func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { invalidAt := time.Now() lggr := logger.Named(n.lfcLog, "InvalidChainID") + + // Need to redial since invalid chain ID nodes are automatically disconnected + state := n.createVerifiedConn(n.nodeCtx, lggr) + if state != nodeStateInvalidChainID { + n.declareState(state) + return + } + lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with invalid chain ID", n.String()), "nodeState", n.State()) chainIDRecheckBackoff := iutils.NewRedialBackoff() @@ -423,18 +420,71 @@ func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { case <-n.nodeCtx.Done(): return case <-time.After(chainIDRecheckBackoff.Duration()): - err := n.verify(n.nodeCtx) - if errors.Is(err, errInvalidChainID) { - lggr.Errorw("Failed to verify RPC node; remote endpoint returned the wrong chain ID", "err", err) + state := n.verifyConn(n.nodeCtx, lggr) + switch state { + case nodeStateInvalidChainID: continue - } else if err != nil { - lggr.Errorw(fmt.Sprintf("Unexpected error while verifying RPC node chain ID; %v", err), "err", err) + case nodeStateAlive: + lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was offline for %s", time.Since(invalidAt)), "nodeState", n.State()) + fallthrough + default: + n.declareState(state) + return + } + } + } +} + +func (n *node[CHAIN_ID, HEAD, RPC]) syncingLoop() { + defer n.wg.Done() + + { + // sanity check + state := n.State() + switch state { + case nodeStateSyncing: + case nodeStateClosed: + return + default: + panic(fmt.Sprintf("syncingLoop can only run for node in nodeStateSyncing state, got: %s", state)) + } + } + + syncingAt := time.Now() + + lggr := logger.Sugared(logger.Named(n.lfcLog, "Syncing")) + lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with syncing status", n.String()), "nodeState", n.State()) + // Need to redial since syncing nodes are automatically disconnected + state := n.createVerifiedConn(n.nodeCtx, lggr) + if state != nodeStateSyncing { + n.declareState(state) + return + } + + recheckBackoff := iutils.NewRedialBackoff() + + for { + select { + case <-n.nodeCtx.Done(): + return + case <-time.After(recheckBackoff.Duration()): + lggr.Tracew("Trying to recheck if the node is still syncing", "nodeState", n.State()) + isSyncing, err := n.rpc.IsSyncing(n.nodeCtx) + if err != nil { + lggr.Errorw("Unexpected error while verifying RPC node synchronization status", "err", err, "nodeState", n.State()) n.declareUnreachable() return } - lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was offline for %s", time.Since(invalidAt)), "nodeState", n.State()) + + if isSyncing { + lggr.Errorw("Verification failed: Node is syncing", "nodeState", n.State()) + continue + } + + lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was syncing for %s", time.Since(syncingAt)), "nodeState", n.State()) n.declareAlive() return } + } } diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index bf94e6bd063..7c31c085dd3 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -3,13 +3,14 @@ package client import ( "errors" "fmt" - big "math/big" + "math/big" "sync/atomic" "testing" "github.com/cometbft/cometbft/libs/rand" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" "go.uber.org/zap" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -225,9 +226,11 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { // disconnects all on transfer to unreachable or outOfSync rpc.On("DisconnectAll").Maybe() // might be called in unreachable loop - rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + rpc.On("Dial", mock.Anything).Run(func(_ mock.Arguments) { + require.Equal(t, nodeStateOutOfSync, node.State()) + }).Return(errors.New("failed to dial")).Maybe() node.declareAlive() - tests.AssertLogEventually(t, observedLogs, "Failed to dial out-of-sync RPC node") + tests.AssertLogEventually(t, observedLogs, "Dial failed: Node is unreachable") }) t.Run("when behind more than SyncThreshold but we are the last live node, forcibly stays alive", func(t *testing.T) { t.Parallel() @@ -436,7 +439,6 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil).Once() - // might be called multiple times rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() outOfSyncSubscription := mocks.NewSubscription(t) @@ -471,7 +473,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { return node.State() == nodeStateUnreachable }) }) - t.Run("if fail to get chainID, transitions to invalidChainID", func(t *testing.T) { + t.Run("if fail to get chainID, transitions to unreachable", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) node := newAliveNode(t, testNodeOpts{ @@ -479,13 +481,16 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() + // for out-of-sync rpc.On("Dial", mock.Anything).Return(nil).Once() + // for unreachable + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() expectedError := errors.New("failed to get chain ID") // might be called multiple times rpc.On("ChainID", mock.Anything).Return(types.NewIDFromInt(0), expectedError) node.declareOutOfSync(stubIsOutOfSync) tests.AssertEventually(t, func() bool { - return node.State() == nodeStateInvalidChainID + return node.State() == nodeStateUnreachable }) }) t.Run("if chainID does not match, transitions to invalidChainID", func(t *testing.T) { @@ -499,7 +504,8 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() - rpc.On("Dial", mock.Anything).Return(nil).Once() + // one for out-of-sync & one for invalid chainID + rpc.On("Dial", mock.Anything).Return(nil).Twice() // might be called multiple times rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) node.declareOutOfSync(stubIsOutOfSync) @@ -507,6 +513,49 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { return node.State() == nodeStateInvalidChainID }) }) + t.Run("if syncing, transitions to syncing", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.NewIDFromInt(10) + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + config: testNodeConfig{nodeIsSyncingEnabled: true}, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) + // might be called multiple times + rpc.On("IsSyncing", mock.Anything).Return(true, nil) + node.declareOutOfSync(stubIsOutOfSync) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateSyncing + }) + }) + t.Run("if fails to fetch syncing status, transitions to unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.NewIDFromInt(10) + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + config: testNodeConfig{nodeIsSyncingEnabled: true}, + }) + defer func() { assert.NoError(t, node.close()) }() + + // one for out-of-sync + rpc.On("Dial", mock.Anything).Return(nil).Once() + // for unreachable + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + // might be called multiple times + rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check syncing")) + node.declareOutOfSync(stubIsOutOfSync) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + }) t.Run("if fails to subscribe, becomes unreachable", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) @@ -518,7 +567,6 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil).Once() - // might be called multiple times rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() expectedError := errors.New("failed to subscribe") rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(nil, expectedError) @@ -541,7 +589,6 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil).Once() - // might be called multiple times rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() sub := mocks.NewSubscription(t) @@ -570,7 +617,6 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil).Once() - // might be called multiple times rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() sub := mocks.NewSubscription(t) @@ -601,7 +647,6 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil).Once() - // might be called multiple times rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() outOfSyncSubscription := mocks.NewSubscription(t) @@ -641,7 +686,6 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { } rpc.On("Dial", mock.Anything).Return(nil).Once() - // might be called multiple times rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() outOfSyncSubscription := mocks.NewSubscription(t) @@ -712,7 +756,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { assert.Equal(t, nodeStateDialed, node.State()) }).Return(nodeChainID, errors.New("failed to get chain id")) node.declareUnreachable() - tests.AssertLogCountEventually(t, observedLogs, "Failed to redial RPC node; verify failed", 2) + tests.AssertLogCountEventually(t, observedLogs, "Failed to verify chain ID for node", 2) }) t.Run("on chain ID mismatch transitions to invalidChainID", func(t *testing.T) { t.Parallel() @@ -732,7 +776,72 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { return node.State() == nodeStateInvalidChainID }) }) - t.Run("on valid chain ID becomes alive", func(t *testing.T) { + t.Run("on syncing status check failure, keeps trying", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + config: testNodeConfig{nodeIsSyncingEnabled: true}, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Run(func(_ mock.Arguments) { + assert.Equal(t, nodeStateDialed, node.State()) + }).Return(nodeChainID, nil) + rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check syncing status")) + node.declareUnreachable() + tests.AssertLogCountEventually(t, observedLogs, "Unexpected error while verifying RPC node synchronization status", 2) + }) + t.Run("on syncing, transitions to syncing state", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + config: testNodeConfig{nodeIsSyncingEnabled: true}, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil).Twice() + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Twice() + rpc.On("IsSyncing", mock.Anything).Return(true, nil) + + setupRPCForAliveLoop(t, rpc) + + node.declareUnreachable() + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateSyncing + }) + }) + t.Run("on successful verification becomes alive", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + config: testNodeConfig{nodeIsSyncingEnabled: true}, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) + rpc.On("IsSyncing", mock.Anything).Return(false, nil) + + setupRPCForAliveLoop(t, rpc) + + node.declareUnreachable() + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateAlive + }) + }) + t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) nodeChainID := types.RandomID() @@ -772,6 +881,22 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { node.invalidChainIDLoop() }) + t.Run("on invalid dial becomes unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + node := newDialedNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) + node.declareInvalidChainID() + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + }) t.Run("on failed chainID call becomes unreachable", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) @@ -785,10 +910,11 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("ChainID", mock.Anything).Return(nodeChainID, errors.New("failed to get chain id")) - // for unreachable loop + // once for chainID and maybe another one for unreachable + rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareInvalidChainID() - tests.AssertLogEventually(t, observedLogs, "Unexpected error while verifying RPC node chain ID") + tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node") tests.AssertEventually(t, func() bool { return node.State() == nodeStateUnreachable }) @@ -806,6 +932,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() + rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) node.declareInvalidChainID() tests.AssertLogCountEventually(t, observedLogs, "Failed to verify RPC node; remote endpoint returned the wrong chain ID", 2) @@ -813,17 +940,44 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { return node.State() == nodeStateInvalidChainID }) }) - t.Run("on valid chainID becomes alive", func(t *testing.T) { + t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) - nodeChainID := types.RandomID() + nodeChainID := types.NewIDFromInt(10) + rpcChainID := types.NewIDFromInt(11) node := newDialedNode(t, testNodeOpts{ rpc: rpc, chainID: nodeChainID, }) defer func() { assert.NoError(t, node.close()) }() - rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) + rpc.On("Dial", mock.Anything).Return(nil).Once() + rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil).Once() + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + + setupRPCForAliveLoop(t, rpc) + + node.declareInvalidChainID() + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateAlive + }) + }) + t.Run("on successful verification becomes alive", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.NewIDFromInt(10) + rpcChainID := types.NewIDFromInt(11) + node := newDialedNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + config: testNodeConfig{nodeIsSyncingEnabled: true}, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil).Once() + rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil).Once() + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + rpc.On("IsSyncing", mock.Anything).Return(false, nil).Once() setupRPCForAliveLoop(t, rpc) @@ -885,7 +1039,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { rpc.On("DisconnectAll") err := node.Start(tests.Context(t)) assert.NoError(t, err) - tests.AssertLogEventually(t, observedLogs, "Verify failed") + tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node") tests.AssertEventually(t, func() bool { return node.State() == nodeStateUnreachable }) @@ -911,7 +1065,81 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { return node.State() == nodeStateInvalidChainID }) }) - t.Run("on valid chain ID becomes alive", func(t *testing.T) { + t.Run("if syncing verification fails, becomes unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) + node := newNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + config: testNodeConfig{nodeIsSyncingEnabled: true}, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil).Once() + rpc.On("ChainID", mock.Anything).Run(func(_ mock.Arguments) { + assert.Equal(t, nodeStateDialed, node.State()) + }).Return(nodeChainID, nil).Once() + rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check syncing status")) + // disconnects all on transfer to unreachable + rpc.On("DisconnectAll") + // fail to redial to stay in unreachable state + rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")) + err := node.Start(tests.Context(t)) + assert.NoError(t, err) + tests.AssertLogEventually(t, observedLogs, "Unexpected error while verifying RPC node synchronization status") + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + }) + t.Run("on isSyncing transitions to syncing", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.NewIDFromInt(10) + node := newNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + config: testNodeConfig{nodeIsSyncingEnabled: true}, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) + rpc.On("IsSyncing", mock.Anything).Return(true, nil) + // disconnects all on transfer to unreachable + rpc.On("DisconnectAll") + err := node.Start(tests.Context(t)) + assert.NoError(t, err) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateSyncing + }) + }) + t.Run("on successful verification becomes alive", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + node := newNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + config: testNodeConfig{nodeIsSyncingEnabled: true}, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) + rpc.On("IsSyncing", mock.Anything).Return(false, nil) + + setupRPCForAliveLoop(t, rpc) + + err := node.Start(tests.Context(t)) + assert.NoError(t, err) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateAlive + }) + }) + t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) nodeChainID := types.RandomID() @@ -1069,3 +1297,150 @@ func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { }) } + +func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { + t.Parallel() + newDialedNode := func(t *testing.T, opts testNodeOpts) testNode { + opts.config.nodeIsSyncingEnabled = true + node := newTestNode(t, opts) + opts.rpc.On("Close").Return(nil).Once() + opts.rpc.On("DisconnectAll") + + node.setState(nodeStateDialed) + return node + } + t.Run("returns on closed", func(t *testing.T) { + t.Parallel() + node := newTestNode(t, testNodeOpts{}) + node.setState(nodeStateClosed) + node.wg.Add(1) + node.syncingLoop() + + }) + t.Run("on invalid dial becomes unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + node := newDialedNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) + node.declareSyncing() + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + }) + t.Run("on failed chainID call becomes unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) + node := newDialedNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("ChainID", mock.Anything).Return(nodeChainID, errors.New("failed to get chain id")) + // once for syncing and maybe another one for unreachable + rpc.On("Dial", mock.Anything).Return(nil).Once() + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + node.declareSyncing() + tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node") + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + }) + t.Run("on chainID mismatch transitions to invalidChainID", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.NewIDFromInt(10) + rpcChainID := types.NewIDFromInt(11) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) + node := newDialedNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil).Twice() + rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) + node.declareSyncing() + tests.AssertLogCountEventually(t, observedLogs, "Failed to verify RPC node; remote endpoint returned the wrong chain ID", 2) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateInvalidChainID + }) + }) + t.Run("on failed Syncing check - becomes unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) + node := newDialedNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + // first one is needed to enter internal loop + rpc.On("IsSyncing", mock.Anything).Return(true, nil).Once() + rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check if syncing")).Once() + rpc.On("Dial", mock.Anything).Return(nil).Once() + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + node.declareSyncing() + tests.AssertLogEventually(t, observedLogs, "Unexpected error while verifying RPC node synchronization status") + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + }) + t.Run("on IsSyncing - keeps trying", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) + node := newDialedNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + rpc.On("IsSyncing", mock.Anything).Return(true, nil) + rpc.On("Dial", mock.Anything).Return(nil).Once() + node.declareSyncing() + tests.AssertLogCountEventually(t, observedLogs, "Verification failed: Node is syncing", 2) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateSyncing + }) + }) + t.Run("on successful verification becomes alive", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + node := newDialedNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil).Once() + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + rpc.On("IsSyncing", mock.Anything).Return(true, nil).Once() + rpc.On("IsSyncing", mock.Anything).Return(false, nil).Once() + + setupRPCForAliveLoop(t, rpc) + + node.declareSyncing() + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateAlive + }) + }) +} diff --git a/common/client/node_test.go b/common/client/node_test.go index 7b6a38e3951..c7a7a710d8f 100644 --- a/common/client/node_test.go +++ b/common/client/node_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/common/types" ) @@ -14,6 +15,7 @@ type testNodeConfig struct { pollInterval time.Duration selectionMode string syncThreshold uint32 + nodeIsSyncingEnabled bool } func (n testNodeConfig) PollFailureThreshold() uint32 { @@ -32,6 +34,10 @@ func (n testNodeConfig) SyncThreshold() uint32 { return n.syncThreshold } +func (n testNodeConfig) NodeIsSyncingEnabled() bool { + return n.nodeIsSyncingEnabled +} + type testNode struct { *node[types.ID, Head, NodeClient[types.ID, Head]] } diff --git a/common/client/types.go b/common/client/types.go index c05ab238f57..485a0b2671a 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -5,6 +5,7 @@ import ( "math/big" "github.com/smartcontractkit/chainlink-common/pkg/assets" + feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" ) @@ -70,6 +71,7 @@ type NodeClient[ SubscribersCount() int32 SetAliveLoopSub(types.Subscription) UnsubscribeAllExceptAliveLoop() + IsSyncing(ctx context.Context) (bool, error) } // clientAPI includes all the direct RPC methods required by the generalized common client to implement its own. diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index 33deed1df32..cd4665aac8c 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -35,7 +35,7 @@ type chainClient struct { *evmtypes.Receipt, *assets.Wei, *evmtypes.Head, - RPCCLient, + RPCClient, rpc.BatchElem, ] logger logger.SugaredLogger @@ -46,8 +46,8 @@ func NewChainClient( selectionMode string, leaseDuration time.Duration, noNewHeadsThreshold time.Duration, - nodes []commonclient.Node[*big.Int, *evmtypes.Head, RPCCLient], - sendonlys []commonclient.SendOnlyNode[*big.Int, RPCCLient], + nodes []commonclient.Node[*big.Int, *evmtypes.Head, RPCClient], + sendonlys []commonclient.SendOnlyNode[*big.Int, RPCClient], chainID *big.Int, chainType config.ChainType, ) Client { @@ -63,7 +63,7 @@ func NewChainClient( *evmtypes.Receipt, *assets.Wei, *evmtypes.Head, - RPCCLient, + RPCClient, ]( lggr, selectionMode, diff --git a/core/chains/evm/client/chain_client_test.go b/core/chains/evm/client/chain_client_test.go index 6af9a67ee1c..1718036641e 100644 --- a/core/chains/evm/client/chain_client_test.go +++ b/core/chains/evm/client/chain_client_test.go @@ -18,8 +18,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" ) -func newMockRpc(t *testing.T) *mocks.RPCCLient { - mockRpc := mocks.NewRPCCLient(t) +func newMockRpc(t *testing.T) *mocks.RPCClient { + mockRpc := mocks.NewRPCClient(t) mockRpc.On("Dial", mock.Anything).Return(nil).Once() mockRpc.On("Close").Return(nil).Once() mockRpc.On("ChainID", mock.Anything).Return(testutils.FixtureChainID, nil).Once() diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index 467195e11e8..c1652c19c26 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" @@ -22,6 +23,7 @@ type TestNodePoolConfig struct { NodeSelectionMode string NodeSyncThreshold uint32 NodeLeaseDuration time.Duration + NodeIsSyncingEnabledVal bool } func (tc TestNodePoolConfig) PollFailureThreshold() uint32 { return tc.NodePollFailureThreshold } @@ -32,6 +34,10 @@ func (tc TestNodePoolConfig) LeaseDuration() time.Duration { return tc.NodeLeaseDuration } +func (tc TestNodePoolConfig) NodeIsSyncingEnabled() bool { + return tc.NodeIsSyncingEnabledVal +} + func NewClientWithTestNode(t *testing.T, nodePoolCfg config.NodePool, noNewHeadsThreshold time.Duration, rpcUrl string, rpcHTTPURL *url.URL, sendonlyRPCURLs []url.URL, id int32, chainID *big.Int) (*client, error) { parsed, err := url.ParseRequestURI(rpcUrl) if err != nil { @@ -89,18 +95,18 @@ func NewChainClientWithTestNode( lggr := logger.Test(t) rpc := NewRPCClient(lggr, *parsed, rpcHTTPURL, "eth-primary-rpc-0", id, chainID, commonclient.Primary) - n := commonclient.NewNode[*big.Int, *evmtypes.Head, RPCCLient]( + n := commonclient.NewNode[*big.Int, *evmtypes.Head, RPCClient]( nodeCfg, noNewHeadsThreshold, lggr, *parsed, rpcHTTPURL, "eth-primary-node-0", id, chainID, 1, rpc, "EVM") - primaries := []commonclient.Node[*big.Int, *evmtypes.Head, RPCCLient]{n} + primaries := []commonclient.Node[*big.Int, *evmtypes.Head, RPCClient]{n} - var sendonlys []commonclient.SendOnlyNode[*big.Int, RPCCLient] + var sendonlys []commonclient.SendOnlyNode[*big.Int, RPCClient] for i, u := range sendonlyRPCURLs { if u.Scheme != "http" && u.Scheme != "https" { return nil, errors.Errorf("sendonly ethereum rpc url scheme must be http(s): %s", u.String()) } var empty url.URL rpc := NewRPCClient(lggr, empty, &sendonlyRPCURLs[i], fmt.Sprintf("eth-sendonly-rpc-%d", i), id, chainID, commonclient.Secondary) - s := commonclient.NewSendOnlyNode[*big.Int, RPCCLient]( + s := commonclient.NewSendOnlyNode[*big.Int, RPCClient]( lggr, u, fmt.Sprintf("eth-sendonly-%d", i), chainID, rpc) sendonlys = append(sendonlys, s) } @@ -133,7 +139,7 @@ func NewChainClientWithMockedRpc( leaseDuration time.Duration, noNewHeadsThreshold time.Duration, chainID *big.Int, - rpc RPCCLient, + rpc RPCClient, ) Client { lggr := logger.Test(t) @@ -145,9 +151,9 @@ func NewChainClientWithMockedRpc( } parsed, _ := url.ParseRequestURI("ws://test") - n := commonclient.NewNode[*big.Int, *evmtypes.Head, RPCCLient]( + n := commonclient.NewNode[*big.Int, *evmtypes.Head, RPCClient]( cfg, noNewHeadsThreshold, lggr, *parsed, nil, "eth-primary-node-0", 1, chainID, 1, rpc, "EVM") - primaries := []commonclient.Node[*big.Int, *evmtypes.Head, RPCCLient]{n} + primaries := []commonclient.Node[*big.Int, *evmtypes.Head, RPCClient]{n} c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, primaries, nil, chainID, chainType) t.Cleanup(c.Close) return c diff --git a/core/chains/evm/client/mocks/rpc_client.go b/core/chains/evm/client/mocks/rpc_client.go index 186fd2534e3..26d5744a1ab 100644 --- a/core/chains/evm/client/mocks/rpc_client.go +++ b/core/chains/evm/client/mocks/rpc_client.go @@ -26,13 +26,13 @@ import ( types "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) -// RPCCLient is an autogenerated mock type for the RPCCLient type -type RPCCLient struct { +// RPCClient is an autogenerated mock type for the RPCClient type +type RPCClient struct { mock.Mock } // BalanceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *RPCCLient) BalanceAt(ctx context.Context, accountAddress common.Address, blockNumber *big.Int) (*big.Int, error) { +func (_m *RPCClient) BalanceAt(ctx context.Context, accountAddress common.Address, blockNumber *big.Int) (*big.Int, error) { ret := _m.Called(ctx, accountAddress, blockNumber) if len(ret) == 0 { @@ -62,7 +62,7 @@ func (_m *RPCCLient) BalanceAt(ctx context.Context, accountAddress common.Addres } // BatchCallContext provides a mock function with given fields: ctx, b -func (_m *RPCCLient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { +func (_m *RPCClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { ret := _m.Called(ctx, b) if len(ret) == 0 { @@ -80,7 +80,7 @@ func (_m *RPCCLient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) er } // BlockByHash provides a mock function with given fields: ctx, hash -func (_m *RPCCLient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Head, error) { +func (_m *RPCClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Head, error) { ret := _m.Called(ctx, hash) if len(ret) == 0 { @@ -110,7 +110,7 @@ func (_m *RPCCLient) BlockByHash(ctx context.Context, hash common.Hash) (*types. } // BlockByHashGeth provides a mock function with given fields: ctx, hash -func (_m *RPCCLient) BlockByHashGeth(ctx context.Context, hash common.Hash) (*coretypes.Block, error) { +func (_m *RPCClient) BlockByHashGeth(ctx context.Context, hash common.Hash) (*coretypes.Block, error) { ret := _m.Called(ctx, hash) if len(ret) == 0 { @@ -140,7 +140,7 @@ func (_m *RPCCLient) BlockByHashGeth(ctx context.Context, hash common.Hash) (*co } // BlockByNumber provides a mock function with given fields: ctx, number -func (_m *RPCCLient) BlockByNumber(ctx context.Context, number *big.Int) (*types.Head, error) { +func (_m *RPCClient) BlockByNumber(ctx context.Context, number *big.Int) (*types.Head, error) { ret := _m.Called(ctx, number) if len(ret) == 0 { @@ -170,7 +170,7 @@ func (_m *RPCCLient) BlockByNumber(ctx context.Context, number *big.Int) (*types } // BlockByNumberGeth provides a mock function with given fields: ctx, number -func (_m *RPCCLient) BlockByNumberGeth(ctx context.Context, number *big.Int) (*coretypes.Block, error) { +func (_m *RPCClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (*coretypes.Block, error) { ret := _m.Called(ctx, number) if len(ret) == 0 { @@ -200,7 +200,7 @@ func (_m *RPCCLient) BlockByNumberGeth(ctx context.Context, number *big.Int) (*c } // CallContext provides a mock function with given fields: ctx, result, method, args -func (_m *RPCCLient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { +func (_m *RPCClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { var _ca []interface{} _ca = append(_ca, ctx, result, method) _ca = append(_ca, args...) @@ -221,7 +221,7 @@ func (_m *RPCCLient) CallContext(ctx context.Context, result interface{}, method } // CallContract provides a mock function with given fields: ctx, msg, blockNumber -func (_m *RPCCLient) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) ([]byte, error) { +func (_m *RPCClient) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) ([]byte, error) { ret := _m.Called(ctx, msg, blockNumber) if len(ret) == 0 { @@ -251,7 +251,7 @@ func (_m *RPCCLient) CallContract(ctx context.Context, msg interface{}, blockNum } // ChainID provides a mock function with given fields: ctx -func (_m *RPCCLient) ChainID(ctx context.Context) (*big.Int, error) { +func (_m *RPCClient) ChainID(ctx context.Context) (*big.Int, error) { ret := _m.Called(ctx) if len(ret) == 0 { @@ -281,7 +281,7 @@ func (_m *RPCCLient) ChainID(ctx context.Context) (*big.Int, error) { } // ClientVersion provides a mock function with given fields: _a0 -func (_m *RPCCLient) ClientVersion(_a0 context.Context) (string, error) { +func (_m *RPCClient) ClientVersion(_a0 context.Context) (string, error) { ret := _m.Called(_a0) if len(ret) == 0 { @@ -309,12 +309,12 @@ func (_m *RPCCLient) ClientVersion(_a0 context.Context) (string, error) { } // Close provides a mock function with given fields: -func (_m *RPCCLient) Close() { +func (_m *RPCClient) Close() { _m.Called() } // CodeAt provides a mock function with given fields: ctx, account, blockNumber -func (_m *RPCCLient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { +func (_m *RPCClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { ret := _m.Called(ctx, account, blockNumber) if len(ret) == 0 { @@ -344,7 +344,7 @@ func (_m *RPCCLient) CodeAt(ctx context.Context, account common.Address, blockNu } // Dial provides a mock function with given fields: ctx -func (_m *RPCCLient) Dial(ctx context.Context) error { +func (_m *RPCClient) Dial(ctx context.Context) error { ret := _m.Called(ctx) if len(ret) == 0 { @@ -362,7 +362,7 @@ func (_m *RPCCLient) Dial(ctx context.Context) error { } // DialHTTP provides a mock function with given fields: -func (_m *RPCCLient) DialHTTP() error { +func (_m *RPCClient) DialHTTP() error { ret := _m.Called() if len(ret) == 0 { @@ -380,12 +380,12 @@ func (_m *RPCCLient) DialHTTP() error { } // DisconnectAll provides a mock function with given fields: -func (_m *RPCCLient) DisconnectAll() { +func (_m *RPCClient) DisconnectAll() { _m.Called() } // EstimateGas provides a mock function with given fields: ctx, call -func (_m *RPCCLient) EstimateGas(ctx context.Context, call interface{}) (uint64, error) { +func (_m *RPCClient) EstimateGas(ctx context.Context, call interface{}) (uint64, error) { ret := _m.Called(ctx, call) if len(ret) == 0 { @@ -413,7 +413,7 @@ func (_m *RPCCLient) EstimateGas(ctx context.Context, call interface{}) (uint64, } // FilterEvents provides a mock function with given fields: ctx, query -func (_m *RPCCLient) FilterEvents(ctx context.Context, query ethereum.FilterQuery) ([]coretypes.Log, error) { +func (_m *RPCClient) FilterEvents(ctx context.Context, query ethereum.FilterQuery) ([]coretypes.Log, error) { ret := _m.Called(ctx, query) if len(ret) == 0 { @@ -443,7 +443,7 @@ func (_m *RPCCLient) FilterEvents(ctx context.Context, query ethereum.FilterQuer } // HeaderByHash provides a mock function with given fields: ctx, h -func (_m *RPCCLient) HeaderByHash(ctx context.Context, h common.Hash) (*coretypes.Header, error) { +func (_m *RPCClient) HeaderByHash(ctx context.Context, h common.Hash) (*coretypes.Header, error) { ret := _m.Called(ctx, h) if len(ret) == 0 { @@ -473,7 +473,7 @@ func (_m *RPCCLient) HeaderByHash(ctx context.Context, h common.Hash) (*coretype } // HeaderByNumber provides a mock function with given fields: ctx, n -func (_m *RPCCLient) HeaderByNumber(ctx context.Context, n *big.Int) (*coretypes.Header, error) { +func (_m *RPCClient) HeaderByNumber(ctx context.Context, n *big.Int) (*coretypes.Header, error) { ret := _m.Called(ctx, n) if len(ret) == 0 { @@ -502,8 +502,36 @@ func (_m *RPCCLient) HeaderByNumber(ctx context.Context, n *big.Int) (*coretypes return r0, r1 } +// IsSyncing provides a mock function with given fields: ctx +func (_m *RPCClient) IsSyncing(ctx context.Context) (bool, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for IsSyncing") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) bool); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // LINKBalance provides a mock function with given fields: ctx, accountAddress, linkAddress -func (_m *RPCCLient) LINKBalance(ctx context.Context, accountAddress common.Address, linkAddress common.Address) (*assets.Link, error) { +func (_m *RPCClient) LINKBalance(ctx context.Context, accountAddress common.Address, linkAddress common.Address) (*assets.Link, error) { ret := _m.Called(ctx, accountAddress, linkAddress) if len(ret) == 0 { @@ -533,7 +561,7 @@ func (_m *RPCCLient) LINKBalance(ctx context.Context, accountAddress common.Addr } // LatestBlockHeight provides a mock function with given fields: _a0 -func (_m *RPCCLient) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { +func (_m *RPCClient) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { ret := _m.Called(_a0) if len(ret) == 0 { @@ -563,7 +591,7 @@ func (_m *RPCCLient) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { } // PendingCallContract provides a mock function with given fields: ctx, msg -func (_m *RPCCLient) PendingCallContract(ctx context.Context, msg interface{}) ([]byte, error) { +func (_m *RPCClient) PendingCallContract(ctx context.Context, msg interface{}) ([]byte, error) { ret := _m.Called(ctx, msg) if len(ret) == 0 { @@ -593,7 +621,7 @@ func (_m *RPCCLient) PendingCallContract(ctx context.Context, msg interface{}) ( } // PendingCodeAt provides a mock function with given fields: ctx, account -func (_m *RPCCLient) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { +func (_m *RPCClient) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { ret := _m.Called(ctx, account) if len(ret) == 0 { @@ -623,7 +651,7 @@ func (_m *RPCCLient) PendingCodeAt(ctx context.Context, account common.Address) } // PendingSequenceAt provides a mock function with given fields: ctx, addr -func (_m *RPCCLient) PendingSequenceAt(ctx context.Context, addr common.Address) (types.Nonce, error) { +func (_m *RPCClient) PendingSequenceAt(ctx context.Context, addr common.Address) (types.Nonce, error) { ret := _m.Called(ctx, addr) if len(ret) == 0 { @@ -651,7 +679,7 @@ func (_m *RPCCLient) PendingSequenceAt(ctx context.Context, addr common.Address) } // SendEmptyTransaction provides a mock function with given fields: ctx, newTxAttempt, seq, gasLimit, fee, fromAddress -func (_m *RPCCLient) SendEmptyTransaction(ctx context.Context, newTxAttempt func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), seq types.Nonce, gasLimit uint32, fee *evmassets.Wei, fromAddress common.Address) (string, error) { +func (_m *RPCClient) SendEmptyTransaction(ctx context.Context, newTxAttempt func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), seq types.Nonce, gasLimit uint32, fee *evmassets.Wei, fromAddress common.Address) (string, error) { ret := _m.Called(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) if len(ret) == 0 { @@ -679,7 +707,7 @@ func (_m *RPCCLient) SendEmptyTransaction(ctx context.Context, newTxAttempt func } // SendTransaction provides a mock function with given fields: ctx, tx -func (_m *RPCCLient) SendTransaction(ctx context.Context, tx *coretypes.Transaction) error { +func (_m *RPCClient) SendTransaction(ctx context.Context, tx *coretypes.Transaction) error { ret := _m.Called(ctx, tx) if len(ret) == 0 { @@ -697,7 +725,7 @@ func (_m *RPCCLient) SendTransaction(ctx context.Context, tx *coretypes.Transact } // SequenceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *RPCCLient) SequenceAt(ctx context.Context, accountAddress common.Address, blockNumber *big.Int) (types.Nonce, error) { +func (_m *RPCClient) SequenceAt(ctx context.Context, accountAddress common.Address, blockNumber *big.Int) (types.Nonce, error) { ret := _m.Called(ctx, accountAddress, blockNumber) if len(ret) == 0 { @@ -725,12 +753,12 @@ func (_m *RPCCLient) SequenceAt(ctx context.Context, accountAddress common.Addre } // SetAliveLoopSub provides a mock function with given fields: _a0 -func (_m *RPCCLient) SetAliveLoopSub(_a0 commontypes.Subscription) { +func (_m *RPCClient) SetAliveLoopSub(_a0 commontypes.Subscription) { _m.Called(_a0) } // SimulateTransaction provides a mock function with given fields: ctx, tx -func (_m *RPCCLient) SimulateTransaction(ctx context.Context, tx *coretypes.Transaction) error { +func (_m *RPCClient) SimulateTransaction(ctx context.Context, tx *coretypes.Transaction) error { ret := _m.Called(ctx, tx) if len(ret) == 0 { @@ -748,7 +776,7 @@ func (_m *RPCCLient) SimulateTransaction(ctx context.Context, tx *coretypes.Tran } // Subscribe provides a mock function with given fields: ctx, channel, args -func (_m *RPCCLient) Subscribe(ctx context.Context, channel chan<- *types.Head, args ...interface{}) (commontypes.Subscription, error) { +func (_m *RPCClient) Subscribe(ctx context.Context, channel chan<- *types.Head, args ...interface{}) (commontypes.Subscription, error) { var _ca []interface{} _ca = append(_ca, ctx, channel) _ca = append(_ca, args...) @@ -781,7 +809,7 @@ func (_m *RPCCLient) Subscribe(ctx context.Context, channel chan<- *types.Head, } // SubscribeFilterLogs provides a mock function with given fields: ctx, q, ch -func (_m *RPCCLient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- coretypes.Log) (ethereum.Subscription, error) { +func (_m *RPCClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- coretypes.Log) (ethereum.Subscription, error) { ret := _m.Called(ctx, q, ch) if len(ret) == 0 { @@ -811,7 +839,7 @@ func (_m *RPCCLient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQ } // SubscribersCount provides a mock function with given fields: -func (_m *RPCCLient) SubscribersCount() int32 { +func (_m *RPCClient) SubscribersCount() int32 { ret := _m.Called() if len(ret) == 0 { @@ -829,7 +857,7 @@ func (_m *RPCCLient) SubscribersCount() int32 { } // SuggestGasPrice provides a mock function with given fields: ctx -func (_m *RPCCLient) SuggestGasPrice(ctx context.Context) (*big.Int, error) { +func (_m *RPCClient) SuggestGasPrice(ctx context.Context) (*big.Int, error) { ret := _m.Called(ctx) if len(ret) == 0 { @@ -859,7 +887,7 @@ func (_m *RPCCLient) SuggestGasPrice(ctx context.Context) (*big.Int, error) { } // SuggestGasTipCap provides a mock function with given fields: ctx -func (_m *RPCCLient) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { +func (_m *RPCClient) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { ret := _m.Called(ctx) if len(ret) == 0 { @@ -889,7 +917,7 @@ func (_m *RPCCLient) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { } // TokenBalance provides a mock function with given fields: ctx, accountAddress, tokenAddress -func (_m *RPCCLient) TokenBalance(ctx context.Context, accountAddress common.Address, tokenAddress common.Address) (*big.Int, error) { +func (_m *RPCClient) TokenBalance(ctx context.Context, accountAddress common.Address, tokenAddress common.Address) (*big.Int, error) { ret := _m.Called(ctx, accountAddress, tokenAddress) if len(ret) == 0 { @@ -919,7 +947,7 @@ func (_m *RPCCLient) TokenBalance(ctx context.Context, accountAddress common.Add } // TransactionByHash provides a mock function with given fields: ctx, txHash -func (_m *RPCCLient) TransactionByHash(ctx context.Context, txHash common.Hash) (*coretypes.Transaction, error) { +func (_m *RPCClient) TransactionByHash(ctx context.Context, txHash common.Hash) (*coretypes.Transaction, error) { ret := _m.Called(ctx, txHash) if len(ret) == 0 { @@ -949,7 +977,7 @@ func (_m *RPCCLient) TransactionByHash(ctx context.Context, txHash common.Hash) } // TransactionReceipt provides a mock function with given fields: ctx, txHash -func (_m *RPCCLient) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { +func (_m *RPCClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { ret := _m.Called(ctx, txHash) if len(ret) == 0 { @@ -979,7 +1007,7 @@ func (_m *RPCCLient) TransactionReceipt(ctx context.Context, txHash common.Hash) } // TransactionReceiptGeth provides a mock function with given fields: ctx, txHash -func (_m *RPCCLient) TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (*coretypes.Receipt, error) { +func (_m *RPCClient) TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (*coretypes.Receipt, error) { ret := _m.Called(ctx, txHash) if len(ret) == 0 { @@ -1009,17 +1037,17 @@ func (_m *RPCCLient) TransactionReceiptGeth(ctx context.Context, txHash common.H } // UnsubscribeAllExceptAliveLoop provides a mock function with given fields: -func (_m *RPCCLient) UnsubscribeAllExceptAliveLoop() { +func (_m *RPCClient) UnsubscribeAllExceptAliveLoop() { _m.Called() } -// NewRPCCLient creates a new instance of RPCCLient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// NewRPCClient creates a new instance of RPCClient. 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 NewRPCCLient(t interface { +func NewRPCClient(t interface { mock.TestingT Cleanup(func()) -}) *RPCCLient { - mock := &RPCCLient{} +}) *RPCClient { + mock := &RPCClient{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 08791658430..d71762ac09a 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -29,8 +29,10 @@ import ( ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) -// RPCCLient includes all the necessary generalized RPC methods along with any additional chain-specific methods. -type RPCCLient interface { +// RPCClient includes all the necessary generalized RPC methods along with any additional chain-specific methods. +// +//go:generate mockery --quiet --name RPCClient --output ./mocks --case=underscore +type RPCClient interface { commonclient.RPC[ *big.Int, evmtypes.Nonce, @@ -90,7 +92,7 @@ func NewRPCClient( id int32, chainID *big.Int, tier commonclient.NodeTier, -) RPCCLient { +) RPCClient { r := new(rpcClient) r.name = name r.id = id @@ -1106,6 +1108,33 @@ func (r *rpcClient) makeQueryCtx(ctx context.Context) (context.Context, context. return makeQueryCtx(ctx, r.getChStopInflight()) } +func (r *rpcClient) IsSyncing(ctx context.Context) (bool, error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return false, err + } + defer cancel() + lggr := r.newRqLggr() + + lggr.Debug("RPC call: evmclient.Client#SyncProgress") + var syncProgress *ethereum.SyncProgress + start := time.Now() + if http != nil { + syncProgress, err = http.geth.SyncProgress(ctx) + err = r.wrapHTTP(err) + } else { + syncProgress, err = ws.geth.SyncProgress(ctx) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "BlockNumber", + "syncProgress", syncProgress, + ) + + return syncProgress != nil, nil +} + // getChStopInflight provides a convenience helper that mutex wraps a // read to the chStopInFlight func (r *rpcClient) getChStopInflight() chan struct{} { diff --git a/core/chains/evm/config/chain_scoped_node_pool.go b/core/chains/evm/config/chain_scoped_node_pool.go index 8244d620a53..fc52caa0aa3 100644 --- a/core/chains/evm/config/chain_scoped_node_pool.go +++ b/core/chains/evm/config/chain_scoped_node_pool.go @@ -29,3 +29,7 @@ func (n *nodePoolConfig) SyncThreshold() uint32 { func (n *nodePoolConfig) LeaseDuration() time.Duration { return n.c.LeaseDuration.Duration() } + +func (n *nodePoolConfig) NodeIsSyncingEnabled() bool { + return *n.c.NodeIsSyncingEnabled +} diff --git a/core/chains/evm/config/config.go b/core/chains/evm/config/config.go index 2cfc497f462..cb1a6073e0b 100644 --- a/core/chains/evm/config/config.go +++ b/core/chains/evm/config/config.go @@ -139,6 +139,7 @@ type NodePool interface { SelectionMode() string SyncThreshold() uint32 LeaseDuration() time.Duration + NodeIsSyncingEnabled() bool } // TODO BCF-2509 does the chainscopedconfig really need the entire app config? diff --git a/core/chains/evm/config/config_test.go b/core/chains/evm/config/config_test.go index cd1967b9582..f62f540dafc 100644 --- a/core/chains/evm/config/config_test.go +++ b/core/chains/evm/config/config_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" configurl "github.com/smartcontractkit/chainlink-common/pkg/config" + commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -474,6 +475,7 @@ func TestNodePoolConfig(t *testing.T) { require.Equal(t, uint32(5), cfg.EVM().NodePool().SyncThreshold()) require.Equal(t, time.Duration(10000000000), cfg.EVM().NodePool().PollInterval()) require.Equal(t, uint32(5), cfg.EVM().NodePool().PollFailureThreshold()) + require.Equal(t, false, cfg.EVM().NodePool().NodeIsSyncingEnabled()) } func ptr[T any](t T) *T { return &t } diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 0ffe3549613..95b6c342161 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -705,6 +705,7 @@ type NodePool struct { SelectionMode *string SyncThreshold *uint32 LeaseDuration *commonconfig.Duration + NodeIsSyncingEnabled *bool } func (p *NodePool) setFrom(f *NodePool) { @@ -723,6 +724,9 @@ func (p *NodePool) setFrom(f *NodePool) { if v := f.LeaseDuration; v != nil { p.LeaseDuration = v } + if v := f.NodeIsSyncingEnabled; v != nil { + p.NodeIsSyncingEnabled = v + } } type OCR struct { diff --git a/core/chains/evm/config/toml/defaults/fallback.toml b/core/chains/evm/config/toml/defaults/fallback.toml index 7b369142133..8587a5b4b20 100644 --- a/core/chains/evm/config/toml/defaults/fallback.toml +++ b/core/chains/evm/config/toml/defaults/fallback.toml @@ -60,6 +60,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 diff --git a/core/chains/legacyevm/chain.go b/core/chains/legacyevm/chain.go index 66907b8352f..4e0344281cf 100644 --- a/core/chains/legacyevm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -471,19 +471,19 @@ func (c *chain) GasEstimator() gas.EvmFeeEstimator { return c.gasEstimato func newEthClientFromCfg(cfg evmconfig.NodePool, noNewHeadsThreshold time.Duration, lggr logger.Logger, chainID *big.Int, chainType commonconfig.ChainType, nodes []*toml.Node) evmclient.Client { var empty url.URL - var primaries []commonclient.Node[*big.Int, *evmtypes.Head, evmclient.RPCCLient] - var sendonlys []commonclient.SendOnlyNode[*big.Int, evmclient.RPCCLient] + var primaries []commonclient.Node[*big.Int, *evmtypes.Head, evmclient.RPCClient] + var sendonlys []commonclient.SendOnlyNode[*big.Int, evmclient.RPCClient] for i, node := range nodes { if node.SendOnly != nil && *node.SendOnly { rpc := evmclient.NewRPCClient(lggr, empty, (*url.URL)(node.HTTPURL), *node.Name, int32(i), chainID, commonclient.Secondary) - sendonly := commonclient.NewSendOnlyNode[*big.Int, evmclient.RPCCLient](lggr, (url.URL)(*node.HTTPURL), + sendonly := commonclient.NewSendOnlyNode[*big.Int, evmclient.RPCClient](lggr, (url.URL)(*node.HTTPURL), *node.Name, chainID, rpc) sendonlys = append(sendonlys, sendonly) } else { rpc := evmclient.NewRPCClient(lggr, (url.URL)(*node.WSURL), (*url.URL)(node.HTTPURL), *node.Name, int32(i), chainID, commonclient.Primary) - primaryNode := commonclient.NewNode[*big.Int, *evmtypes.Head, evmclient.RPCCLient](cfg, noNewHeadsThreshold, + primaryNode := commonclient.NewNode[*big.Int, *evmtypes.Head, evmclient.RPCClient](cfg, noNewHeadsThreshold, lggr, (url.URL)(*node.WSURL), (*url.URL)(node.HTTPURL), *node.Name, int32(i), chainID, *node.Order, rpc, "EVM") primaries = append(primaries, primaryNode) diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index eec580a8e69..f70dcd0ee45 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -332,6 +332,12 @@ SyncThreshold = 5 # Default # # Set to '0s' to disable LeaseDuration = '0s' # Default +# NodeIsSyncingEnabled is a flag that enables `syncing` health check on each reconnection to an RPC. +# Node transitions and remains in `Syncing` state while RPC signals this state (In case of Ethereum `eth_syncing` returns anything other than false). +# All of the requests to node in state `Syncing` are rejected. +# +# Set true to enable this check +NodeIsSyncingEnabled = false # Default [EVM.OCR] # ContractConfirmations sets `OCR.ContractConfirmations` for this EVM chain. diff --git a/core/internal/testutils/testutils.go b/core/internal/testutils/testutils.go index a38b4707179..9fdd50625cc 100644 --- a/core/internal/testutils/testutils.go +++ b/core/internal/testutils/testutils.go @@ -290,6 +290,8 @@ func (ts *testWSServer) newWSHandler(chainID *big.Int, callback JSONRPCHandler) var resp JSONRPCResponse if chainID != nil && m.String() == "eth_chainId" { resp.Result = `"0x` + chainID.Text(16) + `"` + } else if m.String() == "eth_syncing" { + resp.Result = "false" } else { resp = callback(m.String(), req.Get("params")) } diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 13e18145e73..ed252eadd97 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -559,6 +559,7 @@ func TestConfig_Marshal(t *testing.T) { SelectionMode: &selectionMode, SyncThreshold: ptr[uint32](13), LeaseDuration: &zeroSeconds, + NodeIsSyncingEnabled: ptr(true), }, OCR: evmcfg.OCR{ ContractConfirmations: ptr[uint16](11), @@ -995,6 +996,7 @@ PollInterval = '1m0s' SelectionMode = 'HighestHead' SyncThreshold = 13 LeaseDuration = '0s' +NodeIsSyncingEnabled = true [EVM.OCR] ContractConfirmations = 11 diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index fc47602ef3b..e90f6c6611a 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -324,6 +324,7 @@ PollInterval = '1m0s' SelectionMode = 'HighestHead' SyncThreshold = 13 LeaseDuration = '0s' +NodeIsSyncingEnabled = true [EVM.OCR] ContractConfirmations = 11 diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 2ad6bf30c50..c230a764c74 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -295,6 +295,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [EVM.OCR] ContractConfirmations = 4 @@ -383,6 +384,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [EVM.OCR] ContractConfirmations = 4 @@ -465,6 +467,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [EVM.OCR] ContractConfirmations = 4 diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index 2c84024cde6..f698f55fb25 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -323,6 +323,7 @@ PollInterval = '1m0s' SelectionMode = 'HighestHead' SyncThreshold = 13 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [EVM.OCR] ContractConfirmations = 11 diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 2ad6bf30c50..c230a764c74 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -295,6 +295,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [EVM.OCR] ContractConfirmations = 4 @@ -383,6 +384,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [EVM.OCR] ContractConfirmations = 4 @@ -465,6 +467,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [EVM.OCR] ContractConfirmations = 4 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 0d1a5cc365e..fbb08016552 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Gas bumping logic to the `SuggestedPriceEstimator`. The bumping mechanism for this estimator refetches the price from the RPC and adds a buffer on top using the greater of `BumpPercent` and `BumpMin`. +- Added a new configuration field named `NodeIsSyncingEnabled` for `EVM.NodePool` that will check on every reconnection to an RPC if it's syncing and should not be transitioned to `Alive` state. Disabled by default. - Add preliminary support for "llo" job type (Data Streams V1) - Add `LogPrunePageSize` parameter to the EVM configuration. This parameter controls the number of logs removed during prune phase in LogPoller. Default value is 0, which deletes all logs at once - exactly how it used to work, so it doesn't require any changes on the product's side. - Add Juels Fee Per Coin data source caching for OCR2 Feeds. Cache is time based and is turned on by default with default cache refresh of 5 minutes. Cache can be configured through pluginconfig using "juelsPerFeeCoinCacheDuration" and "juelsPerFeeCoinCacheDisabled" tags. Duration tag accepts values between "30s" and "20m" with default of "0s" that is overridden on cache startup to 5 minutes. diff --git a/docs/CONFIG.md b/docs/CONFIG.md index baafab0d347..e4e25e6694f 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -1658,6 +1658,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -1740,6 +1741,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -1822,6 +1824,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -1904,6 +1907,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -1987,6 +1991,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -2069,6 +2074,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -2151,6 +2157,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -2234,6 +2241,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -2316,6 +2324,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -2397,6 +2406,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -2478,6 +2488,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -2560,6 +2571,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -2643,6 +2655,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -2725,6 +2738,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -2807,6 +2821,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -2889,6 +2904,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -2971,6 +2987,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -3053,6 +3070,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -3135,6 +3153,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -3218,6 +3237,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -3300,6 +3320,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -3381,6 +3402,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -3463,6 +3485,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -3544,6 +3567,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -3626,6 +3650,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -3708,6 +3733,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -3789,6 +3815,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -3870,6 +3897,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -3952,6 +3980,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -4034,6 +4063,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -4115,6 +4145,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -4197,6 +4228,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -4279,6 +4311,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -4362,6 +4395,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -4444,6 +4478,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -4526,6 +4561,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -4608,6 +4644,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -4690,6 +4727,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -4771,6 +4809,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -4852,6 +4891,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -4934,6 +4974,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -5016,6 +5057,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -5098,6 +5140,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -5181,6 +5224,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -5264,6 +5308,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -5346,6 +5391,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 10 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -5428,6 +5474,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -5510,6 +5557,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 1 @@ -5592,6 +5640,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -5674,6 +5723,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -5756,6 +5806,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [OCR] ContractConfirmations = 4 @@ -6389,6 +6440,7 @@ PollInterval = '10s' # Default SelectionMode = 'HighestHead' # Default SyncThreshold = 5 # Default LeaseDuration = '0s' # Default +NodeIsSyncingEnabled = false # Default ``` The node pool manages multiple RPC endpoints. @@ -6440,6 +6492,16 @@ Recommended value is over 5m Set to '0s' to disable +### NodeIsSyncingEnabled +```toml +NodeIsSyncingEnabled = false # Default +``` +NodeIsSyncingEnabled is a flag that enables `syncing` health check on each reconnection to an RPC. +Node transitions and remains in `Syncing` state while RPC signals this state (In case of Ethereum `eth_syncing` returns anything other than false). +All of the requests to node in state `Syncing` are rejected. + +Set true to enable this check + ## EVM.OCR ```toml [EVM.OCR] diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index be61aef4898..bb845b05201 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -351,6 +351,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index bf39b7d8394..cdac1b3702c 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -351,6 +351,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index 0ef1c6a7132..832b3fac584 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -351,6 +351,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index e4eccf9895a..280fa209f0d 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -341,6 +341,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index 9c1026d7f4c..bdd83a9eb31 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -348,6 +348,7 @@ PollInterval = '10s' SelectionMode = 'HighestHead' SyncThreshold = 5 LeaseDuration = '0s' +NodeIsSyncingEnabled = false [EVM.OCR] ContractConfirmations = 4 From 0081ed66fa0e0e5be332c0401dc730541aff451e Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri <180277+snehaagni@users.noreply.github.com> Date: Tue, 27 Feb 2024 12:37:52 -0800 Subject: [PATCH 05/16] release/2.9.0 -> develop (#12150) * move the error to after confirming it is intended to use mercury (#11897) (#11904) (cherry picked from commit 56e24d629d0c6382c2cb5476cfe3a9c1b8d0d182) * do not call an RPC if it's not Alive (#11999) (cherry picked from commit e78d3b81fa50dc05d3f7b6c2777a9dbc8a00f1ad) * Finalize date on changelog for $VERSION Signed-off-by: Sneha Agnihotri * remove streams from sonar coverage * Remove merge artifact in multi_node_test.go --------- Signed-off-by: Sneha Agnihotri Co-authored-by: Lei Co-authored-by: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com> --- docs/CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index fbb08016552..ad7903a33f8 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -25,7 +25,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Minimum required version of Postgres is now >= 12. Postgres 11 was EOL'd in November 2023. Added a new version check that will prevent Chainlink from running on EOL'd Postgres. If you are running Postgres <= 11 you should upgrade to the latest version. The check can be forcibly overridden by setting SKIP_PG_VERSION_CHECK=true. -## 2.9.0 - UNRELEASED + + +## 2.9.0 - 2024-02-22 ### Added @@ -54,8 +56,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ServerPubKey = '...' ``` - - ## 2.8.0 - 2024-01-24 ### Added From bf64c293f99866eb8a92de1cc776b3c4ca966e0e Mon Sep 17 00:00:00 2001 From: Cedric Date: Tue, 27 Feb 2024 20:57:08 +0000 Subject: [PATCH 06/16] Bump chainlink-common (#12195) * Bump chainlink-common * bump again --------- Co-authored-by: Bolek Kulbabinski <1416262+bolekk@users.noreply.github.com> --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- 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 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 5039897bec8..b389d30f4ac 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -20,7 +20,7 @@ require ( github.com/pelletier/go-toml/v2 v2.1.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227191958-d13aad8b4d61 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240215150045-fe2ba71b2f0a diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 066bc9592a5..f90d2aade47 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1173,8 +1173,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.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e h1:3fA2bobIvvfB7Ia8XQhpJRAXlm+Fbw7NZaCw4GaclIk= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227191958-d13aad8b4d61 h1:5aer8Pkw3XlXLjcPzWozy3RAbYQjVjpruxDeQENcVMw= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227191958-d13aad8b4d61/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= diff --git a/go.mod b/go.mod index 6dab12267db..b01750a139a 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,7 @@ require ( github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chain-selectors v1.0.10 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227191958-d13aad8b4d61 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 diff --git a/go.sum b/go.sum index e04289334ee..5e386c5b0de 100644 --- a/go.sum +++ b/go.sum @@ -1168,8 +1168,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.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e h1:3fA2bobIvvfB7Ia8XQhpJRAXlm+Fbw7NZaCw4GaclIk= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227191958-d13aad8b4d61 h1:5aer8Pkw3XlXLjcPzWozy3RAbYQjVjpruxDeQENcVMw= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227191958-d13aad8b4d61/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= 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 caec4f79373..a44980cc265 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -23,7 +23,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227191958-d13aad8b4d61 github.com/smartcontractkit/chainlink-testing-framework v1.24.1 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 0340d6b31ac..8c69edc0892 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1507,8 +1507,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.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e h1:3fA2bobIvvfB7Ia8XQhpJRAXlm+Fbw7NZaCw4GaclIk= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227191958-d13aad8b4d61 h1:5aer8Pkw3XlXLjcPzWozy3RAbYQjVjpruxDeQENcVMw= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227191958-d13aad8b4d61/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= 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 ab78012d982..c5aba2776c0 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -15,7 +15,7 @@ require ( github.com/rs/zerolog v1.30.0 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227191958-d13aad8b4d61 github.com/smartcontractkit/chainlink-testing-framework v1.24.1 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 a6517124c67..f1566970ac0 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1490,8 +1490,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.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e h1:3fA2bobIvvfB7Ia8XQhpJRAXlm+Fbw7NZaCw4GaclIk= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227153854-c56d79341a8e/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227191958-d13aad8b4d61 h1:5aer8Pkw3XlXLjcPzWozy3RAbYQjVjpruxDeQENcVMw= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240227191958-d13aad8b4d61/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= From e37f7e2d1a1859a0853ad6ecfbe3c339c02d2248 Mon Sep 17 00:00:00 2001 From: Jim W Date: Tue, 27 Feb 2024 17:51:56 -0500 Subject: [PATCH 07/16] alias github.com/pkg/errors to pkgerrors (#11848) * rename some pkgerror pkgs * rename pkgerrors in evm codebase * fix bad import * run go mod tidy * rename more pkgs * cleanup * fix linting issues * fix merge bugs * revert all changes to the core/cmd dir * revert all changes to core/services/relay/evm * fix merge errors --- .../chainlink-cluster/dashboard/dashboard.go | 6 +- core/auth/auth.go | 7 ++- core/bridges/external_initiator.go | 6 +- core/bridges/orm.go | 26 ++++----- core/cbor/cbor.go | 4 +- core/chains/evm/assets/wei.go | 12 ++-- core/chains/evm/client/client.go | 4 +- core/chains/evm/client/client_test.go | 4 +- core/chains/evm/client/erroring_node.go | 52 ++++++++--------- core/chains/evm/client/errors.go | 26 ++++----- core/chains/evm/client/errors_test.go | 6 +- core/chains/evm/client/helpers_test.go | 10 ++-- core/chains/evm/client/node.go | 26 ++++----- core/chains/evm/client/node_lifecycle.go | 6 +- core/chains/evm/client/pool.go | 8 +-- core/chains/evm/client/rpc_client.go | 16 +++--- .../evm/forwarders/forwarder_manager.go | 18 +++--- core/chains/evm/forwarders/orm.go | 10 ++-- core/chains/evm/gas/arbitrum_estimator.go | 12 ++-- .../chains/evm/gas/arbitrum_estimator_test.go | 4 +- .../chains/evm/gas/block_history_estimator.go | 56 +++++++++---------- .../evm/gas/block_history_estimator_test.go | 20 +++---- core/chains/evm/gas/fixed_price_estimator.go | 4 +- core/chains/evm/gas/models.go | 22 ++++---- core/chains/evm/gas/models_test.go | 12 ++-- .../evm/gas/suggested_price_estimator.go | 22 ++++---- .../evm/gas/suggested_price_estimator_test.go | 8 +-- .../evm/headtracker/head_listener_test.go | 4 +- core/chains/evm/headtracker/orm.go | 12 ++-- core/chains/evm/log/broadcaster.go | 8 +-- core/chains/evm/log/orm.go | 26 ++++----- core/chains/evm/log/registrations.go | 14 ++--- core/chains/evm/logpoller/disabled.go | 4 +- core/chains/evm/logpoller/helper_test.go | 4 +- core/chains/evm/logpoller/log_poller.go | 56 +++++++++---------- .../evm/logpoller/log_poller_internal_test.go | 6 +- core/chains/evm/logpoller/orm.go | 10 ++-- core/chains/evm/logpoller/orm_test.go | 14 ++--- core/chains/evm/monitor/balance.go | 4 +- core/chains/evm/monitor/balance_test.go | 4 +- core/chains/evm/txmgr/attempts.go | 40 ++++++------- core/chains/evm/txmgr/attempts_test.go | 6 +- core/chains/evm/txmgr/common.go | 4 +- core/chains/evm/txmgr/nonce_syncer.go | 8 +-- core/chains/evm/txmgr/nonce_syncer_test.go | 4 +- core/chains/evm/txmgr/resender_test.go | 4 +- core/chains/evm/txmgr/transmitchecker.go | 26 ++++----- core/chains/evm/txmgr/transmitchecker_test.go | 14 ++--- .../evm/types/internal/blocks/transactions.go | 4 +- core/chains/evm/types/models.go | 14 ++--- core/chains/evm/types/models_test.go | 6 +- core/chains/evm/types/types.go | 20 +++---- core/chains/evm/utils/ethabi.go | 4 +- core/chains/evm/utils/ethabi_test.go | 4 +- core/config/app_config.go | 4 +- core/config/docs/docs_test.go | 4 +- core/config/parse/parsers.go | 4 +- core/gethwrappers/versions.go | 28 +++++----- core/internal/testutils/httptest/httptest.go | 4 +- core/internal/testutils/keystest/keystest.go | 4 +- core/logger/zap.go | 4 +- core/sessions/localauth/orm.go | 32 +++++------ core/sessions/session.go | 4 +- core/sessions/user.go | 10 ++-- core/sessions/webauthn.go | 8 +-- core/store/migrate/migrate.go | 8 +-- 66 files changed, 423 insertions(+), 422 deletions(-) diff --git a/charts/chainlink-cluster/dashboard/dashboard.go b/charts/chainlink-cluster/dashboard/dashboard.go index 19a596b63e9..54b59ae25e6 100644 --- a/charts/chainlink-cluster/dashboard/dashboard.go +++ b/charts/chainlink-cluster/dashboard/dashboard.go @@ -14,7 +14,7 @@ import ( "github.com/K-Phoen/grabana/timeseries" "github.com/K-Phoen/grabana/timeseries/axis" "github.com/K-Phoen/grabana/variable/query" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" ) /* @@ -954,10 +954,10 @@ func (m *CLClusterDashboard) Deploy(ctx context.Context) error { client := grabana.NewClient(&http.Client{}, m.GrafanaURL, grabana.WithAPIToken(m.GrafanaToken)) folder, err := client.FindOrCreateFolder(ctx, m.Folder) if err != nil { - return errors.Wrap(err, ErrFailedToCreateFolder) + return pkgerrors.Wrap(err, ErrFailedToCreateFolder) } if _, err := client.UpsertDashboard(ctx, folder, m.builder); err != nil { - return errors.Wrap(err, ErrFailedToCreateDashboard) + return pkgerrors.Wrap(err, ErrFailedToCreateDashboard) } return nil } diff --git a/core/auth/auth.go b/core/auth/auth.go index f881afab736..62f1ab528a2 100644 --- a/core/auth/auth.go +++ b/core/auth/auth.go @@ -4,9 +4,10 @@ import ( "encoding/hex" "fmt" + pkgerrors "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/pkg/errors" "golang.org/x/crypto/sha3" ) @@ -14,7 +15,7 @@ var ( // ErrorAuthFailed is a generic authentication failed - but not because of // some system failure on our behalf (i.e. HTTP 5xx), more detail is not // given - ErrorAuthFailed = errors.New("Authentication failed") + ErrorAuthFailed = pkgerrors.New("Authentication failed") ) // Token is used for API authentication. @@ -57,7 +58,7 @@ func HashedSecret(ta *Token, salt string) (string, error) { hasher := sha3.New256() _, err := hasher.Write(hashInput(ta, salt)) if err != nil { - return "", errors.Wrap(err, "error writing external initiator authentication to hasher") + return "", pkgerrors.Wrap(err, "error writing external initiator authentication to hasher") } return hex.EncodeToString(hasher.Sum(nil)), nil } diff --git a/core/bridges/external_initiator.go b/core/bridges/external_initiator.go index 53d00e77bbb..bfec3adb407 100644 --- a/core/bridges/external_initiator.go +++ b/core/bridges/external_initiator.go @@ -5,11 +5,11 @@ import ( "strings" "time" + pkgerrors "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" - - "github.com/pkg/errors" ) // ExternalInitiatorRequest is the incoming record used to create an ExternalInitiator. @@ -42,7 +42,7 @@ func NewExternalInitiator( salt := utils.NewSecret(utils.DefaultSecretSize) hashedSecret, err := auth.HashedSecret(eia, salt) if err != nil { - return nil, errors.Wrap(err, "error hashing secret for external initiator") + return nil, pkgerrors.Wrap(err, "error hashing secret for external initiator") } return &ExternalInitiator{ diff --git a/core/bridges/orm.go b/core/bridges/orm.go index 8ae6b855c88..f4728ea0662 100644 --- a/core/bridges/orm.go +++ b/core/bridges/orm.go @@ -7,7 +7,7 @@ import ( "time" "github.com/jmoiron/sqlx" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -67,7 +67,7 @@ func (o *orm) FindBridge(name BridgeName) (bt BridgeType, err error) { // Expects all bridges to be unique func (o *orm) FindBridges(names []BridgeName) (bts []BridgeType, err error) { if len(names) == 0 { - return nil, errors.Errorf("at least one bridge name is required") + return nil, pkgerrors.Errorf("at least one bridge name is required") } var allFoundBts []BridgeType @@ -99,7 +99,7 @@ func (o *orm) FindBridges(names []BridgeName) (bts []BridgeType, err error) { } allFoundBts = append(allFoundBts, bts...) if len(allFoundBts) != len(names) { - return nil, errors.Errorf("not all bridges exist, asked for %v, exists %v", names, allFoundBts) + return nil, pkgerrors.Errorf("not all bridges exist, asked for %v, exists %v", names, allFoundBts) } return allFoundBts, nil } @@ -128,11 +128,11 @@ func (o *orm) DeleteBridgeType(bt *BridgeType) error { func (o *orm) BridgeTypes(offset int, limit int) (bridges []BridgeType, count int, err error) { err = o.q.Transaction(func(tx pg.Queryer) error { if err = tx.Get(&count, "SELECT COUNT(*) FROM bridge_types"); err != nil { - return errors.Wrap(err, "BridgeTypes failed to get count") + return pkgerrors.Wrap(err, "BridgeTypes failed to get count") } sql := `SELECT * FROM bridge_types ORDER BY name asc LIMIT $1 OFFSET $2;` if err = tx.Select(&bridges, sql, limit, offset); err != nil { - return errors.Wrap(err, "BridgeTypes failed to load bridge_types") + return pkgerrors.Wrap(err, "BridgeTypes failed to load bridge_types") } return nil }, pg.OptReadOnlyTx()) @@ -157,7 +157,7 @@ func (o *orm) CreateBridgeType(bt *BridgeType) error { o.bridgeTypesCache.Store(bt.Name, *bt) } - return errors.Wrap(err, "CreateBridgeType failed") + return pkgerrors.Wrap(err, "CreateBridgeType failed") } // UpdateBridgeType updates the bridge type. @@ -179,7 +179,7 @@ func (o *orm) GetCachedResponse(dotId string, specId int32, maxElapsed time.Dura finished_at > ($3) ORDER BY finished_at DESC LIMIT 1;` - err = errors.Wrap(o.q.Get(&response, sql, dotId, specId, stalenessThreshold), fmt.Sprintf("failed to fetch last good value for task %s spec %d", dotId, specId)) + err = pkgerrors.Wrap(o.q.Get(&response, sql, dotId, specId, stalenessThreshold), fmt.Sprintf("failed to fetch last good value for task %s spec %d", dotId, specId)) return } @@ -190,7 +190,7 @@ func (o *orm) UpsertBridgeResponse(dotId string, specId int32, response []byte) DO UPDATE SET value = $3, finished_at = $4;` err := o.q.ExecQ(sql, dotId, specId, response, time.Now()) - return errors.Wrap(err, "failed to upsert bridge response") + return pkgerrors.Wrap(err, "failed to upsert bridge response") } // --- External Initiator @@ -199,12 +199,12 @@ func (o *orm) UpsertBridgeResponse(dotId string, specId int32, response []byte) func (o *orm) ExternalInitiators(offset int, limit int) (exis []ExternalInitiator, count int, err error) { err = o.q.Transaction(func(tx pg.Queryer) error { if err = tx.Get(&count, "SELECT COUNT(*) FROM external_initiators"); err != nil { - return errors.Wrap(err, "ExternalInitiators failed to get count") + return pkgerrors.Wrap(err, "ExternalInitiators failed to get count") } sql := `SELECT * FROM external_initiators ORDER BY name asc LIMIT $1 OFFSET $2;` if err = tx.Select(&exis, sql, limit, offset); err != nil { - return errors.Wrap(err, "ExternalInitiators failed to load external_initiators") + return pkgerrors.Wrap(err, "ExternalInitiators failed to load external_initiators") } return nil }, pg.OptReadOnlyTx()) @@ -221,12 +221,12 @@ func (o *orm) CreateExternalInitiator(externalInitiator *ExternalInitiator) (err var stmt *sqlx.NamedStmt stmt, err = tx.PrepareNamed(query) if err != nil { - return errors.Wrap(err, "failed to prepare named stmt") + return pkgerrors.Wrap(err, "failed to prepare named stmt") } defer stmt.Close() - return errors.Wrap(stmt.Get(externalInitiator, externalInitiator), "failed to load external_initiator") + return pkgerrors.Wrap(stmt.Get(externalInitiator, externalInitiator), "failed to load external_initiator") }) - return errors.Wrap(err, "CreateExternalInitiator failed") + return pkgerrors.Wrap(err, "CreateExternalInitiator failed") } // DeleteExternalInitiator removes an external initiator diff --git a/core/cbor/cbor.go b/core/cbor/cbor.go index cc3f74e423e..fb6e6c88f95 100644 --- a/core/cbor/cbor.go +++ b/core/cbor/cbor.go @@ -6,7 +6,7 @@ import ( "math/big" "github.com/fxamacker/cbor/v2" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" ) // ParseDietCBOR attempts to coerce the input byte array into valid CBOR. @@ -28,7 +28,7 @@ func ParseDietCBOR(b []byte) (map[string]interface{}, error) { output, ok := coerced.(map[string]interface{}) if !ok { - return nil, errors.New("cbor data cannot be coerced to map") + return nil, pkgerrors.New("cbor data cannot be coerced to map") } return output, nil diff --git a/core/chains/evm/assets/wei.go b/core/chains/evm/assets/wei.go index 8bacabfdb4a..3621e4492a4 100644 --- a/core/chains/evm/assets/wei.go +++ b/core/chains/evm/assets/wei.go @@ -6,7 +6,7 @@ import ( "math/big" "strings" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/shopspring/decimal" "golang.org/x/exp/constraints" @@ -185,7 +185,7 @@ func (w *Wei) UnmarshalText(b []byte) error { t = strings.TrimSuffix(t, " ") d, err := decimal.NewFromString(t) if err != nil { - return errors.Wrapf(err, "unable to parse %q", s) + return pkgerrors.Wrapf(err, "unable to parse %q", s) } se := suffixExp(suf) if d.IsInteger() { @@ -196,8 +196,8 @@ func (w *Wei) UnmarshalText(b []byte) error { d = d.Mul(decimal.New(1, se)) if !d.IsInteger() { - err := errors.New("maximum precision is wei") - return errors.Wrapf(err, "unable to parse %q", s) + err := pkgerrors.New("maximum precision is wei") + return pkgerrors.Wrapf(err, "unable to parse %q", s) } *w = (Wei)(*d.BigInt()) return nil @@ -206,13 +206,13 @@ func (w *Wei) UnmarshalText(b []byte) error { // unrecognized or missing suffix d, err := decimal.NewFromString(s) if err != nil { - return errors.Wrapf(err, "unable to parse %q", s) + return pkgerrors.Wrapf(err, "unable to parse %q", s) } if d.IsInteger() { *w = (Wei)(*d.BigInt()) return nil } - return errors.Errorf("unable to parse %q", s) + return pkgerrors.Errorf("unable to parse %q", s) } func (w *Wei) ToInt() *big.Int { diff --git a/core/chains/evm/client/client.go b/core/chains/evm/client/client.go index e2ae8c26403..70d989ae808 100644 --- a/core/chains/evm/client/client.go +++ b/core/chains/evm/client/client.go @@ -22,7 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" ) const queryTimeout = 10 * time.Second @@ -126,7 +126,7 @@ func NewClientWithNodes(lggr logger.Logger, selectionMode string, leaseDuration // node's remote chain ID matches the local one func (client *client) Dial(ctx context.Context) error { if err := client.pool.Dial(ctx); err != nil { - return errors.Wrap(err, "failed to dial pool") + return pkgerrors.Wrap(err, "failed to dial pool") } return nil } diff --git a/core/chains/evm/client/client_test.go b/core/chains/evm/client/client_test.go index 281b8bf4227..62acf146e48 100644 --- a/core/chains/evm/client/client_test.go +++ b/core/chains/evm/client/client_test.go @@ -17,7 +17,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" @@ -145,7 +145,7 @@ func TestEthClient_TransactionReceipt(t *testing.T) { hash := common.HexToHash(txHash) _, err = ethClient.TransactionReceipt(testutils.Context(t), hash) - require.Equal(t, ethereum.NotFound, errors.Cause(err)) + require.Equal(t, ethereum.NotFound, pkgerrors.Cause(err)) } }) } diff --git a/core/chains/evm/client/erroring_node.go b/core/chains/evm/client/erroring_node.go index 059f76d608a..00e8465bca3 100644 --- a/core/chains/evm/client/erroring_node.go +++ b/core/chains/evm/client/erroring_node.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" ) var _ Node = (*erroringNode)(nil) @@ -27,104 +27,104 @@ func (e *erroringNode) SubscribersCount() int32 { func (e *erroringNode) ChainID() (chainID *big.Int) { return nil } -func (e *erroringNode) Start(ctx context.Context) error { return errors.New(e.errMsg) } +func (e *erroringNode) Start(ctx context.Context) error { return pkgerrors.New(e.errMsg) } func (e *erroringNode) Close() error { return nil } func (e *erroringNode) Verify(ctx context.Context, expectedChainID *big.Int) (err error) { - return errors.New(e.errMsg) + return pkgerrors.New(e.errMsg) } func (e *erroringNode) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { - return errors.New(e.errMsg) + return pkgerrors.New(e.errMsg) } func (e *erroringNode) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { - return errors.New(e.errMsg) + return pkgerrors.New(e.errMsg) } func (e *erroringNode) SendTransaction(ctx context.Context, tx *types.Transaction) error { - return errors.New(e.errMsg) + return pkgerrors.New(e.errMsg) } func (e *erroringNode) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { - return 0, errors.New(e.errMsg) + return 0, pkgerrors.New(e.errMsg) } func (e *erroringNode) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { - return 0, errors.New(e.errMsg) + return 0, pkgerrors.New(e.errMsg) } func (e *erroringNode) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) BlockNumber(ctx context.Context) (uint64, error) { - return 0, errors.New(e.errMsg) + return 0, pkgerrors.New(e.errMsg) } func (e *erroringNode) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) { - return 0, errors.New(e.errMsg) + return 0, pkgerrors.New(e.errMsg) } func (e *erroringNode) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) HeaderByNumber(_ context.Context, _ *big.Int) (*types.Header, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) HeaderByHash(_ context.Context, _ common.Hash) (*types.Header, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) EthSubscribe(ctx context.Context, channel chan<- *evmtypes.Head, args ...interface{}) (ethereum.Subscription, error) { - return nil, errors.New(e.errMsg) + return nil, pkgerrors.New(e.errMsg) } func (e *erroringNode) String() string { diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go index 1c470f0cfc6..42a983b40d7 100644 --- a/core/chains/evm/client/errors.go +++ b/core/chains/evm/client/errors.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -36,7 +36,7 @@ func (s *SendError) Fatal() bool { // CauseStr returns the string of the original error func (s *SendError) CauseStr() string { if s.err != nil { - return errors.Cause(s.err).Error() + return pkgerrors.Cause(s.err).Error() } return "" } @@ -316,7 +316,7 @@ func (s *SendError) IsTimeout() bool { if s.err == nil { return false } - return errors.Is(s.err, context.DeadlineExceeded) + return pkgerrors.Is(s.err, context.DeadlineExceeded) } // IsCanceled indicates if the error was caused by an context cancellation @@ -327,18 +327,18 @@ func (s *SendError) IsCanceled() bool { if s.err == nil { return false } - return errors.Is(s.err, context.Canceled) + return pkgerrors.Is(s.err, context.Canceled) } func NewFatalSendError(e error) *SendError { if e == nil { return nil } - return &SendError{err: errors.WithStack(e), fatal: true} + return &SendError{err: pkgerrors.WithStack(e), fatal: true} } func NewSendErrorS(s string) *SendError { - return NewSendError(errors.New(s)) + return NewSendError(pkgerrors.New(s)) } func NewSendError(e error) *SendError { @@ -346,7 +346,7 @@ func NewSendError(e error) *SendError { return nil } fatal := isFatalSendError(e) - return &SendError{err: errors.WithStack(e), fatal: fatal} + return &SendError{err: pkgerrors.WithStack(e), fatal: fatal} } // Geth/parity returns these errors if the transaction failed in such a way that: @@ -356,7 +356,7 @@ func isFatalSendError(err error) bool { if err == nil { return false } - str := errors.Cause(err).Error() + str := pkgerrors.Cause(err).Error() for _, client := range clients { if _, ok := client[Fatal]; !ok { continue @@ -414,20 +414,20 @@ func ExtractRPCErrorOrNil(err error) *JsonError { // { "error": { "code": 3, "data": "0xABC123...", "message": "execution reverted: hello world" } } // revert reason automatically parsed if a simple require and included in message. func ExtractRPCError(baseErr error) (*JsonError, error) { if baseErr == nil { - return nil, errors.New("no error present") + return nil, pkgerrors.New("no error present") } - cause := errors.Cause(baseErr) + cause := pkgerrors.Cause(baseErr) jsonBytes, err := json.Marshal(cause) if err != nil { - return nil, errors.Wrap(err, "unable to marshal err to json") + return nil, pkgerrors.Wrap(err, "unable to marshal err to json") } jErr := JsonError{} err = json.Unmarshal(jsonBytes, &jErr) if err != nil { - return nil, errors.Wrapf(err, "unable to unmarshal json into jsonError struct (got: %v)", baseErr) + return nil, pkgerrors.Wrapf(err, "unable to unmarshal json into jsonError struct (got: %v)", baseErr) } if jErr.Code == 0 { - return nil, errors.Errorf("not a RPCError because it does not have a code (got: %v)", baseErr) + return nil, pkgerrors.Errorf("not a RPCError because it does not have a code (got: %v)", baseErr) } return &jErr, nil } diff --git a/core/chains/evm/client/errors_test.go b/core/chains/evm/client/errors_test.go index f47b93c0400..e6ba89fcb5a 100644 --- a/core/chains/evm/client/errors_test.go +++ b/core/chains/evm/client/errors_test.go @@ -3,14 +3,14 @@ package client_test import ( "testing" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" ) func newSendErrorWrapped(s string) *evmclient.SendError { - return evmclient.NewSendError(errors.Wrap(errors.New(s), "wrapped with some old bollocks")) + return evmclient.NewSendError(pkgerrors.Wrap(pkgerrors.New(s), "wrapped with some old bollocks")) } type errorCase struct { @@ -362,7 +362,7 @@ func Test_Eth_Errors_Fatal(t *testing.T) { for _, test := range tests { t.Run(test.message, func(t *testing.T) { - err := evmclient.NewSendError(errors.New(test.message)) + err := evmclient.NewSendError(pkgerrors.New(test.message)) assert.Equal(t, test.expect, err.Fatal()) }) } diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index c1652c19c26..e400d95bef4 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -45,7 +45,7 @@ func NewClientWithTestNode(t *testing.T, nodePoolCfg config.NodePool, noNewHeads } if parsed.Scheme != "ws" && parsed.Scheme != "wss" { - return nil, errors.Errorf("ethereum url scheme must be websocket: %s", parsed.String()) + return nil, pkgerrors.Errorf("ethereum url scheme must be websocket: %s", parsed.String()) } lggr := logger.Sugared(logger.Test(t)) @@ -56,7 +56,7 @@ func NewClientWithTestNode(t *testing.T, nodePoolCfg config.NodePool, noNewHeads var sendonlys []SendOnlyNode for i, url := range sendonlyRPCURLs { if url.Scheme != "http" && url.Scheme != "https" { - return nil, errors.Errorf("sendonly ethereum rpc url scheme must be http(s): %s", url.String()) + return nil, pkgerrors.Errorf("sendonly ethereum rpc url scheme must be http(s): %s", url.String()) } s := NewSendOnlyNode(lggr, url, fmt.Sprintf("eth-sendonly-%d", i), chainID) sendonlys = append(sendonlys, s) @@ -89,7 +89,7 @@ func NewChainClientWithTestNode( } if parsed.Scheme != "ws" && parsed.Scheme != "wss" { - return nil, errors.Errorf("ethereum url scheme must be websocket: %s", parsed.String()) + return nil, pkgerrors.Errorf("ethereum url scheme must be websocket: %s", parsed.String()) } lggr := logger.Test(t) @@ -102,7 +102,7 @@ func NewChainClientWithTestNode( var sendonlys []commonclient.SendOnlyNode[*big.Int, RPCClient] for i, u := range sendonlyRPCURLs { if u.Scheme != "http" && u.Scheme != "https" { - return nil, errors.Errorf("sendonly ethereum rpc url scheme must be http(s): %s", u.String()) + return nil, pkgerrors.Errorf("sendonly ethereum rpc url scheme must be http(s): %s", u.String()) } var empty url.URL rpc := NewRPCClient(lggr, empty, &sendonlyRPCURLs[i], fmt.Sprintf("eth-sendonly-rpc-%d", i), id, chainID, commonclient.Secondary) diff --git a/core/chains/evm/client/node.go b/core/chains/evm/client/node.go index aa472d605a6..474ff2700b4 100644 --- a/core/chains/evm/client/node.go +++ b/core/chains/evm/client/node.go @@ -15,7 +15,7 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" "github.com/google/uuid" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -245,7 +245,7 @@ func (n *node) start(startCtx context.Context) { verifyCtx, verifyCancel := n.makeQueryCtx(startCtx) defer verifyCancel() - if err := n.verify(verifyCtx); errors.Is(err, errInvalidChainID) { + if err := n.verify(verifyCtx); pkgerrors.Is(err, errInvalidChainID) { n.lfcLog.Errorw("Verify failed: EVM Node has the wrong chain ID", "err", err) n.declareInvalidChainID() return @@ -274,7 +274,7 @@ func (n *node) dial(callerCtx context.Context) error { wsrpc, err := rpc.DialWebsocket(ctx, n.ws.uri.String(), "") if err != nil { promEVMPoolRPCNodeDialsFailed.WithLabelValues(n.chainID.String(), n.name).Inc() - return errors.Wrapf(err, "error while dialing websocket: %v", n.ws.uri.Redacted()) + return pkgerrors.Wrapf(err, "error while dialing websocket: %v", n.ws.uri.Redacted()) } var httprpc *rpc.Client @@ -282,7 +282,7 @@ func (n *node) dial(callerCtx context.Context) error { httprpc, err = rpc.DialHTTP(n.http.uri.String()) if err != nil { promEVMPoolRPCNodeDialsFailed.WithLabelValues(n.chainID.String(), n.name).Inc() - return errors.Wrapf(err, "error while dialing HTTP: %v", n.http.uri.Redacted()) + return pkgerrors.Wrapf(err, "error while dialing HTTP: %v", n.http.uri.Redacted()) } } @@ -299,7 +299,7 @@ func (n *node) dial(callerCtx context.Context) error { return nil } -var errInvalidChainID = errors.New("invalid chain id") +var errInvalidChainID = pkgerrors.New("invalid chain id") // verify checks that all connections to eth nodes match the given chain ID // Not thread-safe @@ -323,10 +323,10 @@ func (n *node) verify(callerCtx context.Context) (err error) { var chainID *big.Int if chainID, err = n.ws.geth.ChainID(ctx); err != nil { promFailed() - return errors.Wrapf(err, "failed to verify chain ID for node %s", n.name) + return pkgerrors.Wrapf(err, "failed to verify chain ID for node %s", n.name) } else if chainID.Cmp(n.chainID) != 0 { promFailed() - return errors.Wrapf( + return pkgerrors.Wrapf( errInvalidChainID, "websocket rpc ChainID doesn't match local chain ID: RPC ID=%s, local ID=%s, node name=%s", chainID.String(), @@ -337,10 +337,10 @@ func (n *node) verify(callerCtx context.Context) (err error) { if n.http != nil { if chainID, err = n.http.geth.ChainID(ctx); err != nil { promFailed() - return errors.Wrapf(err, "failed to verify chain ID for node %s", n.name) + return pkgerrors.Wrapf(err, "failed to verify chain ID for node %s", n.name) } else if chainID.Cmp(n.chainID) != 0 { promFailed() - return errors.Wrapf( + return pkgerrors.Wrapf( errInvalidChainID, "http rpc ChainID doesn't match local chain ID: RPC ID=%s, local ID=%s, node name=%s", chainID.String(), @@ -1094,10 +1094,10 @@ func wrap(err error, tp string) error { if err == nil { return nil } - if errors.Cause(err).Error() == "context deadline exceeded" { - err = errors.Wrap(err, "remote eth node timed out") + if pkgerrors.Cause(err).Error() == "context deadline exceeded" { + err = pkgerrors.Wrap(err, "remote eth node timed out") } - return errors.Wrapf(err, "%s call failed", tp) + return pkgerrors.Wrapf(err, "%s call failed", tp) } // makeLiveQueryCtxAndSafeGetClients wraps makeQueryCtx but returns error if node is not NodeStateAlive. @@ -1106,7 +1106,7 @@ func (n *node) makeLiveQueryCtxAndSafeGetClients(parentCtx context.Context) (ctx // context n.stateMu.RLock() if n.state != NodeStateAlive { - err = errors.Errorf("cannot execute RPC call on node with state: %s", n.state) + err = pkgerrors.Errorf("cannot execute RPC call on node with state: %s", n.state) n.stateMu.RUnlock() return } diff --git a/core/chains/evm/client/node_lifecycle.go b/core/chains/evm/client/node_lifecycle.go index f2232a14935..41add532222 100644 --- a/core/chains/evm/client/node_lifecycle.go +++ b/core/chains/evm/client/node_lifecycle.go @@ -7,7 +7,7 @@ import ( "math/big" "time" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -381,7 +381,7 @@ func (n *node) unreachableLoop() { err = n.verify(n.nodeCtx) - if errors.Is(err, errInvalidChainID) { + if pkgerrors.Is(err, errInvalidChainID) { lggr.Errorw("Failed to redial RPC node; remote endpoint returned the wrong chain ID", "err", err) n.declareInvalidChainID() return @@ -426,7 +426,7 @@ func (n *node) invalidChainIDLoop() { return case <-time.After(chainIDRecheckBackoff.Duration()): err := n.verify(n.nodeCtx) - if errors.Is(err, errInvalidChainID) { + if pkgerrors.Is(err, errInvalidChainID) { lggr.Errorw("Failed to verify RPC node; remote endpoint returned the wrong chain ID", "err", err) continue } else if err != nil { diff --git a/core/chains/evm/client/pool.go b/core/chains/evm/client/pool.go index 3c33b3dbd0a..891fd8c9226 100644 --- a/core/chains/evm/client/pool.go +++ b/core/chains/evm/client/pool.go @@ -11,7 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -133,12 +133,12 @@ func NewPool(lggr logger.Logger, selectionMode string, leaseDuration time.Durati func (p *Pool) Dial(ctx context.Context) error { return p.StartOnce("Pool", func() (merr error) { if len(p.nodes) == 0 { - return errors.Errorf("no available nodes for chain %s", p.chainID.String()) + return pkgerrors.Errorf("no available nodes for chain %s", p.chainID.String()) } var ms services.MultiStart for _, n := range p.nodes { if n.ChainID().Cmp(p.chainID) != 0 { - return ms.CloseBecause(errors.Errorf("node %s has chain ID %s which does not match pool chain ID of %s", n.String(), n.ChainID().String(), p.chainID.String())) + return ms.CloseBecause(pkgerrors.Errorf("node %s has chain ID %s which does not match pool chain ID of %s", n.String(), n.ChainID().String(), p.chainID.String())) } rawNode, ok := n.(*node) if ok { @@ -155,7 +155,7 @@ func (p *Pool) Dial(ctx context.Context) error { } for _, s := range p.sendonlys { if s.ChainID().Cmp(p.chainID) != 0 { - return ms.CloseBecause(errors.Errorf("sendonly node %s has chain ID %s which does not match pool chain ID of %s", s.String(), s.ChainID().String(), p.chainID.String())) + return ms.CloseBecause(pkgerrors.Errorf("sendonly node %s has chain ID %s which does not match pool chain ID of %s", s.String(), s.ChainID().String(), p.chainID.String())) } if err := ms.Start(ctx, s); err != nil { return err diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index d71762ac09a..38d6a123f49 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -16,7 +16,7 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" "github.com/google/uuid" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -130,7 +130,7 @@ func (r *rpcClient) Dial(callerCtx context.Context) error { wsrpc, err := rpc.DialWebsocket(ctx, r.ws.uri.String(), "") if err != nil { promEVMPoolRPCNodeDialsFailed.WithLabelValues(r.chainID.String(), r.name).Inc() - return errors.Wrapf(err, "error while dialing websocket: %v", r.ws.uri.Redacted()) + return pkgerrors.Wrapf(err, "error while dialing websocket: %v", r.ws.uri.Redacted()) } r.ws.rpc = wsrpc @@ -159,7 +159,7 @@ func (r *rpcClient) DialHTTP() error { httprpc, err := rpc.DialHTTP(r.http.uri.String()) if err != nil { promEVMPoolRPCNodeDialsFailed.WithLabelValues(r.chainID.String(), r.name).Inc() - return errors.Wrapf(err, "error while dialing HTTP: %v", r.http.uri.Redacted()) + return pkgerrors.Wrapf(err, "error while dialing HTTP: %v", r.http.uri.Redacted()) } r.http.rpc = httprpc @@ -582,7 +582,7 @@ func (r *rpcClient) SendTransaction(ctx context.Context, tx *types.Transaction) func (r *rpcClient) SimulateTransaction(ctx context.Context, tx *types.Transaction) error { // Not Implemented - return errors.New("SimulateTransaction not implemented") + return pkgerrors.New("SimulateTransaction not implemented") } func (r *rpcClient) SendEmptyTransaction( @@ -594,7 +594,7 @@ func (r *rpcClient) SendEmptyTransaction( fromAddress common.Address, ) (txhash string, err error) { // Not Implemented - return "", errors.New("SendEmptyTransaction not implemented") + return "", pkgerrors.New("SendEmptyTransaction not implemented") } // PendingSequenceAt returns one higher than the highest nonce from both mempool and mined transactions @@ -1067,10 +1067,10 @@ func wrapCallError(err error, tp string) error { if err == nil { return nil } - if errors.Cause(err).Error() == "context deadline exceeded" { - err = errors.Wrap(err, "remote node timed out") + if pkgerrors.Cause(err).Error() == "context deadline exceeded" { + err = pkgerrors.Wrap(err, "remote node timed out") } - return errors.Wrapf(err, "%s call failed", tp) + return pkgerrors.Wrapf(err, "%s call failed", tp) } func (r *rpcClient) wrapWS(err error) error { diff --git a/core/chains/evm/forwarders/forwarder_manager.go b/core/chains/evm/forwarders/forwarder_manager.go index cabedf79aee..452bb87cae2 100644 --- a/core/chains/evm/forwarders/forwarder_manager.go +++ b/core/chains/evm/forwarders/forwarder_manager.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/jmoiron/sqlx" @@ -82,7 +82,7 @@ func (f *FwdMgr) Start(ctx context.Context) error { fwdrs, err := f.ORM.FindForwardersByChain(big.Big(*chainId)) if err != nil { - return errors.Wrapf(err, "Failed to retrieve forwarders for chain %d", chainId) + return pkgerrors.Wrapf(err, "Failed to retrieve forwarders for chain %d", chainId) } if len(fwdrs) != 0 { f.initForwardersCache(ctx, fwdrs) @@ -93,12 +93,12 @@ func (f *FwdMgr) Start(ctx context.Context) error { f.authRcvr, err = authorized_receiver.NewAuthorizedReceiver(common.Address{}, f.evmClient) if err != nil { - return errors.Wrap(err, "Failed to init AuthorizedReceiver") + return pkgerrors.Wrap(err, "Failed to init AuthorizedReceiver") } f.offchainAgg, err = offchain_aggregator_wrapper.NewOffchainAggregator(common.Address{}, f.evmClient) if err != nil { - return errors.Wrap(err, "Failed to init OffchainAggregator") + return pkgerrors.Wrap(err, "Failed to init OffchainAggregator") } f.wg.Add(1) @@ -130,7 +130,7 @@ func (f *FwdMgr) ForwarderFor(addr common.Address) (forwarder common.Address, er } } } - return common.Address{}, errors.Errorf("Cannot find forwarder for given EOA") + return common.Address{}, pkgerrors.Errorf("Cannot find forwarder for given EOA") } func (f *FwdMgr) ConvertPayload(dest common.Address, origPayload []byte) ([]byte, error) { @@ -148,7 +148,7 @@ func (f *FwdMgr) ConvertPayload(dest common.Address, origPayload []byte) ([]byte func (f *FwdMgr) getForwardedPayload(dest common.Address, origPayload []byte) ([]byte, error) { callArgs, err := forwardABI.Inputs.Pack(dest, origPayload) if err != nil { - return nil, errors.Wrap(err, "Failed to pack forwarder payload") + return nil, pkgerrors.Wrap(err, "Failed to pack forwarder payload") } dataBytes := append(forwardABI.ID, callArgs...) @@ -161,7 +161,7 @@ func (f *FwdMgr) getContractSenders(addr common.Address) ([]common.Address, erro } senders, err := f.getAuthorizedSenders(f.ctx, addr) if err != nil { - return nil, errors.Wrapf(err, "Failed to call getAuthorizedSenders on %s", addr) + return nil, pkgerrors.Wrapf(err, "Failed to call getAuthorizedSenders on %s", addr) } f.setCachedSenders(addr, senders) if err = f.subscribeSendersChangedLogs(addr); err != nil { @@ -173,7 +173,7 @@ func (f *FwdMgr) getContractSenders(addr common.Address) ([]common.Address, erro func (f *FwdMgr) getAuthorizedSenders(ctx context.Context, addr common.Address) ([]common.Address, error) { c, err := authorized_receiver.NewAuthorizedReceiverCaller(addr, f.evmClient) if err != nil { - return nil, errors.Wrap(err, "Failed to init forwarder caller") + return nil, pkgerrors.Wrap(err, "Failed to init forwarder caller") } opts := bind.CallOpts{Context: ctx, Pending: false} senders, err := c.GetAuthorizedSenders(&opts) @@ -296,7 +296,7 @@ func (f *FwdMgr) handleAuthChange(log evmlogpoller.Log) error { if ethLog.Topics[0] == authChangedTopic { event, err := f.authRcvr.ParseAuthorizedSendersChanged(ethLog) if err != nil { - return errors.New("Failed to parse senders change log") + return pkgerrors.New("Failed to parse senders change log") } f.setCachedSenders(event.Raw.Address, event.Senders) } diff --git a/core/chains/evm/forwarders/orm.go b/core/chains/evm/forwarders/orm.go index 2a455360190..8b4ba5273a3 100644 --- a/core/chains/evm/forwarders/orm.go +++ b/core/chains/evm/forwarders/orm.go @@ -5,7 +5,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/jmoiron/sqlx" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" @@ -64,7 +64,7 @@ func (o *orm) DeleteForwarder(id int64, cleanup func(tx pg.Queryer, evmChainID i // If the forwarder wasn't found, we still want to delete the filter. // In that case, the transaction must return nil, even though DeleteForwarder // will return sql.ErrNoRows - if err2 != nil && !errors.Is(err2, sql.ErrNoRows) { + if err2 != nil && !pkgerrors.Is(err2, sql.ErrNoRows) { return err2 } rowsAffected, err2 = result.RowsAffected() @@ -116,19 +116,19 @@ func (o *orm) FindForwardersInListByChain(evmChainId big.Big, addrs []common.Add ) if err != nil { - return nil, errors.Wrap(err, "Failed to format query") + return nil, pkgerrors.Wrap(err, "Failed to format query") } query, args, err = sqlx.In(query, args...) if err != nil { - return nil, errors.Wrap(err, "Failed to run sqlx.IN on query") + return nil, pkgerrors.Wrap(err, "Failed to run sqlx.IN on query") } query = o.q.Rebind(query) err = o.q.Select(&fwdrs, query, args...) if err != nil { - return nil, errors.Wrap(err, "Failed to execute query") + return nil, pkgerrors.Wrap(err, "Failed to execute query") } return fwdrs, nil diff --git a/core/chains/evm/gas/arbitrum_estimator.go b/core/chains/evm/gas/arbitrum_estimator.go index a94fddc0e9d..525d439e3e4 100644 --- a/core/chains/evm/gas/arbitrum_estimator.go +++ b/core/chains/evm/gas/arbitrum_estimator.go @@ -11,7 +11,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" @@ -76,7 +76,7 @@ func (a *arbitrumEstimator) Name() string { func (a *arbitrumEstimator) Start(ctx context.Context) error { return a.StartOnce("ArbitrumEstimator", func() error { if err := a.EvmEstimator.Start(ctx); err != nil { - return errors.Wrap(err, "failed to start gas price estimator") + return pkgerrors.Wrap(err, "failed to start gas price estimator") } go a.run() <-a.chInitialised @@ -86,7 +86,7 @@ func (a *arbitrumEstimator) Start(ctx context.Context) error { func (a *arbitrumEstimator) Close() error { return a.StopOnce("ArbitrumEstimator", func() (err error) { close(a.chStop) - err = errors.Wrap(a.EvmEstimator.Close(), "failed to stop gas price estimator") + err = pkgerrors.Wrap(a.EvmEstimator.Close(), "failed to stop gas price estimator") <-a.chDone return }) @@ -117,7 +117,7 @@ func (a *arbitrumEstimator) GetLegacyGas(ctx context.Context, calldata []byte, l select { case a.chForceRefetch <- ch: case <-a.chStop: - err = errors.New("estimator stopped") + err = pkgerrors.New("estimator stopped") return case <-ctx.Done(): err = ctx.Err() @@ -126,7 +126,7 @@ func (a *arbitrumEstimator) GetLegacyGas(ctx context.Context, calldata []byte, l select { case <-ch: case <-a.chStop: - err = errors.New("estimator stopped") + err = pkgerrors.New("estimator stopped") return case <-ctx.Done(): err = ctx.Err() @@ -139,7 +139,7 @@ func (a *arbitrumEstimator) GetLegacyGas(ctx context.Context, calldata []byte, l "perL1CalldataUnit", perL1CalldataUnit, "chainSpecificGasLimit", chainSpecificGasLimit) }) if !ok { - return nil, 0, errors.New("estimator is not started") + return nil, 0, pkgerrors.New("estimator is not started") } else if err != nil { return } diff --git a/core/chains/evm/gas/arbitrum_estimator_test.go b/core/chains/evm/gas/arbitrum_estimator_test.go index dad48b528bb..e5a5af8d4f8 100644 --- a/core/chains/evm/gas/arbitrum_estimator_test.go +++ b/core/chains/evm/gas/arbitrum_estimator_test.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -149,7 +149,7 @@ func TestArbitrumEstimator(t *testing.T) { ethClient := mocks.NewETHClient(t) o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{}, client, ethClient) - client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(errors.New("kaboom")) + client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(pkgerrors.New("kaboom")) ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { callMsg := args.Get(1).(ethereum.CallMsg) blockNumber := args.Get(2).(*big.Int) diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index c8eee2f1bd0..66f3e5d2e67 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -11,7 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -204,7 +204,7 @@ func (b *BlockHistoryEstimator) Start(ctx context.Context) error { b.logger.Infof("Inclusion checking enabled, bumping will be prevented on transactions that have been priced above the %d percentile for %d blocks", b.bhConfig.CheckInclusionPercentile(), b.bhConfig.CheckInclusionBlocks()) } if b.bhConfig.BlockHistorySize() == 0 { - return errors.New("BlockHistorySize must be set to a value greater than 0") + return pkgerrors.New("BlockHistorySize must be set to a value greater than 0") } fetchCtx, cancel := context.WithTimeout(ctx, MaxStartTime) @@ -222,7 +222,7 @@ func (b *BlockHistoryEstimator) Start(ctx context.Context) error { // NOTE: This only checks the start context, not the fetch context if ctx.Err() != nil { - return errors.Wrap(ctx.Err(), "failed to start BlockHistoryEstimator due to main context error") + return pkgerrors.Wrap(ctx.Err(), "failed to start BlockHistoryEstimator due to main context error") } b.wg.Add(1) @@ -253,11 +253,11 @@ func (b *BlockHistoryEstimator) GetLegacyGas(_ context.Context, _ []byte, gasLim gasPrice = b.getGasPrice() }) if !ok { - return nil, 0, errors.New("BlockHistoryEstimator is not started; cannot estimate gas") + return nil, 0, pkgerrors.New("BlockHistoryEstimator is not started; cannot estimate gas") } if gasPrice == nil { if !b.initialFetch.Load() { - return nil, 0, errors.New("BlockHistoryEstimator has not finished the first gas estimation yet, likely because a failure on start") + return nil, 0, pkgerrors.New("BlockHistoryEstimator has not finished the first gas estimation yet, likely because a failure on start") } b.logger.Warnw("Failed to estimate gas price. This is likely because there aren't any valid transactions to estimate from."+ "Using Evm.GasEstimator.PriceDefault as fallback.", "blocks", b.getBlockHistoryNumbers()) @@ -290,7 +290,7 @@ func (b *BlockHistoryEstimator) getTipCap() *assets.Wei { func (b *BlockHistoryEstimator) BumpLegacyGas(_ context.Context, originalGasPrice *assets.Wei, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []EvmPriorAttempt) (bumpedGasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { if b.bhConfig.CheckInclusionBlocks() > 0 { if err = b.checkConnectivity(attempts); err != nil { - if errors.Is(err, commonfee.ErrConnectivity) { + if pkgerrors.Is(err, commonfee.ErrConnectivity) { b.logger.Criticalw(BumpingHaltedLabel, "err", err) b.SvcErrBuffer.Append(err) promBlockHistoryEstimatorConnectivityFailureCount.WithLabelValues(b.chainID.String(), "legacy").Inc() @@ -323,7 +323,7 @@ func (b *BlockHistoryEstimator) checkConnectivity(attempts []EvmPriorAttempt) er if attempt.BroadcastBeforeBlockNum == nil { // this shouldn't happen; any broadcast attempt ought to have a // BroadcastBeforeBlockNum otherwise its an assumption violation - return errors.Errorf("BroadcastBeforeBlockNum was unexpectedly nil for attempt %s", attempt.TxHash) + return pkgerrors.Errorf("BroadcastBeforeBlockNum was unexpectedly nil for attempt %s", attempt.TxHash) } broadcastBeforeBlockNum := *attempt.BroadcastBeforeBlockNum blocksSinceBroadcast := *latestBlockNum - broadcastBeforeBlockNum @@ -352,11 +352,11 @@ func (b *BlockHistoryEstimator) checkConnectivity(attempts []EvmPriorAttempt) er case 0x2: eip1559 = true default: - return errors.Errorf("attempt %s has unknown transaction type 0x%d", attempt.TxHash, attempt.TxType) + return pkgerrors.Errorf("attempt %s has unknown transaction type 0x%d", attempt.TxHash, attempt.TxType) } gasPrice, tipCap, err := b.calculatePercentilePrices(blocks, percentile, eip1559, nil, nil) if err != nil { - if errors.Is(err, ErrNoSuitableTransactions) { + if pkgerrors.Is(err, ErrNoSuitableTransactions) { b.logger.Warnf("no suitable transactions found to verify if transaction %s has been included within expected inclusion blocks of %d", attempt.TxHash, expectInclusionWithinBlocks) return nil } @@ -365,7 +365,7 @@ func (b *BlockHistoryEstimator) checkConnectivity(attempts []EvmPriorAttempt) er } if !eip1559 { if attempt.GasPrice.Cmp(gasPrice) > 0 { - return errors.Wrapf(commonfee.ErrConnectivity, "transaction %s has gas price of %s, which is above percentile=%d%% (percentile price: %s) for blocks %d thru %d (checking %d blocks)", attempt.TxHash, attempt.GasPrice, percentile, gasPrice, blockHistory[l-1].Number, blockHistory[0].Number, expectInclusionWithinBlocks) + return pkgerrors.Wrapf(commonfee.ErrConnectivity, "transaction %s has gas price of %s, which is above percentile=%d%% (percentile price: %s) for blocks %d thru %d (checking %d blocks)", attempt.TxHash, attempt.GasPrice, percentile, gasPrice, blockHistory[l-1].Number, blockHistory[0].Number, expectInclusionWithinBlocks) } continue } @@ -382,7 +382,7 @@ func (b *BlockHistoryEstimator) checkConnectivity(attempts []EvmPriorAttempt) er } } if sufficientFeeCap && attempt.DynamicFee.TipCap.Cmp(tipCap) > 0 { - return errors.Wrapf(commonfee.ErrConnectivity, "transaction %s has tip cap of %s, which is above percentile=%d%% (percentile tip cap: %s) for blocks %d thru %d (checking %d blocks)", attempt.TxHash, attempt.DynamicFee.TipCap, percentile, tipCap, blockHistory[l-1].Number, blockHistory[0].Number, expectInclusionWithinBlocks) + return pkgerrors.Wrapf(commonfee.ErrConnectivity, "transaction %s has tip cap of %s, which is above percentile=%d%% (percentile tip cap: %s) for blocks %d thru %d (checking %d blocks)", attempt.TxHash, attempt.DynamicFee.TipCap, percentile, tipCap, blockHistory[l-1].Number, blockHistory[0].Number, expectInclusionWithinBlocks) } } return nil @@ -390,7 +390,7 @@ func (b *BlockHistoryEstimator) checkConnectivity(attempts []EvmPriorAttempt) er func (b *BlockHistoryEstimator) GetDynamicFee(_ context.Context, gasLimit uint32, maxGasPriceWei *assets.Wei) (fee DynamicFee, chainSpecificGasLimit uint32, err error) { if !b.eConfig.EIP1559DynamicFees() { - return fee, 0, errors.New("Can't get dynamic fee, EIP1559 is disabled") + return fee, 0, pkgerrors.New("Can't get dynamic fee, EIP1559 is disabled") } var feeCap *assets.Wei @@ -405,7 +405,7 @@ func (b *BlockHistoryEstimator) GetDynamicFee(_ context.Context, gasLimit uint32 tipCap = b.tipCap if tipCap == nil { if !b.initialFetch.Load() { - err = errors.New("BlockHistoryEstimator has not finished the first gas estimation yet, likely because a failure on start") + err = pkgerrors.New("BlockHistoryEstimator has not finished the first gas estimation yet, likely because a failure on start") return } b.logger.Warnw("Failed to estimate gas price. This is likely because there aren't any valid transactions to estimate from."+ @@ -426,12 +426,12 @@ func (b *BlockHistoryEstimator) GetDynamicFee(_ context.Context, gasLimit uint32 // This shouldn't happen on EIP-1559 blocks, since if the tip cap // is set, Start must have succeeded and we would expect an initial // base fee to be set as well - err = errors.New("BlockHistoryEstimator: no value for latest block base fee; cannot estimate EIP-1559 base fee. Are you trying to run with EIP1559 enabled on a non-EIP1559 chain?") + err = pkgerrors.New("BlockHistoryEstimator: no value for latest block base fee; cannot estimate EIP-1559 base fee. Are you trying to run with EIP1559 enabled on a non-EIP1559 chain?") return } }) if !ok { - return fee, 0, errors.New("BlockHistoryEstimator is not started; cannot estimate gas") + return fee, 0, pkgerrors.New("BlockHistoryEstimator is not started; cannot estimate gas") } if err != nil { return fee, 0, err @@ -464,7 +464,7 @@ func calcFeeCap(latestAvailableBaseFeePerGas *assets.Wei, bufferBlocks int, tipC func (b *BlockHistoryEstimator) BumpDynamicFee(_ context.Context, originalFee DynamicFee, originalGasLimit uint32, maxGasPriceWei *assets.Wei, attempts []EvmPriorAttempt) (bumped DynamicFee, chainSpecificGasLimit uint32, err error) { if b.bhConfig.CheckInclusionBlocks() > 0 { if err = b.checkConnectivity(attempts); err != nil { - if errors.Is(err, commonfee.ErrConnectivity) { + if pkgerrors.Is(err, commonfee.ErrConnectivity) { b.logger.Criticalw(BumpingHaltedLabel, "err", err) b.SvcErrBuffer.Append(err) promBlockHistoryEstimatorConnectivityFailureCount.WithLabelValues(b.chainID.String(), "eip1559").Inc() @@ -531,7 +531,7 @@ func (b *BlockHistoryEstimator) Recalculate(head *evmtypes.Head) { } }) if err != nil { - if errors.Is(err, ErrNoSuitableTransactions) { + if pkgerrors.Is(err, ErrNoSuitableTransactions) { lggr.Debug("No suitable transactions, skipping") } else { lggr.Warnw("Cannot calculate percentile prices", "err", err) @@ -586,12 +586,12 @@ func (b *BlockHistoryEstimator) FetchBlocks(ctx context.Context, head *evmtypes. historySize := b.size if historySize <= 0 { - return errors.Errorf("BlockHistoryEstimator: history size must be > 0, got: %d", historySize) + return pkgerrors.Errorf("BlockHistoryEstimator: history size must be > 0, got: %d", historySize) } highestBlockToFetch := head.Number - blockDelay if highestBlockToFetch < 0 { - return errors.Errorf("BlockHistoryEstimator: cannot fetch, current block height %v is lower than EVM.RPCBlockQueryDelay=%v", head.Number, blockDelay) + return pkgerrors.Errorf("BlockHistoryEstimator: cannot fetch, current block height %v is lower than EVM.RPCBlockQueryDelay=%v", head.Number, blockDelay) } lowestBlockToFetch := head.Number - historySize - blockDelay + 1 if lowestBlockToFetch < 0 { @@ -639,7 +639,7 @@ func (b *BlockHistoryEstimator) FetchBlocks(ctx context.Context, head *evmtypes. for _, req := range reqs { result, err := req.Result, req.Error if err != nil { - if errors.Is(err, evmtypes.ErrMissingBlock) { + if pkgerrors.Is(err, evmtypes.ErrMissingBlock) { num := HexToInt64(req.Args[0]) missingBlocks = append(missingBlocks, num) lggr.Debugw( @@ -655,10 +655,10 @@ func (b *BlockHistoryEstimator) FetchBlocks(ctx context.Context, head *evmtypes. block, is := result.(*evmtypes.Block) if !is { - return errors.Errorf("expected result to be a %T, got %T", &evmtypes.Block{}, result) + return pkgerrors.Errorf("expected result to be a %T, got %T", &evmtypes.Block{}, result) } if block == nil { - return errors.New("invariant violation: got nil block") + return pkgerrors.New("invariant violation: got nil block") } if block.Hash == (common.Hash{}) { lggr.Warnw("Block was missing hash", "block", b, "headNum", head.Number, "blockNum", block.Number) @@ -714,26 +714,26 @@ func (b *BlockHistoryEstimator) batchFetch(ctx context.Context, reqs []rpc.Batch b.logger.Tracew(fmt.Sprintf("Batch fetching blocks %v thru %v", HexToInt64(reqs[i].Args[0]), HexToInt64(reqs[j-1].Args[0]))) err := b.ethClient.BatchCallContext(ctx, reqs[i:j]) - if errors.Is(err, context.DeadlineExceeded) { + if pkgerrors.Is(err, context.DeadlineExceeded) { // We ran out of time, return what we have b.logger.Warnf("Batch fetching timed out; loaded %d/%d results", i, len(reqs)) for k := i; k < len(reqs); k++ { if k < j { - reqs[k].Error = errors.Wrap(err, "request failed") + reqs[k].Error = pkgerrors.Wrap(err, "request failed") } else { - reqs[k].Error = errors.Wrap(err, "request skipped; previous request exceeded deadline") + reqs[k].Error = pkgerrors.Wrap(err, "request skipped; previous request exceeded deadline") } } return nil } else if err != nil { - return errors.Wrap(err, "BlockHistoryEstimator#fetchBlocks error fetching blocks with BatchCallContext") + return pkgerrors.Wrap(err, "BlockHistoryEstimator#fetchBlocks error fetching blocks with BatchCallContext") } } return nil } var ( - ErrNoSuitableTransactions = errors.New("no suitable transactions") + ErrNoSuitableTransactions = pkgerrors.New("no suitable transactions") ) func (b *BlockHistoryEstimator) calculatePercentilePrices(blocks []evmtypes.Block, percentile int, eip1559 bool, f func(gasPrices []*assets.Wei), f2 func(tipCaps []*assets.Wei)) (gasPrice, tipCap *assets.Wei, err error) { @@ -794,7 +794,7 @@ func (b *BlockHistoryEstimator) getPricesFromBlocks(blocks []evmtypes.Block, eip func verifyBlock(block evmtypes.Block, eip1559 bool) error { if eip1559 && block.BaseFeePerGas == nil { - return errors.New("EIP-1559 mode was enabled, but block was missing baseFeePerGas") + return pkgerrors.New("EIP-1559 mode was enabled, but block was missing baseFeePerGas") } return nil } diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go index dfb4adf11ef..0cb160a172c 100644 --- a/core/chains/evm/gas/block_history_estimator_test.go +++ b/core/chains/evm/gas/block_history_estimator_test.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -143,7 +143,7 @@ func TestBlockHistoryEstimator_Start(t *testing.T) { ethClient.On("BatchCallContext", mock.Anything, mock.MatchedBy(func(b []rpc.BatchElem) bool { return len(b) == 1 && b[0].Method == "eth_getBlockByNumber" && b[0].Args[0] == gas.Int64ToHex(41) && b[0].Args[1].(bool) && reflect.TypeOf(b[0].Result) == reflect.TypeOf(&evmtypes.Block{}) - })).Return(errors.Wrap(context.DeadlineExceeded, "some error message")).Once() + })).Return(pkgerrors.Wrap(context.DeadlineExceeded, "some error message")).Once() err := bhe.Start(testutils.Context(t)) require.NoError(t, err) @@ -177,7 +177,7 @@ func TestBlockHistoryEstimator_Start(t *testing.T) { bhe := newBlockHistoryEstimator(t, ethClient, cfg, geCfg, bhCfg) - ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(nil, errors.New("something exploded")) + ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(nil, pkgerrors.New("something exploded")) err := bhe.Start(testutils.Context(t)) require.NoError(t, err) @@ -200,7 +200,7 @@ func TestBlockHistoryEstimator_Start(t *testing.T) { h := &evmtypes.Head{Hash: utils.NewHash(), Number: 42, BaseFeePerGas: assets.NewWeiI(420)} ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h, nil) - ethClient.On("BatchCallContext", mock.Anything, mock.Anything).Return(errors.New("something went wrong")) + ethClient.On("BatchCallContext", mock.Anything, mock.Anything).Return(pkgerrors.New("something went wrong")) err := bhe.Start(testutils.Context(t)) require.NoError(t, err) @@ -223,7 +223,7 @@ func TestBlockHistoryEstimator_Start(t *testing.T) { h := &evmtypes.Head{Hash: utils.NewHash(), Number: 42, BaseFeePerGas: assets.NewWeiI(420)} ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h, nil) - ethClient.On("BatchCallContext", mock.Anything, mock.Anything).Return(errors.New("this error doesn't matter")) + ethClient.On("BatchCallContext", mock.Anything, mock.Anything).Return(pkgerrors.New("this error doesn't matter")) ctx, cancel := context.WithCancel(testutils.Context(t)) cancel() @@ -239,7 +239,7 @@ func TestBlockHistoryEstimator_Start(t *testing.T) { h := &evmtypes.Head{Hash: utils.NewHash(), Number: 42, BaseFeePerGas: assets.NewWeiI(420)} ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h, nil) - ethClient.On("BatchCallContext", mock.Anything, mock.Anything).Return(errors.New("this error doesn't matter")).Run(func(_ mock.Arguments) { + ethClient.On("BatchCallContext", mock.Anything, mock.Anything).Return(pkgerrors.New("this error doesn't matter")).Run(func(_ mock.Arguments) { time.Sleep(gas.MaxStartTime + 1*time.Second) }) @@ -342,7 +342,7 @@ func TestBlockHistoryEstimator_FetchBlocks(t *testing.T) { bhe := newBlockHistoryEstimator(t, ethClient, cfg, geCfg, bhCfg) - ethClient.On("BatchCallContext", mock.Anything, mock.Anything).Return(errors.New("something exploded")) + ethClient.On("BatchCallContext", mock.Anything, mock.Anything).Return(pkgerrors.New("something exploded")) err := bhe.FetchBlocks(testutils.Context(t), cltest.Head(42)) require.Error(t, err) @@ -390,7 +390,7 @@ func TestBlockHistoryEstimator_FetchBlocks(t *testing.T) { elems := args.Get(1).([]rpc.BatchElem) elems[0].Result = &b43 // This errored block (42) will be ignored - elems[1].Error = errors.New("something went wrong") + elems[1].Error = pkgerrors.New("something went wrong") }) ethClient.On("BatchCallContext", mock.Anything, mock.MatchedBy(func(b []rpc.BatchElem) bool { return len(b) == 1 && @@ -2392,7 +2392,7 @@ func TestBlockHistoryEstimator_Bumps(t *testing.T) { _, _, err := bhe.BumpLegacyGas(testutils.Context(t), assets.NewWeiI(42), 100000, maxGasPrice, attempts) require.Error(t, err) - assert.True(t, errors.Is(err, commonfee.ErrConnectivity)) + assert.True(t, pkgerrors.Is(err, commonfee.ErrConnectivity)) assert.Contains(t, err.Error(), fmt.Sprintf("transaction %s has gas price of 1 kwei, which is above percentile=10%% (percentile price: 1 wei) for blocks 1 thru 1 (checking 1 blocks)", attempts[0].TxHash)) }) @@ -2505,7 +2505,7 @@ func TestBlockHistoryEstimator_Bumps(t *testing.T) { _, _, err := bhe.BumpDynamicFee(testutils.Context(t), originalFee, 100000, maxGasPrice, attempts) require.Error(t, err) - assert.True(t, errors.Is(err, commonfee.ErrConnectivity)) + assert.True(t, pkgerrors.Is(err, commonfee.ErrConnectivity)) assert.Contains(t, err.Error(), fmt.Sprintf("transaction %s has tip cap of 25 wei, which is above percentile=10%% (percentile tip cap: 1 wei) for blocks 1 thru 1 (checking 1 blocks)", attempts[0].TxHash)) }) diff --git a/core/chains/evm/gas/fixed_price_estimator.go b/core/chains/evm/gas/fixed_price_estimator.go index 4d9f45a1bd4..a69be3b0d05 100644 --- a/core/chains/evm/gas/fixed_price_estimator.go +++ b/core/chains/evm/gas/fixed_price_estimator.go @@ -3,7 +3,7 @@ package gas import ( "context" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" @@ -99,7 +99,7 @@ func (f *fixedPriceEstimator) GetDynamicFee(_ context.Context, originalGasLimit gasTipCap := f.config.TipCapDefault() if gasTipCap == nil { - return d, 0, errors.New("cannot calculate dynamic fee: EthGasTipCapDefault was not set") + return d, 0, pkgerrors.New("cannot calculate dynamic fee: EthGasTipCapDefault was not set") } chainSpecificGasLimit, err = commonfee.ApplyMultiplier(originalGasLimit, f.config.LimitMultiplier()) if err != nil { diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index 0b3ebdd0ee3..44e85af37ff 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" @@ -187,11 +187,11 @@ func (e *WrappedEvmEstimator) Name() string { func (e *WrappedEvmEstimator) Start(ctx context.Context) error { return e.StartOnce(e.Name(), func() error { if err := e.EvmEstimator.Start(ctx); err != nil { - return errors.Wrap(err, "failed to start EVMEstimator") + return pkgerrors.Wrap(err, "failed to start EVMEstimator") } if e.l1Oracle != nil { if err := e.l1Oracle.Start(ctx); err != nil { - return errors.Wrap(err, "failed to start L1Oracle") + return pkgerrors.Wrap(err, "failed to start L1Oracle") } } return nil @@ -201,9 +201,9 @@ func (e *WrappedEvmEstimator) Close() error { return e.StopOnce(e.Name(), func() error { var errEVM, errOracle error - errEVM = errors.Wrap(e.EvmEstimator.Close(), "failed to stop EVMEstimator") + errEVM = pkgerrors.Wrap(e.EvmEstimator.Close(), "failed to stop EVMEstimator") if e.l1Oracle != nil { - errOracle = errors.Wrap(e.l1Oracle.Close(), "failed to stop L1Oracle") + errOracle = pkgerrors.Wrap(e.l1Oracle.Close(), "failed to stop L1Oracle") } if errEVM != nil { @@ -277,7 +277,7 @@ func (e *WrappedEvmEstimator) GetMaxCost(ctx context.Context, amount assets.Eth, func (e *WrappedEvmEstimator) BumpFee(ctx context.Context, originalFee EvmFee, feeLimit uint32, maxFeePrice *assets.Wei, attempts []EvmPriorAttempt) (bumpedFee EvmFee, chainSpecificFeeLimit uint32, err error) { // validate only 1 fee type is present if (!originalFee.ValidDynamic() && originalFee.Legacy == nil) || (originalFee.ValidDynamic() && originalFee.Legacy != nil) { - err = errors.New("only one dynamic or legacy fee can be defined") + err = pkgerrors.New("only one dynamic or legacy fee can be defined") return } @@ -377,13 +377,13 @@ func bumpGasPrice(cfg bumpConfig, lggr logger.SugaredLogger, currentGasPrice, or bumpedGasPrice = maxBumpedFee(lggr, currentGasPrice, bumpedGasPrice, maxGasPrice, "gas price") if bumpedGasPrice.Cmp(maxGasPrice) > 0 { - return maxGasPrice, errors.Wrapf(commonfee.ErrBumpFeeExceedsLimit, "bumped gas price of %s would exceed configured max gas price of %s (original price was %s). %s", + return maxGasPrice, pkgerrors.Wrapf(commonfee.ErrBumpFeeExceedsLimit, "bumped gas price of %s would exceed configured max gas price of %s (original price was %s). %s", bumpedGasPrice.String(), maxGasPrice, originalGasPrice.String(), label.NodeConnectivityProblemWarning) } else if bumpedGasPrice.Cmp(originalGasPrice) == 0 { // NOTE: This really shouldn't happen since we enforce minimums for // EVM.GasEstimator.BumpPercent and EVM.GasEstimator.BumpMin in the config validation, // but it's here anyway for a "belts and braces" approach - return bumpedGasPrice, errors.Wrapf(commonfee.ErrBump, "bumped gas price of %s is equal to original gas price of %s."+ + return bumpedGasPrice, pkgerrors.Wrapf(commonfee.ErrBump, "bumped gas price of %s is equal to original gas price of %s."+ " ACTION REQUIRED: This is a configuration error, you must increase either "+ "EVM.GasEstimator.BumpPercent or EVM.GasEstimator.BumpMin", bumpedGasPrice.String(), originalGasPrice.String()) } @@ -419,13 +419,13 @@ func bumpDynamicFee(cfg bumpConfig, feeCapBufferBlocks uint16, lggr logger.Sugar bumpedTipCap = maxBumpedFee(lggr, currentTipCap, bumpedTipCap, maxGasPrice, "tip cap") if bumpedTipCap.Cmp(maxGasPrice) > 0 { - return bumpedFee, errors.Wrapf(commonfee.ErrBumpFeeExceedsLimit, "bumped tip cap of %s would exceed configured max gas price of %s (original fee: tip cap %s, fee cap %s). %s", + return bumpedFee, pkgerrors.Wrapf(commonfee.ErrBumpFeeExceedsLimit, "bumped tip cap of %s would exceed configured max gas price of %s (original fee: tip cap %s, fee cap %s). %s", bumpedTipCap.String(), maxGasPrice, originalFee.TipCap.String(), originalFee.FeeCap.String(), label.NodeConnectivityProblemWarning) } else if bumpedTipCap.Cmp(originalFee.TipCap) <= 0 { // NOTE: This really shouldn't happen since we enforce minimums for // EVM.GasEstimator.BumpPercent and EVM.GasEstimator.BumpMin in the config validation, // but it's here anyway for a "belts and braces" approach - return bumpedFee, errors.Wrapf(commonfee.ErrBump, "bumped gas tip cap of %s is less than or equal to original gas tip cap of %s."+ + return bumpedFee, pkgerrors.Wrapf(commonfee.ErrBump, "bumped gas tip cap of %s is less than or equal to original gas tip cap of %s."+ " ACTION REQUIRED: This is a configuration error, you must increase either "+ "EVM.GasEstimator.BumpPercent or EVM.GasEstimator.BumpMin", bumpedTipCap.String(), originalFee.TipCap.String()) } @@ -445,7 +445,7 @@ func bumpDynamicFee(cfg bumpConfig, feeCapBufferBlocks uint16, lggr logger.Sugar } if bumpedFeeCap.Cmp(maxGasPrice) > 0 { - return bumpedFee, errors.Wrapf(commonfee.ErrBumpFeeExceedsLimit, "bumped fee cap of %s would exceed configured max gas price of %s (original fee: tip cap %s, fee cap %s). %s", + return bumpedFee, pkgerrors.Wrapf(commonfee.ErrBumpFeeExceedsLimit, "bumped fee cap of %s would exceed configured max gas price of %s (original fee: tip cap %s, fee cap %s). %s", bumpedFeeCap.String(), maxGasPrice, originalFee.TipCap.String(), originalFee.FeeCap.String(), label.NodeConnectivityProblemWarning) } diff --git a/core/chains/evm/gas/models_test.go b/core/chains/evm/gas/models_test.go index 95a7a471eba..9c0e63a602b 100644 --- a/core/chains/evm/gas/models_test.go +++ b/core/chains/evm/gas/models_test.go @@ -4,7 +4,7 @@ import ( "math/big" "testing" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -201,9 +201,9 @@ func TestWrappedEvmEstimator(t *testing.T) { oracle := rollupMocks.NewL1Oracle(t) evmEstimatorKey := "evm" - evmEstimatorError := errors.New("evm error") + evmEstimatorError := pkgerrors.New("evm error") oracleKey := "oracle" - oracleError := errors.New("oracle error") + oracleError := pkgerrors.New("oracle error") evmEstimator.On("HealthReport").Return(map[string]error{evmEstimatorKey: evmEstimatorError}).Twice() oracle.On("HealthReport").Return(map[string]error{oracleKey: oracleError}).Once() @@ -211,14 +211,14 @@ func TestWrappedEvmEstimator(t *testing.T) { estimator := gas.NewWrappedEvmEstimator(lggr, getEst, false, nil) report := estimator.HealthReport() - require.True(t, errors.Is(report[evmEstimatorKey], evmEstimatorError)) + require.True(t, pkgerrors.Is(report[evmEstimatorKey], evmEstimatorError)) require.Nil(t, report[oracleKey]) require.NotNil(t, report[mockEstimatorName]) estimator = gas.NewWrappedEvmEstimator(lggr, getEst, false, oracle) report = estimator.HealthReport() - require.True(t, errors.Is(report[evmEstimatorKey], evmEstimatorError)) - require.True(t, errors.Is(report[oracleKey], oracleError)) + require.True(t, pkgerrors.Is(report[evmEstimatorKey], evmEstimatorError)) + require.True(t, pkgerrors.Is(report[oracleKey], oracleError)) require.NotNil(t, report[mockEstimatorName]) }) } diff --git a/core/chains/evm/gas/suggested_price_estimator.go b/core/chains/evm/gas/suggested_price_estimator.go index fe6483f40f3..d58a1155b91 100644 --- a/core/chains/evm/gas/suggested_price_estimator.go +++ b/core/chains/evm/gas/suggested_price_estimator.go @@ -8,7 +8,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" @@ -138,14 +138,14 @@ func (o *SuggestedPriceEstimator) forceRefresh(ctx context.Context) (err error) select { case o.chForceRefetch <- ch: case <-o.chStop: - return errors.New("estimator stopped") + return pkgerrors.New("estimator stopped") case <-ctx.Done(): return ctx.Err() } select { case <-ch: case <-o.chStop: - return errors.New("estimator stopped") + return pkgerrors.New("estimator stopped") case <-ctx.Done(): return ctx.Err() } @@ -155,12 +155,12 @@ func (o *SuggestedPriceEstimator) forceRefresh(ctx context.Context) (err error) func (o *SuggestedPriceEstimator) OnNewLongestChain(context.Context, *evmtypes.Head) {} func (*SuggestedPriceEstimator) GetDynamicFee(_ context.Context, _ uint32, _ *assets.Wei) (fee DynamicFee, chainSpecificGasLimit uint32, err error) { - err = errors.New("dynamic fees are not implemented for this estimator") + err = pkgerrors.New("dynamic fees are not implemented for this estimator") return } func (*SuggestedPriceEstimator) BumpDynamicFee(_ context.Context, _ DynamicFee, _ uint32, _ *assets.Wei, _ []EvmPriorAttempt) (bumped DynamicFee, chainSpecificGasLimit uint32, err error) { - err = errors.New("dynamic fees are not implemented for this estimator") + err = pkgerrors.New("dynamic fees are not implemented for this estimator") return } @@ -171,19 +171,19 @@ func (o *SuggestedPriceEstimator) GetLegacyGas(ctx context.Context, _ []byte, Ga err = o.forceRefresh(ctx) } if gasPrice = o.getGasPrice(); gasPrice == nil { - err = errors.New("failed to estimate gas; gas price not set") + err = pkgerrors.New("failed to estimate gas; gas price not set") return } o.logger.Debugw("GetLegacyGas", "GasPrice", gasPrice, "GasLimit", GasLimit) }) if !ok { - return nil, 0, errors.New("estimator is not started") + return nil, 0, pkgerrors.New("estimator is not started") } else if err != nil { return } // For L2 chains, submitting a transaction that is not priced high enough will cause the call to fail, so if the cap is lower than the RPC suggested gas price, this transaction cannot succeed if gasPrice != nil && gasPrice.Cmp(maxGasPriceWei) > 0 { - return nil, 0, errors.Errorf("estimated gas price: %s is greater than the maximum gas price configured: %s", gasPrice.String(), maxGasPriceWei.String()) + return nil, 0, pkgerrors.Errorf("estimated gas price: %s is greater than the maximum gas price configured: %s", gasPrice.String(), maxGasPriceWei.String()) } return } @@ -203,18 +203,18 @@ func (o *SuggestedPriceEstimator) BumpLegacyGas(ctx context.Context, originalFee } err = o.forceRefresh(ctx) if newGasPrice = o.getGasPrice(); newGasPrice == nil { - err = errors.New("failed to refresh and return gas; gas price not set") + err = pkgerrors.New("failed to refresh and return gas; gas price not set") return } o.logger.Debugw("GasPrice", newGasPrice, "GasLimit", feeLimit) }) if !ok { - return nil, 0, errors.New("estimator is not started") + return nil, 0, pkgerrors.New("estimator is not started") } else if err != nil { return } if newGasPrice != nil && newGasPrice.Cmp(maxGasPriceWei) > 0 { - return nil, 0, errors.Errorf("estimated gas price: %s is greater than the maximum gas price configured: %s", newGasPrice.String(), maxGasPriceWei.String()) + return nil, 0, pkgerrors.Errorf("estimated gas price: %s is greater than the maximum gas price configured: %s", newGasPrice.String(), maxGasPriceWei.String()) } // Add a buffer on top of the gas price returned by the RPC. // Bump logic when using the suggested gas price from an RPC is realistically only needed when there is increased volatility in gas price. diff --git a/core/chains/evm/gas/suggested_price_estimator_test.go b/core/chains/evm/gas/suggested_price_estimator_test.go index 9006554564d..c09582774b8 100644 --- a/core/chains/evm/gas/suggested_price_estimator_test.go +++ b/core/chains/evm/gas/suggested_price_estimator_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -87,7 +87,7 @@ func TestSuggestedPriceEstimator(t *testing.T) { client := mocks.NewRPCClient(t) o := gas.NewSuggestedPriceEstimator(logger.Test(t), client, cfg) - client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(errors.New("kaboom")) + client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(pkgerrors.New("kaboom")) servicetest.RunHealthy(t, o) @@ -203,7 +203,7 @@ func TestSuggestedPriceEstimator(t *testing.T) { client := mocks.NewRPCClient(t) o := gas.NewSuggestedPriceEstimator(logger.Test(t), client, cfg) - client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(errors.New("kaboom")) + client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(pkgerrors.New("kaboom")) servicetest.RunHealthy(t, o) @@ -219,7 +219,7 @@ func TestSuggestedPriceEstimator(t *testing.T) { res := args.Get(1).(*hexutil.Big) (*big.Int)(res).SetInt64(40) }).Once() - client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(errors.New("kaboom")) + client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(pkgerrors.New("kaboom")) servicetest.RunHealthy(t, o) diff --git a/core/chains/evm/headtracker/head_listener_test.go b/core/chains/evm/headtracker/head_listener_test.go index 3ba9c0863da..e5131aca422 100644 --- a/core/chains/evm/headtracker/head_listener_test.go +++ b/core/chains/evm/headtracker/head_listener_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -155,7 +155,7 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { closeErr bool }{ {"nil error", nil, false}, - {"socket error", errors.New("close 1006 (abnormal closure): unexpected EOF"), false}, + {"socket error", pkgerrors.New("close 1006 (abnormal closure): unexpected EOF"), false}, {"close Err channel", nil, true}, } diff --git a/core/chains/evm/headtracker/orm.go b/core/chains/evm/headtracker/orm.go index 859f6764b63..a1957388b9b 100644 --- a/core/chains/evm/headtracker/orm.go +++ b/core/chains/evm/headtracker/orm.go @@ -6,7 +6,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/jmoiron/sqlx" @@ -47,7 +47,7 @@ func (orm *orm) IdempotentInsertHead(ctx context.Context, head *evmtypes.Head) e :hash, :number, :parent_hash, :created_at, :timestamp, :l1_block_number, :evm_chain_id, :base_fee_per_gas) ON CONFLICT (evm_chain_id, hash) DO NOTHING` err := q.ExecQNamed(query, head) - return errors.Wrap(err, "IdempotentInsertHead failed to insert head") + return pkgerrors.Wrap(err, "IdempotentInsertHead failed to insert head") } func (orm *orm) TrimOldHeads(ctx context.Context, n uint) (err error) { @@ -69,17 +69,17 @@ func (orm *orm) LatestHead(ctx context.Context) (head *evmtypes.Head, err error) head = new(evmtypes.Head) q := orm.q.WithOpts(pg.WithParentCtx(ctx)) err = q.Get(head, `SELECT * FROM evm.heads WHERE evm_chain_id = $1 ORDER BY number DESC, created_at DESC, id DESC LIMIT 1`, orm.chainID) - if errors.Is(err, sql.ErrNoRows) { + if pkgerrors.Is(err, sql.ErrNoRows) { return nil, nil } - err = errors.Wrap(err, "LatestHead failed") + err = pkgerrors.Wrap(err, "LatestHead failed") return } func (orm *orm) LatestHeads(ctx context.Context, limit uint) (heads []*evmtypes.Head, err error) { q := orm.q.WithOpts(pg.WithParentCtx(ctx)) err = q.Select(&heads, `SELECT * FROM evm.heads WHERE evm_chain_id = $1 ORDER BY number DESC, created_at DESC, id DESC LIMIT $2`, orm.chainID, limit) - err = errors.Wrap(err, "LatestHeads failed") + err = pkgerrors.Wrap(err, "LatestHeads failed") return } @@ -87,7 +87,7 @@ func (orm *orm) HeadByHash(ctx context.Context, hash common.Hash) (head *evmtype q := orm.q.WithOpts(pg.WithParentCtx(ctx)) head = new(evmtypes.Head) err = q.Get(head, `SELECT * FROM evm.heads WHERE evm_chain_id = $1 AND hash = $2`, orm.chainID, hash) - if errors.Is(err, sql.ErrNoRows) { + if pkgerrors.Is(err, sql.ErrNoRows) { return nil, nil } return head, err diff --git a/core/chains/evm/log/broadcaster.go b/core/chains/evm/log/broadcaster.go index 8321dd30bfe..c4db2a4826c 100644 --- a/core/chains/evm/log/broadcaster.go +++ b/core/chains/evm/log/broadcaster.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" @@ -773,13 +773,13 @@ func (n *NullBroadcaster) TrackedAddressesCount() uint32 { return 0 } func (n *NullBroadcaster) WasAlreadyConsumed(lb Broadcast, qopts ...pg.QOpt) (bool, error) { - return false, errors.New(n.ErrMsg) + return false, pkgerrors.New(n.ErrMsg) } func (n *NullBroadcaster) MarkConsumed(lb Broadcast, qopts ...pg.QOpt) error { - return errors.New(n.ErrMsg) + return pkgerrors.New(n.ErrMsg) } func (n *NullBroadcaster) MarkManyConsumed(lbs []Broadcast, qopts ...pg.QOpt) error { - return errors.New(n.ErrMsg) + return pkgerrors.New(n.ErrMsg) } func (n *NullBroadcaster) AddDependents(int) {} diff --git a/core/chains/evm/log/orm.go b/core/chains/evm/log/orm.go index 51ed9f2f132..25012d5c8e0 100644 --- a/core/chains/evm/log/orm.go +++ b/core/chains/evm/log/orm.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/jmoiron/sqlx" @@ -75,7 +75,7 @@ func (o *orm) WasBroadcastConsumed(blockHash common.Hash, logIndex uint, jobID i } q := o.q.WithOpts(qopts...) err = q.Get(&consumed, query, args...) - if errors.Is(err, sql.ErrNoRows) { + if pkgerrors.Is(err, sql.ErrNoRows) { return false, nil } return consumed, err @@ -91,7 +91,7 @@ func (o *orm) FindBroadcasts(fromBlockNum int64, toBlockNum int64) ([]LogBroadca ` err := o.q.Select(&broadcasts, query, fromBlockNum, toBlockNum, o.evmChainID) if err != nil { - return nil, errors.Wrap(err, "failed to find log broadcasts") + return nil, pkgerrors.Wrap(err, "failed to find log broadcasts") } return broadcasts, err } @@ -102,7 +102,7 @@ func (o *orm) CreateBroadcast(blockHash common.Hash, blockNumber uint64, logInde INSERT INTO log_broadcasts (block_hash, block_number, log_index, job_id, created_at, updated_at, consumed, evm_chain_id) VALUES ($1, $2, $3, $4, NOW(), NOW(), false, $5) `, blockHash, blockNumber, logIndex, jobID, o.evmChainID) - return errors.Wrap(err, "failed to create log broadcast") + return pkgerrors.Wrap(err, "failed to create log broadcast") } func (o *orm) MarkBroadcastConsumed(blockHash common.Hash, blockNumber uint64, logIndex uint, jobID int32, qopts ...pg.QOpt) error { @@ -113,7 +113,7 @@ func (o *orm) MarkBroadcastConsumed(blockHash common.Hash, blockNumber uint64, l ON CONFLICT (job_id, block_hash, log_index, evm_chain_id) DO UPDATE SET consumed = true, updated_at = NOW() `, blockHash, blockNumber, logIndex, jobID, o.evmChainID) - return errors.Wrap(err, "failed to mark log broadcast as consumed") + return pkgerrors.Wrap(err, "failed to mark log broadcast as consumed") } // MarkBroadcastsConsumed marks many broadcasts as consumed. @@ -150,7 +150,7 @@ SET consumed = true, updated_at = NOW(); } q := o.q.WithOpts(qopts...) _, err := q.NamedExec(query, inputs) - return errors.Wrap(err, "mark broadcasts consumed") + return pkgerrors.Wrap(err, "mark broadcasts consumed") } // MarkBroadcastsUnconsumed implements the ORM interface. @@ -162,7 +162,7 @@ func (o *orm) MarkBroadcastsUnconsumed(fromBlock int64, qopts ...pg.QOpt) error WHERE block_number >= $1 AND evm_chain_id = $2 `, fromBlock, o.evmChainID) - return errors.Wrap(err, "failed to mark broadcasts unconsumed") + return pkgerrors.Wrap(err, "failed to mark broadcasts unconsumed") } func (o *orm) Reinitialize(qopts ...pg.QOpt) (*int64, error) { @@ -201,7 +201,7 @@ func (o *orm) SetPendingMinBlock(blockNumber *int64, qopts ...pg.QOpt) error { INSERT INTO log_broadcasts_pending (evm_chain_id, block_number, created_at, updated_at) VALUES ($1, $2, NOW(), NOW()) ON CONFLICT (evm_chain_id) DO UPDATE SET block_number = $3, updated_at = NOW() `, o.evmChainID, blockNumber, blockNumber) - return errors.Wrap(err, "failed to set pending broadcast block number") + return pkgerrors.Wrap(err, "failed to set pending broadcast block number") } func (o *orm) GetPendingMinBlock(qopts ...pg.QOpt) (*int64, error) { @@ -210,10 +210,10 @@ func (o *orm) GetPendingMinBlock(qopts ...pg.QOpt) (*int64, error) { err := q.Get(&blockNumber, ` SELECT block_number FROM log_broadcasts_pending WHERE evm_chain_id = $1 `, o.evmChainID) - if errors.Is(err, sql.ErrNoRows) { + if pkgerrors.Is(err, sql.ErrNoRows) { return nil, nil } else if err != nil { - return nil, errors.Wrap(err, "failed to get broadcasts pending number") + return nil, pkgerrors.Wrap(err, "failed to get broadcasts pending number") } return blockNumber, nil } @@ -227,10 +227,10 @@ func (o *orm) getUnconsumedMinBlock(qopts ...pg.QOpt) (*int64, error) { AND consumed = false AND block_number IS NOT NULL `, o.evmChainID) - if errors.Is(err, sql.ErrNoRows) { + if pkgerrors.Is(err, sql.ErrNoRows) { return nil, nil } else if err != nil { - return nil, errors.Wrap(err, "failed to get unconsumed broadcasts min block number") + return nil, pkgerrors.Wrap(err, "failed to get unconsumed broadcasts min block number") } return blockNumber, nil } @@ -243,7 +243,7 @@ func (o *orm) removeUnconsumed(qopts ...pg.QOpt) error { AND consumed = false AND block_number IS NOT NULL `, o.evmChainID) - return errors.Wrap(err, "failed to delete unconsumed broadcasts") + return pkgerrors.Wrap(err, "failed to delete unconsumed broadcasts") } // LogBroadcast - data from log_broadcasts table columns diff --git a/core/chains/evm/log/registrations.go b/core/chains/evm/log/registrations.go index 346a6776e86..1bb6c8c59c0 100644 --- a/core/chains/evm/log/registrations.go +++ b/core/chains/evm/log/registrations.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -119,12 +119,12 @@ func (r *registrations) handlersWithGreaterConfs(confs uint32) (handlersWithGrea // maps modified are only used for checks func (r *registrations) checkAddSubscriber(sub *subscriber) error { if sub.opts.MinIncomingConfirmations <= 0 { - return errors.Errorf("LogBroadcaster requires that MinIncomingConfirmations must be at least 1 (got %v). Logs must have been confirmed in at least 1 block, it does not support reading logs from the mempool before they have been mined", sub.opts.MinIncomingConfirmations) + return pkgerrors.Errorf("LogBroadcaster requires that MinIncomingConfirmations must be at least 1 (got %v). Logs must have been confirmed in at least 1 block, it does not support reading logs from the mempool before they have been mined", sub.opts.MinIncomingConfirmations) } jobID := sub.listener.JobID() if _, exists := r.registeredSubs[sub]; exists { - return errors.Errorf("Cannot add subscriber %p for job ID %v: already added", sub, jobID) + return pkgerrors.Errorf("Cannot add subscriber %p for job ID %v: already added", sub, jobID) } r.registeredSubs[sub] = struct{}{} addrs, exists := r.jobIDAddrs[jobID] @@ -132,7 +132,7 @@ func (r *registrations) checkAddSubscriber(sub *subscriber) error { r.jobIDAddrs[jobID] = make(map[common.Address]struct{}) } if _, exists := addrs[sub.opts.Contract]; exists { - return errors.Errorf("Cannot add subscriber %p: only one subscription is allowed per jobID/contract address. There is already a subscription with job ID %v listening on %s", sub, jobID, sub.opts.Contract.Hex()) + return pkgerrors.Errorf("Cannot add subscriber %p: only one subscription is allowed per jobID/contract address. There is already a subscription with job ID %v listening on %s", sub, jobID, sub.opts.Contract.Hex()) } r.jobIDAddrs[jobID][sub.opts.Contract] = struct{}{} return nil @@ -165,16 +165,16 @@ func (r *registrations) removeSubscriber(sub *subscriber) (needsResubscribe bool func (r *registrations) checkRemoveSubscriber(sub *subscriber) error { jobID := sub.listener.JobID() if _, exists := r.registeredSubs[sub]; !exists { - return errors.Errorf("Cannot remove subscriber %p for job ID %v: not registered", sub, jobID) + return pkgerrors.Errorf("Cannot remove subscriber %p for job ID %v: not registered", sub, jobID) } delete(r.registeredSubs, sub) addrs, exists := r.jobIDAddrs[jobID] if !exists { - return errors.Errorf("Cannot remove subscriber %p: jobIDAddrs was missing job ID %v", sub, jobID) + return pkgerrors.Errorf("Cannot remove subscriber %p: jobIDAddrs was missing job ID %v", sub, jobID) } _, exists = addrs[sub.opts.Contract] if !exists { - return errors.Errorf("Cannot remove subscriber %p: jobIDAddrs was missing address %s", sub, sub.opts.Contract.Hex()) + return pkgerrors.Errorf("Cannot remove subscriber %p: jobIDAddrs was missing address %s", sub, sub.opts.Contract.Hex()) } delete(r.jobIDAddrs[jobID], sub.opts.Contract) if len(r.jobIDAddrs[jobID]) == 0 { diff --git a/core/chains/evm/logpoller/disabled.go b/core/chains/evm/logpoller/disabled.go index 05d591042f4..8d92b8d29f6 100644 --- a/core/chains/evm/logpoller/disabled.go +++ b/core/chains/evm/logpoller/disabled.go @@ -5,13 +5,13 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) var ( - ErrDisabled = errors.New("log poller disabled") + ErrDisabled = pkgerrors.New("log poller disabled") LogPollerDisabled LogPoller = disabled{} ) diff --git a/core/chains/evm/logpoller/helper_test.go b/core/chains/evm/logpoller/helper_test.go index cb0fbd247fc..3215a7ec20c 100644 --- a/core/chains/evm/logpoller/helper_test.go +++ b/core/chains/evm/logpoller/helper_test.go @@ -15,7 +15,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/ethdb" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -99,7 +99,7 @@ func (th *TestHarness) PollAndSaveLogs(ctx context.Context, currentBlockNumber i func (th *TestHarness) assertDontHave(t *testing.T, start, end int) { for i := start; i < end; i++ { _, err := th.ORM.SelectBlockByNumber(int64(i)) - assert.True(t, errors.Is(err, sql.ErrNoRows)) + assert.True(t, pkgerrors.Is(err, sql.ErrNoRows)) } } diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index ba617a2178b..a19bbfda7f1 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -17,7 +17,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "golang.org/x/exp/maps" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -89,9 +89,9 @@ type Client interface { var ( _ LogPollerTest = &logPoller{} - ErrReplayRequestAborted = errors.New("aborted, replay request cancelled") - ErrReplayInProgress = errors.New("replay request cancelled, but replay is already in progress") - ErrLogPollerShutdown = errors.New("replay aborted due to log poller shutdown") + ErrReplayRequestAborted = pkgerrors.New("aborted, replay request cancelled") + ErrReplayInProgress = pkgerrors.New("replay request cancelled, but replay is already in progress") + ErrLogPollerShutdown = pkgerrors.New("replay aborted due to log poller shutdown") ) type logPoller struct { @@ -226,20 +226,20 @@ func (filter *Filter) Contains(other *Filter) bool { // Warnings/debug information is keyed by filter name. func (lp *logPoller) RegisterFilter(filter Filter, qopts ...pg.QOpt) error { if len(filter.Addresses) == 0 { - return errors.Errorf("at least one address must be specified") + return pkgerrors.Errorf("at least one address must be specified") } if len(filter.EventSigs) == 0 { - return errors.Errorf("at least one event must be specified") + return pkgerrors.Errorf("at least one event must be specified") } for _, eventSig := range filter.EventSigs { if eventSig == [common.HashLength]byte{} { - return errors.Errorf("empty event sig") + return pkgerrors.Errorf("empty event sig") } } for _, addr := range filter.Addresses { if addr == [common.AddressLength]byte{} { - return errors.Errorf("empty address") + return pkgerrors.Errorf("empty address") } } @@ -256,7 +256,7 @@ func (lp *logPoller) RegisterFilter(filter Filter, qopts ...pg.QOpt) error { } if err := lp.orm.InsertFilter(filter, qopts...); err != nil { - return errors.Wrap(err, "error inserting filter") + return pkgerrors.Wrap(err, "error inserting filter") } lp.filters[filter.Name] = filter lp.filterDirty = true @@ -277,7 +277,7 @@ func (lp *logPoller) UnregisterFilter(name string, qopts ...pg.QOpt) error { } if err := lp.orm.DeleteFilter(name, qopts...); err != nil { - return errors.Wrap(err, "error deleting filter") + return pkgerrors.Wrap(err, "error deleting filter") } delete(lp.filters, name) lp.filterDirty = true @@ -352,13 +352,13 @@ func (lp *logPoller) Replay(ctx context.Context, fromBlock int64) error { return err } if fromBlock < 1 || fromBlock > latest.Number { - return errors.Errorf("Invalid replay block number %v, acceptable range [1, %v]", fromBlock, latest.Number) + return pkgerrors.Errorf("Invalid replay block number %v, acceptable range [1, %v]", fromBlock, latest.Number) } // Block until replay notification accepted or cancelled. select { case lp.replayStart <- fromBlock: case <-ctx.Done(): - return errors.Wrap(ErrReplayRequestAborted, ctx.Err().Error()) + return pkgerrors.Wrap(ErrReplayRequestAborted, ctx.Err().Error()) } // Block until replay complete or cancelled. select { @@ -423,7 +423,7 @@ func (lp *logPoller) HealthReport() map[string]error { func (lp *logPoller) GetReplayFromBlock(ctx context.Context, requested int64) (int64, error) { lastProcessed, err := lp.orm.SelectLatestBlock(pg.WithParentCtx(ctx)) if err != nil { - if !errors.Is(err, sql.ErrNoRows) { + if !pkgerrors.Is(err, sql.ErrNoRows) { // Real DB error return 0, err } @@ -449,7 +449,7 @@ func (lp *logPoller) run() { filters, err := lp.orm.LoadFilters(pg.WithParentCtx(lp.ctx)) if err != nil { - return errors.Wrapf(err, "Failed to load initial filters from db, retrying") + return pkgerrors.Wrapf(err, "Failed to load initial filters from db, retrying") } lp.filters = filters @@ -503,7 +503,7 @@ func (lp *logPoller) run() { var start int64 lastProcessed, err := lp.orm.SelectLatestBlock(pg.WithParentCtx(lp.ctx)) if err != nil { - if !errors.Is(err, sql.ErrNoRows) { + if !pkgerrors.Is(err, sql.ErrNoRows) { // Assume transient db reading issue, retry forever. lp.lggr.Errorw("unable to get starting block", "err", err) continue @@ -586,7 +586,7 @@ func (lp *logPoller) BackupPollAndSaveLogs(ctx context.Context, backupPollerBloc if lp.backupPollerNextBlock == 0 { lastProcessed, err := lp.orm.SelectLatestBlock(pg.WithParentCtx(ctx)) if err != nil { - if errors.Is(err, sql.ErrNoRows) { + if pkgerrors.Is(err, sql.ErrNoRows) { lp.lggr.Warnw("Backup log poller ran before first successful log poller run, skipping") } else { lp.lggr.Errorw("Backup log poller unable to get starting block", "err", err) @@ -687,7 +687,7 @@ func (lp *logPoller) backfill(ctx context.Context, start, end int64) error { gethLogs, err := lp.ec.FilterLogs(ctx, lp.Filter(big.NewInt(from), big.NewInt(to), nil)) if err != nil { var rpcErr client.JsonError - if errors.As(err, &rpcErr) { + if pkgerrors.As(err, &rpcErr) { if rpcErr.Code != jsonRpcLimitExceeded { lp.lggr.Errorw("Unable to query for logs", "err", err, "from", from, "to", to) return err @@ -740,20 +740,20 @@ func (lp *logPoller) getCurrentBlockMaybeHandleReorg(ctx context.Context, curren // Additional sanity checks, don't necessarily trust the RPC. if currentBlock == nil { lp.lggr.Errorf("Unexpected nil block from RPC", "currentBlockNumber", currentBlockNumber) - return nil, errors.Errorf("Got nil block for %d", currentBlockNumber) + return nil, pkgerrors.Errorf("Got nil block for %d", currentBlockNumber) } if currentBlock.Number != currentBlockNumber { lp.lggr.Warnw("Unable to get currentBlock, rpc returned incorrect block", "currentBlockNumber", currentBlockNumber, "got", currentBlock.Number) - return nil, errors.Errorf("Block mismatch have %d want %d", currentBlock.Number, currentBlockNumber) + return nil, pkgerrors.Errorf("Block mismatch have %d want %d", currentBlock.Number, currentBlockNumber) } } // Does this currentBlock point to the same parent that we have saved? // If not, there was a reorg, so we need to rewind. expectedParent, err1 := lp.orm.SelectBlockByNumber(currentBlockNumber-1, pg.WithParentCtx(ctx)) - if err1 != nil && !errors.Is(err1, sql.ErrNoRows) { + if err1 != nil && !pkgerrors.Is(err1, sql.ErrNoRows) { // If err is not a 'no rows' error, assume transient db issue and retry lp.lggr.Warnw("Unable to read latestBlockNumber currentBlock saved", "err", err1, "currentBlockNumber", currentBlockNumber) - return nil, errors.New("Unable to read latestBlockNumber currentBlock saved") + return nil, pkgerrors.New("Unable to read latestBlockNumber currentBlock saved") } // We will not have the previous currentBlock on initial poll. havePreviousBlock := err1 == nil @@ -769,7 +769,7 @@ func (lp *logPoller) getCurrentBlockMaybeHandleReorg(ctx context.Context, curren blockAfterLCA, err2 := lp.findBlockAfterLCA(ctx, currentBlock, expectedParent.FinalizedBlockNumber) if err2 != nil { lp.lggr.Warnw("Unable to find LCA after reorg, retrying", "err", err2) - return nil, errors.New("Unable to find LCA after reorg, retrying") + return nil, pkgerrors.New("Unable to find LCA after reorg, retrying") } lp.lggr.Infow("Reorg detected", "blockAfterLCA", blockAfterLCA.Number, "currentBlockNumber", currentBlockNumber) @@ -950,7 +950,7 @@ func (lp *logPoller) findBlockAfterLCA(ctx context.Context, current *evmtypes.He } } lp.lggr.Criticalw("Reorg greater than finality depth detected", "finalityTag", lp.useFinalityTag, "current", current.Number, "latestFinalized", latestFinalizedBlockNumber) - rerr := errors.New("Reorg greater than finality depth") + rerr := pkgerrors.New("Reorg greater than finality depth") lp.SvcErrBuffer.Append(rerr) return nil, rerr } @@ -1133,7 +1133,7 @@ func (lp *logPoller) GetBlocksRange(ctx context.Context, numbers []uint64, qopts } if len(blocksNotFound) > 0 { - return nil, errors.Errorf("blocks were not found in db or RPC call: %v", blocksNotFound) + return nil, pkgerrors.Errorf("blocks were not found in db or RPC call: %v", blocksNotFound) } return blocks, nil @@ -1205,16 +1205,16 @@ func (lp *logPoller) batchFetchBlocks(ctx context.Context, blocksRequested []str block, is := r.Result.(*evmtypes.Head) if !is { - return nil, errors.Errorf("expected result to be a %T, got %T", &evmtypes.Head{}, r.Result) + return nil, pkgerrors.Errorf("expected result to be a %T, got %T", &evmtypes.Head{}, r.Result) } if block == nil { - return nil, errors.New("invariant violation: got nil block") + return nil, pkgerrors.New("invariant violation: got nil block") } if block.Hash == (common.Hash{}) { - return nil, errors.Errorf("missing block hash for block number: %d", block.Number) + return nil, pkgerrors.Errorf("missing block hash for block number: %d", block.Number) } if block.Number < 0 { - return nil, errors.Errorf("expected block number to be >= to 0, got %d", block.Number) + return nil, pkgerrors.Errorf("expected block number to be >= to 0, got %d", block.Number) } blocks = append(blocks, block) } diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index 124c16d26f3..0bde65e5556 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -14,7 +14,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -286,7 +286,7 @@ func TestLogPoller_Replay(t *testing.T) { // Replay() should return error code received from replayComplete t.Run("returns error code on replay complete", func(t *testing.T) { ctx := testutils.Context(t) - anyErr := errors.New("any error") + anyErr := pkgerrors.New("any error") done := make(chan struct{}) go func() { defer close(done) @@ -412,7 +412,7 @@ func TestLogPoller_Replay(t *testing.T) { t.Cleanup(lp.reset) servicetest.Run(t, lp) - anyErr := errors.New("async error") + anyErr := pkgerrors.New("async error") observedLogs.TakeAll() lp.ReplayAsync(4) diff --git a/core/chains/evm/logpoller/orm.go b/core/chains/evm/logpoller/orm.go index c0e870870e6..f61845b870f 100644 --- a/core/chains/evm/logpoller/orm.go +++ b/core/chains/evm/logpoller/orm.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/jmoiron/sqlx" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -361,7 +361,7 @@ func (o *DbORM) insertLogsWithinTx(logs []Log, tx pg.Queryer) error { ) if err != nil { - if errors.Is(err, context.DeadlineExceeded) && batchInsertSize > 500 { + if pkgerrors.Is(err, context.DeadlineExceeded) && batchInsertSize > 500 { // In case of DB timeouts, try to insert again with a smaller batch upto a limit batchInsertSize /= 2 i -= batchInsertSize // counteract +=batchInsertSize on next loop iteration @@ -376,7 +376,7 @@ func (o *DbORM) insertLogsWithinTx(logs []Log, tx pg.Queryer) error { func (o *DbORM) validateLogs(logs []Log) error { for _, log := range logs { if o.chainID.Cmp(log.EvmChainId.ToInt()) != 0 { - return errors.Errorf("invalid chainID in log got %v want %v", log.EvmChainId.ToInt(), o.chainID) + return pkgerrors.Errorf("invalid chainID in log got %v want %v", log.EvmChainId.ToInt(), o.chainID) } } return nil @@ -475,7 +475,7 @@ func (o *DbORM) SelectLogsWithSigs(start, end int64, address common.Address, eve AND event_sig = ANY(:event_sig_array) AND block_number BETWEEN :start_block AND :end_block ORDER BY (block_number, log_index)`, args) - if errors.Is(err, sql.ErrNoRows) { + if pkgerrors.Is(err, sql.ErrNoRows) { return nil, nil } return logs, err @@ -526,7 +526,7 @@ func (o *DbORM) SelectLatestLogEventSigsAddrsWithConfs(fromBlock int64, addresse ORDER BY block_number ASC`, nestedBlockNumberQuery(confs)) var logs []Log if err := o.q.WithOpts(qopts...).SelectNamed(&logs, query, args); err != nil { - return nil, errors.Wrap(err, "failed to execute query") + return nil, pkgerrors.Wrap(err, "failed to execute query") } return logs, nil } diff --git a/core/chains/evm/logpoller/orm_test.go b/core/chains/evm/logpoller/orm_test.go index 8f89a237fd4..655a7295dab 100644 --- a/core/chains/evm/logpoller/orm_test.go +++ b/core/chains/evm/logpoller/orm_test.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/jackc/pgx/v4" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -205,17 +205,17 @@ func TestORM(t *testing.T) { require.NoError(t, o1.DeleteLogsAndBlocksAfter(10)) _, err = o1.SelectBlockByHash(common.HexToHash("0x1234")) require.Error(t, err) - assert.True(t, errors.Is(err, sql.ErrNoRows)) + assert.True(t, pkgerrors.Is(err, sql.ErrNoRows)) // Delete blocks from another chain. require.NoError(t, o2.DeleteLogsAndBlocksAfter(11)) _, err = o2.SelectBlockByHash(common.HexToHash("0x1234")) require.Error(t, err) - assert.True(t, errors.Is(err, sql.ErrNoRows)) + assert.True(t, pkgerrors.Is(err, sql.ErrNoRows)) // Delete blocks after should also delete block 12. _, err = o2.SelectBlockByHash(common.HexToHash("0x1235")) require.Error(t, err) - assert.True(t, errors.Is(err, sql.ErrNoRows)) + assert.True(t, pkgerrors.Is(err, sql.ErrNoRows)) // Should be able to insert and read back a log. topic := common.HexToHash("0x1599") @@ -331,7 +331,7 @@ func TestORM(t *testing.T) { // With no blocks, should be an error _, err = o1.SelectLatestLogByEventSigWithConfs(topic, common.HexToAddress("0x1234"), 0) require.Error(t, err) - assert.True(t, errors.Is(err, sql.ErrNoRows)) + assert.True(t, pkgerrors.Is(err, sql.ErrNoRows)) // With block 10, only 0 confs should work require.NoError(t, o1.InsertBlock(common.HexToHash("0x1234"), 10, time.Now(), 0)) log, err := o1.SelectLatestLogByEventSigWithConfs(topic, common.HexToAddress("0x1234"), 0) @@ -339,7 +339,7 @@ func TestORM(t *testing.T) { assert.Equal(t, int64(10), log.BlockNumber) _, err = o1.SelectLatestLogByEventSigWithConfs(topic, common.HexToAddress("0x1234"), 1) require.Error(t, err) - assert.True(t, errors.Is(err, sql.ErrNoRows)) + assert.True(t, pkgerrors.Is(err, sql.ErrNoRows)) // With block 12, anything <=2 should work require.NoError(t, o1.InsertBlock(common.HexToHash("0x1234"), 11, time.Now(), 0)) require.NoError(t, o1.InsertBlock(common.HexToHash("0x1235"), 12, time.Now(), 0)) @@ -351,7 +351,7 @@ func TestORM(t *testing.T) { require.NoError(t, err) _, err = o1.SelectLatestLogByEventSigWithConfs(topic, common.HexToAddress("0x1234"), 3) require.Error(t, err) - assert.True(t, errors.Is(err, sql.ErrNoRows)) + assert.True(t, pkgerrors.Is(err, sql.ErrNoRows)) // Required for confirmations to work require.NoError(t, o1.InsertBlock(common.HexToHash("0x1234"), 13, time.Now(), 0)) diff --git a/core/chains/evm/monitor/balance.go b/core/chains/evm/monitor/balance.go index bb271ad1d46..16e2fd527bf 100644 --- a/core/chains/evm/monitor/balance.go +++ b/core/chains/evm/monitor/balance.go @@ -9,7 +9,7 @@ import ( "time" gethCommon "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -231,7 +231,7 @@ func ApproximateFloat64(e *assets.Eth) (float64, error) { bf := new(big.Float).Quo(ef, weif) f64, _ := bf.Float64() if f64 == math.Inf(1) || f64 == math.Inf(-1) { - return math.Inf(1), errors.New("assets.Eth.Float64: Could not approximate Eth value into float") + return math.Inf(1), pkgerrors.New("assets.Eth.Float64: Could not approximate Eth value into float") } return f64, nil } diff --git a/core/chains/evm/monitor/balance_test.go b/core/chains/evm/monitor/balance_test.go index 246d5d0759f..85e0ec669bf 100644 --- a/core/chains/evm/monitor/balance_test.go +++ b/core/chains/evm/monitor/balance_test.go @@ -8,7 +8,7 @@ import ( "time" "github.com/onsi/gomega" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -123,7 +123,7 @@ func TestBalanceMonitor_Start(t *testing.T) { ethClient.On("BalanceAt", mock.Anything, k0Addr, nilBigInt). Once(). - Return(nil, errors.New("a little easter egg for the 4chan link marines error")) + Return(nil, pkgerrors.New("a little easter egg for the 4chan link marines error")) servicetest.RunHealthy(t, bm) diff --git a/core/chains/evm/txmgr/attempts.go b/core/chains/evm/txmgr/attempts.go index e37f0e4d2d8..892920c0f67 100644 --- a/core/chains/evm/txmgr/attempts.go +++ b/core/chains/evm/txmgr/attempts.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" @@ -59,7 +59,7 @@ func (c *evmTxAttemptBuilder) NewTxAttemptWithType(ctx context.Context, etx Tx, keySpecificMaxGasPriceWei := c.feeConfig.PriceMaxKey(etx.FromAddress) fee, feeLimit, err = c.EvmFeeEstimator.GetFee(ctx, etx.EncodedPayload, etx.FeeLimit, keySpecificMaxGasPriceWei, opts...) if err != nil { - return attempt, fee, feeLimit, true, errors.Wrap(err, "failed to get fee") // estimator errors are retryable + return attempt, fee, feeLimit, true, pkgerrors.Wrap(err, "failed to get fee") // estimator errors are retryable } attempt, retryable, err = c.NewCustomTxAttempt(ctx, etx, fee, feeLimit, txType, lggr) @@ -73,7 +73,7 @@ func (c *evmTxAttemptBuilder) NewBumpTxAttempt(ctx context.Context, etx Tx, prev bumpedFee, bumpedFeeLimit, err = c.EvmFeeEstimator.BumpFee(ctx, previousAttempt.TxFee, etx.FeeLimit, keySpecificMaxGasPriceWei, newEvmPriorAttempts(priorAttempts)) if err != nil { - return attempt, bumpedFee, bumpedFeeLimit, true, errors.Wrap(err, "failed to bump fee") // estimator errors are retryable + return attempt, bumpedFee, bumpedFeeLimit, true, pkgerrors.Wrap(err, "failed to bump fee") // estimator errors are retryable } attempt, retryable, err = c.NewCustomTxAttempt(ctx, etx, bumpedFee, bumpedFeeLimit, previousAttempt.TxType, lggr) @@ -86,7 +86,7 @@ func (c *evmTxAttemptBuilder) NewCustomTxAttempt(ctx context.Context, etx Tx, fe switch txType { case 0x0: // legacy if fee.Legacy == nil { - err = errors.Errorf("Attempt %v is a type 0 transaction but estimator did not return legacy fee bump", attempt.ID) + err = pkgerrors.Errorf("Attempt %v is a type 0 transaction but estimator did not return legacy fee bump", attempt.ID) logger.Sugared(lggr).AssumptionViolation(err.Error()) return attempt, false, err // not retryable } @@ -94,7 +94,7 @@ func (c *evmTxAttemptBuilder) NewCustomTxAttempt(ctx context.Context, etx Tx, fe return attempt, true, err case 0x2: // dynamic, EIP1559 if !fee.ValidDynamic() { - err = errors.Errorf("Attempt %v is a type 2 transaction but estimator did not return dynamic fee bump", attempt.ID) + err = pkgerrors.Errorf("Attempt %v is a type 2 transaction but estimator did not return dynamic fee bump", attempt.ID) logger.Sugared(lggr).AssumptionViolation(err.Error()) return attempt, false, err // not retryable } @@ -104,7 +104,7 @@ func (c *evmTxAttemptBuilder) NewCustomTxAttempt(ctx context.Context, etx Tx, fe }, gasLimit) return attempt, true, err default: - err = errors.Errorf("invariant violation: Attempt %v had unrecognised transaction type %v"+ + err = pkgerrors.Errorf("invariant violation: Attempt %v had unrecognised transaction type %v"+ "This is a bug! Please report to https://github.com/smartcontractkit/chainlink/issues", attempt.ID, attempt.TxType) logger.Sugared(lggr).AssumptionViolation(err.Error()) return attempt, false, err // not retryable @@ -117,7 +117,7 @@ func (c *evmTxAttemptBuilder) NewEmptyTxAttempt(ctx context.Context, nonce evmty payload := []byte{} if fee.Legacy == nil { - return attempt, errors.New("NewEmptyTranscation: legacy fee cannot be nil") + return attempt, pkgerrors.New("NewEmptyTranscation: legacy fee cannot be nil") } tx := newLegacyTransaction( @@ -132,7 +132,7 @@ func (c *evmTxAttemptBuilder) NewEmptyTxAttempt(ctx context.Context, nonce evmty transaction := types.NewTx(&tx) hash, signedTxBytes, err := c.SignTx(ctx, fromAddress, transaction) if err != nil { - return attempt, errors.Wrapf(err, "error using account %s to sign empty transaction", fromAddress.String()) + return attempt, pkgerrors.Wrapf(err, "error using account %s to sign empty transaction", fromAddress.String()) } attempt.SignedRawTx = signedTxBytes @@ -143,7 +143,7 @@ func (c *evmTxAttemptBuilder) NewEmptyTxAttempt(ctx context.Context, nonce evmty func (c *evmTxAttemptBuilder) newDynamicFeeAttempt(ctx context.Context, etx Tx, fee gas.DynamicFee, gasLimit uint32) (attempt TxAttempt, err error) { if err = validateDynamicFeeGas(c.feeConfig, c.feeConfig.TipCapMin(), fee, gasLimit, etx); err != nil { - return attempt, errors.Wrap(err, "error validating gas") + return attempt, pkgerrors.Wrap(err, "error validating gas") } d := newDynamicFeeTransaction( @@ -190,25 +190,25 @@ func validateDynamicFeeGas(kse keySpecificEstimator, tipCapMinimum *assets.Wei, // Assertions from: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md // Prevent impossibly large numbers if gasFeeCap.ToInt().Cmp(Max256BitUInt) > 0 { - return errors.New("impossibly large fee cap") + return pkgerrors.New("impossibly large fee cap") } if gasTipCap.ToInt().Cmp(Max256BitUInt) > 0 { - return errors.New("impossibly large tip cap") + return pkgerrors.New("impossibly large tip cap") } // The total must be at least as large as the tip if gasFeeCap.Cmp(gasTipCap) < 0 { - return errors.Errorf("gas fee cap must be greater than or equal to gas tip cap (fee cap: %s, tip cap: %s)", gasFeeCap.String(), gasTipCap.String()) + return pkgerrors.Errorf("gas fee cap must be greater than or equal to gas tip cap (fee cap: %s, tip cap: %s)", gasFeeCap.String(), gasTipCap.String()) } // Configuration sanity-check max := kse.PriceMaxKey(etx.FromAddress) if gasFeeCap.Cmp(max) > 0 { - return errors.Errorf("cannot create tx attempt: specified gas fee cap of %s would exceed max configured gas price of %s for key %s", gasFeeCap.String(), max.String(), etx.FromAddress.String()) + return pkgerrors.Errorf("cannot create tx attempt: specified gas fee cap of %s would exceed max configured gas price of %s for key %s", gasFeeCap.String(), max.String(), etx.FromAddress.String()) } // Tip must be above minimum minTip := tipCapMinimum if gasTipCap.Cmp(minTip) < 0 { - return errors.Errorf("cannot create tx attempt: specified gas tip cap of %s is below min configured gas tip of %s for key %s", gasTipCap.String(), minTip.String(), etx.FromAddress.String()) + return pkgerrors.Errorf("cannot create tx attempt: specified gas tip cap of %s is below min configured gas tip of %s for key %s", gasTipCap.String(), minTip.String(), etx.FromAddress.String()) } return nil } @@ -228,7 +228,7 @@ func newDynamicFeeTransaction(nonce uint64, to common.Address, value *big.Int, g func (c *evmTxAttemptBuilder) newLegacyAttempt(ctx context.Context, etx Tx, gasPrice *assets.Wei, gasLimit uint32) (attempt TxAttempt, err error) { if err = validateLegacyGas(ctx, c.feeConfig, c.feeConfig.PriceMin(), gasPrice, gasLimit, etx); err != nil { - return attempt, errors.Wrap(err, "error validating gas") + return attempt, pkgerrors.Wrap(err, "error validating gas") } tx := newLegacyTransaction( @@ -243,7 +243,7 @@ func (c *evmTxAttemptBuilder) newLegacyAttempt(ctx context.Context, etx Tx, gasP transaction := types.NewTx(&tx) hash, signedTxBytes, err := c.SignTx(ctx, etx.FromAddress, transaction) if err != nil { - return attempt, errors.Wrapf(err, "error using account %s to sign transaction %v", etx.FromAddress, etx.ID) + return attempt, pkgerrors.Wrapf(err, "error using account %s to sign transaction %v", etx.FromAddress, etx.ID) } attempt.State = txmgrtypes.TxAttemptInProgress @@ -266,11 +266,11 @@ func validateLegacyGas(ctx context.Context, kse keySpecificEstimator, minGasPric } max := kse.PriceMaxKey(etx.FromAddress) if gasPrice.Cmp(max) > 0 { - return errors.Errorf("cannot create tx attempt: specified gas price of %s would exceed max configured gas price of %s for key %s", gasPrice.String(), max.String(), etx.FromAddress.String()) + return pkgerrors.Errorf("cannot create tx attempt: specified gas price of %s would exceed max configured gas price of %s for key %s", gasPrice.String(), max.String(), etx.FromAddress.String()) } min := minGasPriceWei if gasPrice.Cmp(min) < 0 { - return errors.Errorf("cannot create tx attempt: specified gas price of %s is below min configured gas price of %s for key %s", gasPrice.String(), min.String(), etx.FromAddress.String()) + return pkgerrors.Errorf("cannot create tx attempt: specified gas price of %s is below min configured gas price of %s for key %s", gasPrice.String(), min.String(), etx.FromAddress.String()) } return nil } @@ -278,7 +278,7 @@ func validateLegacyGas(ctx context.Context, kse keySpecificEstimator, minGasPric func (c *evmTxAttemptBuilder) newSignedAttempt(ctx context.Context, etx Tx, tx *types.Transaction) (attempt TxAttempt, err error) { hash, signedTxBytes, err := c.SignTx(ctx, etx.FromAddress, tx) if err != nil { - return attempt, errors.Wrapf(err, "error using account %s to sign transaction %v", etx.FromAddress.String(), etx.ID) + return attempt, pkgerrors.Wrapf(err, "error using account %s to sign transaction %v", etx.FromAddress.String(), etx.ID) } attempt.State = txmgrtypes.TxAttemptInProgress @@ -308,7 +308,7 @@ func (c *evmTxAttemptBuilder) SignTx(ctx context.Context, address common.Address } rlp := new(bytes.Buffer) if err := signedTx.EncodeRLP(rlp); err != nil { - return common.Hash{}, nil, errors.Wrap(err, "SignTx failed") + return common.Hash{}, nil, pkgerrors.Wrap(err, "SignTx failed") } txHash := signedTx.Hash() return txHash, rlp.Bytes(), nil diff --git a/core/chains/evm/txmgr/attempts_test.go b/core/chains/evm/txmgr/attempts_test.go index e0b3cd59ce7..b1e24984c37 100644 --- a/core/chains/evm/txmgr/attempts_test.go +++ b/core/chains/evm/txmgr/attempts_test.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" gethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -257,8 +257,8 @@ func TestTxm_NewCustomTxAttempt_NonRetryableErrors(t *testing.T) { func TestTxm_EvmTxAttemptBuilder_RetryableEstimatorError(t *testing.T) { est := gasmocks.NewEvmFeeEstimator(t) - est.On("GetFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(gas.EvmFee{}, uint32(0), errors.New("fail")) - est.On("BumpFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(gas.EvmFee{}, uint32(0), errors.New("fail")) + est.On("GetFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(gas.EvmFee{}, uint32(0), pkgerrors.New("fail")) + est.On("BumpFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(gas.EvmFee{}, uint32(0), pkgerrors.New("fail")) kst := ksmocks.NewEth(t) lggr := logger.Test(t) diff --git a/core/chains/evm/txmgr/common.go b/core/chains/evm/txmgr/common.go index d1e851f0c21..75fd3628ed5 100644 --- a/core/chains/evm/txmgr/common.go +++ b/core/chains/evm/txmgr/common.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -74,7 +74,7 @@ func batchSendTransactions( logger.Debugw(fmt.Sprintf("Batch sending transactions %v thru %v", i, j)) if err := ethClient.BatchCallContextAll(ctx, reqs[i:j]); err != nil { - return reqs, now, successfulBroadcast, errors.Wrap(err, "failed to batch send transactions") + return reqs, now, successfulBroadcast, pkgerrors.Wrap(err, "failed to batch send transactions") } successfulBroadcast = append(successfulBroadcast, ethTxIDs[i:j]...) } diff --git a/core/chains/evm/txmgr/nonce_syncer.go b/core/chains/evm/txmgr/nonce_syncer.go index 2936736b3b3..0cb52a1321e 100644 --- a/core/chains/evm/txmgr/nonce_syncer.go +++ b/core/chains/evm/txmgr/nonce_syncer.go @@ -6,7 +6,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/txmgr" @@ -76,13 +76,13 @@ func NewNonceSyncer( // Calling it later is not safe and could lead to races. func (s nonceSyncerImpl) Sync(ctx context.Context, addr common.Address, localNonce types.Nonce) (nonce types.Nonce, err error) { nonce, err = s.fastForwardNonceIfNecessary(ctx, addr, localNonce) - return nonce, errors.Wrap(err, "NonceSyncer#fastForwardNoncesIfNecessary failed") + return nonce, pkgerrors.Wrap(err, "NonceSyncer#fastForwardNoncesIfNecessary failed") } func (s nonceSyncerImpl) fastForwardNonceIfNecessary(ctx context.Context, address common.Address, localNonce types.Nonce) (types.Nonce, error) { chainNonce, err := s.pendingNonceFromEthClient(ctx, address) if err != nil { - return localNonce, errors.Wrap(err, "GetNextNonce failed to loadInitialNonceFromEthClient") + return localNonce, pkgerrors.Wrap(err, "GetNextNonce failed to loadInitialNonceFromEthClient") } if chainNonce == 0 { return localNonce, nil @@ -102,5 +102,5 @@ func (s nonceSyncerImpl) fastForwardNonceIfNecessary(ctx context.Context, addres func (s nonceSyncerImpl) pendingNonceFromEthClient(ctx context.Context, account common.Address) (types.Nonce, error) { nextNonce, err := s.client.PendingSequenceAt(ctx, account) - return nextNonce, errors.WithStack(err) + return nextNonce, pkgerrors.WithStack(err) } diff --git a/core/chains/evm/txmgr/nonce_syncer_test.go b/core/chains/evm/txmgr/nonce_syncer_test.go index f757b8863d1..d9a741fb3c5 100644 --- a/core/chains/evm/txmgr/nonce_syncer_test.go +++ b/core/chains/evm/txmgr/nonce_syncer_test.go @@ -12,7 +12,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -32,7 +32,7 @@ func Test_NonceSyncer_Sync(t *testing.T) { ns := txmgr.NewNonceSyncer(txStore, logger.Test(t), ethClient) - ethClient.On("PendingNonceAt", mock.Anything, from).Return(uint64(0), errors.New("something exploded")) + ethClient.On("PendingNonceAt", mock.Anything, from).Return(uint64(0), pkgerrors.New("something exploded")) _, err := ns.Sync(testutils.Context(t), from, types.Nonce(0)) require.Error(t, err) assert.Contains(t, err.Error(), "something exploded") diff --git a/core/chains/evm/txmgr/resender_test.go b/core/chains/evm/txmgr/resender_test.go index e8c1c9e079f..7cf575e8c8d 100644 --- a/core/chains/evm/txmgr/resender_test.go +++ b/core/chains/evm/txmgr/resender_test.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -177,7 +177,7 @@ func Test_EthResender_Start(t *testing.T) { })).Return(nil).Run(func(args mock.Arguments) { elems := args.Get(1).([]rpc.BatchElem) // It should update BroadcastAt even if there is an error here - elems[0].Error = errors.New("kaboom") + elems[0].Error = pkgerrors.New("kaboom") }) func() { diff --git a/core/chains/evm/txmgr/transmitchecker.go b/core/chains/evm/txmgr/transmitchecker.go index 5a5cc3dbcd4..8956f2ae626 100644 --- a/core/chains/evm/txmgr/transmitchecker.go +++ b/core/chains/evm/txmgr/transmitchecker.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" gethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" @@ -53,11 +53,11 @@ func (c *CheckerFactory) BuildChecker(spec TransmitCheckerSpec) (TransmitChecker return &SimulateChecker{c.Client}, nil case TransmitCheckerTypeVRFV1: if spec.VRFCoordinatorAddress == nil { - return nil, errors.Errorf("malformed checker, expected non-nil VRFCoordinatorAddress, got: %v", spec) + return nil, pkgerrors.Errorf("malformed checker, expected non-nil VRFCoordinatorAddress, got: %v", spec) } coord, err := v1.NewVRFCoordinator(*spec.VRFCoordinatorAddress, c.Client) if err != nil { - return nil, errors.Wrapf(err, + return nil, pkgerrors.Wrapf(err, "failed to create VRF V1 coordinator at address %v", spec.VRFCoordinatorAddress) } return &VRFV1Checker{ @@ -66,15 +66,15 @@ func (c *CheckerFactory) BuildChecker(spec TransmitCheckerSpec) (TransmitChecker }, nil case TransmitCheckerTypeVRFV2: if spec.VRFCoordinatorAddress == nil { - return nil, errors.Errorf("malformed checker, expected non-nil VRFCoordinatorAddress, got: %v", spec) + return nil, pkgerrors.Errorf("malformed checker, expected non-nil VRFCoordinatorAddress, got: %v", spec) } coord, err := v2.NewVRFCoordinatorV2(*spec.VRFCoordinatorAddress, c.Client) if err != nil { - return nil, errors.Wrapf(err, + return nil, pkgerrors.Wrapf(err, "failed to create VRF V2 coordinator at address %v", spec.VRFCoordinatorAddress) } if spec.VRFRequestBlockNumber == nil { - return nil, errors.New("VRFRequestBlockNumber parameter must be non-nil") + return nil, pkgerrors.New("VRFRequestBlockNumber parameter must be non-nil") } return &VRFV2Checker{ GetCommitment: coord.GetCommitment, @@ -83,15 +83,15 @@ func (c *CheckerFactory) BuildChecker(spec TransmitCheckerSpec) (TransmitChecker }, nil case TransmitCheckerTypeVRFV2Plus: if spec.VRFCoordinatorAddress == nil { - return nil, errors.Errorf("malformed checker, expected non-nil VRFCoordinatorAddress, got: %v", spec) + return nil, pkgerrors.Errorf("malformed checker, expected non-nil VRFCoordinatorAddress, got: %v", spec) } coord, err := vrf_coordinator_v2plus_interface.NewIVRFCoordinatorV2PlusInternal(*spec.VRFCoordinatorAddress, c.Client) if err != nil { - return nil, errors.Wrapf(err, + return nil, pkgerrors.Wrapf(err, "failed to create VRF V2 coordinator plus at address %v", spec.VRFCoordinatorAddress) } if spec.VRFRequestBlockNumber == nil { - return nil, errors.New("VRFRequestBlockNumber parameter must be non-nil") + return nil, pkgerrors.New("VRFRequestBlockNumber parameter must be non-nil") } return &VRFV2Checker{ GetCommitment: coord.SRequestCommitments, @@ -101,7 +101,7 @@ func (c *CheckerFactory) BuildChecker(spec TransmitCheckerSpec) (TransmitChecker case "": return NoChecker, nil default: - return nil, errors.Errorf("unrecognized checker type: %s", spec.CheckerType) + return nil, pkgerrors.Errorf("unrecognized checker type: %s", spec.CheckerType) } } @@ -150,7 +150,7 @@ func (s *SimulateChecker) Check( if jErr := evmclient.ExtractRPCErrorOrNil(err); jErr != nil { l.Criticalw("Transaction reverted during simulation", "ethTxAttemptID", a.ID, "txHash", a.Hash, "err", err, "rpcErr", jErr.String(), "returnValue", b.String()) - return errors.Errorf("transaction reverted during simulation: %s", jErr.String()) + return pkgerrors.Errorf("transaction reverted during simulation: %s", jErr.String()) } l.Warnw("Transaction simulation failed, will attempt to send anyway", "ethTxAttemptID", a.ID, "txHash", a.Hash, "err", err, "returnValue", b.String()) @@ -259,7 +259,7 @@ func (v *VRFV1Checker) Check( "ethTxID", tx.ID, "meta", tx.Meta, "reqID", reqID) - return errors.New("request already fulfilled") + return pkgerrors.New("request already fulfilled") } // Request not fulfilled return nil @@ -350,7 +350,7 @@ func (v *VRFV2Checker) Check( "ethTxID", tx.ID, "meta", tx.Meta, "vrfRequestId", vrfRequestID) - return errors.New("request already fulfilled") + return pkgerrors.New("request already fulfilled") } l.Debugw("Request not yet fulfilled", "ethTxID", tx.ID, diff --git a/core/chains/evm/txmgr/transmitchecker_test.go b/core/chains/evm/txmgr/transmitchecker_test.go index d2f668da11b..2fce9cf7aac 100644 --- a/core/chains/evm/txmgr/transmitchecker_test.go +++ b/core/chains/evm/txmgr/transmitchecker_test.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/mock" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -165,7 +165,7 @@ func TestTransmitCheckers(t *testing.T) { mock.AnythingOfType("*hexutil.Bytes"), "eth_call", mock.MatchedBy(func(callarg map[string]interface{}) bool { return fmt.Sprintf("%s", callarg["value"]) == "0x282" // 642 - }), "latest").Return(errors.New("error")).Once() + }), "latest").Return(pkgerrors.New("error")).Once() // Non-revert errors are logged but should not prevent transmission, and do not need // to be passed to the caller @@ -221,7 +221,7 @@ func TestTransmitCheckers(t *testing.T) { Callbacks: func(opts *bind.CallOpts, reqID [32]byte) (v1.Callbacks, error) { if opts.BlockNumber.Cmp(big.NewInt(6)) != 0 { // Ensure correct logic is applied to get callbacks. - return v1.Callbacks{}, errors.New("error getting callback") + return v1.Callbacks{}, pkgerrors.New("error getting callback") } if reqID == r1 { // Request 1 is already fulfilled @@ -230,7 +230,7 @@ func TestTransmitCheckers(t *testing.T) { }, nil } else if reqID == r2 { // Request 2 errors - return v1.Callbacks{}, errors.New("error getting commitment") + return v1.Callbacks{}, pkgerrors.New("error getting commitment") } return v1.Callbacks{ SeedAndBlockNum: [32]byte{1}, @@ -277,7 +277,7 @@ func TestTransmitCheckers(t *testing.T) { t.Run("failure fetching tx receipt and block head", func(t *testing.T) { tx, attempt := txRequest(t, r1, false) - mockBatch.Return(errors.New("could not fetch")) + mockBatch.Return(pkgerrors.New("could not fetch")) err := checker.Check(ctx, log, tx, attempt) require.NoError(t, err) }) @@ -324,7 +324,7 @@ func TestTransmitCheckers(t *testing.T) { return [32]byte{}, nil } else if requestID.String() == "2" { // Request 2 errors - return [32]byte{}, errors.New("error getting commitment") + return [32]byte{}, pkgerrors.New("error getting commitment") } // All other requests are unfulfilled return [32]byte{1}, nil @@ -355,7 +355,7 @@ func TestTransmitCheckers(t *testing.T) { t.Run("can't get header", func(t *testing.T) { checker.HeadByNumber = func(ctx context.Context, n *big.Int) (*evmtypes.Head, error) { - return nil, errors.New("can't get head") + return nil, pkgerrors.New("can't get head") } tx, attempt := txRequest(t, big.NewInt(3)) require.NoError(t, checker.Check(ctx, log, tx, attempt)) diff --git a/core/chains/evm/types/internal/blocks/transactions.go b/core/chains/evm/types/internal/blocks/transactions.go index b607e2dcfd1..887a16f99d1 100644 --- a/core/chains/evm/types/internal/blocks/transactions.go +++ b/core/chains/evm/types/internal/blocks/transactions.go @@ -6,7 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" ) type TxType uint8 @@ -22,7 +22,7 @@ func (txt *TxType) UnmarshalJSON(data []byte) error { return err } if hx > math.MaxUint8 { - return errors.Errorf("expected 'type' to fit into a single byte, got: '%s'", data) + return pkgerrors.Errorf("expected 'type' to fit into a single byte, got: '%s'", data) } *txt = TxType(hx) return nil diff --git a/core/chains/evm/types/models.go b/core/chains/evm/types/models.go index 7db38fc6821..464eb901005 100644 --- a/core/chains/evm/types/models.go +++ b/core/chains/evm/types/models.go @@ -13,7 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/ugorji/go/codec" "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" @@ -355,7 +355,7 @@ func (b Block) MarshalJSON() ([]byte, error) { return buf.Bytes(), nil } -var ErrMissingBlock = errors.New("missing block") +var ErrMissingBlock = pkgerrors.New("missing block") // UnmarshalJSON unmarshals to a Block func (b *Block) UnmarshalJSON(data []byte) error { @@ -370,12 +370,12 @@ func (b *Block) UnmarshalJSON(data []byte) error { return err } if bi.Empty() { - return errors.WithStack(ErrMissingBlock) + return pkgerrors.WithStack(ErrMissingBlock) } n, err := hexutil.DecodeBig(bi.Number) if err != nil { - return errors.Wrapf(err, "failed to decode block number while unmarshalling block, got: '%s' in '%s'", bi.Number, data) + return pkgerrors.Wrapf(err, "failed to decode block number while unmarshalling block, got: '%s' in '%s'", bi.Number, data) } *b = Block{ Number: n.Int64(), @@ -421,7 +421,7 @@ func (t *Transaction) UnmarshalJSON(data []byte) error { } if ti.Gas == nil { - return errors.Errorf("expected 'gas' to not be null, got: '%s'", data) + return pkgerrors.Errorf("expected 'gas' to not be null, got: '%s'", data) } if ti.Type == nil { tpe := LegacyTxType @@ -502,7 +502,7 @@ func unmarshalFromString(s string, f *FunctionSelector) error { } bytes := common.FromHex(s) if len(bytes) != FunctionSelectorLength { - return errors.New("function ID must be 4 bytes in length") + return pkgerrors.New("function ID must be 4 bytes in length") } f.SetBytes(bytes) } else { @@ -559,7 +559,7 @@ type UntrustedBytes []byte func (ary UntrustedBytes) SafeByteSlice(start int, end int) ([]byte, error) { if end > len(ary) || start > end || start < 0 || end < 0 { var empty []byte - return empty, errors.New("out of bounds slice access") + return empty, pkgerrors.New("out of bounds slice access") } return ary[start:end], nil } diff --git a/core/chains/evm/types/models_test.go b/core/chains/evm/types/models_test.go index f2f58c3a983..4fc986ae9d3 100644 --- a/core/chains/evm/types/models_test.go +++ b/core/chains/evm/types/models_test.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -935,8 +935,8 @@ func TestBlock_UnmarshalJSON(t *testing.T) { b := new(evmtypes.Block) err := b.UnmarshalJSON([]byte("null")) assert.Error(t, err) - assert.Equal(t, errors.Cause(err), evmtypes.ErrMissingBlock) - assert.True(t, errors.Is(err, evmtypes.ErrMissingBlock)) + assert.Equal(t, pkgerrors.Cause(err), evmtypes.ErrMissingBlock) + assert.True(t, pkgerrors.Is(err, evmtypes.ErrMissingBlock)) }) t.Run("unmarshals EIP-4844 block", func(t *testing.T) { b := new(evmtypes.Block) diff --git a/core/chains/evm/types/types.go b/core/chains/evm/types/types.go index c3ad584ebbd..89739e61e1b 100644 --- a/core/chains/evm/types/types.go +++ b/core/chains/evm/types/types.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/jackc/pgtype" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -143,7 +143,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { } var dec Receipt if err := json.Unmarshal(input, &dec); err != nil { - return errors.Wrap(err, "could not unmarshal receipt") + return pkgerrors.Wrap(err, "could not unmarshal receipt") } if dec.PostState != nil { r.PostState = *dec.PostState @@ -182,7 +182,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { func (r *Receipt) Scan(value interface{}) error { b, ok := value.([]byte) if !ok { - return errors.New("type assertion to []byte failed") + return pkgerrors.New("type assertion to []byte failed") } return json.Unmarshal(b, r) @@ -294,7 +294,7 @@ func (l *Log) UnmarshalJSON(input []byte) error { } var dec Log if err := json.Unmarshal(input, &dec); err != nil { - return errors.Wrap(err, "could not unmarshal log") + return pkgerrors.Wrap(err, "could not unmarshal log") } if dec.Address != nil { l.Address = *dec.Address @@ -330,20 +330,20 @@ func (a *AddressArray) Scan(src interface{}) error { baArray := pgtype.ByteaArray{} err := baArray.Scan(src) if err != nil { - return errors.Wrap(err, "Expected BYTEA[] column for AddressArray") + return pkgerrors.Wrap(err, "Expected BYTEA[] column for AddressArray") } if baArray.Status != pgtype.Present { *a = nil return nil } if len(baArray.Dimensions) > 1 { - return errors.Errorf("Expected AddressArray to be 1-dimensional. Dimensions = %v", baArray.Dimensions) + return pkgerrors.Errorf("Expected AddressArray to be 1-dimensional. Dimensions = %v", baArray.Dimensions) } for i, ba := range baArray.Elements { addr := common.Address{} if ba.Status != pgtype.Present { - return errors.Errorf("Expected all addresses in AddressArray to be non-NULL. Got AddressArray[%d] = NULL", i) + return pkgerrors.Errorf("Expected all addresses in AddressArray to be non-NULL. Got AddressArray[%d] = NULL", i) } err = addr.Scan(ba.Bytes) if err != nil { @@ -361,20 +361,20 @@ func (h *HashArray) Scan(src interface{}) error { baArray := pgtype.ByteaArray{} err := baArray.Scan(src) if err != nil { - return errors.Wrap(err, "Expected BYTEA[] column for HashArray") + return pkgerrors.Wrap(err, "Expected BYTEA[] column for HashArray") } if baArray.Status != pgtype.Present { *h = nil return nil } if len(baArray.Dimensions) > 1 { - return errors.Errorf("Expected HashArray to be 1-dimensional. Dimensions = %v", baArray.Dimensions) + return pkgerrors.Errorf("Expected HashArray to be 1-dimensional. Dimensions = %v", baArray.Dimensions) } for i, ba := range baArray.Elements { hash := common.Hash{} if ba.Status != pgtype.Present { - return errors.Errorf("Expected all hashes in HashArray to be non-NULL. Got HashArray[%d] = NULL", i) + return pkgerrors.Errorf("Expected all hashes in HashArray to be non-NULL. Got HashArray[%d] = NULL", i) } err = hash.Scan(ba.Bytes) if err != nil { diff --git a/core/chains/evm/utils/ethabi.go b/core/chains/evm/utils/ethabi.go index 61d365933d2..08dfd54f430 100644 --- a/core/chains/evm/utils/ethabi.go +++ b/core/chains/evm/utils/ethabi.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/shopspring/decimal" "github.com/tidwall/gjson" @@ -222,7 +222,7 @@ func EVMWordSignedBigInt(val *big.Int) ([]byte, error) { // a signed representation. Returns error on overflow. func EVMWordBigInt(val *big.Int) ([]byte, error) { if val.Sign() == -1 { - return nil, errors.New("Uint256 cannot be negative") + return nil, pkgerrors.New("Uint256 cannot be negative") } bytes := val.Bytes() if len(bytes) > EVMWordByteLen { diff --git a/core/chains/evm/utils/ethabi_test.go b/core/chains/evm/utils/ethabi_test.go index b99d906eae7..f28a083ff01 100644 --- a/core/chains/evm/utils/ethabi_test.go +++ b/core/chains/evm/utils/ethabi_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" @@ -636,7 +636,7 @@ func EVMTranscodeBytes(value gjson.Result) ([]byte, error) { vInt, _ := v.Int(nil) word, err := EVMWordSignedBigInt(vInt) if err != nil { - return nil, errors.Wrap(err, "while converting float to int256") + return nil, pkgerrors.Wrap(err, "while converting float to int256") } return EVMEncodeBytes(word), nil default: diff --git a/core/config/app_config.go b/core/config/app_config.go index 648939b871b..290e14dcc45 100644 --- a/core/config/app_config.go +++ b/core/config/app_config.go @@ -4,13 +4,13 @@ import ( "time" "github.com/google/uuid" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "go.uber.org/zap/zapcore" ) // nolint var ( - ErrEnvUnset = errors.New("env var unset") + ErrEnvUnset = pkgerrors.New("env var unset") ) type LogfFn func(string, ...any) diff --git a/core/config/docs/docs_test.go b/core/config/docs/docs_test.go index 8b4e38b980d..919113e1d93 100644 --- a/core/config/docs/docs_test.go +++ b/core/config/docs/docs_test.go @@ -6,7 +6,7 @@ import ( "github.com/kylelemons/godebug/diff" gotoml "github.com/pelletier/go-toml/v2" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -31,7 +31,7 @@ func TestDoc(t *testing.T) { var strict *gotoml.StrictMissingError if err != nil && strings.Contains(err.Error(), "undecoded keys: ") { t.Errorf("Docs contain extra fields: %v", err) - } else if errors.As(err, &strict) { + } else if pkgerrors.As(err, &strict) { t.Fatal("StrictMissingError:", strict.String()) } else { require.NoError(t, err) diff --git a/core/config/parse/parsers.go b/core/config/parse/parsers.go index 6243b74dd52..371354ba36e 100644 --- a/core/config/parse/parsers.go +++ b/core/config/parse/parsers.go @@ -10,7 +10,7 @@ import ( "time" "github.com/mitchellh/go-homedir" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "go.uber.org/zap/zapcore" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" @@ -113,7 +113,7 @@ func HomeDir(str string) (string, error) { func DatabaseURL(s string) (url.URL, error) { uri, err := url.Parse(s) if err != nil { - return url.URL{}, errors.Wrapf(err, "invalid database url %s", s) + return url.URL{}, pkgerrors.Wrapf(err, "invalid database url %s", s) } if uri.String() == "" { return *uri, nil diff --git a/core/gethwrappers/versions.go b/core/gethwrappers/versions.go index acdefd06a59..43a59ddbb75 100644 --- a/core/gethwrappers/versions.go +++ b/core/gethwrappers/versions.go @@ -9,7 +9,7 @@ import ( "sort" "strings" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "go.uber.org/multierr" ) @@ -44,11 +44,11 @@ func dbPath() (path string, err error) { func versionsDBLineReader() (*bufio.Scanner, error) { versionsDBPath, err := dbPath() if err != nil { - return nil, errors.Wrapf(err, "could not construct versions DB path") + return nil, pkgerrors.Wrapf(err, "could not construct versions DB path") } versionsDBFile, err := os.Open(versionsDBPath) if err != nil { - return nil, errors.Wrapf(err, "could not open versions database") + return nil, pkgerrors.Wrapf(err, "could not open versions database") } return bufio.NewScanner(versionsDBFile), nil @@ -66,28 +66,28 @@ func ReadVersionsDB() (*IntegratedVersion, error) { for db.Scan() { line := strings.Fields(db.Text()) if !strings.HasSuffix(line[0], ":") { - return nil, errors.Errorf( + return nil, pkgerrors.Errorf( `each line in versions.txt should start with "$TOPIC:"`) } topic := stripTrailingColon(line[0], "") if topic == "GETH_VERSION" { if len(line) != 2 { - return nil, errors.Errorf("GETH_VERSION line should contain geth "+ + return nil, pkgerrors.Errorf("GETH_VERSION line should contain geth "+ "version, and only that: %s", line) } if rv.GethVersion != "" { - return nil, errors.Errorf("more than one geth version") + return nil, pkgerrors.Errorf("more than one geth version") } rv.GethVersion = line[1] } else { // It's a wrapper from a compiler artifact if len(line) != 4 { - return nil, errors.Errorf(`"%s" should have four elements `+ + return nil, pkgerrors.Errorf(`"%s" should have four elements `+ `": "`, db.Text()) } _, alreadyExists := rv.ContractVersions[topic] if alreadyExists { - return nil, errors.Errorf(`topic "%s" already mentioned`, topic) + return nil, pkgerrors.Errorf(`topic "%s" already mentioned`, topic) } rv.ContractVersions[topic] = ContractVersion{ AbiPath: line[1], BinaryPath: line[2], Hash: line[3], @@ -102,11 +102,11 @@ var stripTrailingColon = regexp.MustCompile(":$").ReplaceAllString func WriteVersionsDB(db *IntegratedVersion) (err error) { versionsDBPath, err := dbPath() if err != nil { - return errors.Wrap(err, "could not construct path to versions DB") + return pkgerrors.Wrap(err, "could not construct path to versions DB") } f, err := os.Create(versionsDBPath) if err != nil { - return errors.Wrapf(err, "while opening %s", versionsDBPath) + return pkgerrors.Wrapf(err, "while opening %s", versionsDBPath) } defer func() { if cerr := f.Close(); cerr != nil { @@ -116,10 +116,10 @@ func WriteVersionsDB(db *IntegratedVersion) (err error) { gethLine := "GETH_VERSION: " + db.GethVersion + "\n" n, err := f.WriteString(gethLine) if err != nil { - return errors.Wrapf(err, "while recording geth version line") + return pkgerrors.Wrapf(err, "while recording geth version line") } if n != len(gethLine) { - return errors.Errorf("failed to write entire geth version line, %s", gethLine) + return pkgerrors.Errorf("failed to write entire geth version line, %s", gethLine) } var pkgNames []string for name := range db.ContractVersions { @@ -132,10 +132,10 @@ func WriteVersionsDB(db *IntegratedVersion) (err error) { vinfo.AbiPath, vinfo.BinaryPath, vinfo.Hash) n, err = f.WriteString(versionLine) if err != nil { - return errors.Wrapf(err, "while recording %s version line", name) + return pkgerrors.Wrapf(err, "while recording %s version line", name) } if n != len(versionLine) { - return errors.Errorf("failed to write entire version line %s", versionLine) + return pkgerrors.Errorf("failed to write entire version line %s", versionLine) } } return nil diff --git a/core/internal/testutils/httptest/httptest.go b/core/internal/testutils/httptest/httptest.go index 7607ca7552d..a1bd941fb02 100644 --- a/core/internal/testutils/httptest/httptest.go +++ b/core/internal/testutils/httptest/httptest.go @@ -6,7 +6,7 @@ import ( "net/http" "time" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" ) // NewTestHTTPClient returns a real HTTP client that may only make requests to @@ -31,7 +31,7 @@ func testDialContext(ctx context.Context, network, address string) (net.Conn, er } a := con.RemoteAddr().(*net.TCPAddr) if a != nil && !a.IP.IsLoopback() { - return nil, errors.Errorf("Test HTTP client may only dial localhost, got address: %v", a.String()) + return nil, pkgerrors.Errorf("Test HTTP client may only dial localhost, got address: %v", a.String()) } return con, err } diff --git a/core/internal/testutils/keystest/keystest.go b/core/internal/testutils/keystest/keystest.go index beb7e580f1b..850e1ad1fa0 100644 --- a/core/internal/testutils/keystest/keystest.go +++ b/core/internal/testutils/keystest/keystest.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/crypto" "github.com/google/uuid" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" ) // NewKey pulled from geth @@ -19,7 +19,7 @@ func NewKey() (key keystore.Key, err error) { id, err := uuid.NewRandom() if err != nil { - return key, errors.Errorf("Could not create random uuid: %v", err) + return key, pkgerrors.Errorf("Could not create random uuid: %v", err) } return keystore.Key{ diff --git a/core/logger/zap.go b/core/logger/zap.go index c739a80d45a..e11458bdf8b 100644 --- a/core/logger/zap.go +++ b/core/logger/zap.go @@ -3,7 +3,7 @@ package logger import ( "os" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) @@ -76,7 +76,7 @@ func (l *zapLogger) Sync() error { return nil } var msg string - if uw := errors.Unwrap(err); uw != nil { + if uw := pkgerrors.Unwrap(err); uw != nil { msg = uw.Error() } else { msg = err.Error() diff --git a/core/sessions/localauth/orm.go b/core/sessions/localauth/orm.go index 013f719ad34..8afaf5e7fc1 100644 --- a/core/sessions/localauth/orm.go +++ b/core/sessions/localauth/orm.go @@ -7,7 +7,7 @@ import ( "time" "github.com/jmoiron/sqlx" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" "github.com/smartcontractkit/chainlink/v2/core/auth" @@ -69,7 +69,7 @@ func (o *orm) ListUsers() (users []sessions.User, err error) { func (o *orm) findValidSession(sessionID string) (email string, err error) { if err := o.q.Get(&email, "SELECT email FROM sessions WHERE id = $1 AND last_used + $2 >= now() FOR UPDATE", sessionID, o.sessionDuration); err != nil { o.lggr.Infof("query result: %v", email) - return email, errors.Wrap(err, "no matching user for provided session token") + return email, pkgerrors.Wrap(err, "no matching user for provided session token") } return email, nil } @@ -152,12 +152,12 @@ func (o *orm) CreateSession(sr sessions.SessionRequest) (string, error) { // for MFA tokens leaking if an account has MFA tokens or not. if !constantTimeEmailCompare(strings.ToLower(sr.Email), strings.ToLower(user.Email)) { o.auditLogger.Audit(audit.AuthLoginFailedEmail, map[string]interface{}{"email": sr.Email}) - return "", errors.New("Invalid email") + return "", pkgerrors.New("Invalid email") } if !utils.CheckPasswordHash(sr.Password, user.HashedPassword) { o.auditLogger.Audit(audit.AuthLoginFailedPassword, map[string]interface{}{"email": sr.Email}) - return "", errors.New("Invalid password") + return "", pkgerrors.New("Invalid password") } // Load all valid MFA tokens associated with user's email @@ -165,7 +165,7 @@ func (o *orm) CreateSession(sr sessions.SessionRequest) (string, error) { if err != nil { // There was an error with the database query lggr.Errorf("Could not fetch user's MFA data: %v", err) - return "", errors.New("MFA Error") + return "", pkgerrors.New("MFA Error") } // No webauthn tokens registered for the current user, so normal authentication is now complete @@ -185,16 +185,16 @@ func (o *orm) CreateSession(sr sessions.SessionRequest) (string, error) { options, webauthnError := sessions.BeginWebAuthnLogin(user, uwas, sr) if webauthnError != nil { lggr.Errorf("Could not begin WebAuthn verification: %v", webauthnError) - return "", errors.New("MFA Error") + return "", pkgerrors.New("MFA Error") } j, jsonError := json.Marshal(options) if jsonError != nil { lggr.Errorf("Could not serialize WebAuthn challenge: %v", jsonError) - return "", errors.New("MFA Error") + return "", pkgerrors.New("MFA Error") } - return "", errors.New(string(j)) + return "", pkgerrors.New(string(j)) } // The user is at the final stage of logging in with MFA. We have an @@ -206,7 +206,7 @@ func (o *orm) CreateSession(sr sessions.SessionRequest) (string, error) { // The user does have WebAuthn enabled but failed the check o.auditLogger.Audit(audit.AuthLoginFailed2FA, map[string]interface{}{"email": sr.Email, "error": err}) lggr.Errorf("User sent an invalid attestation: %v", err) - return "", errors.New("MFA Error") + return "", pkgerrors.New("MFA Error") } lggr.Infof("User passed MFA authentication and login will proceed") @@ -256,13 +256,13 @@ func (o *orm) UpdateRole(email, newRole string) (sessions.User, error) { var userToEdit sessions.User if newRole == "" { - return userToEdit, errors.New("user role must be specified") + return userToEdit, pkgerrors.New("user role must be specified") } err := o.q.Transaction(func(tx pg.Queryer) error { // First, attempt to load specified user by email if err := tx.Get(&userToEdit, "SELECT * FROM users WHERE lower(email) = lower($1)", email); err != nil { - return errors.New("no matching user for provided email") + return pkgerrors.New("no matching user for provided email") } // Patch validated role @@ -275,13 +275,13 @@ func (o *orm) UpdateRole(email, newRole string) (sessions.User, error) { _, err = tx.Exec("DELETE FROM sessions WHERE email = lower($1)", email) if err != nil { o.lggr.Errorf("Failed to purge user sessions for UpdateRole", "err", err) - return errors.New("error updating API user") + return pkgerrors.New("error updating API user") } sql := "UPDATE users SET role = $1, updated_at = now() WHERE lower(email) = lower($2) RETURNING *" if err := tx.Get(&userToEdit, sql, userToEdit.Role, email); err != nil { o.lggr.Errorf("Error updating API user", "err", err) - return errors.New("error updating API user") + return pkgerrors.New("error updating API user") } return nil @@ -304,10 +304,10 @@ func (o *orm) SetPassword(user *sessions.User, newPassword string) error { func (o *orm) TestPassword(email string, password string) error { var hashedPassword string if err := o.q.Get(&hashedPassword, "SELECT hashed_password FROM users WHERE lower(email) = lower($1)", email); err != nil { - return errors.New("no matching user for provided email") + return pkgerrors.New("no matching user for provided email") } if !utils.CheckPasswordHash(password, hashedPassword) { - return errors.New("passwords don't match") + return pkgerrors.New("passwords don't match") } return nil } @@ -328,7 +328,7 @@ func (o *orm) SetAuthToken(user *sessions.User, token *auth.Token) error { salt := utils.NewSecret(utils.DefaultSecretSize) hashedSecret, err := auth.HashedSecret(token, salt) if err != nil { - return errors.Wrap(err, "user") + return pkgerrors.Wrap(err, "user") } sql := "UPDATE users SET token_salt = $1, token_key = $2, token_hashed_secret = $3, updated_at = now() WHERE email = $4 RETURNING *" return o.q.Get(user, sql, salt, token.AccessKey, hashedSecret, user.Email) diff --git a/core/sessions/session.go b/core/sessions/session.go index 90964596e9a..49f403b6a81 100644 --- a/core/sessions/session.go +++ b/core/sessions/session.go @@ -4,7 +4,7 @@ import ( "crypto/subtle" "time" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink/v2/core/auth" @@ -55,7 +55,7 @@ func (u *User) SetAuthToken(token *auth.Token) error { salt := utils.NewSecret(utils.DefaultSecretSize) hashedSecret, err := auth.HashedSecret(token, salt) if err != nil { - return errors.Wrap(err, "user") + return pkgerrors.Wrap(err, "user") } u.TokenSalt = null.StringFrom(salt) u.TokenKey = null.StringFrom(token.AccessKey) diff --git a/core/sessions/user.go b/core/sessions/user.go index f2e4827b922..bcedaa4b197 100644 --- a/core/sessions/user.go +++ b/core/sessions/user.go @@ -5,7 +5,7 @@ import ( "net/mail" "time" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -58,7 +58,7 @@ func NewUser(email string, plainPwd string, role UserRole) (User, error) { // ValidateEmail is the single point of logic for user email validations func ValidateEmail(email string) error { if len(email) == 0 { - return errors.New("Must enter an email") + return pkgerrors.New("Must enter an email") } _, err := mail.ParseAddress(email) return err @@ -67,10 +67,10 @@ func ValidateEmail(email string) error { // ValidateAndHashPassword is the single point of logic for user password validations func ValidateAndHashPassword(plainPwd string) (string, error) { if err := utils.VerifyPasswordComplexity(plainPwd); err != nil { - return "", errors.Wrapf(err, "password insufficiently complex:\n%s", utils.PasswordComplexityRequirements) + return "", pkgerrors.Wrapf(err, "password insufficiently complex:\n%s", utils.PasswordComplexityRequirements) } if len(plainPwd) > MaxBcryptPasswordLength { - return "", errors.Errorf("must enter a password less than %v characters", MaxBcryptPasswordLength) + return "", pkgerrors.Errorf("must enter a password less than %v characters", MaxBcryptPasswordLength) } pwd, err := utils.HashPassword(plainPwd) @@ -104,5 +104,5 @@ func GetUserRole(role string) (UserRole, error) { UserRoleRun, UserRoleView, ) - return UserRole(""), errors.New(errStr) + return UserRole(""), pkgerrors.New(errStr) } diff --git a/core/sessions/webauthn.go b/core/sessions/webauthn.go index 89e7758bc5b..c959bec08de 100644 --- a/core/sessions/webauthn.go +++ b/core/sessions/webauthn.go @@ -10,7 +10,7 @@ import ( "github.com/go-webauthn/webauthn/protocol" "github.com/go-webauthn/webauthn/webauthn" sqlxTypes "github.com/jmoiron/sqlx/types" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" ) // WebAuthn holds the credentials for API user. @@ -93,7 +93,7 @@ func (store *WebAuthnSessionStore) FinishWebAuthnRegistration(user User, uwas [] credential, err := webAuthn.FinishRegistration(waUser, sessionData, response) if err != nil { - return nil, errors.Wrap(err, "failed to FinishRegistration") + return nil, pkgerrors.Wrap(err, "failed to FinishRegistration") } return credential, nil @@ -137,7 +137,7 @@ func FinishWebAuthnLogin(user User, uwas []WebAuthn, sr SessionRequest) error { }) if err != nil { - return errors.Wrapf(err, "failed to create webAuthn structure with RPID: %s and RPOrigin: %s", sr.WebAuthnConfig.RPID, sr.WebAuthnConfig.RPOrigin) + return pkgerrors.Wrapf(err, "failed to create webAuthn structure with RPID: %s and RPOrigin: %s", sr.WebAuthnConfig.RPID, sr.WebAuthnConfig.RPOrigin) } credential, err := protocol.ParseCredentialRequestResponseBody(strings.NewReader(sr.WebAuthnData)) @@ -272,7 +272,7 @@ func (store *WebAuthnSessionStore) take(key string) (val string, ok bool) { func (store *WebAuthnSessionStore) GetWebauthnSession(key string) (data webauthn.SessionData, err error) { assertion, ok := store.take(key) if !ok { - err = errors.New("assertion not in challenge store") + err = pkgerrors.New("assertion not in challenge store") return } err = json.Unmarshal([]byte(assertion), &data) diff --git a/core/store/migrate/migrate.go b/core/store/migrate/migrate.go index 04da4c48e92..aff3229e92a 100644 --- a/core/store/migrate/migrate.go +++ b/core/store/migrate/migrate.go @@ -10,7 +10,7 @@ import ( "strings" "github.com/jmoiron/sqlx" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/pressly/goose/v3" "gopkg.in/guregu/null.v4" @@ -62,7 +62,7 @@ func ensureMigrated(ctx context.Context, db *sql.DB, lggr logger.Logger) error { } } if !found { - return errors.New("database state is too old. Need to migrate to chainlink version 0.9.10 first before upgrading to this version. This upgrade is NOT REVERSIBLE, so it is STRONGLY RECOMMENDED that you take a database backup before continuing") + return pkgerrors.New("database state is too old. Need to migrate to chainlink version 0.9.10 first before upgrading to this version. This upgrade is NOT REVERSIBLE, so it is STRONGLY RECOMMENDED that you take a database backup before continuing") } // ensure a goose migrations table exists with it's initial v0 @@ -88,7 +88,7 @@ func ensureMigrated(ctx context.Context, db *sql.DB, lggr logger.Logger) error { id, err = strconv.ParseInt(name[:idx], 10, 64) if err == nil && id <= 0 { - return errors.New("migration IDs must be greater than zero") + return pkgerrors.New("migration IDs must be greater than zero") } } @@ -144,7 +144,7 @@ func SetMigrationENVVars(generalConfig chainlink.GeneralConfig) error { if generalConfig.EVMEnabled() { err := os.Setenv(env.EVMChainIDNotNullMigration0195, generalConfig.EVMConfigs()[0].ChainID.String()) if err != nil { - panic(errors.Wrap(err, "failed to set migrations env variables")) + panic(pkgerrors.Wrap(err, "failed to set migrations env variables")) } } return nil From e9a2dd0df8d4384056c36b76dce33fd5ff06faa2 Mon Sep 17 00:00:00 2001 From: Domino Valdano <2644901+reductionista@users.noreply.github.com> Date: Tue, 27 Feb 2024 23:53:35 -0800 Subject: [PATCH 08/16] Replace postgresql 14+ function with postgresql 12 compatible function (#12154) * Replace postgresql 14+ function with postgresql 11 compatible function * Capitalize sql keywords * Update to postgresql 12, add note about size of multicolumn index --- core/chains/evm/logpoller/orm.go | 2 +- ...poller_filters_add_topics_logs_per_block.sql | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/core/chains/evm/logpoller/orm.go b/core/chains/evm/logpoller/orm.go index f61845b870f..ca14d29563f 100644 --- a/core/chains/evm/logpoller/orm.go +++ b/core/chains/evm/logpoller/orm.go @@ -128,7 +128,7 @@ func (o *DbORM) InsertFilter(filter Filter, qopts ...pg.QOpt) (err error) { (SELECT unnest(:address_array ::::BYTEA[]) addr) a, (SELECT unnest(:event_sig_array ::::BYTEA[]) ev) e %s - ON CONFLICT (hash_record_extended((name, evm_chain_id, address, event, topic2, topic3, topic4), 0)) + ON CONFLICT (evm.f_log_poller_filter_hash(name, evm_chain_id, address, event, topic2, topic3, topic4)) DO UPDATE SET retention=:retention ::::BIGINT, max_logs_kept=:max_logs_kept ::::NUMERIC, logs_per_block=:logs_per_block ::::NUMERIC`, topicsColumns.String(), topicsSql.String()) diff --git a/core/store/migrate/migrations/0225_log_poller_filters_add_topics_logs_per_block.sql b/core/store/migrate/migrations/0225_log_poller_filters_add_topics_logs_per_block.sql index 77e3d5fbd51..90c6d4d0451 100644 --- a/core/store/migrate/migrations/0225_log_poller_filters_add_topics_logs_per_block.sql +++ b/core/store/migrate/migrations/0225_log_poller_filters_add_topics_logs_per_block.sql @@ -1,5 +1,13 @@ -- +goose Up +-- This generates a unique BIGINT for the log_poller_filters table from hashing (name, evm_chain_id, address, event, topic2, topic3, topic4). +-- Using an ordinary multi-column index on 7 columns would require a lot of extra storage space, and there are additional complications due to the topics being allowed to be NULL. +-- Note for updating this if and when we drop support for postgresql 12 & 13: hashrecordextended() can be used directly in postgresql 14, avoiding the need for a helper function. +-- The helper function is necessary only for the IMMUTABLE keyword +CREATE OR REPLACE FUNCTION evm.f_log_poller_filter_hash(name TEXT, evm_chain_id NUMERIC, address BYTEA, event BYTEA, topic2 BYTEA, topic3 BYTEA, topic4 BYTEA) + RETURNS BIGINT + LANGUAGE SQL IMMUTABLE COST 25 PARALLEL SAFE AS 'SELECT hashtextextended(textin(record_out(($1,$2,$3,$4,$5,$6,$7))), 0)'; + ALTER TABLE evm.log_poller_filters ADD COLUMN topic2 BYTEA CHECK (octet_length(topic2) = 32), ADD COLUMN topic3 BYTEA CHECK (octet_length(topic3) = 32), @@ -7,10 +15,7 @@ ALTER TABLE evm.log_poller_filters ADD COLUMN max_logs_kept BIGINT, ADD COLUMN logs_per_block BIGINT; --- Ordinary UNIQUE CONSTRAINT can't work for topics because they can be NULL. Any row with any column being NULL automatically satisfies the unique constraint (NULL != NULL) --- Using a hash of all the columns treats NULL's as the same as any other field. If we ever get to a point where we can require postgresql >= 15 then this can --- be fixed by using UNIQUE CONSTRAINT NULLS NOT DISTINCT which treats NULL's as if they were ordinary values (NULL == NULL) -CREATE UNIQUE INDEX evm_log_poller_filters_name_chain_address_event_topics_key ON evm.log_poller_filters (hash_record_extended((name, evm_chain_id, address, event, topic2, topic3, topic4), 0)); +CREATE UNIQUE INDEX log_poller_filters_hash_key ON evm.log_poller_filters (evm.f_log_poller_filter_hash(name, evm_chain_id, address, event, topic2, topic3, topic4)); ALTER TABLE evm.log_poller_filters DROP CONSTRAINT evm_log_poller_filters_name_evm_chain_id_address_event_key; @@ -19,7 +24,7 @@ ALTER TABLE evm.log_poller_filters ALTER TABLE evm.log_poller_filters ADD CONSTRAINT evm_log_poller_filters_name_evm_chain_id_address_event_key UNIQUE (name, evm_chain_id, address, event); -DROP INDEX IF EXISTS evm_log_poller_filters_name_chain_address_event_topics_key; +DROP INDEX IF EXISTS log_poller_filters_hash_key; ALTER TABLE evm.log_poller_filters DROP COLUMN topic2, @@ -27,3 +32,5 @@ ALTER TABLE evm.log_poller_filters DROP COLUMN topic4, DROP COLUMN max_logs_kept, DROP COLUMN logs_per_block; + +DROP FUNCTION IF EXISTS evm.f_log_poller_filter_hash(TEXT, NUMERIC, BYTEA, BYTEA, BYTEA, BYTEA, BYTEA); From aa22ad5cf1a7165a07183691525da27e3696eaed Mon Sep 17 00:00:00 2001 From: Mateusz Sekara Date: Wed, 28 Feb 2024 09:05:10 +0100 Subject: [PATCH 09/16] CCIP-1496 Delete expired logs by the block_timestamp (#12040) * Removing created_at index * SonarQube fix Post rebase fix Added debug log --- core/chains/evm/logpoller/log_poller.go | 1 + core/chains/evm/logpoller/orm.go | 4 +- core/chains/evm/logpoller/orm_test.go | 152 +++++++++--------- .../migrations/0226_log_poller_index_drop.sql | 5 + 4 files changed, 88 insertions(+), 74 deletions(-) create mode 100644 core/store/migrate/migrations/0226_log_poller_index_drop.sql diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index a19bbfda7f1..b1d00206130 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -916,6 +916,7 @@ func (lp *logPoller) latestBlocks(ctx context.Context) (*evmtypes.Head, int64, e } latest := blocks[0] finalized := blocks[1] + lp.lggr.Debugw("Latest blocks read from chain", "latest", latest.Number, "finalized", finalized.Number) return latest, finalized.Number, nil } diff --git a/core/chains/evm/logpoller/orm.go b/core/chains/evm/logpoller/orm.go index ca14d29563f..777cf3546cb 100644 --- a/core/chains/evm/logpoller/orm.go +++ b/core/chains/evm/logpoller/orm.go @@ -309,7 +309,7 @@ func (o *DbORM) DeleteExpiredLogs(limit int64, qopts ...pg.QOpt) (int64, error) GROUP BY evm_chain_id,address, event HAVING NOT 0 = ANY(ARRAY_AGG(retention)) ) DELETE FROM evm.logs l USING r WHERE l.evm_chain_id = $1 AND l.address=r.address AND l.event_sig=r.event - AND l.created_at <= STATEMENT_TIMESTAMP() - (r.retention / 10^9 * interval '1 second')`, // retention is in nanoseconds (time.Duration aka BIGINT) + AND l.block_timestamp <= STATEMENT_TIMESTAMP() - (r.retention / 10^9 * interval '1 second')`, // retention is in nanoseconds (time.Duration aka BIGINT) ubig.New(o.chainID)) } @@ -397,7 +397,7 @@ func (o *DbORM) SelectLogsByBlockRange(start, end int64) ([]Log, error) { WHERE evm_chain_id = :evm_chain_id AND block_number >= :start_block AND block_number <= :end_block - ORDER BY (block_number, log_index, created_at)`, args) + ORDER BY (block_number, log_index)`, args) if err != nil { return nil, err } diff --git a/core/chains/evm/logpoller/orm_test.go b/core/chains/evm/logpoller/orm_test.go index 655a7295dab..a9f39a7ac4a 100644 --- a/core/chains/evm/logpoller/orm_test.go +++ b/core/chains/evm/logpoller/orm_test.go @@ -222,92 +222,100 @@ func TestORM(t *testing.T) { topic2 := common.HexToHash("0x1600") require.NoError(t, o1.InsertLogs([]logpoller.Log{ { - EvmChainId: ubig.New(th.ChainID), - LogIndex: 1, - BlockHash: common.HexToHash("0x1234"), - BlockNumber: int64(10), - EventSig: topic, - Topics: [][]byte{topic[:]}, - Address: common.HexToAddress("0x1234"), - TxHash: common.HexToHash("0x1888"), - Data: []byte("hello"), + EvmChainId: ubig.New(th.ChainID), + LogIndex: 1, + BlockHash: common.HexToHash("0x1234"), + BlockNumber: int64(10), + EventSig: topic, + Topics: [][]byte{topic[:]}, + Address: common.HexToAddress("0x1234"), + TxHash: common.HexToHash("0x1888"), + Data: []byte("hello"), + BlockTimestamp: time.Now(), }, { - EvmChainId: ubig.New(th.ChainID), - LogIndex: 2, - BlockHash: common.HexToHash("0x1234"), - BlockNumber: int64(11), - EventSig: topic, - Topics: [][]byte{topic[:]}, - Address: common.HexToAddress("0x1234"), - TxHash: common.HexToHash("0x1888"), - Data: []byte("hello"), + EvmChainId: ubig.New(th.ChainID), + LogIndex: 2, + BlockHash: common.HexToHash("0x1234"), + BlockNumber: int64(11), + EventSig: topic, + Topics: [][]byte{topic[:]}, + Address: common.HexToAddress("0x1234"), + TxHash: common.HexToHash("0x1888"), + Data: []byte("hello"), + BlockTimestamp: time.Now(), }, { - EvmChainId: ubig.New(th.ChainID), - LogIndex: 3, - BlockHash: common.HexToHash("0x1234"), - BlockNumber: int64(12), - EventSig: topic, - Topics: [][]byte{topic[:]}, - Address: common.HexToAddress("0x1235"), - TxHash: common.HexToHash("0x1888"), - Data: []byte("hello"), + EvmChainId: ubig.New(th.ChainID), + LogIndex: 3, + BlockHash: common.HexToHash("0x1234"), + BlockNumber: int64(12), + EventSig: topic, + Topics: [][]byte{topic[:]}, + Address: common.HexToAddress("0x1235"), + TxHash: common.HexToHash("0x1888"), + Data: []byte("hello"), + BlockTimestamp: time.Now(), }, { - EvmChainId: ubig.New(th.ChainID), - LogIndex: 4, - BlockHash: common.HexToHash("0x1234"), - BlockNumber: int64(13), - EventSig: topic, - Topics: [][]byte{topic[:]}, - Address: common.HexToAddress("0x1235"), - TxHash: common.HexToHash("0x1888"), - Data: []byte("hello"), + EvmChainId: ubig.New(th.ChainID), + LogIndex: 4, + BlockHash: common.HexToHash("0x1234"), + BlockNumber: int64(13), + EventSig: topic, + Topics: [][]byte{topic[:]}, + Address: common.HexToAddress("0x1235"), + TxHash: common.HexToHash("0x1888"), + Data: []byte("hello"), + BlockTimestamp: time.Now(), }, { - EvmChainId: ubig.New(th.ChainID), - LogIndex: 5, - BlockHash: common.HexToHash("0x1234"), - BlockNumber: int64(14), - EventSig: topic2, - Topics: [][]byte{topic2[:]}, - Address: common.HexToAddress("0x1234"), - TxHash: common.HexToHash("0x1888"), - Data: []byte("hello2"), + EvmChainId: ubig.New(th.ChainID), + LogIndex: 5, + BlockHash: common.HexToHash("0x1234"), + BlockNumber: int64(14), + EventSig: topic2, + Topics: [][]byte{topic2[:]}, + Address: common.HexToAddress("0x1234"), + TxHash: common.HexToHash("0x1888"), + Data: []byte("hello2"), + BlockTimestamp: time.Now(), }, { - EvmChainId: ubig.New(th.ChainID), - LogIndex: 6, - BlockHash: common.HexToHash("0x1234"), - BlockNumber: int64(15), - EventSig: topic2, - Topics: [][]byte{topic2[:]}, - Address: common.HexToAddress("0x1235"), - TxHash: common.HexToHash("0x1888"), - Data: []byte("hello2"), + EvmChainId: ubig.New(th.ChainID), + LogIndex: 6, + BlockHash: common.HexToHash("0x1234"), + BlockNumber: int64(15), + EventSig: topic2, + Topics: [][]byte{topic2[:]}, + Address: common.HexToAddress("0x1235"), + TxHash: common.HexToHash("0x1888"), + Data: []byte("hello2"), + BlockTimestamp: time.Now(), }, { - EvmChainId: ubig.New(th.ChainID), - LogIndex: 7, - BlockHash: common.HexToHash("0x1237"), - BlockNumber: int64(16), - EventSig: topic, - Topics: [][]byte{topic[:]}, - Address: common.HexToAddress("0x1236"), - TxHash: common.HexToHash("0x1888"), - Data: []byte("hello short retention"), + EvmChainId: ubig.New(th.ChainID), + LogIndex: 7, + BlockHash: common.HexToHash("0x1237"), + BlockNumber: int64(16), + EventSig: topic, + Topics: [][]byte{topic[:]}, + Address: common.HexToAddress("0x1236"), + TxHash: common.HexToHash("0x1888"), + Data: []byte("hello short retention"), + BlockTimestamp: time.Now(), }, { - EvmChainId: ubig.New(th.ChainID), - LogIndex: 8, - BlockHash: common.HexToHash("0x1238"), - BlockNumber: int64(17), - EventSig: topic2, - Topics: [][]byte{topic2[:]}, - Address: common.HexToAddress("0x1236"), - TxHash: common.HexToHash("0x1888"), - Data: []byte("hello2 long retention"), + EvmChainId: ubig.New(th.ChainID), + LogIndex: 8, + BlockHash: common.HexToHash("0x1238"), + BlockNumber: int64(17), + EventSig: topic2, + Topics: [][]byte{topic2[:]}, + Address: common.HexToAddress("0x1236"), + TxHash: common.HexToHash("0x1888"), + Data: []byte("hello2 long retention"), + BlockTimestamp: time.Now(), }, })) diff --git a/core/store/migrate/migrations/0226_log_poller_index_drop.sql b/core/store/migrate/migrations/0226_log_poller_index_drop.sql new file mode 100644 index 00000000000..f20fd00aaa2 --- /dev/null +++ b/core/store/migrate/migrations/0226_log_poller_index_drop.sql @@ -0,0 +1,5 @@ +-- +goose Up +DROP INDEX evm.evm_logs_idx_created_at; + +-- +goose Down +CREATE INDEX evm_logs_idx_created_at ON evm.logs (created_at); From 24c3718de8d3d392f232384a8ea8dc702f5bb84b Mon Sep 17 00:00:00 2001 From: Bolek <1416262+bolekk@users.noreply.github.com> Date: Wed, 28 Feb 2024 02:38:27 -0800 Subject: [PATCH 10/16] [KS-55] Extend demo Engine to two targets (#12151) Additionally handle requests in separate goroutines, as they can be relatively long-lived. --- core/services/workflows/engine.go | 142 ++++++++++++++++--------- core/services/workflows/engine_test.go | 24 ++++- 2 files changed, 109 insertions(+), 57 deletions(-) diff --git a/core/services/workflows/engine.go b/core/services/workflows/engine.go index 01d1326e072..8985f9d1599 100644 --- a/core/services/workflows/engine.go +++ b/core/services/workflows/engine.go @@ -2,6 +2,7 @@ package workflows import ( "context" + "errors" "fmt" "time" @@ -31,13 +32,17 @@ type Engine struct { consensusType string consensusConfig *values.Map consensus capabilities.ConsensusCapability - targetType string - targetConfig *values.Map - target capabilities.TargetCapability + targets []target callbackCh chan capabilities.CapabilityResponse cancel func() } +type target struct { + typeStr string + config *values.Map + capability capabilities.TargetCapability +} + func (e *Engine) Start(ctx context.Context) error { return e.StartOnce("Engine", func() error { // create a new context, since the one passed in via Start is short-lived. @@ -70,12 +75,18 @@ LOOP: e.logger.Errorf("failed to get consensus capability: %s, retrying in %d seconds", err, retrySec) break } - e.target, err = e.registry.GetTarget(ctx, e.targetType) - if err != nil { - e.logger.Errorf("failed to get target capability: %s, retrying in %d seconds", err, retrySec) - break + failed := false + for i := range e.targets { + e.targets[i].capability, err = e.registry.GetTarget(ctx, e.targets[i].typeStr) + if err != nil { + e.logger.Errorf("failed to get target capability: %s, retrying in %d seconds", err, retrySec) + failed = true + break + } + } + if !failed { + break LOOP } - break LOOP } } @@ -130,50 +141,25 @@ func (e *Engine) triggerHandlerLoop(ctx context.Context) { case <-ctx.Done(): return case resp := <-e.callbackCh: - err := e.handleExecution(ctx, resp) - if err != nil { - e.logger.Error("error executing event %+v: %w", resp, err) - } + go e.handleExecution(ctx, resp) } } } -func (e *Engine) handleExecution(ctx context.Context, event capabilities.CapabilityResponse) error { +func (e *Engine) handleExecution(ctx context.Context, event capabilities.CapabilityResponse) { e.logger.Debugw("executing on a trigger event", "event", event) - results, err := e.handleConsensus(ctx, event) + result, err := e.handleConsensus(ctx, event) if err != nil { - return err - } - if len(results.Underlying) == 0 { - return fmt.Errorf("consensus returned no reports") + e.logger.Errorf("error in handleConsensus %v", err) + return } - if len(results.Underlying) > 1 { - e.logger.Debugw("consensus returned more than one report") - } - - // we're expecting exactly one report - _, err = e.handleTarget(ctx, results.Underlying[0]) - return err -} - -func (e *Engine) handleTarget(ctx context.Context, resp values.Value) (*values.List, error) { - e.logger.Debugw("handle target") - inputs := map[string]values.Value{ - "report": resp, - } - - tr := capabilities.CapabilityRequest{ - Inputs: &values.Map{Underlying: inputs}, - Config: e.targetConfig, - Metadata: capabilities.RequestMetadata{ - WorkflowID: mockedWorkflowID, - WorkflowExecutionID: mockedExecutionID, - }, + err = e.handleTargets(ctx, result) + if err != nil { + e.logger.Error("error in handleTargets %v", err) } - return capabilities.ExecuteSync(ctx, e.target, tr) } -func (e *Engine) handleConsensus(ctx context.Context, event capabilities.CapabilityResponse) (*values.List, error) { +func (e *Engine) handleConsensus(ctx context.Context, event capabilities.CapabilityResponse) (values.Value, error) { e.logger.Debugw("running consensus", "event", event) cr := capabilities.CapabilityRequest{ Metadata: capabilities.RequestMetadata{ @@ -190,7 +176,45 @@ func (e *Engine) handleConsensus(ctx context.Context, event capabilities.Capabil }, Config: e.consensusConfig, } - return capabilities.ExecuteSync(ctx, e.consensus, cr) + chReports := make(chan capabilities.CapabilityResponse, 10) + newCtx, cancel := context.WithCancel(ctx) + defer cancel() + err := e.consensus.Execute(newCtx, chReports, cr) + if err != nil { + return nil, err + } + select { + case resp := <-chReports: + if resp.Err != nil { + return nil, resp.Err + } + return resp.Value, nil + case <-ctx.Done(): + return nil, ctx.Err() + } +} + +func (e *Engine) handleTargets(ctx context.Context, resp values.Value) error { + e.logger.Debugw("handle targets") + inputs := map[string]values.Value{ + "report": resp, + } + + var combinedErr error + for _, t := range e.targets { + e.logger.Debugw("sending to target", "target", t.typeStr, "inputs", inputs) + tr := capabilities.CapabilityRequest{ + Inputs: &values.Map{Underlying: inputs}, + Config: t.config, + Metadata: capabilities.RequestMetadata{ + WorkflowID: mockedWorkflowID, + WorkflowExecutionID: mockedExecutionID, + }, + } + _, err := capabilities.ExecuteSync(ctx, t.capability, tr) + combinedErr = errors.Join(combinedErr, err) + } + return combinedErr } func (e *Engine) Close() error { @@ -227,7 +251,9 @@ func NewEngine(lggr logger.Logger, registry types.CapabilitiesRegistry) (engine engine.triggerConfig, err = values.NewMap( map[string]any{ "feedlist": []any{ - 123, 456, 789, // ETHUSD, LINKUSD, USDBTC + "0x1111111111111111111100000000000000000000000000000000000000000000", // ETHUSD + "0x2222222222222222222200000000000000000000000000000000000000000000", // LINKUSD + "0x3333333333333333333300000000000000000000000000000000000000000000", // BTCUSD }, }, ) @@ -242,18 +268,18 @@ func NewEngine(lggr logger.Logger, registry types.CapabilitiesRegistry) (engine "aggregation_config": map[string]any{ // ETHUSD "0x1111111111111111111100000000000000000000000000000000000000000000": map[string]any{ - "deviation": decimal.NewFromFloat(0.003), - "heartbeat": 24, + "deviation": decimal.NewFromFloat(0.001), + "heartbeat": 1800, }, // LINKUSD "0x2222222222222222222200000000000000000000000000000000000000000000": map[string]any{ "deviation": decimal.NewFromFloat(0.001), - "heartbeat": 24, + "heartbeat": 1800, }, // BTCUSD "0x3333333333333333333300000000000000000000000000000000000000000000": map[string]any{ - "deviation": decimal.NewFromFloat(0.002), - "heartbeat": 6, + "deviation": decimal.NewFromFloat(0.001), + "heartbeat": 1800, }, }, "encoder": "EVM", @@ -265,10 +291,20 @@ func NewEngine(lggr logger.Logger, registry types.CapabilitiesRegistry) (engine return nil, err } - // Target - engine.targetType = "write_polygon-testnet-mumbai" - engine.targetConfig, err = values.NewMap(map[string]any{ - "address": "0xaabbcc", + // Targets + engine.targets = make([]target, 2) + engine.targets[0].typeStr = "write_polygon-testnet-mumbai" + engine.targets[0].config, err = values.NewMap(map[string]any{ + "address": "0x3F3554832c636721F1fD1822Ccca0354576741Ef", + "params": []any{"$(report)"}, + "abi": "receive(report bytes)", + }) + if err != nil { + return nil, err + } + engine.targets[1].typeStr = "write_ethereum-testnet-sepolia" + engine.targets[1].config, err = values.NewMap(map[string]any{ + "address": "0x54e220867af6683aE6DcBF535B4f952cB5116510", "params": []any{"$(report)"}, "abi": "receive(report bytes)", }) diff --git a/core/services/workflows/engine_test.go b/core/services/workflows/engine_test.go index aa84dc29cc9..e63264c789f 100644 --- a/core/services/workflows/engine_test.go +++ b/core/services/workflows/engine_test.go @@ -95,22 +95,37 @@ func TestEngineWithHardcodedWorkflow(t *testing.T) { ) require.NoError(t, reg.Add(ctx, consensus)) - target := newMockCapability( + target1 := newMockCapability( capabilities.MustNewCapabilityInfo( "write_polygon-testnet-mumbai", capabilities.CapabilityTypeTarget, - "a write capability targeting polygon mainnet", + "a write capability targeting polygon mumbai testnet", "v1.0.0", ), func(req capabilities.CapabilityRequest) (capabilities.CapabilityResponse, error) { + list := req.Inputs.Underlying["report"].(*values.List) + return capabilities.CapabilityResponse{ + Value: list.Underlying[0], + }, nil + }, + ) + require.NoError(t, reg.Add(ctx, target1)) + target2 := newMockCapability( + capabilities.MustNewCapabilityInfo( + "write_ethereum-testnet-sepolia", + capabilities.CapabilityTypeTarget, + "a write capability targeting ethereum sepolia testnet", + "v1.0.0", + ), + func(req capabilities.CapabilityRequest) (capabilities.CapabilityResponse, error) { list := req.Inputs.Underlying["report"].(*values.List) return capabilities.CapabilityResponse{ Value: list.Underlying[0], }, nil }, ) - require.NoError(t, reg.Add(ctx, target)) + require.NoError(t, reg.Add(ctx, target2)) lggr := logger.TestLogger(t) eng, err := NewEngine(lggr, reg) @@ -130,5 +145,6 @@ func TestEngineWithHardcodedWorkflow(t *testing.T) { err = eng.Start(ctx) require.NoError(t, err) defer eng.Close() - assert.Equal(t, cr, <-target.response) + assert.Equal(t, cr, <-target1.response) + assert.Equal(t, cr, <-target2.response) } From 96b990338d678feb9f6a7f87f96299c03d0f245c Mon Sep 17 00:00:00 2001 From: Vyzaldy Sanchez Date: Wed, 28 Feb 2024 12:33:51 -0400 Subject: [PATCH 11/16] Removes nightly fuzz CI workflow (#12202) --- .github/workflows/nightlyfuzz.yml | 53 ------------------------------- 1 file changed, 53 deletions(-) delete mode 100644 .github/workflows/nightlyfuzz.yml diff --git a/.github/workflows/nightlyfuzz.yml b/.github/workflows/nightlyfuzz.yml deleted file mode 100644 index 7becbe73de5..00000000000 --- a/.github/workflows/nightlyfuzz.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: 'nightly/tag fuzz' -on: - schedule: - # Note: The schedule event can be delayed during periods of high - # loads of GitHub Actions workflow runs. High load times include - # the start of every hour. To decrease the chance of delay, - # schedule your workflow to run at a different time of the hour. - - cron: "25 0 * * *" # at 25 past midnight every day - push: - tags: - - '*' - workflow_dispatch: null -jobs: - fuzzrun: - name: "run native fuzzers" - runs-on: "ubuntu20.04-4cores-16GB" - steps: - - name: "Checkout" - uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" # v4.1.1 - - name: "Setup go" - uses: "actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491" # v5.0.0 - with: - go-version-file: 'go.mod' - cache: true - cache-dependency-path: 'go.sum' - - name: "Get corpus directory" - id: "get-corpus-dir" - run: echo "corpus_dir=$(go env GOCACHE)/fuzz" >> $GITHUB_OUTPUT - shell: "bash" - - name: "Restore corpus" - uses: "actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2" # v4.0.0 - id: "restore-corpus" - with: - path: "${{ steps.get-corpus-dir.outputs.corpus_dir }}" - # We need to ensure uniqueness of the key, as saving to a key more than once will fail (see Save corpus step). - # We never expect a cache hit with the key but we do expect a hit with the restore-keys prefix that is going - # to match the latest cache that has that prefix. - key: "nightlyfuzz-corpus-${{ github.run_id }}-${{ github.run_attempt }}" - restore-keys: "nightlyfuzz-corpus-" - - name: "Run native fuzzers" - # Fuzz for 1 hour - run: "cd fuzz && ./fuzz_all_native.py --seconds 3600" - - name: "Print failing testcases" - if: failure() - run: find . -type f|fgrep '/testdata/fuzz/'|while read f; do echo $f; cat $f; done - - name: "Save corpus" - uses: "actions/cache/save@13aacd865c20de90d75de3b17ebe84f7a17d57d2" # v4.0.0 - # We save also on failure, so that we can keep the valuable corpus generated that led to finding a crash. - # If the corpus gets clobbered for any reason, we can remove the offending cache from the Github UI. - if: always() - with: - path: "${{ steps.get-corpus-dir.outputs.corpus_dir }}" - key: "${{ steps.restore-corpus.outputs.cache-primary-key }}" \ No newline at end of file From fb8b399ee50b729e530150b209a8a411ba0d22e8 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Wed, 28 Feb 2024 14:42:26 -0300 Subject: [PATCH 12/16] [TT-791] Migrate OCRv1/v2 tests from EVMClient to Seth (#12076) * use simple bash script to set BASE64 env var * added some OCR actions that use Seth, refactored others to use it if they weren't used outside of OCR soak tests, renamed old Ethereum contracts to Legacy* (only ones reimplemented with Seth), removed unused methods from contract interfaces, added Seth to testconfig * move ocr2 smoke tests to seth * don't panic, when network is not known to Seth, use defaults instead * restore some old ocr helper functions * add chain-of-responsibility-based fund returning function * fix some bugs, add Deprecated comments to deprecated functions, add logs to funds sendnig retriers * small streamlines * fix lints * go mod tidy, use latest Seth * more go.mod * lints + more go.mods * use EVMClient by default when building docker test env * ignore code duplication in files that will be deleted when we finish the migration * fixed forwarder soak test * change flow so that OCRv2 soak tests can run with forwarders * code review changes * merge 2 interfaces + move OCRv1 smoke tests to Seth * add method for funding CL nodes from root private key --- core/scripts/go.mod | 4 +- core/scripts/go.sum | 6 +- go.mod | 4 +- go.sum | 6 +- integration-tests/Makefile | 18 +- integration-tests/actions/actions.go | 4 +- integration-tests/actions/actions_local.go | 4 +- integration-tests/actions/ocr2_helpers.go | 4 +- integration-tests/actions/ocr_helpers.go | 17 +- .../actions/ocr_helpers_local.go | 31 +- .../actions/operator_forwarder_helpers.go | 3 + integration-tests/actions/seth/actions.go | 528 ++++++++++++++++ integration-tests/actions/seth/refund.go | 287 +++++++++ integration-tests/chaos/ocr_chaos_test.go | 45 +- integration-tests/client/chainlink.go | 1 + .../contracts/contract_deployer.go | 10 +- .../contracts/contract_loader.go | 4 +- .../contracts/contract_models.go | 20 +- .../contracts/ethereum_contracts.go | 164 +++-- .../contracts/ethereum_contracts_local.go | 98 --- .../contracts/ethereum_contracts_seth.go | 581 ++++++++++++++++++ integration-tests/docker/test_env/test_env.go | 54 +- .../docker/test_env/test_env_builder.go | 108 +++- integration-tests/go.mod | 8 +- integration-tests/go.sum | 14 +- integration-tests/k8s/connect.go | 20 +- integration-tests/load/go.mod | 9 +- integration-tests/load/go.sum | 14 +- integration-tests/load/ocr/gun.go | 11 +- integration-tests/load/ocr/helper.go | 31 +- integration-tests/load/ocr/ocr_test.go | 42 +- integration-tests/load/ocr/vu.go | 29 +- .../scripts/check_base64_env_var.sh | 36 ++ integration-tests/smoke/forwarder_ocr_test.go | 2 + .../smoke/forwarders_ocr2_test.go | 3 + integration-tests/smoke/ocr2_test.go | 234 ++----- integration-tests/smoke/ocr_test.go | 137 ++--- integration-tests/soak/forwarder_ocr_test.go | 4 +- integration-tests/soak/ocr_test.go | 4 +- integration-tests/testconfig/default.toml | 82 ++- integration-tests/testconfig/ocr/ocr.toml | 2 +- integration-tests/testconfig/testconfig.go | 22 +- integration-tests/testsetups/ocr.go | 189 +++--- integration-tests/types/testconfigs.go | 1 + integration-tests/utils/seth.go | 84 +++ sonar-project.properties | 7 +- 46 files changed, 2216 insertions(+), 770 deletions(-) create mode 100644 integration-tests/actions/seth/actions.go create mode 100644 integration-tests/actions/seth/refund.go delete mode 100644 integration-tests/contracts/ethereum_contracts_local.go create mode 100644 integration-tests/contracts/ethereum_contracts_seth.go create mode 100755 integration-tests/scripts/check_base64_env_var.sh create mode 100644 integration-tests/utils/seth.go diff --git a/core/scripts/go.mod b/core/scripts/go.mod index b389d30f4ac..63fa0531c9f 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -53,10 +53,10 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/XSAM/otelsql v0.27.0 // indirect - github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/avast/retry-go/v4 v4.5.1 // indirect github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect + github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect @@ -298,7 +298,7 @@ require ( go.opentelemetry.io/otel/trace v1.21.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/ratelimit v0.2.0 // indirect + go.uber.org/ratelimit v0.3.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/arch v0.7.0 // indirect golang.org/x/crypto v0.19.0 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index f90d2aade47..30e711b8af6 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -134,7 +134,6 @@ github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1L github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= @@ -157,6 +156,8 @@ github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -1411,8 +1412,9 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA= go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= +go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= +go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= diff --git a/go.mod b/go.mod index b01750a139a..82963eaaa71 100644 --- a/go.mod +++ b/go.mod @@ -130,9 +130,9 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/VictoriaMetrics/fastcache v1.12.1 // indirect - github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect + github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect @@ -315,7 +315,7 @@ require ( go.opentelemetry.io/otel/sdk v1.21.0 // indirect go.opentelemetry.io/otel/trace v1.21.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect - go.uber.org/ratelimit v0.2.0 // indirect + go.uber.org/ratelimit v0.3.0 // indirect golang.org/x/arch v0.7.0 // indirect golang.org/x/net v0.21.0 // indirect golang.org/x/oauth2 v0.17.0 // indirect diff --git a/go.sum b/go.sum index 5e386c5b0de..07b4e8ccdcb 100644 --- a/go.sum +++ b/go.sum @@ -138,7 +138,6 @@ github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1L github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= @@ -161,6 +160,8 @@ github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -1406,8 +1407,9 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA= go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= +go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= +go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= diff --git a/integration-tests/Makefile b/integration-tests/Makefile index 8415c00f9cd..2d887e97baf 100644 --- a/integration-tests/Makefile +++ b/integration-tests/Makefile @@ -130,7 +130,7 @@ test_node_migrations: install_gotestfmt ## Run all node migration tests. go test -timeout 1h -count=1 -json $(args) ./migration 2>&1 | tee /tmp/gotest.log | gotestfmt .PHONY: test_node_migrations_simulated -test_node_migrations_simulated: install_gotestfmt +test_node_migrations_simulated: install_gotestfmt TEST_LOG_LEVEL="disabled" \ go test -timeout 1h -count=1 -json $(args) ./migration 2>&1 | tee /tmp/gotest.log | gotestfmt @@ -145,30 +145,22 @@ test_node_migrations_simulated_verbose: # Soak .PHONY: test_soak_ocr test_soak_ocr: - go test -v -count=1 -run TestOCRSoak ./soak - -.PHONY: test_soak_ocr_simulated -test_soak_ocr_simulated: + . ./scripts/check_base64_env_var.sh go test -v -count=1 -run TestOCRSoak ./soak .PHONY: test_soak_forwarder_ocr test_soak_forwarder_ocr: - go test -v -count=1 -run TestForwarderOCRSoak ./soak - -.PHONY: test_soak_forwarder_ocr_simulated -test_soak_forwarder_ocr_simulated: + . ./scripts/check_base64_env_var.sh go test -v -count=1 -run TestForwarderOCRSoak ./soak .PHONY: test_soak_automation test_soak_automation: - go test -v -run ^TestAutomationBenchmark$$ ./benchmark -count=1 - -.PHONY: test_soak_automation_simulated -test_soak_automation_simulated: + . ./scripts/check_base64_env_var.sh go test -v -run ^TestAutomationBenchmark$$ ./benchmark -count=1 .PHONY: test_benchmark_automation test_benchmark_automation: ## Run the automation benchmark tests + . ./scripts/check_base64_env_var.sh go test -timeout 30m -v -run ^TestAutomationBenchmark$$ ./benchmark -count=1 .PHONY: test_reorg_automation diff --git a/integration-tests/actions/actions.go b/integration-tests/actions/actions.go index 6f820247535..f5c91b63527 100644 --- a/integration-tests/actions/actions.go +++ b/integration-tests/actions/actions.go @@ -39,7 +39,8 @@ import ( // Example: When deploying 1000 contracts, stop every ContractDeploymentInterval have been deployed to wait before continuing var ContractDeploymentInterval = 200 -// FundChainlinkNodes will fund all of the provided Chainlink nodes with a set amount of native currency +// FundChainlinkNodes will fund all of the provided Chainlink nodes with a set amountCreateOCRv2Jobs of native currency +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.FundChainlinkNodes func FundChainlinkNodes( nodes []*client.ChainlinkK8sClient, client blockchain.EVMClient, @@ -303,6 +304,7 @@ func TeardownSuite( // TeardownRemoteSuite is used when running a test within a remote-test-runner, like for long-running performance and // soak tests +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.TeardownRemoteSuite func TeardownRemoteSuite( t *testing.T, namespace string, diff --git a/integration-tests/actions/actions_local.go b/integration-tests/actions/actions_local.go index 8ac623d8841..5bb0c202a55 100644 --- a/integration-tests/actions/actions_local.go +++ b/integration-tests/actions/actions_local.go @@ -1,9 +1,7 @@ // Package actions enables common chainlink interactions package actions -import ( - "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" -) +import "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" // UpgradeChainlinkNodeVersions upgrades all Chainlink nodes to a new version, and then runs the test environment // to apply the upgrades diff --git a/integration-tests/actions/ocr2_helpers.go b/integration-tests/actions/ocr2_helpers.go index 829d85a8498..e5f9beff748 100644 --- a/integration-tests/actions/ocr2_helpers.go +++ b/integration-tests/actions/ocr2_helpers.go @@ -33,6 +33,7 @@ import ( ) // DeployOCRv2Contracts deploys a number of OCRv2 contracts and configures them with defaults +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.DeployOCRv2Contracts func DeployOCRv2Contracts( numberOfContracts int, linkTokenContract contracts.LinkToken, @@ -85,6 +86,7 @@ func DeployOCRv2Contracts( return ocrInstances, client.WaitForEvents() } +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.ConfigureOCRv2AggregatorContracts func ConfigureOCRv2AggregatorContracts( client blockchain.EVMClient, contractConfig *contracts.OCRv2Config, @@ -266,7 +268,7 @@ func CreateOCRv2Jobs( workerChainlinkNodes []*client.ChainlinkK8sClient, mockserver *ctfClient.MockserverClient, mockServerValue int, // Value to get from the mock server when querying the path - chainId uint64, // EVM chain ID + chainId int64, // EVM chain ID forwardingAllowed bool, ) error { // Collect P2P ID diff --git a/integration-tests/actions/ocr_helpers.go b/integration-tests/actions/ocr_helpers.go index dd0e6606e43..89a9d1574d2 100644 --- a/integration-tests/actions/ocr_helpers.go +++ b/integration-tests/actions/ocr_helpers.go @@ -22,7 +22,7 @@ import ( // This actions file often returns functions, rather than just values. These are used as common test helpers, and are // handy to have returning as functions so that Ginkgo can use them in an aesthetically pleasing way. -// DeployOCRContracts deploys and funds a certain number of offchain aggregator contracts +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.DeployOCRContracts func DeployOCRContracts( numberOfContracts int, linkTokenContract contracts.LinkToken, @@ -90,7 +90,7 @@ func DeployOCRContracts( for contractCount, ocrInstance := range ocrInstances { // Exclude the first node, which will be used as a bootstrapper err = ocrInstance.SetConfig( - workerNodes, + contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes), contracts.DefaultOffChainAggregatorConfig(len(workerNodes)), transmitterAddresses, ) @@ -113,6 +113,7 @@ func DeployOCRContracts( // DeployOCRContractsForwarderFlow deploys and funds a certain number of offchain // aggregator contracts with forwarders as effectiveTransmitters +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.DeployOCRContractsForwarderFlow func DeployOCRContractsForwarderFlow( t *testing.T, numberOfContracts int, @@ -163,7 +164,7 @@ func DeployOCRContractsForwarderFlow( for contractCount, ocrInstance := range ocrInstances { // Exclude the first node, which will be used as a bootstrapper err = ocrInstance.SetConfig( - workerNodes, + contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes), contracts.DefaultOffChainAggregatorConfig(len(workerNodes)), forwarderAddresses, ) @@ -267,7 +268,7 @@ func CreateOCRJobsWithForwarder( workerNodes []*client.ChainlinkK8sClient, mockValue int, mockserver *ctfClient.MockserverClient, - evmChainID string, + evmChainID int64, ) { for _, ocrInstance := range ocrInstances { bootstrapP2PIds, err := bootstrapNode.MustReadP2PKeys() @@ -276,7 +277,7 @@ func CreateOCRJobsWithForwarder( bootstrapSpec := &client.OCRBootstrapJobSpec{ Name: fmt.Sprintf("bootstrap-%s", uuid.New().String()), ContractAddress: ocrInstance.Address(), - EVMChainID: evmChainID, + EVMChainID: fmt.Sprint(evmChainID), P2PPeerID: bootstrapP2PId, IsBootstrapPeer: true, } @@ -307,7 +308,7 @@ func CreateOCRJobsWithForwarder( bootstrapPeers := []*client.ChainlinkClient{bootstrapNode.ChainlinkClient} ocrSpec := &client.OCRTaskJobSpec{ ContractAddress: ocrInstance.Address(), - EVMChainID: evmChainID, + EVMChainID: fmt.Sprint(evmChainID), P2PPeerID: nodeP2PId, P2PBootstrapPeers: bootstrapPeers, KeyBundleID: nodeOCRKeyId, @@ -322,6 +323,7 @@ func CreateOCRJobsWithForwarder( } // StartNewRound requests a new round from the ocr contracts and waits for confirmation +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.StartNewRound func StartNewRound( roundNumber int64, ocrInstances []contracts.OffchainAggregator, @@ -345,6 +347,7 @@ func StartNewRound( // WatchNewRound watches for a new OCR round, similarly to StartNewRound, but it does not explicitly request a new // round from the contract, as this can cause some odd behavior in some cases +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.WatchNewRound func WatchNewRound( roundNumber int64, ocrInstances []contracts.OffchainAggregator, @@ -430,7 +433,7 @@ func SetAllAdapterResponsesToDifferentValues( } // BuildNodeContractPairID builds a UUID based on a related pair of a Chainlink node and OCR contract -func BuildNodeContractPairID(node *client.ChainlinkK8sClient, ocrInstance contracts.OffchainAggregator) (string, error) { +func BuildNodeContractPairID(node contracts.ChainlinkNodeWithKeysAndAddress, ocrInstance contracts.OffchainAggregator) (string, error) { if node == nil { return "", fmt.Errorf("chainlink node is nil") } diff --git a/integration-tests/actions/ocr_helpers_local.go b/integration-tests/actions/ocr_helpers_local.go index e9cad3f67ea..1a7371517a4 100644 --- a/integration-tests/actions/ocr_helpers_local.go +++ b/integration-tests/actions/ocr_helpers_local.go @@ -118,10 +118,11 @@ func DeployOCRContractsLocal( if err != nil { return nil, fmt.Errorf("getting node common addresses should not fail: %w", err) } + for _, ocrInstance := range ocrInstances { // Exclude the first node, which will be used as a bootstrapper - err = ocrInstance.SetConfigLocal( - workerNodes, + err = ocrInstance.SetConfig( + contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(workerNodes), contracts.DefaultOffChainAggregatorConfig(len(workerNodes)), transmitterAddresses, ) @@ -182,7 +183,7 @@ func CreateOCRJobsLocal( } nodeOCRKeyId := nodeOCRKeys.Data[0].ID - nodeContractPairID, err := BuildNodeContractPairIDLocal(node, ocrInstance) + nodeContractPairID, err := BuildNodeContractPairID(node, ocrInstance) if err != nil { return err } @@ -218,29 +219,13 @@ func CreateOCRJobsLocal( return nil } -func BuildNodeContractPairIDLocal(node *client.ChainlinkClient, ocrInstance contracts.OffchainAggregator) (string, error) { - if node == nil { - return "", fmt.Errorf("chainlink node is nil") - } - if ocrInstance == nil { - return "", fmt.Errorf("OCR Instance is nil") - } - nodeAddress, err := node.PrimaryEthAddress() - if err != nil { - return "", fmt.Errorf("getting chainlink node's primary ETH address failed: %w", err) - } - shortNodeAddr := nodeAddress[2:12] - shortOCRAddr := ocrInstance.Address()[2:12] - return strings.ToLower(fmt.Sprintf("node_%s_contract_%s", shortNodeAddr, shortOCRAddr)), nil -} - func SetAdapterResponseLocal( response int, ocrInstance contracts.OffchainAggregator, chainlinkNode *client.ChainlinkClient, mockAdapter *test_env.Killgrave, ) error { - nodeContractPairID, err := BuildNodeContractPairIDLocal(chainlinkNode, ocrInstance) + nodeContractPairID, err := BuildNodeContractPairID(chainlinkNode, ocrInstance) if err != nil { return err } @@ -342,8 +327,8 @@ func DeployOCRContractsForwarderFlowLocal( // Set Config for _, ocrInstance := range ocrInstances { // Exclude the first node, which will be used as a bootstrapper - err := ocrInstance.SetConfigLocal( - workerNodes, + err := ocrInstance.SetConfig( + contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(workerNodes), contracts.DefaultOffChainAggregatorConfig(len(workerNodes)), forwarderAddresses, ) @@ -399,7 +384,7 @@ func CreateOCRJobsWithForwarderLocal( } nodeOCRKeyId := nodeOCRKeys.Data[0].ID - nodeContractPairID, err := BuildNodeContractPairIDLocal(node, ocrInstance) + nodeContractPairID, err := BuildNodeContractPairID(node, ocrInstance) if err != nil { return err } diff --git a/integration-tests/actions/operator_forwarder_helpers.go b/integration-tests/actions/operator_forwarder_helpers.go index e308cd8fd5b..31eed7b7aae 100644 --- a/integration-tests/actions/operator_forwarder_helpers.go +++ b/integration-tests/actions/operator_forwarder_helpers.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.DeployForwarderContracts func DeployForwarderContracts( t *testing.T, contractDeployer contracts.ContractDeployer, @@ -49,6 +50,7 @@ func DeployForwarderContracts( return operators, authorizedForwarders, operatorFactoryInstance } +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.AcceptAuthorizedReceiversOperator func AcceptAuthorizedReceiversOperator( t *testing.T, operator common.Address, @@ -178,6 +180,7 @@ func SubscribeOperatorFactoryEvents( }() } +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.TrackForwarder func TrackForwarder( t *testing.T, chainClient blockchain.EVMClient, diff --git a/integration-tests/actions/seth/actions.go b/integration-tests/actions/seth/actions.go new file mode 100644 index 00000000000..02cc2802517 --- /dev/null +++ b/integration-tests/actions/seth/actions.go @@ -0,0 +1,528 @@ +package actions_seth + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "github.com/smartcontractkit/seth" + "github.com/test-go/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/testreporters" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_factory" + + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" +) + +var ContractDeploymentInterval = 200 + +// FundChainlinkNodesFromRootAddress sends native token amount (expressed in human-scale) to each Chainlink Node +// from root private key. It returns an error if any of the transactions failed. +func FundChainlinkNodesFromRootAddress( + logger zerolog.Logger, + client *seth.Client, + nodes []contracts.ChainlinkNodeWithKeysAndAddress, + amount *big.Float, +) error { + if len(client.PrivateKeys) == 0 { + return errors.Wrap(errors.New(seth.ErrNoKeyLoaded), fmt.Sprintf("requested key: %d", 0)) + } + + return FundChainlinkNodes(logger, client, nodes, client.PrivateKeys[0], amount) +} + +// FundChainlinkNodes sends native token amount (expressed in human-scale) to each Chainlink Node +// from private key's address. It returns an error if any of the transactions failed. +func FundChainlinkNodes( + logger zerolog.Logger, + client *seth.Client, + nodes []contracts.ChainlinkNodeWithKeysAndAddress, + privateKey *ecdsa.PrivateKey, + amount *big.Float, +) error { + refundErrors := []error{} + for _, cl := range nodes { + toAddress, err := cl.PrimaryEthAddress() + if err != nil { + return err + } + + err = SendFunds(logger, client, FundsToSendPayload{ + ToAddress: common.HexToAddress(toAddress), + Amount: conversions.EtherToWei(amount), + PrivateKey: privateKey, + }) + if err != nil { + refundErrors = append(refundErrors, err) + } + } + + if len(refundErrors) > 0 { + var wrapped error + for _, e := range refundErrors { + wrapped = errors.Wrapf(e, ", ") + } + return fmt.Errorf("failed to fund chainlink nodes due to following errors: %w", wrapped) + } + + return nil +} + +type FundsToSendPayload struct { + ToAddress common.Address + Amount *big.Int + PrivateKey *ecdsa.PrivateKey + GasLimit *uint64 +} + +// TODO: move to CTF? +// SendFunds sends native token amount (expressed in human-scale) from address controlled by private key +// to given address. If no gas limit is set, then network's default will be used. +func SendFunds(logger zerolog.Logger, client *seth.Client, payload FundsToSendPayload) error { + ctx, cancel := context.WithTimeout(context.Background(), client.Cfg.Network.TxnTimeout.Duration()) + + publicKey := payload.PrivateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return errors.New("error casting public key to ECDSA") + } + fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) + nonce, err := client.Client.PendingNonceAt(ctx, fromAddress) + defer cancel() + if err != nil { + return err + } + + gasLimit := uint64(client.Cfg.Network.GasLimit) + if payload.GasLimit != nil { + gasLimit = *payload.GasLimit + } + + rawTx := &types.LegacyTx{ + Nonce: nonce, + To: &payload.ToAddress, + Value: payload.Amount, + Gas: gasLimit, + GasPrice: big.NewInt(client.Cfg.Network.GasPrice), + } + signedTx, err := types.SignNewTx(payload.PrivateKey, types.NewEIP155Signer(big.NewInt(client.ChainID)), rawTx) + if err != nil { + return errors.Wrap(err, "failed to sign tx") + } + + ctx, cancel = context.WithTimeout(ctx, client.Cfg.Network.TxnTimeout.Duration()) + defer cancel() + err = client.Client.SendTransaction(ctx, signedTx) + if err != nil { + return errors.Wrap(err, "failed to send transaction") + } + _, err = client.WaitMined(ctx, logger, client.Client, signedTx) + return err +} + +// DeployForwarderContracts first deploys Operator Factory and then uses it to deploy given number of +// operator and forwarder pairs. It waits for each transaction to be mined and then extracts operator and +// forwarder addresses from emitted events. +func DeployForwarderContracts( + t *testing.T, + seth *seth.Client, + linkTokenData seth.DeploymentData, + numberOfOperatorForwarderPairs int, +) (operators []common.Address, authorizedForwarders []common.Address, operatorFactoryInstance contracts.OperatorFactory) { + instance, err := contracts.DeployEthereumOperatorFactory(seth, linkTokenData.Address) + require.NoError(t, err, "failed to create new instance of operator factory") + operatorFactoryInstance = &instance + + for i := 0; i < numberOfOperatorForwarderPairs; i++ { + decodedTx, err := seth.Decode(operatorFactoryInstance.DeployNewOperatorAndForwarder()) + require.NoError(t, err, "Deploying new operator with proposed ownership with forwarder shouldn't fail") + + for i, event := range decodedTx.Events { + require.True(t, len(event.Topics) > 0, fmt.Sprintf("Event %d should have topics", i)) + switch event.Topics[0] { + case operator_factory.OperatorFactoryOperatorCreated{}.Topic().String(): + if address, ok := event.EventData["operator"]; ok { + operators = append(operators, address.(common.Address)) + } else { + require.Fail(t, "Operator address not found in event", event) + } + case operator_factory.OperatorFactoryAuthorizedForwarderCreated{}.Topic().String(): + if address, ok := event.EventData["forwarder"]; ok { + authorizedForwarders = append(authorizedForwarders, address.(common.Address)) + } else { + require.Fail(t, "Forwarder address not found in event", event) + } + } + } + } + return operators, authorizedForwarders, operatorFactoryInstance +} + +// WatchNewRound watches for a new OCR round, similarly to StartNewRound, but it does not explicitly request a new +// round from the contract, as this can cause some odd behavior in some cases. It announces success if latest round +// is >= roundNumber. +func WatchNewRound( + l zerolog.Logger, + seth *seth.Client, + roundNumber int64, + ocrInstances []contracts.OffChainAggregatorWithRounds, + timeout time.Duration, +) error { + confirmed := make(map[string]bool) + timeoutC := time.After(timeout) + ticker := time.NewTicker(time.Millisecond * 200) + defer ticker.Stop() + + l.Info().Msgf("Waiting for round %d to be confirmed by all nodes", roundNumber) + + for { + select { + case <-timeoutC: + return fmt.Errorf("timeout waiting for round %d to be confirmed. %d/%d nodes confirmed it", roundNumber, len(confirmed), len(ocrInstances)) + case <-ticker.C: + for i := 0; i < len(ocrInstances); i++ { + if confirmed[ocrInstances[i].Address()] { + continue + } + ctx, cancel := context.WithTimeout(context.Background(), seth.Cfg.Network.TxnTimeout.Duration()) + roundData, err := ocrInstances[i].GetLatestRound(ctx) + if err != nil { + cancel() + return fmt.Errorf("getting latest round from OCR instance %d have failed: %w", i+1, err) + } + cancel() + if roundData.RoundId.Cmp(big.NewInt(roundNumber)) >= 0 { + l.Debug().Msgf("OCR instance %d/%d confirmed round %d", i+1, len(ocrInstances), roundNumber) + confirmed[ocrInstances[i].Address()] = true + } + } + if len(confirmed) == len(ocrInstances) { + return nil + } + } + } +} + +// AcceptAuthorizedReceiversOperator sets authorized receivers for each operator contract to +// authorizedForwarder and authorized EA to nodeAddresses. Once done, it confirms that authorizations +// were set correctly. +func AcceptAuthorizedReceiversOperator( + t *testing.T, + logger zerolog.Logger, + seth *seth.Client, + operator common.Address, + authorizedForwarder common.Address, + nodeAddresses []common.Address, +) { + operatorInstance, err := contracts.LoadEthereumOperator(logger, seth, operator) + require.NoError(t, err, "Loading operator contract shouldn't fail") + forwarderInstance, err := contracts.LoadEthereumAuthorizedForwarder(seth, authorizedForwarder) + require.NoError(t, err, "Loading authorized forwarder contract shouldn't fail") + + err = operatorInstance.AcceptAuthorizedReceivers([]common.Address{authorizedForwarder}, nodeAddresses) + require.NoError(t, err, "Accepting authorized forwarder shouldn't fail") + + senders, err := forwarderInstance.GetAuthorizedSenders(testcontext.Get(t)) + require.NoError(t, err, "Getting authorized senders shouldn't fail") + var nodesAddrs []string + for _, o := range nodeAddresses { + nodesAddrs = append(nodesAddrs, o.Hex()) + } + require.Equal(t, nodesAddrs, senders, "Senders addresses should match node addresses") + + owner, err := forwarderInstance.Owner(testcontext.Get(t)) + require.NoError(t, err, "Getting authorized forwarder owner shouldn't fail") + require.Equal(t, operator.Hex(), owner, "Forwarder owner should match operator") +} + +// TrackForwarder creates forwarder track for a given Chainlink node +func TrackForwarder( + t *testing.T, + seth *seth.Client, + authorizedForwarder common.Address, + node *client.ChainlinkK8sClient, +) { + l := logging.GetTestLogger(t) + chainID := big.NewInt(seth.ChainID) + _, _, err := node.TrackForwarder(chainID, authorizedForwarder) + require.NoError(t, err, "Forwarder track should be created") + l.Info().Str("NodeURL", node.Config.URL). + Str("ForwarderAddress", authorizedForwarder.Hex()). + Str("ChaindID", chainID.String()). + Msg("Forwarder tracked") +} + +// DeployOCRv2Contracts deploys a number of OCRv2 contracts and configures them with defaults +func DeployOCRv2Contracts( + l zerolog.Logger, + seth *seth.Client, + numberOfContracts int, + linkTokenAddress common.Address, + transmitters []string, + ocrOptions contracts.OffchainOptions, +) ([]contracts.OffchainAggregatorV2, error) { + var ocrInstances []contracts.OffchainAggregatorV2 + for contractCount := 0; contractCount < numberOfContracts; contractCount++ { + ocrInstance, err := contracts.DeployOffchainAggregatorV2( + l, + seth, + linkTokenAddress, + ocrOptions, + ) + if err != nil { + return nil, fmt.Errorf("OCRv2 instance deployment have failed: %w", err) + } + ocrInstances = append(ocrInstances, &ocrInstance) + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } + } + + // Gather address payees + var payees []string + for range transmitters { + payees = append(payees, seth.Addresses[0].Hex()) + } + + // Set Payees + for contractCount, ocrInstance := range ocrInstances { + err := ocrInstance.SetPayees(transmitters, payees) + if err != nil { + return nil, fmt.Errorf("error settings OCR payees: %w", err) + } + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } + } + return ocrInstances, nil +} + +// ConfigureOCRv2AggregatorContracts sets configuration for a number of OCRv2 contracts +func ConfigureOCRv2AggregatorContracts( + contractConfig *contracts.OCRv2Config, + ocrv2Contracts []contracts.OffchainAggregatorV2, +) error { + for contractCount, ocrInstance := range ocrv2Contracts { + // Exclude the first node, which will be used as a bootstrapper + err := ocrInstance.SetConfig(contractConfig) + if err != nil { + return fmt.Errorf("error setting OCR config for contract '%s': %w", ocrInstance.Address(), err) + } + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } + } + return nil +} + +// TeardownRemoteSuite sends a report and returns funds from chainlink nodes to network's default wallet +func TeardownRemoteSuite( + t *testing.T, + client *seth.Client, + namespace string, + chainlinkNodes []*client.ChainlinkK8sClient, + optionalTestReporter testreporters.TestReporter, // Optionally pass in a test reporter to log further metrics + grafnaUrlProvider testreporters.GrafanaURLProvider, +) error { + l := logging.GetTestLogger(t) + if err := testreporters.SendReport(t, namespace, "./", optionalTestReporter, grafnaUrlProvider); err != nil { + l.Warn().Err(err).Msg("Error writing test report") + } + // Delete all jobs to stop depleting the funds + err := DeleteAllJobs(chainlinkNodes) + if err != nil { + l.Warn().Msgf("Error deleting jobs %+v", err) + } + + if err = ReturnFunds(l, client, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(chainlinkNodes)); err != nil { + l.Error().Err(err).Str("Namespace", namespace). + Msg("Error attempting to return funds from chainlink nodes to network's default wallet. " + + "Environment is left running so you can try manually!") + } + return err +} + +// DeleteAllJobs deletes all jobs from all chainlink nodes +// added here temporarily to avoid circular import +func DeleteAllJobs(chainlinkNodes []*client.ChainlinkK8sClient) error { + for _, node := range chainlinkNodes { + if node == nil { + return fmt.Errorf("found a nil chainlink node in the list of chainlink nodes while tearing down: %v", chainlinkNodes) + } + jobs, _, err := node.ReadJobs() + if err != nil { + return fmt.Errorf("error reading jobs from chainlink node, err: %w", err) + } + for _, maps := range jobs.Data { + if _, ok := maps["id"]; !ok { + return fmt.Errorf("error reading job id from chainlink node's jobs %+v", jobs.Data) + } + id := maps["id"].(string) + _, err := node.DeleteJob(id) + if err != nil { + return fmt.Errorf("error deleting job from chainlink node, err: %w", err) + } + } + } + return nil +} + +// StartNewRound requests a new round from the ocr contracts and returns once transaction was mined +func StartNewRound( + ocrInstances []contracts.OffChainAggregatorWithRounds, +) error { + for i := 0; i < len(ocrInstances); i++ { + err := ocrInstances[i].RequestNewRound() + if err != nil { + return fmt.Errorf("requesting new OCR round %d have failed: %w", i+1, err) + } + } + return nil +} + +// DeployOCRContractsForwarderFlow deploys and funds a certain number of offchain +// aggregator contracts with forwarders as effectiveTransmitters +func DeployOCRContractsForwarderFlow( + logger zerolog.Logger, + seth *seth.Client, + numberOfContracts int, + linkTokenContractAddress common.Address, + workerNodes []contracts.ChainlinkNodeWithKeysAndAddress, + forwarderAddresses []common.Address, +) ([]contracts.OffchainAggregator, error) { + transmitterPayeesFn := func() (transmitters []string, payees []string, err error) { + transmitters = make([]string, 0) + payees = make([]string, 0) + for _, forwarderCommonAddress := range forwarderAddresses { + forwarderAddress := forwarderCommonAddress.Hex() + transmitters = append(transmitters, forwarderAddress) + payees = append(payees, seth.Addresses[0].Hex()) + } + + return + } + + transmitterAddressesFn := func() ([]common.Address, error) { + return forwarderAddresses, nil + } + + return deployAnyOCRv1Contracts(logger, seth, numberOfContracts, linkTokenContractAddress, workerNodes, transmitterPayeesFn, transmitterAddressesFn) +} + +// DeployOCRv1Contracts deploys and funds a certain number of offchain aggregator contracts +func DeployOCRv1Contracts( + logger zerolog.Logger, + seth *seth.Client, + numberOfContracts int, + linkTokenContractAddress common.Address, + workerNodes []contracts.ChainlinkNodeWithKeysAndAddress, +) ([]contracts.OffchainAggregator, error) { + transmitterPayeesFn := func() (transmitters []string, payees []string, err error) { + transmitters = make([]string, 0) + payees = make([]string, 0) + for _, node := range workerNodes { + var addr string + addr, err = node.PrimaryEthAddress() + if err != nil { + err = fmt.Errorf("error getting node's primary ETH address: %w", err) + return + } + transmitters = append(transmitters, addr) + payees = append(payees, seth.Addresses[0].Hex()) + } + + return + } + + transmitterAddressesFn := func() ([]common.Address, error) { + transmitterAddresses := make([]common.Address, 0) + for _, node := range workerNodes { + primaryAddress, err := node.PrimaryEthAddress() + if err != nil { + return nil, err + } + transmitterAddresses = append(transmitterAddresses, common.HexToAddress(primaryAddress)) + } + + return transmitterAddresses, nil + } + + return deployAnyOCRv1Contracts(logger, seth, numberOfContracts, linkTokenContractAddress, workerNodes, transmitterPayeesFn, transmitterAddressesFn) +} + +func deployAnyOCRv1Contracts( + logger zerolog.Logger, + seth *seth.Client, + numberOfContracts int, + linkTokenContractAddress common.Address, + workerNodes []contracts.ChainlinkNodeWithKeysAndAddress, + getTransmitterAndPayeesFn func() ([]string, []string, error), + getTransmitterAddressesFn func() ([]common.Address, error), +) ([]contracts.OffchainAggregator, error) { + // Deploy contracts + var ocrInstances []contracts.OffchainAggregator + for contractCount := 0; contractCount < numberOfContracts; contractCount++ { + ocrInstance, err := contracts.DeployOffchainAggregator(logger, seth, linkTokenContractAddress, contracts.DefaultOffChainAggregatorOptions()) + if err != nil { + return nil, fmt.Errorf("OCR instance deployment have failed: %w", err) + } + ocrInstances = append(ocrInstances, &ocrInstance) + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } + } + + // Gather transmitter and address payees + var transmitters, payees []string + var err error + transmitters, payees, err = getTransmitterAndPayeesFn() + if err != nil { + return nil, fmt.Errorf("error getting transmitter and payees: %w", err) + } + + // Set Payees + for contractCount, ocrInstance := range ocrInstances { + err := ocrInstance.SetPayees(transmitters, payees) + if err != nil { + return nil, fmt.Errorf("error settings OCR payees: %w", err) + } + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } + } + + // Set Config + transmitterAddresses, err := getTransmitterAddressesFn() + if err != nil { + return nil, fmt.Errorf("getting transmitter addresses should not fail: %w", err) + } + + for contractCount, ocrInstance := range ocrInstances { + // Exclude the first node, which will be used as a bootstrapper + err = ocrInstance.SetConfig( + workerNodes, + contracts.DefaultOffChainAggregatorConfig(len(workerNodes)), + transmitterAddresses, + ) + if err != nil { + return nil, fmt.Errorf("error setting OCR config for contract '%s': %w", ocrInstance.Address(), err) + } + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } + } + + return ocrInstances, nil +} diff --git a/integration-tests/actions/seth/refund.go b/integration-tests/actions/seth/refund.go new file mode 100644 index 00000000000..e2bfcc143c8 --- /dev/null +++ b/integration-tests/actions/seth/refund.go @@ -0,0 +1,287 @@ +package actions_seth + +import ( + "context" + "crypto/ecdsa" + "encoding/json" + "fmt" + "math/big" + "regexp" + "strconv" + "strings" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "github.com/smartcontractkit/seth" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" +) + +const ( + InsufficientFundsErr = "insufficient funds" + GasTooLowErr = "gas too low" + OvershotErr = "overshot" +) + +var ( + RetrySuccessfulMsg = "Retry successful" + NotSupportedMsg = "Error not supported. Passing to next retrier" +) + +// TransactionRetrier is an interface that every retrier of failed funds transfer transaction needs to implement +type TransactionRetrier interface { + Retry(ctx context.Context, logger zerolog.Logger, client *seth.Client, txErr error, payload FundsToSendPayload, currentAttempt int) error +} + +// InsufficientFundTransferRetrier will retry a failed funds transfer transaction if the error is due to insufficient funds +// by subtracting 1 Gwei from amount to send and retrying it up to maxRetries times +type InsufficientFundTransferRetrier struct { + nextRetrier TransactionRetrier + maxRetries int +} + +func (r *InsufficientFundTransferRetrier) Retry(ctx context.Context, logger zerolog.Logger, client *seth.Client, txErr error, payload FundsToSendPayload, currentAttempt int) error { + if currentAttempt >= r.maxRetries { + if r.nextRetrier != nil { + logger.Debug(). + Str("retier", "InsufficientFundTransferRetrier"). + Msg("Max gas limit reached. Passing to next retrier") + return r.nextRetrier.Retry(ctx, logger, client, txErr, payload, 0) + } + return txErr + } + + for txErr != nil && (strings.Contains(txErr.Error(), InsufficientFundsErr)) { + logger.Info(). + Msg("Insufficient funds error detected, retrying with less funds") + + newAmount := big.NewInt(0).Sub(payload.Amount, big.NewInt(blockchain.GWei)) + + logger.Debug(). + Str("retier", "InsufficientFundTransferRetrier"). + Str("old amount", payload.Amount.String()). + Str("new amount", newAmount.String()). + Str("diff", big.NewInt(0).Sub(payload.Amount, newAmount).String()). + Msg("New amount to send") + + payload.Amount = newAmount + + retryErr := SendFunds(logger, client, payload) + if retryErr == nil { + logger.Info(). + Str("retier", "InsufficientFundTransferRetrier"). + Msg(RetrySuccessfulMsg) + return nil + } + + if strings.Contains(retryErr.Error(), InsufficientFundsErr) { + return r.Retry(ctx, logger, client, retryErr, payload, currentAttempt+1) + } + } + + if r.nextRetrier != nil { + logger.Debug(). + Str("retier", "InsufficientFundTransferRetrier"). + Msg(NotSupportedMsg) + return r.nextRetrier.Retry(ctx, logger, client, txErr, payload, 0) + } + + logger.Warn(). + Str("retier", "InsufficientFundTransferRetrier"). + Msg("No more retriers available. Unable to retry transaction. Returning error.") + + return txErr +} + +// GasTooLowTransferRetrier will retry a failed funds transfer transaction if the error is due to gas too low +// by doubling the gas limit and retrying until reaching maxGasLimit +type GasTooLowTransferRetrier struct { + nextRetrier TransactionRetrier + maxGasLimit uint64 +} + +func (r *GasTooLowTransferRetrier) Retry(ctx context.Context, logger zerolog.Logger, client *seth.Client, txErr error, payload FundsToSendPayload, currentAttempt int) error { + if payload.GasLimit != nil && *payload.GasLimit >= r.maxGasLimit { + if r.nextRetrier != nil { + logger.Debug(). + Str("retier", "GasTooLowTransferRetrier"). + Msg("Max gas limit reached. Passing to next retrier") + return r.nextRetrier.Retry(ctx, logger, client, txErr, payload, 0) + } + return txErr + } + + for txErr != nil && strings.Contains(txErr.Error(), GasTooLowErr) { + logger.Info(). + Msg("Too low gas error detected, retrying with more gas") + var newGasLimit uint64 + if payload.GasLimit != nil { + newGasLimit = *payload.GasLimit * 2 + } else { + newGasLimit = uint64(client.Cfg.Network.TransferGasFee) * 2 + } + + logger.Debug(). + Str("retier", "GasTooLowTransferRetrier"). + Uint64("old gas limit", newGasLimit/2). + Uint64("new gas limit", newGasLimit). + Uint64("diff", newGasLimit). + Msg("New gas limit to use") + + payload.GasLimit = &newGasLimit + + retryErr := SendFunds(logger, client, payload) + if retryErr == nil { + logger.Info(). + Str("retier", "GasTooLowTransferRetrier"). + Msg(RetrySuccessfulMsg) + return nil + } + + if strings.Contains(retryErr.Error(), GasTooLowErr) { + return r.Retry(ctx, logger, client, retryErr, payload, currentAttempt+1) + } + } + + if r.nextRetrier != nil { + logger.Debug(). + Str("retier", "OvershotTransferRetrier"). + Msg(NotSupportedMsg) + return r.nextRetrier.Retry(ctx, logger, client, txErr, payload, 0) + } + + logger.Warn(). + Str("retier", "OvershotTransferRetrier"). + Msg("No more retriers available. Unable to retry transaction. Returning error.") + + return txErr +} + +// OvershotTransferRetrier will retry a failed funds transfer transaction if the error is due to overshot +// by subtracting the overshot amount from the amount to send and retrying it up to maxRetries times +type OvershotTransferRetrier struct { + nextRetrier TransactionRetrier + maxRetries int +} + +func (r *OvershotTransferRetrier) Retry(ctx context.Context, logger zerolog.Logger, client *seth.Client, txErr error, payload FundsToSendPayload, currentAttempt int) error { + if currentAttempt >= r.maxRetries { + logger.Debug(). + Str("retier", "OvershotTransferRetrier"). + Msg("Max retries reached. Passing to next retrier") + if r.nextRetrier != nil { + return r.nextRetrier.Retry(ctx, logger, client, txErr, payload, 0) + } + return txErr + } + + overshotRe := regexp.MustCompile(`overshot (\d+)`) + if txErr != nil && strings.Contains(txErr.Error(), OvershotErr) { + logger.Info(). + Msg("Overshot error detected, retrying with less funds") + submatches := overshotRe.FindStringSubmatch(txErr.Error()) + if len(submatches) < 1 { + return fmt.Errorf("error parsing overshot amount in error: %w", txErr) + } + numberString := submatches[1] + overshotAmount, err := strconv.Atoi(numberString) + if err != nil { + return err + } + + newAmount := big.NewInt(0).Sub(payload.Amount, big.NewInt(int64(overshotAmount))) + logger.Debug(). + Str("retier", "OvershotTransferRetrier"). + Str("old amount", payload.Amount.String()). + Str("new amount", newAmount.String()). + Str("diff", big.NewInt(0).Sub(payload.Amount, newAmount).String()). + Msg("New amount to send") + + payload.Amount = newAmount + + retryErr := SendFunds(logger, client, payload) + if retryErr == nil { + logger.Info(). + Str("retier", "OvershotTransferRetrier"). + Msg(RetrySuccessfulMsg) + return nil + } + + if strings.Contains(retryErr.Error(), OvershotErr) { + return r.Retry(ctx, logger, client, retryErr, payload, currentAttempt+1) + } + } + + if r.nextRetrier != nil { + logger.Debug(). + Str("retier", "OvershotTransferRetrier"). + Msg(NotSupportedMsg) + return r.nextRetrier.Retry(ctx, logger, client, txErr, payload, 0) + } + + return txErr +} + +// ReturnFunds returns funds from the given chainlink nodes to the default network wallet. It will use a variety +// of strategies to attempt to return funds, including retrying with less funds if the transaction fails due to +// insufficient funds, and retrying with a higher gas limit if the transaction fails due to gas too low. +func ReturnFunds(log zerolog.Logger, seth *seth.Client, chainlinkNodes []contracts.ChainlinkNodeWithKeysAndAddress) error { + if seth == nil { + return fmt.Errorf("Seth client is nil, unable to return funds from chainlink nodes") + } + log.Info().Msg("Attempting to return Chainlink node funds to default network wallets") + if seth.Cfg.IsSimulatedNetwork() { + log.Info().Str("Network Name", seth.Cfg.Network.Name). + Msg("Network is a simulated network. Skipping fund return.") + return nil + } + + for _, chainlinkNode := range chainlinkNodes { + fundedKeys, err := chainlinkNode.ExportEVMKeysForChain(fmt.Sprint(seth.ChainID)) + if err != nil { + return err + } + for _, key := range fundedKeys { + keyToDecrypt, err := json.Marshal(key) + if err != nil { + return err + } + // This can take up a good bit of RAM and time. When running on the remote-test-runner, this can lead to OOM + // issues. So we avoid running in parallel; slower, but safer. + decryptedKey, err := keystore.DecryptKey(keyToDecrypt, client.ChainlinkKeyPassword) + if err != nil { + return err + } + + publicKey := decryptedKey.PrivateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return errors.New("error casting public key to ECDSA") + } + fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) + + balance, err := seth.Client.BalanceAt(context.Background(), fromAddress, nil) + if err != nil { + return err + } + + totalGasCost := new(big.Int).Mul(big.NewInt(0).SetUint64(seth.Cfg.Network.GasLimit), big.NewInt(0).SetInt64(seth.Cfg.Network.GasPrice)) + toSend := new(big.Int).Sub(balance, totalGasCost) + + payload := FundsToSendPayload{ToAddress: seth.Addresses[0], Amount: toSend, PrivateKey: decryptedKey.PrivateKey} + + err = SendFunds(log, seth, payload) + if err != nil { + handler := OvershotTransferRetrier{maxRetries: 3, nextRetrier: &InsufficientFundTransferRetrier{maxRetries: 3, nextRetrier: &GasTooLowTransferRetrier{maxGasLimit: seth.Cfg.Network.GasLimit * 3}}} + return handler.Retry(context.Background(), log, seth, err, payload, 0) + } + } + } + + return nil +} diff --git a/integration-tests/chaos/ocr_chaos_test.go b/integration-tests/chaos/ocr_chaos_test.go index 97f7c67d1b9..d667e32da34 100644 --- a/integration-tests/chaos/ocr_chaos_test.go +++ b/integration-tests/chaos/ocr_chaos_test.go @@ -6,10 +6,9 @@ import ( "testing" "github.com/onsi/gomega" + "github.com/smartcontractkit/seth" "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" @@ -22,10 +21,12 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) @@ -60,9 +61,7 @@ func TestOCRChaos(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) config, err := tc.GetConfig("Chaos", tc.OCR) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err, "Error getting config") var overrideFn = func(_ interface{}, target interface{}) { ctf_config.MustConfigOverrideChainlinkVersion(config.ChainlinkImage, target) @@ -164,39 +163,37 @@ func TestOCRChaos(t *testing.T) { err = testEnvironment.Client.LabelChaosGroup(testEnvironment.Cfg.Namespace, "instance=node-", 2, 5, ChaosGroupMajorityPlus) require.NoError(t, err) - chainClient, err := blockchain.NewEVMClient(blockchain.SimulatedEVMNetwork, testEnvironment, l) - require.NoError(t, err, "Connecting to blockchain nodes shouldn't fail") - cd, err := contracts.NewContractDeployer(chainClient, l) - require.NoError(t, err, "Deploying contracts shouldn't fail") + cfg := config.MustCopy().(tc.TestConfig) + readSethCfg := cfg.GetSethConfig() + require.NotNil(t, readSethCfg, "Seth config shouldn't be nil") + + network := networks.MustGetSelectedNetworkConfig(cfg.GetNetworkConfig())[0] + network = utils.MustReplaceSimulatedNetworkUrlWithK8(l, network, *testEnvironment) + + sethCfg := utils.MergeSethAndEvmNetworkConfigs(l, network, *readSethCfg) + seth, err := seth.NewClientWithConfig(&sethCfg) + require.NoError(t, err, "Error creating seth client") chainlinkNodes, err := client.ConnectChainlinkNodes(testEnvironment) require.NoError(t, err, "Connecting to chainlink nodes shouldn't fail") bootstrapNode, workerNodes := chainlinkNodes[0], chainlinkNodes[1:] t.Cleanup(func() { - if chainClient != nil { - chainClient.GasStats().PrintStats() - } - err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, &config, chainClient) + err := actions_seth.TeardownRemoteSuite(t, seth, testEnvironment.Cfg.Namespace, chainlinkNodes, nil, &cfg) require.NoError(t, err, "Error tearing down environment") }) ms, err := ctfClient.ConnectMockServer(testEnvironment) require.NoError(t, err, "Creating mockserver clients shouldn't fail") - chainClient.ParallelTransactions(true) - require.NoError(t, err) - - lt, err := cd.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + linkDeploymentData, err := contracts.DeployLinkTokenContract(seth) + require.NoError(t, err, "Error deploying link token contract") - err = actions.FundChainlinkNodes(chainlinkNodes, chainClient, big.NewFloat(10)) + err = actions_seth.FundChainlinkNodesFromRootAddress(l, seth, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(chainlinkNodes), big.NewFloat(10)) require.NoError(t, err) - ocrInstances, err := actions.DeployOCRContracts(1, lt, cd, workerNodes, chainClient) - require.NoError(t, err) - err = chainClient.WaitForEvents() + ocrInstances, err := actions_seth.DeployOCRv1Contracts(l, seth, 1, linkDeploymentData.Address, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes)) require.NoError(t, err) - err = actions.CreateOCRJobs(ocrInstances, bootstrapNode, workerNodes, 5, ms, chainClient.GetChainID().String()) + err = actions.CreateOCRJobs(ocrInstances, bootstrapNode, workerNodes, 5, ms, fmt.Sprint(seth.ChainID)) require.NoError(t, err) chaosApplied := false diff --git a/integration-tests/client/chainlink.go b/integration-tests/client/chainlink.go index 56670812fbc..1de41ab9b5d 100644 --- a/integration-tests/client/chainlink.go +++ b/integration-tests/client/chainlink.go @@ -84,6 +84,7 @@ func initRestyClient(url string, email string, password string, timeout *time.Du return nil, fmt.Errorf("error connecting to chainlink node after %d attempts: %w", retryCount, err) } rc.SetCookies(resp.Cookies()) + log.Debug().Str("URL", url).Msg("Connected to Chainlink node") return rc, nil } diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index fead418fb96..78ec0c18c64 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -637,7 +637,7 @@ func (e *EthereumContractDeployer) DeployOffChainAggregator( if err != nil { return nil, err } - return &EthereumOffchainAggregator{ + return &LegacyEthereumOffchainAggregator{ client: e.client, ocr: instance.(*offchainaggregator.OffchainAggregator), address: address, @@ -656,7 +656,7 @@ func (e *EthereumContractDeployer) LoadOffChainAggregator(address *common.Addres if err != nil { return nil, err } - return &EthereumOffchainAggregator{ + return &LegacyEthereumOffchainAggregator{ address: address, client: e.client, ocr: instance.(*offchainaggregator.OffchainAggregator), @@ -1708,7 +1708,7 @@ func (e *EthereumContractDeployer) DeployOperatorFactory(linkAddr string) (Opera if err != nil { return nil, err } - return &EthereumOperatorFactory{ + return &LegacyEthereumOperatorFactory{ address: addr, client: e.client, operatorFactory: instance.(*operator_factory.OperatorFactory), @@ -1775,7 +1775,7 @@ func (e *EthereumContractDeployer) DeployOffchainAggregatorV2( if err != nil { return nil, err } - return &EthereumOffchainAggregatorV2{ + return &LegacyEthereumOffchainAggregatorV2{ client: e.client, contract: instance.(*ocr2aggregator.OCR2Aggregator), address: address, @@ -1794,7 +1794,7 @@ func (e *EthereumContractDeployer) LoadOffChainAggregatorV2(address *common.Addr if err != nil { return nil, err } - return &EthereumOffchainAggregatorV2{ + return &LegacyEthereumOffchainAggregatorV2{ client: e.client, contract: instance.(*ocr2aggregator.OCR2Aggregator), address: address, diff --git a/integration-tests/contracts/contract_loader.go b/integration-tests/contracts/contract_loader.go index 92526c7ddda..a2a4fb60be5 100644 --- a/integration-tests/contracts/contract_loader.go +++ b/integration-tests/contracts/contract_loader.go @@ -254,7 +254,7 @@ func (e *EthereumContractLoader) LoadOperatorContract(address common.Address) (O if err != nil { return nil, err } - return &EthereumOperator{ + return &LegacyEthereumOperator{ address: address, client: e.client, operator: instance.(*operator_wrapper.Operator), @@ -273,7 +273,7 @@ func (e *EthereumContractLoader) LoadAuthorizedForwarder(address common.Address) if err != nil { return nil, err } - return &EthereumAuthorizedForwarder{ + return &LegacyEthereumAuthorizedForwarder{ address: address, client: e.client, authorizedForwarder: instance.(*authorized_forwarder.AuthorizedForwarder), diff --git a/integration-tests/contracts/contract_models.go b/integration-tests/contracts/contract_models.go index 979a9b1d954..3aaf4f4f39d 100644 --- a/integration-tests/contracts/contract_models.go +++ b/integration-tests/contracts/contract_models.go @@ -134,12 +134,22 @@ type OffchainAggregatorData struct { LatestRoundData RoundData // Data about the latest round } +type ChainlinkNodeWithKeysAndAddress interface { + MustReadOCRKeys() (*client.OCRKeys, error) + MustReadP2PKeys() (*client.P2PKeys, error) + ExportEVMKeysForChain(string) ([]*client.ExportedEVMKey, error) + PrimaryEthAddress() (string, error) +} + +type OffChainAggregatorWithRounds interface { + Address() string + GetLatestRound(ctx context.Context) (*RoundData, error) + RequestNewRound() error +} + type OffchainAggregator interface { Address() string - Fund(nativeAmount *big.Float) error - GetContractData(ctx context.Context) (*OffchainAggregatorData, error) - SetConfig(chainlinkNodes []*client.ChainlinkK8sClient, ocrConfig OffChainAggregatorConfig, transmitters []common.Address) error - SetConfigLocal(chainlinkNodes []*client.ChainlinkClient, ocrConfig OffChainAggregatorConfig, transmitters []common.Address) error + SetConfig(chainlinkNodes []ChainlinkNodeWithKeysAndAddress, ocrConfig OffChainAggregatorConfig, transmitters []common.Address) error SetPayees([]string, []string) error RequestNewRound() error GetLatestAnswer(ctx context.Context) (*big.Int, error) @@ -151,10 +161,8 @@ type OffchainAggregator interface { type OffchainAggregatorV2 interface { Address() string - Fund(nativeAmount *big.Float) error RequestNewRound() error SetConfig(ocrConfig *OCRv2Config) error - GetConfig(ctx context.Context) ([32]byte, uint32, error) SetPayees(transmitters, payees []string) error GetLatestAnswer(ctx context.Context) (*big.Int, error) GetLatestRound(ctx context.Context) (*RoundData, error) diff --git a/integration-tests/contracts/ethereum_contracts.go b/integration-tests/contracts/ethereum_contracts.go index a43e4bd2f6a..f78e64d82be 100644 --- a/integration-tests/contracts/ethereum_contracts.go +++ b/integration-tests/contracts/ethereum_contracts.go @@ -1289,45 +1289,17 @@ func (l *EthereumLinkToken) TransferAndCall(to string, amount *big.Int, data []b return tx, l.client.ProcessTransaction(tx) } -// EthereumOffchainAggregator represents the offchain aggregation contract -type EthereumOffchainAggregator struct { +// LegacyEthereumOffchainAggregator represents the offchain aggregation contract +// Deprecated: we are moving away from blockchain.EVMClient, use EthereumOffchainAggregator instead +type LegacyEthereumOffchainAggregator struct { client blockchain.EVMClient ocr *offchainaggregator.OffchainAggregator address *common.Address l zerolog.Logger } -// Fund sends specified currencies to the contract -func (o *EthereumOffchainAggregator) Fund(ethAmount *big.Float) error { - gasEstimates, err := o.client.EstimateGas(ethereum.CallMsg{ - To: o.address, - }) - if err != nil { - return err - } - return o.client.Fund(o.address.Hex(), ethAmount, gasEstimates) -} - -// GetContractData retrieves basic data for the offchain aggregator contract -func (o *EthereumOffchainAggregator) GetContractData(ctxt context.Context) (*OffchainAggregatorData, error) { - opts := &bind.CallOpts{ - From: common.HexToAddress(o.client.GetDefaultWallet().Address()), - Context: ctxt, - } - - lr, err := o.ocr.LatestRoundData(opts) - if err != nil { - return &OffchainAggregatorData{}, err - } - latestRound := RoundData(lr) - - return &OffchainAggregatorData{ - LatestRoundData: latestRound, - }, nil -} - // SetPayees sets wallets for the contract to pay out to? -func (o *EthereumOffchainAggregator) SetPayees( +func (o *LegacyEthereumOffchainAggregator) SetPayees( transmitters, payees []string, ) error { opts, err := o.client.TransactionOpts(o.client.GetDefaultWallet()) @@ -1356,8 +1328,8 @@ func (o *EthereumOffchainAggregator) SetPayees( } // SetConfig sets the payees and the offchain reporting protocol configuration -func (o *EthereumOffchainAggregator) SetConfig( - chainlinkNodes []*client.ChainlinkK8sClient, +func (o *LegacyEthereumOffchainAggregator) SetConfig( + chainlinkNodes []ChainlinkNodeWithKeysAndAddress, ocrConfig OffChainAggregatorConfig, transmitters []common.Address, ) error { @@ -1441,7 +1413,7 @@ func (o *EthereumOffchainAggregator) SetConfig( } // RequestNewRound requests the OCR contract to create a new round -func (o *EthereumOffchainAggregator) RequestNewRound() error { +func (o *LegacyEthereumOffchainAggregator) RequestNewRound() error { opts, err := o.client.TransactionOpts(o.client.GetDefaultWallet()) if err != nil { return err @@ -1456,7 +1428,7 @@ func (o *EthereumOffchainAggregator) RequestNewRound() error { } // GetLatestAnswer returns the latest answer from the OCR contract -func (o *EthereumOffchainAggregator) GetLatestAnswer(ctxt context.Context) (*big.Int, error) { +func (o *LegacyEthereumOffchainAggregator) GetLatestAnswer(ctxt context.Context) (*big.Int, error) { opts := &bind.CallOpts{ From: common.HexToAddress(o.client.GetDefaultWallet().Address()), Context: ctxt, @@ -1464,12 +1436,12 @@ func (o *EthereumOffchainAggregator) GetLatestAnswer(ctxt context.Context) (*big return o.ocr.LatestAnswer(opts) } -func (o *EthereumOffchainAggregator) Address() string { +func (o *LegacyEthereumOffchainAggregator) Address() string { return o.address.Hex() } // GetLatestRound returns data from the latest round -func (o *EthereumOffchainAggregator) GetLatestRound(ctx context.Context) (*RoundData, error) { +func (o *LegacyEthereumOffchainAggregator) GetLatestRound(ctx context.Context) (*RoundData, error) { opts := &bind.CallOpts{ From: common.HexToAddress(o.client.GetDefaultWallet().Address()), Context: ctx, @@ -1489,7 +1461,7 @@ func (o *EthereumOffchainAggregator) GetLatestRound(ctx context.Context) (*Round }, err } -func (o *EthereumOffchainAggregator) LatestRoundDataUpdatedAt() (*big.Int, error) { +func (o *LegacyEthereumOffchainAggregator) LatestRoundDataUpdatedAt() (*big.Int, error) { data, err := o.ocr.LatestRoundData(&bind.CallOpts{ From: common.HexToAddress(o.client.GetDefaultWallet().Address()), Context: context.Background(), @@ -1501,7 +1473,7 @@ func (o *EthereumOffchainAggregator) LatestRoundDataUpdatedAt() (*big.Int, error } // GetRound retrieves an OCR round by the round ID -func (o *EthereumOffchainAggregator) GetRound(ctx context.Context, roundID *big.Int) (*RoundData, error) { +func (o *LegacyEthereumOffchainAggregator) GetRound(ctx context.Context, roundID *big.Int) (*RoundData, error) { opts := &bind.CallOpts{ From: common.HexToAddress(o.client.GetDefaultWallet().Address()), Context: ctx, @@ -1521,7 +1493,7 @@ func (o *EthereumOffchainAggregator) GetRound(ctx context.Context, roundID *big. } // ParseEventAnswerUpdated parses the log for event AnswerUpdated -func (o *EthereumOffchainAggregator) ParseEventAnswerUpdated(eventLog types.Log) (*offchainaggregator.OffchainAggregatorAnswerUpdated, error) { +func (o *LegacyEthereumOffchainAggregator) ParseEventAnswerUpdated(eventLog types.Log) (*offchainaggregator.OffchainAggregatorAnswerUpdated, error) { return o.ocr.ParseAnswerUpdated(eventLog) } @@ -1808,26 +1780,27 @@ func (e *EthereumFlags) GetFlag(ctx context.Context, addr string) (bool, error) return flag, nil } -// EthereumOperatorFactory represents operator factory contract -type EthereumOperatorFactory struct { +// LegacyEthereumOperatorFactory represents operator factory contract +// Deprecated: we are moving away from blockchain.EVMClient, use EthereumOperatorFactory instead +type LegacyEthereumOperatorFactory struct { address *common.Address client blockchain.EVMClient operatorFactory *operator_factory.OperatorFactory } -func (e *EthereumOperatorFactory) ParseAuthorizedForwarderCreated(eventLog types.Log) (*operator_factory.OperatorFactoryAuthorizedForwarderCreated, error) { +func (e *LegacyEthereumOperatorFactory) ParseAuthorizedForwarderCreated(eventLog types.Log) (*operator_factory.OperatorFactoryAuthorizedForwarderCreated, error) { return e.operatorFactory.ParseAuthorizedForwarderCreated(eventLog) } -func (e *EthereumOperatorFactory) ParseOperatorCreated(eventLog types.Log) (*operator_factory.OperatorFactoryOperatorCreated, error) { +func (e *LegacyEthereumOperatorFactory) ParseOperatorCreated(eventLog types.Log) (*operator_factory.OperatorFactoryOperatorCreated, error) { return e.operatorFactory.ParseOperatorCreated(eventLog) } -func (e *EthereumOperatorFactory) Address() string { +func (e *LegacyEthereumOperatorFactory) Address() string { return e.address.Hex() } -func (e *EthereumOperatorFactory) DeployNewOperatorAndForwarder() (*types.Transaction, error) { +func (e *LegacyEthereumOperatorFactory) DeployNewOperatorAndForwarder() (*types.Transaction, error) { opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) if err != nil { return nil, err @@ -1839,19 +1812,20 @@ func (e *EthereumOperatorFactory) DeployNewOperatorAndForwarder() (*types.Transa return tx, nil } -// EthereumOperator represents operator contract -type EthereumOperator struct { +// LegacyEthereumOperator represents operator contract +// Deprecated: we are moving away from blockchain.EVMClient, use EthereumOperator instead +type LegacyEthereumOperator struct { address common.Address client blockchain.EVMClient operator *operator_wrapper.Operator l zerolog.Logger } -func (e *EthereumOperator) Address() string { +func (e *LegacyEthereumOperator) Address() string { return e.address.Hex() } -func (e *EthereumOperator) AcceptAuthorizedReceivers(forwarders []common.Address, eoa []common.Address) error { +func (e *LegacyEthereumOperator) AcceptAuthorizedReceivers(forwarders []common.Address, eoa []common.Address) error { opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) if err != nil { return err @@ -1867,15 +1841,16 @@ func (e *EthereumOperator) AcceptAuthorizedReceivers(forwarders []common.Address return e.client.ProcessTransaction(tx) } -// EthereumAuthorizedForwarder represents authorized forwarder contract -type EthereumAuthorizedForwarder struct { +// LegacyEthereumAuthorizedForwarder represents authorized forwarder contract +// Deprecated: we are moving away from blockchain.EVMClient, use EthereumAuthorizedForwarder instead +type LegacyEthereumAuthorizedForwarder struct { address common.Address client blockchain.EVMClient authorizedForwarder *authorized_forwarder.AuthorizedForwarder } // Owner return authorized forwarder owner address -func (e *EthereumAuthorizedForwarder) Owner(ctx context.Context) (string, error) { +func (e *LegacyEthereumAuthorizedForwarder) Owner(ctx context.Context) (string, error) { opts := &bind.CallOpts{ From: common.HexToAddress(e.client.GetDefaultWallet().Address()), Context: ctx, @@ -1885,7 +1860,7 @@ func (e *EthereumAuthorizedForwarder) Owner(ctx context.Context) (string, error) return owner.Hex(), err } -func (e *EthereumAuthorizedForwarder) GetAuthorizedSenders(ctx context.Context) ([]string, error) { +func (e *LegacyEthereumAuthorizedForwarder) GetAuthorizedSenders(ctx context.Context) ([]string, error) { opts := &bind.CallOpts{ From: common.HexToAddress(e.client.GetDefaultWallet().Address()), Context: ctx, @@ -1901,7 +1876,7 @@ func (e *EthereumAuthorizedForwarder) GetAuthorizedSenders(ctx context.Context) return sendersAddrs, nil } -func (e *EthereumAuthorizedForwarder) Address() string { +func (e *LegacyEthereumAuthorizedForwarder) Address() string { return e.address.Hex() } @@ -1949,7 +1924,8 @@ func channelClosed(ch <-chan struct{}) bool { return false } -type EthereumOffchainAggregatorV2 struct { +// Deprecated: we are moving away from blockchain.EVMClient, use EthereumOffchainAggregatorV2 instead +type LegacyEthereumOffchainAggregatorV2 struct { address *common.Address client blockchain.EVMClient contract *ocr2aggregator.OCR2Aggregator @@ -1966,21 +1942,11 @@ type OCRv2Config struct { OffchainConfig []byte } -func (e *EthereumOffchainAggregatorV2) Address() string { +func (e *LegacyEthereumOffchainAggregatorV2) Address() string { return e.address.Hex() } -func (e *EthereumOffchainAggregatorV2) Fund(nativeAmount *big.Float) error { - gasEstimates, err := e.client.EstimateGas(ethereum.CallMsg{ - To: e.address, - }) - if err != nil { - return err - } - return e.client.Fund(e.address.Hex(), nativeAmount, gasEstimates) -} - -func (e *EthereumOffchainAggregatorV2) RequestNewRound() error { +func (e *LegacyEthereumOffchainAggregatorV2) RequestNewRound() error { opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) if err != nil { return err @@ -1992,7 +1958,7 @@ func (e *EthereumOffchainAggregatorV2) RequestNewRound() error { return e.client.ProcessTransaction(tx) } -func (e *EthereumOffchainAggregatorV2) GetLatestAnswer(ctx context.Context) (*big.Int, error) { +func (e *LegacyEthereumOffchainAggregatorV2) GetLatestAnswer(ctx context.Context) (*big.Int, error) { opts := &bind.CallOpts{ From: common.HexToAddress(e.client.GetDefaultWallet().Address()), Context: ctx, @@ -2000,7 +1966,7 @@ func (e *EthereumOffchainAggregatorV2) GetLatestAnswer(ctx context.Context) (*bi return e.contract.LatestAnswer(opts) } -func (e *EthereumOffchainAggregatorV2) GetLatestRound(ctx context.Context) (*RoundData, error) { +func (e *LegacyEthereumOffchainAggregatorV2) GetLatestRound(ctx context.Context) (*RoundData, error) { opts := &bind.CallOpts{ From: common.HexToAddress(e.client.GetDefaultWallet().Address()), Context: ctx, @@ -2018,7 +1984,7 @@ func (e *EthereumOffchainAggregatorV2) GetLatestRound(ctx context.Context) (*Rou }, nil } -func (e *EthereumOffchainAggregatorV2) GetRound(ctx context.Context, roundID *big.Int) (*RoundData, error) { +func (e *LegacyEthereumOffchainAggregatorV2) GetRound(ctx context.Context, roundID *big.Int) (*RoundData, error) { opts := &bind.CallOpts{ From: common.HexToAddress(e.client.GetDefaultWallet().Address()), Context: ctx, @@ -2036,7 +2002,7 @@ func (e *EthereumOffchainAggregatorV2) GetRound(ctx context.Context, roundID *bi }, nil } -func (e *EthereumOffchainAggregatorV2) SetPayees(transmitters, payees []string) error { +func (e *LegacyEthereumOffchainAggregatorV2) SetPayees(transmitters, payees []string) error { opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) if err != nil { return err @@ -2062,7 +2028,7 @@ func (e *EthereumOffchainAggregatorV2) SetPayees(transmitters, payees []string) return e.client.ProcessTransaction(tx) } -func (e *EthereumOffchainAggregatorV2) SetConfig(ocrConfig *OCRv2Config) error { +func (e *LegacyEthereumOffchainAggregatorV2) SetConfig(ocrConfig *OCRv2Config) error { opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) if err != nil { return err @@ -2091,19 +2057,7 @@ func (e *EthereumOffchainAggregatorV2) SetConfig(ocrConfig *OCRv2Config) error { return e.client.ProcessTransaction(tx) } -func (e *EthereumOffchainAggregatorV2) GetConfig(ctx context.Context) ([32]byte, uint32, error) { - opts := &bind.CallOpts{ - From: common.HexToAddress(e.client.GetDefaultWallet().Address()), - Context: ctx, - } - details, err := e.contract.LatestConfigDetails(opts) - if err != nil { - return [32]byte{}, 0, err - } - return details.ConfigDigest, details.BlockNumber, err -} - -func (e *EthereumOffchainAggregatorV2) ParseEventAnswerUpdated(log types.Log) (*ocr2aggregator.OCR2AggregatorAnswerUpdated, error) { +func (e *LegacyEthereumOffchainAggregatorV2) ParseEventAnswerUpdated(log types.Log) (*ocr2aggregator.OCR2AggregatorAnswerUpdated, error) { return e.contract.ParseAnswerUpdated(log) } @@ -2519,3 +2473,39 @@ func (e *EthereumWERC20Mock) Mint(account common.Address, amount *big.Int) (*typ } return tx, e.client.ProcessTransaction(tx) } + +func ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(k8sNodes []*client.ChainlinkK8sClient) []ChainlinkNodeWithKeysAndAddress { + var nodesAsInterface = make([]ChainlinkNodeWithKeysAndAddress, len(k8sNodes)) + for i, node := range k8sNodes { + nodesAsInterface[i] = node + } + + return nodesAsInterface +} + +func ChainlinkClientToChainlinkNodeWithKeysAndAddress(k8sNodes []*client.ChainlinkClient) []ChainlinkNodeWithKeysAndAddress { + var nodesAsInterface = make([]ChainlinkNodeWithKeysAndAddress, len(k8sNodes)) + for i, node := range k8sNodes { + nodesAsInterface[i] = node + } + + return nodesAsInterface +} + +func V2OffChainAgrregatorToOffChainAggregatorWithRounds(contracts []OffchainAggregatorV2) []OffChainAggregatorWithRounds { + var contractsAsInterface = make([]OffChainAggregatorWithRounds, len(contracts)) + for i, contract := range contracts { + contractsAsInterface[i] = contract + } + + return contractsAsInterface +} + +func V1OffChainAgrregatorToOffChainAggregatorWithRounds(contracts []OffchainAggregator) []OffChainAggregatorWithRounds { + var contractsAsInterface = make([]OffChainAggregatorWithRounds, len(contracts)) + for i, contract := range contracts { + contractsAsInterface[i] = contract + } + + return contractsAsInterface +} diff --git a/integration-tests/contracts/ethereum_contracts_local.go b/integration-tests/contracts/ethereum_contracts_local.go deleted file mode 100644 index 316658a791e..00000000000 --- a/integration-tests/contracts/ethereum_contracts_local.go +++ /dev/null @@ -1,98 +0,0 @@ -package contracts - -import ( - "encoding/hex" - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/rs/zerolog/log" - ocrConfigHelper "github.com/smartcontractkit/libocr/offchainreporting/confighelper" - ocrTypes "github.com/smartcontractkit/libocr/offchainreporting/types" - - "github.com/smartcontractkit/chainlink/integration-tests/client" -) - -// SetConfigLocal sets the payees and the offchain reporting protocol configuration -func (o *EthereumOffchainAggregator) SetConfigLocal( - chainlinkNodes []*client.ChainlinkClient, - ocrConfig OffChainAggregatorConfig, - transmitters []common.Address, -) error { - // Gather necessary addresses and keys from our chainlink nodes to properly configure the OCR contract - log.Info().Str("Contract Address", o.address.Hex()).Msg("Configuring OCR Contract") - for i, node := range chainlinkNodes { - ocrKeys, err := node.MustReadOCRKeys() - if err != nil { - return err - } - if len(ocrKeys.Data) == 0 { - return fmt.Errorf("no OCR keys found for node %v", node) - } - primaryOCRKey := ocrKeys.Data[0] - if err != nil { - return err - } - p2pKeys, err := node.MustReadP2PKeys() - if err != nil { - return err - } - primaryP2PKey := p2pKeys.Data[0] - - // Need to convert the key representations - var onChainSigningAddress [20]byte - var configPublicKey [32]byte - offchainSigningAddress, err := hex.DecodeString(primaryOCRKey.Attributes.OffChainPublicKey) - if err != nil { - return err - } - decodeConfigKey, err := hex.DecodeString(primaryOCRKey.Attributes.ConfigPublicKey) - if err != nil { - return err - } - - // https://stackoverflow.com/questions/8032170/how-to-assign-string-to-bytes-array - copy(onChainSigningAddress[:], common.HexToAddress(primaryOCRKey.Attributes.OnChainSigningAddress).Bytes()) - copy(configPublicKey[:], decodeConfigKey) - - oracleIdentity := ocrConfigHelper.OracleIdentity{ - TransmitAddress: transmitters[i], - OnChainSigningAddress: onChainSigningAddress, - PeerID: primaryP2PKey.Attributes.PeerID, - OffchainPublicKey: offchainSigningAddress, - } - oracleIdentityExtra := ocrConfigHelper.OracleIdentityExtra{ - OracleIdentity: oracleIdentity, - SharedSecretEncryptionPublicKey: ocrTypes.SharedSecretEncryptionPublicKey(configPublicKey), - } - - ocrConfig.OracleIdentities = append(ocrConfig.OracleIdentities, oracleIdentityExtra) - } - - signers, transmitters, threshold, encodedConfigVersion, encodedConfig, err := ocrConfigHelper.ContractSetConfigArgs( - ocrConfig.DeltaProgress, - ocrConfig.DeltaResend, - ocrConfig.DeltaRound, - ocrConfig.DeltaGrace, - ocrConfig.DeltaC, - ocrConfig.AlphaPPB, - ocrConfig.DeltaStage, - ocrConfig.RMax, - ocrConfig.S, - ocrConfig.OracleIdentities, - ocrConfig.F, - ) - if err != nil { - return err - } - - // Set Config - opts, err := o.client.TransactionOpts(o.client.GetDefaultWallet()) - if err != nil { - return err - } - tx, err := o.ocr.SetConfig(opts, signers, transmitters, threshold, encodedConfigVersion, encodedConfig) - if err != nil { - return err - } - return o.client.ProcessTransaction(tx) -} diff --git a/integration-tests/contracts/ethereum_contracts_seth.go b/integration-tests/contracts/ethereum_contracts_seth.go new file mode 100644 index 00000000000..237d6896234 --- /dev/null +++ b/integration-tests/contracts/ethereum_contracts_seth.go @@ -0,0 +1,581 @@ +package contracts + +import ( + "context" + "encoding/hex" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/smartcontractkit/seth" + + "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" + ocrConfigHelper "github.com/smartcontractkit/libocr/offchainreporting/confighelper" + ocrTypes "github.com/smartcontractkit/libocr/offchainreporting/types" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_factory" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_wrapper" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/link_token" +) + +// EthereumOffchainAggregator represents the offchain aggregation contract +type EthereumOffchainAggregator struct { + client *seth.Client + ocr *offchainaggregator.OffchainAggregator + address *common.Address + l zerolog.Logger +} + +func LoadOffchainAggregator(l zerolog.Logger, seth *seth.Client, contractAddress common.Address) (EthereumOffchainAggregator, error) { + oAbi, err := offchainaggregator.OffchainAggregatorMetaData.GetAbi() + if err != nil { + return EthereumOffchainAggregator{}, fmt.Errorf("failed to get OffChain Aggregator ABI: %w", err) + } + seth.ContractStore.AddABI("OffChainAggregator", *oAbi) + seth.ContractStore.AddBIN("OffChainAggregator", common.FromHex(offchainaggregator.OffchainAggregatorMetaData.Bin)) + + ocr, err := offchainaggregator.NewOffchainAggregator(contractAddress, seth.Client) + if err != nil { + return EthereumOffchainAggregator{}, fmt.Errorf("failed to instantiate OCR instance: %w", err) + } + + return EthereumOffchainAggregator{ + client: seth, + ocr: ocr, + address: &contractAddress, + l: l, + }, nil +} + +func DeployOffchainAggregator(l zerolog.Logger, seth *seth.Client, linkTokenAddress common.Address, offchainOptions OffchainOptions) (EthereumOffchainAggregator, error) { + oAbi, err := offchainaggregator.OffchainAggregatorMetaData.GetAbi() + if err != nil { + return EthereumOffchainAggregator{}, fmt.Errorf("failed to get OffChain Aggregator ABI: %w", err) + } + + ocrDeploymentData, err := seth.DeployContract( + seth.NewTXOpts(), + "OffChainAggregator", + *oAbi, + common.FromHex(offchainaggregator.OffchainAggregatorMetaData.Bin), + offchainOptions.MaximumGasPrice, + offchainOptions.ReasonableGasPrice, + offchainOptions.MicroLinkPerEth, + offchainOptions.LinkGweiPerObservation, + offchainOptions.LinkGweiPerTransmission, + linkTokenAddress, + offchainOptions.MinimumAnswer, + offchainOptions.MaximumAnswer, + offchainOptions.BillingAccessController, + offchainOptions.RequesterAccessController, + offchainOptions.Decimals, + offchainOptions.Description) + if err != nil { + return EthereumOffchainAggregator{}, fmt.Errorf("OCR instance deployment have failed: %w", err) + } + + ocr, err := offchainaggregator.NewOffchainAggregator(ocrDeploymentData.Address, seth.Client) + if err != nil { + return EthereumOffchainAggregator{}, fmt.Errorf("failed to instantiate OCR instance: %w", err) + } + + return EthereumOffchainAggregator{ + client: seth, + ocr: ocr, + address: &ocrDeploymentData.Address, + l: l, + }, nil +} + +// SetPayees sets wallets for the contract to pay out to? +func (o *EthereumOffchainAggregator) SetPayees( + transmitters, payees []string, +) error { + var transmittersAddr, payeesAddr []common.Address + for _, tr := range transmitters { + transmittersAddr = append(transmittersAddr, common.HexToAddress(tr)) + } + for _, p := range payees { + payeesAddr = append(payeesAddr, common.HexToAddress(p)) + } + + o.l.Info(). + Str("Transmitters", fmt.Sprintf("%v", transmitters)). + Str("Payees", fmt.Sprintf("%v", payees)). + Str("OCR Address", o.Address()). + Msg("Setting OCR Payees") + + _, err := o.client.Decode(o.ocr.SetPayees(o.client.NewTXOpts(), transmittersAddr, payeesAddr)) + return err +} + +// SetConfig sets the payees and the offchain reporting protocol configuration +func (o *EthereumOffchainAggregator) SetConfig( + chainlinkNodes []ChainlinkNodeWithKeysAndAddress, + ocrConfig OffChainAggregatorConfig, + transmitters []common.Address, +) error { + // Gather necessary addresses and keys from our chainlink nodes to properly configure the OCR contract + log.Info().Str("Contract Address", o.address.Hex()).Msg("Configuring OCR Contract") + for i, node := range chainlinkNodes { + ocrKeys, err := node.MustReadOCRKeys() + if err != nil { + return err + } + if len(ocrKeys.Data) == 0 { + return fmt.Errorf("no OCR keys found for node %v", node) + } + primaryOCRKey := ocrKeys.Data[0] + if err != nil { + return err + } + p2pKeys, err := node.MustReadP2PKeys() + if err != nil { + return err + } + primaryP2PKey := p2pKeys.Data[0] + + // Need to convert the key representations + var onChainSigningAddress [20]byte + var configPublicKey [32]byte + offchainSigningAddress, err := hex.DecodeString(primaryOCRKey.Attributes.OffChainPublicKey) + if err != nil { + return err + } + decodeConfigKey, err := hex.DecodeString(primaryOCRKey.Attributes.ConfigPublicKey) + if err != nil { + return err + } + + // https://stackoverflow.com/questions/8032170/how-to-assign-string-to-bytes-array + copy(onChainSigningAddress[:], common.HexToAddress(primaryOCRKey.Attributes.OnChainSigningAddress).Bytes()) + copy(configPublicKey[:], decodeConfigKey) + + oracleIdentity := ocrConfigHelper.OracleIdentity{ + TransmitAddress: transmitters[i], + OnChainSigningAddress: onChainSigningAddress, + PeerID: primaryP2PKey.Attributes.PeerID, + OffchainPublicKey: offchainSigningAddress, + } + oracleIdentityExtra := ocrConfigHelper.OracleIdentityExtra{ + OracleIdentity: oracleIdentity, + SharedSecretEncryptionPublicKey: ocrTypes.SharedSecretEncryptionPublicKey(configPublicKey), + } + + ocrConfig.OracleIdentities = append(ocrConfig.OracleIdentities, oracleIdentityExtra) + } + + signers, transmitters, threshold, encodedConfigVersion, encodedConfig, err := ocrConfigHelper.ContractSetConfigArgs( + ocrConfig.DeltaProgress, + ocrConfig.DeltaResend, + ocrConfig.DeltaRound, + ocrConfig.DeltaGrace, + ocrConfig.DeltaC, + ocrConfig.AlphaPPB, + ocrConfig.DeltaStage, + ocrConfig.RMax, + ocrConfig.S, + ocrConfig.OracleIdentities, + ocrConfig.F, + ) + if err != nil { + return err + } + + // fails with error setting OCR config for contract '0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82': both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified + // but we only have gasPrice set... It also fails with the same error when we enable EIP-1559 + // fails when we wait for it to be minted, inside the wrapper there's no error when we call it, so it must be something inside smart contract + // that's reverting it and maybe the error message is completely off + _, err = o.client.Decode(o.ocr.SetConfig(o.client.NewTXOpts(), signers, transmitters, threshold, encodedConfigVersion, encodedConfig)) + return err +} + +// RequestNewRound requests the OCR contract to create a new round +func (o *EthereumOffchainAggregator) RequestNewRound() error { + o.l.Info().Str("Contract Address", o.address.Hex()).Msg("New OCR round requested") + _, err := o.client.Decode(o.ocr.RequestNewRound(o.client.NewTXOpts())) + return err +} + +// GetLatestAnswer returns the latest answer from the OCR contract +func (o *EthereumOffchainAggregator) GetLatestAnswer(ctx context.Context) (*big.Int, error) { + return o.ocr.LatestAnswer(&bind.CallOpts{ + From: o.client.Addresses[0], + Context: ctx, + }) +} + +func (o *EthereumOffchainAggregator) Address() string { + return o.address.Hex() +} + +// GetLatestRound returns data from the latest round +func (o *EthereumOffchainAggregator) GetLatestRound(ctx context.Context) (*RoundData, error) { + roundData, err := o.ocr.LatestRoundData(&bind.CallOpts{ + From: o.client.Addresses[0], + Context: ctx, + }) + if err != nil { + return nil, err + } + + return &RoundData{ + RoundId: roundData.RoundId, + Answer: roundData.Answer, + AnsweredInRound: roundData.AnsweredInRound, + StartedAt: roundData.StartedAt, + UpdatedAt: roundData.UpdatedAt, + }, err +} + +func (o *EthereumOffchainAggregator) LatestRoundDataUpdatedAt() (*big.Int, error) { + data, err := o.ocr.LatestRoundData(o.client.NewCallOpts()) + if err != nil { + return nil, err + } + return data.UpdatedAt, nil +} + +// GetRound retrieves an OCR round by the round ID +func (o *EthereumOffchainAggregator) GetRound(ctx context.Context, roundID *big.Int) (*RoundData, error) { + roundData, err := o.ocr.GetRoundData(&bind.CallOpts{ + From: o.client.Addresses[0], + Context: ctx, + }, roundID) + if err != nil { + return nil, err + } + + return &RoundData{ + RoundId: roundData.RoundId, + Answer: roundData.Answer, + AnsweredInRound: roundData.AnsweredInRound, + StartedAt: roundData.StartedAt, + UpdatedAt: roundData.UpdatedAt, + }, nil +} + +// ParseEventAnswerUpdated parses the log for event AnswerUpdated +func (o *EthereumOffchainAggregator) ParseEventAnswerUpdated(eventLog types.Log) (*offchainaggregator.OffchainAggregatorAnswerUpdated, error) { + return o.ocr.ParseAnswerUpdated(eventLog) +} + +// LegacyEthereumOperatorFactory represents operator factory contract +type EthereumOperatorFactory struct { + address *common.Address + client *seth.Client + operatorFactory *operator_factory.OperatorFactory +} + +func DeployEthereumOperatorFactory(seth *seth.Client, linkTokenAddress common.Address) (EthereumOperatorFactory, error) { + operatorAbi, err := operator_factory.OperatorFactoryMetaData.GetAbi() + if err != nil { + return EthereumOperatorFactory{}, fmt.Errorf("failed to get OperatorFactory ABI: %w", err) + } + operatorData, err := seth.DeployContract(seth.NewTXOpts(), "OperatorFactory", *operatorAbi, common.FromHex(operator_factory.OperatorFactoryMetaData.Bin), linkTokenAddress) + if err != nil { + return EthereumOperatorFactory{}, fmt.Errorf("OperatorFactory instance deployment have failed: %w", err) + } + + operatorFactory, err := operator_factory.NewOperatorFactory(operatorData.Address, seth.Client) + if err != nil { + return EthereumOperatorFactory{}, fmt.Errorf("failed to instantiate OperatorFactory instance: %w", err) + } + + return EthereumOperatorFactory{ + address: &operatorData.Address, + client: seth, + operatorFactory: operatorFactory, + }, nil +} + +func (e *EthereumOperatorFactory) ParseAuthorizedForwarderCreated(eventLog types.Log) (*operator_factory.OperatorFactoryAuthorizedForwarderCreated, error) { + return e.operatorFactory.ParseAuthorizedForwarderCreated(eventLog) +} + +func (e *EthereumOperatorFactory) ParseOperatorCreated(eventLog types.Log) (*operator_factory.OperatorFactoryOperatorCreated, error) { + return e.operatorFactory.ParseOperatorCreated(eventLog) +} + +func (e *EthereumOperatorFactory) Address() string { + return e.address.Hex() +} + +func (e *EthereumOperatorFactory) DeployNewOperatorAndForwarder() (*types.Transaction, error) { + return e.operatorFactory.DeployNewOperatorAndForwarder(e.client.NewTXOpts()) +} + +// EthereumOperator represents operator contract +type EthereumOperator struct { + address *common.Address + client *seth.Client + operator *operator_wrapper.Operator + l zerolog.Logger +} + +func LoadEthereumOperator(logger zerolog.Logger, seth *seth.Client, contractAddress common.Address) (EthereumOperator, error) { + + abi, err := operator_wrapper.OperatorMetaData.GetAbi() + if err != nil { + return EthereumOperator{}, err + } + seth.ContractStore.AddABI("EthereumOperator", *abi) + seth.ContractStore.AddBIN("EthereumOperator", common.FromHex(operator_wrapper.OperatorMetaData.Bin)) + + operator, err := operator_wrapper.NewOperator(contractAddress, seth.Client) + if err != nil { + return EthereumOperator{}, err + } + + return EthereumOperator{ + address: &contractAddress, + client: seth, + operator: operator, + l: logger, + }, nil +} + +func (e *EthereumOperator) Address() string { + return e.address.Hex() +} + +func (e *EthereumOperator) AcceptAuthorizedReceivers(forwarders []common.Address, eoa []common.Address) error { + e.l.Info(). + Str("ForwardersAddresses", fmt.Sprint(forwarders)). + Str("EoaAddresses", fmt.Sprint(eoa)). + Msg("Accepting Authorized Receivers") + _, err := e.client.Decode(e.operator.AcceptAuthorizedReceivers(e.client.NewTXOpts(), forwarders, eoa)) + return err +} + +// EthereumAuthorizedForwarder represents authorized forwarder contract +type EthereumAuthorizedForwarder struct { + address *common.Address + client *seth.Client + authorizedForwarder *authorized_forwarder.AuthorizedForwarder +} + +func LoadEthereumAuthorizedForwarder(seth *seth.Client, contractAddress common.Address) (EthereumAuthorizedForwarder, error) { + abi, err := authorized_forwarder.AuthorizedForwarderMetaData.GetAbi() + if err != nil { + return EthereumAuthorizedForwarder{}, err + } + seth.ContractStore.AddABI("AuthorizedForwarder", *abi) + seth.ContractStore.AddBIN("AuthorizedForwarder", common.FromHex(authorized_forwarder.AuthorizedForwarderMetaData.Bin)) + + authorizedForwarder, err := authorized_forwarder.NewAuthorizedForwarder(contractAddress, seth.Client) + if err != nil { + return EthereumAuthorizedForwarder{}, fmt.Errorf("failed to instantiate AuthorizedForwarder instance: %w", err) + } + + return EthereumAuthorizedForwarder{ + address: &contractAddress, + client: seth, + authorizedForwarder: authorizedForwarder, + }, nil +} + +// Owner return authorized forwarder owner address +func (e *EthereumAuthorizedForwarder) Owner(_ context.Context) (string, error) { + owner, err := e.authorizedForwarder.Owner(e.client.NewCallOpts()) + + return owner.Hex(), err +} + +func (e *EthereumAuthorizedForwarder) GetAuthorizedSenders(ctx context.Context) ([]string, error) { + opts := &bind.CallOpts{ + From: e.client.Addresses[0], + Context: ctx, + } + authorizedSenders, err := e.authorizedForwarder.GetAuthorizedSenders(opts) + if err != nil { + return nil, err + } + var sendersAddrs []string + for _, o := range authorizedSenders { + sendersAddrs = append(sendersAddrs, o.Hex()) + } + return sendersAddrs, nil +} + +func (e *EthereumAuthorizedForwarder) Address() string { + return e.address.Hex() +} + +type EthereumOffchainAggregatorV2 struct { + address *common.Address + client *seth.Client + contract *ocr2aggregator.OCR2Aggregator + l zerolog.Logger +} + +func LoadOffChainAggregatorV2(l zerolog.Logger, seth *seth.Client, contractAddress common.Address) (EthereumOffchainAggregatorV2, error) { + oAbi, err := ocr2aggregator.OCR2AggregatorMetaData.GetAbi() + if err != nil { + return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to get OffChain Aggregator ABI: %w", err) + } + seth.ContractStore.AddABI("OffChainAggregatorV2", *oAbi) + seth.ContractStore.AddBIN("OffChainAggregatorV2", common.FromHex(ocr2aggregator.OCR2AggregatorMetaData.Bin)) + + ocr2, err := ocr2aggregator.NewOCR2Aggregator(contractAddress, seth.Client) + if err != nil { + return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to instantiate OCR instance: %w", err) + } + + return EthereumOffchainAggregatorV2{ + client: seth, + contract: ocr2, + address: &contractAddress, + l: l, + }, nil +} + +func DeployOffchainAggregatorV2(l zerolog.Logger, seth *seth.Client, linkTokenAddress common.Address, offchainOptions OffchainOptions) (EthereumOffchainAggregatorV2, error) { + oAbi, err := ocr2aggregator.OCR2AggregatorMetaData.GetAbi() + if err != nil { + return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to get OffChain Aggregator ABI: %w", err) + } + seth.ContractStore.AddABI("OffChainAggregatorV2", *oAbi) + seth.ContractStore.AddBIN("OffChainAggregatorV2", common.FromHex(ocr2aggregator.OCR2AggregatorMetaData.Bin)) + + ocrDeploymentData2, err := seth.DeployContract(seth.NewTXOpts(), "OffChainAggregatorV2", *oAbi, common.FromHex(ocr2aggregator.OCR2AggregatorMetaData.Bin), + linkTokenAddress, + offchainOptions.MinimumAnswer, + offchainOptions.MaximumAnswer, + offchainOptions.BillingAccessController, + offchainOptions.RequesterAccessController, + offchainOptions.Decimals, + offchainOptions.Description, + ) + + if err != nil { + return EthereumOffchainAggregatorV2{}, fmt.Errorf("OCR instance deployment have failed: %w", err) + } + + ocr2, err := ocr2aggregator.NewOCR2Aggregator(ocrDeploymentData2.Address, seth.Client) + if err != nil { + return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to instantiate OCR instance: %w", err) + } + + return EthereumOffchainAggregatorV2{ + client: seth, + contract: ocr2, + address: &ocrDeploymentData2.Address, + l: l, + }, nil +} + +func (e *EthereumOffchainAggregatorV2) Address() string { + return e.address.Hex() +} + +func (e *EthereumOffchainAggregatorV2) RequestNewRound() error { + _, err := e.client.Decode(e.contract.RequestNewRound(e.client.NewTXOpts())) + return err +} + +func (e *EthereumOffchainAggregatorV2) GetLatestAnswer(ctx context.Context) (*big.Int, error) { + return e.contract.LatestAnswer(&bind.CallOpts{ + From: e.client.Addresses[0], + Context: ctx, + }) +} + +func (e *EthereumOffchainAggregatorV2) GetLatestRound(ctx context.Context) (*RoundData, error) { + data, err := e.contract.LatestRoundData(&bind.CallOpts{ + From: e.client.Addresses[0], + Context: ctx, + }) + if err != nil { + return nil, err + } + return &RoundData{ + RoundId: data.RoundId, + StartedAt: data.StartedAt, + UpdatedAt: data.UpdatedAt, + AnsweredInRound: data.AnsweredInRound, + Answer: data.Answer, + }, nil +} + +func (e *EthereumOffchainAggregatorV2) GetRound(ctx context.Context, roundID *big.Int) (*RoundData, error) { + data, err := e.contract.GetRoundData(&bind.CallOpts{ + From: e.client.Addresses[0], + Context: ctx, + }, roundID) + if err != nil { + return nil, err + } + return &RoundData{ + RoundId: data.RoundId, + StartedAt: data.StartedAt, + UpdatedAt: data.UpdatedAt, + AnsweredInRound: data.AnsweredInRound, + Answer: data.Answer, + }, nil +} + +func (e *EthereumOffchainAggregatorV2) SetPayees(transmitters, payees []string) error { + e.l.Info(). + Str("Transmitters", fmt.Sprintf("%v", transmitters)). + Str("Payees", fmt.Sprintf("%v", payees)). + Str("OCRv2 Address", e.Address()). + Msg("Setting OCRv2 Payees") + + var addTransmitters, addrPayees []common.Address + for _, t := range transmitters { + addTransmitters = append(addTransmitters, common.HexToAddress(t)) + } + for _, p := range payees { + addrPayees = append(addrPayees, common.HexToAddress(p)) + } + + _, err := e.client.Decode(e.contract.SetPayees(e.client.NewTXOpts(), addTransmitters, addrPayees)) + return err +} + +func (e *EthereumOffchainAggregatorV2) SetConfig(ocrConfig *OCRv2Config) error { + e.l.Info(). + Str("Address", e.Address()). + Interface("Signers", ocrConfig.Signers). + Interface("Transmitters", ocrConfig.Transmitters). + Uint8("F", ocrConfig.F). + Bytes("OnchainConfig", ocrConfig.OnchainConfig). + Uint64("OffchainConfigVersion", ocrConfig.OffchainConfigVersion). + Bytes("OffchainConfig", ocrConfig.OffchainConfig). + Msg("Setting OCRv2 Config") + + _, err := e.client.Decode(e.contract.SetConfig( + e.client.NewTXOpts(), + ocrConfig.Signers, + ocrConfig.Transmitters, + ocrConfig.F, + ocrConfig.OnchainConfig, + ocrConfig.OffchainConfigVersion, + ocrConfig.OffchainConfig, + )) + return err +} + +func (e *EthereumOffchainAggregatorV2) ParseEventAnswerUpdated(log types.Log) (*ocr2aggregator.OCR2AggregatorAnswerUpdated, error) { + return e.contract.ParseAnswerUpdated(log) +} + +func DeployLinkTokenContract(client *seth.Client) (seth.DeploymentData, error) { + linkTokenAbi, err := link_token.LinkTokenMetaData.GetAbi() + if err != nil { + return seth.DeploymentData{}, fmt.Errorf("failed to get LinkToken ABI: %w", err) + } + linkDeploymentData, err := client.DeployContract(client.NewTXOpts(), "LinkToken", *linkTokenAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + if err != nil { + return seth.DeploymentData{}, fmt.Errorf("LinkToken instance deployment have failed: %w", err) + } + + return linkDeploymentData, nil +} diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index cbe2ed2d462..beea1db983b 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/seth" tc "github.com/testcontainers/testcontainers-go" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" @@ -20,9 +21,9 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/utils/runid" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - core_testconfig "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) @@ -31,21 +32,23 @@ var ( ) type CLClusterTestEnv struct { - Cfg *TestEnvConfig - Network *tc.DockerNetwork - LogStream *logstream.LogStream + Cfg *TestEnvConfig + DockerNetwork *tc.DockerNetwork + LogStream *logstream.LogStream /* components */ ClCluster *ClCluster PrivateChain []test_env.PrivateChain // for tests using non-dev networks -- unify it with new approach MockAdapter *test_env.Killgrave EVMClient blockchain.EVMClient + SethClient *seth.Client ContractDeployer contracts.ContractDeployer ContractLoader contracts.ContractLoader RpcProvider test_env.RpcProvider PrivateEthereumConfig *test_env.EthereumNetwork // new approach to private chains, supporting eth1 and eth2 l zerolog.Logger t *testing.T + isSimulatedNetwork bool } func NewTestEnv() (*CLClusterTestEnv, error) { @@ -55,8 +58,8 @@ func NewTestEnv() (*CLClusterTestEnv, error) { return nil, err } return &CLClusterTestEnv{ - Network: network, - l: log.Logger, + DockerNetwork: network, + l: log.Logger, }, nil } @@ -65,7 +68,7 @@ func NewTestEnv() (*CLClusterTestEnv, error) { func (te *CLClusterTestEnv) WithTestEnvConfig(cfg *TestEnvConfig) *CLClusterTestEnv { te.Cfg = cfg if cfg.MockAdapter.ContainerName != "" { - n := []string{te.Network.Name} + n := []string{te.DockerNetwork.Name} te.MockAdapter = test_env.NewKillgrave(n, te.Cfg.MockAdapter.ImpostersPath, test_env.WithContainerName(te.Cfg.MockAdapter.ContainerName), test_env.WithLogStream(te.LogStream)) } return te @@ -81,14 +84,16 @@ func (te *CLClusterTestEnv) WithTestInstance(t *testing.T) *CLClusterTestEnv { } func (te *CLClusterTestEnv) ParallelTransactions(enabled bool) { - te.EVMClient.ParallelTransactions(enabled) + if te.EVMClient != nil { + te.EVMClient.ParallelTransactions(enabled) + } } func (te *CLClusterTestEnv) WithPrivateChain(evmNetworks []blockchain.EVMNetwork) *CLClusterTestEnv { var chains []test_env.PrivateChain for _, evmNetwork := range evmNetworks { n := evmNetwork - pgc := test_env.NewPrivateGethChain(&n, []string{te.Network.Name}) + pgc := test_env.NewPrivateGethChain(&n, []string{te.DockerNetwork.Name}) if te.t != nil { pgc.GetPrimaryNode().WithTestInstance(te.t) } @@ -96,9 +101,9 @@ func (te *CLClusterTestEnv) WithPrivateChain(evmNetworks []blockchain.EVMNetwork var privateChain test_env.PrivateChain switch n.SimulationType { case "besu": - privateChain = test_env.NewPrivateBesuChain(&n, []string{te.Network.Name}) + privateChain = test_env.NewPrivateBesuChain(&n, []string{te.DockerNetwork.Name}) default: - privateChain = test_env.NewPrivateGethChain(&n, []string{te.Network.Name}) + privateChain = test_env.NewPrivateGethChain(&n, []string{te.DockerNetwork.Name}) } chains = append(chains, privateChain) } @@ -158,7 +163,7 @@ func (te *CLClusterTestEnv) StartClCluster(nodeConfig *chainlink.Config, count i opts = append(opts, WithSecrets(secretsConfig), WithLogStream(te.LogStream)) te.ClCluster = &ClCluster{} for i := 0; i < count; i++ { - ocrNode, err := NewClNode([]string{te.Network.Name}, *testconfig.GetChainlinkImageConfig().Image, *testconfig.GetChainlinkImageConfig().Version, nodeConfig, opts...) + ocrNode, err := NewClNode([]string{te.DockerNetwork.Name}, *testconfig.GetChainlinkImageConfig().Image, *testconfig.GetChainlinkImageConfig().Version, nodeConfig, opts...) if err != nil { return err } @@ -212,11 +217,10 @@ func (te *CLClusterTestEnv) Cleanup() error { te.logWhetherAllContainersAreRunning() - if te.EVMClient == nil { - return fmt.Errorf("evm client is nil, unable to return funds from chainlink nodes during cleanup") - } else if te.EVMClient.NetworkSimulated() { + if te.EVMClient == nil && te.SethClient == nil { + return fmt.Errorf("both EVMClient and SethClient are nil, unable to return funds from chainlink nodes during cleanup") + } else if te.isSimulatedNetwork { te.l.Info(). - Str("Network Name", te.EVMClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return.") } else { if err := te.returnFunds(); err != nil { @@ -230,6 +234,10 @@ func (te *CLClusterTestEnv) Cleanup() error { return err } + if te.SethClient != nil { + te.SethClient.Client.Close() + } + return nil } @@ -270,13 +278,21 @@ func (te *CLClusterTestEnv) returnFunds() error { if err != nil { return err } - if err = te.EVMClient.ReturnFunds(decryptedKey.PrivateKey); err != nil { - // If we fail to return funds from one, go on to try the others anyway - te.l.Error().Err(err).Str("Node", chainlinkNode.ContainerName).Msg("Error returning funds from node") + if te.EVMClient != nil { + if err = te.EVMClient.ReturnFunds(decryptedKey.PrivateKey); err != nil { + // If we fail to return funds from one, go on to try the others anyway + te.l.Error().Err(err).Str("Node", chainlinkNode.ContainerName).Msg("Error returning funds from node") + } } } } + if te.SethClient != nil { + if err := actions_seth.ReturnFunds(te.l, te.SethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(te.ClCluster.NodeAPIs())); err != nil { + te.l.Error().Err(err).Msg("Error returning funds from node") + } + } + te.l.Info().Msg("Returned funds from Chainlink nodes") return nil } diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index 6195ce22bca..cfe10d1f783 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -1,6 +1,7 @@ package test_env import ( + "errors" "fmt" "math/big" "os" @@ -9,6 +10,7 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/seth" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" @@ -16,14 +18,15 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logstream" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/osutil" - "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" - tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) type CleanUpType string @@ -38,6 +41,8 @@ type CLTestEnvBuilder struct { hasLogStream bool hasKillgrave bool hasForwarders bool + hasSeth bool + hasEVMClient bool clNodeConfig *chainlink.Config secretsConfig string nonDevGethNetworks []blockchain.EVMNetwork @@ -53,7 +58,7 @@ type CLTestEnvBuilder struct { cleanUpCustomFn func() chainOptionsFn []ChainOption evmClientNetworkOption []EVMClientNetworkOption - ethereumNetwork *test_env.EthereumNetwork + privateEthereumNetwork *test_env.EthereumNetwork testConfig tc.GlobalTestConfig /* funding */ @@ -64,6 +69,7 @@ func NewCLTestEnvBuilder() *CLTestEnvBuilder { return &CLTestEnvBuilder{ l: log.Logger, hasLogStream: true, + hasEVMClient: true, } } @@ -150,13 +156,19 @@ func (b *CLTestEnvBuilder) WithGeth() *CLTestEnvBuilder { panic(err) } - b.ethereumNetwork = &cfg + b.privateEthereumNetwork = &cfg return b } +func (b *CLTestEnvBuilder) WithSeth() *CLTestEnvBuilder { + b.hasSeth = true + b.hasEVMClient = false + return b +} + func (b *CLTestEnvBuilder) WithPrivateEthereumNetwork(en test_env.EthereumNetwork) *CLTestEnvBuilder { - b.ethereumNetwork = &en + b.privateEthereumNetwork = &en return b } @@ -246,11 +258,11 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } if b.hasKillgrave { - if b.te.Network == nil { + if b.te.DockerNetwork == nil { return nil, fmt.Errorf("test environment builder failed: %w", fmt.Errorf("cannot start mock adapter without a network")) } - b.te.MockAdapter = test_env.NewKillgrave([]string{b.te.Network.Name}, "", test_env.WithLogStream(b.te.LogStream)) + b.te.MockAdapter = test_env.NewKillgrave([]string{b.te.DockerNetwork.Name}, "", test_env.WithLogStream(b.te.LogStream)) err = b.te.StartMockAdapter() if err != nil { @@ -324,17 +336,27 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { networkConfig := networks.MustGetSelectedNetworkConfig(b.testConfig.GetNetworkConfig())[0] var rpcProvider test_env.RpcProvider - if b.ethereumNetwork != nil && networkConfig.Simulated { + if b.privateEthereumNetwork != nil && networkConfig.Simulated { // TODO here we should save the ethereum network config to te.Cfg, but it doesn't exist at this point // in general it seems we have no methods for saving config to file and we only load it from file // but I don't know how that config file is to be created or whether anyone ever done that - b.ethereumNetwork.DockerNetworkNames = []string{b.te.Network.Name} - networkConfig, rpcProvider, err = b.te.StartEthereumNetwork(b.ethereumNetwork) + b.privateEthereumNetwork.DockerNetworkNames = []string{b.te.DockerNetwork.Name} + networkConfig, rpcProvider, err = b.te.StartEthereumNetwork(b.privateEthereumNetwork) if err != nil { return nil, err } b.te.RpcProvider = rpcProvider - b.te.PrivateEthereumConfig = b.ethereumNetwork + b.te.PrivateEthereumConfig = b.privateEthereumNetwork + + b.te.isSimulatedNetwork = true + } + + if !b.hasSeth && !b.hasEVMClient { + return nil, errors.New("you need to specify, which evm client to use: Seth or EMVClient") + } + + if b.hasSeth && b.hasEVMClient { + return nil, errors.New("you can't use both Seth and EMVClient at the same time") } if !b.isNonEVM { @@ -343,23 +365,38 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { fn(&networkConfig) } } - bc, err := blockchain.NewEVMClientFromNetwork(networkConfig, b.l) - if err != nil { - return nil, err - } + if b.hasEVMClient { + bc, err := blockchain.NewEVMClientFromNetwork(networkConfig, b.l) + if err != nil { + return nil, err + } - b.te.EVMClient = bc - cd, err := contracts.NewContractDeployer(bc, b.l) - if err != nil { - return nil, err + b.te.EVMClient = bc + cd, err := contracts.NewContractDeployer(bc, b.l) + if err != nil { + return nil, err + } + b.te.ContractDeployer = cd + + cl, err := contracts.NewContractLoader(bc, b.l) + if err != nil { + return nil, err + } + b.te.ContractLoader = cl } - b.te.ContractDeployer = cd - cl, err := contracts.NewContractLoader(bc, b.l) - if err != nil { - return nil, err + if b.hasSeth { + readSethCfg := b.testConfig.GetSethConfig() + sethCfg := utils.MergeSethAndEvmNetworkConfigs(b.l, networkConfig, *readSethCfg) + seth, err := seth.NewClientWithConfig(&sethCfg) + if err != nil { + return nil, err + } + + b.te.SethClient = seth } - b.te.ContractLoader = cl + + b.te.isSimulatedNetwork = true } var nodeCsaKeys []string @@ -396,6 +433,8 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } } } + + b.te.isSimulatedNetwork = true } err := b.te.StartClCluster(cfg, b.clNodesCount, b.secretsConfig, b.testConfig, b.clNodesOpts...) @@ -410,12 +449,21 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { b.defaultNodeCsaKeys = nodeCsaKeys } - if b.ethereumNetwork != nil && b.clNodesCount > 0 && b.ETHFunds != nil { - b.te.ParallelTransactions(true) - defer b.te.ParallelTransactions(false) - if err := b.te.FundChainlinkNodes(b.ETHFunds); err != nil { - return nil, err + if b.privateEthereumNetwork != nil && b.clNodesCount > 0 && b.ETHFunds != nil { + if b.hasEVMClient { + b.te.ParallelTransactions(true) + defer b.te.ParallelTransactions(false) + if err := b.te.FundChainlinkNodes(b.ETHFunds); err != nil { + return nil, err + } + } + if b.hasSeth { + if err := actions_seth.FundChainlinkNodesFromRootAddress(b.l, b.te.SethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(b.te.ClCluster.NodeAPIs()), b.ETHFunds); err != nil { + return nil, err + } } + + b.te.isSimulatedNetwork = true } var enDesc string diff --git a/integration-tests/go.mod b/integration-tests/go.mod index a44980cc265..449ad7d6d41 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -28,6 +28,7 @@ require ( github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240215150045-fe2ba71b2f0a + github.com/smartcontractkit/seth v0.1.1 github.com/smartcontractkit/wasp v0.4.5 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.8.4 @@ -84,7 +85,6 @@ require ( github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/XSAM/otelsql v0.27.0 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect - github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/avast/retry-go v3.0.0+incompatible // indirect @@ -93,6 +93,7 @@ require ( github.com/aws/constructs-go/constructs/v10 v10.1.255 // indirect github.com/aws/jsii-runtime-go v1.75.0 // indirect github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect @@ -321,6 +322,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect github.com/mr-tron/base58 v1.2.0 // indirect @@ -328,6 +330,8 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9 // indirect + github.com/naoina/go-stringutil v0.1.0 // indirect + github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 // indirect github.com/oklog/run v1.1.0 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect @@ -433,7 +437,7 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/ratelimit v0.2.0 // indirect + go.uber.org/ratelimit v0.3.0 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.org/x/arch v0.7.0 // indirect golang.org/x/crypto v0.19.0 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 8c69edc0892..eb1eb89a526 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -187,7 +187,6 @@ github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6u github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -221,6 +220,8 @@ github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0= github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -1267,6 +1268,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk= @@ -1285,6 +1288,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= @@ -1529,6 +1536,8 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= github.com/smartcontractkit/libocr v0.0.0-20240215150045-fe2ba71b2f0a h1:nGkZ9uXS8lPIJOi68rdftEo2c9Q8qbRAi5+XMnKobVc= github.com/smartcontractkit/libocr v0.0.0-20240215150045-fe2ba71b2f0a/go.mod h1:kC0qmVPUaVkFqGiZMNhmRmjdphuUmeyLEdlWFOQzFWI= +github.com/smartcontractkit/seth v0.1.1 h1:Uh7Ldr6N+4HV6YPI/s5s3P8I+UndJXzDSL2VsBsiL/Y= +github.com/smartcontractkit/seth v0.1.1/go.mod h1:aOaGwrIVFG/MYaLSj9UUMyE5QJnYQoAgnxm5cKfT9Ng= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= @@ -1783,8 +1792,9 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA= go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= +go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= +go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= diff --git a/integration-tests/k8s/connect.go b/integration-tests/k8s/connect.go index a543f8139c1..d9a4223c077 100644 --- a/integration-tests/k8s/connect.go +++ b/integration-tests/k8s/connect.go @@ -6,17 +6,15 @@ import ( "time" "github.com/pelletier/go-toml/v2" - "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" client2 "github.com/smartcontractkit/chainlink-testing-framework/client" "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) const ( - DefaultConfigFilePath = "connect.toml" + DefaultConfigFilePath = "../connect.toml" ErrReadConnectionConfig = "failed to read TOML environment connection config" ErrUnmarshalConnectionConfig = "failed to unmarshal TOML environment connection config" ) @@ -37,10 +35,10 @@ type ConnectionVars struct { } // ConnectRemote connects to a local environment, see charts/chainlink-cluster -func ConnectRemote(l zerolog.Logger) (blockchain.EVMClient, *client2.MockserverClient, contracts.ContractDeployer, *client.ChainlinkK8sClient, []*client.ChainlinkK8sClient, error) { +func ConnectRemote() (*blockchain.EVMNetwork, *client2.MockserverClient, *client.ChainlinkK8sClient, []*client.ChainlinkK8sClient, error) { cfg, err := ReadConfig() if err != nil { - return nil, nil, nil, nil, nil, err + return &blockchain.EVMNetwork{}, nil, nil, nil, err } net := &blockchain.EVMNetwork{ Name: cfg.NetworkName, @@ -58,14 +56,6 @@ func ConnectRemote(l zerolog.Logger) (blockchain.EVMClient, *client2.MockserverC MinimumConfirmations: 1, GasEstimationBuffer: 10000, } - cc, err := blockchain.NewEVMClientFromNetwork(*net, l) - if err != nil { - return nil, nil, nil, nil, nil, err - } - cd, err := contracts.NewContractDeployer(cc, l) - if err != nil { - return nil, nil, nil, nil, nil, err - } clClients := make([]*client.ChainlinkK8sClient, 0) for i := 1; i <= cfg.CLNodesNum; i++ { c, err := client.NewChainlinkK8sClient(&client.ChainlinkConfig{ @@ -75,7 +65,7 @@ func ConnectRemote(l zerolog.Logger) (blockchain.EVMClient, *client2.MockserverC Password: cfg.CLNodePassword, }, fmt.Sprintf(cfg.CLNodeInternalDNSRecordTemplate, i), cfg.Namespace) if err != nil { - return nil, nil, nil, nil, nil, err + return &blockchain.EVMNetwork{}, nil, nil, nil, err } clClients = append(clClients, c) } @@ -83,7 +73,7 @@ func ConnectRemote(l zerolog.Logger) (blockchain.EVMClient, *client2.MockserverC LocalURL: cfg.MockServerURL, ClusterURL: cfg.MockServerURL, }) - return cc, msClient, cd, clClients[0], clClients[1:], nil + return net, msClient, clClients[0], clClients[1:], nil } func ReadConfig() (*ConnectionVars, error) { diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index c5aba2776c0..d8835fab620 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -20,10 +20,11 @@ require ( github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20240214231432-4ad5eb95178c github.com/smartcontractkit/chainlink/v2 v2.9.0-beta0.0.20240216210048-da02459ddad8 github.com/smartcontractkit/libocr v0.0.0-20240215150045-fe2ba71b2f0a + github.com/smartcontractkit/seth v0.1.1 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/wasp v0.4.5 github.com/stretchr/testify v1.8.4 - go.uber.org/ratelimit v0.2.0 + go.uber.org/ratelimit v0.3.0 ) // avoids ambigious imports of indirect dependencies @@ -60,7 +61,6 @@ require ( github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/XSAM/otelsql v0.27.0 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect - github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/avast/retry-go v3.0.0+incompatible // indirect @@ -70,6 +70,7 @@ require ( github.com/aws/jsii-runtime-go v1.75.0 // indirect github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df // indirect + github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect @@ -304,6 +305,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect github.com/mr-tron/base58 v1.2.0 // indirect @@ -311,6 +313,8 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9 // indirect + github.com/naoina/go-stringutil v0.1.0 // indirect + github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 // indirect github.com/oklog/run v1.1.0 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect @@ -380,6 +384,7 @@ require ( github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 // indirect + github.com/test-go/testify v1.1.4 // indirect github.com/testcontainers/testcontainers-go v0.23.0 // indirect github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a // indirect github.com/tidwall/btree v1.6.0 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index f1566970ac0..2b7e0773e34 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -187,7 +187,6 @@ github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6u github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -221,6 +220,8 @@ github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0= github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -1254,6 +1255,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk= @@ -1268,6 +1271,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= @@ -1512,6 +1519,8 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= github.com/smartcontractkit/libocr v0.0.0-20240215150045-fe2ba71b2f0a h1:nGkZ9uXS8lPIJOi68rdftEo2c9Q8qbRAi5+XMnKobVc= github.com/smartcontractkit/libocr v0.0.0-20240215150045-fe2ba71b2f0a/go.mod h1:kC0qmVPUaVkFqGiZMNhmRmjdphuUmeyLEdlWFOQzFWI= +github.com/smartcontractkit/seth v0.1.1 h1:Uh7Ldr6N+4HV6YPI/s5s3P8I+UndJXzDSL2VsBsiL/Y= +github.com/smartcontractkit/seth v0.1.1/go.mod h1:aOaGwrIVFG/MYaLSj9UUMyE5QJnYQoAgnxm5cKfT9Ng= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= @@ -1764,8 +1773,9 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA= go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= +go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= +go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= diff --git a/integration-tests/load/ocr/gun.go b/integration-tests/load/ocr/gun.go index ed92e328024..c4b79ceaf42 100644 --- a/integration-tests/load/ocr/gun.go +++ b/integration-tests/load/ocr/gun.go @@ -6,11 +6,10 @@ import ( "time" "github.com/rs/zerolog" + "github.com/smartcontractkit/seth" + "github.com/smartcontractkit/wasp" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - - "github.com/smartcontractkit/wasp" ) // Gun is a gun for the OCR load test @@ -18,14 +17,14 @@ import ( type Gun struct { roundNum atomic.Int64 ocrInstances []contracts.OffchainAggregator - cc blockchain.EVMClient + seth *seth.Client l zerolog.Logger } -func NewGun(l zerolog.Logger, cc blockchain.EVMClient, ocrInstances []contracts.OffchainAggregator) *Gun { +func NewGun(l zerolog.Logger, seth *seth.Client, ocrInstances []contracts.OffchainAggregator) *Gun { return &Gun{ l: l, - cc: cc, + seth: seth, ocrInstances: ocrInstances, } } diff --git a/integration-tests/load/ocr/helper.go b/integration-tests/load/ocr/helper.go index c35dc384d17..c80cedb64fc 100644 --- a/integration-tests/load/ocr/helper.go +++ b/integration-tests/load/ocr/helper.go @@ -1,49 +1,52 @@ package ocr import ( + "fmt" "math/big" "math/rand" "time" + "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/seth" client2 "github.com/smartcontractkit/chainlink-testing-framework/client" "github.com/smartcontractkit/chainlink/integration-tests/actions" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) func SetupCluster( - cc blockchain.EVMClient, - cd contracts.ContractDeployer, + l zerolog.Logger, + seth *seth.Client, workerNodes []*client.ChainlinkK8sClient, -) (contracts.LinkToken, error) { - err := actions.FundChainlinkNodes(workerNodes, cc, big.NewFloat(3)) +) (common.Address, error) { + err := actions_seth.FundChainlinkNodesFromRootAddress(l, seth, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes), big.NewFloat(3)) if err != nil { - return nil, err + return common.Address{}, err } - lt, err := cd.DeployLinkTokenContract() + linkDeploymentData, err := contracts.DeployLinkTokenContract(seth) if err != nil { - return nil, err + return common.Address{}, err } - return lt, nil + return linkDeploymentData.Address, nil } func SetupFeed( - cc blockchain.EVMClient, + l zerolog.Logger, + seth *seth.Client, + lta common.Address, msClient *client2.MockserverClient, - cd contracts.ContractDeployer, bootstrapNode *client.ChainlinkK8sClient, workerNodes []*client.ChainlinkK8sClient, - lt contracts.LinkToken, ) ([]contracts.OffchainAggregator, error) { - ocrInstances, err := actions.DeployOCRContracts(1, lt, cd, workerNodes, cc) + ocrInstances, err := actions_seth.DeployOCRv1Contracts(l, seth, 1, lta, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes)) if err != nil { return nil, err } - err = actions.CreateOCRJobs(ocrInstances, bootstrapNode, workerNodes, 5, msClient, cc.GetChainID().String()) + err = actions.CreateOCRJobs(ocrInstances, bootstrapNode, workerNodes, 5, msClient, fmt.Sprint(seth.ChainID)) if err != nil { return nil, err } diff --git a/integration-tests/load/ocr/ocr_test.go b/integration-tests/load/ocr/ocr_test.go index c6edf9122b9..0a06206e60d 100644 --- a/integration-tests/load/ocr/ocr_test.go +++ b/integration-tests/load/ocr/ocr_test.go @@ -5,10 +5,12 @@ import ( "github.com/stretchr/testify/require" + "github.com/smartcontractkit/seth" "github.com/smartcontractkit/wasp" "github.com/smartcontractkit/chainlink-testing-framework/logging" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/smartcontractkit/chainlink/integration-tests/k8s" ) @@ -22,14 +24,24 @@ var ( func TestOCRLoad(t *testing.T) { l := logging.GetTestLogger(t) - cc, msClient, cd, bootstrapNode, workerNodes, err := k8s.ConnectRemote(l) - require.NoError(t, err) - lt, err := SetupCluster(cc, cd, workerNodes) + + config, err := tc.GetConfig("Load", tc.OCR) require.NoError(t, err) - ocrInstances, err := SetupFeed(cc, msClient, cd, bootstrapNode, workerNodes, lt) + + evmNetwork, msClient, bootstrapNode, workerNodes, err := k8s.ConnectRemote() require.NoError(t, err) - config, err := tc.GetConfig("Load", tc.OCR) + readSethCfg := config.GetSethConfig() + require.NotNil(t, readSethCfg, "Seth config shouldn't be nil") + + sethCfg := utils.MergeSethAndEvmNetworkConfigs(l, *evmNetwork, *readSethCfg) + + seth, err := seth.NewClientWithConfig(&sethCfg) + require.NoError(t, err, "Error creating seth client") + + lta, err := SetupCluster(l, seth, workerNodes) + require.NoError(t, err) + ocrInstances, err := SetupFeed(l, seth, lta, msClient, bootstrapNode, workerNodes) require.NoError(t, err) cfg := config.OCR @@ -44,7 +56,7 @@ func TestOCRLoad(t *testing.T) { CallTimeout: cfg.Load.VerificationTimeout.Duration, RateLimitUnitDuration: cfg.Load.RateLimitUnitDuration.Duration, Schedule: wasp.Plain(*cfg.Load.Rate, cfg.Load.TestDuration.Duration), - Gun: NewGun(l, cc, ocrInstances), + Gun: NewGun(l, seth, ocrInstances), Labels: CommonTestLabels, LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), })) @@ -54,11 +66,21 @@ func TestOCRLoad(t *testing.T) { func TestOCRVolume(t *testing.T) { l := logging.GetTestLogger(t) - cc, msClient, cd, bootstrapNode, workerNodes, err := k8s.ConnectRemote(l) + config, err := tc.GetConfig("Volume", tc.OCR) require.NoError(t, err) - lt, err := SetupCluster(cc, cd, workerNodes) + + evmNetwork, msClient, bootstrapNode, workerNodes, err := k8s.ConnectRemote() require.NoError(t, err) - config, err := tc.GetConfig("Volume", tc.OCR) + + readSethCfg := config.GetSethConfig() + require.NotNil(t, readSethCfg, "Seth config shouldn't be nil") + + sethCfg := utils.MergeSethAndEvmNetworkConfigs(l, *evmNetwork, *readSethCfg) + + seth, err := seth.NewClientWithConfig(&sethCfg) + require.NoError(t, err, "Error creating seth client") + + lta, err := SetupCluster(l, seth, workerNodes) require.NoError(t, err) cfg := config.OCR @@ -71,7 +93,7 @@ func TestOCRVolume(t *testing.T) { LoadType: wasp.VU, CallTimeout: cfg.Volume.VerificationTimeout.Duration, Schedule: wasp.Plain(*cfg.Volume.Rate, cfg.Volume.TestDuration.Duration), - VU: NewVU(l, *cfg.Volume.VURequestsPerUnit, cfg.Volume.RateLimitUnitDuration.Duration, cc, lt, cd, bootstrapNode, workerNodes, msClient), + VU: NewVU(l, seth, *cfg.Volume.VURequestsPerUnit, cfg.Volume.RateLimitUnitDuration.Duration, lta, bootstrapNode, workerNodes, msClient), Labels: CommonTestLabels, LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), })) diff --git a/integration-tests/load/ocr/vu.go b/integration-tests/load/ocr/vu.go index bd733b08ae5..83f43a94e4d 100644 --- a/integration-tests/load/ocr/vu.go +++ b/integration-tests/load/ocr/vu.go @@ -2,12 +2,14 @@ package ocr import ( "context" + "fmt" "sync/atomic" "time" + "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/seth" "github.com/smartcontractkit/wasp" "go.uber.org/ratelimit" @@ -15,6 +17,7 @@ import ( client2 "github.com/smartcontractkit/chainlink-testing-framework/client" "github.com/smartcontractkit/chainlink/integration-tests/actions" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) @@ -27,9 +30,8 @@ type VU struct { rate int rateUnit time.Duration roundNum atomic.Int64 - cc blockchain.EVMClient - lt contracts.LinkToken - cd contracts.ContractDeployer + seth *seth.Client + lta common.Address bootstrapNode *client.ChainlinkK8sClient workerNodes []*client.ChainlinkK8sClient msClient *client2.MockserverClient @@ -39,11 +41,10 @@ type VU struct { func NewVU( l zerolog.Logger, + seth *seth.Client, rate int, rateUnit time.Duration, - cc blockchain.EVMClient, - lt contracts.LinkToken, - cd contracts.ContractDeployer, + lta common.Address, bootstrapNode *client.ChainlinkK8sClient, workerNodes []*client.ChainlinkK8sClient, msClient *client2.MockserverClient, @@ -54,9 +55,8 @@ func NewVU( rate: rate, rateUnit: rateUnit, l: l, - cc: cc, - lt: lt, - cd: cd, + seth: seth, + lta: lta, msClient: msClient, bootstrapNode: bootstrapNode, workerNodes: workerNodes, @@ -70,9 +70,8 @@ func (m *VU) Clone(_ *wasp.Generator) wasp.VirtualUser { rate: m.rate, rateUnit: m.rateUnit, l: m.l, - cc: m.cc, - lt: m.lt, - cd: m.cd, + seth: m.seth, + lta: m.lta, msClient: m.msClient, bootstrapNode: m.bootstrapNode, workerNodes: m.workerNodes, @@ -80,11 +79,11 @@ func (m *VU) Clone(_ *wasp.Generator) wasp.VirtualUser { } func (m *VU) Setup(_ *wasp.Generator) error { - ocrInstances, err := actions.DeployOCRContracts(1, m.lt, m.cd, m.workerNodes, m.cc) + ocrInstances, err := actions_seth.DeployOCRv1Contracts(m.l, m.seth, 1, m.lta, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(m.workerNodes)) if err != nil { return err } - err = actions.CreateOCRJobs(ocrInstances, m.bootstrapNode, m.workerNodes, 5, m.msClient, m.cc.GetChainID().String()) + err = actions.CreateOCRJobs(ocrInstances, m.bootstrapNode, m.workerNodes, 5, m.msClient, fmt.Sprint(m.seth.ChainID)) if err != nil { return err } diff --git a/integration-tests/scripts/check_base64_env_var.sh b/integration-tests/scripts/check_base64_env_var.sh new file mode 100755 index 00000000000..f1bd4632da5 --- /dev/null +++ b/integration-tests/scripts/check_base64_env_var.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +WHITE='\033[0;37m' +NC='\033[0m' # No Color + +echo +if [ ! -z "$BASE64_CONFIG_OVERRIDE" ]; then + echo "${GREEN}BASE64_CONFIG_OVERRIDE is set, it will be used.${NC}" + echo "Decoded content:" + echo + echo "$BASE64_CONFIG_OVERRIDE" | base64 --decode + echo + echo + echo "${GREEN}Press RETURN to confirm and continue...${NC} ${RED}or CTRL+C to exit${NC}" + read -r -n 1 key +else + echo "${YELLOW}BASE64_CONFIG_OVERRIDE is not set, checking for overrides.toml file...${NC}" + if [ -f "testconfig/overrides.toml" ]; then + echo "${GREEN}Found testconfig/overrides.toml file. Here's its content:${NC}" + echo + cat "testconfig/overrides.toml" + echo + echo + echo "${GREEN}Press RETURN to base64-encode it and run the test...${NC} ${RED}or CTRL+C to exit${NC}" + read -r -n 1 key + if [[ $key = "" ]]; then + export BASE64_CONFIG_OVERRIDE=$(cat testconfig/overrides.toml | base64) + fi + else + echo "${RED}testconfig/overrides.toml file does not exist. Please create it or set BASE64_CONFIG_OVERRIDE manually.${NC}" + echo + fi +fi \ No newline at end of file diff --git a/integration-tests/smoke/forwarder_ocr_test.go b/integration-tests/smoke/forwarder_ocr_test.go index 5c603c5b08f..60fe1db3c25 100644 --- a/integration-tests/smoke/forwarder_ocr_test.go +++ b/integration-tests/smoke/forwarder_ocr_test.go @@ -50,10 +50,12 @@ func TestForwarderOCRBasic(t *testing.T) { err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) require.NoError(t, err, "Error funding Chainlink nodes") + //nolint:staticcheck //ignore SA1019 we will migrate that test later operators, authorizedForwarders, _ := actions.DeployForwarderContracts( t, env.ContractDeployer, linkTokenContract, env.EVMClient, len(workerNodes), ) for i := range workerNodes { + //nolint:staticcheck //ignore SA1019 we will migrate that test later actions.AcceptAuthorizedReceiversOperator( t, operators[i], authorizedForwarders[i], []common.Address{workerNodeAddresses[i]}, env.EVMClient, env.ContractLoader, ) diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index 9385fb4f9a5..7c5b988276b 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -60,11 +60,13 @@ func TestForwarderOCR2Basic(t *testing.T) { err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) require.NoError(t, err, "Error funding Chainlink nodes") + //nolint:staticcheck //ignore SA1019 we will migrate that test later operators, authorizedForwarders, _ := actions.DeployForwarderContracts( t, env.ContractDeployer, linkTokenContract, env.EVMClient, len(workerNodes), ) for i := range workerNodes { + //nolint:staticcheck //ignore SA1019 we will migrate that test later actions.AcceptAuthorizedReceiversOperator(t, operators[i], authorizedForwarders[i], []common.Address{workerNodeAddresses[i]}, env.EVMClient, env.ContractLoader) require.NoError(t, err, "Accepting Authorized Receivers on Operator shouldn't fail") err = actions.TrackForwarderLocal(env.EVMClient, authorizedForwarders[i], workerNodes[i], l) @@ -94,6 +96,7 @@ func TestForwarderOCR2Basic(t *testing.T) { require.NoError(t, err, "Error building OCRv2 config") ocrv2Config.Transmitters = authorizedForwarders + //nolint:staticcheck //ignore SA1019 we will migrate that test later err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, ocrInstances) require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 6d3cf796cea..33538c1eaa4 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/rs/zerolog" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" @@ -14,6 +15,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/integration-tests/actions" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" @@ -39,83 +41,16 @@ func TestOCRv2Basic(t *testing.T) { test := test t.Run(test.name, func(t *testing.T) { t.Parallel() - l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.OCR2) - if err != nil { - t.Fatal(err) - } - - network, err := actions.EthereumNetworkConfigFromConfig(l, &config) - require.NoError(t, err, "Error building ethereum network config") - - env, err := test_env.NewCLTestEnvBuilder(). - WithTestInstance(t). - WithTestConfig(&config). - WithPrivateEthereumNetwork(network). - WithMockAdapter(). - WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), - node.WithOCR2(), - node.WithP2Pv2(), - node.WithTracing(), - )). - WithCLNodeOptions(test_env.WithNodeEnvVars(test.env)). - WithCLNodes(6). - WithFunding(big.NewFloat(.1)). - WithStandardCleanup(). - Build() - require.NoError(t, err) - - env.ParallelTransactions(true) - - nodeClients := env.ClCluster.NodeAPIs() - bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - - linkToken, err := env.ContractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - - err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) - require.NoError(t, err, "Error funding Chainlink nodes") - - // Gather transmitters - var transmitters []string - for _, node := range workerNodes { - addr, err := node.PrimaryEthAddress() - if err != nil { - require.NoError(t, fmt.Errorf("error getting node's primary ETH address: %w", err)) - } - transmitters = append(transmitters, addr) - } - - ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) - require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") + env, aggregatorContracts := prepareORCv2SmokeTestEnv(t, l, 5) - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false, test.chainReaderAndCodec) - require.NoError(t, err, "Error creating OCRv2 jobs") - - ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) - require.NoError(t, err, "Error building OCRv2 config") - - err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) - require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - - err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) - require.NoError(t, err, "Error watching for new OCR2 round") - roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), roundData.Answer.Int64(), - "Expected latest answer from OCR contract to be 5 but got %d", - roundData.Answer.Int64(), - ) - - err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) + err := env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) require.NoError(t, err) - err = actions.WatchNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) + err = actions_seth.WatchNewRound(l, env.SethClient, 2, contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts), time.Minute*5) require.NoError(t, err) - roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) + roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) require.NoError(t, err, "Error getting latest OCR answer") require.Equal(t, int64(10), roundData.Answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", @@ -130,77 +65,14 @@ func TestOCRv2Request(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.ForwarderOcr) - if err != nil { - t.Fatal(err) - } - - network, err := actions.EthereumNetworkConfigFromConfig(l, &config) - require.NoError(t, err, "Error building ethereum network config") - - env, err := test_env.NewCLTestEnvBuilder(). - WithTestInstance(t). - WithTestConfig(&config). - WithPrivateEthereumNetwork(network). - WithMockAdapter(). - WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), - node.WithOCR2(), - node.WithP2Pv2(), - node.WithTracing(), - )). - WithCLNodes(6). - WithFunding(big.NewFloat(.1)). - WithStandardCleanup(). - Build() - require.NoError(t, err) - - env.ParallelTransactions(true) - - nodeClients := env.ClCluster.NodeAPIs() - bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - - linkToken, err := env.ContractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - - err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) - require.NoError(t, err, "Error funding Chainlink nodes") - - // Gather transmitters - var transmitters []string - for _, node := range workerNodes { - addr, err := node.PrimaryEthAddress() - if err != nil { - require.NoError(t, fmt.Errorf("error getting node's primary ETH address: %w", err)) - } - transmitters = append(transmitters, addr) - } - - ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) - require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") - - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false, false) - require.NoError(t, err, "Error creating OCRv2 jobs") - - ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) - require.NoError(t, err, "Error building OCRv2 config") - - err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) - require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - - err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) - require.NoError(t, err, "Error watching for new OCR2 round") - roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), roundData.Answer.Int64(), - "Expected latest answer from OCR contract to be 5 but got %d", - roundData.Answer.Int64(), - ) + env, aggregatorContracts := prepareORCv2SmokeTestEnv(t, l, 5) // Keep the mockserver value the same and continually request new rounds for round := 2; round <= 4; round++ { - err = actions.StartNewOCR2Round(int64(round), aggregatorContracts, env.EVMClient, time.Minute*5, l) + err := actions_seth.StartNewRound(contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts)) require.NoError(t, err, "Error starting new OCR2 round") + err = actions_seth.WatchNewRound(l, env.SethClient, int64(round), contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts), time.Minute*5) + require.NoError(t, err, "Error watching for new OCR2 round") roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(int64(round))) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), roundData.Answer.Int64(), @@ -215,6 +87,43 @@ func TestOCRv2JobReplacement(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + env, aggregatorContracts := prepareORCv2SmokeTestEnv(t, l, 5) + nodeClients := env.ClCluster.NodeAPIs() + bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] + + err := env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) + require.NoError(t, err) + err = actions_seth.WatchNewRound(l, env.SethClient, 2, contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts), time.Minute*5) + require.NoError(t, err, "Error watching for new OCR2 round") + + roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) + require.NoError(t, err, "Error getting latest OCR answer") + require.Equal(t, int64(10), roundData.Answer.Int64(), + "Expected latest answer from OCR contract to be 10 but got %d", + roundData.Answer.Int64(), + ) + + err = actions.DeleteJobs(nodeClients) + require.NoError(t, err) + + err = actions.DeleteBridges(nodeClients) + require.NoError(t, err) + + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 15, uint64(env.SethClient.ChainID), false, false) + require.NoError(t, err, "Error creating OCRv2 jobs") + + err = actions_seth.WatchNewRound(l, env.SethClient, 3, contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts), time.Minute*3) + require.NoError(t, err, "Error watching for new OCR2 round") + + roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(3)) + require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") + require.Equal(t, int64(15), roundData.Answer.Int64(), + "Expected latest answer from OCR contract to be 15 but got %d", + roundData.Answer.Int64(), + ) +} + +func prepareORCv2SmokeTestEnv(t *testing.T, l zerolog.Logger, firstRoundResult int) (*test_env.CLClusterTestEnv, []contracts.OffchainAggregatorV2) { config, err := tc.GetConfig("Smoke", tc.OCR2) if err != nil { t.Fatal(err) @@ -233,18 +142,17 @@ func TestOCRv2JobReplacement(t *testing.T) { WithCLNodes(6). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). + WithSeth(). Build() require.NoError(t, err) - env.ParallelTransactions(true) - nodeClients := env.ClCluster.NodeAPIs() bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - linkToken, err := env.ContractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + linkDeploymentData, err := contracts.DeployLinkTokenContract(env.SethClient) + require.NoError(t, err, "Error deploying link token contract") - err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) + err = actions_seth.FundChainlinkNodesFromRootAddress(l, env.SethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(workerNodes), big.NewFloat(.05)) require.NoError(t, err, "Error funding Chainlink nodes") // Gather transmitters @@ -258,54 +166,26 @@ func TestOCRv2JobReplacement(t *testing.T) { } ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) + aggregatorContracts, err := actions_seth.DeployOCRv2Contracts(l, env.SethClient, 1, linkDeploymentData.Address, transmitters, ocrOffchainOptions) require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false, false) + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, uint64(env.SethClient.ChainID), false, false) require.NoError(t, err, "Error creating OCRv2 jobs") ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) require.NoError(t, err, "Error building OCRv2 config") - err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) + err = actions_seth.ConfigureOCRv2AggregatorContracts(ocrv2Config, aggregatorContracts) require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) + err = actions_seth.WatchNewRound(l, env.SethClient, 1, contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts), time.Minute*5) require.NoError(t, err, "Error watching for new OCR2 round") roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), roundData.Answer.Int64(), + require.Equal(t, int64(firstRoundResult), roundData.Answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", roundData.Answer.Int64(), ) - err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) - require.NoError(t, err) - err = actions.WatchNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) - require.NoError(t, err) - - roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) - require.NoError(t, err, "Error getting latest OCR answer") - require.Equal(t, int64(10), roundData.Answer.Int64(), - "Expected latest answer from OCR contract to be 10 but got %d", - roundData.Answer.Int64(), - ) - - err = actions.DeleteJobs(nodeClients) - require.NoError(t, err) - - err = actions.DeleteBridges(nodeClients) - require.NoError(t, err) - - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 15, env.EVMClient.GetChainID().Uint64(), false, false) - require.NoError(t, err, "Error creating OCRv2 jobs") - - err = actions.WatchNewOCR2Round(3, aggregatorContracts, env.EVMClient, time.Minute*3, l) - require.NoError(t, err, "Error watching for new OCR2 round") - roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(3)) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(15), roundData.Answer.Int64(), - "Expected latest answer from OCR contract to be 15 but got %d", - roundData.Answer.Int64(), - ) + return env, aggregatorContracts } diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index 549d6b0a63b..8047a19a50c 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -3,141 +3,118 @@ package smoke import ( "math/big" "testing" + "time" + "github.com/rs/zerolog" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) +const ( + ErrWatchingNewOCRRound = "Error watching for new OCR round" +) + func TestOCRBasic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.OCR) - if err != nil { - t.Fatal(err) - } + env, ocrInstances := prepareORCv1SmokeTestEnv(t, l, 5) + nodeClients := env.ClCluster.NodeAPIs() + workerNodes := nodeClients[1:] - network, err := actions.EthereumNetworkConfigFromConfig(l, &config) - require.NoError(t, err, "Error building ethereum network config") + err := actions.SetAllAdapterResponsesToTheSameValueLocal(10, ocrInstances, workerNodes, env.MockAdapter) + require.NoError(t, err, "Error setting all adapter responses to the same value") + err = actions_seth.WatchNewRound(l, env.SethClient, 2, contracts.V1OffChainAgrregatorToOffChainAggregatorWithRounds(ocrInstances), time.Duration(3*time.Minute)) + require.NoError(t, err, ErrWatchingNewOCRRound) - env, err := test_env.NewCLTestEnvBuilder(). - WithTestInstance(t). - WithTestConfig(&config). - WithPrivateEthereumNetwork(network). - WithMockAdapter(). - WithCLNodes(6). - WithFunding(big.NewFloat(.5)). - WithStandardCleanup(). - Build() - require.NoError(t, err) + answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) + require.NoError(t, err, "Error getting latest OCR answer") + require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) +} - env.ParallelTransactions(true) +func TestOCRJobReplacement(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + env, ocrInstances := prepareORCv1SmokeTestEnv(t, l, 5) nodeClients := env.ClCluster.NodeAPIs() bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - linkTokenContract, err := env.ContractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + err := actions.SetAllAdapterResponsesToTheSameValueLocal(10, ocrInstances, workerNodes, env.MockAdapter) + require.NoError(t, err, "Error setting all adapter responses to the same value") + err = actions_seth.WatchNewRound(l, env.SethClient, 2, contracts.V1OffChainAgrregatorToOffChainAggregatorWithRounds(ocrInstances), time.Duration(3*time.Minute)) + require.NoError(t, err, ErrWatchingNewOCRRound) - ocrInstances, err := actions.DeployOCRContractsLocal(1, linkTokenContract, env.ContractDeployer, workerNodes, env.EVMClient) - require.NoError(t, err) - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, "Error waiting for events") + answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) + require.NoError(t, err, "Error getting latest OCR answer") + require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) - err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID()) - require.NoError(t, err) + err = actions.DeleteJobs(nodeClients) + require.NoError(t, err, "Error deleting OCR jobs") - err = actions.WatchNewRound(1, ocrInstances, env.EVMClient, l) - require.NoError(t, err) + err = actions.DeleteBridges(nodeClients) + require.NoError(t, err, "Error deleting OCR bridges") - answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) + //Recreate job + err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, big.NewInt(env.SethClient.ChainID)) + require.NoError(t, err, "Error creating OCR jobs") - err = actions.SetAllAdapterResponsesToTheSameValueLocal(10, ocrInstances, workerNodes, env.MockAdapter) - require.NoError(t, err) - err = actions.WatchNewRound(2, ocrInstances, env.EVMClient, l) - require.NoError(t, err) + err = actions_seth.WatchNewRound(l, env.SethClient, 1, contracts.V1OffChainAgrregatorToOffChainAggregatorWithRounds(ocrInstances), time.Duration(3*time.Minute)) + require.NoError(t, err, ErrWatchingNewOCRRound) answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) - require.NoError(t, err, "Error getting latest OCR answer") + require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) } -func TestOCRJobReplacement(t *testing.T) { - t.Parallel() - l := logging.GetTestLogger(t) - +func prepareORCv1SmokeTestEnv(t *testing.T, l zerolog.Logger, firstRoundResult int64) (*test_env.CLClusterTestEnv, []contracts.OffchainAggregator) { config, err := tc.GetConfig("Smoke", tc.OCR) if err != nil { t.Fatal(err) } + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(&config). - WithGeth(). + WithPrivateEthereumNetwork(network). WithMockAdapter(). WithCLNodes(6). - WithFunding(big.NewFloat(.1)). + WithFunding(big.NewFloat(.5)). WithStandardCleanup(). + WithSeth(). Build() require.NoError(t, err) - env.ParallelTransactions(true) - nodeClients := env.ClCluster.NodeAPIs() bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - linkTokenContract, err := env.ContractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + linkDeploymentData, err := contracts.DeployLinkTokenContract(env.SethClient) + require.NoError(t, err, "Error deploying link token contract") - ocrInstances, err := actions.DeployOCRContractsLocal(1, linkTokenContract, env.ContractDeployer, workerNodes, env.EVMClient) - require.NoError(t, err) - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, "Error waiting for events") + ocrInstances, err := actions_seth.DeployOCRv1Contracts(l, env.SethClient, 1, linkDeploymentData.Address, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(workerNodes)) + require.NoError(t, err, "Error deploying OCR contracts") - err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID()) - require.NoError(t, err) + err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, big.NewInt(env.SethClient.ChainID)) + require.NoError(t, err, "Error creating OCR jobs") - err = actions.WatchNewRound(1, ocrInstances, env.EVMClient, l) - require.NoError(t, err) + err = actions_seth.WatchNewRound(l, env.SethClient, 1, contracts.V1OffChainAgrregatorToOffChainAggregatorWithRounds(ocrInstances), time.Duration(3*time.Minute)) + require.NoError(t, err, "Error watching for new OCR round") answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) - - err = actions.SetAllAdapterResponsesToTheSameValueLocal(10, ocrInstances, workerNodes, env.MockAdapter) - require.NoError(t, err) - err = actions.WatchNewRound(2, ocrInstances, env.EVMClient, l) - require.NoError(t, err) - - answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) - require.NoError(t, err, "Error getting latest OCR answer") - require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) - - err = actions.DeleteJobs(nodeClients) - require.NoError(t, err) - - err = actions.DeleteBridges(nodeClients) - require.NoError(t, err) - - //Recreate job - err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID()) - require.NoError(t, err) - - err = actions.WatchNewRound(1, ocrInstances, env.EVMClient, l) - require.NoError(t, err) - - answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) + require.Equal(t, firstRoundResult, answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) + return env, ocrInstances } diff --git a/integration-tests/soak/forwarder_ocr_test.go b/integration-tests/soak/forwarder_ocr_test.go index b5355a6c3f5..401100748d7 100644 --- a/integration-tests/soak/forwarder_ocr_test.go +++ b/integration-tests/soak/forwarder_ocr_test.go @@ -7,7 +7,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink/integration-tests/actions" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" ) @@ -32,7 +32,7 @@ ForwardersEnabled = true` return } t.Cleanup(func() { - if err := actions.TeardownRemoteSuite(ocrSoakTest.TearDownVals(t)); err != nil { + if err := actions_seth.TeardownRemoteSuite(ocrSoakTest.TearDownVals(t)); err != nil { l.Error().Err(err).Msg("Error tearing down environment") } }) diff --git a/integration-tests/soak/ocr_test.go b/integration-tests/soak/ocr_test.go index e25391e8450..e99ecdf072d 100644 --- a/integration-tests/soak/ocr_test.go +++ b/integration-tests/soak/ocr_test.go @@ -7,7 +7,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink/integration-tests/actions" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" ) @@ -34,7 +34,7 @@ func TestOCRSoak(t *testing.T) { return } t.Cleanup(func() { - if err := actions.TeardownRemoteSuite(ocrSoakTest.TearDownVals(t)); err != nil { + if err := actions_seth.TeardownRemoteSuite(ocrSoakTest.TearDownVals(t)); err != nil { l.Error().Err(err).Msg("Error tearing down environment") } }) diff --git a/integration-tests/testconfig/default.toml b/integration-tests/testconfig/default.toml index a65c23d70dc..b1815b1395b 100644 --- a/integration-tests/testconfig/default.toml +++ b/integration-tests/testconfig/default.toml @@ -19,4 +19,84 @@ slots_per_epoch=2 genesis_delay=15 validator_count=4 chain_id=1337 -addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] \ No newline at end of file +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] + +[Seth] +# enables automatic tracing of all transactions that are decoded via Decode() method +tracing_enabled = false +# saves each tracing result to json file in ./traces/.json +trace_to_json = false +# number of addresses to be generated and runtime, if set to 0, no addresses will be generated +# each generated address will receive a proportion of native tokens from root private key's balance +# with the value equal to (root_balance / ephemeral_addresses_number) - transfer_fee * ephemeral_addresses_number +ephemeral_addresses_number = 0 + +[Seth.nonce_manager] +key_sync_rate_limit_per_sec = 10 +key_sync_timeout = "2s" +key_sync_retry_delay = "1s" +key_sync_retries = 10 + +[[Seth.networks]] +name = "Geth" +chain_id = "1337" +transaction_timeout = "30s" +urls = ["ws://localhost:8546"] +transfer_gas_fee = 21_000 +gas_limit = 8_000_000 +# legacy transactions +gas_price = 1_000_000_000 +# EIP-1559 transactions +#eip_1559_dynamic_fees = true +gas_fee_cap = 10_000_000_000 +gas_tip_cap = 3_000_000_000 + +[[Seth.networks]] +name = "Fuji" +chain_id = "43113" +transaction_timeout = "30s" +transfer_gas_fee = 21_000 +# legacy transactions +gas_price = 30_000_000_000 +# EIP-1559 transactions +eip_1559_dynamic_fees = true +gas_fee_cap = 30_000_000_000 +gas_tip_cap = 1_800_000_000 + +[[Seth.networks]] +name = "Sepolia" +chain_id = "11155111" +transaction_timeout = "30s" +transfer_gas_fee = 21_000 +gas_limit = 14_000_000 +# legacy transactions +gas_price = 1_000_000_000 +# EIP-1559 transactions +eip_1559_dynamic_fees = true +gas_fee_cap = 25_000_000_000 +gas_tip_cap = 5_000_000_000 + +[[Seth.networks]] +name = "Mumbai" +chain_id = "80001" +transaction_timeout = "30s" +transfer_gas_fee = 21_000 +# legacy transactions +#gas_price = 1_800_000_000 +# EIP-1559 transactions +eip_1559_dynamic_fees = true +gas_fee_cap = 1_800_000_000 +gas_tip_cap = 1_800_000_000 + +[[Seth.networks]] +name = "zkEVM" +chain_id = "1442" +transaction_timeout = "30s" +transfer_gas_fee = 21_000 +gas_limit = 3_000_000 +# legacy transactions +gas_price = 50_000_000 +# EIP-1559 transactions +#eip_1559_dynamic_fees = true +gas_fee_cap = 1_800_000_000 +gas_tip_cap = 1_800_000_000 \ No newline at end of file diff --git a/integration-tests/testconfig/ocr/ocr.toml b/integration-tests/testconfig/ocr/ocr.toml index 8d3c73ca761..67d7d4588a0 100644 --- a/integration-tests/testconfig/ocr/ocr.toml +++ b/integration-tests/testconfig/ocr/ocr.toml @@ -40,4 +40,4 @@ test_duration="15m" [Soak.OCR.Soak] ocr_version="1" number_of_contracts=2 -time_between_rounds="1m" \ No newline at end of file +time_between_rounds="1m" diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go index 16a56051da0..0de5e12f489 100644 --- a/integration-tests/testconfig/testconfig.go +++ b/integration-tests/testconfig/testconfig.go @@ -16,6 +16,8 @@ import ( "golang.org/x/text/cases" "golang.org/x/text/language" + "github.com/smartcontractkit/seth" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" @@ -39,6 +41,7 @@ type GlobalTestConfig interface { GetNetworkConfig() *ctf_config.NetworkConfig GetPrivateEthereumNetworkConfig() *test_env.EthereumNetwork GetPyroscopeConfig() *ctf_config.PyroscopeConfig + SethConfig } type UpgradeableChainlinkTestConfig interface { @@ -77,6 +80,10 @@ type NamedConfiguration interface { GetConfigurationName() string } +type SethConfig interface { + GetSethConfig() *seth.Config +} + type TestConfig struct { ChainlinkImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkImage"` ChainlinkUpgradeImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkUpgradeImage"` @@ -86,6 +93,8 @@ type TestConfig struct { PrivateEthereumNetwork *ctf_test_env.EthereumNetwork `toml:"PrivateEthereumNetwork"` WaspConfig *ctf_config.WaspAutoBuildConfig `toml:"WaspAutoBuild"` + Seth *seth.Config `toml:"Seth"` + Common *Common `toml:"Common"` Automation *a_config.Config `toml:"Automation"` Functions *f_config.Config `toml:"Functions"` @@ -209,6 +218,10 @@ func (c TestConfig) GetConfigurationName() string { return c.ConfigurationName } +func (c TestConfig) GetSethConfig() *seth.Config { + return c.Seth +} + func (c *TestConfig) AsBase64() (string, error) { content, err := toml.Marshal(*c) if err != nil { @@ -431,14 +444,7 @@ func (c *TestConfig) Validate() error { return errors.Wrapf(err, "logging config validation failed") } - // require Loki config only if these tests run locally - _, willUseRemoteRunner := os.LookupEnv(k8s_config.EnvVarJobImage) - _, isInsideK8s := os.LookupEnv(k8s_config.EnvVarInsideK8s) - if (!willUseRemoteRunner && !isInsideK8s) && slices.Contains(TestTypesWithLoki, c.ConfigurationName) { - if c.Logging.Loki == nil { - return fmt.Errorf("for local execution you must set Loki config in logging config") - } - + if c.Logging.Loki != nil { if err := c.Logging.Loki.Validate(); err != nil { return errors.Wrapf(err, "loki config validation failed") } diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index a2e2fe42bae..f3a7a77e8cf 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -20,6 +20,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog" + "github.com/smartcontractkit/seth" "github.com/stretchr/testify/require" "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" @@ -39,11 +40,13 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" tt "github.com/smartcontractkit/chainlink/integration-tests/types" + "github.com/smartcontractkit/chainlink/integration-tests/utils" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) @@ -58,6 +61,7 @@ type OCRSoakTest struct { Config *tc.TestConfig TestReporter testreporters.OCRSoakTestReporter OperatorForwarderFlow bool + seth *seth.Client t *testing.T startTime time.Time @@ -68,7 +72,6 @@ type OCRSoakTest struct { log zerolog.Logger bootstrapNode *client.ChainlinkK8sClient workerNodes []*client.ChainlinkK8sClient - chainClient blockchain.EVMClient mockServer *ctfClient.MockserverClient filterQuery geth.FilterQuery @@ -152,21 +155,6 @@ func (o *OCRSoakTest) DeployEnvironment(customChainlinkNetworkTOML string, ocrTe o.namespace = testEnvironment.Cfg.Namespace } -// LoadEnvironment loads an existing test environment using the provided URLs -func (o *OCRSoakTest) LoadEnvironment(chainlinkURLs []string, mockServerURL string, ocrTestConfig tt.OcrTestConfig) { - var ( - network = networks.MustGetSelectedNetworkConfig(ocrTestConfig.GetNetworkConfig())[0] - err error - ) - o.chainClient, err = blockchain.ConnectEVMClient(network, o.log) - require.NoError(o.t, err, "Error connecting to EVM client") - chainlinkNodes, err := client.ConnectChainlinkNodeURLs(chainlinkURLs) - require.NoError(o.t, err, "Error connecting to chainlink nodes") - o.bootstrapNode, o.workerNodes = chainlinkNodes[0], chainlinkNodes[1:] - o.mockServer, err = ctfClient.ConnectMockServerURL(mockServerURL) - require.NoError(o.t, err, "Error connecting to mockserver") -} - // Environment returns the full K8s test environment func (o *OCRSoakTest) Environment() *environment.Environment { return o.testEnvironment @@ -178,89 +166,102 @@ func (o *OCRSoakTest) Setup(ocrTestConfig tt.OcrTestConfig) { network = networks.MustGetSelectedNetworkConfig(ocrTestConfig.GetNetworkConfig())[0] ) - // Environment currently being used to soak test on - // Make connections to soak test resources - o.chainClient, err = blockchain.NewEVMClient(network, o.testEnvironment, o.log) - require.NoError(o.t, err, "Error creating EVM client") - contractDeployer, err := contracts.NewContractDeployer(o.chainClient, o.log) - require.NoError(o.t, err, "Unable to create contract deployer") - require.NotNil(o.t, contractDeployer, "Contract deployer shouldn't be nil") + network = utils.MustReplaceSimulatedNetworkUrlWithK8(o.log, network, *o.testEnvironment) + readSethCfg := ocrTestConfig.GetSethConfig() + require.NotNil(o.t, readSethCfg, "Seth config shouldn't be nil") + + sethCfg := utils.MergeSethAndEvmNetworkConfigs(o.log, network, *readSethCfg) + + seth, err := seth.NewClientWithConfig(&sethCfg) + require.NoError(o.t, err, "Error creating seth client") + + o.seth = seth + nodes, err := client.ConnectChainlinkNodes(o.testEnvironment) require.NoError(o.t, err, "Connecting to chainlink nodes shouldn't fail") o.bootstrapNode, o.workerNodes = nodes[0], nodes[1:] o.mockServer, err = ctfClient.ConnectMockServer(o.testEnvironment) require.NoError(o.t, err, "Creating mockserver clients shouldn't fail") - o.chainClient.ParallelTransactions(true) + // Deploy LINK - linkTokenContract, err := contractDeployer.DeployLinkTokenContract() - require.NoError(o.t, err, "Deploying Link Token Contract shouldn't fail") + linkDeploymentData, err := contracts.DeployLinkTokenContract(seth) + require.NoError(o.t, err, "Error deploying LINK contract") // Fund Chainlink nodes, excluding the bootstrap node o.log.Info().Float64("ETH amount per node", *o.Config.Common.ChainlinkNodeFunding).Msg("Funding Chainlink nodes") - err = actions.FundChainlinkNodes(o.workerNodes, o.chainClient, big.NewFloat(*o.Config.Common.ChainlinkNodeFunding)) + err = actions_seth.FundChainlinkNodesFromRootAddress(o.log, seth, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(o.workerNodes), big.NewFloat(*o.Config.Common.ChainlinkNodeFunding)) require.NoError(o.t, err, "Error funding Chainlink nodes") - if o.OperatorForwarderFlow { - contractLoader, err := contracts.NewContractLoader(o.chainClient, o.log) - require.NoError(o.t, err, "Loading contracts shouldn't fail") + var forwarders []common.Address - operators, authorizedForwarders, _ := actions.DeployForwarderContracts( - o.t, contractDeployer, linkTokenContract, o.chainClient, len(o.workerNodes), + if o.OperatorForwarderFlow { + var operators []common.Address + operators, forwarders, _ = actions_seth.DeployForwarderContracts( + o.t, o.seth, linkDeploymentData, len(o.workerNodes), ) + require.Equal(o.t, len(o.workerNodes), len(operators), "Number of operators should match number of nodes") + require.Equal(o.t, len(o.workerNodes), len(forwarders), "Number of authorized forwarders should match number of nodes") forwarderNodesAddresses, err := actions.ChainlinkNodeAddresses(o.workerNodes) require.NoError(o.t, err, "Retrieving on-chain wallet addresses for chainlink nodes shouldn't fail") for i := range o.workerNodes { - actions.AcceptAuthorizedReceiversOperator( - o.t, operators[i], authorizedForwarders[i], []common.Address{forwarderNodesAddresses[i]}, o.chainClient, contractLoader, - ) + actions_seth.AcceptAuthorizedReceiversOperator( + o.t, o.log, o.seth, operators[i], forwarders[i], []common.Address{forwarderNodesAddresses[i]}) require.NoError(o.t, err, "Accepting Authorize Receivers on Operator shouldn't fail") - actions.TrackForwarder(o.t, o.chainClient, authorizedForwarders[i], o.workerNodes[i]) - err = o.chainClient.WaitForEvents() - } - o.ocrV1Instances = actions.DeployOCRContractsForwarderFlow( - o.t, - *o.Config.OCR.Soak.NumberOfContracts, - linkTokenContract, - contractDeployer, - o.workerNodes, - authorizedForwarders, - o.chainClient, - ) + actions_seth.TrackForwarder(o.t, o.seth, forwarders[i], o.workerNodes[i]) + } } else if *ocrTestConfig.GetOCRConfig().Soak.OCRVersion == "1" { - o.ocrV1Instances, err = actions.DeployOCRContracts( - *o.Config.OCR.Soak.NumberOfContracts, - linkTokenContract, - contractDeployer, - o.workerNodes, - o.chainClient, - ) - require.NoError(o.t, err) + if o.OperatorForwarderFlow { + o.ocrV1Instances, err = actions_seth.DeployOCRContractsForwarderFlow( + o.log, + o.seth, + *o.Config.OCR.Soak.NumberOfContracts, + linkDeploymentData.Address, + contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(o.workerNodes), + forwarders, + ) + require.NoError(o.t, err, "Error deploying OCR Forwarder contracts") + } else { + o.ocrV1Instances, err = actions_seth.DeployOCRv1Contracts( + o.log, + seth, + *o.Config.OCR.Soak.NumberOfContracts, + linkDeploymentData.Address, + contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(o.workerNodes), + ) + require.NoError(o.t, err) + } } else if *ocrTestConfig.GetOCRConfig().Soak.OCRVersion == "2" { var transmitters []string - for _, node := range o.workerNodes { - nodeAddress, err := node.PrimaryEthAddress() - require.NoError(o.t, err, "Error getting node's primary ETH address") - transmitters = append(transmitters, nodeAddress) + + if o.OperatorForwarderFlow { + for _, forwarder := range forwarders { + transmitters = append(transmitters, forwarder.Hex()) + } + } else { + for _, node := range o.workerNodes { + nodeAddress, err := node.PrimaryEthAddress() + require.NoError(o.t, err, "Error getting node's primary ETH address") + transmitters = append(transmitters, nodeAddress) + } } + ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - o.ocrV2Instances, err = actions.DeployOCRv2Contracts( + o.ocrV2Instances, err = actions_seth.DeployOCRv2Contracts( + o.log, + o.seth, *ocrTestConfig.GetOCRConfig().Soak.NumberOfContracts, - linkTokenContract, - contractDeployer, + linkDeploymentData.Address, transmitters, - o.chainClient, ocrOffchainOptions, ) require.NoError(o.t, err, "Error deploying OCRv2 contracts") contractConfig, err := actions.BuildMedianOCR2Config(o.workerNodes, ocrOffchainOptions) require.NoError(o.t, err, "Error building median config") - err = actions.ConfigureOCRv2AggregatorContracts(o.chainClient, contractConfig, o.ocrV2Instances) + err = actions_seth.ConfigureOCRv2AggregatorContracts(contractConfig, o.ocrV2Instances) require.NoError(o.t, err, "Error configuring OCRv2 aggregator contracts") } - err = o.chainClient.WaitForEvents() - require.NoError(o.t, err, "Error waiting for OCR contracts to be deployed") if *ocrTestConfig.GetOCRConfig().Soak.OCRVersion == "1" { for _, ocrInstance := range o.ocrV1Instances { o.ocrV1InstanceMap[ocrInstance.Address()] = ocrInstance @@ -280,19 +281,23 @@ func (o *OCRSoakTest) Run() { require.NoError(o.t, err, "Error getting config") ctx, cancel := context.WithTimeout(testcontext.Get(o.t), time.Second*5) - latestBlockNum, err := o.chainClient.LatestBlockNumber(ctx) + latestBlockNum, err := o.seth.Client.BlockNumber(ctx) cancel() require.NoError(o.t, err, "Error getting current block number") o.startingBlockNum = latestBlockNum startingValue := 5 if o.OperatorForwarderFlow { - actions.CreateOCRJobsWithForwarder(o.t, o.ocrV1Instances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, o.chainClient.GetChainID().String()) + actions.CreateOCRJobsWithForwarder(o.t, o.ocrV1Instances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, o.seth.ChainID) } else if *config.OCR.Soak.OCRVersion == "1" { - err := actions.CreateOCRJobs(o.ocrV1Instances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, o.chainClient.GetChainID().String()) + ctx, cancel := context.WithTimeout(testcontext.Get(o.t), time.Second*5) + chainId, err := o.seth.Client.ChainID(ctx) + cancel() + require.NoError(o.t, err, "Error getting chain ID") + err = actions.CreateOCRJobs(o.ocrV1Instances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, chainId.String()) require.NoError(o.t, err, "Error creating OCR jobs") } else if *config.OCR.Soak.OCRVersion == "2" { - err := actions.CreateOCRv2Jobs(o.ocrV2Instances, o.bootstrapNode, o.workerNodes, o.mockServer, startingValue, o.chainClient.GetChainID().Uint64(), o.OperatorForwarderFlow) + err := actions.CreateOCRv2Jobs(o.ocrV2Instances, o.bootstrapNode, o.workerNodes, o.mockServer, startingValue, o.seth.ChainID, o.OperatorForwarderFlow) require.NoError(o.t, err, "Error creating OCR jobs") } @@ -309,13 +314,13 @@ func (o *OCRSoakTest) Run() { // Networks returns the networks that the test is running on func (o *OCRSoakTest) TearDownVals(t *testing.T) ( *testing.T, + *seth.Client, string, []*client.ChainlinkK8sClient, reportModel.TestReporter, reportModel.GrafanaURLProvider, - blockchain.EVMClient, ) { - return t, o.namespace, append(o.workerNodes, o.bootstrapNode), &o.TestReporter, o.Config, o.chainClient + return t, o.seth, o.namespace, append(o.workerNodes, o.bootstrapNode), &o.TestReporter, o.Config } // ********************* @@ -359,7 +364,6 @@ func (o *OCRSoakTest) SaveState() error { OCRContractAddresses: ocrAddresses, OCRVersion: *o.Config.OCR.Soak.OCRVersion, - ChainURL: o.chainClient.GetNetworkConfig().URL, MockServerURL: "http://mockserver:1080", // TODO: Make this dynamic BootStrapNodeURL: o.bootstrapNode.URL(), WorkerNodeURLs: workerNodeURLs, @@ -415,15 +419,6 @@ func (o *OCRSoakTest) LoadState() error { o.startingBlockNum = testState.StartingBlockNum o.Config.OCR.Soak.OCRVersion = &testState.OCRVersion - network := networks.MustGetSelectedNetworkConfig(o.Config.Network)[0] - o.chainClient, err = blockchain.ConnectEVMClient(network, o.log) - if err != nil { - return err - } - contractDeployer, err := contracts.NewContractDeployer(o.chainClient, o.log) - if err != nil { - return err - } o.bootstrapNode, err = client.ConnectChainlinkNodeURL(testState.BootStrapNodeURL) if err != nil { return err @@ -436,22 +431,20 @@ func (o *OCRSoakTest) LoadState() error { if testState.OCRVersion == "1" { o.ocrV1Instances = make([]contracts.OffchainAggregator, len(testState.OCRContractAddresses)) for i, addr := range testState.OCRContractAddresses { - address := common.HexToAddress(addr) - instance, err := contractDeployer.LoadOffChainAggregator(&address) + instance, err := contracts.LoadOffchainAggregator(o.log, o.seth, common.HexToAddress(addr)) if err != nil { - return err + return fmt.Errorf("failed to instantiate OCR instance: %w", err) } - o.ocrV1Instances[i] = instance + o.ocrV1Instances[i] = &instance } } else if testState.OCRVersion == "2" { o.ocrV2Instances = make([]contracts.OffchainAggregatorV2, len(testState.OCRContractAddresses)) for i, addr := range testState.OCRContractAddresses { - address := common.HexToAddress(addr) - instance, err := contractDeployer.LoadOffChainAggregatorV2(&address) + instance, err := contracts.LoadOffChainAggregatorV2(o.log, o.seth, common.HexToAddress(addr)) if err != nil { return err } - o.ocrV2Instances[i] = instance + o.ocrV2Instances[i] = &instance } } @@ -565,16 +558,6 @@ func (o *OCRSoakTest) testLoop(testDuration time.Duration, newValue int) { newValue = rand.Intn(256) + 1 // #nosec G404 - kudos to you if you actually find a way to exploit this } lastValue = newValue - case t := <-o.chainClient.ConnectionIssue(): - o.testIssues = append(o.testIssues, &testreporters.TestIssue{ - StartTime: t, - Message: "RPC Connection Lost", - }) - case t := <-o.chainClient.ConnectionRestored(): - o.testIssues = append(o.testIssues, &testreporters.TestIssue{ - StartTime: t, - Message: "RPC Connection Restored", - }) } } } @@ -612,7 +595,7 @@ func (o *OCRSoakTest) setFilterQuery() { func (o *OCRSoakTest) observeOCREvents() error { eventLogs := make(chan types.Log) ctx, cancel := context.WithTimeout(testcontext.Get(o.t), 5*time.Second) - eventSub, err := o.chainClient.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) + eventSub, err := o.seth.Client.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) cancel() if err != nil { return err @@ -664,7 +647,7 @@ func (o *OCRSoakTest) observeOCREvents() error { Interface("Query", o.filterQuery). Msg("Error while subscribed to OCR Logs. Resubscribing") ctx, cancel = context.WithTimeout(testcontext.Get(o.t), backoff) - eventSub, err = o.chainClient.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) + eventSub, err = o.seth.Client.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) cancel() if err != nil { time.Sleep(backoff) @@ -729,12 +712,12 @@ func (o *OCRSoakTest) collectEvents() error { o.log.Info().Interface("Filter Query", o.filterQuery).Str("Timeout", timeout.String()).Msg("Retrieving on-chain events") ctx, cancel := context.WithTimeout(testcontext.Get(o.t), timeout) - contractEvents, err := o.chainClient.FilterLogs(ctx, o.filterQuery) + contractEvents, err := o.seth.Client.FilterLogs(ctx, o.filterQuery) cancel() for err != nil { o.log.Info().Interface("Filter Query", o.filterQuery).Str("Timeout", timeout.String()).Msg("Retrieving on-chain events") ctx, cancel := context.WithTimeout(testcontext.Get(o.t), timeout) - contractEvents, err = o.chainClient.FilterLogs(ctx, o.filterQuery) + contractEvents, err = o.seth.Client.FilterLogs(ctx, o.filterQuery) cancel() if err != nil { o.log.Warn().Interface("Filter Query", o.filterQuery).Str("Timeout", timeout.String()).Msg("Error collecting on-chain events, trying again") diff --git a/integration-tests/types/testconfigs.go b/integration-tests/types/testconfigs.go index 6eab6ec0678..cb36a1d3e8b 100644 --- a/integration-tests/types/testconfigs.go +++ b/integration-tests/types/testconfigs.go @@ -41,6 +41,7 @@ type OcrTestConfig interface { tc.GlobalTestConfig tc.CommonTestConfig tc.OcrTestConfig + tc.SethConfig } type Ocr2TestConfig interface { diff --git a/integration-tests/utils/seth.go b/integration-tests/utils/seth.go new file mode 100644 index 00000000000..adb63e4f9c2 --- /dev/null +++ b/integration-tests/utils/seth.go @@ -0,0 +1,84 @@ +package utils + +import ( + "fmt" + + "github.com/rs/zerolog" + "github.com/smartcontractkit/seth" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" +) + +// MergeSethAndEvmNetworkConfigs merges EVMNetwork to Seth config. If Seth config already has Network settings, +// it will return unchanged Seth config that was passed to it. If the network is simulated, it will +// use Geth-specific settings. Otherwise it will use the chain ID to find the correct network settings. +// If no match is found it will use default settings (currently based on Sepolia network settings). +func MergeSethAndEvmNetworkConfigs(l zerolog.Logger, evmNetwork blockchain.EVMNetwork, sethConfig seth.Config) seth.Config { + if sethConfig.Network != nil { + return sethConfig + } + + var sethNetwork *seth.Network + + for _, conf := range sethConfig.Networks { + if evmNetwork.Simulated { + if conf.Name == seth.GETH { + conf.PrivateKeys = evmNetwork.PrivateKeys + conf.URLs = evmNetwork.URLs + // important since Besu doesn't support EIP-1559, but other EVM clients do + conf.EIP1559DynamicFees = evmNetwork.SupportsEIP1559 + + sethNetwork = conf + break + } + } else if conf.ChainID == fmt.Sprint(evmNetwork.ChainID) { + conf.PrivateKeys = evmNetwork.PrivateKeys + conf.URLs = evmNetwork.URLs + + sethNetwork = conf + break + } + } + + if sethNetwork == nil { + //TODO in the future we could run gas estimator here + l.Warn(). + Int64("chainID", evmNetwork.ChainID). + Msg("Could not find any Seth network settings for chain ID. Using default network settings") + sethNetwork = &seth.Network{} + sethNetwork.PrivateKeys = evmNetwork.PrivateKeys + sethNetwork.URLs = evmNetwork.URLs + sethNetwork.EIP1559DynamicFees = evmNetwork.SupportsEIP1559 + sethNetwork.ChainID = fmt.Sprint(evmNetwork.ChainID) + // Sepolia settings + sethNetwork.GasLimit = 14_000_000 + sethNetwork.GasPrice = 1_000_000_000 + sethNetwork.GasFeeCap = 25_000_000_000 + sethNetwork.GasTipCap = 5_000_000_000 + sethNetwork.TransferGasFee = 21_000 + sethNetwork.TxnTimeout = seth.MustMakeDuration(evmNetwork.Timeout.Duration) + } + + sethConfig.Network = sethNetwork + + return sethConfig +} + +// MustReplaceSimulatedNetworkUrlWithK8 replaces the simulated network URL with the K8 URL and returns the network. +// If the network is not simulated, it will return the network unchanged. +func MustReplaceSimulatedNetworkUrlWithK8(l zerolog.Logger, network blockchain.EVMNetwork, testEnvironment environment.Environment) blockchain.EVMNetwork { + if !network.Simulated { + return network + } + + if _, ok := testEnvironment.URLs["Simulated Geth"]; !ok { + for k := range testEnvironment.URLs { + l.Info().Str("Network", k).Msg("Available networks") + } + panic("no network settings for Simulated Geth") + } + network.URLs = testEnvironment.URLs["Simulated Geth"] + + return network +} diff --git a/sonar-project.properties b/sonar-project.properties index 7ae3d52c3a5..80f0ae7601b 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -47,13 +47,18 @@ sonar.coverage.exclusions=\ **/0195_add_not_null_to_evm_chain_id_in_job_specs.go # Duplication exclusions: mercury excluded because current MercuryProvider and Factory APIs are inherently duplicated due to embedded versioning +# Ethereum contracts were added temporarily, since until we fully migrate we will have parallel helpers that use Seth and since it's temporary +# it doesn't make sense to fight with some cyclic imports and similar issues and it's easier to have copy of some functions in both places sonar.cpd.exclusions=\ **/contracts/**/*.sol,\ **/config.go,\ **/core/services/ocr2/plugins/ocr2keeper/evm/**/*,\ **/core/services/ocr2/plugins/mercury/plugin.go,\ **/integration-tests/load/**/*,\ -**/integration-tests/contracts/ethereum_keeper_contracts.go +**/integration-tests/contracts/ethereum_keeper_contracts.go,\ +integration-tests/contracts/ethereum_contracts_seth.go,\ +integration-tests/contracts/ethereum_contracts_seth.go,\ +integration-tests/actions/seth/actions.go # Tests' root folder, inclusions (tests to check and count) and exclusions sonar.tests=. From c033e0242f2b8600078f2ab7de6dafc09ec55cde Mon Sep 17 00:00:00 2001 From: Clement Erena Date: Wed, 28 Feb 2024 18:43:34 +0100 Subject: [PATCH 13/16] feat(dashboard): generate dashboard depending on platform (#11996) * feat(dashboard): generate dashboard depending on platform * feat(dashboard): depending on platform env var variables and panels adapts * feat(dashboard): add env var to select panels to be included * feat(dashboard): delete old file * feat(dashboard): add panels for solana balance + go metrics panels * feat(dashboard): prepare wasp integration * feat(dashboard): add wasp integration * feat(dashboard): use new wasp version --------- Co-authored-by: Sergey Kudasov --- .../dashboard/cmd/dashboard_deploy.go | 74 +- .../chainlink-cluster/dashboard/dashboard.go | 1041 +------- charts/chainlink-cluster/dashboard/panels.go | 1773 ++++++++++++++ charts/chainlink-cluster/dashboard/utils.go | 10 + charts/chainlink-cluster/go.mod | 164 +- charts/chainlink-cluster/go.sum | 2130 +---------------- 6 files changed, 1974 insertions(+), 3218 deletions(-) create mode 100644 charts/chainlink-cluster/dashboard/panels.go create mode 100644 charts/chainlink-cluster/dashboard/utils.go diff --git a/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go b/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go index b65be29501d..a818ff44095 100644 --- a/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go +++ b/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go @@ -1,10 +1,10 @@ package main import ( - "os" - + "fmt" "github.com/smartcontractkit/chainlink/charts/chainlink-cluster/dashboard/dashboard" - "github.com/smartcontractkit/wasp" + "os" + "strings" ) func main() { @@ -12,38 +12,64 @@ func main() { if name == "" { panic("DASHBOARD_NAME must be provided") } - ldsn := os.Getenv("LOKI_DATA_SOURCE_NAME") - if ldsn == "" { - panic("DATA_SOURCE_NAME must be provided") - } - os.Setenv("DATA_SOURCE_NAME", ldsn) - pdsn := os.Getenv("PROMETHEUS_DATA_SOURCE_NAME") - if ldsn == "" { - panic("DATA_SOURCE_NAME must be provided") + + lokiDataSourceName := os.Getenv("LOKI_DATA_SOURCE_NAME") + if lokiDataSourceName == "" { + fmt.Println("LOKI_DATA_SOURCE_NAME is empty, panels with logs will be disabled") } - dbf := os.Getenv("DASHBOARD_FOLDER") - if dbf == "" { - panic("DASHBOARD_FOLDER must be provided") + + prometheusDataSourceName := os.Getenv("PROMETHEUS_DATA_SOURCE_NAME") + if prometheusDataSourceName == "" { + panic("PROMETHEUS_DATA_SOURCE_NAME must be provided") } + grafanaURL := os.Getenv("GRAFANA_URL") if grafanaURL == "" { panic("GRAFANA_URL must be provided") } + grafanaToken := os.Getenv("GRAFANA_TOKEN") if grafanaToken == "" { panic("GRAFANA_TOKEN must be provided") } - // if you'll use this dashboard base in other projects, you can add your own opts here to extend it - db, err := dashboard.NewCLClusterDashboard(6, name, ldsn, pdsn, dbf, grafanaURL, grafanaToken, nil) - if err != nil { - panic(err) + + grafanaFolder := os.Getenv("GRAFANA_FOLDER") + if grafanaFolder == "" { + panic("GRAFANA_FOLDER must be provided") } - // here we are extending load testing dashboard with core metrics, for example - wdb, err := wasp.NewDashboard(nil, db.Opts()) - if err != nil { - panic(err) + + infraPlatform := os.Getenv("INFRA_PLATFORM") + if infraPlatform == "" { + panic("INFRA_PLATFORM must be provided, can be either docker|kubernetes") } - if _, err := wdb.Deploy(); err != nil { - panic(err) + + panelsIncluded := os.Getenv("PANELS_INCLUDED") + // can be empty + if panelsIncluded == "" { + fmt.Println("PANELS_INCLUDED can be provided to specify panels groups, value must be separated by comma. Possible values are: core, wasp") + } + panelsIncludedArray := strings.Split(panelsIncluded, ",") + + err := dashboard.NewDashboard( + name, + grafanaURL, + grafanaToken, + grafanaFolder, + []string{"generated"}, + lokiDataSourceName, + prometheusDataSourceName, + infraPlatform, + panelsIncludedArray, + nil, + ) + if err != nil { + fmt.Printf("Could not create dashbard: %s\n", name) + fmt.Printf("Error: %s\n", err) + os.Exit(1) } + fmt.Printf("Successfully deployed %s dashboard on grafana instance %s in folder %s\n", + name, + grafanaURL, + grafanaFolder, + ) } diff --git a/charts/chainlink-cluster/dashboard/dashboard.go b/charts/chainlink-cluster/dashboard/dashboard.go index 54b59ae25e6..bc5fa51cc31 100644 --- a/charts/chainlink-cluster/dashboard/dashboard.go +++ b/charts/chainlink-cluster/dashboard/dashboard.go @@ -3,306 +3,159 @@ package dashboard import ( "context" "fmt" - "net/http" - "github.com/K-Phoen/grabana" "github.com/K-Phoen/grabana/dashboard" - "github.com/K-Phoen/grabana/logs" - "github.com/K-Phoen/grabana/row" - "github.com/K-Phoen/grabana/stat" - "github.com/K-Phoen/grabana/target/prometheus" - "github.com/K-Phoen/grabana/timeseries" - "github.com/K-Phoen/grabana/timeseries/axis" "github.com/K-Phoen/grabana/variable/query" - pkgerrors "github.com/pkg/errors" + wasp "github.com/smartcontractkit/wasp/dashboard" + "net/http" + "os" ) -/* -Use ripgrep to get the full list -rg -oU ".*promauto.*\n.*Name: \"(.*)\"" -r '$1' > metrics.txt - -duplicates? - -common/client/node.go:pool_rpc_node_verifies -common/client/node.go:pool_rpc_node_verifies_failed -common/client/node.go:pool_rpc_node_verifies_success -common/client/node_fsm.go:pool_rpc_node_num_transitions_to_alive -common/client/node_fsm.go:pool_rpc_node_num_transitions_to_in_sync -common/client/node_fsm.go:pool_rpc_node_num_transitions_to_out_of_sync -common/client/node_fsm.go:pool_rpc_node_num_transitions_to_unreachable -common/client/node_fsm.go:pool_rpc_node_num_transitions_to_invalid_chain_id -common/client/node_fsm.go:pool_rpc_node_num_transitions_to_unusable -common/client/node_lifecycle.go:pool_rpc_node_highest_seen_block -common/client/node_lifecycle.go:pool_rpc_node_num_seen_blocks -common/client/node_lifecycle.go:pool_rpc_node_polls_total -common/client/node_lifecycle.go:pool_rpc_node_polls_failed -common/client/node_lifecycle.go:pool_rpc_node_polls_success - -covered - -core/logger/prometheus.go:log_warn_count -core/logger/prometheus.go:log_error_count -core/logger/prometheus.go:log_critical_count -core/logger/prometheus.go:log_panic_count -core/logger/prometheus.go:log_fatal_count -common/client/multi_node.go:multi_node_states -common/txmgr/broadcaster.go:tx_manager_time_until_tx_broadcast -common/txmgr/confirmer.go:tx_manager_num_gas_bumps -common/txmgr/confirmer.go:tx_manager_gas_bump_exceeds_limit -common/txmgr/confirmer.go:tx_manager_num_confirmed_transactions -common/txmgr/confirmer.go:tx_manager_num_successful_transactions -common/txmgr/confirmer.go:tx_manager_num_tx_reverted -common/txmgr/confirmer.go:tx_manager_fwd_tx_count -common/txmgr/confirmer.go:tx_manager_tx_attempt_count -common/txmgr/confirmer.go:tx_manager_time_until_tx_confirmed -common/txmgr/confirmer.go:tx_manager_blocks_until_tx_confirmed -common/headtracker/head_tracker.go:head_tracker_current_head -common/headtracker/head_tracker.go:head_tracker_very_old_head -common/headtracker/head_listener.go:head_tracker_heads_received -common/headtracker/head_listener.go:head_tracker_connection_errors -core/chains/evm/client/node_fsm.go:evm_pool_rpc_node_num_transitions_to_alive -core/chains/evm/client/node_fsm.go:evm_pool_rpc_node_num_transitions_to_in_sync -core/chains/evm/client/node_fsm.go:evm_pool_rpc_node_num_transitions_to_out_of_sync -core/chains/evm/client/node_fsm.go:evm_pool_rpc_node_num_transitions_to_unreachable -core/chains/evm/client/node_fsm.go:evm_pool_rpc_node_num_transitions_to_invalid_chain_id -core/chains/evm/client/node_fsm.go:evm_pool_rpc_node_num_transitions_to_unusable -core/services/promreporter/prom_reporter.go:unconfirmed_transactions -core/services/promreporter/prom_reporter.go:max_unconfirmed_tx_age -core/services/promreporter/prom_reporter.go:max_unconfirmed_blocks -core/services/promreporter/prom_reporter.go:pipeline_runs_queued -core/services/promreporter/prom_reporter.go:pipeline_task_runs_queued -core/services/pipeline/task.bridge.go:bridge_latency_seconds -core/services/pipeline/task.bridge.go:bridge_errors_total -core/services/pipeline/task.bridge.go:bridge_cache_hits_total -core/services/pipeline/task.bridge.go:bridge_cache_errors_total -core/services/pipeline/task.http.go:pipeline_task_http_fetch_time -core/services/pipeline/task.http.go:pipeline_task_http_response_body_size -core/services/pipeline/task.eth_call.go:pipeline_task_eth_call_execution_time -core/services/pipeline/runner.go:pipeline_task_execution_time -core/services/pipeline/runner.go:pipeline_run_errors -core/services/pipeline/runner.go:pipeline_run_total_time_to_completion -core/services/pipeline/runner.go:pipeline_tasks_total_finished -core/chains/evm/client/node.go:evm_pool_rpc_node_dials_total -core/chains/evm/client/node.go:evm_pool_rpc_node_dials_failed -core/chains/evm/client/node.go:evm_pool_rpc_node_dials_success -core/chains/evm/client/node.go:evm_pool_rpc_node_verifies -core/chains/evm/client/node.go:evm_pool_rpc_node_verifies_failed -core/chains/evm/client/node.go:evm_pool_rpc_node_verifies_success -core/chains/evm/client/node.go:evm_pool_rpc_node_calls_total -core/chains/evm/client/node.go:evm_pool_rpc_node_calls_failed -core/chains/evm/client/node.go:evm_pool_rpc_node_calls_success -core/chains/evm/client/node.go:evm_pool_rpc_node_rpc_call_time -core/chains/evm/client/pool.go:evm_pool_rpc_node_states -core/utils/mailbox_prom.go:mailbox_load_percent -core/services/pg/stats.go:db_conns_max -core/services/pg/stats.go:db_conns_open -core/services/pg/stats.go:db_conns_used -core/services/pg/stats.go:db_wait_count -core/services/pg/stats.go:db_wait_time_seconds -core/chains/evm/client/node_lifecycle.go:evm_pool_rpc_node_highest_seen_block -core/chains/evm/client/node_lifecycle.go:evm_pool_rpc_node_num_seen_blocks -core/chains/evm/client/node_lifecycle.go:evm_pool_rpc_node_polls_total -core/chains/evm/client/node_lifecycle.go:evm_pool_rpc_node_polls_failed -core/chains/evm/client/node_lifecycle.go:evm_pool_rpc_node_polls_success -core/services/relay/evm/config_poller.go:ocr2_failed_rpc_contract_calls -core/services/feeds/service.go:feeds_job_proposal_requests -core/services/feeds/service.go:feeds_job_proposal_count -core/services/ocrcommon/prom.go:bridge_json_parse_values -core/services/ocrcommon/prom.go:ocr_median_values -core/chains/evm/logpoller/observability.go:log_poller_query_dataset_size - -not-covered and product specific (definitions/usage should be moved to plugins) - -mercury - -core/services/relay/evm/mercury/types/types.go:mercury_price_feed_missing -core/services/relay/evm/mercury/types/types.go:mercury_price_feed_errors -core/services/relay/evm/mercury/queue.go:mercury_transmit_queue_load -core/services/relay/evm/mercury/v1/data_source.go:mercury_insufficient_blocks_count -core/services/relay/evm/mercury/v1/data_source.go:mercury_zero_blocks_count -core/services/relay/evm/mercury/wsrpc/client.go:mercury_transmit_timeout_count -core/services/relay/evm/mercury/wsrpc/client.go:mercury_dial_count -core/services/relay/evm/mercury/wsrpc/client.go:mercury_dial_success_count -core/services/relay/evm/mercury/wsrpc/client.go:mercury_dial_error_count -core/services/relay/evm/mercury/wsrpc/client.go:mercury_connection_reset_count -core/services/relay/evm/mercury/transmitter.go:mercury_transmit_success_count -core/services/relay/evm/mercury/transmitter.go:mercury_transmit_duplicate_count -core/services/relay/evm/mercury/transmitter.go:mercury_transmit_connection_error_count -core/services/relay/evm/mercury/transmitter.go:mercury_transmit_queue_delete_error_count -core/services/relay/evm/mercury/transmitter.go:mercury_transmit_queue_insert_error_count -core/services/relay/evm/mercury/transmitter.go:mercury_transmit_queue_push_error_count -core/services/relay/evm/mercury/transmitter.go:mercury_transmit_server_error_count - -functions - -core/services/gateway/connectionmanager.go:gateway_heartbeats_sent -core/services/gateway/gateway.go:gateway_request -core/services/gateway/handlers/functions/handler.functions.go:gateway_functions_handler_error -core/services/gateway/handlers/functions/handler.functions.go:gateway_functions_secrets_set_success -core/services/gateway/handlers/functions/handler.functions.go:gateway_functions_secrets_set_failure -core/services/gateway/handlers/functions/handler.functions.go:gateway_functions_secrets_list_success -core/services/gateway/handlers/functions/handler.functions.go:gateway_functions_secrets_list_failure -core/services/functions/external_adapter_client.go:functions_external_adapter_client_latency -core/services/functions/external_adapter_client.go:functions_external_adapter_client_errors_total -core/services/functions/listener.go:functions_request_received -core/services/functions/listener.go:functions_request_internal_error -core/services/functions/listener.go:functions_request_computation_error -core/services/functions/listener.go:functions_request_computation_success -core/services/functions/listener.go:functions_request_timeout -core/services/functions/listener.go:functions_request_confirmed -core/services/functions/listener.go:functions_request_computation_result_size -core/services/functions/listener.go:functions_request_computation_error_size -core/services/functions/listener.go:functions_request_computation_duration -core/services/functions/listener.go:functions_request_pruned -core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_restarts -core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_query -core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_observation -core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_report -core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_report_num_observations -core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_accept -core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_transmit -core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_query -core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_observation -core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_report -core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_accept -core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_query_byte_size -core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_query_rows_count -core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_observation_rows_count -core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_report_rows_count -core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_wrong_sig_count -core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_expired_rows - -vrf - -core/services/vrf/vrfcommon/metrics.go:vrf_request_queue_size -core/services/vrf/vrfcommon/metrics.go:vrf_processed_request_count -core/services/vrf/vrfcommon/metrics.go:vrf_dropped_request_count -core/services/vrf/vrfcommon/metrics.go:vrf_duplicate_requests -core/services/vrf/vrfcommon/metrics.go:vrf_request_time_between_sims -core/services/vrf/vrfcommon/metrics.go:vrf_request_time_until_initial_sim - -keeper -core/services/keeper/upkeep_executer.go:keeper_check_upkeep_execution_time -*/ - -const ( - ErrFailedToCreateDashboard = "failed to create dashboard" - ErrFailedToCreateFolder = "failed to create folder" -) +type PanelOption struct { + labelFilter string +} -// CLClusterDashboard is a dashboard for a Chainlink cluster -type CLClusterDashboard struct { - Nodes int +type Dashboard struct { Name string + grafanaURL string + grafanaToken string + grafanaFolder string + grafanaTags []string LokiDataSourceName string PrometheusDataSourceName string - Folder string - GrafanaURL string - GrafanaToken string + platform string + panels []string + panelOption PanelOption + Builder dashboard.Builder opts []dashboard.Option extendedOpts []dashboard.Option - builder dashboard.Builder } -// NewCLClusterDashboard returns a new dashboard for a Chainlink cluster, can be used as a base for more complex plugin based dashboards -func NewCLClusterDashboard(nodes int, name, ldsn, pdsn, dbf, grafanaURL, grafanaToken string, opts []dashboard.Option) (*CLClusterDashboard, error) { - db := &CLClusterDashboard{ - Nodes: nodes, +// NewDashboard returns a new Grafana dashboard, it comes empty and depending on user inputs panels are added to it +func NewDashboard( + name string, + grafanaURL string, + grafanaToken string, + grafanaFolder string, + grafanaTags []string, + lokiDataSourceName string, + prometheusDataSourceName string, + platform string, + panels []string, + extendedOpts []dashboard.Option, +) error { + db := &Dashboard{ Name: name, - Folder: dbf, - LokiDataSourceName: ldsn, - PrometheusDataSourceName: pdsn, - GrafanaURL: grafanaURL, - GrafanaToken: grafanaToken, - extendedOpts: opts, + grafanaURL: grafanaURL, + grafanaToken: grafanaToken, + grafanaFolder: grafanaFolder, + grafanaTags: grafanaTags, + LokiDataSourceName: lokiDataSourceName, + PrometheusDataSourceName: prometheusDataSourceName, + platform: platform, + panels: panels, + extendedOpts: extendedOpts, } - if err := db.generate(); err != nil { - return db, err + db.init() + db.addCoreVariables() + + if Contains(db.panels, "core") { + db.addCorePanels() } - return db, nil -} -func (m *CLClusterDashboard) Opts() []dashboard.Option { - return m.opts + switch db.platform { + case "kubernetes": + db.addKubernetesVariables() + db.addKubernetesPanels() + panelQuery := map[string]string{ + "branch": `=~"${branch:pipe}"`, + "commit": `=~"${commit:pipe}"`, + } + waspPanelsLoadStats := wasp.WASPLoadStatsRow(db.PrometheusDataSourceName, panelQuery) + waspPanelsDebugData := wasp.WASPDebugDataRow(db.PrometheusDataSourceName, panelQuery, false) + db.opts = append(db.opts, waspPanelsLoadStats) + db.opts = append(db.opts, waspPanelsDebugData) + break + } + + db.opts = append(db.opts, db.extendedOpts...) + err := db.deploy() + if err != nil { + os.Exit(1) + return err + } + return nil } -// logsRowOption returns a row option for a node's logs with name and instance selector -func (m *CLClusterDashboard) logsRowOption(name, q string) row.Option { - return row.WithLogs( - name, - logs.DataSource(m.LokiDataSourceName), - logs.Span(12), - logs.Height("300px"), - logs.Transparent(), - logs.WithLokiTarget(q), +func (m *Dashboard) deploy() error { + ctx := context.Background() + + builder, builderErr := dashboard.New( + m.Name, + m.opts..., ) -} + if builderErr != nil { + fmt.Printf("Could not build dashboard: %s\n", builderErr) + return builderErr + } -func (m *CLClusterDashboard) logsRowOptionsForNodes(nodes int) []row.Option { - opts := make([]row.Option, 0) - for i := 1; i <= nodes; i++ { - opts = append(opts, row.WithLogs( - fmt.Sprintf("Node %d", i), - logs.DataSource(m.LokiDataSourceName), - logs.Span(12), - logs.Height("300px"), - logs.Transparent(), - logs.WithLokiTarget(fmt.Sprintf(`{namespace="${namespace}", app="app", instance="node-%d", container="node"}`, i)), - )) + client := grabana.NewClient(&http.Client{}, m.grafanaURL, grabana.WithAPIToken(m.grafanaToken)) + fo, folderErr := client.FindOrCreateFolder(ctx, m.grafanaFolder) + if folderErr != nil { + fmt.Printf("Could not find or create folder: %s\n", folderErr) + return folderErr + } + if _, err := client.UpsertDashboard(ctx, fo, builder); err != nil { + fmt.Printf("Could not upsert dashboard: %s\n", err) + return err } - return opts + + return nil } -// timeseriesRowOption returns a row option for a timeseries with name, axis unit, query and legend template -func (m *CLClusterDashboard) timeseriesRowOption(name, axisUnit, query, legendTemplate string) row.Option { - var tsq timeseries.Option - if legendTemplate != "" { - tsq = timeseries.WithPrometheusTarget( - query, - prometheus.Legend(legendTemplate), - ) - } else { - tsq = timeseries.WithPrometheusTarget(query) +func (m *Dashboard) init() { + opts := []dashboard.Option{ + dashboard.AutoRefresh("10s"), + dashboard.Tags(m.grafanaTags), } - var au timeseries.Option - if axisUnit != "" { - au = timeseries.Axis( - axis.Unit(axisUnit), - ) - } else { - au = timeseries.Axis() + + switch m.platform { + case "kubernetes": + m.panelOption.labelFilter = "job" + break + case "docker": + m.panelOption.labelFilter = "instance" + break } - return row.WithTimeSeries( - name, - timeseries.Span(6), - timeseries.Height("300px"), - timeseries.DataSource(m.PrometheusDataSourceName), - au, - tsq, - ) + + m.opts = append(m.opts, opts...) } -// statRowOption returns a row option for a stat with name, prometheus target and legend template -func (m *CLClusterDashboard) statRowOption(name, target, legend string) row.Option { - return row.WithStat( - name, - stat.Transparent(), - stat.DataSource(m.PrometheusDataSourceName), - stat.Text(stat.TextValueAndName), - stat.Orientation(stat.OrientationVertical), - stat.TitleFontSize(12), - stat.ValueFontSize(20), - stat.Span(12), - stat.Height("100px"), - stat.WithPrometheusTarget(target, prometheus.Legend(legend)), - ) +func (m *Dashboard) addCoreVariables() { + opts := []dashboard.Option{ + dashboard.VariableAsQuery( + "instance", + query.DataSource(m.PrometheusDataSourceName), + query.Multiple(), + query.IncludeAll(), + query.Request(fmt.Sprintf("label_values(%s)", m.panelOption.labelFilter)), + query.Sort(query.NumericalAsc), + ), + dashboard.VariableAsQuery( + "evmChainID", + query.DataSource(m.PrometheusDataSourceName), + query.Multiple(), + query.IncludeAll(), + query.Request(fmt.Sprintf("label_values(%s)", "evmChainID")), + query.Sort(query.NumericalAsc), + ), + } + + m.opts = append(m.opts, opts...) } -// generate generates the dashboard, adding extendedOpts to the default options -func (m *CLClusterDashboard) generate() error { +func (m *Dashboard) addKubernetesVariables() { opts := []dashboard.Option{ - dashboard.AutoRefresh("10s"), - dashboard.Tags([]string{"generated"}), dashboard.VariableAsQuery( "namespace", query.DataSource(m.LokiDataSourceName), @@ -311,653 +164,9 @@ func (m *CLClusterDashboard) generate() error { query.Request(fmt.Sprintf("label_values(%s)", "namespace")), query.Sort(query.NumericalAsc), ), - dashboard.Row( - "Cluster health", - row.Collapse(), - m.statRowOption( - "App Version", - `version{namespace="${namespace}"}`, - "{{pod}} - {{version}}", - ), - row.WithTimeSeries( - "Restarts", - timeseries.Span(12), - timeseries.Height("200px"), - timeseries.DataSource(m.PrometheusDataSourceName), - timeseries.WithPrometheusTarget( - `sum(increase(kube_pod_container_status_restarts_total{namespace=~"${namespace}"}[5m])) by (pod)`, - prometheus.Legend("{{pod}}"), - ), - ), - row.WithTimeSeries( - "Service Components Health", - timeseries.Span(12), - timeseries.Height("200px"), - timeseries.DataSource(m.PrometheusDataSourceName), - timeseries.WithPrometheusTarget( - `health{namespace="${namespace}"}`, - prometheus.Legend("{{pod}} - {{service_id}}"), - ), - ), - row.WithTimeSeries( - "ETH Balance", - timeseries.Span(12), - timeseries.Height("200px"), - timeseries.DataSource(m.PrometheusDataSourceName), - timeseries.WithPrometheusTarget( - `eth_balance{namespace="${namespace}"}`, - prometheus.Legend("{{pod}} - {{account}}"), - ), - ), - ), - // HeadTracker - dashboard.Row("Head tracker", - row.Collapse(), - m.timeseriesRowOption( - "Head tracker current head", - "Block", - `head_tracker_current_head{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "Head tracker very old head", - "Block", - `head_tracker_very_old_head{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "Head tracker heads received", - "Block", - `head_tracker_heads_received{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "Head tracker connection errors", - "Errors", - `head_tracker_connection_errors{namespace="${namespace}"}`, - "{{ pod }}", - ), - ), - dashboard.Row("LogPoller", - row.Collapse(), - m.timeseriesRowOption( - "LogPoller Query Dataset Size", - "", - `log_poller_query_dataset_size{namespace="${namespace}"}`, - "{{ pod }}", - ), - ), - dashboard.Row("OCRCommon", - row.Collapse(), - m.timeseriesRowOption( - "Bridge JSON Parse Values", - "", - `bridge_json_parse_values{namespace="${namespace}"}`, - "{{ pod }} JobID: {{ job_id }}", - ), - m.timeseriesRowOption( - "OCR Median Values", - "", - `ocr_median_values{namespace="${namespace}"}`, - "{{pod}} JobID: {{ job_id }}", - ), - ), - dashboard.Row("Relay Config Poller", - row.Collapse(), - m.timeseriesRowOption( - "Relay Config Poller RPC Contract Calls", - "", - `ocr2_failed_rpc_contract_calls{namespace="${namespace}"}`, - "{{ pod }}", - ), - ), - dashboard.Row("Feeds Jobs", - row.Collapse(), - m.timeseriesRowOption( - "Feeds Job Proposal Requests", - "", - `feeds_job_proposal_requests{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "Feeds Job Proposal Count", - "", - `feeds_job_proposal_count{namespace="${namespace}"}`, - "{{ pod }}", - ), - ), - dashboard.Row("Mailbox", - row.Collapse(), - m.timeseriesRowOption( - "Mailbox Load Percent", - "", - `mailbox_load_percent{namespace="${namespace}"}`, - "{{ pod }} {{ name }}", - ), - ), - dashboard.Row("Multi Node States", - row.Collapse(), - m.timeseriesRowOption( - "Multi Node States", - "", - `multi_node_states{namespace="${namespace}"}`, - "{{ pod }}", - ), - ), - dashboard.Row("Block History Estimator", - row.Collapse(), - m.timeseriesRowOption( - "Gas Updater All Gas Price Percentiles", - "", - `gas_updater_all_gas_price_percentiles{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "Gas Updater All Tip Cap Percentiles", - "", - `gas_updater_all_tip_cap_percentiles{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "Gas Updater Set Gas Price", - "", - `gas_updater_set_gas_price{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "Gas Updater Set Tip Cap", - "", - `gas_updater_set_tip_cap{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "Gas Updater Current Base Fee", - "", - `gas_updater_current_base_fee{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "Block History Estimator Connectivity Failure Count", - "", - `block_history_estimator_connectivity_failure_count{namespace="${namespace}"}`, - "{{ pod }}", - ), - ), - // PromReporter - dashboard.Row("Prom Reporter", - row.Collapse(), - m.timeseriesRowOption( - "Unconfirmed Transactions", - "Tx", - `unconfirmed_transactions{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "Unconfirmed TX Age", - "Sec", - `max_unconfirmed_tx_age{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "Unconfirmed TX Blocks", - "Blocks", - `max_unconfirmed_blocks{namespace="${namespace}"}`, - "{{ pod }}", - ), - ), - dashboard.Row("TX Manager", - row.Collapse(), - m.timeseriesRowOption( - "TX Manager Time Until TX Broadcast", - "", - `tx_manager_time_until_tx_broadcast{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "TX Manager Num Gas Bumps", - "", - `tx_manager_num_gas_bumps{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "TX Manager Num Gas Bumps Exceeds Limit", - "", - `tx_manager_gas_bump_exceeds_limit{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "TX Manager Num Confirmed Transactions", - "", - `tx_manager_num_confirmed_transactions{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "TX Manager Num Successful Transactions", - "", - `tx_manager_num_successful_transactions{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "TX Manager Num Reverted Transactions", - "", - `tx_manager_num_tx_reverted{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "TX Manager Num Fwd Transactions", - "", - `tx_manager_fwd_tx_count{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "TX Manager Num Transactions Attempts", - "", - `tx_manager_tx_attempt_count{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "TX Manager Time Until TX Confirmed", - "", - `tx_manager_time_until_tx_confirmed{namespace="${namespace}"}`, - "{{ pod }}", - ), - m.timeseriesRowOption( - "TX Manager Block Until TX Confirmed", - "", - `tx_manager_blocks_until_tx_confirmed{namespace="${namespace}"}`, - "{{ pod }}", - ), - ), - // DON report metrics - dashboard.Row("DON Report metrics", - row.Collapse(), - m.timeseriesRowOption( - "Plugin Query() count", - "Count", - `sum(rate(ocr2_reporting_plugin_query_count{namespace="${namespace}", app="app"}[$__rate_interval])) by (service)`, - "", - ), - m.timeseriesRowOption( - "Plugin Observation() time (95th)", - "Sec", - `histogram_quantile(0.95, sum(rate(ocr2_reporting_plugin_observation_time_bucket{namespace="${namespace}", app="app"}[$__rate_interval])) by (le, service)) / 1e9`, - "", - ), - m.timeseriesRowOption( - "Plugin ShouldAcceptReport() time (95th)", - "Sec", - `histogram_quantile(0.95, sum(rate(ocr2_reporting_plugin_should_accept_report_time_bucket{namespace="${namespace}", app="app"}[$__rate_interval])) by (le, service)) / 1e9`, - "", - ), - m.timeseriesRowOption( - "Plugin Report() time (95th)", - "Sec", - `histogram_quantile(0.95, sum(rate(ocr2_reporting_plugin_report_time_bucket{namespace="${namespace}", app="app"}[$__rate_interval])) by (le, service)) / 1e9`, - "", - ), - m.timeseriesRowOption( - "Plugin ShouldTransmitReport() time (95th)", - "Sec", - `histogram_quantile(0.95, sum(rate(ocr2_reporting_plugin_should_transmit_report_time_bucket{namespace="${namespace}", app="app"}[$__rate_interval])) by (le, service)) / 1e9`, - "", - ), - ), - dashboard.Row( - "EVM Pool Lifecycle", - row.Collapse(), - m.timeseriesRowOption( - "EVM Pool Highest Seen Block", - "Block", - `evm_pool_rpc_node_highest_seen_block{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool Num Seen Blocks", - "Block", - `evm_pool_rpc_node_num_seen_blocks{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool Node Polls Total", - "Block", - `evm_pool_rpc_node_polls_total{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool Node Polls Failed", - "Block", - `evm_pool_rpc_node_polls_failed{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool Node Polls Success", - "Block", - `evm_pool_rpc_node_polls_success{namespace="${namespace}"}`, - "{{pod}}", - ), - ), - dashboard.Row( - "DB Connection Metrics (App)", - row.Collapse(), - m.timeseriesRowOption( - "DB Connections MAX", - "Conn", - `db_conns_max{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "DB Connections Open", - "Conn", - `db_conns_open{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "DB Connections Used", - "Conn", - `db_conns_used{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "DB Connections Wait", - "Conn", - `db_conns_wait{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "DB Wait Count", - "", - `db_wait_count{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "DB Wait time", - "Sec", - `db_wait_time_seconds{namespace="${namespace}"}`, - "{{pod}}", - ), - ), - dashboard.Row( - "EVM Pool RPC Node Metrics (App)", - row.Collapse(), - m.timeseriesRowOption( - "EVM Pool RPC Node Calls Success", - "", - `evm_pool_rpc_node_calls_success{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Node Calls Total", - "", - `evm_pool_rpc_node_calls_total{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Node Dials Success", - "", - `evm_pool_rpc_node_dials_success{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Node Dials Failed", - "", - `evm_pool_rpc_node_dials_failed{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Node Dials Total", - "", - `evm_pool_rpc_node_dials_total{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Node Dials Failed", - "", - `evm_pool_rpc_node_dials_failed{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Total Transitions to Alive", - "", - `evm_pool_rpc_node_num_transitions_to_alive{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Total Transitions to In Sync", - "", - `evm_pool_rpc_node_num_transitions_to_in_sync{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Total Transitions to Out of Sync", - "", - `evm_pool_rpc_node_num_transitions_to_out_of_sync{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Total Transitions to Unreachable", - "", - `evm_pool_rpc_node_num_transitions_to_unreachable{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Total Transitions to invalid Chain ID", - "", - `evm_pool_rpc_node_num_transitions_to_invalid_chain_id{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Total Transitions to unusable", - "", - `evm_pool_rpc_node_num_transitions_to_unusable{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Node Polls Success", - "", - `evm_pool_rpc_node_polls_success{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Node Polls Total", - "", - `evm_pool_rpc_node_polls_total{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Node States", - "", - `evm_pool_rpc_node_states{namespace="${namespace}"}`, - "{{pod}} - {{evmChainID}} - {{state}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Node Verifies Total", - "", - `evm_pool_rpc_node_verifies{namespace="${namespace}"}`, - "{{pod}} - {{evmChainID}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Node Verifies Success", - "", - `evm_pool_rpc_node_verifies_success{namespace="${namespace}"}`, - "{{pod}} - {{evmChainID}}", - ), - m.timeseriesRowOption( - "EVM Pool RPC Node Verifies Failed", - "", - `evm_pool_rpc_node_verifies_failed{namespace="${namespace}"}`, - "{{pod}} - {{evmChainID}}", - ), - ), - dashboard.Row( - "EVM Pool RPC Node Latencies (App)", - row.Collapse(), - m.timeseriesRowOption( - "EVM Pool RPC Node Calls Latency 0.95 quantile", - "ms", - `histogram_quantile(0.95, sum(rate(evm_pool_rpc_node_rpc_call_time_bucket{namespace="${namespace}"}[$__rate_interval])) by (le, rpcCallName)) / 1e6`, - "{{pod}}", - ), - ), - dashboard.Row( - "Pipeline Metrics (Runner)", - row.Collapse(), - m.timeseriesRowOption( - "Pipeline Task Execution Time", - "Sec", - `pipeline_task_execution_time{namespace="${namespace}"} / 1e6`, - "{{ pod }} JobID: {{ job_id }}", - ), - m.timeseriesRowOption( - "Pipeline Run Errors", - "", - `pipeline_run_errors{namespace="${namespace}"}`, - "{{ pod }} JobID: {{ job_id }}", - ), - m.timeseriesRowOption( - "Pipeline Run Total Time to Completion", - "Sec", - `pipeline_run_total_time_to_completion{namespace="${namespace}"} / 1e6`, - "{{ pod }} JobID: {{ job_id }}", - ), - m.timeseriesRowOption( - "Pipeline Tasks Total Finished", - "", - `pipeline_tasks_total_finished{namespace="${namespace}"}`, - "{{ pod }} JobID: {{ job_id }}", - ), - ), - dashboard.Row( - "Pipeline Metrics (ETHCall)", - row.Collapse(), - m.timeseriesRowOption( - "Pipeline Task ETH Call Execution Time", - "Sec", - `pipeline_task_eth_call_execution_time{namespace="${namespace}"}`, - "{{pod}}", - ), - ), - dashboard.Row( - "Pipeline Metrics (HTTP)", - row.Collapse(), - m.timeseriesRowOption( - "Pipeline Task HTTP Fetch Time", - "Sec", - `pipeline_task_http_fetch_time{namespace="${namespace}"} / 1e6`, - "{{pod}}", - ), - m.timeseriesRowOption( - "Pipeline Task HTTP Response Body Size", - "Bytes", - `pipeline_task_http_response_body_size{namespace="${namespace}"}`, - "{{pod}}", - ), - ), - dashboard.Row( - "Pipeline Metrics (Bridge)", - row.Collapse(), - m.timeseriesRowOption( - "Pipeline Bridge Latency", - "Sec", - `bridge_latency_seconds{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "Pipeline Bridge Errors Total", - "", - `bridge_errors_total{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "Pipeline Bridge Cache Hits Total", - "", - `bridge_cache_hits_total{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "Pipeline Bridge Cache Errors Total", - "", - `bridge_cache_errors_total{namespace="${namespace}"}`, - "{{pod}}", - ), - ), - dashboard.Row( - "Pipeline Metrics", - row.Collapse(), - m.timeseriesRowOption( - "Pipeline Runs Queued", - "", - `pipeline_runs_queued{namespace="${namespace}"}`, - "{{pod}}", - ), - m.timeseriesRowOption( - "Pipeline Runs Tasks Queued", - "", - `pipeline_task_runs_queued{namespace="${namespace}"}`, - "{{pod}}", - ), - ), } - logOptsFinal := make([]row.Option, 0) - logOptsFinal = append( - logOptsFinal, - row.Collapse(), - row.WithTimeSeries( - "Log Counters", - timeseries.Span(12), - timeseries.Height("200px"), - timeseries.DataSource(m.PrometheusDataSourceName), - timeseries.WithPrometheusTarget( - `log_panic_count{namespace="${namespace}"}`, - prometheus.Legend("{{pod}} - panic"), - ), - timeseries.WithPrometheusTarget( - `log_fatal_count{namespace="${namespace}"}`, - prometheus.Legend("{{pod}} - fatal"), - ), - timeseries.WithPrometheusTarget( - `log_critical_count{namespace="${namespace}"}`, - prometheus.Legend("{{pod}} - critical"), - ), - timeseries.WithPrometheusTarget( - `log_warn_count{namespace="${namespace}"}`, - prometheus.Legend("{{pod}} - warn"), - ), - timeseries.WithPrometheusTarget( - `log_error_count{namespace="${namespace}"}`, - prometheus.Legend("{{pod}} - error"), - ), - ), - m.logsRowOption("All errors", ` - {namespace="${namespace}", app="app", container="node"} - | json - | level="error" - | line_format "{{ .instance }} {{ .level }} {{ .ts }} {{ .logger }} {{ .caller }} {{ .msg }} {{ .version }} {{ .nodeTier }} {{ .nodeName }} {{ .node }} {{ .evmChainID }} {{ .nodeOrder }} {{ .mode }} {{ .nodeState }} {{ .sentryEventID }} {{ .stacktrace }}"`), - ) - logOptsFinal = append(logOptsFinal, m.logsRowOptionsForNodes(m.Nodes)...) - logRowOpts := dashboard.Row( - "Logs", - logOptsFinal..., - ) - opts = append(opts, logRowOpts) - opts = append(opts, m.extendedOpts...) - builder, err := dashboard.New( - "Chainlink Cluster Dashboard", - opts..., - ) - m.opts = opts - m.builder = builder - return err -} -// Deploy deploys the dashboard to Grafana -func (m *CLClusterDashboard) Deploy(ctx context.Context) error { - client := grabana.NewClient(&http.Client{}, m.GrafanaURL, grabana.WithAPIToken(m.GrafanaToken)) - folder, err := client.FindOrCreateFolder(ctx, m.Folder) - if err != nil { - return pkgerrors.Wrap(err, ErrFailedToCreateFolder) - } - if _, err := client.UpsertDashboard(ctx, folder, m.builder); err != nil { - return pkgerrors.Wrap(err, ErrFailedToCreateDashboard) - } - return nil + m.opts = append(m.opts, opts...) + waspVariables := wasp.AddVariables(m.PrometheusDataSourceName) + m.opts = append(m.opts, waspVariables...) } diff --git a/charts/chainlink-cluster/dashboard/panels.go b/charts/chainlink-cluster/dashboard/panels.go new file mode 100644 index 00000000000..597c4faf679 --- /dev/null +++ b/charts/chainlink-cluster/dashboard/panels.go @@ -0,0 +1,1773 @@ +package dashboard + +import ( + "github.com/K-Phoen/grabana/dashboard" + "github.com/K-Phoen/grabana/gauge" + "github.com/K-Phoen/grabana/row" + "github.com/K-Phoen/grabana/stat" + "github.com/K-Phoen/grabana/table" + "github.com/K-Phoen/grabana/target/prometheus" + "github.com/K-Phoen/grabana/timeseries" + "github.com/K-Phoen/grabana/timeseries/axis" +) + +func (m *Dashboard) addMainPanels() { + var balancePanelSpanSize float32 = 4 + var panelsIncluded []row.Option + var goVersionLegend string = "version" + + if m.platform == "kubernetes" { + balancePanelSpanSize = 3 + goVersionLegend = "exported_version" + } + + globalInfoPanels := []row.Option{ + row.WithStat( + "App Version", + stat.DataSource(m.PrometheusDataSourceName), + stat.Text(stat.TextValueAndName), + stat.Orientation(stat.OrientationVertical), + stat.TitleFontSize(12), + stat.ValueFontSize(20), + stat.Span(2), + stat.Text("name"), + stat.Height("100px"), + stat.WithPrometheusTarget( + `version{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{version}}"), + ), + ), + row.WithStat( + "Go Version", + stat.DataSource(m.PrometheusDataSourceName), + stat.Text(stat.TextValueAndName), + stat.Orientation(stat.OrientationVertical), + stat.TitleFontSize(12), + stat.ValueFontSize(20), + stat.Span(2), + stat.Text("name"), + stat.Height("100px"), + stat.WithPrometheusTarget( + `go_info{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+goVersionLegend+"}}"), + ), + ), + row.WithStat( + "Uptime in days", + stat.DataSource(m.PrometheusDataSourceName), + stat.Text(stat.TextValueAndName), + stat.Orientation(stat.OrientationVertical), + stat.TitleFontSize(12), + stat.ValueFontSize(20), + stat.Span(2), + stat.Height("100px"), + stat.WithPrometheusTarget( + `uptime_seconds{`+m.panelOption.labelFilter+`=~"$instance"} / 86400`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithStat( + "ETH Balance", + stat.DataSource(m.PrometheusDataSourceName), + stat.Text(stat.TextValueAndName), + stat.Orientation(stat.OrientationVertical), + stat.TitleFontSize(12), + stat.ValueFontSize(20), + stat.Span(balancePanelSpanSize), + stat.Height("100px"), + stat.Decimals(2), + stat.WithPrometheusTarget( + `eth_balance{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{account}}"), + ), + ), + row.WithStat( + "Solana Balance", + stat.DataSource(m.PrometheusDataSourceName), + stat.Text(stat.TextValueAndName), + stat.Orientation(stat.OrientationVertical), + stat.TitleFontSize(12), + stat.ValueFontSize(20), + stat.Span(balancePanelSpanSize), + stat.Height("100px"), + stat.Decimals(2), + stat.WithPrometheusTarget( + `solana_balance{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{account}}"), + ), + ), + } + + additionalPanels := []row.Option{ + row.WithTimeSeries( + "Service Components Health", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `health{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{service_id}}"), + ), + ), + row.WithTimeSeries( + "ETH Balance", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + axis.Decimals(2), + ), + timeseries.WithPrometheusTarget( + `eth_balance{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{account}}"), + ), + ), + row.WithTimeSeries( + "SOL Balance", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + axis.Decimals(2), + ), + timeseries.WithPrometheusTarget( + `solana_balance{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{account}}"), + ), + ), + } + + panelsIncluded = append(panelsIncluded, globalInfoPanels...) + if m.platform == "kubernetes" { + panelsIncluded = append(panelsIncluded, row.WithStat( + "Pod Restarts", + stat.Span(2), + stat.Height("100px"), + stat.DataSource(m.PrometheusDataSourceName), + stat.WithPrometheusTarget( + `sum(increase(kube_pod_container_status_restarts_total{pod=~"$instance.*", namespace=~"${namespace}"}[$__rate_interval])) by (pod)`, + prometheus.Legend("{{pod}}"), + ), + )) + } + panelsIncluded = append(panelsIncluded, additionalPanels...) + + opts := []dashboard.Option{ + dashboard.Row( + "Global health", + panelsIncluded..., + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addKubePanels() { + opts := []dashboard.Option{ + dashboard.Row( + "Pod health", + row.WithStat( + "Pod Restarts", + stat.Span(4), + stat.Height("200px"), + stat.DataSource(m.PrometheusDataSourceName), + stat.SparkLine(), + stat.SparkLineYMin(0), + stat.WithPrometheusTarget( + `sum(increase(kube_pod_container_status_restarts_total{pod=~"$instance.*", namespace=~"${namespace}"}[$__rate_interval])) by (pod)`, + prometheus.Legend("{{pod}}"), + ), + ), + row.WithStat( + "OOM Events", + stat.Span(4), + stat.Height("200px"), + stat.DataSource(m.PrometheusDataSourceName), + stat.SparkLine(), + stat.SparkLineYMin(0), + stat.WithPrometheusTarget( + `sum(container_oom_events_total{pod=~"$instance.*", namespace=~"${namespace}"}) by (pod)`, + prometheus.Legend("{{pod}}"), + ), + ), + row.WithStat( + "OOM Killed", + stat.Span(4), + stat.Height("200px"), + stat.DataSource(m.PrometheusDataSourceName), + stat.SparkLine(), + stat.SparkLineYMin(0), + stat.WithPrometheusTarget( + `kube_pod_container_status_last_terminated_reason{reason="OOMKilled", pod=~"$instance.*", namespace=~"${namespace}"}`, + prometheus.Legend("{{pod}}"), + ), + ), + row.WithTimeSeries( + "CPU Usage", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `sum(node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{pod=~"$instance.*", namespace=~"${namespace}"}) by (pod)`, + prometheus.Legend("{{pod}}"), + ), + ), + row.WithTimeSeries( + "Memory Usage", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("bytes"), + axis.Label("Memory"), + axis.SoftMin(0), + ), + timeseries.WithPrometheusTarget( + `sum(container_memory_rss{pod=~"$instance.*", namespace=~"${namespace}", container!=""}) by (pod)`, + prometheus.Legend("{{pod}}"), + ), + ), + row.WithTimeSeries( + "Receive Bandwidth", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Bps"), + axis.SoftMin(0), + ), + timeseries.WithPrometheusTarget( + `sum(irate(container_network_receive_bytes_total{pod=~"$instance.*", namespace=~"${namespace}"}[$__rate_interval])) by (pod)`, + prometheus.Legend("{{pod}}"), + ), + ), + row.WithTimeSeries( + "Transmit Bandwidth", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Bps"), + axis.SoftMin(0), + ), + timeseries.WithPrometheusTarget( + `sum(irate(container_network_transmit_bytes_total{pod=~"$instance.*", namespace=~"${namespace}"}[$__rate_interval])) by (pod)`, + prometheus.Legend("{{pod}}"), + ), + ), + row.WithTimeSeries( + "Average Container Bandwidth by Namespace: Received", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Bps"), + axis.SoftMin(0), + ), + timeseries.WithPrometheusTarget( + `avg(irate(container_network_receive_bytes_total{pod=~"$instance.*", namespace=~"${namespace}"}[$__rate_interval])) by (pod)`, + prometheus.Legend("{{pod}}"), + ), + ), + row.WithTimeSeries( + "Average Container Bandwidth by Namespace: Transmitted", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Bps"), + axis.SoftMin(0), + ), + timeseries.WithPrometheusTarget( + `avg(irate(container_network_transmit_bytes_total{pod=~"$instance.*", namespace=~"${namespace}"}[$__rate_interval])) by (pod)`, + prometheus.Legend("{{pod}}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addLogPollerPanels() { + opts := []dashboard.Option{ + dashboard.Row("LogPoller", + row.Collapse(), + row.WithTimeSeries( + "LogPoller RPS", + timeseries.Span(4), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `avg(sum(rate(log_poller_query_duration_count{`+m.panelOption.labelFilter+`=~"$instance", evmChainID=~"$evmChainID"}[$__rate_interval])) by (query, instance)) by (query)`, + prometheus.Legend("{{query}}"), + ), + timeseries.WithPrometheusTarget( + `avg(sum(rate(log_poller_query_duration_count{`+m.panelOption.labelFilter+`=~"$instance", evmChainID=~"$evmChainID"}[$__rate_interval]))) by (instance)`, + prometheus.Legend("Total"), + ), + ), + row.WithTimeSeries( + "LogPoller Logs Number Returned", + timeseries.Span(4), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `log_poller_query_dataset_size{`+m.panelOption.labelFilter+`=~"$instance", evmChainID=~"$evmChainID"}`, + prometheus.Legend("{{query}} : {{type}}"), + ), + ), + row.WithTimeSeries( + "LogPoller Average Logs Number Returned", + timeseries.Span(4), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `avg(log_poller_query_dataset_size{`+m.panelOption.labelFilter+`=~"$instance", evmChainID=~"$evmChainID"}) by (query)`, + prometheus.Legend("{{query}}"), + ), + ), + row.WithTimeSeries( + "LogPoller Max Logs Number Returned", + timeseries.Span(4), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `max(log_poller_query_dataset_size{`+m.panelOption.labelFilter+`=~"$instance", evmChainID=~"$evmChainID"}) by (query)`, + prometheus.Legend("{{query}}"), + ), + ), + row.WithTimeSeries( + "LogPoller Logs Number Returned by Chain", + timeseries.Span(4), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `max(log_poller_query_dataset_size{`+m.panelOption.labelFilter+`=~"$instance"}) by (evmChainID)`, + prometheus.Legend("{{evmChainID}}"), + ), + ), + row.WithTimeSeries( + "LogPoller Queries Duration Avg", + timeseries.Span(4), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `(sum(rate(log_poller_query_duration_sum{`+m.panelOption.labelFilter+`=~"$instance", evmChainID=~"$evmChainID"}[$__rate_interval])) by (query) / sum(rate(log_poller_query_duration_count{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (query)) / 1e6`, + prometheus.Legend("{{query}}"), + ), + ), + row.WithTimeSeries( + "LogPoller Queries Duration p99", + timeseries.Span(4), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `histogram_quantile(0.99, sum(rate(log_poller_query_duration_bucket{`+m.panelOption.labelFilter+`=~"$instance", evmChainID=~"$evmChainID"}[$__rate_interval])) by (le, query)) / 1e6`, + prometheus.Legend("{{query}}"), + ), + ), + row.WithTimeSeries( + "LogPoller Queries Duration p95", + timeseries.Span(4), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `histogram_quantile(0.95, sum(rate(log_poller_query_duration_bucket{`+m.panelOption.labelFilter+`=~"$instance", evmChainID=~"$evmChainID"}[$__rate_interval])) by (le, query)) / 1e6`, + prometheus.Legend("{{query}}"), + ), + ), + row.WithTimeSeries( + "LogPoller Queries Duration p90", + timeseries.Span(4), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `histogram_quantile(0.95, sum(rate(log_poller_query_duration_bucket{`+m.panelOption.labelFilter+`=~"$instance", evmChainID=~"$evmChainID"}[$__rate_interval])) by (le, query)) / 1e6`, + prometheus.Legend("{{query}}"), + ), + ), + row.WithTimeSeries( + "LogPoller Queries Duration Median", + timeseries.Span(4), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `histogram_quantile(0.5, sum(rate(log_poller_query_duration_bucket{`+m.panelOption.labelFilter+`=~"$instance", evmChainID=~"$evmChainID"}[$__rate_interval])) by (le, query)) / 1e6`, + prometheus.Legend("{{query}}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addFeedsJobsPanels() { + opts := []dashboard.Option{ + dashboard.Row("Feeds Jobs", + row.Collapse(), + row.WithTimeSeries( + "Feeds Job Proposal Requests", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `feeds_job_proposal_requests{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Feeds Job Proposal Count", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `feeds_job_proposal_count{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addMailboxPanels() { + opts := []dashboard.Option{ + dashboard.Row("Mailbox", + row.Collapse(), + row.WithTimeSeries( + "Mailbox Load Percent", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `mailbox_load_percent{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{ name }}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addPromReporterPanels() { + opts := []dashboard.Option{ + dashboard.Row("Prom Reporter", + row.Collapse(), + row.WithTimeSeries( + "Unconfirmed Transactions", + timeseries.Span(4), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Tx"), + ), + timeseries.WithPrometheusTarget( + `unconfirmed_transactions{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Unconfirmed TX Age", + timeseries.Span(4), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Sec"), + ), + timeseries.WithPrometheusTarget( + `max_unconfirmed_tx_age{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Unconfirmed TX Blocks", + timeseries.Span(4), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Blocks"), + ), + timeseries.WithPrometheusTarget( + `max_unconfirmed_blocks{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addTxManagerPanels() { + opts := []dashboard.Option{ + dashboard.Row("TX Manager", + row.Collapse(), + row.WithTimeSeries( + "TX Manager Time Until TX Broadcast", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `tx_manager_time_until_tx_broadcast{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "TX Manager Num Gas Bumps", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `tx_manager_num_gas_bumps{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "TX Manager Num Gas Bumps Exceeds Limit", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `tx_manager_gas_bump_exceeds_limit{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "TX Manager Num Confirmed Transactions", + timeseries.Span(3), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `tx_manager_num_confirmed_transactions{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "TX Manager Num Successful Transactions", + timeseries.Span(3), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `tx_manager_num_successful_transactions{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "TX Manager Num Reverted Transactions", + timeseries.Span(3), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `tx_manager_num_tx_reverted{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "TX Manager Num Fwd Transactions", + timeseries.Span(3), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `tx_manager_fwd_tx_count{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "TX Manager Num Transactions Attempts", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `tx_manager_tx_attempt_count{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "TX Manager Time Until TX Confirmed", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `tx_manager_time_until_tx_confirmed{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "TX Manager Block Until TX Confirmed", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `tx_manager_blocks_until_tx_confirmed{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addHeadTrackerPanels() { + opts := []dashboard.Option{ + dashboard.Row("Head tracker", + row.Collapse(), + row.WithTimeSeries( + "Head tracker current head", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Block"), + ), + timeseries.WithPrometheusTarget( + `head_tracker_current_head{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Head tracker very old head", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Block"), + ), + timeseries.WithPrometheusTarget( + `head_tracker_very_old_head{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Head tracker heads received", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Block"), + ), + timeseries.WithPrometheusTarget( + `head_tracker_heads_received{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Head tracker connection errors", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Block"), + ), + timeseries.WithPrometheusTarget( + `head_tracker_connection_errors{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addDatabasePanels() { + opts := []dashboard.Option{ + // DB Metrics + dashboard.Row("DB Connection Metrics (App)", + row.Collapse(), + row.WithTimeSeries( + "DB Connections", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Conn"), + ), + timeseries.WithPrometheusTarget( + `db_conns_max{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Max"), + ), + timeseries.WithPrometheusTarget( + `db_conns_open{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Open"), + ), + timeseries.WithPrometheusTarget( + `db_conns_used{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Used"), + ), + timeseries.WithPrometheusTarget( + `db_conns_wait{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Wait"), + ), + ), + row.WithTimeSeries( + "DB Wait Count", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `db_wait_count{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "DB Wait Time", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Sec"), + ), + timeseries.WithPrometheusTarget( + `db_wait_time_seconds{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addSQLQueryPanels() { + opts := []dashboard.Option{ + dashboard.Row( + "SQL Query", + row.Collapse(), + row.WithTimeSeries( + "SQL Query Timeout Percent", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("percent"), + ), + timeseries.WithPrometheusTarget( + `histogram_quantile(0.9, sum(rate(sql_query_timeout_percent_bucket{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (le))`, + prometheus.Legend("p90"), + ), + timeseries.WithPrometheusTarget( + `histogram_quantile(0.95, sum(rate(sql_query_timeout_percent_bucket{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (le))`, + prometheus.Legend("p95"), + ), + timeseries.WithPrometheusTarget( + `histogram_quantile(0.99, sum(rate(sql_query_timeout_percent_bucket{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (le))`, + prometheus.Legend("p99"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addLogsPanels() { + opts := []dashboard.Option{ + dashboard.Row("Logs Metrics", + row.Collapse(), + row.WithTimeSeries( + "Logs Counters", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `log_panic_count{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - panic"), + ), + timeseries.WithPrometheusTarget( + `log_fatal_count{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - fatal"), + ), + timeseries.WithPrometheusTarget( + `log_critical_count{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - critical"), + ), + timeseries.WithPrometheusTarget( + `log_warn_count{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - warn"), + ), + timeseries.WithPrometheusTarget( + `log_error_count{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - error"), + ), + ), + row.WithTimeSeries( + "Logs Rate", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `sum(rate(log_panic_count{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval]))`, + prometheus.Legend("panic"), + ), + timeseries.WithPrometheusTarget( + `sum(rate(log_fatal_count{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval]))`, + prometheus.Legend("fatal"), + ), + timeseries.WithPrometheusTarget( + `sum(rate(log_critical_count{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval]))`, + prometheus.Legend("critical"), + ), + timeseries.WithPrometheusTarget( + `sum(rate(log_warn_count{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval]))`, + prometheus.Legend("warn"), + ), + timeseries.WithPrometheusTarget( + `sum(rate(log_error_count{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval]))`, + prometheus.Legend("error"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addEVMPoolLifecyclePanels() { + opts := []dashboard.Option{ + dashboard.Row( + "EVM Pool Lifecycle", + row.Collapse(), + row.WithTimeSeries( + "EVM Pool Highest Seen Block", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Block"), + ), + timeseries.WithPrometheusTarget( + `evm_pool_rpc_node_highest_seen_block{`+m.panelOption.labelFilter+`=~"$instance", evmChainID="${evmChainID}"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "EVM Pool Num Seen Blocks", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Block"), + ), + timeseries.WithPrometheusTarget( + `evm_pool_rpc_node_num_seen_blocks{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "EVM Pool Node Polls Total", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Block"), + ), + timeseries.WithPrometheusTarget( + `evm_pool_rpc_node_polls_total{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "EVM Pool Node Polls Failed", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Block"), + ), + timeseries.WithPrometheusTarget( + `evm_pool_rpc_node_polls_failed{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "EVM Pool Node Polls Success", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Block"), + ), + timeseries.WithPrometheusTarget( + `evm_pool_rpc_node_polls_success{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addEVMPoolRPCNodePanels() { + opts := []dashboard.Option{ + dashboard.Row( + "EVM Pool RPC Node Metrics (App)", + row.Collapse(), + row.WithTimeSeries( + "EVM Pool RPC Node Calls Success Rate", + timeseries.Span(7), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + axis.Label("%"), + axis.SoftMin(0), + axis.SoftMax(100), + ), + timeseries.WithPrometheusTarget( + `sum(increase(evm_pool_rpc_node_calls_success{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (`+m.panelOption.labelFilter+`, evmChainID, nodeName) / sum(increase(evm_pool_rpc_node_calls_total{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (`+m.panelOption.labelFilter+`, evmChainID, nodeName) * 100`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{evmChainID}} - {{nodeName}}"), + ), + ), + row.WithGauge( + "EVM Pool RPC Node Calls Success Rate", + gauge.Span(5), + gauge.Height("200px"), + gauge.DataSource(m.PrometheusDataSourceName), + gauge.Unit("percentunit"), + gauge.WithPrometheusTarget( + `sum(increase(evm_pool_rpc_node_calls_success{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (`+m.panelOption.labelFilter+`, evmChainID, nodeName) / sum(increase(evm_pool_rpc_node_calls_total{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (`+m.panelOption.labelFilter+`, evmChainID, nodeName)`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{evmChainID}} - {{nodeName}}"), + ), + gauge.AbsoluteThresholds([]gauge.ThresholdStep{ + {Color: "#ff0000"}, + {Color: "#ffa500", Value: float64Ptr(0.8)}, + {Color: "#00ff00", Value: float64Ptr(0.9)}, + }), + ), + // issue when value is 0 + row.WithTimeSeries( + "EVM Pool RPC Node Dials Success Rate", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + axis.Label("%"), + axis.SoftMin(0), + axis.SoftMax(100), + ), + timeseries.WithPrometheusTarget( + `sum(increase(evm_pool_rpc_node_dials_success{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (`+m.panelOption.labelFilter+`, evmChainID, nodeName) / sum(increase(evm_pool_rpc_node_dials_total{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (`+m.panelOption.labelFilter+`, evmChainID, nodeName) * 100`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{evmChainID}} - {{nodeName}}"), + ), + ), + // issue when value is 0 + row.WithTimeSeries( + "EVM Pool RPC Node Dials Failure Rate", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + axis.Label("%"), + axis.SoftMin(0), + axis.SoftMax(100), + ), + timeseries.WithPrometheusTarget( + `sum(increase(evm_pool_rpc_node_dials_failed{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (`+m.panelOption.labelFilter+`, evmChainID, nodeName) / sum(increase(evm_pool_rpc_node_dials_total{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (`+m.panelOption.labelFilter+`, evmChainID, nodeName) * 100`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{evmChainID}} - {{nodeName}}"), + ), + ), + row.WithTimeSeries( + "EVM Pool RPC Node Transitions", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `evm_pool_rpc_node_num_transitions_to_alive{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend(""), + ), + timeseries.WithPrometheusTarget( + `evm_pool_rpc_node_num_transitions_to_in_sync{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend(""), + ), + timeseries.WithPrometheusTarget( + `evm_pool_rpc_node_num_transitions_to_out_of_sync{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend(""), + ), + timeseries.WithPrometheusTarget( + `evm_pool_rpc_node_num_transitions_to_unreachable{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend(""), + ), + timeseries.WithPrometheusTarget( + `evm_pool_rpc_node_num_transitions_to_invalid_chain_id{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend(""), + ), + timeseries.WithPrometheusTarget( + `evm_pool_rpc_node_num_transitions_to_unusable{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend(""), + ), + ), + row.WithTimeSeries( + "EVM Pool RPC Node States", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `evm_pool_rpc_node_states{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{evmChainID}} - {{state}}"), + ), + ), + row.WithTimeSeries( + "EVM Pool RPC Node Verifies Success Rate", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + axis.Label("%"), + axis.SoftMin(0), + axis.SoftMax(100), + ), + timeseries.WithPrometheusTarget( + `sum(increase(evm_pool_rpc_node_verifies_success{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (`+m.panelOption.labelFilter+`, evmChainID, nodeName) / sum(increase(evm_pool_rpc_node_verifies{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (`+m.panelOption.labelFilter+`, evmChainID, nodeName) * 100`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{evmChainID}} - {{nodeName}}"), + ), + ), + row.WithTimeSeries( + "EVM Pool RPC Node Verifies Failure Rate", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + axis.Label("%"), + axis.SoftMin(0), + axis.SoftMax(100), + ), + timeseries.WithPrometheusTarget( + `sum(increase(evm_pool_rpc_node_verifies_failed{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (`+m.panelOption.labelFilter+`, evmChainID, nodeName) / sum(increase(evm_pool_rpc_node_verifies{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (`+m.panelOption.labelFilter+`, evmChainID, nodeName) * 100`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{evmChainID}} - {{nodeName}}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addEVMRPCNodeLatenciesPanels() { + opts := []dashboard.Option{ + dashboard.Row( + "EVM Pool RPC Node Latencies (App)", + row.Collapse(), + row.WithTimeSeries( + "EVM Pool RPC Node Calls Latency 0.95 quantile", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("ms"), + ), + timeseries.WithPrometheusTarget( + `histogram_quantile(0.95, sum(rate(evm_pool_rpc_node_rpc_call_time_bucket{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (le, rpcCallName)) / 1e6`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addBlockHistoryEstimatorPanels() { + opts := []dashboard.Option{ + dashboard.Row("Block History Estimator", + row.Collapse(), + row.WithTimeSeries( + "Gas Updater All Gas Price Percentiles", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `gas_updater_all_gas_price_percentiles{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{ percentile }}"), + ), + ), + row.WithTimeSeries( + "Gas Updater All Tip Cap Percentiles", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `gas_updater_all_tip_cap_percentiles{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - {{ percentile }}"), + ), + ), + row.WithTimeSeries( + "Gas Updater Set Gas Price", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `gas_updater_set_gas_price{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Gas Updater Set Tip Cap", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `gas_updater_set_tip_cap{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Gas Updater Current Base Fee", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `gas_updater_current_base_fee{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Block History Estimator Connectivity Failure Count", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `block_history_estimator_connectivity_failure_count{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addPipelinePanels() { + opts := []dashboard.Option{ + dashboard.Row("Pipeline Metrics (Runner)", + row.Collapse(), + row.WithTimeSeries( + "Pipeline Task Execution Time", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Sec"), + ), + timeseries.WithPrometheusTarget( + `pipeline_task_execution_time{`+m.panelOption.labelFilter+`=~"$instance"} / 1e6`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} JobID: {{ job_id }}"), + ), + ), + row.WithTimeSeries( + "Pipeline Run Errors", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `pipeline_run_errors{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} JobID: {{ job_id }}"), + ), + ), + row.WithTimeSeries( + "Pipeline Run Total Time to Completion", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Sec"), + ), + timeseries.WithPrometheusTarget( + `pipeline_run_total_time_to_completion{`+m.panelOption.labelFilter+`=~"$instance"} / 1e6`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} JobID: {{ job_id }}"), + ), + ), + row.WithTimeSeries( + "Pipeline Tasks Total Finished", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `pipeline_tasks_total_finished{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} JobID: {{ job_id }}"), + ), + ), + ), + dashboard.Row( + "Pipeline Metrics (ETHCall)", + row.Collapse(), + row.WithTimeSeries( + "Pipeline Task ETH Call Execution Time", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Sec"), + ), + timeseries.WithPrometheusTarget( + `pipeline_task_eth_call_execution_time{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + ), + dashboard.Row( + "Pipeline Metrics (HTTP)", + row.Collapse(), + row.WithTimeSeries( + "Pipeline Task HTTP Fetch Time", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Sec"), + ), + timeseries.WithPrometheusTarget( + `pipeline_task_http_fetch_time{`+m.panelOption.labelFilter+`=~"$instance"} / 1e6`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Pipeline Task HTTP Response Body Size", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Bytes"), + ), + timeseries.WithPrometheusTarget( + `pipeline_task_http_response_body_size{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + ), + dashboard.Row( + "Pipeline Metrics (Bridge)", + row.Collapse(), + row.WithTimeSeries( + "Pipeline Bridge Latency", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Sec"), + ), + timeseries.WithPrometheusTarget( + `bridge_latency_seconds{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Pipeline Bridge Errors Total", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `bridge_errors_total{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Pipeline Bridge Cache Hits Total", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `bridge_cache_hits_total{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Pipeline Bridge Cache Errors Total", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `bridge_cache_errors_total{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + ), + dashboard.Row( + "Pipeline Metrics", + row.Collapse(), + row.WithTimeSeries( + "Pipeline Runs Queued", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `pipeline_runs_queued{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Pipeline Runs Tasks Queued", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `pipeline_task_runs_queued{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addHTTPAPIPanels() { + opts := []dashboard.Option{ + // HTTP API Metrics + dashboard.Row( + "HTTP API Metrics", + row.Collapse(), + row.WithTimeSeries( + "Request Duration p95", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Sec"), + ), + timeseries.WithPrometheusTarget( + `histogram_quantile(0.95, sum(rate(service_gonic_request_duration_bucket{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (le, path, method))`, + prometheus.Legend("{{ method }} {{ path }}"), + ), + ), + row.WithTimeSeries( + "Request Total Rate over interval", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `sum(rate(service_gonic_requests_total{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (path, method, code)`, + prometheus.Legend("{{ method }} {{ path }} {{ code }}"), + ), + ), + row.WithTimeSeries( + "Request Size", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Bytes"), + ), + timeseries.WithPrometheusTarget( + `avg(rate(service_gonic_request_size_bytes_sum{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval]))/avg(rate(service_gonic_request_size_bytes_count{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval]))`, + prometheus.Legend("Average"), + ), + ), + row.WithTimeSeries( + "Response Size", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("Bytes"), + ), + timeseries.WithPrometheusTarget( + `avg(rate(service_gonic_response_size_bytes_sum{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval]))/avg(rate(service_gonic_response_size_bytes_count{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval]))`, + prometheus.Legend("Average"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addPromHTTPPanels() { + opts := []dashboard.Option{ + dashboard.Row( + "PromHTTP Metrics", + row.Collapse(), + row.WithGauge("HTTP Request in flight", + gauge.Span(2), + gauge.Height("200px"), + gauge.DataSource(m.PrometheusDataSourceName), + gauge.WithPrometheusTarget( + `promhttp_metric_handler_requests_in_flight`, + prometheus.Legend(""), + ), + ), + row.WithTimeSeries( + "HTTP rate", + timeseries.Span(10), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `sum(rate(promhttp_metric_handler_requests_total{`+m.panelOption.labelFilter+`=~"$instance"}[$__rate_interval])) by (code)`, + prometheus.Legend("{{ code }}"), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addGoMetricsPanels() { + opts := []dashboard.Option{ + dashboard.Row( + "Go Metrics", + row.Collapse(), + row.WithTable( + "Threads", + table.Span(3), + table.Height("200px"), + table.DataSource(m.PrometheusDataSourceName), + table.WithPrometheusTarget( + `sum(go_threads{`+m.panelOption.labelFilter+`=~"$instance"}) by (instance)`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}")), + table.HideColumn("Time"), + table.AsTimeSeriesAggregations([]table.Aggregation{ + {Label: "AVG", Type: table.AVG}, + {Label: "Current", Type: table.Current}, + }), + ), + row.WithTimeSeries( + "Threads", + timeseries.Span(9), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit(""), + ), + timeseries.WithPrometheusTarget( + `sum(go_threads{`+m.panelOption.labelFilter+`=~"$instance"}) by (instance)`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + ), + row.WithTimeSeries( + "Heap allocations", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `sum(go_memstats_heap_alloc_bytes{`+m.panelOption.labelFilter+`=~"$instance"}) by (instance)`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + timeseries.Axis( + axis.Unit("bytes"), + axis.Label("Memory"), + axis.SoftMin(0), + ), + ), + row.WithStat( + "Heap Allocations", + stat.Span(6), + stat.Height("200px"), + stat.DataSource(m.PrometheusDataSourceName), + stat.Unit("bytes"), + stat.ColorValue(), + stat.WithPrometheusTarget(`sum(go_memstats_heap_alloc_bytes{`+m.panelOption.labelFilter+`=~"$instance"})`), + /*stat.AbsoluteThresholds([]stat.ThresholdStep{ + { + Color: "green", + Value: nil, + }, + { + Color: "orange", + Value: float64Ptr(6.711e+7), + }, + { + Color: "red", + Value: float64Ptr(1.342e+8), + }, + }),*/ + ), + row.WithTimeSeries( + "Memory in Heap", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("bytes"), + axis.Label("Memory"), + axis.SoftMin(0), + ), + timeseries.WithPrometheusTarget( + `go_memstats_heap_alloc_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Alloc"), + ), + timeseries.WithPrometheusTarget( + `go_memstats_heap_sys_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Sys"), + ), + timeseries.WithPrometheusTarget( + `go_memstats_heap_idle_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Idle"), + ), + timeseries.WithPrometheusTarget( + `go_memstats_heap_inuse_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - InUse"), + ), + timeseries.WithPrometheusTarget( + `go_memstats_heap_released_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Released"), + ), + ), + row.WithTimeSeries( + "Memory in Off-Heap", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.Axis( + axis.Unit("bytes"), + axis.Label("Memory"), + axis.SoftMin(0), + ), + timeseries.WithPrometheusTarget( + `go_memstats_mspan_inuse_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Total InUse"), + ), + timeseries.WithPrometheusTarget( + `go_memstats_mspan_sys_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Total Sys"), + ), + timeseries.WithPrometheusTarget( + `go_memstats_mcache_inuse_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Cache InUse"), + ), + timeseries.WithPrometheusTarget( + `go_memstats_mcache_sys_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Cache Sys"), + ), + timeseries.WithPrometheusTarget( + `go_memstats_buck_hash_sys_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Hash Sys"), + ), + timeseries.WithPrometheusTarget( + `go_memstats_gc_sys_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - GC Sys"), + ), + timeseries.WithPrometheusTarget( + `go_memstats_other_sys_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - bytes of memory are used for other runtime allocations"), + ), + timeseries.WithPrometheusTarget( + `go_memstats_next_gc_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Next GC"), + ), + ), + row.WithTimeSeries( + "Memory in Stack", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `go_memstats_stack_inuse_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - InUse"), + ), + timeseries.WithPrometheusTarget( + `go_memstats_stack_sys_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}} - Sys"), + ), + timeseries.Axis( + axis.Unit("bytes"), + axis.Label("Memory"), + axis.SoftMin(0), + ), + ), + row.WithTimeSeries( + "Total Used Memory", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `go_memstats_sys_bytes{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + timeseries.Axis( + axis.Unit("bytes"), + axis.Label("Memory"), + axis.SoftMin(0), + ), + ), + row.WithTimeSeries( + "Number of Live Objects", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `go_memstats_mallocs_total{`+m.panelOption.labelFilter+`=~"$instance"} - go_memstats_frees_total{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + timeseries.Axis( + axis.SoftMin(0), + ), + ), + row.WithTimeSeries( + "Rate of Objects Allocated", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `rate(go_memstats_mallocs_total{`+m.panelOption.labelFilter+`=~"$instance"}[1m])`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + timeseries.Axis( + axis.SoftMin(0), + ), + ), + row.WithTimeSeries( + "Rate of a Pointer Dereferences", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `rate(go_memstats_lookups_total{`+m.panelOption.labelFilter+`=~"$instance"}[1m])`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + timeseries.Axis( + axis.Unit("ops"), + axis.SoftMin(0), + ), + ), + row.WithTimeSeries( + "Goroutines", + timeseries.Span(6), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `go_goroutines{`+m.panelOption.labelFilter+`=~"$instance"}`, + prometheus.Legend("{{"+m.panelOption.labelFilter+"}}"), + ), + timeseries.Axis( + axis.SoftMin(0), + ), + ), + ), + } + + m.opts = append(m.opts, opts...) +} + +func (m *Dashboard) addCorePanels() { + m.addMainPanels() + m.addLogPollerPanels() + m.addFeedsJobsPanels() + m.addMailboxPanels() + m.addPromReporterPanels() + m.addTxManagerPanels() + m.addHeadTrackerPanels() + m.addDatabasePanels() + m.addSQLQueryPanels() + m.addLogsPanels() + m.addEVMPoolLifecyclePanels() + m.addEVMPoolRPCNodePanels() + m.addEVMRPCNodeLatenciesPanels() + m.addBlockHistoryEstimatorPanels() + m.addPipelinePanels() + m.addHTTPAPIPanels() + m.addPromHTTPPanels() + m.addGoMetricsPanels() +} + +func (m *Dashboard) addKubernetesPanels() { + m.addKubePanels() +} + +func float64Ptr(input float64) *float64 { + return &input +} diff --git a/charts/chainlink-cluster/dashboard/utils.go b/charts/chainlink-cluster/dashboard/utils.go new file mode 100644 index 00000000000..cc095d13eba --- /dev/null +++ b/charts/chainlink-cluster/dashboard/utils.go @@ -0,0 +1,10 @@ +package dashboard + +func Contains[T comparable](arr []T, x T) bool { + for _, v := range arr { + if v == x { + return true + } + } + return false +} diff --git a/charts/chainlink-cluster/go.mod b/charts/chainlink-cluster/go.mod index 14471683b2a..f07d94fec98 100644 --- a/charts/chainlink-cluster/go.mod +++ b/charts/chainlink-cluster/go.mod @@ -3,170 +3,15 @@ module github.com/smartcontractkit/chainlink/charts/chainlink-cluster/dashboard go 1.21 require ( - github.com/K-Phoen/grabana v0.21.19 - github.com/pkg/errors v0.9.1 - github.com/smartcontractkit/wasp v0.3.6 + github.com/K-Phoen/grabana v0.22.1 + github.com/smartcontractkit/wasp v0.4.6 ) require ( - github.com/K-Phoen/sdk v0.12.3 // indirect - github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect - github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect - github.com/armon/go-metrics v0.4.1 // indirect - github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go v1.44.217 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee // indirect - github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/coreos/go-semver v0.3.0 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dennwc/varint v1.0.0 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect - github.com/edsrzf/mmap-go v1.1.0 // indirect - github.com/emicklei/go-restful/v3 v3.10.1 // indirect - github.com/fatih/color v1.14.1 // indirect - github.com/felixge/httpsnoop v1.0.3 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.8.1 // indirect - github.com/go-kit/log v0.2.1 // indirect - github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/go-logr/logr v1.2.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/analysis v0.21.4 // indirect - github.com/go-openapi/errors v0.20.3 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/loads v0.21.2 // indirect - github.com/go-openapi/spec v0.20.8 // indirect - github.com/go-openapi/strfmt v0.21.3 // indirect - github.com/go-openapi/swag v0.22.3 // indirect - github.com/go-openapi/validate v0.22.1 // indirect - github.com/go-playground/locales v0.14.0 // indirect - github.com/go-playground/universal-translator v0.18.0 // indirect - github.com/go-playground/validator/v10 v10.11.1 // indirect - github.com/go-resty/resty/v2 v2.7.0 // indirect - github.com/goccy/go-json v0.9.11 // indirect - github.com/gogo/googleapis v1.4.0 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/gogo/status v1.1.1 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/google/btree v1.1.2 // indirect - github.com/google/gnostic v0.6.9 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/gorilla/mux v1.8.0 // indirect + github.com/K-Phoen/sdk v0.12.4 // indirect github.com/gosimple/slug v1.13.1 // indirect github.com/gosimple/unidecode v1.0.1 // indirect - github.com/grafana/dskit v0.0.0-20230201083518-528d8a7d52f2 // indirect - github.com/grafana/loki v1.6.2-0.20231017135925-990ac685e6a6 // indirect - github.com/grafana/loki/pkg/push v0.0.0-20230127102416-571f88bc5765 // indirect - github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect - github.com/hashicorp/consul/api v1.20.0 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.4.0 // indirect - github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-msgpack v0.5.5 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-sockaddr v1.0.2 // indirect - github.com/hashicorp/golang-lru v0.6.0 // indirect - github.com/hashicorp/memberlist v0.5.0 // indirect - github.com/hashicorp/serf v0.10.1 // indirect - github.com/imdario/mergo v0.3.13 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/jpillora/backoff v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/julienschmidt/httprouter v1.3.0 // indirect - github.com/klauspost/compress v1.16.3 // indirect - github.com/leodido/go-urn v1.2.1 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/miekg/dns v1.1.51 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect - github.com/oklog/ulid v1.3.1 // indirect - github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e // indirect - github.com/opentracing-contrib/go-stdlib v1.0.0 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pelletier/go-toml/v2 v2.0.6 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/alertmanager v0.25.0 // indirect - github.com/prometheus/client_golang v1.15.1 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/common/sigv4 v0.1.0 // indirect - github.com/prometheus/exporter-toolkit v0.9.1 // indirect - github.com/prometheus/procfs v0.9.0 // indirect - github.com/prometheus/prometheus v0.43.1-0.20230327151049-211ae4f1f0a2 // indirect - github.com/rs/zerolog v1.29.0 // indirect - github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect - github.com/sercand/kuberesolver/v4 v4.0.0 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.5.0 // indirect - github.com/stretchr/testify v1.8.3 // indirect - github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect - github.com/uber/jaeger-lib v2.4.1+incompatible // indirect - github.com/ugorji/go/codec v1.2.7 // indirect - github.com/weaveworks/common v0.0.0-20230411130259-f7d83a041205 // indirect - github.com/weaveworks/promrus v1.2.0 // indirect - go.etcd.io/etcd/api/v3 v3.5.7 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect - go.etcd.io/etcd/client/v3 v3.5.7 // indirect - go.mongodb.org/mongo-driver v1.11.2 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 // indirect - go.opentelemetry.io/otel v1.14.0 // indirect - go.opentelemetry.io/otel/metric v0.37.0 // indirect - go.opentelemetry.io/otel/trace v1.14.0 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/goleak v1.2.1 // indirect - go.uber.org/multierr v1.9.0 // indirect - go.uber.org/ratelimit v0.2.0 // indirect - go.uber.org/zap v1.21.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/exp v0.0.0-20230307190834-24139beb5833 // indirect - golang.org/x/mod v0.9.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.7.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect - google.golang.org/grpc v1.57.2 // indirect - google.golang.org/protobuf v1.31.0 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.26.2 // indirect - k8s.io/apimachinery v0.26.2 // indirect - k8s.io/client-go v0.26.2 // indirect - k8s.io/klog/v2 v2.90.1 // indirect - k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d // indirect - k8s.io/utils v0.0.0-20230308161112-d77c459e9343 // indirect - nhooyr.io/websocket v1.8.7 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + github.com/prometheus/common v0.45.0 // indirect ) replace ( @@ -181,6 +26,5 @@ replace ( // until merged upstream: https://github.com/mwitkow/grpc-proxy/pull/69 github.com/mwitkow/grpc-proxy => github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f - github.com/prometheus/prometheus => github.com/prometheus/prometheus v0.43.1-0.20230327151049-211ae4f1f0a2 github.com/sercand/kuberesolver/v4 => github.com/sercand/kuberesolver/v5 v5.1.1 ) diff --git a/charts/chainlink-cluster/go.sum b/charts/chainlink-cluster/go.sum index 4a7704cac49..a235ef089d7 100644 --- a/charts/chainlink-cluster/go.sum +++ b/charts/chainlink-cluster/go.sum @@ -1,2126 +1,20 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= -cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= -cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= -cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= -cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= -cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= -cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= -cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= -cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= -cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= -cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= -cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= -cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= -cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= -cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= -cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= -cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= -cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= -cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= -cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= -cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= -cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= -cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= -cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= -cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= -cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= -cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= -cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= -cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= -cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= -cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= -cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= -cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= -cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= -cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= -cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= -cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= -cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= -cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= -cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= -cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= -cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= -cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= -cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= -cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= -cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= -cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= -cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= -cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= -cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= -cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= -cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= -cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= -cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= -cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= -cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= -cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= -cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= -cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= -cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= -cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= -cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= -cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= -cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= -cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= -cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= -cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= -cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= -cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= -cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= -cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= -cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= -cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= -cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= -cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= -cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= -cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= -cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= -cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= -cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= -cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= -cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= -cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= -cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= -cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= -cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= -cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= -cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= -cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= -cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= -cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= -cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= -cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= -cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= -cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= -cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= -cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= -cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= -cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= -cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= -cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= -cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= -cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= -cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= -cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= -cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= -cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= -cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= -cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= -cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= -cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= -cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= -cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= -cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= -cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= -cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= -cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= -cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= -cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= -cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= -cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= -cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= -cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= -cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= -cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= -cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= -cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= -cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= -cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= -cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= -cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= -cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= -cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= -cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= -cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= -cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= -cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= -cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= -cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= -cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= -cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= -cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= -cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= -cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= -cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= -cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= -cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= -cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= -cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= -cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= -cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= -cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= -cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= -cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= -cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= -cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= -cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= -cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= -cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= -cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= -cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= -cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= -cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= -cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= -cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= -cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= -cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= -cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= -cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= -cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= -cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= -cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= -cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= -cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= -cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= -cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= -cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= -cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= -cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= -cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= -cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= -cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= -cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= -cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= -cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= -cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= -cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= -cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= -cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= -cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= -cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= -cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= -cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= -cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= -cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= -cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= -cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= -cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= -cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= -cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= -cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= -cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= -cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= -cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= -cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= -cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= -cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= -cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= -cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= -cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= -cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= -cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= -cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= -cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= -cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= -cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= -cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= -cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= -cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= -cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= -cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= -cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= -cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= -cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= -cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= -cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= -cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= -cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= -cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= -cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= -cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= -cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= -cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= -cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= -cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= -cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= -cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= -cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= -cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= -cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= -cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= -cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= -cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= -cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= -cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= -cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= -cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= -cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= -cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= -cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= -cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= -cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= -cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= -cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= -cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= -cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= -cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= -cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= -cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= -cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= -cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= -cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= -cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= -cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= -cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= -cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= -cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= -cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= -cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= -cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= -cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= -cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= -cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= -cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= -cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= -cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= -cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= -cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= -cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= -cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= -cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= -cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= -cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= -cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= -cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= -cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= -cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= -cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= -cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= -cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= -cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= -cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= -cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= -cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= -cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= -cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= -cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= -cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= -cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= -cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= -cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= -cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= -cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= -cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= -cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= -cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= -cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= -cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= -cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= -cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= -cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= -cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= -cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= -cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= -cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= -cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= -cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= -cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= -cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= -cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= -cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= -cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= -cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= -cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= -cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= -cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= -cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= -cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= -cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= -cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= -cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= -cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= -cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= -cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= -github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw= -github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= -github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= -github.com/Azure/go-autorest/autorest/adal v0.9.22 h1:/GblQdIudfEM3AWWZ0mrYJQSd7JS4S/Mbzh6F0ov0Xc= -github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= -github.com/K-Phoen/grabana v0.21.19 h1:tJjRO8nN9JrFjLoQGtOB9P5ILoqENZZGAtt3nK+Ry2Y= -github.com/K-Phoen/grabana v0.21.19/go.mod h1:B7gxVxacQUgHWmgqduf4WPZoKYHO1mvZnRVCoyQiwdw= -github.com/K-Phoen/sdk v0.12.3 h1:ScutEQASc9VEKJCm3OjIMD82BIS9B2XtNg3gEf6Gs+M= -github.com/K-Phoen/sdk v0.12.3/go.mod h1:qmM0wO23CtoDux528MXPpYvS4XkRWkWX6rvX9Za8EVU= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= -github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= -github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= -github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.217 h1:FcWC56MRl+k756aH3qeMQTylSdeJ58WN0iFz3fkyRz0= -github.com/aws/aws-sdk-go v1.44.217/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee h1:BnPxIde0gjtTnc9Er7cxvBk8DHLWhEux0SxayC8dP6I= -github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= -github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g= -github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= -github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= -github.com/digitalocean/godo v1.97.0 h1:p9w1yCcWMZcxFSLPToNGXA96WfUVLXqoHti6GzVomL4= -github.com/digitalocean/godo v1.97.0/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY= -github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= -github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= -github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= -github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f h1:7T++XKzy4xg7PKy+bM+Sa9/oe1OC88yz2hXQUISoXfA= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= -github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= -github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= -github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= -github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= -github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= -github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= -github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= -github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= -github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU= -github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= -github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= -github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= -github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= -github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= -github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= -github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= -github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= -github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/googleapis v1.4.0 h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI= -github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= -github.com/gogo/status v1.0.3/go.mod h1:SavQ51ycCLnc7dGyJxp8YAmudx8xqiVrRf+6IXRsugc= -github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= -github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= -github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gophercloud/gophercloud v1.2.0 h1:1oXyj4g54KBg/kFtCdMM6jtxSzeIyg8wv4z1HoGPp1E= -github.com/gophercloud/gophercloud v1.2.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/K-Phoen/grabana v0.22.1 h1:b/O+C3H2H6VNYSeMCYUO4X4wYuwFXgBcRkvYa+fjpQA= +github.com/K-Phoen/grabana v0.22.1/go.mod h1:3LTXrTzQzTKTgvKSXdRjlsJbizSOW/V23Q3iX00R5bU= +github.com/K-Phoen/sdk v0.12.4 h1:j2EYuBJm3zDTD0fGKACVFWxAXtkR0q5QzfVqxmHSeGQ= +github.com/K-Phoen/sdk v0.12.4/go.mod h1:qmM0wO23CtoDux528MXPpYvS4XkRWkWX6rvX9Za8EVU= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= -github.com/grafana/dskit v0.0.0-20230201083518-528d8a7d52f2 h1:IOks+FXJ6iO/pfbaVEf4efNw+YzYBYNCkCabyrbkFTM= -github.com/grafana/dskit v0.0.0-20230201083518-528d8a7d52f2/go.mod h1:zj+5BNZAVmQafV583uLTAOzRr963KPdEm4d6NPmtbwg= -github.com/grafana/loki v1.6.2-0.20231017135925-990ac685e6a6 h1:V5PspEXlSlNh22sMyGkgfSOVVLTsSmhbmsp1VPt8Fdc= -github.com/grafana/loki v1.6.2-0.20231017135925-990ac685e6a6/go.mod h1:+aWr7OBDuZMT+p0rKmLfW5saO2m3YOGBnt++IlgLhVk= -github.com/grafana/loki/pkg/push v0.0.0-20230127102416-571f88bc5765 h1:VXitROTlmZtLzvokNe8ZbUKpmwldM4Hy1zdNRO32jKU= -github.com/grafana/loki/pkg/push v0.0.0-20230127102416-571f88bc5765/go.mod h1:DhJMrd2QInI/1CNtTN43BZuTmkccdizW1jZ+F6aHkhY= -github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww= -github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= -github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= -github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= -github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= -github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= -github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c= -github.com/hashicorp/cronexpr v1.1.1/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= -github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= -github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= -github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= -github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= -github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= -github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= -github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= -github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= -github.com/hashicorp/nomad/api v0.0.0-20230308192510-48e7d70fcd4b h1:EkuSTU8c/63q4LMayj8ilgg/4I5PXDFVcnqKfs9qcwI= -github.com/hashicorp/nomad/api v0.0.0-20230308192510-48e7d70fcd4b/go.mod h1:bKUb1ytds5KwUioHdvdq9jmrDqCThv95si0Ub7iNeBg= -github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= -github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= -github.com/hetznercloud/hcloud-go v1.41.0 h1:KJGFRRc68QiVu4PrEP5BmCQVveCP2CM26UGQUKGpIUs= -github.com/hetznercloud/hcloud-go v1.41.0/go.mod h1:NaHg47L6C77mngZhwBG652dTAztYrsZ2/iITJKhQkHA= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/ionos-cloud/sdk-go/v6 v6.1.4 h1:BJHhFA8Q1SZC7VOXqKKr2BV2ysQ2/4hlk1e4hZte7GY= -github.com/ionos-cloud/sdk-go/v6 v6.1.4/go.mod h1:Ox3W0iiEz0GHnfY9e5LmAxwklsxguuNFEUSu0gVRTME= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= -github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= -github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/linode/linodego v1.14.1 h1:uGxQyy0BidoEpLGdvfi4cPgEW+0YUFsEGrLEhcTfjNc= -github.com/linode/linodego v1.14.1/go.mod h1:NJlzvlNtdMRRkXb0oN6UWzUkj6t+IBsyveHgZ5Ppjyk= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.51 h1:0+Xg7vObnhrz/4ZCZcZh7zPXlmU0aveS2HDBd0m0qSo= -github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= -github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= -github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= -github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02/go.mod h1:JNdpVEzCpXBgIiv4ds+TzhN1hrtxq6ClLrTlT9OQRSc= -github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cPxUYdgaGzZIT5/j0IfqOrrXmq6bG8AwvwisMXpdrg= -github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= -github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= -github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w= -github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/ovh/go-ovh v1.3.0 h1:mvZaddk4E4kLcXhzb+cxBsMPYp2pHqiQpWYkInsuZPQ= -github.com/ovh/go-ovh v1.3.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= -github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= -github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/prometheus/alertmanager v0.25.0 h1:vbXKUR6PYRiZPRIKfmXaG+dmCKG52RtPL4Btl8hQGvg= -github.com/prometheus/alertmanager v0.25.0/go.mod h1:MEZ3rFVHqKZsw7IcNS/m4AWZeXThmJhumpiWR4eHU/w= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= -github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= -github.com/prometheus/exporter-toolkit v0.8.2/go.mod h1:00shzmJL7KxcsabLWcONwpyNEuWhREOnFqZW7vadFS0= -github.com/prometheus/exporter-toolkit v0.9.1 h1:cNkC01riqiOS+kh3zdnNwRsbe/Blh0WwK3ij5rPJ9Sw= -github.com/prometheus/exporter-toolkit v0.9.1/go.mod h1:iFlTmFISCix0vyuyBmm0UqOUCTao9+RsAsKJP3YM9ec= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/prometheus/prometheus v0.43.1-0.20230327151049-211ae4f1f0a2 h1:i5hmbBzR+VeL5pPl1ZncsJ1bpg3SO66bwkE1msJBsMA= -github.com/prometheus/prometheus v0.43.1-0.20230327151049-211ae4f1f0a2/go.mod h1:Mm42Acga98xgA+u5yTaC3ki3i0rJEJWFpbdHN7q2trk= -github.com/pyroscope-io/client v0.6.0 h1:rcUFgcnfmuyVYDYT+4d0zfqc8YedOyruHSsUb9ImaBw= -github.com/pyroscope-io/client v0.6.0/go.mod h1:4h21iOU4pUOq0prKyDlvYRL+SCKsBc5wKiEtV+rJGqU= -github.com/pyroscope-io/godeltaprof v0.1.2 h1:MdlEmYELd5w+lvIzmZvXGNMVzW2Qc9jDMuJaPOR75g4= -github.com/pyroscope-io/godeltaprof v0.1.2/go.mod h1:psMITXp90+8pFenXkKIpNhrfmI9saQnPbba27VIaiQE= -github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= -github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= -github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.14 h1:yFl3jyaSVLNYXlnNYM5z2pagEk1dYQhfr1p20T1NyKY= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.14/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sercand/kuberesolver/v5 v5.1.1 h1:CYH+d67G0sGBj7q5wLK61yzqJJ8gLLC8aeprPTHb6yY= -github.com/sercand/kuberesolver/v5 v5.1.1/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYMWZJ294T3BtmVCpQ= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartcontractkit/wasp v0.3.6 h1:1TLWfrTzqZwNvyyoKzPZ8FLQat2lNz640eM+mMh2YxM= -github.com/smartcontractkit/wasp v0.3.6/go.mod h1:L/cyUGfpaWxy/2twOVJLRt2mySJEIqGrFj9nyvRLpSo= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/uber/jaeger-client-go v2.28.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= -github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= -github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= -github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= -github.com/weaveworks/common v0.0.0-20230411130259-f7d83a041205 h1:gjb7t9LCnRu14LHubyLIgrE+EYlAaREiPn/VknV7R3s= -github.com/weaveworks/common v0.0.0-20230411130259-f7d83a041205/go.mod h1:O9wmSPNVSuqxzUZPFlHnPQ8xnyvx0qBnKGFfGbj95uY= -github.com/weaveworks/promrus v1.2.0 h1:jOLf6pe6/vss4qGHjXmGz4oDJQA+AOCqEL3FvvZGz7M= -github.com/weaveworks/promrus v1.2.0/go.mod h1:SaE82+OJ91yqjrE1rsvBWVzNZKcHYFtMUyS1+Ogs/KA= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY= -go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= -go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg= -go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY= -go.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4= -go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw= -go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= -go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= -go.mongodb.org/mongo-driver v1.11.2 h1:+1v2rDQUWNcGW7/7E0Jvdz51V38XXxJfhzbV17aNHCw= -go.mongodb.org/mongo-driver v1.11.2/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 h1:lE9EJyw3/JhrjWH/hEy9FptnalDQgj7vpbgC2KCCCxE= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0/go.mod h1:pcQ3MM3SWvrA71U4GDqv9UFDJ3HQsW7y5ZO3tDTlUdI= -go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= -go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= -go.opentelemetry.io/otel/metric v0.37.0 h1:pHDQuLQOZwYD+Km0eb657A25NaRzy0a+eLyKfDXedEs= -go.opentelemetry.io/otel/metric v0.37.0/go.mod h1:DmdaHfGt54iV6UKxsV9slj2bBRJcKC1B1uvDLIioc1s= -go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= -go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= -go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA= -go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= -go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= -go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20230307190834-24139beb5833 h1:SChBja7BCQewoTAU7IgvucQKMIXrEpFxNMs0spT3/5s= -golang.org/x/exp v0.0.0-20230307190834-24139beb5833/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= -google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200324203455-a04cca1dde73/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= -google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 h1:m8v1xLLLzMe1m5P+gCTF8nJB9epwZQUBERm20Oy1poQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= -google.golang.org/grpc v1.57.2 h1:uw37EN34aMFFXB2QPW7Tq6tdTbind1GpRxw5aOX3a5k= -google.golang.org/grpc v1.57.2/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/smartcontractkit/wasp v0.4.6 h1:s6J8HgpxMHORl19nCpZPxc5jaVUQv8EXB6QjTuLXXnw= +github.com/smartcontractkit/wasp v0.4.6/go.mod h1:+ViWdUf1ap6powiEiwPskpZfH/Q1sG29YoVav7zGOIo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -k8s.io/api v0.26.2 h1:dM3cinp3PGB6asOySalOZxEG4CZ0IAdJsrYZXE/ovGQ= -k8s.io/api v0.26.2/go.mod h1:1kjMQsFE+QHPfskEcVNgL3+Hp88B80uj0QtSOlj8itU= -k8s.io/apimachinery v0.26.2 h1:da1u3D5wfR5u2RpLhE/ZtZS2P7QvDgLZTi9wrNZl/tQ= -k8s.io/apimachinery v0.26.2/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= -k8s.io/client-go v0.26.2 h1:s1WkVujHX3kTp4Zn4yGNFK+dlDXy1bAAkIl+cFAiuYI= -k8s.io/client-go v0.26.2/go.mod h1:u5EjOuSyBa09yqqyY7m3abZeovO/7D/WehVVlZ2qcqU= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d h1:VcFq5n7wCJB2FQMCIHfC+f+jNcGgNMar1uKd6rVlifU= -k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY= -k8s.io/utils v0.0.0-20230308161112-d77c459e9343 h1:m7tbIjXGcGIAtpmQr7/NAi7RsWoW3E7Zcm4jI1HicTc= -k8s.io/utils v0.0.0-20230308161112-d77c459e9343/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= -nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= -nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= From 014e84241f35205442e6e1116b7c2e0a0f809ad5 Mon Sep 17 00:00:00 2001 From: Domino Valdano <2644901+reductionista@users.noreply.github.com> Date: Wed, 28 Feb 2024 10:07:57 -0800 Subject: [PATCH 14/16] Allow customizing `BackupPollerDelayBlock` or disabling Backup LogPoller entirely for tests (#11850) * Refactor NewLogPoller() params into logpoller.Opts struct, add BackupPollerBlockDela * Fix chain reader test & logpoller test This failing chainreader test had FinalityDepth set to 0, so LogPoller was starting after the block where the logs were emitted. It was only passing due to Backup LogPoller. With Backup LogPoller disabled, we have to increase the finality depth so that LogPoller will start a few blocks back (at the first unfinalized block). The failing logpoller test was looking for an extra log entry emitted by BackupLogPoller. Now we can run it with BackupLogPoller disabled (leading to a more robust test) and stop looking for that * Fix LogPoller_Replay test * Address SonarQube CodeSmell: reduce cognitive complexity of lp.run() * Add BackupLogPollerBlockDelay to toml config * Fix some merge conflicts * Restore KeepFinalizedBlocksDepth: 1000 * Use default BackupPollerBlockDelay: 0 instead of explicitly setting to 0 * disable backup log poller in lp smoke tests * Fix generated docs * Add missing web resolver testdata --------- Co-authored-by: Bartek Tofel --- core/chains/evm/config/chain_scoped.go | 4 + core/chains/evm/config/config.go | 1 + core/chains/evm/config/toml/config.go | 39 ++-- core/chains/evm/config/toml/defaults.go | 3 + .../evm/config/toml/defaults/fallback.toml | 1 + .../evm/forwarders/forwarder_manager_test.go | 19 +- core/chains/evm/logpoller/helper_test.go | 8 +- core/chains/evm/logpoller/log_poller.go | 144 ++++++++------ .../evm/logpoller/log_poller_internal_test.go | 66 +++++-- core/chains/evm/logpoller/log_poller_test.go | 186 +++++++++++++++--- core/chains/evm/logpoller/orm_test.go | 39 ++-- core/chains/evm/txmgr/txmgr_test.go | 9 +- core/chains/legacyevm/chain.go | 22 +-- core/config/docs/chains-evm.toml | 4 + core/services/chainlink/config_test.go | 26 +-- .../chainlink/testdata/config-full.toml | 1 + .../config-multi-chain-effective.toml | 3 + ...annel_definition_cache_integration_test.go | 28 ++- .../v21/logprovider/integration_test.go | 9 +- .../promreporter/prom_reporter_test.go | 9 +- core/services/relay/evm/chain_reader_test.go | 14 +- core/services/relay/evm/config_poller_test.go | 10 +- .../relay/evm/functions/config_poller_test.go | 9 +- .../relay/evm/mercury/helpers_test.go | 10 +- core/services/vrf/v2/bhs_feeder_test.go | 6 +- .../vrf/v2/integration_helpers_test.go | 5 +- .../vrf/v2/listener_v2_log_listener_test.go | 11 +- core/web/resolver/chain_test.go | 2 + core/web/resolver/testdata/config-full.toml | 1 + .../config-multi-chain-effective.toml | 3 + docs/CONFIG.md | 59 ++++++ integration-tests/smoke/log_poller_test.go | 3 +- .../testconfig/log_poller/config.go | 12 +- .../testconfig/log_poller/log_poller.toml | 3 + .../universal/log_poller/helpers.go | 2 + .../disk-based-logging-disabled.txtar | 1 + .../validate/disk-based-logging-no-dir.txtar | 1 + .../node/validate/disk-based-logging.txtar | 1 + testdata/scripts/node/validate/invalid.txtar | 1 + testdata/scripts/node/validate/valid.txtar | 1 + 40 files changed, 584 insertions(+), 192 deletions(-) diff --git a/core/chains/evm/config/chain_scoped.go b/core/chains/evm/config/chain_scoped.go index 69cc4c0a6ad..9247e77ba9d 100644 --- a/core/chains/evm/config/chain_scoped.go +++ b/core/chains/evm/config/chain_scoped.go @@ -134,6 +134,10 @@ func (e *evmConfig) LogKeepBlocksDepth() uint32 { return *e.c.LogKeepBlocksDepth } +func (e *evmConfig) BackupLogPollerBlockDelay() uint64 { + return *e.c.BackupLogPollerBlockDelay +} + func (e *evmConfig) NonceAutoSync() bool { return *e.c.NonceAutoSync } diff --git a/core/chains/evm/config/config.go b/core/chains/evm/config/config.go index cb1a6073e0b..c878fea46e9 100644 --- a/core/chains/evm/config/config.go +++ b/core/chains/evm/config/config.go @@ -36,6 +36,7 @@ type EVM interface { LinkContractAddress() string LogBackfillBatchSize() uint32 LogKeepBlocksDepth() uint32 + BackupLogPollerBlockDelay() uint64 LogPollInterval() time.Duration LogPrunePageSize() uint32 MinContractPayment() *commonassets.Link diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 95b6c342161..1ab4aa20d01 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -341,25 +341,26 @@ func (c *EVMConfig) TOMLString() (string, error) { } type Chain struct { - AutoCreateKey *bool - BlockBackfillDepth *uint32 - BlockBackfillSkip *bool - ChainType *string - FinalityDepth *uint32 - FinalityTagEnabled *bool - FlagsContractAddress *ethkey.EIP55Address - LinkContractAddress *ethkey.EIP55Address - LogBackfillBatchSize *uint32 - LogPollInterval *commonconfig.Duration - LogKeepBlocksDepth *uint32 - LogPrunePageSize *uint32 - MinIncomingConfirmations *uint32 - MinContractPayment *commonassets.Link - NonceAutoSync *bool - NoNewHeadsThreshold *commonconfig.Duration - OperatorFactoryAddress *ethkey.EIP55Address - RPCDefaultBatchSize *uint32 - RPCBlockQueryDelay *uint16 + AutoCreateKey *bool + BlockBackfillDepth *uint32 + BlockBackfillSkip *bool + ChainType *string + FinalityDepth *uint32 + FinalityTagEnabled *bool + FlagsContractAddress *ethkey.EIP55Address + LinkContractAddress *ethkey.EIP55Address + LogBackfillBatchSize *uint32 + LogPollInterval *commonconfig.Duration + LogKeepBlocksDepth *uint32 + LogPrunePageSize *uint32 + BackupLogPollerBlockDelay *uint64 + MinIncomingConfirmations *uint32 + MinContractPayment *commonassets.Link + NonceAutoSync *bool + NoNewHeadsThreshold *commonconfig.Duration + OperatorFactoryAddress *ethkey.EIP55Address + RPCDefaultBatchSize *uint32 + RPCBlockQueryDelay *uint16 Transactions Transactions `toml:",omitempty"` BalanceMonitor BalanceMonitor `toml:",omitempty"` diff --git a/core/chains/evm/config/toml/defaults.go b/core/chains/evm/config/toml/defaults.go index 242373fd4af..951246eeb22 100644 --- a/core/chains/evm/config/toml/defaults.go +++ b/core/chains/evm/config/toml/defaults.go @@ -140,6 +140,9 @@ func (c *Chain) SetFrom(f *Chain) { if v := f.LogPrunePageSize; v != nil { c.LogPrunePageSize = v } + if v := f.BackupLogPollerBlockDelay; v != nil { + c.BackupLogPollerBlockDelay = v + } if v := f.MinIncomingConfirmations; v != nil { c.MinIncomingConfirmations = v } diff --git a/core/chains/evm/config/toml/defaults/fallback.toml b/core/chains/evm/config/toml/defaults/fallback.toml index 8587a5b4b20..1a1d9b69439 100644 --- a/core/chains/evm/config/toml/defaults/fallback.toml +++ b/core/chains/evm/config/toml/defaults/fallback.toml @@ -7,6 +7,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinContractPayment = '.00001 link' MinIncomingConfirmations = 3 NonceAutoSync = true diff --git a/core/chains/evm/forwarders/forwarder_manager_test.go b/core/chains/evm/forwarders/forwarder_manager_test.go index 4480e533525..0eb51a535e0 100644 --- a/core/chains/evm/forwarders/forwarder_manager_test.go +++ b/core/chains/evm/forwarders/forwarder_manager_test.go @@ -60,7 +60,15 @@ func TestFwdMgr_MaybeForwardTransaction(t *testing.T) { t.Log(authorized) evmClient := client.NewSimulatedBackendClient(t, ec, testutils.FixtureChainID) - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), evmClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000, 0) + + lpOpts := logpoller.Opts{ + PollPeriod: 100 * time.Millisecond, + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), evmClient, lggr, lpOpts) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM(), evmcfg.Database()) fwdMgr.ORM = forwarders.NewORM(db, logger.Test(t), cfg.Database()) @@ -113,7 +121,14 @@ func TestFwdMgr_AccountUnauthorizedToForward_SkipsForwarding(t *testing.T) { ec.Commit() evmClient := client.NewSimulatedBackendClient(t, ec, testutils.FixtureChainID) - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), evmClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000, 0) + lpOpts := logpoller.Opts{ + PollPeriod: 100 * time.Millisecond, + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), evmClient, lggr, lpOpts) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM(), evmcfg.Database()) fwdMgr.ORM = forwarders.NewORM(db, logger.Test(t), cfg.Database()) diff --git a/core/chains/evm/logpoller/helper_test.go b/core/chains/evm/logpoller/helper_test.go index 3215a7ec20c..a2a470741f6 100644 --- a/core/chains/evm/logpoller/helper_test.go +++ b/core/chains/evm/logpoller/helper_test.go @@ -46,7 +46,7 @@ type TestHarness struct { EthDB ethdb.Database } -func SetupTH(t testing.TB, useFinalityTag bool, finalityDepth, backfillBatchSize, rpcBatchSize, keepFinalizedBlocksDepth int64) TestHarness { +func SetupTH(t testing.TB, opts logpoller.Opts) TestHarness { lggr := logger.Test(t) chainID := testutils.NewRandomEVMChainID() chainID2 := testutils.NewRandomEVMChainID() @@ -67,7 +67,11 @@ func SetupTH(t testing.TB, useFinalityTag bool, finalityDepth, backfillBatchSize // Mark genesis block as finalized to avoid any nulls in the tests head := esc.Backend().Blockchain().CurrentHeader() esc.Backend().Blockchain().SetFinalized(head) - lp := logpoller.NewLogPoller(o, esc, lggr, 1*time.Hour, useFinalityTag, finalityDepth, backfillBatchSize, rpcBatchSize, keepFinalizedBlocksDepth, 0) + + if opts.PollPeriod == 0 { + opts.PollPeriod = 1 * time.Hour + } + lp := logpoller.NewLogPoller(o, esc, lggr, opts) emitterAddress1, _, emitter1, err := log_emitter.DeployLogEmitter(owner, ec) require.NoError(t, err) emitterAddress2, _, emitter2, err := log_emitter.DeployLogEmitter(owner, ec) diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index b1d00206130..444d0153542 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -73,7 +73,7 @@ const ( type LogPollerTest interface { LogPoller PollAndSaveLogs(ctx context.Context, currentBlockNumber int64) - BackupPollAndSaveLogs(ctx context.Context, backupPollerBlockDelay int64) + BackupPollAndSaveLogs(ctx context.Context) Filter(from, to *big.Int, bh *common.Hash) ethereum.FilterQuery GetReplayFromBlock(ctx context.Context, requested int64) (int64, error) PruneOldBlocks(ctx context.Context) (bool, error) @@ -105,8 +105,9 @@ type logPoller struct { keepFinalizedBlocksDepth int64 // the number of blocks behind the last finalized block we keep in database backfillBatchSize int64 // batch size to use when backfilling finalized logs rpcBatchSize int64 // batch size to use for fallback RPC calls made in GetBlocks - backupPollerNextBlock int64 logPrunePageSize int64 + backupPollerNextBlock int64 // next block to be processed by Backup LogPoller + backupPollerBlockDelay int64 // how far behind regular LogPoller should BackupLogPoller run. 0 = disabled filterMu sync.RWMutex filters map[string]Filter @@ -121,6 +122,17 @@ type logPoller struct { wg sync.WaitGroup } +type Opts struct { + PollPeriod time.Duration + UseFinalityTag bool + FinalityDepth int64 + BackfillBatchSize int64 + RpcBatchSize int64 + KeepFinalizedBlocksDepth int64 + BackupPollerBlockDelay int64 + LogPrunePageSize int64 +} + // NewLogPoller creates a log poller. Note there is an assumption // that blocks can be processed faster than they are produced for the given chain, or the poller will fall behind. // Block processing involves the following calls in steady state (without reorgs): @@ -131,7 +143,7 @@ type logPoller struct { // // How fast that can be done depends largely on network speed and DB, but even for the fastest // support chain, polygon, which has 2s block times, we need RPCs roughly with <= 500ms latency -func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, pollPeriod time.Duration, useFinalityTag bool, finalityDepth int64, backfillBatchSize int64, rpcBatchSize int64, keepFinalizedBlocksDepth int64, logsPrunePageSize int64) *logPoller { +func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, opts Opts) *logPoller { ctx, cancel := context.WithCancel(context.Background()) return &logPoller{ ctx: ctx, @@ -141,13 +153,14 @@ func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, pollPeriod time.Durati lggr: logger.Sugared(logger.Named(lggr, "LogPoller")), replayStart: make(chan int64), replayComplete: make(chan error), - pollPeriod: pollPeriod, - finalityDepth: finalityDepth, - useFinalityTag: useFinalityTag, - backfillBatchSize: backfillBatchSize, - rpcBatchSize: rpcBatchSize, - keepFinalizedBlocksDepth: keepFinalizedBlocksDepth, - logPrunePageSize: logsPrunePageSize, + pollPeriod: opts.PollPeriod, + backupPollerBlockDelay: opts.BackupPollerBlockDelay, + finalityDepth: opts.FinalityDepth, + useFinalityTag: opts.UseFinalityTag, + backfillBatchSize: opts.BackfillBatchSize, + rpcBatchSize: opts.RpcBatchSize, + keepFinalizedBlocksDepth: opts.KeepFinalizedBlocksDepth, + logPrunePageSize: opts.LogPrunePageSize, filters: make(map[string]Filter), filterDirty: true, // Always build Filter on first call to cache an empty filter if nothing registered yet. } @@ -436,6 +449,20 @@ func (lp *logPoller) GetReplayFromBlock(ctx context.Context, requested int64) (i return mathutil.Min(requested, lastProcessed.BlockNumber), nil } +func (lp *logPoller) loadFilters() error { + lp.filterMu.Lock() + defer lp.filterMu.Unlock() + filters, err := lp.orm.LoadFilters(pg.WithParentCtx(lp.ctx)) + + if err != nil { + return pkgerrors.Wrapf(err, "Failed to load initial filters from db, retrying") + } + + lp.filters = filters + lp.filterDirty = true + return nil +} + func (lp *logPoller) run() { defer lp.wg.Done() logPollTick := time.After(0) @@ -443,60 +470,20 @@ func (lp *logPoller) run() { backupLogPollTick := time.After(100 * time.Millisecond) filtersLoaded := false - loadFilters := func() error { - lp.filterMu.Lock() - defer lp.filterMu.Unlock() - filters, err := lp.orm.LoadFilters(pg.WithParentCtx(lp.ctx)) - - if err != nil { - return pkgerrors.Wrapf(err, "Failed to load initial filters from db, retrying") - } - - lp.filters = filters - lp.filterDirty = true - filtersLoaded = true - return nil - } - for { select { case <-lp.ctx.Done(): return case fromBlockReq := <-lp.replayStart: - fromBlock, err := lp.GetReplayFromBlock(lp.ctx, fromBlockReq) - if err == nil { - if !filtersLoaded { - lp.lggr.Warnw("Received replayReq before filters loaded", "fromBlock", fromBlock, "requested", fromBlockReq) - if err = loadFilters(); err != nil { - lp.lggr.Errorw("Failed loading filters during Replay", "err", err, "fromBlock", fromBlock) - } - } - if err == nil { - // Serially process replay requests. - lp.lggr.Infow("Executing replay", "fromBlock", fromBlock, "requested", fromBlockReq) - lp.PollAndSaveLogs(lp.ctx, fromBlock) - lp.lggr.Infow("Executing replay finished", "fromBlock", fromBlock, "requested", fromBlockReq) - } - } else { - lp.lggr.Errorw("Error executing replay, could not get fromBlock", "err", err) - } - select { - case <-lp.ctx.Done(): - // We're shutting down, notify client and exit - select { - case lp.replayComplete <- ErrReplayRequestAborted: - default: - } - return - case lp.replayComplete <- err: - } + lp.handleReplayRequest(fromBlockReq, filtersLoaded) case <-logPollTick: logPollTick = time.After(utils.WithJitter(lp.pollPeriod)) if !filtersLoaded { - if err := loadFilters(); err != nil { + if err := lp.loadFilters(); err != nil { lp.lggr.Errorw("Failed loading filters in main logpoller loop, retrying later", "err", err) continue } + filtersLoaded = true } // Always start from the latest block in the db. @@ -529,22 +516,23 @@ func (lp *logPoller) run() { } lp.PollAndSaveLogs(lp.ctx, start) case <-backupLogPollTick: + if lp.backupPollerBlockDelay == 0 { + continue // backup poller is disabled + } // Backup log poller: this serves as an emergency backup to protect against eventual-consistency behavior // of an rpc node (seen occasionally on optimism, but possibly could happen on other chains?). If the first // time we request a block, no logs or incomplete logs come back, this ensures that every log is eventually - // re-requested after it is finalized. This doesn't add much overhead, because we can request all of them - // in one shot, since we don't need to worry about re-orgs after finality depth, and it runs 100x less - // frequently than the primary log poller. - - // If pollPeriod is set to 1 block time, backup log poller will run once every 100 blocks - const backupPollerBlockDelay = 100 + // re-requested after it is finalized. This doesn't add much overhead, because we can request all of them + // in one shot, since we don't need to worry about re-orgs after finality depth, and it runs far less + // frequently than the primary log poller (instead of roughly once per block it runs once roughly once every + // lp.backupPollerDelay blocks--with default settings about 100x less frequently). - backupLogPollTick = time.After(utils.WithJitter(backupPollerBlockDelay * lp.pollPeriod)) + backupLogPollTick = time.After(utils.WithJitter(time.Duration(lp.backupPollerBlockDelay) * lp.pollPeriod)) if !filtersLoaded { lp.lggr.Warnw("Backup log poller ran before filters loaded, skipping") continue } - lp.BackupPollAndSaveLogs(lp.ctx, backupPollerBlockDelay) + lp.BackupPollAndSaveLogs(lp.ctx) } } } @@ -582,7 +570,37 @@ func (lp *logPoller) backgroundWorkerRun() { } } -func (lp *logPoller) BackupPollAndSaveLogs(ctx context.Context, backupPollerBlockDelay int64) { +func (lp *logPoller) handleReplayRequest(fromBlockReq int64, filtersLoaded bool) { + fromBlock, err := lp.GetReplayFromBlock(lp.ctx, fromBlockReq) + if err == nil { + if !filtersLoaded { + lp.lggr.Warnw("Received replayReq before filters loaded", "fromBlock", fromBlock, "requested", fromBlockReq) + if err = lp.loadFilters(); err != nil { + lp.lggr.Errorw("Failed loading filters during Replay", "err", err, "fromBlock", fromBlock) + } + } + if err == nil { + // Serially process replay requests. + lp.lggr.Infow("Executing replay", "fromBlock", fromBlock, "requested", fromBlockReq) + lp.PollAndSaveLogs(lp.ctx, fromBlock) + lp.lggr.Infow("Executing replay finished", "fromBlock", fromBlock, "requested", fromBlockReq) + } + } else { + lp.lggr.Errorw("Error executing replay, could not get fromBlock", "err", err) + } + select { + case <-lp.ctx.Done(): + // We're shutting down, notify client and exit + select { + case lp.replayComplete <- ErrReplayRequestAborted: + default: + } + return + case lp.replayComplete <- err: + } +} + +func (lp *logPoller) BackupPollAndSaveLogs(ctx context.Context) { if lp.backupPollerNextBlock == 0 { lastProcessed, err := lp.orm.SelectLatestBlock(pg.WithParentCtx(ctx)) if err != nil { @@ -594,7 +612,7 @@ func (lp *logPoller) BackupPollAndSaveLogs(ctx context.Context, backupPollerBloc return } // If this is our first run, start from block min(lastProcessed.FinalizedBlockNumber-1, lastProcessed.BlockNumber-backupPollerBlockDelay) - backupStartBlock := mathutil.Min(lastProcessed.FinalizedBlockNumber-1, lastProcessed.BlockNumber-backupPollerBlockDelay) + backupStartBlock := mathutil.Min(lastProcessed.FinalizedBlockNumber-1, lastProcessed.BlockNumber-lp.backupPollerBlockDelay) // (or at block 0 if whole blockchain is too short) lp.backupPollerNextBlock = mathutil.Max(backupStartBlock, 0) } diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index 0bde65e5556..af2d9a558e1 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -64,7 +64,13 @@ func TestLogPoller_RegisterFilter(t *testing.T) { orm := NewORM(chainID, db, lggr, pgtest.NewQConfig(true)) // Set up a test chain with a log emitting contract deployed. - lp := NewLogPoller(orm, nil, lggr, time.Hour, false, 1, 1, 2, 1000, 0) + lpOpts := Opts{ + PollPeriod: time.Hour, + BackfillBatchSize: 1, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := NewLogPoller(orm, nil, lggr, lpOpts) // We expect a zero Filter if nothing registered yet. f := lp.Filter(nil, nil, nil) @@ -217,9 +223,15 @@ func TestLogPoller_BackupPollerStartup(t *testing.T) { ec.On("ConfiguredChainID").Return(chainID, nil) ctx := testutils.Context(t) - - lp := NewLogPoller(orm, ec, lggr, 1*time.Hour, false, 2, 3, 2, 1000, 0) - lp.BackupPollAndSaveLogs(ctx, 100) + lpOpts := Opts{ + PollPeriod: time.Hour, + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := NewLogPoller(orm, ec, lggr, lpOpts) + lp.BackupPollAndSaveLogs(ctx) assert.Equal(t, int64(0), lp.backupPollerNextBlock) assert.Equal(t, 1, observedLogs.FilterMessageSnippet("ran before first successful log poller run").Len()) @@ -229,7 +241,7 @@ func TestLogPoller_BackupPollerStartup(t *testing.T) { require.NoError(t, err) require.Equal(t, int64(3), lastProcessed.BlockNumber) - lp.BackupPollAndSaveLogs(ctx, 100) + lp.BackupPollAndSaveLogs(ctx) assert.Equal(t, int64(1), lp.backupPollerNextBlock) // Ensure non-negative! } @@ -258,7 +270,15 @@ func TestLogPoller_Replay(t *testing.T) { ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(&head, nil) ec.On("FilterLogs", mock.Anything, mock.Anything).Return([]types.Log{log1}, nil).Once() ec.On("ConfiguredChainID").Return(chainID, nil) - lp := NewLogPoller(orm, ec, lggr, time.Hour, false, 3, 3, 3, 20, 0) + lpOpts := Opts{ + PollPeriod: time.Hour, + FinalityDepth: 3, + BackfillBatchSize: 3, + RpcBatchSize: 3, + KeepFinalizedBlocksDepth: 20, + BackupPollerBlockDelay: 100, + } + lp := NewLogPoller(orm, ec, lggr, lpOpts) // process 1 log in block 3 lp.PollAndSaveLogs(testutils.Context(t), 4) @@ -440,17 +460,26 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { db := pgtest.NewSqlxDB(t) orm := NewORM(chainID, db, lggr, pgtest.NewQConfig(true)) + lpOpts := Opts{ + PollPeriod: time.Hour, + BackfillBatchSize: 3, + RpcBatchSize: 3, + KeepFinalizedBlocksDepth: 20, + } + t.Run("pick latest block from chain and use finality from config with finality disabled", func(t *testing.T) { head := evmtypes.Head{Number: 4} - finalityDepth := int64(3) + + lpOpts.UseFinalityTag = false + lpOpts.FinalityDepth = int64(3) ec := evmclimocks.NewClient(t) ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(&head, nil) - lp := NewLogPoller(orm, ec, lggr, time.Hour, false, finalityDepth, 3, 3, 20, 0) + lp := NewLogPoller(orm, ec, lggr, lpOpts) latestBlock, lastFinalizedBlockNumber, err := lp.latestBlocks(testutils.Context(t)) require.NoError(t, err) require.Equal(t, latestBlock.Number, head.Number) - require.Equal(t, finalityDepth, latestBlock.Number-lastFinalizedBlockNumber) + require.Equal(t, lpOpts.FinalityDepth, latestBlock.Number-lastFinalizedBlockNumber) }) t.Run("finality tags in use", func(t *testing.T) { @@ -470,7 +499,8 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { *(elems[1].Result.(*evmtypes.Head)) = evmtypes.Head{Number: expectedLastFinalizedBlockNumber, Hash: utils.RandomBytes32()} }) - lp := NewLogPoller(orm, ec, lggr, time.Hour, true, 3, 3, 3, 20, 0) + lpOpts.UseFinalityTag = true + lp := NewLogPoller(orm, ec, lggr, lpOpts) latestBlock, lastFinalizedBlockNumber, err := lp.latestBlocks(testutils.Context(t)) require.NoError(t, err) @@ -488,7 +518,8 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { elems[1].Error = fmt.Errorf("some error") }) - lp := NewLogPoller(orm, ec, lggr, time.Hour, true, 3, 3, 3, 20, 0) + lpOpts.UseFinalityTag = true + lp := NewLogPoller(orm, ec, lggr, lpOpts) _, _, err := lp.latestBlocks(testutils.Context(t)) require.Error(t, err) }) @@ -496,8 +527,8 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { t.Run("BatchCall returns an error", func(t *testing.T) { ec := evmclimocks.NewClient(t) ec.On("BatchCallContext", mock.Anything, mock.Anything).Return(fmt.Errorf("some error")) - - lp := NewLogPoller(orm, ec, lggr, time.Hour, true, 3, 3, 3, 20, 0) + lpOpts.UseFinalityTag = true + lp := NewLogPoller(orm, ec, lggr, lpOpts) _, _, err := lp.latestBlocks(testutils.Context(t)) require.Error(t, err) }) @@ -506,7 +537,14 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { func benchmarkFilter(b *testing.B, nFilters, nAddresses, nEvents int) { lggr := logger.Test(b) - lp := NewLogPoller(nil, nil, lggr, 1*time.Hour, false, 2, 3, 2, 1000, 0) + lpOpts := Opts{ + PollPeriod: time.Hour, + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := NewLogPoller(nil, nil, lggr, lpOpts) for i := 0; i < nFilters; i++ { var addresses []common.Address var events []common.Hash diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 5b894b8a19a..21246e24ec0 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -147,7 +147,14 @@ func TestPopulateLoadedDB(t *testing.T) { } func TestLogPoller_Integration(t *testing.T) { - th := SetupTH(t, false, 2, 3, 2, 1000) + lpOpts := logpoller.Opts{ + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + BackupPollerBlockDelay: 100, + } + th := SetupTH(t, lpOpts) th.Client.Commit() // Block 2. Ensure we have finality number of blocks require.NoError(t, th.LogPoller.RegisterFilter(logpoller.Filter{Name: "Integration test", EventSigs: []common.Hash{EmitterABI.Events["Log1"].ID}, Addresses: []common.Address{th.EmitterAddress1}})) @@ -242,7 +249,16 @@ func Test_BackupLogPoller(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - th := SetupTH(t, tt.finalityTag, tt.finalityDepth, 3, 2, 1000) + th := SetupTH(t, + logpoller.Opts{ + UseFinalityTag: tt.finalityTag, + FinalityDepth: tt.finalityDepth, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + BackupPollerBlockDelay: 100, + }, + ) // later, we will need at least 32 blocks filled with logs for cache invalidation for i := int64(0); i < 32; i++ { // to invalidate geth's internal read-cache, a matching log must be found in the bloom Filter @@ -350,7 +366,7 @@ func Test_BackupLogPoller(t *testing.T) { // Run ordinary poller + backup poller at least once currentBlock, _ := th.LogPoller.LatestBlock(pg.WithParentCtx(testutils.Context(t))) th.LogPoller.PollAndSaveLogs(ctx, currentBlock.BlockNumber+1) - th.LogPoller.BackupPollAndSaveLogs(ctx, 100) + th.LogPoller.BackupPollAndSaveLogs(ctx) currentBlock, _ = th.LogPoller.LatestBlock(pg.WithParentCtx(testutils.Context(t))) require.Equal(t, int64(37), currentBlock.BlockNumber+1) @@ -366,7 +382,7 @@ func Test_BackupLogPoller(t *testing.T) { // Run ordinary poller + backup poller at least once more th.LogPoller.PollAndSaveLogs(ctx, currentBlockNumber+1) - th.LogPoller.BackupPollAndSaveLogs(ctx, 100) + th.LogPoller.BackupPollAndSaveLogs(ctx) currentBlock, _ = th.LogPoller.LatestBlock(pg.WithParentCtx(testutils.Context(t))) require.Equal(t, int64(38), currentBlock.BlockNumber+1) @@ -391,9 +407,15 @@ func Test_BackupLogPoller(t *testing.T) { func TestLogPoller_BackupPollAndSaveLogsWithPollerNotWorking(t *testing.T) { emittedLogs := 30 // Intentionally use very low backupLogPollerDelay to verify if finality is used properly - backupLogPollerDelay := int64(0) ctx := testutils.Context(t) - th := SetupTH(t, true, 0, 3, 2, 1000) + lpOpts := logpoller.Opts{ + UseFinalityTag: true, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + BackupPollerBlockDelay: 1, + } + th := SetupTH(t, lpOpts) header, err := th.Client.HeaderByNumber(ctx, nil) require.NoError(t, err) @@ -424,7 +446,7 @@ func TestLogPoller_BackupPollAndSaveLogsWithPollerNotWorking(t *testing.T) { // LogPoller should backfill starting from the last finalized block stored in db (genesis block) // till the latest finalized block reported by chain. - th.LogPoller.BackupPollAndSaveLogs(ctx, backupLogPollerDelay) + th.LogPoller.BackupPollAndSaveLogs(ctx) require.NoError(t, err) logs, err := th.LogPoller.Logs( @@ -440,7 +462,7 @@ func TestLogPoller_BackupPollAndSaveLogsWithPollerNotWorking(t *testing.T) { // Progressing even more, move blockchain forward by 1 block and mark it as finalized th.Client.Commit() markBlockAsFinalized(t, th, currentBlock) - th.LogPoller.BackupPollAndSaveLogs(ctx, backupLogPollerDelay) + th.LogPoller.BackupPollAndSaveLogs(ctx) // All emitted logs should be backfilled logs, err = th.LogPoller.Logs( @@ -457,7 +479,14 @@ func TestLogPoller_BackupPollAndSaveLogsWithPollerNotWorking(t *testing.T) { func TestLogPoller_BackupPollAndSaveLogsWithDeepBlockDelay(t *testing.T) { emittedLogs := 30 ctx := testutils.Context(t) - th := SetupTH(t, true, 0, 3, 2, 1000) + lpOpts := logpoller.Opts{ + UseFinalityTag: true, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + BackupPollerBlockDelay: int64(emittedLogs), + } + th := SetupTH(t, lpOpts) // Emit some logs in blocks for i := 0; i < emittedLogs; i++ { @@ -493,7 +522,7 @@ func TestLogPoller_BackupPollAndSaveLogsWithDeepBlockDelay(t *testing.T) { require.NoError(t, err) // Should fallback to the backupPollerBlockDelay when finalization was very high in a previous PollAndSave - th.LogPoller.BackupPollAndSaveLogs(ctx, int64(emittedLogs)) + th.LogPoller.BackupPollAndSaveLogs(ctx) require.NoError(t, err) // All emitted logs should be backfilled @@ -512,8 +541,14 @@ func TestLogPoller_BackupPollAndSaveLogsSkippingLogsThatAreTooOld(t *testing.T) logsBatch := 10 // Intentionally use very low backupLogPollerDelay to verify if finality is used properly ctx := testutils.Context(t) - th := SetupTH(t, true, 0, 3, 2, 1000) - + lpOpts := logpoller.Opts{ + UseFinalityTag: true, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + BackupPollerBlockDelay: 1, + } + th := SetupTH(t, lpOpts) //header, err := th.Client.HeaderByNumber(ctx, nil) //require.NoError(t, err) @@ -551,7 +586,7 @@ func TestLogPoller_BackupPollAndSaveLogsSkippingLogsThatAreTooOld(t *testing.T) require.NoError(t, err) // Should pick logs starting from one block behind the latest finalized block - th.LogPoller.BackupPollAndSaveLogs(ctx, 0) + th.LogPoller.BackupPollAndSaveLogs(ctx) require.NoError(t, err) // Only the 2nd batch + 1 log from a previous batch should be backfilled, because we perform backfill starting @@ -571,7 +606,13 @@ func TestLogPoller_BackupPollAndSaveLogsSkippingLogsThatAreTooOld(t *testing.T) func TestLogPoller_BlockTimestamps(t *testing.T) { t.Parallel() ctx := testutils.Context(t) - th := SetupTH(t, false, 2, 3, 2, 1000) + lpOpts := logpoller.Opts{ + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + th := SetupTH(t, lpOpts) addresses := []common.Address{th.EmitterAddress1, th.EmitterAddress2} events := []common.Hash{EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID} @@ -676,7 +717,15 @@ func TestLogPoller_SynchronizedWithGeth(t *testing.T) { }, 10e6) _, _, emitter1, err := log_emitter.DeployLogEmitter(owner, ec) require.NoError(t, err) - lp := logpoller.NewLogPoller(orm, client.NewSimulatedBackendClient(t, ec, chainID), lggr, 15*time.Second, false, int64(finalityDepth), 3, 2, 1000, 0) + + lpOpts := logpoller.Opts{ + PollPeriod: 15 * time.Second, + FinalityDepth: int64(finalityDepth), + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := logpoller.NewLogPoller(orm, client.NewSimulatedBackendClient(t, ec, chainID), lggr, lpOpts) for i := 0; i < finalityDepth; i++ { // Have enough blocks that we could reorg the full finalityDepth-1. ec.Commit() } @@ -763,7 +812,14 @@ func TestLogPoller_PollAndSaveLogs(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - th := SetupTH(t, tt.finalityTag, tt.finalityDepth, 3, 2, 1000) + lpOpts := logpoller.Opts{ + UseFinalityTag: tt.finalityTag, + FinalityDepth: tt.finalityDepth, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + th := SetupTH(t, lpOpts) // Set up a log poller listening for log emitter logs. err := th.LogPoller.RegisterFilter(logpoller.Filter{ @@ -1012,7 +1068,14 @@ func TestLogPoller_PollAndSaveLogsDeepReorg(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - th := SetupTH(t, tt.finalityTag, tt.finalityDepth, 3, 2, 1000) + lpOpts := logpoller.Opts{ + UseFinalityTag: tt.finalityTag, + FinalityDepth: tt.finalityDepth, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + th := SetupTH(t, lpOpts) // Set up a log poller listening for log emitter logs. err := th.LogPoller.RegisterFilter(logpoller.Filter{ @@ -1072,7 +1135,15 @@ func TestLogPoller_PollAndSaveLogsDeepReorg(t *testing.T) { func TestLogPoller_LoadFilters(t *testing.T) { t.Parallel() - th := SetupTH(t, false, 2, 3, 2, 1000) + + lpOpts := logpoller.Opts{ + UseFinalityTag: false, + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + th := SetupTH(t, lpOpts) filter1 := logpoller.Filter{ Name: "first Filter", @@ -1133,7 +1204,14 @@ func TestLogPoller_LoadFilters(t *testing.T) { func TestLogPoller_GetBlocks_Range(t *testing.T) { t.Parallel() - th := SetupTH(t, false, 2, 3, 2, 1000) + lpOpts := logpoller.Opts{ + UseFinalityTag: false, + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + th := SetupTH(t, lpOpts) err := th.LogPoller.RegisterFilter(logpoller.Filter{ Name: "GetBlocks Test", @@ -1245,7 +1323,14 @@ func TestLogPoller_GetBlocks_Range(t *testing.T) { func TestGetReplayFromBlock(t *testing.T) { t.Parallel() - th := SetupTH(t, false, 2, 3, 2, 1000) + lpOpts := logpoller.Opts{ + UseFinalityTag: false, + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + th := SetupTH(t, lpOpts) // Commit a few blocks for i := 0; i < 10; i++ { th.Client.Commit() @@ -1306,7 +1391,14 @@ func TestLogPoller_DBErrorHandling(t *testing.T) { ec.Commit() ec.Commit() - lp := logpoller.NewLogPoller(o, client.NewSimulatedBackendClient(t, ec, chainID2), lggr, 1*time.Hour, false, 2, 3, 2, 1000, 0) + lpOpts := logpoller.Opts{ + PollPeriod: time.Hour, + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := logpoller.NewLogPoller(o, client.NewSimulatedBackendClient(t, ec, chainID2), lggr, lpOpts) err = lp.Replay(ctx, 5) // block number too high require.ErrorContains(t, err, "Invalid replay block number") @@ -1321,7 +1413,7 @@ func TestLogPoller_DBErrorHandling(t *testing.T) { time.Sleep(100 * time.Millisecond) require.NoError(t, lp.Start(ctx)) require.Eventually(t, func() bool { - return observedLogs.Len() >= 5 + return observedLogs.Len() >= 4 }, 2*time.Second, 20*time.Millisecond) lp.Close() @@ -1338,7 +1430,6 @@ func TestLogPoller_DBErrorHandling(t *testing.T) { assert.Contains(t, logMsgs, "SQL ERROR") assert.Contains(t, logMsgs, "Failed loading filters in main logpoller loop, retrying later") assert.Contains(t, logMsgs, "Error executing replay, could not get fromBlock") - assert.Contains(t, logMsgs, "Backup log poller ran before filters loaded, skipping") } type getLogErrData struct { @@ -1354,7 +1445,15 @@ func TestTooManyLogResults(t *testing.T) { chainID := testutils.NewRandomEVMChainID() db := pgtest.NewSqlxDB(t) o := logpoller.NewORM(chainID, db, lggr, pgtest.NewQConfig(true)) - lp := logpoller.NewLogPoller(o, ec, lggr, 1*time.Hour, false, 2, 20, 10, 1000, 0) + + lpOpts := logpoller.Opts{ + PollPeriod: time.Hour, + FinalityDepth: 2, + BackfillBatchSize: 20, + RpcBatchSize: 10, + KeepFinalizedBlocksDepth: 1000, + } + lp := logpoller.NewLogPoller(o, ec, lggr, lpOpts) expected := []int64{10, 5, 2, 1} clientErr := client.JsonError{ @@ -1441,7 +1540,13 @@ func Test_PollAndQueryFinalizedBlocks(t *testing.T) { firstBatchLen := 3 secondBatchLen := 5 - th := SetupTH(t, true, 2, 3, 2, 1000) + lpOpts := logpoller.Opts{ + UseFinalityTag: true, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + th := SetupTH(t, lpOpts) eventSig := EmitterABI.Events["Log1"].ID err := th.LogPoller.RegisterFilter(logpoller.Filter{ @@ -1480,7 +1585,7 @@ func Test_PollAndQueryFinalizedBlocks(t *testing.T) { logpoller.Finalized, ) require.NoError(t, err) - require.Len(t, finalizedLogs, firstBatchLen) + require.Len(t, finalizedLogs, firstBatchLen, fmt.Sprintf("len(finalizedLogs) = %d, should have been %d", len(finalizedLogs), firstBatchLen)) numberOfConfirmations := 1 logsByConfs, err := th.LogPoller.LogsDataWordGreaterThan( @@ -1525,7 +1630,14 @@ func Test_PollAndSavePersistsFinalityInBlocks(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - th := SetupTH(t, tt.useFinalityTag, tt.finalityDepth, 3, 2, 1000) + lpOpts := logpoller.Opts{ + UseFinalityTag: tt.useFinalityTag, + FinalityDepth: tt.finalityDepth, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + th := SetupTH(t, lpOpts) // Should return error before the first poll and save _, err := th.LogPoller.LatestBlock() require.Error(t, err) @@ -1572,7 +1684,15 @@ func Test_CreatedAfterQueriesWithBackfill(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - th := SetupTH(t, tt.finalityTag, tt.finalityDepth, 3, 2, 1000) + lpOpts := logpoller.Opts{ + UseFinalityTag: tt.finalityTag, + FinalityDepth: tt.finalityDepth, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + BackupPollerBlockDelay: 100, + } + th := SetupTH(t, lpOpts) header, err := th.Client.HeaderByNumber(ctx, nil) require.NoError(t, err) @@ -1603,7 +1723,7 @@ func Test_CreatedAfterQueriesWithBackfill(t *testing.T) { } // LogPoller should backfill entire history - th.LogPoller.BackupPollAndSaveLogs(ctx, 100) + th.LogPoller.BackupPollAndSaveLogs(ctx) require.NoError(t, err) // Make sure that all logs are backfilled @@ -1663,7 +1783,13 @@ func Test_PruneOldBlocks(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - th := SetupTH(t, true, 0, 3, 2, tt.keepFinalizedBlocksDepth) + lpOpts := logpoller.Opts{ + UseFinalityTag: true, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: tt.keepFinalizedBlocksDepth, + } + th := SetupTH(t, lpOpts) for i := 1; i <= tt.blockToCreate; i++ { err := th.ORM.InsertBlock(utils.RandomBytes32(), int64(i+10), time.Now(), int64(i)) diff --git a/core/chains/evm/logpoller/orm_test.go b/core/chains/evm/logpoller/orm_test.go index a9f39a7ac4a..5bc26ed62b4 100644 --- a/core/chains/evm/logpoller/orm_test.go +++ b/core/chains/evm/logpoller/orm_test.go @@ -34,6 +34,13 @@ type block struct { timestamp int64 } +var lpOpts = logpoller.Opts{ + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, +} + func GenLog(chainID *big.Int, logIndex int64, blockNum int64, blockHash string, topic1 []byte, address common.Address) logpoller.Log { return GenLogWithTimestamp(chainID, logIndex, blockNum, blockHash, topic1, address, time.Now()) } @@ -70,7 +77,7 @@ func GenLogWithData(chainID *big.Int, address common.Address, eventSig common.Ha func TestLogPoller_Batching(t *testing.T) { t.Parallel() - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) var logs []logpoller.Log // Inserts are limited to 65535 parameters. A log being 10 parameters this results in // a maximum of 6553 log inserts per tx. As inserting more than 6553 would result in @@ -86,7 +93,7 @@ func TestLogPoller_Batching(t *testing.T) { } func TestORM_GetBlocks_From_Range(t *testing.T) { - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) o1 := th.ORM // Insert many blocks and read them back together blocks := []block{ @@ -141,7 +148,7 @@ func TestORM_GetBlocks_From_Range(t *testing.T) { } func TestORM_GetBlocks_From_Range_Recent_Blocks(t *testing.T) { - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) o1 := th.ORM // Insert many blocks and read them back together var recentBlocks []block @@ -173,7 +180,7 @@ func TestORM_GetBlocks_From_Range_Recent_Blocks(t *testing.T) { } func TestORM(t *testing.T) { - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) o1 := th.ORM o2 := th.ORM2 // Insert and read back a block. @@ -572,7 +579,7 @@ func insertLogsTopicValueRange(t *testing.T, chainID *big.Int, o *logpoller.DbOR } func TestORM_IndexedLogs(t *testing.T) { - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) o1 := th.ORM eventSig := common.HexToHash("0x1599") addr := common.HexToAddress("0x1234") @@ -633,7 +640,7 @@ func TestORM_IndexedLogs(t *testing.T) { } func TestORM_SelectIndexedLogsByTxHash(t *testing.T) { - th := SetupTH(t, false, 0, 3, 2, 1000) + th := SetupTH(t, lpOpts) o1 := th.ORM eventSig := common.HexToHash("0x1599") txHash := common.HexToHash("0x1888") @@ -699,7 +706,7 @@ func TestORM_SelectIndexedLogsByTxHash(t *testing.T) { } func TestORM_DataWords(t *testing.T) { - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) o1 := th.ORM eventSig := common.HexToHash("0x1599") addr := common.HexToAddress("0x1234") @@ -762,7 +769,7 @@ func TestORM_DataWords(t *testing.T) { } func TestORM_SelectLogsWithSigsByBlockRangeFilter(t *testing.T) { - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) o1 := th.ORM // Insert logs on different topics, should be able to read them @@ -856,7 +863,7 @@ func TestORM_SelectLogsWithSigsByBlockRangeFilter(t *testing.T) { } func TestORM_DeleteBlocksBefore(t *testing.T) { - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) o1 := th.ORM require.NoError(t, o1.InsertBlock(common.HexToHash("0x1234"), 1, time.Now(), 0)) require.NoError(t, o1.InsertBlock(common.HexToHash("0x1235"), 2, time.Now(), 0)) @@ -883,7 +890,7 @@ func TestORM_DeleteBlocksBefore(t *testing.T) { func TestLogPoller_Logs(t *testing.T) { t.Parallel() - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) event1 := EmitterABI.Events["Log1"].ID event2 := EmitterABI.Events["Log2"].ID address1 := common.HexToAddress("0x2ab9a2Dc53736b361b72d900CdF9F78F9406fbbb") @@ -931,7 +938,7 @@ func TestLogPoller_Logs(t *testing.T) { } func BenchmarkLogs(b *testing.B) { - th := SetupTH(b, false, 2, 3, 2, 1000) + th := SetupTH(b, lpOpts) o := th.ORM var lgs []logpoller.Log addr := common.HexToAddress("0x1234") @@ -957,7 +964,7 @@ func BenchmarkLogs(b *testing.B) { } func TestSelectLogsWithSigsExcluding(t *testing.T) { - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) orm := th.ORM addressA := common.HexToAddress("0x11111") addressB := common.HexToAddress("0x22222") @@ -1203,7 +1210,7 @@ func TestSelectLogsWithSigsExcluding(t *testing.T) { } func TestSelectLatestBlockNumberEventSigsAddrsWithConfs(t *testing.T) { - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) event1 := EmitterABI.Events["Log1"].ID event2 := EmitterABI.Events["Log2"].ID address1 := utils.RandomAddress() @@ -1300,7 +1307,7 @@ func TestSelectLatestBlockNumberEventSigsAddrsWithConfs(t *testing.T) { } func TestSelectLogsCreatedAfter(t *testing.T) { - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) event := EmitterABI.Events["Log1"].ID address := utils.RandomAddress() @@ -1404,7 +1411,7 @@ func TestSelectLogsCreatedAfter(t *testing.T) { } func TestNestedLogPollerBlocksQuery(t *testing.T) { - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) event := EmitterABI.Events["Log1"].ID address := utils.RandomAddress() @@ -1564,7 +1571,7 @@ func TestInsertLogsInTx(t *testing.T) { func TestSelectLogsDataWordBetween(t *testing.T) { address := utils.RandomAddress() eventSig := utils.RandomBytes32() - th := SetupTH(t, false, 2, 3, 2, 1000) + th := SetupTH(t, lpOpts) firstLogData := make([]byte, 0, 64) firstLogData = append(firstLogData, logpoller.EvmWord(1).Bytes()...) diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 8e4a59bc099..da18c592a55 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -50,7 +50,14 @@ import ( func makeTestEvmTxm( t *testing.T, db *sqlx.DB, ethClient evmclient.Client, estimator gas.EvmFeeEstimator, ccfg txmgr.ChainConfig, fcfg txmgr.FeeConfig, txConfig evmconfig.Transactions, dbConfig txmgr.DatabaseConfig, listenerConfig txmgr.ListenerConfig, keyStore keystore.Eth) (txmgr.TxManager, error) { lggr := logger.Test(t) - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000, 0) + lpOpts := logpoller.Opts{ + PollPeriod: 100 * time.Millisecond, + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, lpOpts) // logic for building components (from evm/evm_txm.go) ------- lggr.Infow("Initializing EVM transaction manager", diff --git a/core/chains/legacyevm/chain.go b/core/chains/legacyevm/chain.go index 4e0344281cf..50e6d3914c6 100644 --- a/core/chains/legacyevm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -242,17 +242,17 @@ func newChain(ctx context.Context, cfg *evmconfig.ChainScoped, nodes []*toml.Nod if opts.GenLogPoller != nil { logPoller = opts.GenLogPoller(chainID) } else { - logPoller = logpoller.NewLogPoller( - logpoller.NewObservedORM(chainID, db, l, cfg.Database()), - client, - l, - cfg.EVM().LogPollInterval(), - cfg.EVM().FinalityTagEnabled(), - int64(cfg.EVM().FinalityDepth()), - int64(cfg.EVM().LogBackfillBatchSize()), - int64(cfg.EVM().RPCDefaultBatchSize()), - int64(cfg.EVM().LogKeepBlocksDepth()), - int64(cfg.EVM().LogPrunePageSize())) + lpOpts := logpoller.Opts{ + PollPeriod: cfg.EVM().LogPollInterval(), + UseFinalityTag: cfg.EVM().FinalityTagEnabled(), + FinalityDepth: int64(cfg.EVM().FinalityDepth()), + BackfillBatchSize: int64(cfg.EVM().LogBackfillBatchSize()), + RpcBatchSize: int64(cfg.EVM().RPCDefaultBatchSize()), + KeepFinalizedBlocksDepth: int64(cfg.EVM().LogKeepBlocksDepth()), + LogPrunePageSize: int64(cfg.EVM().LogPrunePageSize()), + BackupPollerBlockDelay: int64(cfg.EVM().BackupLogPollerBlockDelay()), + } + logPoller = logpoller.NewLogPoller(logpoller.NewObservedORM(chainID, db, l, cfg.Database()), client, l, lpOpts) } } diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index f70dcd0ee45..5359fe4b22b 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -57,6 +57,10 @@ LogKeepBlocksDepth = 100000 # Default # **ADVANCED** # LogPrunePageSize defines size of the page for pruning logs. Controls how many logs/blocks (at most) are deleted in a single prune tick. Default value 0 means no paging, delete everything at once. LogPrunePageSize = 0 # Default +# **ADVANCED** +# BackupLogPollerBlockDelay works in conjunction with Feature.LogPoller. Controls the block delay of Backup LogPoller, affecting how far behind the latest finalized block it starts and how often it runs. +# BackupLogPollerDelay=0 will disable Backup LogPoller (_not recommended for production environment_). +BackupLogPollerBlockDelay = 100 # Default # MinContractPayment is the minimum payment in LINK required to execute a direct request job. This can be overridden on a per-job basis. MinContractPayment = '10000000000000 juels' # Default # MinIncomingConfirmations is the minimum required confirmations before a log event will be consumed. diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index ed252eadd97..44c19aa257d 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -525,18 +525,19 @@ func TestConfig_Marshal(t *testing.T) { }, }, - LinkContractAddress: mustAddress("0x538aAaB4ea120b2bC2fe5D296852D948F07D849e"), - LogBackfillBatchSize: ptr[uint32](17), - LogPollInterval: &minute, - LogKeepBlocksDepth: ptr[uint32](100000), - LogPrunePageSize: ptr[uint32](0), - MinContractPayment: commonassets.NewLinkFromJuels(math.MaxInt64), - MinIncomingConfirmations: ptr[uint32](13), - NonceAutoSync: ptr(true), - NoNewHeadsThreshold: &minute, - OperatorFactoryAddress: mustAddress("0xa5B85635Be42F21f94F28034B7DA440EeFF0F418"), - RPCDefaultBatchSize: ptr[uint32](17), - RPCBlockQueryDelay: ptr[uint16](10), + LinkContractAddress: mustAddress("0x538aAaB4ea120b2bC2fe5D296852D948F07D849e"), + LogBackfillBatchSize: ptr[uint32](17), + LogPollInterval: &minute, + LogKeepBlocksDepth: ptr[uint32](100000), + LogPrunePageSize: ptr[uint32](0), + BackupLogPollerBlockDelay: ptr[uint64](532), + MinContractPayment: commonassets.NewLinkFromJuels(math.MaxInt64), + MinIncomingConfirmations: ptr[uint32](13), + NonceAutoSync: ptr(true), + NoNewHeadsThreshold: &minute, + OperatorFactoryAddress: mustAddress("0xa5B85635Be42F21f94F28034B7DA440EeFF0F418"), + RPCDefaultBatchSize: ptr[uint32](17), + RPCBlockQueryDelay: ptr[uint16](10), Transactions: evmcfg.Transactions{ MaxInFlight: ptr[uint32](19), @@ -926,6 +927,7 @@ LogBackfillBatchSize = 17 LogPollInterval = '1m0s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 532 MinIncomingConfirmations = 13 MinContractPayment = '9.223372036854775807 link' NonceAutoSync = true diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index e90f6c6611a..c1606a5b067 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -254,6 +254,7 @@ LogBackfillBatchSize = 17 LogPollInterval = '1m0s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 532 MinIncomingConfirmations = 13 MinContractPayment = '9.223372036854775807 link' NonceAutoSync = true diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index c230a764c74..9f69d4aa909 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -241,6 +241,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -330,6 +331,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -414,6 +416,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 5 MinContractPayment = '0.00001 link' NonceAutoSync = true diff --git a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go index 427dd6b32c2..c24ea46231d 100644 --- a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go +++ b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go @@ -77,7 +77,15 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { require.NoError(t, err) t.Run("with zero fromblock", func(t *testing.T) { - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 1, 3, 2, 1000, 0) + lpOpts := logpoller.Opts{ + PollPeriod: 100 * time.Millisecond, + FinalityDepth: 1, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := logpoller.NewLogPoller( + logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, lpOpts) servicetest.Run(t, lp) cdc := llo.NewChannelDefinitionCache(lggr, orm, lp, configStoreAddress, 0) @@ -141,8 +149,15 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { t.Run("loads from ORM", func(t *testing.T) { // Override logpoller to always return no logs + lpOpts := logpoller.Opts{ + PollPeriod: 100 * time.Millisecond, + FinalityDepth: 1, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } lp := &mockLogPoller{ - LogPoller: logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 1, 3, 2, 1000, 0), + LogPoller: logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, lpOpts), LatestBlockFn: func(qopts ...pg.QOpt) (int64, error) { return 0, nil }, @@ -176,7 +191,14 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { pgtest.MustExec(t, db, `DELETE FROM channel_definitions`) t.Run("with non-zero fromBlock", func(t *testing.T) { - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 1, 3, 2, 1000, 0) + lpOpts := logpoller.Opts{ + PollPeriod: 100 * time.Millisecond, + FinalityDepth: 1, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, lpOpts) servicetest.Run(t, lp) cdc := llo.NewChannelDefinitionCache(lggr, orm, lp, configStoreAddress, channel2Block.Number().Int64()+1) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go index ba89c52c2ef..aa8a5c97d70 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go @@ -661,7 +661,14 @@ func setupDependencies(t *testing.T, db *sqlx.DB, backend *backends.SimulatedBac pollerLggr := logger.TestLogger(t) pollerLggr.SetLogLevel(zapcore.WarnLevel) lorm := logpoller.NewORM(big.NewInt(1337), db, pollerLggr, pgtest.NewQConfig(false)) - lp := logpoller.NewLogPoller(lorm, ethClient, pollerLggr, 100*time.Millisecond, false, 1, 2, 2, 1000, 0) + lpOpts := logpoller.Opts{ + PollPeriod: 100 * time.Millisecond, + FinalityDepth: 1, + BackfillBatchSize: 2, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := logpoller.NewLogPoller(lorm, ethClient, pollerLggr, lpOpts) return lp, ethClient } diff --git a/core/services/promreporter/prom_reporter_test.go b/core/services/promreporter/prom_reporter_test.go index d283cb8f873..66133072ebd 100644 --- a/core/services/promreporter/prom_reporter_test.go +++ b/core/services/promreporter/prom_reporter_test.go @@ -38,7 +38,14 @@ func newLegacyChainContainer(t *testing.T, db *sqlx.DB) legacyevm.LegacyChainCon ethClient := evmtest.NewEthClientMockWithDefaultChain(t) estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) lggr := logger.TestLogger(t) - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000, 0) + lpOpts := logpoller.Opts{ + PollPeriod: 100 * time.Millisecond, + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, lpOpts) txm, err := txmgr.NewTxm( db, diff --git a/core/services/relay/evm/chain_reader_test.go b/core/services/relay/evm/chain_reader_test.go index 64d9f9f1cac..39cf317204b 100644 --- a/core/services/relay/evm/chain_reader_test.go +++ b/core/services/relay/evm/chain_reader_test.go @@ -136,11 +136,11 @@ func (it *chainReaderInterfaceTester) MaxWaitTimeForEvents() time.Duration { maxWaitTime := time.Second * 20 maxWaitTimeStr, ok := os.LookupEnv("MAX_WAIT_TIME_FOR_EVENTS_S") if ok { - wiatS, err := strconv.ParseInt(maxWaitTimeStr, 10, 64) + waitS, err := strconv.ParseInt(maxWaitTimeStr, 10, 64) if err != nil { fmt.Printf("Error parsing MAX_WAIT_TIME_FOR_EVENTS_S: %v, defaulting to %v\n", err, maxWaitTime) } - maxWaitTime = time.Second * time.Duration(wiatS) + maxWaitTime = time.Second * time.Duration(waitS) } return maxWaitTime @@ -263,7 +263,15 @@ func (it *chainReaderInterfaceTester) GetChainReader(t *testing.T) clcommontypes lggr := logger.NullLogger db := pgtest.NewSqlxDB(t) - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), it.chain.Client(), lggr, time.Millisecond, false, 0, 1, 1, 10000, 0) + + lpOpts := logpoller.Opts{ + PollPeriod: time.Millisecond, + FinalityDepth: 4, + BackfillBatchSize: 1, + RpcBatchSize: 1, + KeepFinalizedBlocksDepth: 10000, + } + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), it.chain.Client(), lggr, lpOpts) require.NoError(t, lp.Start(ctx)) it.chain.On("LogPoller").Return(lp) cr, err := evm.NewChainReaderService(lggr, lp, it.chain, it.chainConfig) diff --git a/core/services/relay/evm/config_poller_test.go b/core/services/relay/evm/config_poller_test.go index 089db6decd5..d2d33944df7 100644 --- a/core/services/relay/evm/config_poller_test.go +++ b/core/services/relay/evm/config_poller_test.go @@ -90,7 +90,15 @@ func TestConfigPoller(t *testing.T) { cfg := pgtest.NewQConfig(false) ethClient = evmclient.NewSimulatedBackendClient(t, b, testutils.SimulatedChainID) lorm := logpoller.NewORM(testutils.SimulatedChainID, db, lggr, cfg) - lp = logpoller.NewLogPoller(lorm, ethClient, lggr, 100*time.Millisecond, false, 1, 2, 2, 1000, 0) + + lpOpts := logpoller.Opts{ + PollPeriod: 100 * time.Millisecond, + FinalityDepth: 1, + BackfillBatchSize: 2, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp = logpoller.NewLogPoller(lorm, ethClient, lggr, lpOpts) servicetest.Run(t, lp) } diff --git a/core/services/relay/evm/functions/config_poller_test.go b/core/services/relay/evm/functions/config_poller_test.go index 6a8c682a81b..ab80f3ae565 100644 --- a/core/services/relay/evm/functions/config_poller_test.go +++ b/core/services/relay/evm/functions/config_poller_test.go @@ -81,7 +81,14 @@ func runTest(t *testing.T, pluginType functions.FunctionsPluginType, expectedDig defer ethClient.Close() lggr := logger.TestLogger(t) lorm := logpoller.NewORM(big.NewInt(1337), db, lggr, cfg) - lp := logpoller.NewLogPoller(lorm, ethClient, lggr, 100*time.Millisecond, false, 1, 2, 2, 1000, 0) + lpOpts := logpoller.Opts{ + PollPeriod: 100 * time.Millisecond, + FinalityDepth: 1, + BackfillBatchSize: 2, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := logpoller.NewLogPoller(lorm, ethClient, lggr, lpOpts) servicetest.Run(t, lp) configPoller, err := functions.NewFunctionsConfigPoller(pluginType, lp, lggr) require.NoError(t, err) diff --git a/core/services/relay/evm/mercury/helpers_test.go b/core/services/relay/evm/mercury/helpers_test.go index 8283e80916e..4b05b974c3d 100644 --- a/core/services/relay/evm/mercury/helpers_test.go +++ b/core/services/relay/evm/mercury/helpers_test.go @@ -167,7 +167,15 @@ func SetupTH(t *testing.T, feedID common.Hash) TestHarness { ethClient := evmclient.NewSimulatedBackendClient(t, b, big.NewInt(1337)) lggr := logger.TestLogger(t) lorm := logpoller.NewORM(big.NewInt(1337), db, lggr, cfg) - lp := logpoller.NewLogPoller(lorm, ethClient, lggr, 100*time.Millisecond, false, 1, 2, 2, 1000, 0) + + lpOpts := logpoller.Opts{ + PollPeriod: 100 * time.Millisecond, + FinalityDepth: 1, + BackfillBatchSize: 2, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + lp := logpoller.NewLogPoller(lorm, ethClient, lggr, lpOpts) servicetest.Run(t, lp) configPoller, err := NewConfigPoller(lggr, lp, verifierAddress, feedID) diff --git a/core/services/vrf/v2/bhs_feeder_test.go b/core/services/vrf/v2/bhs_feeder_test.go index a02eea75757..6f857c80834 100644 --- a/core/services/vrf/v2/bhs_feeder_test.go +++ b/core/services/vrf/v2/bhs_feeder_test.go @@ -7,12 +7,14 @@ import ( "github.com/stretchr/testify/require" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrftesthelpers" ) @@ -37,14 +39,14 @@ func TestStartHeartbeats(t *testing.T) { bhsKeyAddresses = append(bhsKeyAddresses, bhsKey.Address.String()) keys = append(keys, bhsKey) keySpecificOverrides = append(keySpecificOverrides, toml.KeySpecific{ - Key: ptr(bhsKey.EIP55Address), + Key: ptr[ethkey.EIP55Address](bhsKey.EIP55Address), GasEstimator: toml.KeySpecificGasEstimator{PriceMax: gasLanePriceWei}, }) sendEth(t, ownerKey, uni.backend, bhsKey.Address, 10) } keySpecificOverrides = append(keySpecificOverrides, toml.KeySpecific{ // Gas lane. - Key: ptr(vrfKey.EIP55Address), + Key: ptr[ethkey.EIP55Address](vrfKey.EIP55Address), GasEstimator: toml.KeySpecificGasEstimator{PriceMax: gasLanePriceWei}, }) diff --git a/core/services/vrf/v2/integration_helpers_test.go b/core/services/vrf/v2/integration_helpers_test.go index b0ae4266b12..6649b5972e0 100644 --- a/core/services/vrf/v2/integration_helpers_test.go +++ b/core/services/vrf/v2/integration_helpers_test.go @@ -17,6 +17,7 @@ import ( "github.com/stretchr/testify/require" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -66,11 +67,11 @@ func testSingleConsumerHappyPath( config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), toml.KeySpecific{ // Gas lane. - Key: ptr(key1.EIP55Address), + Key: ptr[ethkey.EIP55Address](key1.EIP55Address), GasEstimator: toml.KeySpecificGasEstimator{PriceMax: gasLanePriceWei}, }, toml.KeySpecific{ // Gas lane. - Key: ptr(key2.EIP55Address), + Key: ptr[ethkey.EIP55Address](key2.EIP55Address), GasEstimator: toml.KeySpecificGasEstimator{PriceMax: gasLanePriceWei}, })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) diff --git a/core/services/vrf/v2/listener_v2_log_listener_test.go b/core/services/vrf/v2/listener_v2_log_listener_test.go index c92795f55a6..cda172abefd 100644 --- a/core/services/vrf/v2/listener_v2_log_listener_test.go +++ b/core/services/vrf/v2/listener_v2_log_listener_test.go @@ -92,7 +92,16 @@ func setupVRFLogPollerListenerTH(t *testing.T, // Poll period doesn't matter, we intend to call poll and save logs directly in the test. // Set it to some insanely high value to not interfere with any tests. - lp := logpoller.NewLogPoller(o, esc, lggr, 1*time.Hour, useFinalityTag, finalityDepth, backfillBatchSize, rpcBatchSize, keepFinalizedBlocksDepth, 0) + + lpOpts := logpoller.Opts{ + PollPeriod: time.Hour, + UseFinalityTag: useFinalityTag, + FinalityDepth: finalityDepth, + BackfillBatchSize: backfillBatchSize, + RpcBatchSize: rpcBatchSize, + KeepFinalizedBlocksDepth: keepFinalizedBlocksDepth, + } + lp := logpoller.NewLogPoller(o, esc, lggr, lpOpts) emitterAddress1, _, emitter1, err := log_emitter.DeployLogEmitter(owner, ec) require.NoError(t, err) diff --git a/core/web/resolver/chain_test.go b/core/web/resolver/chain_test.go index e2663af561f..a0f2ca22b07 100644 --- a/core/web/resolver/chain_test.go +++ b/core/web/resolver/chain_test.go @@ -46,6 +46,7 @@ LogBackfillBatchSize = 17 LogPollInterval = '1m0s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 13 MinContractPayment = '9.223372036854775807 link' NonceAutoSync = true @@ -164,6 +165,7 @@ LogBackfillBatchSize = 17 LogPollInterval = '1m0s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 13 MinContractPayment = '9.223372036854775807 link' NonceAutoSync = true diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index f698f55fb25..cdfb85a6f5c 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -254,6 +254,7 @@ LogBackfillBatchSize = 17 LogPollInterval = '1m0s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 13 MinContractPayment = '9.223372036854775807 link' NonceAutoSync = true diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index c230a764c74..9f69d4aa909 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -241,6 +241,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -330,6 +331,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -414,6 +416,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 5 MinContractPayment = '0.00001 link' NonceAutoSync = true diff --git a/docs/CONFIG.md b/docs/CONFIG.md index e4e25e6694f..938c3de66b1 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -1604,6 +1604,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -1688,6 +1689,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -1771,6 +1773,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -1854,6 +1857,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -1938,6 +1942,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2021,6 +2026,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '30s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.001 link' NonceAutoSync = true @@ -2104,6 +2110,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '30s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.001 link' NonceAutoSync = true @@ -2187,6 +2194,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -2271,6 +2279,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2353,6 +2362,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2435,6 +2445,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2518,6 +2529,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2602,6 +2614,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '5s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2685,6 +2698,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2768,6 +2782,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 5 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2851,6 +2866,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2934,6 +2950,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3017,6 +3034,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '5s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3100,6 +3118,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '5s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3184,6 +3203,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3267,6 +3287,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3349,6 +3370,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3432,6 +3454,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3514,6 +3537,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '30s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3597,6 +3621,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3680,6 +3705,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3762,6 +3788,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '100' NonceAutoSync = true @@ -3844,6 +3871,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '30s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3927,6 +3955,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4010,6 +4039,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4092,6 +4122,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4175,6 +4206,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4258,6 +4290,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '5s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4342,6 +4375,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4425,6 +4459,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '5s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4508,6 +4543,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4591,6 +4627,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4674,6 +4711,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '5s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4756,6 +4794,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4838,6 +4877,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4921,6 +4961,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 5 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5004,6 +5045,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5087,6 +5129,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5171,6 +5214,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5255,6 +5299,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5338,6 +5383,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5421,6 +5467,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5504,6 +5551,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5587,6 +5635,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -5670,6 +5719,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5753,6 +5803,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5935,6 +5986,14 @@ LogPrunePageSize = 0 # Default ``` LogPrunePageSize defines size of the page for pruning logs. Controls how many logs/blocks (at most) are deleted in a single prune tick. Default value 0 means no paging, delete everything at once. +### BackupLogPollerBlockDelay +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +BackupLogPollerBlockDelay = 100 # Default +``` +BackupLogPollerBlockDelay works in conjunction with Feature.LogPoller. Controls the block delay of Backup LogPoller, affecting how far behind the latest finalized block it starts and how often it runs. +BackupLogPollerDelay=0 will disable Backup LogPoller (_not recommended for production environment_). + ### MinContractPayment ```toml MinContractPayment = '10000000000000 juels' # Default diff --git a/integration-tests/smoke/log_poller_test.go b/integration-tests/smoke/log_poller_test.go index 593b4eb879a..4b4533d3a37 100644 --- a/integration-tests/smoke/log_poller_test.go +++ b/integration-tests/smoke/log_poller_test.go @@ -277,7 +277,8 @@ func prepareEnvironment(l zerolog.Logger, t *testing.T, testConfig *tc.TestConfi ethereum.RegistryVersion_2_1, logpoller.DefaultOCRRegistryConfig, upKeepsNeeded, - time.Duration(500*time.Millisecond), + cfg.General.LogPollInterval.Duration, + *cfg.General.BackupLogPollerBlockDelay, *cfg.General.UseFinalityTag, testConfig, ) diff --git a/integration-tests/testconfig/log_poller/config.go b/integration-tests/testconfig/log_poller/config.go index 96c3b55c276..890c33f26c9 100644 --- a/integration-tests/testconfig/log_poller/config.go +++ b/integration-tests/testconfig/log_poller/config.go @@ -90,11 +90,13 @@ func (l *LoopedConfig) Validate() error { } type General struct { - Generator *string `toml:"generator"` - EventsToEmit []abi.Event `toml:"-"` - Contracts *int `toml:"contracts"` - EventsPerTx *int `toml:"events_per_tx"` - UseFinalityTag *bool `toml:"use_finality_tag"` + Generator *string `toml:"generator"` + EventsToEmit []abi.Event `toml:"-"` + Contracts *int `toml:"contracts"` + EventsPerTx *int `toml:"events_per_tx"` + UseFinalityTag *bool `toml:"use_finality_tag"` + BackupLogPollerBlockDelay *uint64 `toml:"backup_log_poller_block_delay"` + LogPollInterval *blockchain.StrDuration `toml:"log_poll_interval"` } func (g *General) Validate() error { diff --git a/integration-tests/testconfig/log_poller/log_poller.toml b/integration-tests/testconfig/log_poller/log_poller.toml index 2f46ebf11c2..89d2f07b4e3 100644 --- a/integration-tests/testconfig/log_poller/log_poller.toml +++ b/integration-tests/testconfig/log_poller/log_poller.toml @@ -5,6 +5,9 @@ generator = "looped" contracts = 2 events_per_tx = 4 use_finality_tag = true +log_poll_interval = "500ms" +# 0 disables backup poller +backup_log_poller_block_delay = 0 [LogPoller.Looped] execution_count = 100 diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index b91156a3784..b6e62cff2f6 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -1079,6 +1079,7 @@ func SetupLogPollerTestDocker( registryConfig contracts.KeeperRegistrySettings, upkeepsNeeded int, lpPollingInterval time.Duration, + backupPollingInterval uint64, finalityTagEnabled bool, testConfig *tc.TestConfig, ) ( @@ -1121,6 +1122,7 @@ func SetupLogPollerTestDocker( chain.LogPollInterval = commonconfig.MustNewDuration(lpPollingInterval) chain.FinalityDepth = ptr.Ptr[uint32](uint32(finalityDepth)) chain.FinalityTagEnabled = ptr.Ptr[bool](finalityTagEnabled) + chain.BackupLogPollerBlockDelay = ptr.Ptr[uint64](backupPollingInterval) return chain } diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index bb845b05201..873b9e91bc1 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -297,6 +297,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index cdac1b3702c..0c00fbb7adc 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -297,6 +297,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index 832b3fac584..0bbddd6f40f 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -297,6 +297,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index 280fa209f0d..011298fcde7 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -287,6 +287,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index bdd83a9eb31..e0bd015a184 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -294,6 +294,7 @@ LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true From 9e3e3086be2d80db58c2dfdd690064ffe2eebc94 Mon Sep 17 00:00:00 2001 From: Chris Cushman <104409744+vreff@users.noreply.github.com> Date: Wed, 28 Feb 2024 13:36:52 -0500 Subject: [PATCH 15/16] Fix solhint warnings on MercuryRegistry.sol (#12196) --- .../v0.8/automation/dev/MercuryRegistry.sol | 32 +++++++++---------- .../dev/MercuryRegistryBatchUpkeep.sol | 4 +-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/contracts/src/v0.8/automation/dev/MercuryRegistry.sol b/contracts/src/v0.8/automation/dev/MercuryRegistry.sol index c49543b3e7c..e6b6920f5db 100644 --- a/contracts/src/v0.8/automation/dev/MercuryRegistry.sol +++ b/contracts/src/v0.8/automation/dev/MercuryRegistry.sol @@ -69,11 +69,11 @@ contract MercuryRegistry is ConfirmedOwner, AutomationCompatibleInterface, Strea uint32 private constant MIN_GAS_FOR_PERFORM = 200_000; - string constant c_feedParamKey = "feedIdHex"; // for Mercury v0.2 - format by which feeds are identified - string constant c_timeParamKey = "blockNumber"; // for Mercury v0.2 - format by which feeds are filtered to be sufficiently recent + string private constant FEED_PARAM_KEY = "feedIdHex"; // for Mercury v0.2 - format by which feeds are identified + string private constant TIME_PARAM_KEY = "blockNumber"; // for Mercury v0.2 - format by which feeds are filtered to be sufficiently recent IVerifierProxy public s_verifier; // for Mercury v0.2 - verifies off-chain reports - int192 constant scale = 1_000_000; // a scalar used for measuring deviation with precision + int192 private constant SCALE = 1_000_000; // a scalar used for measuring deviation with precision string[] public s_feeds; // list of feed Ids mapping(string => Feed) public s_feedMapping; // mapping of feed Ids to stored feed data @@ -110,7 +110,7 @@ contract MercuryRegistry is ConfirmedOwner, AutomationCompatibleInterface, Strea // Extracted from `checkUpkeep` for batching purposes. function revertForFeedLookup(string[] memory feeds) public view returns (bool, bytes memory) { uint256 blockNumber = ChainSpecificUtil._getBlockNumber(); - revert StreamsLookup(c_feedParamKey, feeds, c_timeParamKey, blockNumber, ""); + revert StreamsLookup(FEED_PARAM_KEY, feeds, TIME_PARAM_KEY, blockNumber, ""); } // Filter for feeds that have deviated sufficiently from their respective on-chain values, or where @@ -122,8 +122,8 @@ contract MercuryRegistry is ConfirmedOwner, AutomationCompatibleInterface, Strea bytes[] memory filteredValues = new bytes[](values.length); uint256 count = 0; for (uint256 i = 0; i < values.length; i++) { - Report memory report = getReport(values[i]); - string memory feedId = bytes32ToHexString(abi.encodePacked(report.feedId)); + Report memory report = _getReport(values[i]); + string memory feedId = _bytes32ToHexString(abi.encodePacked(report.feedId)); Feed memory feed = s_feedMapping[feedId]; if ( (report.observationsTimestamp - feed.observationsTimestamp > feed.stalenessSeconds) || @@ -145,8 +145,8 @@ contract MercuryRegistry is ConfirmedOwner, AutomationCompatibleInterface, Strea } function checkErrorHandler( - uint256 errCode, - bytes memory extraData + uint256 /* errCode */, + bytes memory /* extraData */ ) external view override returns (bool upkeepNeeded, bytes memory performData) { // dummy function with default values return (false, new bytes(0)); @@ -158,7 +158,7 @@ contract MercuryRegistry is ConfirmedOwner, AutomationCompatibleInterface, Strea for (uint256 i = 0; i < values.length; i++) { // Verify and decode the Mercury report. Report memory report = abi.decode(s_verifier.verify(values[i]), (Report)); - string memory feedId = bytes32ToHexString(abi.encodePacked(report.feedId)); + string memory feedId = _bytes32ToHexString(abi.encodePacked(report.feedId)); // Feeds that have been removed between checkUpkeep and performUpkeep should not be updated. if (!s_feedMapping[feedId].active) { @@ -187,7 +187,7 @@ contract MercuryRegistry is ConfirmedOwner, AutomationCompatibleInterface, Strea } // Decodes a mercury respone into an on-chain object. Thanks @mikestone!! - function getReport(bytes memory signedReport) internal pure returns (Report memory) { + function _getReport(bytes memory signedReport) internal pure returns (Report memory) { /* * bytes32[3] memory reportContext, * bytes memory reportData, @@ -209,20 +209,20 @@ contract MercuryRegistry is ConfirmedOwner, AutomationCompatibleInterface, Strea int192 deviationPercentagePPM ) public pure returns (bool) { // Compute absolute difference between the on-chain and off-chain values. - int192 scaledDifference = (onChain - offChain) * scale; + int192 scaledDifference = (onChain - offChain) * SCALE; if (scaledDifference < 0) { scaledDifference = -scaledDifference; } // Compare to the allowed deviation from the on-chain value. - int192 deviationMax = ((onChain * scale) * deviationPercentagePPM) / scale; + int192 deviationMax = ((onChain * SCALE) * deviationPercentagePPM) / SCALE; return scaledDifference > deviationMax; } // Helper function to reconcile a difference in formatting: // - Automation passes feedId into their off-chain lookup function as a string. // - Mercury stores feedId in their reports as a bytes32. - function bytes32ToHexString(bytes memory buffer) internal pure returns (string memory) { + function _bytes32ToHexString(bytes memory buffer) internal pure returns (string memory) { bytes memory converted = new bytes(buffer.length * 2); bytes memory _base = "0123456789abcdef"; for (uint256 i = 0; i < buffer.length; i++) { @@ -243,7 +243,7 @@ contract MercuryRegistry is ConfirmedOwner, AutomationCompatibleInterface, Strea if (s_feedMapping[feedId].active) { revert DuplicateFeed(feedId); } - updateFeed(feedId, feedNames[i], deviationPercentagePPMs[i], stalenessSeconds[i]); + _updateFeed(feedId, feedNames[i], deviationPercentagePPMs[i], stalenessSeconds[i]); s_feedMapping[feedId].active = true; s_feeds.push(feedId); @@ -267,13 +267,13 @@ contract MercuryRegistry is ConfirmedOwner, AutomationCompatibleInterface, Strea if (s_feedMapping[feedId].active) { revert DuplicateFeed(feedId); } - updateFeed(feedId, feedNames[i], deviationPercentagePPMs[i], stalenessSeconds[i]); + _updateFeed(feedId, feedNames[i], deviationPercentagePPMs[i], stalenessSeconds[i]); s_feedMapping[feedId].active = true; } s_feeds = feedIds; } - function updateFeed( + function _updateFeed( string memory feedId, string memory feedName, int192 deviationPercentagePPM, diff --git a/contracts/src/v0.8/automation/dev/MercuryRegistryBatchUpkeep.sol b/contracts/src/v0.8/automation/dev/MercuryRegistryBatchUpkeep.sol index 7005ca4812d..518275f34e7 100644 --- a/contracts/src/v0.8/automation/dev/MercuryRegistryBatchUpkeep.sol +++ b/contracts/src/v0.8/automation/dev/MercuryRegistryBatchUpkeep.sol @@ -62,8 +62,8 @@ contract MercuryRegistryBatchUpkeep is ConfirmedOwner, AutomationCompatibleInter } function checkErrorHandler( - uint256 errCode, - bytes memory extraData + uint256 /* errCode */, + bytes memory /* extraData */ ) external view override returns (bool upkeepNeeded, bytes memory performData) { // dummy function with default values return (false, new bytes(0)); From f9795b37932136e96e5d9960a19e18b849d76b7f Mon Sep 17 00:00:00 2001 From: Jim W Date: Wed, 28 Feb 2024 14:30:16 -0500 Subject: [PATCH 16/16] change error log to warn log (#12204) Co-authored-by: Prashant Yadav <34992934+prashantkumar1982@users.noreply.github.com> --- core/services/pg/transaction.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/services/pg/transaction.go b/core/services/pg/transaction.go index fd7e74baca3..d60270b4fe8 100644 --- a/core/services/pg/transaction.go +++ b/core/services/pg/transaction.go @@ -78,7 +78,7 @@ func sqlxTransactionQ(ctx context.Context, db txBeginner, lggr logger.Logger, fn panic(fmt.Sprintf("panic in transaction; aborting rollback that took longer than 10s: %s", p)) } } else if err != nil { - lggr.Errorf("Error in transaction, rolling back: %s", err) + lggr.Warnf("Error in transaction, rolling back: %s", err) // An error occurred, rollback and return error if rerr := tx.Rollback(); rerr != nil { err = multierr.Combine(err, errors.WithStack(rerr))