Skip to content

Commit

Permalink
- added BlockBackfillDepth & BlockBackfillSkip fallback values
Browse files Browse the repository at this point in the history
- added `v2.ChainScoped` to implement `config.ChainScopedConfig` with a `gencfg.BasicConfig` and `v2.EVMConfig`
- refactored monolithic `setFrom()` methods to be per-struct
- expanded unique name & URL validation cross chains of the same type
- updated graphql & node controllers to use `Name` instead of `ID` from database (still unqiue)
- added TOML Chain and ChainSet variants
- added immutable TOML ORM variants
- pull core defaults from docs
- test EVM fallback defaults against docs
- rename `GeneralOnlyConfig` to `BasicConfig` and move some `GlobalConfig` methods
- added standard `ErrUnsupported` error ("unsupported with config v2")
- cleaner validation error format
  • Loading branch information
jmank88 committed Sep 27, 2022
1 parent 6a252a1 commit 68797d2
Show file tree
Hide file tree
Showing 113 changed files with 4,693 additions and 1,722 deletions.
2 changes: 1 addition & 1 deletion GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ test_chaos: # run core node chaos tests.

.PHONY: config-docs
config-docs: # Generate core node configuration documentation
go run ./internal/config/docs/main.go > ./docs/CONFIG.md
go run ./core/config/v2/docs/cmd/generate/main.go > ./docs/CONFIG.md

help:
@echo ""
Expand Down
4 changes: 2 additions & 2 deletions config_docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import (

"github.com/stretchr/testify/assert"

"github.com/smartcontractkit/chainlink/internal/config"
"github.com/smartcontractkit/chainlink/core/config/v2/docs"
)

//go:embed docs/CONFIG.md
var markdown string

func TestConfigDocs(t *testing.T) {
got, err := config.GenerateDocs()
got, err := docs.GenerateDocs()
assert.NoError(t, err, "invalid config docs")
assert.Equal(t, markdown, got, "docs/CONFIG.md is out of date. Run 'make config-docs' to regenerate.")

Expand Down
35 changes: 35 additions & 0 deletions core/chains/chain_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/pkg/errors"
"go.uber.org/multierr"

cfgv2 "github.com/smartcontractkit/chainlink/core/config/v2"
"github.com/smartcontractkit/chainlink/core/logger"
"github.com/smartcontractkit/chainlink/core/services"
"github.com/smartcontractkit/chainlink/core/services/pg"
Expand Down Expand Up @@ -73,6 +74,8 @@ type chainSet[I ID, C Config, N Node, S ChainService[C]] struct {

chainsMu sync.RWMutex
chains map[string]S

immutable bool // toml config is immutable
}

// NewChainSet returns a new ChainSet for the given ChainSetOpts.
Expand Down Expand Up @@ -103,6 +106,26 @@ func NewChainSet[I ID, C Config, N Node, S ChainService[C]](
}
}

return &cs, err
}

// NewChainSetImmut returns a new immutable ChainSet for the given ChainSetOpts.
func NewChainSetImmut[I ID, C Config, N Node, S ChainService[C]](chains map[string]S,
opts ChainSetOpts[I, C, N, S], formatID func(I) string,
) (ChainSet[I, C, N, S], error) {
if err := opts.Validate(); err != nil {
return nil, err
}
orm, lggr := opts.ORMAndLogger()
cs := chainSet[I, C, N, S]{
opts: opts,
formatID: formatID,
orm: orm,
lggr: lggr.Named("ChainSet"),
chains: chains,
immutable: true,
}

return &cs, nil
}

Expand Down Expand Up @@ -170,6 +193,9 @@ func (c *chainSet[I, C, N, S]) initializeChain(ctx context.Context, dbchain DBCh
}

func (c *chainSet[I, C, N, S]) Add(ctx context.Context, id I, config C) (DBChain[I, C], error) {
if c.immutable {
return DBChain[I, C]{}, cfgv2.ErrUnsupported
}
c.chainsMu.Lock()
defer c.chainsMu.Unlock()

Expand All @@ -190,6 +216,9 @@ func (c *chainSet[I, C, N, S]) Show(id I) (DBChain[I, C], error) {
}

func (c *chainSet[I, C, N, S]) Configure(ctx context.Context, id I, enabled bool, config C) (DBChain[I, C], error) {
if c.immutable {
return DBChain[I, C]{}, cfgv2.ErrUnsupported
}
c.chainsMu.Lock()
defer c.chainsMu.Unlock()

Expand Down Expand Up @@ -219,6 +248,9 @@ func (c *chainSet[I, C, N, S]) Configure(ctx context.Context, id I, enabled bool
}

func (c *chainSet[I, C, N, S]) Remove(id I) error {
if c.immutable {
return cfgv2.ErrUnsupported
}
c.chainsMu.Lock()
defer c.chainsMu.Unlock()

Expand Down Expand Up @@ -253,6 +285,9 @@ func (c *chainSet[I, C, N, S]) CreateNode(ctx context.Context, n N) (N, error) {
}

func (c *chainSet[I, C, N, S]) DeleteNode(ctx context.Context, id int32) error {
if c.immutable {
return cfgv2.ErrUnsupported
}
return c.orm.DeleteNode(id, pg.WithParentCtx(ctx))
}

Expand Down
120 changes: 64 additions & 56 deletions core/chains/evm/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import (

evmclient "github.com/smartcontractkit/chainlink/core/chains/evm/client"
evmconfig "github.com/smartcontractkit/chainlink/core/chains/evm/config"
v2 "github.com/smartcontractkit/chainlink/core/chains/evm/config/v2"
"github.com/smartcontractkit/chainlink/core/chains/evm/headtracker"
httypes "github.com/smartcontractkit/chainlink/core/chains/evm/headtracker/types"
"github.com/smartcontractkit/chainlink/core/chains/evm/log"
"github.com/smartcontractkit/chainlink/core/chains/evm/logpoller"
"github.com/smartcontractkit/chainlink/core/chains/evm/monitor"
"github.com/smartcontractkit/chainlink/core/chains/evm/txmgr"
"github.com/smartcontractkit/chainlink/core/chains/evm/types"
cfgv2 "github.com/smartcontractkit/chainlink/core/config/v2"
"github.com/smartcontractkit/chainlink/core/logger"
"github.com/smartcontractkit/chainlink/core/services"
"github.com/smartcontractkit/chainlink/core/services/keystore"
Expand Down Expand Up @@ -46,6 +48,7 @@ type chain struct {
utils.StartStopOnce
id *big.Int
cfg evmconfig.ChainScopedConfig
cfgImmutable bool // toml config is immutable
client evmclient.Client
txm txmgr.TxManager
logger logger.Logger
Expand All @@ -57,27 +60,60 @@ type chain struct {
keyStore keystore.Eth
}

func newChain(ctx context.Context, dbchain types.DBChain, nodes []types.Node, opts ChainSetOpts) (*chain, error) {
type errChainDisabled struct {
ChainID *utils.Big
}

func (e errChainDisabled) Error() string {
return fmt.Sprintf("cannot create new chain with ID %s, the chain is disabled", e.ChainID.String())
}

func newDBChain(ctx context.Context, dbchain types.DBChain, nodes []types.Node, opts ChainSetOpts) (*chain, error) {
chainID := dbchain.ID.ToInt()
l := opts.Logger.With("evmChainID", chainID.String())
if !dbchain.Enabled {
return nil, errors.Errorf("cannot create new chain with ID %s, the chain is disabled", dbchain.ID.String())
return nil, errChainDisabled{ChainID: &dbchain.ID}
}
cfg := evmconfig.NewChainScopedConfig(chainID, *dbchain.Cfg, opts.ORM, l, opts.Config)
if err := cfg.Validate(); err != nil {
return nil, errors.Wrapf(err, "cannot create new chain with ID %s, config validation failed", dbchain.ID.String())
}
v2ns := make([]*v2.Node, len(nodes))
for i, n := range nodes {
n2 := new(v2.Node)
if err := n2.SetFromDB(n); err != nil {
return nil, errors.Wrapf(err, "failed to convert node")
}
v2ns[i] = n2
}
return newChain(ctx, cfg, v2ns, opts)
}

func newTOMLChain(ctx context.Context, chain *v2.EVMConfig, opts ChainSetOpts) (*chain, error) {
chainID := chain.ChainID
l := opts.Logger.With("evmChainID", chainID.String())
if chain.Enabled != nil && !*chain.Enabled {
return nil, errChainDisabled{ChainID: chainID}
}
cfg := v2.NewTOMLChainScopedConfig(opts.Config, chain, l)
// note: per-chain validation is not ncessary at this point since everything is checked earlier on boot.
return newChain(ctx, cfg, chain.Nodes, opts)
}

func newChain(ctx context.Context, cfg evmconfig.ChainScopedConfig, nodes []*v2.Node, opts ChainSetOpts) (*chain, error) {
chainID := cfg.ChainID()
l := opts.Logger.With("evmChainID", chainID.String())
var client evmclient.Client
if !cfg.EVMRPCEnabled() {
client = evmclient.NewNullClient(chainID, l)
} else if opts.GenEthClient == nil {
var err2 error
client, err2 = newEthClientFromChain(cfg, l, dbchain, nodes)
client, err2 = newEthClientFromChain(cfg, l, cfg.ChainID(), nodes)
if err2 != nil {
return nil, errors.Wrapf(err2, "failed to instantiate eth client for chain with ID %s", dbchain.ID.String())
return nil, errors.Wrapf(err2, "failed to instantiate eth client for chain with ID %s", cfg.ChainID().String())
}
} else {
client = opts.GenEthClient(dbchain)
client = opts.GenEthClient()
}

db := opts.DB
Expand All @@ -91,12 +127,12 @@ func newChain(ctx context.Context, dbchain types.DBChain, nodes []types.Node, op
headSaver = headtracker.NewHeadSaver(l, orm, cfg)
headTracker = headtracker.NewHeadTracker(l, client, cfg, headBroadcaster, headSaver)
} else {
headTracker = opts.GenHeadTracker(dbchain, headBroadcaster)
headTracker = opts.GenHeadTracker(headBroadcaster)
}

var logPoller logpoller.LogPoller = logpoller.NewLogPoller(logpoller.NewORM(chainID, db, l, cfg), client, l, cfg.EvmLogPollInterval(), int64(cfg.EvmFinalityDepth()), int64(cfg.EvmLogBackfillBatchSize()), int64(cfg.EvmRPCDefaultBatchSize()))
if opts.GenLogPoller != nil {
logPoller = opts.GenLogPoller(dbchain)
logPoller = opts.GenLogPoller()
}

var txm txmgr.TxManager
Expand All @@ -106,7 +142,7 @@ func newChain(ctx context.Context, dbchain types.DBChain, nodes []types.Node, op
checker := &txmgr.CheckerFactory{Client: client}
txm = txmgr.NewTxm(db, client, cfg, opts.KeyStore, opts.EventBroadcaster, l, checker, logPoller)
} else {
txm = opts.GenTxManager(dbchain)
txm = opts.GenTxManager()
}

headBroadcaster.Subscribe(txm)
Expand All @@ -130,7 +166,7 @@ func newChain(ctx context.Context, dbchain types.DBChain, nodes []types.Node, op
logORM := log.NewORM(db, l, cfg, *chainID)
logBroadcaster = log.NewBroadcaster(logORM, client, cfg, l, highestSeenHead)
} else {
logBroadcaster = opts.GenLogBroadcaster(dbchain)
logBroadcaster = opts.GenLogBroadcaster()
}

// AddDependent for this chain
Expand Down Expand Up @@ -230,10 +266,16 @@ func (c *chain) Healthy() (merr error) {
return
}

func (c *chain) ID() *big.Int { return c.id }
func (c *chain) Client() evmclient.Client { return c.client }
func (c *chain) Config() evmconfig.ChainScopedConfig { return c.cfg }
func (c *chain) UpdateConfig(cfg *types.ChainCfg) { c.cfg.Configure(*cfg) }
func (c *chain) ID() *big.Int { return c.id }
func (c *chain) Client() evmclient.Client { return c.client }
func (c *chain) Config() evmconfig.ChainScopedConfig { return c.cfg }
func (c *chain) UpdateConfig(cfg *types.ChainCfg) {
if c.cfgImmutable {
c.logger.Criticalw("TOML configuration cannot be updated", "err", cfgv2.ErrUnsupported)
return
}
c.cfg.Configure(*cfg)
}
func (c *chain) LogBroadcaster() log.Broadcaster { return c.logBroadcaster }
func (c *chain) LogPoller() logpoller.LogPoller { return c.logPoller }
func (c *chain) HeadBroadcaster() httypes.HeadBroadcaster { return c.headBroadcaster }
Expand All @@ -242,62 +284,28 @@ func (c *chain) HeadTracker() httypes.HeadTracker { return c.headTracker
func (c *chain) Logger() logger.Logger { return c.logger }
func (c *chain) BalanceMonitor() monitor.BalanceMonitor { return c.balanceMonitor }

func newEthClientFromChain(cfg evmclient.NodeConfig, lggr logger.Logger, chain types.DBChain, nodes []types.Node) (evmclient.Client, error) {
chainID := big.Int(chain.ID)
func newEthClientFromChain(cfg evmclient.NodeConfig, lggr logger.Logger, chainID *big.Int, nodes []*v2.Node) (evmclient.Client, error) {
var primaries []evmclient.Node
var sendonlys []evmclient.SendOnlyNode
for _, node := range nodes {
if node.SendOnly {
sendonly, err := newSendOnly(lggr, node)
if err != nil {
return nil, err
}
for i, node := range nodes {
if node.SendOnly != nil && *node.SendOnly {
sendonly := evmclient.NewSendOnlyNode(lggr, (url.URL)(*node.HTTPURL), *node.Name, chainID)
sendonlys = append(sendonlys, sendonly)
} else {
primary, err := newPrimary(cfg, lggr, node)
primary, err := newPrimary(cfg, lggr, node, int32(i), chainID)
if err != nil {
return nil, err
}
primaries = append(primaries, primary)
}
}
return evmclient.NewClientWithNodes(lggr, cfg, primaries, sendonlys, &chainID)
return evmclient.NewClientWithNodes(lggr, cfg, primaries, sendonlys, chainID)
}

func newPrimary(cfg evmclient.NodeConfig, lggr logger.Logger, n types.Node) (evmclient.Node, error) {
if n.SendOnly {
func newPrimary(cfg evmclient.NodeConfig, lggr logger.Logger, n *v2.Node, id int32, chainID *big.Int) (evmclient.Node, error) {
if n.SendOnly != nil && *n.SendOnly {
return nil, errors.New("cannot cast send-only node to primary")
}
if !n.WSURL.Valid {
return nil, errors.New("primary node was missing WS url")
}
wsuri, err := url.Parse(n.WSURL.String)
if err != nil {
return nil, errors.Wrap(err, "invalid websocket uri")
}
var httpuri *url.URL
if n.HTTPURL.Valid {
u, err := url.Parse(n.HTTPURL.String)
if err != nil {
return nil, errors.Wrap(err, "invalid http uri")
}
httpuri = u
}

return evmclient.NewNode(cfg, lggr, *wsuri, httpuri, n.Name, n.ID, (*big.Int)(&n.EVMChainID)), nil
}

func newSendOnly(lggr logger.Logger, n types.Node) (evmclient.SendOnlyNode, error) {
if !n.SendOnly {
return nil, errors.New("cannot cast non send-only node to send-only node")
}
if !n.HTTPURL.Valid {
return nil, errors.New("send only node was missing HTTP url")
}
httpuri, err := url.Parse(n.HTTPURL.String)
if err != nil {
return nil, errors.Wrap(err, "invalid http uri")
}

return evmclient.NewSendOnlyNode(lggr, *httpuri, n.Name, (*big.Int)(&n.EVMChainID)), nil
return evmclient.NewNode(cfg, lggr, (url.URL)(*n.WSURL), (*url.URL)(n.HTTPURL), *n.Name, id, chainID), nil
}
Loading

0 comments on commit 68797d2

Please sign in to comment.