diff --git a/pkg/solana/cache_test.go b/pkg/solana/cache_test.go index 59e6c7bb6..5529be342 100644 --- a/pkg/solana/cache_test.go +++ b/pkg/solana/cache_test.go @@ -23,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink-solana/pkg/solana/client" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/db" ) var mockTransmission = []byte{ @@ -94,7 +93,7 @@ func testTransmissionsResponse(t *testing.T, body []byte, sub uint64) []byte { func testSetupReader(t *testing.T, endpoint string) client.Reader { lggr := logger.Test(t) - cfg := config.NewConfig(db.ChainCfg{}, lggr) + cfg := config.NewDefault() client, err := client.NewClient(endpoint, cfg, 1*time.Second, lggr) require.NoError(t, err) return client @@ -170,7 +169,7 @@ func TestCache(t *testing.T) { lggr := logger.Test(t) stateCache := StateCache{ StateID: solana.MustPublicKeyFromBase58("11111111111111111111111111111111"), - cfg: config.NewConfig(db.ChainCfg{}, lggr), + cfg: config.NewDefault(), reader: testSetupReader(t, mockServer.URL), lggr: lggr, } @@ -182,7 +181,7 @@ func TestCache(t *testing.T) { transmissionsCache := TransmissionsCache{ TransmissionsID: solana.MustPublicKeyFromBase58("11111111111111111111111111111112"), - cfg: config.NewConfig(db.ChainCfg{}, lggr), + cfg: config.NewDefault(), reader: testSetupReader(t, mockServer.URL), lggr: lggr, } diff --git a/pkg/solana/chain.go b/pkg/solana/chain.go index bc69a453e..6c2927d76 100644 --- a/pkg/solana/chain.go +++ b/pkg/solana/chain.go @@ -24,7 +24,6 @@ import ( "github.com/smartcontractkit/chainlink-solana/pkg/solana/client" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/db" "github.com/smartcontractkit/chainlink-solana/pkg/solana/monitor" "github.com/smartcontractkit/chainlink-solana/pkg/solana/txm" ) @@ -65,7 +64,7 @@ func (o *ChainOpts) GetLogger() logger.Logger { return o.Logger } -func NewChain(cfg *TOMLConfig, opts ChainOpts) (Chain, error) { +func NewChain(cfg *config.TOMLConfig, opts ChainOpts) (Chain, error) { if !cfg.IsEnabled() { return nil, fmt.Errorf("cannot create new chain with ID %s: chain is disabled", *cfg.ChainID) } @@ -81,7 +80,7 @@ var _ Chain = (*chain)(nil) type chain struct { services.StateMachine id string - cfg *TOMLConfig + cfg *config.TOMLConfig txm *txm.Txm balanceMonitor services.Service lggr logger.Logger @@ -214,7 +213,7 @@ func (v *verifiedCachedClient) GetAccountInfoWithOpts(ctx context.Context, addr return v.ReaderWriter.GetAccountInfoWithOpts(ctx, addr, opts) } -func newChain(id string, cfg *TOMLConfig, ks loop.Keystore, lggr logger.Logger) (*chain, error) { +func newChain(id string, cfg *config.TOMLConfig, ks loop.Keystore, lggr logger.Logger) (*chain, error) { lggr = logger.With(lggr, "chainID", id, "chain", "solana") var ch = chain{ id: id, @@ -297,12 +296,9 @@ func (c *chain) ChainID() string { // getClient returns a client, randomly selecting one from available and valid nodes func (c *chain) getClient() (client.ReaderWriter, error) { - var node db.Node + var node *config.Node var client client.ReaderWriter - nodes, err := c.cfg.ListNodes() - if err != nil { - return nil, fmt.Errorf("failed to get nodes: %w", err) - } + nodes := c.cfg.ListNodes() if len(nodes) == 0 { return nil, errors.New("no nodes available") } @@ -311,10 +307,11 @@ func (c *chain) getClient() (client.ReaderWriter, error) { for _, i := range index { node = nodes[i] // create client and check + var err error client, err = c.verifiedClient(node) // if error, try another node if err != nil { - c.lggr.Warnw("failed to create node", "name", node.Name, "solana-url", node.SolanaURL, "err", err.Error()) + c.lggr.Warnw("failed to create node", "name", node.Name, "solana-url", node.URL, "err", err.Error()) continue } // if all checks passed, mark found and break loop @@ -324,15 +321,23 @@ func (c *chain) getClient() (client.ReaderWriter, error) { if client == nil { return nil, errors.New("no node valid nodes available") } - c.lggr.Debugw("Created client", "name", node.Name, "solana-url", node.SolanaURL) + c.lggr.Debugw("Created client", "name", node.Name, "solana-url", node.URL) return client, nil } // verifiedClient returns a client for node or an error if fails to create the client. // The client will still be returned if the nodes are not valid, or the chain id doesn't match. // Further client calls will try and verify the client, and fail if the client is still not valid. -func (c *chain) verifiedClient(node db.Node) (client.ReaderWriter, error) { - url := node.SolanaURL +func (c *chain) verifiedClient(node *config.Node) (client.ReaderWriter, error) { + if node == nil { + return nil, fmt.Errorf("nil node") + } + + if node.Name == nil || node.URL == nil { + return nil, fmt.Errorf("node config contains nil: %+v", node) + } + + url := node.URL.String() var err error // check if cached client exists @@ -346,7 +351,7 @@ func (c *chain) verifiedClient(node db.Node) (client.ReaderWriter, error) { expectedChainID: c.id, } // create client - cl.ReaderWriter, err = client.NewClient(url, c.cfg, DefaultRequestTimeout, logger.Named(c.lggr, "Client."+node.Name)) + cl.ReaderWriter, err = client.NewClient(url, c.cfg, DefaultRequestTimeout, logger.Named(c.lggr, "Client."+*node.Name)) if err != nil { return nil, fmt.Errorf("failed to create client: %w", err) } diff --git a/pkg/solana/chain_test.go b/pkg/solana/chain_test.go index f01406e81..aa52b8b4d 100644 --- a/pkg/solana/chain_test.go +++ b/pkg/solana/chain_test.go @@ -10,6 +10,7 @@ import ( "sync" "testing" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -18,7 +19,6 @@ import ( "github.com/smartcontractkit/chainlink-solana/pkg/solana/client" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/db" ) const TestSolanaGenesisHashTemplate = `{"jsonrpc":"2.0","result":"%s","id":1}` @@ -46,7 +46,7 @@ func TestSolanaChain_GetClient(t *testing.T) { ch := solcfg.Chain{} ch.SetDefaults() - cfg := &TOMLConfig{ + cfg := &solcfg.TOMLConfig{ ChainID: ptr("devnet"), Chain: ch, } @@ -143,7 +143,7 @@ func TestSolanaChain_VerifiedClient(t *testing.T) { ch := solcfg.Chain{} ch.SetDefaults() - cfg := &TOMLConfig{ + cfg := &solcfg.TOMLConfig{ ChainID: ptr("devnet"), Chain: ch, } @@ -152,28 +152,32 @@ func TestSolanaChain_VerifiedClient(t *testing.T) { lggr: logger.Test(t), clientCache: map[string]*verifiedCachedClient{}, } - node := db.Node{SolanaURL: mockServer.URL} + nName := t.Name() + "-" + uuid.NewString() + node := &solcfg.Node{ + Name: &nName, + URL: config.MustParseURL(mockServer.URL), + } // happy path testChain.id = "devnet" _, err := testChain.verifiedClient(node) - assert.NoError(t, err) + require.NoError(t, err) // retrieve cached client and retrieve slot height c, err := testChain.verifiedClient(node) - assert.NoError(t, err) + require.NoError(t, err) slot, err := c.SlotHeight() assert.NoError(t, err) assert.Equal(t, uint64(1234), slot) - node.SolanaURL = mockServer.URL + "/mismatch" + node.URL = config.MustParseURL(mockServer.URL + "/mismatch") testChain.id = "incorrect" c, err = testChain.verifiedClient(node) assert.NoError(t, err) _, err = c.ChainID() // expect error from id mismatch (even if using a cached client) when performing RPC calls assert.Error(t, err) - assert.Equal(t, fmt.Sprintf("client returned mismatched chain id (expected: %s, got: %s): %s", "incorrect", "devnet", node.SolanaURL), err.Error()) + assert.Equal(t, fmt.Sprintf("client returned mismatched chain id (expected: %s, got: %s): %s", "incorrect", "devnet", node.URL), err.Error()) } func TestSolanaChain_VerifiedClient_ParallelClients(t *testing.T) { @@ -186,7 +190,7 @@ func TestSolanaChain_VerifiedClient_ParallelClients(t *testing.T) { ch := solcfg.Chain{} ch.SetDefaults() - cfg := &TOMLConfig{ + cfg := &solcfg.TOMLConfig{ ChainID: ptr("devnet"), Enabled: ptr(true), Chain: ch, @@ -197,7 +201,11 @@ func TestSolanaChain_VerifiedClient_ParallelClients(t *testing.T) { lggr: logger.Test(t), clientCache: map[string]*verifiedCachedClient{}, } - node := db.Node{SolanaURL: mockServer.URL} + nName := t.Name() + "-" + uuid.NewString() + node := &solcfg.Node{ + Name: &nName, + URL: config.MustParseURL(mockServer.URL), + } var wg sync.WaitGroup wg.Add(2) diff --git a/pkg/solana/client/client_test.go b/pkg/solana/client/client_test.go index 0ac0ebcdb..94871e53a 100644 --- a/pkg/solana/client/client_test.go +++ b/pkg/solana/client/client_test.go @@ -19,7 +19,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/db" ) func TestClient_Reader_Integration(t *testing.T) { @@ -31,7 +30,7 @@ func TestClient_Reader_Integration(t *testing.T) { requestTimeout := 5 * time.Second lggr := logger.Test(t) - cfg := config.NewConfig(db.ChainCfg{}, lggr) + cfg := config.NewDefault() c, err := NewClient(url, cfg, requestTimeout, lggr) require.NoError(t, err) @@ -105,7 +104,7 @@ func TestClient_Reader_ChainID(t *testing.T) { requestTimeout := 5 * time.Second lggr := logger.Test(t) - cfg := config.NewConfig(db.ChainCfg{}, lggr) + cfg := config.NewDefault() c, err := NewClient(mockServer.URL, cfg, requestTimeout, lggr) require.NoError(t, err) @@ -126,7 +125,7 @@ func TestClient_Writer_Integration(t *testing.T) { requestTimeout := 5 * time.Second lggr := logger.Test(t) - cfg := config.NewConfig(db.ChainCfg{}, lggr) + cfg := config.NewDefault() ctx := context.Background() c, err := NewClient(url, cfg, requestTimeout, lggr) @@ -212,7 +211,7 @@ func TestClient_SendTxDuplicates_Integration(t *testing.T) { // create client requestTimeout := 5 * time.Second lggr := logger.Test(t) - cfg := config.NewConfig(db.ChainCfg{}, lggr) + cfg := config.NewDefault() c, err := NewClient(url, cfg, requestTimeout, lggr) require.NoError(t, err) diff --git a/pkg/solana/client/test_helpers_test.go b/pkg/solana/client/test_helpers_test.go index dd9e682a8..1f530da2b 100644 --- a/pkg/solana/client/test_helpers_test.go +++ b/pkg/solana/client/test_helpers_test.go @@ -11,7 +11,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/db" ) func TestSetupLocalSolNode_SimultaneousNetworks(t *testing.T) { @@ -25,7 +24,7 @@ func TestSetupLocalSolNode_SimultaneousNetworks(t *testing.T) { // client configs requestTimeout := 5 * time.Second lggr := logger.Test(t) - cfg := config.NewConfig(db.ChainCfg{}, lggr) + cfg := config.NewDefault() // check & fund address checkFunded := func(t *testing.T, url string) { diff --git a/pkg/solana/cmd/chainlink-solana/main.go b/pkg/solana/cmd/chainlink-solana/main.go index 22f614a18..e8a211af2 100644 --- a/pkg/solana/cmd/chainlink-solana/main.go +++ b/pkg/solana/cmd/chainlink-solana/main.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-solana/pkg/solana" + solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" ) const ( @@ -53,7 +54,7 @@ func (c *pluginRelayer) NewRelayer(ctx context.Context, config string, keystore d := toml.NewDecoder(strings.NewReader(config)) d.DisallowUnknownFields() var cfg struct { - Solana solana.TOMLConfig + Solana solcfg.TOMLConfig } if err := d.Decode(&cfg); err != nil { diff --git a/pkg/solana/config.go b/pkg/solana/config.go index 82c463b64..83ce47883 100644 --- a/pkg/solana/config.go +++ b/pkg/solana/config.go @@ -4,9 +4,7 @@ import ( "errors" "fmt" "net/url" - "time" - "github.com/gagliardetto/solana-go/rpc" "github.com/pelletier/go-toml/v2" "golang.org/x/exp/slices" @@ -14,13 +12,9 @@ import ( relaytypes "github.com/smartcontractkit/chainlink-common/pkg/types" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" - soldb "github.com/smartcontractkit/chainlink-solana/pkg/solana/db" ) -// Deprecated: use TOMLConfigs -type SolanaConfigs = TOMLConfigs - -type TOMLConfigs []*TOMLConfig +type TOMLConfigs []*solcfg.TOMLConfig func (cs TOMLConfigs) ValidateConfig() (err error) { return cs.validateKeys() @@ -65,7 +59,7 @@ func (cs *TOMLConfigs) SetFrom(fs *TOMLConfigs) (err error) { for _, f := range *fs { if f.ChainID == nil { *cs = append(*cs, f) - } else if i := slices.IndexFunc(*cs, func(c *TOMLConfig) bool { + } else if i := slices.IndexFunc(*cs, func(c *solcfg.TOMLConfig) bool { return c.ChainID != nil && *c.ChainID == *f.ChainID }); i == -1 { *cs = append(*cs, f) @@ -87,190 +81,3 @@ func nodeStatus(n *solcfg.Node, id string) (relaytypes.NodeStatus, error) { s.Config = string(b) return s, nil } - -type SolanaNodes []*solcfg.Node - -func (ns *SolanaNodes) SetFrom(fs *SolanaNodes) { - for _, f := range *fs { - if f.Name == nil { - *ns = append(*ns, f) - } else if i := slices.IndexFunc(*ns, func(n *solcfg.Node) bool { - return n.Name != nil && *n.Name == *f.Name - }); i == -1 { - *ns = append(*ns, f) - } else { - setFromNode((*ns)[i], f) - } - } -} - -func setFromNode(n, f *solcfg.Node) { - if f.Name != nil { - n.Name = f.Name - } - if f.URL != nil { - n.URL = f.URL - } -} - -func legacySolNode(n *solcfg.Node, id string) soldb.Node { - return soldb.Node{ - Name: *n.Name, - SolanaChainID: id, - SolanaURL: (*url.URL)(n.URL).String(), - } -} - -// Deprecated: use TOMLConfig -type SolanaConfig = TOMLConfig - -type TOMLConfig struct { - ChainID *string - // Do not access directly, use [IsEnabled] - Enabled *bool - solcfg.Chain - Nodes SolanaNodes -} - -func (c *TOMLConfig) IsEnabled() bool { - return c.Enabled == nil || *c.Enabled -} - -func (c *TOMLConfig) SetFrom(f *TOMLConfig) { - if f.ChainID != nil { - c.ChainID = f.ChainID - } - if f.Enabled != nil { - c.Enabled = f.Enabled - } - setFromChain(&c.Chain, &f.Chain) - c.Nodes.SetFrom(&f.Nodes) -} - -func setFromChain(c, f *solcfg.Chain) { - if f.BalancePollPeriod != nil { - c.BalancePollPeriod = f.BalancePollPeriod - } - if f.ConfirmPollPeriod != nil { - c.ConfirmPollPeriod = f.ConfirmPollPeriod - } - if f.OCR2CachePollPeriod != nil { - c.OCR2CachePollPeriod = f.OCR2CachePollPeriod - } - if f.OCR2CacheTTL != nil { - c.OCR2CacheTTL = f.OCR2CacheTTL - } - if f.TxTimeout != nil { - c.TxTimeout = f.TxTimeout - } - if f.TxRetryTimeout != nil { - c.TxRetryTimeout = f.TxRetryTimeout - } - if f.TxConfirmTimeout != nil { - c.TxConfirmTimeout = f.TxConfirmTimeout - } - if f.SkipPreflight != nil { - c.SkipPreflight = f.SkipPreflight - } - if f.Commitment != nil { - c.Commitment = f.Commitment - } - if f.MaxRetries != nil { - c.MaxRetries = f.MaxRetries - } -} - -func (c *TOMLConfig) ValidateConfig() (err error) { - if c.ChainID == nil { - err = errors.Join(err, config.ErrMissing{Name: "ChainID", Msg: "required for all chains"}) - } else if *c.ChainID == "" { - err = errors.Join(err, config.ErrEmpty{Name: "ChainID", Msg: "required for all chains"}) - } - - if len(c.Nodes) == 0 { - err = errors.Join(err, config.ErrMissing{Name: "Nodes", Msg: "must have at least one node"}) - } - return -} - -func (c *TOMLConfig) TOMLString() (string, error) { - b, err := toml.Marshal(c) - if err != nil { - return "", err - } - return string(b), nil -} - -var _ solcfg.Config = &TOMLConfig{} - -func (c *TOMLConfig) BalancePollPeriod() time.Duration { - return c.Chain.BalancePollPeriod.Duration() -} - -func (c *TOMLConfig) ConfirmPollPeriod() time.Duration { - return c.Chain.ConfirmPollPeriod.Duration() -} - -func (c *TOMLConfig) OCR2CachePollPeriod() time.Duration { - return c.Chain.OCR2CachePollPeriod.Duration() -} - -func (c *TOMLConfig) OCR2CacheTTL() time.Duration { - return c.Chain.OCR2CacheTTL.Duration() -} - -func (c *TOMLConfig) TxTimeout() time.Duration { - return c.Chain.TxTimeout.Duration() -} - -func (c *TOMLConfig) TxRetryTimeout() time.Duration { - return c.Chain.TxRetryTimeout.Duration() -} - -func (c *TOMLConfig) TxConfirmTimeout() time.Duration { - return c.Chain.TxConfirmTimeout.Duration() -} - -func (c *TOMLConfig) SkipPreflight() bool { - return *c.Chain.SkipPreflight -} - -func (c *TOMLConfig) Commitment() rpc.CommitmentType { - return rpc.CommitmentType(*c.Chain.Commitment) -} - -func (c *TOMLConfig) MaxRetries() *uint { - if c.Chain.MaxRetries == nil { - return nil - } - mr := uint(*c.Chain.MaxRetries) - return &mr -} - -func (c *TOMLConfig) FeeEstimatorMode() string { - return *c.Chain.FeeEstimatorMode -} - -func (c *TOMLConfig) ComputeUnitPriceMax() uint64 { - return *c.Chain.ComputeUnitPriceMax -} - -func (c *TOMLConfig) ComputeUnitPriceMin() uint64 { - return *c.Chain.ComputeUnitPriceMin -} - -func (c *TOMLConfig) ComputeUnitPriceDefault() uint64 { - return *c.Chain.ComputeUnitPriceDefault -} - -func (c *TOMLConfig) FeeBumpPeriod() time.Duration { - return c.Chain.FeeBumpPeriod.Duration() -} - -func (c *TOMLConfig) ListNodes() ([]soldb.Node, error) { - var allNodes []soldb.Node - for _, n := range c.Nodes { - allNodes = append(allNodes, legacySolNode(n, *c.ChainID)) - } - return allNodes, nil -} diff --git a/pkg/solana/config/config.go b/pkg/solana/config/config.go index 15a96c62d..83753e5cd 100644 --- a/pkg/solana/config/config.go +++ b/pkg/solana/config/config.go @@ -2,15 +2,11 @@ package config import ( "errors" - "strings" "time" "github.com/gagliardetto/solana-go/rpc" "github.com/smartcontractkit/chainlink-common/pkg/config" - - "github.com/smartcontractkit/chainlink-solana/pkg/solana/db" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/logger" ) // Global solana defaults. @@ -75,172 +71,6 @@ type configSet struct { FeeBumpPeriod time.Duration } -var _ Config = (*cfg)(nil) - -// Deprecated -type cfg struct { - defaults configSet - chain db.ChainCfg - lggr logger.Logger -} - -// NewConfig returns a Config with defaults overridden by dbcfg. -// Deprecated -func NewConfig(dbcfg db.ChainCfg, lggr logger.Logger) *cfg { - return &cfg{ - defaults: defaultConfigSet, - chain: dbcfg, - lggr: lggr, - } -} - -func (c *cfg) BalancePollPeriod() time.Duration { - ch := c.chain.BalancePollPeriod - if ch != nil { - return ch.Duration() - } - return c.defaults.BalancePollPeriod -} - -func (c *cfg) ConfirmPollPeriod() time.Duration { - ch := c.chain.ConfirmPollPeriod - if ch != nil { - return ch.Duration() - } - return c.defaults.ConfirmPollPeriod -} - -func (c *cfg) OCR2CachePollPeriod() time.Duration { - ch := c.chain.OCR2CachePollPeriod - if ch != nil { - return ch.Duration() - } - return c.defaults.OCR2CachePollPeriod -} - -func (c *cfg) OCR2CacheTTL() time.Duration { - ch := c.chain.OCR2CacheTTL - if ch != nil { - return ch.Duration() - } - return c.defaults.OCR2CacheTTL -} - -func (c *cfg) TxTimeout() time.Duration { - ch := c.chain.TxTimeout - if ch != nil { - return ch.Duration() - } - return c.defaults.TxTimeout -} - -func (c *cfg) TxRetryTimeout() time.Duration { - ch := c.chain.TxRetryTimeout - if ch != nil { - return ch.Duration() - } - return c.defaults.TxRetryTimeout -} - -func (c *cfg) TxConfirmTimeout() time.Duration { - ch := c.chain.TxConfirmTimeout - if ch != nil { - return ch.Duration() - } - return c.defaults.TxConfirmTimeout -} - -func (c *cfg) SkipPreflight() bool { - ch := c.chain.SkipPreflight - if ch.Valid { - return ch.Bool - } - return c.defaults.SkipPreflight -} - -func (c *cfg) Commitment() rpc.CommitmentType { - ch := c.chain.Commitment - if ch.Valid { - str := ch.String - var commitment rpc.CommitmentType - switch str { - case "processed": - commitment = rpc.CommitmentProcessed - case "confirmed": - commitment = rpc.CommitmentConfirmed - case "finalized": - commitment = rpc.CommitmentFinalized - default: - c.lggr.Warnf(`Invalid value provided for %s, "%s" - falling back to default "%s"`, "CommitmentType", str, c.defaults.Commitment) - commitment = rpc.CommitmentConfirmed - } - return commitment - } - return c.defaults.Commitment -} - -func (c *cfg) FeeEstimatorMode() string { - ch := c.chain.FeeEstimatorMode - if ch.Valid { - return strings.ToLower(ch.String) - } - return c.defaults.FeeEstimatorMode -} - -func (c *cfg) ComputeUnitPriceMax() uint64 { - ch := c.chain.ComputeUnitPriceMax - if ch.Valid { - if ch.Int64 >= 0 { - return uint64(ch.Int64) - } - c.lggr.Warnf("Negative value provided for ComputeUnitPriceMax, falling back to default: %d", c.defaults.ComputeUnitPriceMax) - } - return c.defaults.ComputeUnitPriceMax -} - -func (c *cfg) ComputeUnitPriceMin() uint64 { - ch := c.chain.ComputeUnitPriceMin - if ch.Valid { - if ch.Int64 >= 0 { - return uint64(ch.Int64) - } - c.lggr.Warnf("Negative value provided for ComputeUnitPriceMin, falling back to default: %d", c.defaults.ComputeUnitPriceMin) - } - return c.defaults.ComputeUnitPriceMin -} - -func (c *cfg) ComputeUnitPriceDefault() uint64 { - ch := c.chain.ComputeUnitPriceDefault - if ch.Valid { - if ch.Int64 >= 0 { - return uint64(ch.Int64) - } - c.lggr.Warnf("Negative value provided for ComputeUnitPriceDefault, falling back to default: %d", c.defaults.ComputeUnitPriceDefault) - } - return c.defaults.ComputeUnitPriceDefault -} - -func (c *cfg) MaxRetries() *uint { - ch := c.chain.MaxRetries - if ch.Valid { - if ch.Int64 < 0 { - c.lggr.Warnf(`Negative value provided for %s: %d, falling back to - let RPC node do a reasonable amount of tries`, "MaxRetries", ch.Int64) - return nil - } - val := uint(ch.Int64) - return &val - } - return c.defaults.MaxRetries -} - -func (c *cfg) FeeBumpPeriod() time.Duration { - ch := c.chain.FeeBumpPeriod - if ch != nil { - return ch.Duration() - } - return c.defaults.FeeBumpPeriod -} - type Chain struct { BalancePollPeriod *config.Duration ConfirmPollPeriod *config.Duration diff --git a/pkg/solana/config/toml.go b/pkg/solana/config/toml.go new file mode 100644 index 000000000..dd337e22d --- /dev/null +++ b/pkg/solana/config/toml.go @@ -0,0 +1,205 @@ +package config + +import ( + "errors" + "time" + + "github.com/gagliardetto/solana-go/rpc" + "github.com/pelletier/go-toml/v2" + "golang.org/x/exp/slices" + + "github.com/smartcontractkit/chainlink-common/pkg/config" +) + +type SolanaNodes []*Node + +func (ns *SolanaNodes) SetFrom(fs *SolanaNodes) { + for _, f := range *fs { + if f.Name == nil { + *ns = append(*ns, f) + } else if i := slices.IndexFunc(*ns, func(n *Node) bool { + return n.Name != nil && *n.Name == *f.Name + }); i == -1 { + *ns = append(*ns, f) + } else { + setFromNode((*ns)[i], f) + } + } +} + +func setFromNode(n, f *Node) { + if f.Name != nil { + n.Name = f.Name + } + if f.URL != nil { + n.URL = f.URL + } +} + +type TOMLConfig struct { + ChainID *string + // Do not access directly, use [IsEnabled] + Enabled *bool + Chain + Nodes SolanaNodes +} + +func (c *TOMLConfig) IsEnabled() bool { + return c.Enabled == nil || *c.Enabled +} + +func (c *TOMLConfig) SetFrom(f *TOMLConfig) { + if f.ChainID != nil { + c.ChainID = f.ChainID + } + if f.Enabled != nil { + c.Enabled = f.Enabled + } + setFromChain(&c.Chain, &f.Chain) + c.Nodes.SetFrom(&f.Nodes) +} + +func setFromChain(c, f *Chain) { + if f.BalancePollPeriod != nil { + c.BalancePollPeriod = f.BalancePollPeriod + } + if f.ConfirmPollPeriod != nil { + c.ConfirmPollPeriod = f.ConfirmPollPeriod + } + if f.OCR2CachePollPeriod != nil { + c.OCR2CachePollPeriod = f.OCR2CachePollPeriod + } + if f.OCR2CacheTTL != nil { + c.OCR2CacheTTL = f.OCR2CacheTTL + } + if f.TxTimeout != nil { + c.TxTimeout = f.TxTimeout + } + if f.TxRetryTimeout != nil { + c.TxRetryTimeout = f.TxRetryTimeout + } + if f.TxConfirmTimeout != nil { + c.TxConfirmTimeout = f.TxConfirmTimeout + } + if f.SkipPreflight != nil { + c.SkipPreflight = f.SkipPreflight + } + if f.Commitment != nil { + c.Commitment = f.Commitment + } + if f.MaxRetries != nil { + c.MaxRetries = f.MaxRetries + } + if f.FeeEstimatorMode != nil { + c.FeeEstimatorMode = f.FeeEstimatorMode + } + if f.ComputeUnitPriceMax != nil { + c.ComputeUnitPriceMax = f.ComputeUnitPriceMax + } + if f.ComputeUnitPriceMin != nil { + c.ComputeUnitPriceMin = f.ComputeUnitPriceMin + } + if f.ComputeUnitPriceDefault != nil { + c.ComputeUnitPriceDefault = f.ComputeUnitPriceDefault + } + if f.FeeBumpPeriod != nil { + c.FeeBumpPeriod = f.FeeBumpPeriod + } +} + +func (c *TOMLConfig) ValidateConfig() (err error) { + if c.ChainID == nil { + err = errors.Join(err, config.ErrMissing{Name: "ChainID", Msg: "required for all chains"}) + } else if *c.ChainID == "" { + err = errors.Join(err, config.ErrEmpty{Name: "ChainID", Msg: "required for all chains"}) + } + + if len(c.Nodes) == 0 { + err = errors.Join(err, config.ErrMissing{Name: "Nodes", Msg: "must have at least one node"}) + } + return +} + +func (c *TOMLConfig) TOMLString() (string, error) { + b, err := toml.Marshal(c) + if err != nil { + return "", err + } + return string(b), nil +} + +var _ Config = &TOMLConfig{} + +func (c *TOMLConfig) BalancePollPeriod() time.Duration { + return c.Chain.BalancePollPeriod.Duration() +} + +func (c *TOMLConfig) ConfirmPollPeriod() time.Duration { + return c.Chain.ConfirmPollPeriod.Duration() +} + +func (c *TOMLConfig) OCR2CachePollPeriod() time.Duration { + return c.Chain.OCR2CachePollPeriod.Duration() +} + +func (c *TOMLConfig) OCR2CacheTTL() time.Duration { + return c.Chain.OCR2CacheTTL.Duration() +} + +func (c *TOMLConfig) TxTimeout() time.Duration { + return c.Chain.TxTimeout.Duration() +} + +func (c *TOMLConfig) TxRetryTimeout() time.Duration { + return c.Chain.TxRetryTimeout.Duration() +} + +func (c *TOMLConfig) TxConfirmTimeout() time.Duration { + return c.Chain.TxConfirmTimeout.Duration() +} + +func (c *TOMLConfig) SkipPreflight() bool { + return *c.Chain.SkipPreflight +} + +func (c *TOMLConfig) Commitment() rpc.CommitmentType { + return rpc.CommitmentType(*c.Chain.Commitment) +} + +func (c *TOMLConfig) MaxRetries() *uint { + if c.Chain.MaxRetries == nil { + return nil + } + mr := uint(*c.Chain.MaxRetries) + return &mr +} + +func (c *TOMLConfig) FeeEstimatorMode() string { + return *c.Chain.FeeEstimatorMode +} + +func (c *TOMLConfig) ComputeUnitPriceMax() uint64 { + return *c.Chain.ComputeUnitPriceMax +} + +func (c *TOMLConfig) ComputeUnitPriceMin() uint64 { + return *c.Chain.ComputeUnitPriceMin +} + +func (c *TOMLConfig) ComputeUnitPriceDefault() uint64 { + return *c.Chain.ComputeUnitPriceDefault +} + +func (c *TOMLConfig) FeeBumpPeriod() time.Duration { + return c.Chain.FeeBumpPeriod.Duration() +} + +func (c *TOMLConfig) ListNodes() SolanaNodes { + return c.Nodes +} + +func NewDefault() *TOMLConfig { + cfg := &TOMLConfig{} + cfg.SetDefaults() + return cfg +} diff --git a/pkg/solana/db/db.go b/pkg/solana/db/db.go deleted file mode 100644 index 30c3e6a1a..000000000 --- a/pkg/solana/db/db.go +++ /dev/null @@ -1,54 +0,0 @@ -package db - -import ( - "database/sql/driver" - "encoding/json" - "errors" - "time" - - "gopkg.in/guregu/null.v4" - - "github.com/smartcontractkit/chainlink-common/pkg/config" -) - -type Node struct { - ID int32 - Name string - SolanaChainID string `json:"solanaChainId" db:"solana_chain_id"` - SolanaURL string `json:"solanaURL" db:"solana_url"` - CreatedAt time.Time - UpdatedAt time.Time -} - -// Deprecated -type ChainCfg struct { - BalancePollPeriod *config.Duration - ConfirmPollPeriod *config.Duration - OCR2CachePollPeriod *config.Duration - OCR2CacheTTL *config.Duration - TxTimeout *config.Duration - TxRetryTimeout *config.Duration - TxConfirmTimeout *config.Duration - SkipPreflight null.Bool // to enable or disable preflight checks - Commitment null.String - MaxRetries null.Int - - FeeEstimatorMode null.String - ComputeUnitPriceMax null.Int - ComputeUnitPriceMin null.Int - ComputeUnitPriceDefault null.Int - FeeBumpPeriod *config.Duration -} - -func (c *ChainCfg) Scan(value interface{}) error { - b, ok := value.([]byte) - if !ok { - return errors.New("type assertion to []byte failed") - } - - return json.Unmarshal(b, c) -} - -func (c *ChainCfg) Value() (driver.Value, error) { - return json.Marshal(c) -} diff --git a/pkg/solana/txm/txm_internal_test.go b/pkg/solana/txm/txm_internal_test.go index 8e303a712..3299e7da6 100644 --- a/pkg/solana/txm/txm_internal_test.go +++ b/pkg/solana/txm/txm_internal_test.go @@ -21,7 +21,6 @@ import ( "github.com/smartcontractkit/chainlink-solana/pkg/solana/client" "github.com/smartcontractkit/chainlink-solana/pkg/solana/client/mocks" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/db" "github.com/smartcontractkit/chainlink-solana/pkg/solana/fees" keyMocks "github.com/smartcontractkit/chainlink-solana/pkg/solana/txm/mocks" @@ -94,7 +93,7 @@ func TestTxm(t *testing.T) { ctx := tests.Context(t) lggr := logger.Test(t) - cfg := config.NewConfig(db.ChainCfg{}, lggr) + cfg := config.NewDefault() mc := mocks.NewReaderWriter(t) // mock solana keystore @@ -577,7 +576,7 @@ func TestTxm(t *testing.T) { func TestTxm_Enqueue(t *testing.T) { // set up configs needed in txm lggr := logger.Test(t) - cfg := config.NewConfig(db.ChainCfg{}, lggr) + cfg := config.NewDefault() mc := mocks.NewReaderWriter(t) // mock solana keystore diff --git a/pkg/solana/txm/txm_test.go b/pkg/solana/txm/txm_test.go index 597f32043..85e84ba90 100644 --- a/pkg/solana/txm/txm_test.go +++ b/pkg/solana/txm/txm_test.go @@ -16,7 +16,6 @@ import ( solanaClient "github.com/smartcontractkit/chainlink-solana/pkg/solana/client" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/db" "github.com/smartcontractkit/chainlink-solana/pkg/solana/txm" keyMocks "github.com/smartcontractkit/chainlink-solana/pkg/solana/txm/mocks" @@ -59,11 +58,8 @@ func TestTxm_Integration(t *testing.T) { // set up txm lggr := logger.Test(t) - confirmDuration, err := relayconfig.NewDuration(500 * time.Millisecond) - require.NoError(t, err) - cfg := config.NewConfig(db.ChainCfg{ - ConfirmPollPeriod: &confirmDuration, - }, lggr) + cfg := config.NewDefault() + cfg.Chain.ConfirmPollPeriod = relayconfig.MustNewDuration(500 * time.Millisecond) client, err := solanaClient.NewClient(url, cfg, 2*time.Second, lggr) require.NoError(t, err) getClient := func() (solanaClient.ReaderWriter, error) {