diff --git a/.golangci.yml b/.golangci.yml index d419b3ebd..64fee9d75 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,3 +1,5 @@ +run: + timeout: 15m0s linters: enable: - exhaustive @@ -7,15 +9,18 @@ linters: - gosec - misspell - rowserrcheck - disable: - # These are all considered deprecated: https://github.com/golangci/golangci-lint/issues/1841 - - deadcode - - structcheck - - unused - - varcheck + - errorlint + - unconvert + - sqlclosecheck + - noctx + - whitespace + - depguard + - containedctx linters-settings: exhaustive: default-signifies-exhaustive: true + goimports: + local-prefixes: github.com/smartcontractkit/chainlink-solana golint: min-confidence: 1.0 gosec: @@ -28,6 +33,9 @@ linters-settings: govet: # report about shadowed variables check-shadowing: true + errorlint: + # Allow formatting of errors without %w + errorf: false revive: confidence: 0.8 rules: @@ -38,10 +46,10 @@ linters-settings: - name: error-return - name: error-strings - name: error-naming - # - name: exported + - name: exported - name: if-return - name: increment-decrement - # - name: var-naming + - name: var-naming - name: var-declaration - name: package-comments - name: range @@ -60,30 +68,54 @@ linters-settings: - name: struct-tag # - name: string-format - name: string-of-int - # - name: range-val-address + - name: range-val-address - name: range-val-in-closure - name: modifies-value-receiver - name: modifies-parameter - name: identical-branches - name: get-return # - name: flag-parameter - # - name: early-return + - name: early-return - name: defer - name: constant-logical-expr # - name: confusing-naming # - name: confusing-results - name: bool-literal-in-expr - name: atomic + depguard: + rules: + main: + list-mode: lax + deny: + - pkg: "cosmossdk.io/errors" + desc: Use the standard library instead + - pkg: "github.com/ethereum/go-ethereum" + desc: This is chain must be isolated from ethereum + - pkg: "github.com/go-gorm/gorm" + desc: Use github.com/jmoiron/sqlx directly instead + - pkg: "github.com/gofrs/uuid" + desc: Use github.com/google/uuid instead + - pkg: "github.com/pkg/errors" + desc: Use the standard library instead, for example https://pkg.go.dev/errors#Join + - pkg: "github.com/satori/go.uuid" + desc: Use github.com/google/uuid instead + - pkg: "github.com/test-go/testify/assert" + desc: Use github.com/stretchr/testify/assert instead + - pkg: "github.com/test-go/testify/mock" + desc: Use github.com/stretchr/testify/mock instead + - pkg: "github.com/test-go/testify/require" + desc: Use github.com/stretchr/testify/require instead + - pkg: "go.uber.org/multierr" + desc: Use the standard library instead, for example https://pkg.go.dev/errors#Join + - pkg: "gopkg.in/guregu/null.v1" + desc: Use gopkg.in/guregu/null.v4 instead + - pkg: "gopkg.in/guregu/null.v2" + desc: Use gopkg.in/guregu/null.v4 instead + - pkg: "gopkg.in/guregu/null.v3" + desc: Use gopkg.in/guregu/null.v4 instead issues: exclude-rules: - path: test text: "^G404:" linters: - - gosec - include: - # Enable GoDoc comment checks. - - EXC0002 - - EXC0011 - - EXC0012 - - EXC0013 - - EXC0014 + - gosec \ No newline at end of file diff --git a/Makefile b/Makefile index a1adf0dec..43acdd31a 100644 --- a/Makefile +++ b/Makefile @@ -94,11 +94,11 @@ gomodtidy: .PHONY: lint-go-integration-tests lint-go-integration-tests: - cd ./integration-tests && golangci-lint --color=always --exclude=dot-imports --timeout 10m --out-format checkstyle:golangci-lint-integration-tests-report.xml run || true + cd ./integration-tests && golangci-lint --max-issues-per-linter 0 --max-same-issues 0 --color=always --exclude=dot-imports --timeout 10m --out-format checkstyle:golangci-lint-integration-tests-report.xml run || true .PHONY: lint-go-relay lint-go-relay: - cd ./pkg && golangci-lint --color=always --exclude=dot-imports --timeout 10m --out-format checkstyle:golangci-lint-relay-report.xml run || true + cd ./pkg && golangci-lint --max-issues-per-linter 0 --max-same-issues 0 --color=always --exclude=dot-imports --timeout 10m --out-format checkstyle:golangci-lint-relay-report.xml run || true .PHONY: upgrade-e2e-solana-image upgrade-e2e-solana-image: diff --git a/go.mod b/go.mod index 4524577f1..d24e3f5fb 100644 --- a/go.mod +++ b/go.mod @@ -13,12 +13,10 @@ require ( github.com/hashicorp/go-plugin v1.6.0 github.com/mitchellh/mapstructure v1.5.0 github.com/pelletier/go-toml/v2 v2.1.1 - github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.17.0 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240507142850-569a909ad3b4 github.com/smartcontractkit/libocr v0.0.0-20240326191951-2bbe9382d052 github.com/stretchr/testify v1.9.0 - go.uber.org/multierr v1.11.0 go.uber.org/zap v1.26.0 golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa golang.org/x/sync v0.6.0 @@ -77,6 +75,7 @@ require ( github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9 // indirect github.com/oklog/run v1.0.0 // indirect github.com/onsi/gomega v1.24.1 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect @@ -104,6 +103,7 @@ require ( go.opentelemetry.io/otel/sdk v1.19.0 // indirect go.opentelemetry.io/otel/trace v1.19.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 golang.org/x/crypto v0.18.0 // indirect golang.org/x/mod v0.14.0 // indirect diff --git a/integration-tests/common/test_common.go b/integration-tests/common/test_common.go index 59db94fc4..c809a6f1d 100644 --- a/integration-tests/common/test_common.go +++ b/integration-tests/common/test_common.go @@ -14,12 +14,13 @@ import ( "github.com/rs/zerolog/log" "github.com/stretchr/testify/require" - test_env_sol "github.com/smartcontractkit/chainlink-solana/integration-tests/docker/test_env" - "github.com/smartcontractkit/chainlink-solana/integration-tests/solclient" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/osutil" "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + test_env_sol "github.com/smartcontractkit/chainlink-solana/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink-solana/integration-tests/solclient" + "golang.org/x/sync/errgroup" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -117,7 +118,6 @@ type ProposalAcceptConfig struct { } func NewOCRv2State(t *testing.T, contracts int, namespacePrefix string, env string, isK8s bool, testConfig *testconfig.TestConfig) (*OCRv2TestState, error) { - c, err := New(env, isK8s).Default(t, namespacePrefix) if err != nil { return nil, err @@ -260,7 +260,6 @@ func (m *OCRv2TestState) NewSolanaClientSetup(networkSettings *solclient.SolNetw Interface("URLs", networkSettings.URLs). Msg("Connected Solana client") return ec, nil - } func (m *OCRv2TestState) SetupClients() { @@ -465,7 +464,6 @@ func (m *OCRv2TestState) ValidateRoundsAfter(chaosStartTime time.Time, timeout t } func (m *OCRv2TestState) GenerateOnChainConfig(nodeKeys []client.NodeKeysBundle, vaultAddress string, proposalId string) (OCR2OnChainConfig, error) { - var oracles []Operator for _, nodeKey := range nodeKeys { @@ -501,7 +499,6 @@ func (m *OCRv2TestState) GenerateOffChainConfig( secret string, ) OCROffChainConfig { - offchainPublicKeys := make([]string, len(nodeKeysBundle)) peerIds := make([]string, len(nodeKeysBundle)) configPublicKeys := make([]string, len(nodeKeysBundle)) @@ -631,7 +628,6 @@ func (m *OCRv2TestState) ConfigureGauntlet(secret string) map[string]string { "LINK": linkToken, "VAULT": vault, } - } // GauntletEnvToRemoteRunner Setup the environment variables that will be needed inside the remote runner diff --git a/integration-tests/docker/test_env/sol.go b/integration-tests/docker/test_env/sol.go index 8920d8aa6..8bec26f7f 100644 --- a/integration-tests/docker/test_env/sol.go +++ b/integration-tests/docker/test_env/sol.go @@ -17,10 +17,11 @@ import ( tcwait "github.com/testcontainers/testcontainers-go/wait" "golang.org/x/exp/slices" - "github.com/smartcontractkit/chainlink-solana/integration-tests/utils" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + + "github.com/smartcontractkit/chainlink-solana/integration-tests/utils" ) const ( @@ -211,7 +212,7 @@ func (f InactiveFeatures) CLIString() string { } // GetInactiveFeatureHashes uses the solana CLI to fetch inactive solana features -// This is used in conjuction with the solana-test-validator command to produce a solana network that has the same features as mainnet +// This is used in conjunction with the solana-test-validator command to produce a solana network that has the same features as mainnet // the solana-test-validator has all features on by default (released + unreleased) func GetInactiveFeatureHashes(url string) (output InactiveFeatures, err error) { cmd := exec.Command("solana", "feature", "status", "-u="+url, "--output=json") // -um is for mainnet url diff --git a/integration-tests/gauntlet/gauntlet_solana.go b/integration-tests/gauntlet/gauntlet_solana.go index 2eea42bee..5244dc6cf 100644 --- a/integration-tests/gauntlet/gauntlet_solana.go +++ b/integration-tests/gauntlet/gauntlet_solana.go @@ -3,9 +3,10 @@ package gauntlet import ( "encoding/json" "fmt" - "github.com/smartcontractkit/chainlink-solana/integration-tests/common" "os" + "github.com/smartcontractkit/chainlink-solana/integration-tests/common" + "github.com/smartcontractkit/chainlink-testing-framework/gauntlet" ) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 328a42a03..3c972d161 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -37,7 +37,7 @@ func TestSolanaOCRV2Smoke(t *testing.T) { name string env map[string]string }{ - {name: "embeded"}, + {name: "embedded"}, {name: "plugins", env: map[string]string{ "CL_MEDIAN_CMD": "chainlink-feeds", "CL_SOLANA_CMD": "chainlink-solana", @@ -238,7 +238,6 @@ func TestSolanaGauntletOCRV2Smoke(t *testing.T) { } _, err = node.MustCreateJob(jobSpec) require.NoError(t, err) - } // Test start diff --git a/integration-tests/solclient/access_controller.go b/integration-tests/solclient/access_controller.go index de5290320..734b026c3 100644 --- a/integration-tests/solclient/access_controller.go +++ b/integration-tests/solclient/access_controller.go @@ -2,6 +2,7 @@ package solclient import ( "github.com/gagliardetto/solana-go" + access_controller2 "github.com/smartcontractkit/chainlink-solana/contracts/generated/access_controller" ) diff --git a/integration-tests/solclient/solclient.go b/integration-tests/solclient/solclient.go index 0aa3990c1..ea54027bb 100644 --- a/integration-tests/solclient/solclient.go +++ b/integration-tests/solclient/solclient.go @@ -497,7 +497,6 @@ func (c *Client) AddHeaderEventSubscription(key string, subscriber blockchain.He } func SendFunds(senderPrivateKey string, receiverPublicKey string, lamports uint64, rpcClient *rpc.Client, wsClient *ws.Client) error { - // Convert the private key string to a byte slice var privateKeyBytes []byte err := json.Unmarshal([]byte(senderPrivateKey), &privateKeyBytes) diff --git a/integration-tests/solclient/store.go b/integration-tests/solclient/store.go index abc9eebe8..238d5cc31 100644 --- a/integration-tests/solclient/store.go +++ b/integration-tests/solclient/store.go @@ -5,6 +5,7 @@ import ( "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" + "github.com/smartcontractkit/chainlink-solana/contracts/generated/store" relaySol "github.com/smartcontractkit/chainlink-solana/pkg/solana" ) diff --git a/pkg/monitoring/chain_reader.go b/pkg/monitoring/chain_reader.go index 11f19e384..ffefd4f59 100644 --- a/pkg/monitoring/chain_reader.go +++ b/pkg/monitoring/chain_reader.go @@ -5,6 +5,7 @@ import ( "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" + pkgSolana "github.com/smartcontractkit/chainlink-solana/pkg/solana" ) diff --git a/pkg/monitoring/config/feed_config.go b/pkg/monitoring/config/feed_config.go index 2c4cc31ae..0090de34e 100644 --- a/pkg/monitoring/config/feed_config.go +++ b/pkg/monitoring/config/feed_config.go @@ -89,7 +89,7 @@ func (s SolanaFeedConfig) ToMapping() map[string]interface{} { "feed_name": s.Name, "feed_path": s.Path, "symbol": s.Symbol, - "heartbeat_sec": int64(s.HeartbeatSec), + "heartbeat_sec": s.HeartbeatSec, "contract_type": s.ContractType, "contract_status": s.ContractStatus, "contract_address": s.ContractAddress.Bytes(), diff --git a/pkg/monitoring/exporter/feedbalances.go b/pkg/monitoring/exporter/feedbalances.go index 5db261b50..5affa4f6d 100644 --- a/pkg/monitoring/exporter/feedbalances.go +++ b/pkg/monitoring/exporter/feedbalances.go @@ -7,6 +7,7 @@ import ( "github.com/gagliardetto/solana-go" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types" ) diff --git a/pkg/monitoring/exporter/feedbalances_test.go b/pkg/monitoring/exporter/feedbalances_test.go index 0a333f461..9972cfa63 100644 --- a/pkg/monitoring/exporter/feedbalances_test.go +++ b/pkg/monitoring/exporter/feedbalances_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics/mocks" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/testutils" diff --git a/pkg/monitoring/exporter/fees_test.go b/pkg/monitoring/exporter/fees_test.go index 2f9bc478a..c715d4e4a 100644 --- a/pkg/monitoring/exporter/fees_test.go +++ b/pkg/monitoring/exporter/fees_test.go @@ -10,7 +10,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" - "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics/mocks" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/testutils" @@ -19,7 +19,7 @@ import ( ) func TestFees(t *testing.T) { - ctx := utils.Context(t) + ctx := tests.Context(t) lgr, logs := logger.TestObserved(t, zapcore.ErrorLevel) m := mocks.NewFees(t) m.On("Set", mock.Anything, mock.Anything, mock.Anything).Once() diff --git a/pkg/monitoring/exporter/nodebalances.go b/pkg/monitoring/exporter/nodebalances.go index 90da67704..62a577b63 100644 --- a/pkg/monitoring/exporter/nodebalances.go +++ b/pkg/monitoring/exporter/nodebalances.go @@ -7,6 +7,7 @@ import ( "github.com/gagliardetto/solana-go" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types" ) @@ -18,7 +19,6 @@ func NewNodeBalancesFactory(log commonMonitoring.Logger, metricsFunc metricsBuil log, metricsFunc, } - } type nodeBalancesFactory struct { diff --git a/pkg/monitoring/exporter/nodebalances_test.go b/pkg/monitoring/exporter/nodebalances_test.go index 60c8091b1..b68f8a5d4 100644 --- a/pkg/monitoring/exporter/nodebalances_test.go +++ b/pkg/monitoring/exporter/nodebalances_test.go @@ -10,15 +10,15 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" - "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/testutils" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types" ) func TestNodeBalances(t *testing.T) { - ctx := utils.Context(t) + ctx := tests.Context(t) lgr, logs := logger.TestObserved(t, zapcore.ErrorLevel) factory := NewNodeBalancesFactory(lgr, metrics.NewNodeBalances) diff --git a/pkg/monitoring/exporter/reportobservations.go b/pkg/monitoring/exporter/reportobservations.go index 80e980c81..e7809024a 100644 --- a/pkg/monitoring/exporter/reportobservations.go +++ b/pkg/monitoring/exporter/reportobservations.go @@ -4,6 +4,7 @@ import ( "context" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types" ) diff --git a/pkg/monitoring/exporter/reportobservations_test.go b/pkg/monitoring/exporter/reportobservations_test.go index 70726720e..b7a3b4b3d 100644 --- a/pkg/monitoring/exporter/reportobservations_test.go +++ b/pkg/monitoring/exporter/reportobservations_test.go @@ -10,14 +10,15 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" - "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics/mocks" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/testutils" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types" ) func TestReportObservations(t *testing.T) { - ctx := utils.Context(t) + ctx := tests.Context(t) lgr, logs := logger.TestObserved(t, zapcore.ErrorLevel) m := mocks.NewReportObservations(t) m.On("SetCount", mock.Anything, mock.Anything).Once() diff --git a/pkg/monitoring/exporter/slotheight.go b/pkg/monitoring/exporter/slotheight.go index 6982e3c18..4b49c1c7d 100644 --- a/pkg/monitoring/exporter/slotheight.go +++ b/pkg/monitoring/exporter/slotheight.go @@ -4,6 +4,7 @@ import ( "context" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types" ) diff --git a/pkg/monitoring/exporter/slotheight_test.go b/pkg/monitoring/exporter/slotheight_test.go index 072e27b49..91ca1827c 100644 --- a/pkg/monitoring/exporter/slotheight_test.go +++ b/pkg/monitoring/exporter/slotheight_test.go @@ -8,14 +8,15 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" - "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics/mocks" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/testutils" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types" ) func TestSlotHeight(t *testing.T) { - ctx := utils.Context(t) + ctx := tests.Context(t) m := mocks.NewSlotHeight(t) m.On("Set", mock.Anything, mock.Anything, mock.Anything).Once() m.On("Cleanup").Once() diff --git a/pkg/monitoring/metrics/nodebalances.go b/pkg/monitoring/metrics/nodebalances.go index 0bee640ba..9e14fa19d 100644 --- a/pkg/monitoring/metrics/nodebalances.go +++ b/pkg/monitoring/metrics/nodebalances.go @@ -4,6 +4,7 @@ import ( "github.com/prometheus/client_golang/prometheus" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types" ) diff --git a/pkg/monitoring/source_balances_test.go b/pkg/monitoring/source_balances_test.go index cf940a04d..33f510aac 100644 --- a/pkg/monitoring/source_balances_test.go +++ b/pkg/monitoring/source_balances_test.go @@ -14,7 +14,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" - "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/config" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/mocks" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/testutils" @@ -25,18 +26,19 @@ import ( func TestFeedBalancesSource(t *testing.T) { cr := mocks.NewChainReader(t) lgr := logger.Test(t) - ctx := utils.Context(t) + ctx := tests.Context(t) factory := NewFeedBalancesSourceFactory(cr, lgr) assert.Equal(t, types.BalanceType, factory.GetType()) // generate source - source, err := factory.NewSource(nil, nil) + _, err := factory.NewSource(nil, nil) assert.Error(t, err) - source, err = factory.NewSource(nil, config.SolanaFeedConfig{ + source, err := factory.NewSource(nil, config.SolanaFeedConfig{ ContractAddress: testutils.GeneratePublicKey(), StateAccount: testutils.GeneratePublicKey(), }) + require.NoError(t, err) cr.On("GetState", mock.Anything, mock.Anything, mock.Anything).Return(pkgSolana.State{}, uint64(0), fmt.Errorf("fail")).Once() cr.On("GetState", mock.Anything, mock.Anything, mock.Anything).Return(pkgSolana.State{ @@ -76,7 +78,7 @@ func TestFeedBalancesSource(t *testing.T) { func TestBalancesSource(t *testing.T) { cr := mocks.NewChainReader(t) lgr, logs := logger.TestObserved(t, zapcore.ErrorLevel) - ctx := utils.Context(t) + ctx := tests.Context(t) b := balancesSource{ client: cr, @@ -132,7 +134,7 @@ func TestBalancesSource(t *testing.T) { func TestNodeBalancesSource(t *testing.T) { cr := mocks.NewChainReader(t) lgr := logger.Test(t) - ctx := utils.Context(t) + ctx := tests.Context(t) key := solana.PublicKey{1} factory := NewNodeBalancesSourceFactory(cr, lgr) diff --git a/pkg/monitoring/source_envelope.go b/pkg/monitoring/source_envelope.go index 4b0d519c6..8c2380d7d 100644 --- a/pkg/monitoring/source_envelope.go +++ b/pkg/monitoring/source_envelope.go @@ -2,6 +2,7 @@ package monitoring import ( "context" + "errors" "fmt" "math/big" "sync" @@ -10,9 +11,9 @@ import ( "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" "github.com/smartcontractkit/libocr/offchainreporting2/types" - "go.uber.org/multierr" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/config" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/event" pkgSolana "github.com/smartcontractkit/chainlink-solana/pkg/solana" @@ -88,7 +89,7 @@ func (s *envelopeSource) Fetch(ctx context.Context) (interface{}, error) { envelopeMu.Lock() defer envelopeMu.Unlock() if transmissionErr != nil { - envelopeErr = multierr.Combine(envelopeErr, fmt.Errorf("failed to fetch latest on-chain transmission: %w", transmissionErr)) + envelopeErr = errors.Join(envelopeErr, fmt.Errorf("failed to fetch latest on-chain transmission: %w", transmissionErr)) return } envelope.LatestAnswer = answer.Data @@ -100,7 +101,7 @@ func (s *envelopeSource) Fetch(ctx context.Context) (interface{}, error) { envelopeMu.Lock() defer envelopeMu.Unlock() if linkBalanceErr != nil { - envelopeErr = multierr.Combine(envelopeErr, fmt.Errorf("failed to get the feed's link balance: %w", linkBalanceErr)) + envelopeErr = errors.Join(envelopeErr, fmt.Errorf("failed to get the feed's link balance: %w", linkBalanceErr)) return } envelope.LinkBalance = linkBalance @@ -111,7 +112,7 @@ func (s *envelopeSource) Fetch(ctx context.Context) (interface{}, error) { envelopeMu.Lock() defer envelopeMu.Unlock() if juelsErr != nil { - envelopeErr = multierr.Combine(envelopeErr, fmt.Errorf("Failed to fetch Juels/FeeCoin: %w", juelsErr)) + envelopeErr = errors.Join(envelopeErr, fmt.Errorf("Failed to fetch Juels/FeeCoin: %w", juelsErr)) return } envelope.JuelsPerFeeCoin = juelsPerLamport diff --git a/pkg/monitoring/source_envelope_test.go b/pkg/monitoring/source_envelope_test.go index f882eb0fc..d16db09b9 100644 --- a/pkg/monitoring/source_envelope_test.go +++ b/pkg/monitoring/source_envelope_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/require" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/mocks" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/testutils" pkgSolana "github.com/smartcontractkit/chainlink-solana/pkg/solana" diff --git a/pkg/monitoring/source_feed_balances.go b/pkg/monitoring/source_feed_balances.go index 27b255c7e..577748273 100644 --- a/pkg/monitoring/source_feed_balances.go +++ b/pkg/monitoring/source_feed_balances.go @@ -10,6 +10,7 @@ import ( "github.com/gagliardetto/solana-go/rpc" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/config" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types" ) diff --git a/pkg/monitoring/source_node_balances.go b/pkg/monitoring/source_node_balances.go index 45d76a725..c79b4ef38 100644 --- a/pkg/monitoring/source_node_balances.go +++ b/pkg/monitoring/source_node_balances.go @@ -6,6 +6,7 @@ import ( "github.com/gagliardetto/solana-go" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/config" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types" ) diff --git a/pkg/monitoring/source_slotheight_test.go b/pkg/monitoring/source_slotheight_test.go index 45c704748..3c7af1cfa 100644 --- a/pkg/monitoring/source_slotheight_test.go +++ b/pkg/monitoring/source_slotheight_test.go @@ -8,7 +8,8 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/mocks" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types" ) @@ -16,13 +17,14 @@ import ( func TestSlotHeightSource(t *testing.T) { cr := mocks.NewChainReader(t) lgr := logger.Test(t) - ctx := utils.Context(t) + ctx := tests.Context(t) factory := NewSlotHeightSourceFactory(cr, lgr) assert.Equal(t, types.SlotHeightType, factory.GetType()) // generate source source, err := factory.NewSource(nil, nil) + require.NoError(t, err) cr.On("GetSlot", mock.Anything, mock.Anything, mock.Anything).Return(uint64(1), nil).Once() // happy path diff --git a/pkg/monitoring/source_txresults_test.go b/pkg/monitoring/source_txresults_test.go index e6ca0ba52..9d895be94 100644 --- a/pkg/monitoring/source_txresults_test.go +++ b/pkg/monitoring/source_txresults_test.go @@ -11,7 +11,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" - "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/config" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/mocks" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/testutils" @@ -20,17 +21,18 @@ import ( func TestTxResultsSource(t *testing.T) { cr := mocks.NewChainReader(t) lgr := logger.Test(t) - ctx := utils.Context(t) + ctx := tests.Context(t) factory := NewTxResultsSourceFactory(cr, lgr) assert.Equal(t, txresultsType, factory.GetType()) // generate source - source, err := factory.NewSource(nil, nil) + _, err := factory.NewSource(nil, nil) assert.Error(t, err) - source, err = factory.NewSource(nil, config.SolanaFeedConfig{ + source, err := factory.NewSource(nil, config.SolanaFeedConfig{ StateAccount: testutils.GeneratePublicKey(), }) + require.NoError(t, err) success, fail, sigs := testutils.GenerateTransactionSignatures() assert.Equal(t, 100, success+fail) diff --git a/pkg/monitoring/testutils/testutils.go b/pkg/monitoring/testutils/testutils.go index 37180c8e4..d1998fef7 100644 --- a/pkg/monitoring/testutils/testutils.go +++ b/pkg/monitoring/testutils/testutils.go @@ -12,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/config" "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types" ) diff --git a/pkg/monitoring/types/txdetails_test.go b/pkg/monitoring/types/txdetails_test.go index 8b83e9b7a..84fcf1de8 100644 --- a/pkg/monitoring/types/txdetails_test.go +++ b/pkg/monitoring/types/txdetails_test.go @@ -6,9 +6,10 @@ import ( "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/fees" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-solana/pkg/solana/fees" ) func getTestTxResult(t *testing.T) *rpc.GetTransactionResult { @@ -61,11 +62,11 @@ func TestParseTx(t *testing.T) { txInvalidProgramIndex := getTestTx(t) // copy txInvalidProgramIndex.Message.Instructions[1].ProgramIDIndex = 100 // index 1 is ocr transmit call - out, err := ParseTx(txInvalidProgramIndex, SampleTxResultProgram) + _, err = ParseTx(txInvalidProgramIndex, SampleTxResultProgram) require.Error(t, err) // don't match program - out, err = ParseTx(getTestTx(t), solana.PublicKey{}) + _, err = ParseTx(getTestTx(t), solana.PublicKey{}) require.Error(t, err) // invalid length transmit instruction + compute budget instruction @@ -78,7 +79,7 @@ func TestParseTx(t *testing.T) { require.ErrorContains(t, err, "computeUnitPrice") // happy path - out, err = ParseTx(getTestTx(t), SampleTxResultProgram) + out, err := ParseTx(getTestTx(t), SampleTxResultProgram) require.NoError(t, err) assert.Equal(t, sampleTxResultSigner, out.Sender) assert.Equal(t, uint8(4), out.ObservationCount) diff --git a/pkg/solana/cache_test.go b/pkg/solana/cache_test.go index 57fc83bba..59e6c7bb6 100644 --- a/pkg/solana/cache_test.go +++ b/pkg/solana/cache_test.go @@ -19,6 +19,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-solana/pkg/solana/client" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" @@ -148,6 +149,7 @@ func TestGetLatestTransmission(t *testing.T) { } func TestCache(t *testing.T) { + ctx := tests.Context(t) mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // create response body, err := io.ReadAll(r.Body) @@ -172,9 +174,9 @@ func TestCache(t *testing.T) { reader: testSetupReader(t, mockServer.URL), lggr: lggr, } - require.NoError(t, stateCache.Start()) + require.NoError(t, stateCache.Start(ctx)) require.NoError(t, stateCache.Close()) - require.NoError(t, stateCache.fetchState(context.Background())) + require.NoError(t, stateCache.fetchState(ctx)) assert.Equal(t, "GADeYvXjPwZP7ds1yDY9VFp12bNjdxT1YyksMvFGK9xn", stateCache.state.Transmissions.String()) assert.True(t, !stateCache.stateTime.IsZero()) @@ -184,10 +186,10 @@ func TestCache(t *testing.T) { reader: testSetupReader(t, mockServer.URL), lggr: lggr, } - require.NoError(t, transmissionsCache.Start()) + require.NoError(t, transmissionsCache.Start(ctx)) require.NoError(t, transmissionsCache.Close()) - require.NoError(t, transmissionsCache.fetchLatestTransmission(context.Background())) + require.NoError(t, transmissionsCache.fetchLatestTransmission(ctx)) answer, err := transmissionsCache.ReadAnswer() assert.NoError(t, err) assert.Equal(t, expectedTime, answer.Timestamp) @@ -228,5 +230,4 @@ func TestNilPointerHandling(t *testing.T) { passFirst = true // allow proper response for header query, fail on transmission _, _, err = GetLatestTransmission(context.TODO(), reader, solana.PublicKey{}, "") assert.EqualError(t, err, errString+"GetLatestTransmission.GetAccountInfoWithOpts.Transmission") - } diff --git a/pkg/solana/chain.go b/pkg/solana/chain.go index cc8a1c6f3..bc69a453e 100644 --- a/pkg/solana/chain.go +++ b/pkg/solana/chain.go @@ -2,6 +2,7 @@ package solana import ( "context" + "errors" "fmt" "math/big" "math/rand" @@ -12,8 +13,6 @@ import ( solanago "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/programs/system" "github.com/gagliardetto/solana-go/rpc" - "github.com/pkg/errors" - "go.uber.org/multierr" "github.com/smartcontractkit/chainlink-common/pkg/chains" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -22,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" relaytypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/types/core" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/client" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana/db" @@ -50,13 +50,13 @@ type ChainOpts struct { func (o *ChainOpts) Validate() (err error) { required := func(s string) error { - return errors.Errorf("%s is required", s) + return fmt.Errorf("%s is required", s) } if o.Logger == nil { - err = multierr.Append(err, required("Logger")) + err = errors.Join(err, required("Logger")) } if o.KeyStore == nil { - err = multierr.Append(err, required("KeyStore")) + err = errors.Join(err, required("KeyStore")) } return } @@ -83,7 +83,7 @@ type chain struct { id string cfg *TOMLConfig txm *txm.Txm - balanceMonitor types.Service + balanceMonitor services.Service lggr logger.Logger // tracking node chain id for verification @@ -118,14 +118,14 @@ func (v *verifiedCachedClient) verifyChainID() (bool, error) { v.chainID, err = v.ReaderWriter.ChainID() if err != nil { v.chainIDVerified = false - return v.chainIDVerified, errors.Wrap(err, "failed to fetch ChainID in verifiedCachedClient") + return v.chainIDVerified, fmt.Errorf("failed to fetch ChainID in verifiedCachedClient: %w", err) } // check chainID matches expected chainID expectedChainID := strings.ToLower(v.expectedChainID) if v.chainID != expectedChainID { v.chainIDVerified = false - return v.chainIDVerified, errors.Errorf("client returned mismatched chain id (expected: %s, got: %s): %s", expectedChainID, v.chainID, v.nodeURL) + return v.chainIDVerified, fmt.Errorf("client returned mismatched chain id (expected: %s, got: %s): %s", expectedChainID, v.chainID, v.nodeURL) } v.chainIDVerified = true @@ -301,7 +301,7 @@ func (c *chain) getClient() (client.ReaderWriter, error) { var client client.ReaderWriter nodes, err := c.cfg.ListNodes() if err != nil { - return nil, errors.Wrap(err, "failed to get nodes") + return nil, fmt.Errorf("failed to get nodes: %w", err) } if len(nodes) == 0 { return nil, errors.New("no nodes available") @@ -348,7 +348,7 @@ func (c *chain) verifiedClient(node db.Node) (client.ReaderWriter, error) { // create client cl.ReaderWriter, err = client.NewClient(url, c.cfg, DefaultRequestTimeout, logger.Named(c.lggr, "Client."+node.Name)) if err != nil { - return nil, errors.Wrap(err, "failed to create client") + return nil, fmt.Errorf("failed to create client: %w", err) } c.clientLock.Lock() @@ -384,7 +384,7 @@ func (c *chain) Close() error { } func (c *chain) Ready() error { - return multierr.Combine( + return errors.Join( c.StateMachine.Ready(), c.txm.Ready(), ) diff --git a/pkg/solana/chain_test.go b/pkg/solana/chain_test.go index af87f0485..f01406e81 100644 --- a/pkg/solana/chain_test.go +++ b/pkg/solana/chain_test.go @@ -1,6 +1,7 @@ package solana import ( + "errors" "fmt" "io" "net/http" @@ -9,7 +10,6 @@ import ( "sync" "testing" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -34,7 +34,7 @@ func TestSolanaChain_GetClient(t *testing.T) { // clients with correct chainID should request chainID only once if _, exists := checkOnce[r.URL.Path]; exists { - assert.NoError(t, errors.Errorf("rpc has been called once already for successful client '%s'", r.URL.Path)) + assert.NoError(t, fmt.Errorf("rpc has been called once already for successful client '%s'", r.URL.Path)) } checkOnce[r.URL.Path] = struct{}{} } diff --git a/pkg/solana/client/client.go b/pkg/solana/client/client.go index 766959f3a..89175f511 100644 --- a/pkg/solana/client/client.go +++ b/pkg/solana/client/client.go @@ -2,15 +2,16 @@ package client import ( "context" + "errors" "fmt" "time" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" - "github.com/pkg/errors" + "golang.org/x/sync/singleflight" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana/logger" - "golang.org/x/sync/singleflight" ) const ( @@ -146,7 +147,7 @@ func (c *Client) GetFeeForMessage(msg string) (uint64, error) { defer cancel() res, err := c.rpc.GetFeeForMessage(ctx, msg, c.commitment) if err != nil { - return 0, errors.Wrap(err, "error in GetFeeForMessage") + return 0, fmt.Errorf("error in GetFeeForMessage: %w", err) } if res == nil || res.Value == nil { @@ -163,7 +164,7 @@ func (c *Client) SignatureStatuses(ctx context.Context, sigs []solana.Signature) // searchTransactionHistory = false res, err := c.rpc.GetSignatureStatuses(ctx, false, sigs...) if err != nil { - return nil, errors.Wrap(err, "error in GetSignatureStatuses") + return nil, fmt.Errorf("error in GetSignatureStatuses: %w", err) } if res == nil || res.Value == nil { @@ -187,7 +188,7 @@ func (c *Client) SimulateTx(ctx context.Context, tx *solana.Transaction, opts *r res, err := c.rpc.SimulateTransactionWithOpts(ctx, tx, opts) if err != nil { - return nil, errors.Wrap(err, "error in SimulateTransactionWithOpts") + return nil, fmt.Errorf("error in SimulateTransactionWithOpts: %w", err) } if res == nil || res.Value == nil { diff --git a/pkg/solana/client/client_test.go b/pkg/solana/client/client_test.go index 37abac6b7..0ac0ebcdb 100644 --- a/pkg/solana/client/client_test.go +++ b/pkg/solana/client/client_test.go @@ -13,7 +13,6 @@ import ( "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/programs/system" "github.com/gagliardetto/solana-go/rpc" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -258,7 +257,7 @@ func TestClient_SendTxDuplicates_Integration(t *testing.T) { go func(i int) { time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond) // randomly submit txs sig, err := c.SendTx(ctx, tx) - assert.NoError(t, errors.Wrapf(err, "try #%d", i)) + assert.NoError(t, err) sigs[i] = sig wg.Done() }(i) diff --git a/pkg/solana/client/test_helpers_test.go b/pkg/solana/client/test_helpers_test.go index f1f1dfffe..dd9e682a8 100644 --- a/pkg/solana/client/test_helpers_test.go +++ b/pkg/solana/client/test_helpers_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana/db" ) diff --git a/pkg/solana/cmd/chainlink-solana/main.go b/pkg/solana/cmd/chainlink-solana/main.go index 924dd5a2b..22f614a18 100644 --- a/pkg/solana/cmd/chainlink-solana/main.go +++ b/pkg/solana/cmd/chainlink-solana/main.go @@ -9,6 +9,7 @@ import ( "github.com/pelletier/go-toml/v2" "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-solana/pkg/solana" ) diff --git a/pkg/solana/codec/anchoridl.go b/pkg/solana/codec/anchoridl.go index 359409d5c..e54710c5c 100644 --- a/pkg/solana/codec/anchoridl.go +++ b/pkg/solana/codec/anchoridl.go @@ -64,7 +64,6 @@ type IdlInstruction struct { type IdlAccountItemSlice []IdlAccountItem func (slice IdlAccountItemSlice) NumAccounts() (count int) { - for _, item := range slice { if item.IdlAccount != nil { count++ @@ -224,7 +223,6 @@ func (env *IdlType) UnmarshalJSON(data []byte) error { env.asIdlTypeDefined = &target } if got, ok := v["array"]; ok { - if _, ok := got.([]interface{}); !ok { panic(utilz.Sf("array is not in expected format:\n%s", spew.Sdump(got))) } diff --git a/pkg/solana/config.go b/pkg/solana/config.go index 822064edd..82c463b64 100644 --- a/pkg/solana/config.go +++ b/pkg/solana/config.go @@ -1,13 +1,13 @@ package solana import ( + "errors" "fmt" "net/url" "time" "github.com/gagliardetto/solana-go/rpc" "github.com/pelletier/go-toml/v2" - "go.uber.org/multierr" "golang.org/x/exp/slices" "github.com/smartcontractkit/chainlink-common/pkg/config" @@ -31,7 +31,7 @@ func (cs TOMLConfigs) validateKeys() (err error) { chainIDs := config.UniqueStrings{} for i, c := range cs { if chainIDs.IsDupe(c.ChainID) { - err = multierr.Append(err, config.NewErrDuplicate(fmt.Sprintf("%d.ChainID", i), *c.ChainID)) + err = errors.Join(err, config.NewErrDuplicate(fmt.Sprintf("%d.ChainID", i), *c.ChainID)) } } @@ -40,7 +40,7 @@ func (cs TOMLConfigs) validateKeys() (err error) { for i, c := range cs { for j, n := range c.Nodes { if names.IsDupe(n.Name) { - err = multierr.Append(err, config.NewErrDuplicate(fmt.Sprintf("%d.Nodes.%d.Name", i, j), *n.Name)) + err = errors.Join(err, config.NewErrDuplicate(fmt.Sprintf("%d.Nodes.%d.Name", i, j), *n.Name)) } } } @@ -51,7 +51,7 @@ func (cs TOMLConfigs) validateKeys() (err error) { for j, n := range c.Nodes { u := (*url.URL)(n.URL) if urls.IsDupeFmt(u) { - err = multierr.Append(err, config.NewErrDuplicate(fmt.Sprintf("%d.Nodes.%d.URL", i, j), u.String())) + err = errors.Join(err, config.NewErrDuplicate(fmt.Sprintf("%d.Nodes.%d.URL", i, j), u.String())) } } } @@ -182,13 +182,13 @@ func setFromChain(c, f *solcfg.Chain) { func (c *TOMLConfig) ValidateConfig() (err error) { if c.ChainID == nil { - err = multierr.Append(err, config.ErrMissing{Name: "ChainID", Msg: "required for all chains"}) + err = errors.Join(err, config.ErrMissing{Name: "ChainID", Msg: "required for all chains"}) } else if *c.ChainID == "" { - err = multierr.Append(err, config.ErrEmpty{Name: "ChainID", Msg: "required for all chains"}) + err = errors.Join(err, config.ErrEmpty{Name: "ChainID", Msg: "required for all chains"}) } if len(c.Nodes) == 0 { - err = multierr.Append(err, config.ErrMissing{Name: "Nodes", Msg: "must have at least one node"}) + err = errors.Join(err, config.ErrMissing{Name: "Nodes", Msg: "must have at least one node"}) } return } diff --git a/pkg/solana/config/config.go b/pkg/solana/config/config.go index fd07bf57a..15a96c62d 100644 --- a/pkg/solana/config/config.go +++ b/pkg/solana/config/config.go @@ -1,13 +1,14 @@ package config import ( + "errors" "strings" "time" "github.com/gagliardetto/solana-go/rpc" - "go.uber.org/multierr" "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/db" "github.com/smartcontractkit/chainlink-solana/pkg/solana/logger" ) @@ -305,7 +306,6 @@ func (c *Chain) SetDefaults() { if c.FeeBumpPeriod == nil { c.FeeBumpPeriod = config.MustNewDuration(defaultConfigSet.FeeBumpPeriod) } - return } type Node struct { @@ -315,12 +315,12 @@ type Node struct { func (n *Node) ValidateConfig() (err error) { if n.Name == nil { - err = multierr.Append(err, config.ErrMissing{Name: "Name", Msg: "required for all nodes"}) + err = errors.Join(err, config.ErrMissing{Name: "Name", Msg: "required for all nodes"}) } else if *n.Name == "" { - err = multierr.Append(err, config.ErrEmpty{Name: "Name", Msg: "required for all nodes"}) + err = errors.Join(err, config.ErrEmpty{Name: "Name", Msg: "required for all nodes"}) } if n.URL == nil { - err = multierr.Append(err, config.ErrMissing{Name: "URL", Msg: "required for all nodes"}) + err = errors.Join(err, config.ErrMissing{Name: "URL", Msg: "required for all nodes"}) } return } diff --git a/pkg/solana/config_digester.go b/pkg/solana/config_digester.go index 00d446464..17c70e04a 100644 --- a/pkg/solana/config_digester.go +++ b/pkg/solana/config_digester.go @@ -5,8 +5,6 @@ import ( "encoding/binary" "fmt" - "github.com/pkg/errors" - "github.com/gagliardetto/solana-go" "github.com/smartcontractkit/libocr/offchainreporting2/types" ) @@ -51,14 +49,14 @@ func (d OffchainConfigDigester) ConfigDigest(cfg types.ContractConfig) (types.Co for _, transmitter := range cfg.Transmitters { pubKey, err := solana.PublicKeyFromBase58(string(transmitter)) if err != nil { - return digest, errors.Wrapf(err, "error on parsing base58 encoded public key %s", transmitter) + return digest, fmt.Errorf("error on parsing base58 encoded public key %s: %w", transmitter, err) } if _, err := buf.Write(pubKey.Bytes()); err != nil { return digest, err } } - if err := binary.Write(buf, binary.BigEndian, byte(cfg.F)); err != nil { + if err := binary.Write(buf, binary.BigEndian, cfg.F); err != nil { return digest, err } diff --git a/pkg/solana/fees/computebudget_test.go b/pkg/solana/fees/computebudget_test.go index 4fc3a35c9..64ce7fdce 100644 --- a/pkg/solana/fees/computebudget_test.go +++ b/pkg/solana/fees/computebudget_test.go @@ -62,7 +62,6 @@ func TestSetComputeUnitPrice(t *testing.T) { data, err := ComputeUnitPrice(1).Data() assert.NoError(t, err) assert.Equal(t, data, []byte(tx.Message.Instructions[0].Data)) - }) // // not a valid test, account must exist for tx to be added @@ -99,7 +98,6 @@ func TestSetComputeUnitPrice(t *testing.T) { assert.NoError(t, err) assert.Equal(t, data, []byte(tx.Message.Instructions[1].Data)) }) - } func TestParseComputeUnitPrice(t *testing.T) { diff --git a/pkg/solana/fees/recent_fees.go b/pkg/solana/fees/recent_fees.go index e331a1314..73d49b2d9 100644 --- a/pkg/solana/fees/recent_fees.go +++ b/pkg/solana/fees/recent_fees.go @@ -8,6 +8,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" ) diff --git a/pkg/solana/fees/utils.go b/pkg/solana/fees/utils.go index dd5b5dd7a..f2a376766 100644 --- a/pkg/solana/fees/utils.go +++ b/pkg/solana/fees/utils.go @@ -8,13 +8,13 @@ func CalculateFee(base, max, min uint64, count uint) uint64 { if base == 0 && i == 0 { amount = 1 } else { - // check for overflow - if next := amount + amount; next > amount { - amount = next - } else { + next := amount + amount + if next <= amount { + // overflowed amount = max - break // exit loop if value overflowed + break } + amount = next } } diff --git a/pkg/solana/monitor/balance.go b/pkg/solana/monitor/balance.go index d4b10dbdd..dd7be2344 100644 --- a/pkg/solana/monitor/balance.go +++ b/pkg/solana/monitor/balance.go @@ -8,7 +8,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/utils" solanaClient "github.com/smartcontractkit/chainlink-solana/pkg/solana/client" @@ -25,7 +24,7 @@ type Keystore interface { } // NewBalanceMonitor returns a balance monitoring services.Service which reports the SOL balance of all ks keys to prometheus. -func NewBalanceMonitor(chainID string, cfg Config, lggr logger.Logger, ks Keystore, newReader func() (solanaClient.Reader, error)) types.Service { +func NewBalanceMonitor(chainID string, cfg Config, lggr logger.Logger, ks Keystore, newReader func() (solanaClient.Reader, error)) services.Service { return newBalanceMonitor(chainID, cfg, lggr, ks, newReader) } @@ -54,7 +53,8 @@ type balanceMonitor struct { reader solanaClient.Reader - stop, done chan struct{} + stop services.StopChan + done chan struct{} } func (b *balanceMonitor) Name() string { @@ -82,7 +82,7 @@ func (b *balanceMonitor) HealthReport() map[string]error { func (b *balanceMonitor) monitor() { defer close(b.done) - ctx, cancel := utils.ContextFromChan(b.stop) + ctx, cancel := b.stop.NewCtx() defer cancel() tick := time.After(utils.WithJitter(b.cfg.BalancePollPeriod())) diff --git a/pkg/solana/monitor/balance_test.go b/pkg/solana/monitor/balance_test.go index 508661007..ff98d0508 100644 --- a/pkg/solana/monitor/balance_test.go +++ b/pkg/solana/monitor/balance_test.go @@ -13,6 +13,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/client/mocks" "github.com/smartcontractkit/chainlink-solana/pkg/solana/internal" ) diff --git a/pkg/solana/relay.go b/pkg/solana/relay.go index a74c6afbe..8b5df44ad 100644 --- a/pkg/solana/relay.go +++ b/pkg/solana/relay.go @@ -3,9 +3,10 @@ package solana import ( "context" "encoding/json" + "errors" + "fmt" "github.com/gagliardetto/solana-go" - "github.com/pkg/errors" "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" "github.com/smartcontractkit/libocr/offchainreporting2/types" @@ -30,18 +31,15 @@ var _ relaytypes.Relayer = &Relayer{} type Relayer struct { lggr logger.Logger chain Chain - ctx context.Context - cancel func() + stopCh services.StopChan } // Note: constructed in core func NewRelayer(lggr logger.Logger, chain Chain) *Relayer { - ctx, cancel := context.WithCancel(context.Background()) return &Relayer{ lggr: lggr, chain: chain, - ctx: ctx, - cancel: cancel, + stopCh: make(chan struct{}), } } @@ -60,7 +58,7 @@ func (r *Relayer) Start(context.Context) error { // Close will close all open subservices func (r *Relayer) Close() error { - r.cancel() + close(r.stopCh) return nil } @@ -84,7 +82,9 @@ func (r *Relayer) NewLLOProvider(rargs relaytypes.RelayArgs, pargs relaytypes.Pl } func (r *Relayer) NewConfigProvider(args relaytypes.RelayArgs) (relaytypes.ConfigProvider, error) { - configWatcher, err := newConfigProvider(r.ctx, r.lggr, r.chain, args) + ctx, cancel := r.stopCh.NewCtx() + defer cancel() + configWatcher, err := newConfigProvider(ctx, r.lggr, r.chain, args) if err != nil { // Never return (*configProvider)(nil) return nil, err @@ -93,8 +93,10 @@ func (r *Relayer) NewConfigProvider(args relaytypes.RelayArgs) (relaytypes.Confi } func (r *Relayer) NewMedianProvider(rargs relaytypes.RelayArgs, pargs relaytypes.PluginArgs) (relaytypes.MedianProvider, error) { + ctx, cancel := r.stopCh.NewCtx() + defer cancel() lggr := relaylogger.Named(r.lggr, "MedianProvider") - configWatcher, err := newConfigProvider(r.ctx, lggr, r.chain, rargs) + configWatcher, err := newConfigProvider(ctx, lggr, r.chain, rargs) if err != nil { return nil, err } @@ -102,7 +104,7 @@ func (r *Relayer) NewMedianProvider(rargs relaytypes.RelayArgs, pargs relaytypes // parse transmitter account transmitterAccount, err := solana.PublicKeyFromBase58(pargs.TransmitterID) if err != nil { - return nil, errors.Wrap(err, "error on 'solana.PublicKeyFromBase58' for 'spec.PluginArgs.TransmissionsID") + return nil, fmt.Errorf("error on 'solana.PublicKeyFromBase58' for 'spec.PluginArgs.TransmissionsID: %w", err) } // parse transmissions state account @@ -113,7 +115,7 @@ func (r *Relayer) NewMedianProvider(rargs relaytypes.RelayArgs, pargs relaytypes } transmissionsID, err := solana.PublicKeyFromBase58(relayConfig.TransmissionsID) if err != nil { - return nil, errors.Wrap(err, "error on 'solana.PublicKeyFromBase58' for 'spec.RelayConfig.TransmissionsID") + return nil, fmt.Errorf("error on 'solana.PublicKeyFromBase58' for 'spec.RelayConfig.TransmissionsID: %w", err) } cfg := configWatcher.chain.Config() @@ -170,15 +172,15 @@ func newConfigProvider(ctx context.Context, lggr logger.Logger, chain Chain, arg } stateID, err := solana.PublicKeyFromBase58(args.ContractID) if err != nil { - return nil, errors.Wrap(err, "error on 'solana.PublicKeyFromBase58' for 'spec.ContractID") + return nil, fmt.Errorf("error on 'solana.PublicKeyFromBase58' for 'spec.ContractID: %w", err) } programID, err := solana.PublicKeyFromBase58(relayConfig.OCR2ProgramID) if err != nil { - return nil, errors.Wrap(err, "error on 'solana.PublicKeyFromBase58' for 'spec.RelayConfig.OCR2ProgramID") + return nil, fmt.Errorf("error on 'solana.PublicKeyFromBase58' for 'spec.RelayConfig.OCR2ProgramID: %w", err) } storeProgramID, err := solana.PublicKeyFromBase58(relayConfig.StoreProgramID) if err != nil { - return nil, errors.Wrap(err, "error on 'solana.PublicKeyFromBase58' for 'spec.RelayConfig.StateID") + return nil, fmt.Errorf("error on 'solana.PublicKeyFromBase58' for 'spec.RelayConfig.StateID: %w", err) } offchainConfigDigester := OffchainConfigDigester{ ProgramID: programID, @@ -187,7 +189,7 @@ func newConfigProvider(ctx context.Context, lggr logger.Logger, chain Chain, arg reader, err := chain.Reader() if err != nil { - return nil, errors.Wrap(err, "error in NewMedianProvider.chain.Reader") + return nil, fmt.Errorf("error in NewMedianProvider.chain.Reader: %w", err) } stateCache := NewStateCache(stateID, chain.Config(), reader, lggr) return &configProvider{ @@ -209,7 +211,7 @@ func (c *configProvider) Name() string { func (c *configProvider) Start(ctx context.Context) error { return c.StartOnce("SolanaConfigProvider", func() error { - return c.stateCache.Start() + return c.stateCache.Start(ctx) }) } @@ -241,17 +243,17 @@ type medianProvider struct { transmitter types.ContractTransmitter } -func (m *medianProvider) Name() string { - return m.stateCache.lggr.Name() +func (p *medianProvider) Name() string { + return p.stateCache.lggr.Name() } // start both cache services func (p *medianProvider) Start(ctx context.Context) error { return p.StartOnce("SolanaMedianProvider", func() error { - if err := p.configProvider.stateCache.Start(); err != nil { + if err := p.configProvider.stateCache.Start(ctx); err != nil { return err } - return p.transmissionsCache.Start() + return p.transmissionsCache.Start(ctx) }) } diff --git a/pkg/solana/report_test.go b/pkg/solana/report_test.go index 2ae298e18..4f4b38bdd 100644 --- a/pkg/solana/report_test.go +++ b/pkg/solana/report_test.go @@ -164,7 +164,6 @@ func TestMedianFromReport(t *testing.T) { assert.Equal(t, len(tc.obs), int(count)) }) } - } func TestHashReport(t *testing.T) { @@ -226,7 +225,7 @@ func TestNegativeMedianValue(t *testing.T) { for i, j := 0, len(medianBytes)-1; i < j; i, j = i+1, j-1 { medianBytes[i], medianBytes[j] = medianBytes[j], medianBytes[i] } - bin.NewBinDecoder(medianBytes).Decode(&medianFromRaw) + assert.NoError(t, bin.NewBinDecoder(medianBytes).Decode(&medianFromRaw)) assert.True(t, oo[0].Value.Cmp(medianFromRaw.BigInt()) == 0, "median observation in raw report does not match") // check report can be parsed properly with a negative number diff --git a/pkg/solana/state_cache.go b/pkg/solana/state_cache.go index 18f1e7634..a55ea3930 100644 --- a/pkg/solana/state_cache.go +++ b/pkg/solana/state_cache.go @@ -40,8 +40,7 @@ type StateCache struct { // polling done chan struct{} - ctx context.Context - cancel context.CancelFunc + stopCh services.StopChan } func NewStateCache(stateID solana.PublicKey, cfg config.Config, reader client.Reader, lggr logger.Logger) *StateCache { @@ -54,16 +53,14 @@ func NewStateCache(stateID solana.PublicKey, cfg config.Config, reader client.Re } // Start polling -func (c *StateCache) Start() error { +func (c *StateCache) Start(ctx context.Context) error { return c.StartOnce("pollState", func() error { c.done = make(chan struct{}) - ctx, cancel := context.WithCancel(context.Background()) - c.ctx = ctx - c.cancel = cancel + c.stopCh = make(chan struct{}) // We synchronously update the config on start so that // when OCR starts there is config available (if possible). // Avoids confusing "contract has not been configured" OCR errors. - err := c.fetchState(c.ctx) + err := c.fetchState(ctx) if err != nil { c.lggr.Warnf("error in initial PollState.fetchState %s", err) } @@ -75,17 +72,19 @@ func (c *StateCache) Start() error { // PollState contains the state and transmissions polling implementation func (c *StateCache) PollState() { defer close(c.done) + ctx, cancel := c.stopCh.NewCtx() + defer cancel() c.lggr.Debugf("Starting state polling for state: %s", c.StateID) tick := time.After(0) for { select { - case <-c.ctx.Done(): + case <-ctx.Done(): c.lggr.Debugf("Stopping state polling for state: %s", c.StateID) return case <-tick: // async poll both ocr2 states start := time.Now() - err := c.fetchState(c.ctx) + err := c.fetchState(ctx) if err != nil { c.lggr.Errorf("error in PollState.fetchState %s", err) } @@ -98,7 +97,7 @@ func (c *StateCache) PollState() { // Close stops the polling func (c *StateCache) Close() error { return c.StopOnce("pollState", func() error { - c.cancel() + close(c.stopCh) <-c.done return nil }) @@ -117,7 +116,6 @@ func (c *StateCache) ReadState() (State, error) { } func (c *StateCache) fetchState(ctx context.Context) error { - c.lggr.Debugf("fetch state for account: %s", c.StateID.String()) state, _, err := GetState(ctx, c.reader, c.StateID, c.cfg.Commitment()) if err != nil { diff --git a/pkg/solana/transmissions_cache.go b/pkg/solana/transmissions_cache.go index adea65104..27763caaf 100644 --- a/pkg/solana/transmissions_cache.go +++ b/pkg/solana/transmissions_cache.go @@ -2,13 +2,14 @@ package solana import ( "context" + "errors" + "fmt" "sync" "time" bin "github.com/gagliardetto/binary" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" - "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/utils" @@ -35,8 +36,7 @@ type TransmissionsCache struct { // polling done chan struct{} - ctx context.Context - cancel context.CancelFunc + stopCh services.StopChan } func NewTransmissionsCache(transmissionsID solana.PublicKey, cfg config.Config, reader client.Reader, lggr logger.Logger) *TransmissionsCache { @@ -49,16 +49,14 @@ func NewTransmissionsCache(transmissionsID solana.PublicKey, cfg config.Config, } // Start polling -func (c *TransmissionsCache) Start() error { +func (c *TransmissionsCache) Start(ctx context.Context) error { return c.StartOnce("pollTransmissions", func() error { c.done = make(chan struct{}) - ctx, cancel := context.WithCancel(context.Background()) - c.ctx = ctx - c.cancel = cancel + c.stopCh = make(chan struct{}) // We synchronously update the config on start so that // when OCR starts there is config available (if possible). // Avoids confusing "contract has not been configured" OCR errors. - err := c.fetchLatestTransmission(c.ctx) + err := c.fetchLatestTransmission(ctx) if err != nil { c.lggr.Warnf("error in initial PollTransmissions %s", err) } @@ -70,7 +68,7 @@ func (c *TransmissionsCache) Start() error { // Close stops the polling func (c *TransmissionsCache) Close() error { return c.StopOnce("transmissionCache", func() error { - c.cancel() + close(c.stopCh) <-c.done return nil }) @@ -79,17 +77,19 @@ func (c *TransmissionsCache) Close() error { // PollTransmissions contains the transmissions polling implementation func (c *TransmissionsCache) PollTransmissions() { defer close(c.done) + ctx, cancel := c.stopCh.NewCtx() + defer cancel() c.lggr.Debugf("Starting state polling transmissions: %s", c.TransmissionsID) tick := time.After(0) for { select { - case <-c.ctx.Done(): + case <-ctx.Done(): c.lggr.Debugf("Stopping state polling transmissions: %s", c.TransmissionsID) return case <-tick: // async poll both transmission + ocr2 states start := time.Now() - err := c.fetchLatestTransmission(c.ctx) + err := c.fetchLatestTransmission(ctx) if err != nil { c.lggr.Errorf("error in PollTransmissions.fetchLatestTransmission %s", err) } @@ -141,7 +141,7 @@ func GetLatestTransmission(ctx context.Context, reader client.AccountReader, acc }, }) if err != nil { - return Answer{}, 0, errors.Wrap(err, "error on rpc.GetAccountInfo [cursor]") + return Answer{}, 0, fmt.Errorf("error on rpc.GetAccountInfo [cursor]: %w", err) } // check for nil pointers @@ -152,11 +152,11 @@ func GetLatestTransmission(ctx context.Context, reader client.AccountReader, acc // parse header var header TransmissionsHeader if err = bin.NewBinDecoder(res.Value.Data.GetBinary()).Decode(&header); err != nil { - return Answer{}, 0, errors.Wrap(err, "failed to decode transmission account header") + return Answer{}, 0, fmt.Errorf("failed to decode transmission account header: %w", err) } if header.Version != 2 { - return Answer{}, 0, errors.Wrapf(err, "can't parse feed version %v", header.Version) + return Answer{}, 0, fmt.Errorf("can't parse feed version %v: %w", header.Version, err) } cursor := header.LiveCursor @@ -181,7 +181,7 @@ func GetLatestTransmission(ctx context.Context, reader client.AccountReader, acc }, }) if err != nil { - return Answer{}, 0, errors.Wrap(err, "error on rpc.GetAccountInfo [transmission]") + return Answer{}, 0, fmt.Errorf("error on rpc.GetAccountInfo [transmission]: %w", err) } // check for nil pointers if res == nil || res.Value == nil || res.Value.Data == nil { @@ -191,7 +191,7 @@ func GetLatestTransmission(ctx context.Context, reader client.AccountReader, acc // parse tranmission var t Transmission if err := bin.NewBinDecoder(res.Value.Data.GetBinary()).Decode(&t); err != nil { - return Answer{}, 0, errors.Wrap(err, "failed to decode transmission") + return Answer{}, 0, fmt.Errorf("failed to decode transmission: %w", err) } return Answer{ diff --git a/pkg/solana/transmitter.go b/pkg/solana/transmitter.go index 3d4eb971d..51e31d8ba 100644 --- a/pkg/solana/transmitter.go +++ b/pkg/solana/transmitter.go @@ -3,9 +3,10 @@ package solana import ( "bytes" "context" + "errors" + "fmt" "github.com/gagliardetto/solana-go" - "github.com/pkg/errors" "github.com/smartcontractkit/libocr/offchainreporting2/types" "github.com/smartcontractkit/chainlink-common/pkg/utils" @@ -33,7 +34,7 @@ func (c *Transmitter) Transmit( ) error { blockhash, err := c.reader.LatestBlockhash() if err != nil { - return errors.Wrap(err, "error on Transmit.GetRecentBlockhash") + return fmt.Errorf("error on Transmit.GetRecentBlockhash: %w", err) } if blockhash == nil || blockhash.Value == nil { return errors.New("nil pointer returned from Transmit.GetRecentBlockhash") @@ -43,7 +44,7 @@ func (c *Transmitter) Transmit( seeds := [][]byte{[]byte("store"), c.stateID.Bytes()} storeAuthority, storeNonce, err := solana.FindProgramAddress(seeds, c.programID) if err != nil { - return errors.Wrap(err, "error on Transmit.FindProgramAddress") + return fmt.Errorf("error on Transmit.FindProgramAddress: %w", err) } accounts := []*solana.AccountMeta{ @@ -78,13 +79,13 @@ func (c *Transmitter) Transmit( solana.TransactionPayer(c.transmissionSigner), ) if err != nil { - return errors.Wrap(err, "error on Transmit.NewTransaction") + return fmt.Errorf("error on Transmit.NewTransaction: %w", err) } // pass transmit payload to tx manager queue c.lggr.Debugf("Queuing transmit tx: state (%s) + transmissions (%s)", c.stateID.String(), c.transmissionsID.String()) err = c.txManager.Enqueue(c.stateID.String(), tx) - return errors.Wrap(err, "error on Transmit.txManager.Enqueue") + return fmt.Errorf("error on Transmit.txManager.Enqueue: %w", err) } func (c *Transmitter) LatestConfigDigestAndEpoch( diff --git a/pkg/solana/txm/pendingtx.go b/pkg/solana/txm/pendingtx.go index 3bb004b6f..3a4a5152b 100644 --- a/pkg/solana/txm/pendingtx.go +++ b/pkg/solana/txm/pendingtx.go @@ -27,7 +27,7 @@ var _ PendingTxContext = &pendingTxContext{} type pendingTxContext struct { cancelBy map[uuid.UUID]context.CancelFunc timestamp map[uuid.UUID]time.Time - sigToId map[solana.Signature]uuid.UUID + sigToID map[solana.Signature]uuid.UUID idToSigs map[uuid.UUID][]solana.Signature lock sync.RWMutex } @@ -36,7 +36,7 @@ func newPendingTxContext() *pendingTxContext { return &pendingTxContext{ cancelBy: map[uuid.UUID]context.CancelFunc{}, timestamp: map[uuid.UUID]time.Time{}, - sigToId: map[solana.Signature]uuid.UUID{}, + sigToID: map[solana.Signature]uuid.UUID{}, idToSigs: map[uuid.UUID][]solana.Signature{}, } } @@ -44,7 +44,7 @@ func newPendingTxContext() *pendingTxContext { func (c *pendingTxContext) New(sig solana.Signature, cancel context.CancelFunc) (uuid.UUID, error) { // validate signature does not exist c.lock.RLock() - if _, exists := c.sigToId[sig]; exists { + if _, exists := c.sigToID[sig]; exists { c.lock.RUnlock() return uuid.UUID{}, errors.New("signature already exists") } @@ -53,14 +53,14 @@ func (c *pendingTxContext) New(sig solana.Signature, cancel context.CancelFunc) // upgrade to write lock if sig does not exist c.lock.Lock() defer c.lock.Unlock() - if _, exists := c.sigToId[sig]; exists { + if _, exists := c.sigToID[sig]; exists { return uuid.UUID{}, errors.New("signature already exists") } // save cancel func id := uuid.New() c.cancelBy[id] = cancel c.timestamp[id] = time.Now() - c.sigToId[sig] = id + c.sigToID[sig] = id c.idToSigs[id] = []solana.Signature{sig} return id, nil } @@ -68,7 +68,7 @@ func (c *pendingTxContext) New(sig solana.Signature, cancel context.CancelFunc) func (c *pendingTxContext) Add(id uuid.UUID, sig solana.Signature) error { // already exists c.lock.RLock() - if _, exists := c.sigToId[sig]; exists { + if _, exists := c.sigToID[sig]; exists { c.lock.RUnlock() return errors.New("signature already exists") } @@ -81,14 +81,14 @@ func (c *pendingTxContext) Add(id uuid.UUID, sig solana.Signature) error { // upgrade to write lock if sig does not exist c.lock.Lock() defer c.lock.Unlock() - if _, exists := c.sigToId[sig]; exists { + if _, exists := c.sigToID[sig]; exists { return errors.New("signature already exists") } if _, exists := c.idToSigs[id]; !exists { return errors.New("id does not exist - tx likely confirmed by other signature") } // save signature - c.sigToId[sig] = id + c.sigToID[sig] = id c.idToSigs[id] = append(c.idToSigs[id], sig) return nil } @@ -97,7 +97,7 @@ func (c *pendingTxContext) Add(id uuid.UUID, sig solana.Signature) error { func (c *pendingTxContext) Remove(sig solana.Signature) (id uuid.UUID) { // check if already cancelled c.lock.RLock() - id, sigExists := c.sigToId[sig] + id, sigExists := c.sigToID[sig] if !sigExists { c.lock.RUnlock() return id @@ -111,7 +111,7 @@ func (c *pendingTxContext) Remove(sig solana.Signature) (id uuid.UUID) { // upgrade to write lock if sig does not exist c.lock.Lock() defer c.lock.Unlock() - id, sigExists = c.sigToId[sig] + id, sigExists = c.sigToID[sig] if !sigExists { return id } @@ -126,7 +126,7 @@ func (c *pendingTxContext) Remove(sig solana.Signature) (id uuid.UUID) { delete(c.timestamp, id) delete(c.idToSigs, id) for _, s := range sigs { - delete(c.sigToId, s) + delete(c.sigToID, s) } return id } @@ -134,14 +134,14 @@ func (c *pendingTxContext) Remove(sig solana.Signature) (id uuid.UUID) { func (c *pendingTxContext) ListAll() []solana.Signature { c.lock.RLock() defer c.lock.RUnlock() - return maps.Keys(c.sigToId) + return maps.Keys(c.sigToID) } // Expired returns if the timeout for trying to confirm a signature has been reached func (c *pendingTxContext) Expired(sig solana.Signature, lifespan time.Duration) bool { c.lock.RLock() defer c.lock.RUnlock() - id, exists := c.sigToId[sig] + id, exists := c.sigToID[sig] if !exists { return false // return expired = false if timestamp does not exist (likely cleaned up by something else previously) } diff --git a/pkg/solana/txm/txm.go b/pkg/solana/txm/txm.go index d4ac9389d..c6b2d709f 100644 --- a/pkg/solana/txm/txm.go +++ b/pkg/solana/txm/txm.go @@ -2,6 +2,7 @@ package txm import ( "context" + "errors" "fmt" "strings" "sync" @@ -10,9 +11,9 @@ import ( solanaGo "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" "github.com/google/uuid" - "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/services" + solanaClient "github.com/smartcontractkit/chainlink-solana/pkg/solana/client" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana/fees" @@ -41,7 +42,7 @@ type Txm struct { lggr logger.Logger chSend chan pendingTx chSim chan pendingTx - chStop chan struct{} + chStop services.StopChan done sync.WaitGroup cfg config.Config txs PendingTxContext @@ -101,7 +102,7 @@ func (txm *Txm) Start(ctx context.Context) error { func (txm *Txm) run() { defer txm.done.Done() - ctx, cancel := relayutils.ContextFromChan(txm.chStop) + ctx, cancel := txm.chStop.NewCtx() defer cancel() // start confirmer + simulator @@ -140,7 +141,7 @@ func (txm *Txm) sendWithRetry(chanCtx context.Context, baseTx solanaGo.Transacti // fetch client client, clientErr := txm.client.Get() if clientErr != nil { - return solanaGo.Transaction{}, uuid.Nil, solanaGo.Signature{}, errors.Wrap(clientErr, "failed to get client in soltxm.sendWithRetry") + return solanaGo.Transaction{}, uuid.Nil, solanaGo.Signature{}, fmt.Errorf("failed to get client in soltxm.sendWithRetry: %w", clientErr) } // get key @@ -170,11 +171,11 @@ func (txm *Txm) sendWithRetry(chanCtx context.Context, baseTx solanaGo.Transacti // sign tx txMsg, marshalErr := newTx.Message.MarshalBinary() if marshalErr != nil { - return solanaGo.Transaction{}, errors.Wrap(marshalErr, "error in soltxm.SendWithRetry.MarshalBinary") + return solanaGo.Transaction{}, fmt.Errorf("error in soltxm.SendWithRetry.MarshalBinary: %w", marshalErr) } sigBytes, signErr := txm.ks.Sign(context.TODO(), key, txMsg) if signErr != nil { - return solanaGo.Transaction{}, errors.Wrap(signErr, "error in soltxm.SendWithRetry.Sign") + return solanaGo.Transaction{}, fmt.Errorf("error in soltxm.SendWithRetry.Sign: %w", signErr) } var finalSig [64]byte copy(finalSig[:], sigBytes) @@ -196,14 +197,14 @@ func (txm *Txm) sendWithRetry(chanCtx context.Context, baseTx solanaGo.Transacti if initSendErr != nil { cancel() // cancel context when exiting early txm.txs.OnError(sig, TxFailReject) // increment failed metric - return solanaGo.Transaction{}, uuid.Nil, solanaGo.Signature{}, errors.Wrap(initSendErr, "tx failed initial transmit") + return solanaGo.Transaction{}, uuid.Nil, solanaGo.Signature{}, fmt.Errorf("tx failed initial transmit: %w", initSendErr) } // store tx signature + cancel function id, initStoreErr := txm.txs.New(sig, cancel) if initStoreErr != nil { cancel() // cancel context when exiting early - return solanaGo.Transaction{}, uuid.Nil, solanaGo.Signature{}, errors.Wrapf(initStoreErr, "failed to save tx signature (%s) to inflight txs", sig) + return solanaGo.Transaction{}, uuid.Nil, solanaGo.Signature{}, fmt.Errorf("failed to save tx signature (%s) to inflight txs: %w", sig, initStoreErr) } // used for tracking rebroadcasting only in SendWithRetry @@ -514,7 +515,7 @@ func (txm *Txm) Enqueue(accountID string, tx *solanaGo.Transaction) error { // https://github.com/gagliardetto/solana-go/blob/main/transaction.go#L252 _, err := txm.ks.Sign(context.TODO(), tx.Message.AccountKeys[0].String(), nil) if err != nil { - return errors.Wrap(err, "error in soltxm.Enqueue.GetKey") + return fmt.Errorf("error in soltxm.Enqueue.GetKey: %w", err) } msg := pendingTx{ @@ -526,7 +527,7 @@ func (txm *Txm) Enqueue(accountID string, tx *solanaGo.Transaction) error { case txm.chSend <- msg: default: txm.lggr.Errorw("failed to enqeue tx", "queueFull", len(txm.chSend) == MaxQueueLen, "tx", msg) - return errors.Errorf("failed to enqueue transaction for %s", accountID) + return fmt.Errorf("failed to enqueue transaction for %s", accountID) } return nil } diff --git a/pkg/solana/txm/txm_internal_test.go b/pkg/solana/txm/txm_internal_test.go index 8adb606e6..8e303a712 100644 --- a/pkg/solana/txm/txm_internal_test.go +++ b/pkg/solana/txm/txm_internal_test.go @@ -4,6 +4,7 @@ package txm import ( "context" + "errors" "math/rand" "sync" "testing" @@ -12,7 +13,6 @@ import ( "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/programs/system" "github.com/gagliardetto/solana-go/rpc" - "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" diff --git a/pkg/solana/txm/txm_race_test.go b/pkg/solana/txm/txm_race_test.go index 13d54b907..2a49eb58b 100644 --- a/pkg/solana/txm/txm_race_test.go +++ b/pkg/solana/txm/txm_race_test.go @@ -106,7 +106,8 @@ func TestTxm_SendWithRetry_Race(t *testing.T) { return val } sig := make([]byte, 16) - rand.Read(sig) + _, err := rand.Read(sig) + require.NoError(t, err) txs[strTx] = solanaGo.SignatureFromBytes(sig) return txs[strTx] @@ -134,7 +135,8 @@ func TestTxm_SendWithRetry_Race(t *testing.T) { return val } sig := make([]byte, 16) - rand.Read(sig) + _, err := rand.Read(sig) + require.NoError(t, err) txs[strTx] = solanaGo.SignatureFromBytes(sig) lock.Unlock() @@ -169,7 +171,8 @@ func TestTxm_SendWithRetry_Race(t *testing.T) { return val } sig := make([]byte, 16) - rand.Read(sig) + _, err := rand.Read(sig) + require.NoError(t, err) txs[strTx] = solanaGo.SignatureFromBytes(sig) triggerDelay := len(txs) == 2