diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 1a2ea7f0d..6f4814680 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -11,7 +11,6 @@ import ( cfg "github.com/tendermint/tendermint/config" tmos "github.com/tendermint/tendermint/libs/os" - nm "github.com/tendermint/tendermint/node" ) var ( @@ -99,7 +98,7 @@ func AddNodeFlags(cmd *cobra.Command) { // NewRunNodeCmd returns the command that allows the CLI to start a node. // It can be used with a custom PrivValidator and in-process ABCI application. -func NewRunNodeCmd(nodeProvider nm.Provider) *cobra.Command { +func NewRunNodeCmd(nodeProvider cfg.ServiceProvider) *cobra.Command { cmd := &cobra.Command{ Use: "start", Aliases: []string{"node", "run"}, @@ -118,7 +117,7 @@ func NewRunNodeCmd(nodeProvider nm.Provider) *cobra.Command { return fmt.Errorf("failed to start node: %w", err) } - logger.Info("Started node", "nodeInfo", n.NodeInfo()) + logger.Info("started node", "node", n.String()) // Stop upon receiving SIGTERM or CTRL-C. tmos.TrapSignal(logger, func() { diff --git a/cmd/tendermint/main.go b/cmd/tendermint/main.go index 311a59a65..52a26b4f3 100644 --- a/cmd/tendermint/main.go +++ b/cmd/tendermint/main.go @@ -38,8 +38,8 @@ func main() { // * Supply a genesis doc file from another source // * Provide their own DB implementation // can copy this file and use something other than the - // DefaultNewNode function - nodeFunc := nm.DefaultNewNode + // node.NewDefault function + nodeFunc := nm.NewDefault // Create & start node rootCmd.AddCommand(cmd.NewRunNodeCmd(nodeFunc)) diff --git a/config/db.go b/config/db.go new file mode 100644 index 000000000..3ae274a50 --- /dev/null +++ b/config/db.go @@ -0,0 +1,26 @@ +package config + +import ( + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/libs/service" + db "github.com/tendermint/tm-db" +) + +// ServiceProvider takes a config and a logger and returns a ready to go Node. +type ServiceProvider func(*Config, log.Logger) (service.Service, error) + +// DBContext specifies config information for loading a new DB. +type DBContext struct { + ID string + Config *Config +} + +// DBProvider takes a DBContext and returns an instantiated DB. +type DBProvider func(*DBContext) (db.DB, error) + +// DefaultDBProvider returns a database using the DBBackend and DBDir +// specified in the Config. +func DefaultDBProvider(ctx *DBContext) (db.DB, error) { + dbType := db.BackendType(ctx.Config.DBBackend) + return db.NewDB(ctx.ID, dbType, ctx.Config.DBDir()) +} diff --git a/node/doc.go b/node/doc.go index 08f3fa258..109f29e6c 100644 --- a/node/doc.go +++ b/node/doc.go @@ -1,40 +1,6 @@ /* -Package node is the main entry point, where the Node struct, which -represents a full node, is defined. - -Adding new p2p.Reactor(s) - -To add a new p2p.Reactor, use the CustomReactors option: - - node, err := NewNode( - config, - privVal, - nodeKey, - clientCreator, - genesisDocProvider, - dbProvider, - metricsProvider, - logger, - CustomReactors(map[string]p2p.Reactor{"CUSTOM": customReactor}), - ) - -Replacing existing p2p.Reactor(s) - -To replace the built-in p2p.Reactor, use the CustomReactors option: - - node, err := NewNode( - config, - privVal, - nodeKey, - clientCreator, - genesisDocProvider, - dbProvider, - metricsProvider, - logger, - CustomReactors(map[string]p2p.Reactor{"BLOCKCHAIN": customBlockchainReactor}), - ) - -The list of existing reactors can be found in CustomReactors documentation. - +Package node is the main entry point, where the tendermint node +service is constructed and the implementation of that service is +defined. */ package node diff --git a/node/id.go b/node/id.go deleted file mode 100644 index ffa162f81..000000000 --- a/node/id.go +++ /dev/null @@ -1,35 +0,0 @@ -package node - -import ( - "time" - - "github.com/tendermint/tendermint/crypto" -) - -type ID struct { - Name string - PubKey crypto.PubKey -} - -type PrivNodeID struct { - ID - PrivKey crypto.PrivKey -} - -type Greeting struct { - ID - Version string - ChainID string - Message string - Time time.Time -} - -type SignedNodeGreeting struct { - Greeting - Signature []byte -} - -func (pnid *PrivNodeID) SignGreeting() *SignedNodeGreeting { - // greeting := NodeGreeting{} - return nil -} diff --git a/node/node.go b/node/node.go index a4c427504..34589ea0f 100644 --- a/node/node.go +++ b/node/node.go @@ -10,12 +10,10 @@ import ( "strconv" "time" + _ "github.com/lib/pq" // provide the psql db driver "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/rs/cors" - dbm "github.com/tendermint/tm-db" - - _ "github.com/lib/pq" // provide the psql db driver abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" cs "github.com/tendermint/tendermint/consensus" @@ -43,11 +41,12 @@ import ( "github.com/tendermint/tendermint/store" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" + dbm "github.com/tendermint/tm-db" ) -// Node is the highest level interface to a full Tendermint node. +// nodeImpl is the highest level interface to a full Tendermint node. // It includes all configuration information and running services. -type Node struct { +type nodeImpl struct { service.BaseService // config @@ -89,19 +88,19 @@ type Node struct { prometheusSrv *http.Server } -// DefaultNewNode returns a Tendermint node with default settings for the +// newDefaultNode returns a Tendermint node with default settings for the // PrivValidator, ClientCreator, GenesisDoc, and DBProvider. // It implements NodeProvider. -func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { +func newDefaultNode(config *cfg.Config, logger log.Logger) (service.Service, error) { nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) if err != nil { return nil, fmt.Errorf("failed to load or gen node key %s: %w", config.NodeKeyFile(), err) } if config.Mode == cfg.ModeSeed { - return NewSeedNode(config, - DefaultDBProvider, + return makeSeedNode(config, + cfg.DefaultDBProvider, nodeKey, - DefaultGenesisDocProviderFunc(config), + defaultGenesisDocProviderFunc(config), logger, ) } @@ -117,27 +116,24 @@ func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { } appClient, _ := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) - return NewNode(config, + return makeNode(config, pval, nodeKey, appClient, - DefaultGenesisDocProviderFunc(config), - DefaultDBProvider, - DefaultMetricsProvider(config.Instrumentation), + defaultGenesisDocProviderFunc(config), + cfg.DefaultDBProvider, logger, ) } -// NewNode returns a new, ready to go, Tendermint Node. -func NewNode(config *cfg.Config, +// makeNode returns a new, ready to go, Tendermint Node. +func makeNode(config *cfg.Config, privValidator types.PrivValidator, nodeKey p2p.NodeKey, clientCreator proxy.ClientCreator, - genesisDocProvider GenesisDocProvider, - dbProvider DBProvider, - metricsProvider MetricsProvider, - logger log.Logger, - options ...Option) (*Node, error) { + genesisDocProvider genesisDocProvider, + dbProvider cfg.DBProvider, + logger log.Logger) (service.Service, error) { blockStore, stateDB, err := initDBs(config, dbProvider) if err != nil { @@ -146,7 +142,7 @@ func NewNode(config *cfg.Config, stateStore := sm.NewStore(stateDB) - state, genDoc, err := LoadStateFromDBOrGenesisDocProvider(stateDB, genesisDocProvider) + state, genDoc, err := loadStateFromDBOrGenesisDocProvider(stateDB, genesisDocProvider) if err != nil { return nil, err } @@ -245,7 +241,7 @@ func NewNode(config *cfg.Config, return nil, fmt.Errorf("failed to create peer manager: %w", err) } - csMetrics, p2pMetrics, memplMetrics, smMetrics := metricsProvider(genDoc.ChainID) + csMetrics, p2pMetrics, memplMetrics, smMetrics := defaultMetricsProvider(config.Instrumentation)(genDoc.ChainID) router, err := createRouter(p2pLogger, p2pMetrics, nodeInfo, nodeKey.PrivKey, peerManager, transport, getRouterConfig(config, proxyApp)) @@ -412,7 +408,7 @@ func NewNode(config *cfg.Config, }() } - node := &Node{ + node := &nodeImpl{ config: config, genesisDoc: genDoc, privValidator: privValidator, @@ -446,20 +442,16 @@ func NewNode(config *cfg.Config, } node.BaseService = *service.NewBaseService(logger, "Node", node) - for _, option := range options { - option(node) - } - return node, nil } -// NewSeedNode returns a new seed node, containing only p2p, pex reactor -func NewSeedNode(config *cfg.Config, - dbProvider DBProvider, +// makeSeedNode returns a new seed node, containing only p2p, pex reactor +func makeSeedNode(config *cfg.Config, + dbProvider cfg.DBProvider, nodeKey p2p.NodeKey, - genesisDocProvider GenesisDocProvider, + genesisDocProvider genesisDocProvider, logger log.Logger, - options ...Option) (*Node, error) { +) (service.Service, error) { genDoc, err := genesisDocProvider() if err != nil { @@ -538,7 +530,7 @@ func NewSeedNode(config *cfg.Config, }() } - node := &Node{ + node := &nodeImpl{ config: config, genesisDoc: genDoc, @@ -555,58 +547,17 @@ func NewSeedNode(config *cfg.Config, } node.BaseService = *service.NewBaseService(logger, "SeedNode", node) - for _, option := range options { - option(node) - } - return node, nil } -// Option sets a parameter for the node. -type Option func(*Node) - // Temporary interface for switching to fast sync, we should get rid of v0. // See: https://github.com/tendermint/tendermint/issues/4595 type fastSyncReactor interface { SwitchToFastSync(sm.State) error } -// CustomReactors allows you to add custom reactors (name -> p2p.Reactor) to -// the node's Switch. -// -// WARNING: using any name from the below list of the existing reactors will -// result in replacing it with the custom one. -// -// - MEMPOOL -// - BLOCKCHAIN -// - CONSENSUS -// - EVIDENCE -// - PEX -// - STATESYNC -func CustomReactors(reactors map[string]p2p.Reactor) Option { - return func(n *Node) { - for name, reactor := range reactors { - if existingReactor := n.sw.Reactor(name); existingReactor != nil { - n.sw.Logger.Info("Replacing existing reactor with a custom one", - "name", name, "existing", existingReactor, "custom", reactor) - n.sw.RemoveReactor(name, existingReactor) - } - n.sw.AddReactor(name, reactor) - } - } -} - -// StateProvider overrides the state provider used by state sync to retrieve trusted app hashes and -// build a State object for bootstrapping the node. -// WARNING: this interface is considered unstable and subject to change. -func StateProvider(stateProvider statesync.StateProvider) Option { - return func(n *Node) { - n.stateSyncProvider = stateProvider - } -} - // OnStart starts the Node. It implements service.Service. -func (n *Node) OnStart() error { +func (n *nodeImpl) OnStart() error { now := tmtime.Now() genTime := n.genesisDoc.GenesisTime if genTime.After(now) { @@ -712,7 +663,7 @@ func (n *Node) OnStart() error { } // OnStop stops the Node. It implements service.Service. -func (n *Node) OnStop() { +func (n *nodeImpl) OnStop() { n.Logger.Info("Stopping Node") @@ -799,7 +750,7 @@ func (n *Node) OnStop() { } // ConfigureRPC makes sure RPC has all the objects it needs to operate. -func (n *Node) ConfigureRPC() (*rpccore.Environment, error) { +func (n *nodeImpl) ConfigureRPC() (*rpccore.Environment, error) { rpcCoreEnv := rpccore.Environment{ ProxyAppQuery: n.proxyApp.Query(), ProxyAppMempool: n.proxyApp.Mempool(), @@ -835,7 +786,7 @@ func (n *Node) ConfigureRPC() (*rpccore.Environment, error) { return &rpcCoreEnv, nil } -func (n *Node) startRPC() ([]net.Listener, error) { +func (n *nodeImpl) startRPC() ([]net.Listener, error) { env, err := n.ConfigureRPC() if err != nil { return nil, err @@ -956,7 +907,7 @@ func (n *Node) startRPC() ([]net.Listener, error) { // startPrometheusServer starts a Prometheus HTTP server, listening for metrics // collectors on addr. -func (n *Node) startPrometheusServer(addr string) *http.Server { +func (n *nodeImpl) startPrometheusServer(addr string) *http.Server { srv := &http.Server{ Addr: addr, Handler: promhttp.InstrumentMetricHandler( @@ -976,90 +927,90 @@ func (n *Node) startPrometheusServer(addr string) *http.Server { } // Switch returns the Node's Switch. -func (n *Node) Switch() *p2p.Switch { +func (n *nodeImpl) Switch() *p2p.Switch { return n.sw } // BlockStore returns the Node's BlockStore. -func (n *Node) BlockStore() *store.BlockStore { +func (n *nodeImpl) BlockStore() *store.BlockStore { return n.blockStore } // ConsensusState returns the Node's ConsensusState. -func (n *Node) ConsensusState() *cs.State { +func (n *nodeImpl) ConsensusState() *cs.State { return n.consensusState } // ConsensusReactor returns the Node's ConsensusReactor. -func (n *Node) ConsensusReactor() *cs.Reactor { +func (n *nodeImpl) ConsensusReactor() *cs.Reactor { return n.consensusReactor } // MempoolReactor returns the Node's mempool reactor. -func (n *Node) MempoolReactor() service.Service { +func (n *nodeImpl) MempoolReactor() service.Service { return n.mempoolReactor } // Mempool returns the Node's mempool. -func (n *Node) Mempool() mempool.Mempool { +func (n *nodeImpl) Mempool() mempool.Mempool { return n.mempool } // PEXReactor returns the Node's PEXReactor. It returns nil if PEX is disabled. -func (n *Node) PEXReactor() *pex.Reactor { +func (n *nodeImpl) PEXReactor() *pex.Reactor { return n.pexReactor } // EvidencePool returns the Node's EvidencePool. -func (n *Node) EvidencePool() *evidence.Pool { +func (n *nodeImpl) EvidencePool() *evidence.Pool { return n.evidencePool } // EventBus returns the Node's EventBus. -func (n *Node) EventBus() *types.EventBus { +func (n *nodeImpl) EventBus() *types.EventBus { return n.eventBus } // PrivValidator returns the Node's PrivValidator. // XXX: for convenience only! -func (n *Node) PrivValidator() types.PrivValidator { +func (n *nodeImpl) PrivValidator() types.PrivValidator { return n.privValidator } // GenesisDoc returns the Node's GenesisDoc. -func (n *Node) GenesisDoc() *types.GenesisDoc { +func (n *nodeImpl) GenesisDoc() *types.GenesisDoc { return n.genesisDoc } // ProxyApp returns the Node's AppConns, representing its connections to the ABCI application. -func (n *Node) ProxyApp() proxy.AppConns { +func (n *nodeImpl) ProxyApp() proxy.AppConns { return n.proxyApp } // Config returns the Node's config. -func (n *Node) Config() *cfg.Config { +func (n *nodeImpl) Config() *cfg.Config { return n.config } // EventSinks returns the Node's event indexing sinks. -func (n *Node) EventSinks() []indexer.EventSink { +func (n *nodeImpl) EventSinks() []indexer.EventSink { return n.eventSinks } //------------------------------------------------------------------------------ -func (n *Node) Listeners() []string { +func (n *nodeImpl) Listeners() []string { return []string{ fmt.Sprintf("Listener(@%v)", n.config.P2P.ExternalAddress), } } -func (n *Node) IsListening() bool { +func (n *nodeImpl) IsListening() bool { return n.isListening } // NodeInfo returns the Node's Info from the Switch. -func (n *Node) NodeInfo() p2p.NodeInfo { +func (n *nodeImpl) NodeInfo() p2p.NodeInfo { return n.nodeInfo } @@ -1119,44 +1070,25 @@ func startStateSync(ssR *statesync.Reactor, bcR fastSyncReactor, conR *cs.Reacto return nil } -// DBContext specifies config information for loading a new DB. -type DBContext struct { - ID string - Config *cfg.Config -} - -// DBProvider takes a DBContext and returns an instantiated DB. -type DBProvider func(*DBContext) (dbm.DB, error) - -// DefaultDBProvider returns a database using the DBBackend and DBDir -// specified in the ctx.Config. -func DefaultDBProvider(ctx *DBContext) (dbm.DB, error) { - dbType := dbm.BackendType(ctx.Config.DBBackend) - return dbm.NewDB(ctx.ID, dbType, ctx.Config.DBDir()) -} - -// GenesisDocProvider returns a GenesisDoc. +// genesisDocProvider returns a GenesisDoc. // It allows the GenesisDoc to be pulled from sources other than the // filesystem, for instance from a distributed key-value store cluster. -type GenesisDocProvider func() (*types.GenesisDoc, error) +type genesisDocProvider func() (*types.GenesisDoc, error) -// DefaultGenesisDocProviderFunc returns a GenesisDocProvider that loads +// defaultGenesisDocProviderFunc returns a GenesisDocProvider that loads // the GenesisDoc from the config.GenesisFile() on the filesystem. -func DefaultGenesisDocProviderFunc(config *cfg.Config) GenesisDocProvider { +func defaultGenesisDocProviderFunc(config *cfg.Config) genesisDocProvider { return func() (*types.GenesisDoc, error) { return types.GenesisDocFromFile(config.GenesisFile()) } } -// Provider takes a config and a logger and returns a ready to go Node. -type Provider func(*cfg.Config, log.Logger) (*Node, error) - -// MetricsProvider returns a consensus, p2p and mempool Metrics. -type MetricsProvider func(chainID string) (*cs.Metrics, *p2p.Metrics, *mempool.Metrics, *sm.Metrics) +// metricsProvider returns a consensus, p2p and mempool Metrics. +type metricsProvider func(chainID string) (*cs.Metrics, *p2p.Metrics, *mempool.Metrics, *sm.Metrics) -// DefaultMetricsProvider returns Metrics build using Prometheus client library +// defaultMetricsProvider returns Metrics build using Prometheus client library // if Prometheus is enabled. Otherwise, it returns no-op Metrics. -func DefaultMetricsProvider(config *cfg.InstrumentationConfig) MetricsProvider { +func defaultMetricsProvider(config *cfg.InstrumentationConfig) metricsProvider { return func(chainID string) (*cs.Metrics, *p2p.Metrics, *mempool.Metrics, *sm.Metrics) { if config.Prometheus { return cs.PrometheusMetrics(config.Namespace, "chain_id", chainID), @@ -1174,12 +1106,12 @@ var ( genesisDocKey = []byte("genesisDoc") ) -// LoadStateFromDBOrGenesisDocProvider attempts to load the state from the +// loadStateFromDBOrGenesisDocProvider attempts to load the state from the // database, or creates one using the given genesisDocProvider. On success this also // returns the genesis doc loaded through the given provider. -func LoadStateFromDBOrGenesisDocProvider( +func loadStateFromDBOrGenesisDocProvider( stateDB dbm.DB, - genesisDocProvider GenesisDocProvider, + genesisDocProvider genesisDocProvider, ) (sm.State, *types.GenesisDoc, error) { // Get genesis doc genDoc, err := loadGenesisDoc(stateDB) diff --git a/node/node_test.go b/node/node_test.go index 14d914352..069cdc1ab 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -27,7 +27,6 @@ import ( "github.com/tendermint/tendermint/mempool" mempoolv0 "github.com/tendermint/tendermint/mempool/v0" "github.com/tendermint/tendermint/p2p" - p2pmock "github.com/tendermint/tendermint/p2p/mock" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" @@ -42,10 +41,12 @@ func TestNodeStartStop(t *testing.T) { defer os.RemoveAll(config.RootDir) // create & start node - n, err := DefaultNewNode(config, log.TestingLogger()) - require.NoError(t, err) - err = n.Start() + ns, err := newDefaultNode(config, log.TestingLogger()) require.NoError(t, err) + require.NoError(t, ns.Start()) + + n, ok := ns.(*nodeImpl) + require.True(t, ok) t.Logf("Started node %v", n.sw.NodeInfo()) @@ -80,18 +81,26 @@ func TestNodeStartStop(t *testing.T) { } } +func getTestNode(t *testing.T, conf *cfg.Config, logger log.Logger) *nodeImpl { + t.Helper() + ns, err := newDefaultNode(conf, logger) + require.NoError(t, err) + + n, ok := ns.(*nodeImpl) + require.True(t, ok) + return n +} + func TestNodeDelayedStart(t *testing.T) { config := cfg.ResetTestRoot("node_delayed_start_test") defer os.RemoveAll(config.RootDir) now := tmtime.Now() // create & start node - n, err := DefaultNewNode(config, log.TestingLogger()) + n := getTestNode(t, config, log.TestingLogger()) n.GenesisDoc().GenesisTime = now.Add(2 * time.Second) - require.NoError(t, err) - err = n.Start() - require.NoError(t, err) + require.NoError(t, n.Start()) defer n.Stop() //nolint:errcheck // ignore for tests startTime := tmtime.Now() @@ -102,9 +111,8 @@ func TestNodeSetAppVersion(t *testing.T) { config := cfg.ResetTestRoot("node_app_version_test") defer os.RemoveAll(config.RootDir) - // create & start node - n, err := DefaultNewNode(config, log.TestingLogger()) - require.NoError(t, err) + // create node + n := getTestNode(t, config, log.TestingLogger()) // default config uses the kvstore app var appVersion uint64 = kvstore.ProtocolVersion @@ -146,8 +154,7 @@ func TestNodeSetPrivValTCP(t *testing.T) { }() defer signerServer.Stop() //nolint:errcheck // ignore for tests - n, err := DefaultNewNode(config, log.TestingLogger()) - require.NoError(t, err) + n := getTestNode(t, config, log.TestingLogger()) assert.IsType(t, &privval.RetrySignerClient{}, n.PrivValidator()) } @@ -159,7 +166,7 @@ func TestPrivValidatorListenAddrNoProtocol(t *testing.T) { defer os.RemoveAll(config.RootDir) config.PrivValidator.ListenAddr = addrNoPrefix - _, err := DefaultNewNode(config, log.TestingLogger()) + _, err := newDefaultNode(config, log.TestingLogger()) assert.Error(t, err) } @@ -189,9 +196,7 @@ func TestNodeSetPrivValIPC(t *testing.T) { require.NoError(t, err) }() defer pvsc.Stop() //nolint:errcheck // ignore for tests - - n, err := DefaultNewNode(config, log.TestingLogger()) - require.NoError(t, err) + n := getTestNode(t, config, log.TestingLogger()) assert.IsType(t, &privval.RetrySignerClient{}, n.PrivValidator()) } @@ -470,43 +475,6 @@ func TestMaxProposalBlockSize(t *testing.T) { } -func TestNodeNewNodeCustomReactors(t *testing.T) { - config := cfg.ResetTestRoot("node_new_node_custom_reactors_test") - defer os.RemoveAll(config.RootDir) - - cr := p2pmock.NewReactor() - customBlockchainReactor := p2pmock.NewReactor() - - nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) - require.NoError(t, err) - pval, err := privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) - require.NoError(t, err) - - appClient, closer := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) - t.Cleanup(func() { closer.Close() }) - n, err := NewNode(config, - pval, - nodeKey, - appClient, - DefaultGenesisDocProviderFunc(config), - DefaultDBProvider, - DefaultMetricsProvider(config.Instrumentation), - log.TestingLogger(), - CustomReactors(map[string]p2p.Reactor{"FOO": cr, "BLOCKCHAIN": customBlockchainReactor}), - ) - require.NoError(t, err) - - err = n.Start() - require.NoError(t, err) - defer n.Stop() //nolint:errcheck // ignore for tests - - assert.True(t, cr.IsRunning()) - assert.Equal(t, cr, n.Switch().Reactor("FOO")) - - assert.True(t, customBlockchainReactor.IsRunning()) - assert.Equal(t, customBlockchainReactor, n.Switch().Reactor("BLOCKCHAIN")) -} - func TestNodeNewSeedNode(t *testing.T) { config := cfg.ResetTestRoot("node_new_node_custom_reactors_test") config.Mode = cfg.ModeSeed @@ -515,13 +483,15 @@ func TestNodeNewSeedNode(t *testing.T) { nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) require.NoError(t, err) - n, err := NewSeedNode(config, - DefaultDBProvider, + ns, err := makeSeedNode(config, + cfg.DefaultDBProvider, nodeKey, - DefaultGenesisDocProviderFunc(config), + defaultGenesisDocProviderFunc(config), log.TestingLogger(), ) require.NoError(t, err) + n, ok := ns.(*nodeImpl) + require.True(t, ok) err = n.Start() require.NoError(t, err) @@ -533,58 +503,51 @@ func TestNodeSetEventSink(t *testing.T) { config := cfg.ResetTestRoot("node_app_version_test") defer os.RemoveAll(config.RootDir) - // create & start node - n, err := DefaultNewNode(config, log.TestingLogger()) - require.NoError(t, err) + n := getTestNode(t, config, log.TestingLogger()) assert.Equal(t, 1, len(n.eventSinks)) assert.Equal(t, indexer.KV, n.eventSinks[0].Type()) config.TxIndex.Indexer = []string{"null"} - n, err = DefaultNewNode(config, log.TestingLogger()) - require.NoError(t, err) + n = getTestNode(t, config, log.TestingLogger()) assert.Equal(t, 1, len(n.eventSinks)) assert.Equal(t, indexer.NULL, n.eventSinks[0].Type()) config.TxIndex.Indexer = []string{"null", "kv"} - n, err = DefaultNewNode(config, log.TestingLogger()) - require.NoError(t, err) + n = getTestNode(t, config, log.TestingLogger()) assert.Equal(t, 1, len(n.eventSinks)) assert.Equal(t, indexer.NULL, n.eventSinks[0].Type()) config.TxIndex.Indexer = []string{"kvv"} - n, err = DefaultNewNode(config, log.TestingLogger()) - assert.Nil(t, n) + ns, err := newDefaultNode(config, log.TestingLogger()) + assert.Nil(t, ns) assert.Equal(t, errors.New("unsupported event sink type"), err) config.TxIndex.Indexer = []string{} - n, err = DefaultNewNode(config, log.TestingLogger()) - require.NoError(t, err) + n = getTestNode(t, config, log.TestingLogger()) assert.Equal(t, 1, len(n.eventSinks)) assert.Equal(t, indexer.NULL, n.eventSinks[0].Type()) config.TxIndex.Indexer = []string{"psql"} - n, err = DefaultNewNode(config, log.TestingLogger()) - assert.Nil(t, n) + ns, err = newDefaultNode(config, log.TestingLogger()) + assert.Nil(t, ns) assert.Equal(t, errors.New("the psql connection settings cannot be empty"), err) var psqlConn = "test" config.TxIndex.Indexer = []string{"psql"} config.TxIndex.PsqlConn = psqlConn - n, err = DefaultNewNode(config, log.TestingLogger()) - require.NoError(t, err) + n = getTestNode(t, config, log.TestingLogger()) assert.Equal(t, 1, len(n.eventSinks)) assert.Equal(t, indexer.PSQL, n.eventSinks[0].Type()) n.OnStop() config.TxIndex.Indexer = []string{"psql", "kv"} config.TxIndex.PsqlConn = psqlConn - n, err = DefaultNewNode(config, log.TestingLogger()) - require.NoError(t, err) + n = getTestNode(t, config, log.TestingLogger()) assert.Equal(t, 2, len(n.eventSinks)) // we use map to filter the duplicated sinks, so it's not guarantee the order when append sinks. if n.eventSinks[0].Type() == indexer.KV { @@ -597,8 +560,7 @@ func TestNodeSetEventSink(t *testing.T) { config.TxIndex.Indexer = []string{"kv", "psql"} config.TxIndex.PsqlConn = psqlConn - n, err = DefaultNewNode(config, log.TestingLogger()) - require.NoError(t, err) + n = getTestNode(t, config, log.TestingLogger()) assert.Equal(t, 2, len(n.eventSinks)) if n.eventSinks[0].Type() == indexer.KV { assert.Equal(t, indexer.PSQL, n.eventSinks[1].Type()) @@ -611,13 +573,13 @@ func TestNodeSetEventSink(t *testing.T) { var e = errors.New("found duplicated sinks, please check the tx-index section in the config.toml") config.TxIndex.Indexer = []string{"psql", "kv", "Kv"} config.TxIndex.PsqlConn = psqlConn - _, err = DefaultNewNode(config, log.TestingLogger()) + _, err = newDefaultNode(config, log.TestingLogger()) require.Error(t, err) assert.Equal(t, e, err) config.TxIndex.Indexer = []string{"Psql", "kV", "kv", "pSql"} config.TxIndex.PsqlConn = psqlConn - _, err = DefaultNewNode(config, log.TestingLogger()) + _, err = newDefaultNode(config, log.TestingLogger()) require.Error(t, err) assert.Equal(t, e, err) } diff --git a/node/public.go b/node/public.go new file mode 100644 index 000000000..80f5fc39d --- /dev/null +++ b/node/public.go @@ -0,0 +1,67 @@ +// Package node provides a high level wrapper around tendermint services. +package node + +import ( + "fmt" + + "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/libs/service" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/privval" + "github.com/tendermint/tendermint/proxy" + "github.com/tendermint/tendermint/types" +) + +// NewDefault constructs a tendermint node service for use in go +// process that host their own process-local tendermint node. This is +// equivalent to running tendermint in it's own process communicating +// to an external ABCI application. +func NewDefault(conf *config.Config, logger log.Logger) (service.Service, error) { + return newDefaultNode(conf, logger) +} + +// New constructs a tendermint node. The ClientCreator makes it +// possible to construct an ABCI application that runs in the same +// process as the tendermint node. The final option is a pointer to a +// Genesis document: if the value is nil, the genesis document is read +// from the file specified in the config, and otherwise the node uses +// value of the final argument. +func New(conf *config.Config, + logger log.Logger, + cf proxy.ClientCreator, + gen *types.GenesisDoc, +) (service.Service, error) { + nodeKey, err := p2p.LoadOrGenNodeKey(conf.NodeKeyFile()) + if err != nil { + return nil, fmt.Errorf("failed to load or gen node key %s: %w", conf.NodeKeyFile(), err) + } + + var genProvider genesisDocProvider + switch gen { + case nil: + genProvider = defaultGenesisDocProviderFunc(conf) + default: + genProvider = func() (*types.GenesisDoc, error) { return gen, nil } + } + + switch conf.Mode { + case config.ModeFull, config.ModeValidator: + pval, err := privval.LoadOrGenFilePV(conf.PrivValidatorKeyFile(), conf.PrivValidatorStateFile()) + if err != nil { + return nil, err + } + + return makeNode(conf, + pval, + nodeKey, + cf, + genProvider, + config.DefaultDBProvider, + logger) + case config.ModeSeed: + return makeSeedNode(conf, config.DefaultDBProvider, nodeKey, genProvider, logger) + default: + return nil, fmt.Errorf("%q is not a valid mode", conf.Mode) + } +} diff --git a/node/setup.go b/node/setup.go index 51a35f7db..d56502d2f 100644 --- a/node/setup.go +++ b/node/setup.go @@ -41,15 +41,15 @@ import ( "github.com/tendermint/tendermint/version" ) -func initDBs(config *cfg.Config, dbProvider DBProvider) (blockStore *store.BlockStore, stateDB dbm.DB, err error) { +func initDBs(config *cfg.Config, dbProvider cfg.DBProvider) (blockStore *store.BlockStore, stateDB dbm.DB, err error) { var blockStoreDB dbm.DB - blockStoreDB, err = dbProvider(&DBContext{"blockstore", config}) + blockStoreDB, err = dbProvider(&cfg.DBContext{ID: "blockstore", Config: config}) if err != nil { return } blockStore = store.NewBlockStore(blockStoreDB) - stateDB, err = dbProvider(&DBContext{"state", config}) + stateDB, err = dbProvider(&cfg.DBContext{ID: "state", Config: config}) return } @@ -73,7 +73,7 @@ func createAndStartEventBus(logger log.Logger) (*types.EventBus, error) { func createAndStartIndexerService( config *cfg.Config, - dbProvider DBProvider, + dbProvider cfg.DBProvider, eventBus *types.EventBus, logger log.Logger, chainID string, @@ -99,7 +99,7 @@ loop: eventSinks = []indexer.EventSink{null.NewEventSink()} break loop case string(indexer.KV): - store, err := dbProvider(&DBContext{"tx_index", config}) + store, err := dbProvider(&cfg.DBContext{ID: "tx_index", Config: config}) if err != nil { return nil, nil, err } @@ -278,14 +278,14 @@ func createMempoolReactor( func createEvidenceReactor( config *cfg.Config, - dbProvider DBProvider, + dbProvider cfg.DBProvider, stateDB dbm.DB, blockStore *store.BlockStore, peerManager *p2p.PeerManager, router *p2p.Router, logger log.Logger, ) (*p2p.ReactorShim, *evidence.Reactor, *evidence.Pool, error) { - evidenceDB, err := dbProvider(&DBContext{"evidence", config}) + evidenceDB, err := dbProvider(&cfg.DBContext{ID: "evidence", Config: config}) if err != nil { return nil, nil, nil, err } @@ -450,7 +450,7 @@ func createTransport(logger log.Logger, config *cfg.Config) *p2p.MConnTransport func createPeerManager( config *cfg.Config, - dbProvider DBProvider, + dbProvider cfg.DBProvider, p2pLogger log.Logger, nodeID p2p.NodeID, ) (*p2p.PeerManager, error) { @@ -513,7 +513,7 @@ func createPeerManager( peers = append(peers, address) } - peerDB, err := dbProvider(&DBContext{"peerstore", config}) + peerDB, err := dbProvider(&cfg.DBContext{ID: "peerstore", Config: config}) if err != nil { return nil, err } diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index f681414e8..de7b0611e 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -14,8 +14,6 @@ import ( tmnet "github.com/tendermint/tendermint/libs/net" "github.com/tendermint/tendermint/libs/service" nm "github.com/tendermint/tendermint/node" - "github.com/tendermint/tendermint/p2p" - "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" ctypes "github.com/tendermint/tendermint/rpc/core/types" core_grpc "github.com/tendermint/tendermint/rpc/grpc" @@ -118,22 +116,8 @@ func StartTendermint(ctx context.Context, logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) logger = log.NewFilter(logger, log.AllowError()) } - pvKeyFile := conf.PrivValidatorKeyFile() - pvKeyStateFile := conf.PrivValidatorStateFile() - pv, err := privval.LoadOrGenFilePV(pvKeyFile, pvKeyStateFile) - if err != nil { - return nil, func(_ context.Context) error { return nil }, err - } papp := proxy.NewLocalClientCreator(app) - nodeKey, err := p2p.LoadOrGenNodeKey(conf.NodeKeyFile()) - if err != nil { - return nil, func(_ context.Context) error { return nil }, err - } - node, err := nm.NewNode(conf, pv, nodeKey, papp, - nm.DefaultGenesisDocProviderFunc(conf), - nm.DefaultDBProvider, - nm.DefaultMetricsProvider(conf.Instrumentation), - logger) + node, err := nm.New(conf, logger, papp, nil) if err != nil { return nil, func(_ context.Context) error { return nil }, err } diff --git a/test/e2e/app/main.go b/test/e2e/app/main.go index b1b96deeb..a4a0c7d8b 100644 --- a/test/e2e/app/main.go +++ b/test/e2e/app/main.go @@ -124,23 +124,15 @@ func startNode(cfg *Config) error { return err } - tmcfg, nodeLogger, nodeKey, err := setupNode() + tmcfg, nodeLogger, err := setupNode() if err != nil { return fmt.Errorf("failed to setup config: %w", err) } - pval, err := privval.LoadOrGenFilePV(tmcfg.PrivValidatorKeyFile(), tmcfg.PrivValidatorStateFile()) - if err != nil { - return err - } - n, err := node.NewNode(tmcfg, - pval, - *nodeKey, - proxy.NewLocalClientCreator(app), - node.DefaultGenesisDocProviderFunc(tmcfg), - node.DefaultDBProvider, - node.DefaultMetricsProvider(tmcfg.Instrumentation), + n, err := node.New(tmcfg, nodeLogger, + proxy.NewLocalClientCreator(app), + nil, ) if err != nil { return err @@ -149,18 +141,14 @@ func startNode(cfg *Config) error { } func startSeedNode(cfg *Config) error { - tmcfg, nodeLogger, nodeKey, err := setupNode() + tmcfg, nodeLogger, err := setupNode() if err != nil { return fmt.Errorf("failed to setup config: %w", err) } - n, err := node.NewSeedNode( - tmcfg, - node.DefaultDBProvider, - *nodeKey, - node.DefaultGenesisDocProviderFunc(tmcfg), - nodeLogger, - ) + tmcfg.Mode = config.ModeSeed + + n, err := node.New(tmcfg, nodeLogger, nil, nil) if err != nil { return err } @@ -168,13 +156,13 @@ func startSeedNode(cfg *Config) error { } func startLightNode(cfg *Config) error { - tmcfg, nodeLogger, _, err := setupNode() + tmcfg, nodeLogger, err := setupNode() if err != nil { return err } - dbContext := &node.DBContext{ID: "light", Config: tmcfg} - lightDB, err := node.DefaultDBProvider(dbContext) + dbContext := &config.DBContext{ID: "light", Config: tmcfg} + lightDB, err := config.DefaultDBProvider(dbContext) if err != nil { return err } @@ -272,31 +260,31 @@ func startSigner(cfg *Config) error { return nil } -func setupNode() (*config.Config, log.Logger, *p2p.NodeKey, error) { +func setupNode() (*config.Config, log.Logger, error) { var tmcfg *config.Config home := os.Getenv("TMHOME") if home == "" { - return nil, nil, nil, errors.New("TMHOME not set") + return nil, nil, errors.New("TMHOME not set") } viper.AddConfigPath(filepath.Join(home, "config")) viper.SetConfigName("config") if err := viper.ReadInConfig(); err != nil { - return nil, nil, nil, err + return nil, nil, err } tmcfg = config.DefaultConfig() if err := viper.Unmarshal(tmcfg); err != nil { - return nil, nil, nil, err + return nil, nil, err } tmcfg.SetRoot(home) if err := tmcfg.ValidateBasic(); err != nil { - return nil, nil, nil, fmt.Errorf("error in config file: %w", err) + return nil, nil, fmt.Errorf("error in config file: %w", err) } if tmcfg.LogFormat == config.LogFormatJSON { @@ -305,17 +293,12 @@ func setupNode() (*config.Config, log.Logger, *p2p.NodeKey, error) { nodeLogger, err := tmflags.ParseLogLevel(tmcfg.LogLevel, logger, config.DefaultLogLevel) if err != nil { - return nil, nil, nil, err + return nil, nil, err } nodeLogger = nodeLogger.With("module", "main") - nodeKey, err := p2p.LoadOrGenNodeKey(tmcfg.NodeKeyFile()) - if err != nil { - return nil, nil, nil, fmt.Errorf("failed to load or gen node key %s: %w", tmcfg.NodeKeyFile(), err) - } - - return tmcfg, nodeLogger, &nodeKey, nil + return tmcfg, nodeLogger, nil } // rpcEndpoints takes a list of persistent peers and splits them into a list of rpc endpoints