package commands import ( "bytes" "crypto/sha256" "fmt" "io" "os" "github.com/spf13/cobra" cfg "github.com/tendermint/tendermint/config" tmos "github.com/tendermint/tendermint/libs/os" ) var ( genesisHash []byte ) // AddNodeFlags exposes some common configuration options on the command-line // These are exposed for convenience of commands embedding a tendermint node func AddNodeFlags(cmd *cobra.Command) { // bind flags cmd.Flags().String("moniker", config.Moniker, "node name") // mode flags cmd.Flags().String("mode", config.Mode, "node mode (full | validator | seed)") // priv val flags cmd.Flags().String( "priv-validator-laddr", config.PrivValidator.ListenAddr, "socket address to listen on for connections from external priv-validator process") // node flags cmd.Flags().Bool("fast-sync", config.FastSyncMode, "fast blockchain syncing") cmd.Flags().BytesHexVar( &genesisHash, "genesis-hash", []byte{}, "optional SHA-256 hash of the genesis file") cmd.Flags().Int64("consensus.double-sign-check-height", config.Consensus.DoubleSignCheckHeight, "how many blocks to look back to check existence of the node's "+ "consensus votes before joining consensus") // abci flags cmd.Flags().String( "proxy-app", config.ProxyApp, "proxy app address, or one of: 'kvstore',"+ " 'persistent_kvstore',"+ " 'counter',"+ " 'counter_serial' or 'noop' for local testing.") cmd.Flags().String("abci", config.ABCI, "specify abci transport (socket | grpc)") // rpc flags cmd.Flags().String("rpc.laddr", config.RPC.ListenAddress, "RPC listen address. Port required") cmd.Flags().String( "rpc.grpc-laddr", config.RPC.GRPCListenAddress, "GRPC listen address (BroadcastTx only). Port required") cmd.Flags().Bool("rpc.unsafe", config.RPC.Unsafe, "enabled unsafe rpc methods") cmd.Flags().String("rpc.pprof-laddr", config.RPC.PprofListenAddress, "pprof listen address (https://golang.org/pkg/net/http/pprof)") // p2p flags cmd.Flags().String( "p2p.laddr", config.P2P.ListenAddress, "node listen address. (0.0.0.0:0 means any interface, any port)") cmd.Flags().String("p2p.seeds", config.P2P.Seeds, "comma-delimited ID@host:port seed nodes") cmd.Flags().String("p2p.persistent-peers", config.P2P.PersistentPeers, "comma-delimited ID@host:port persistent peers") cmd.Flags().String("p2p.unconditional-peer-ids", config.P2P.UnconditionalPeerIDs, "comma-delimited IDs of unconditional peers") cmd.Flags().Bool("p2p.upnp", config.P2P.UPNP, "enable/disable UPNP port forwarding") cmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "enable/disable Peer-Exchange") cmd.Flags().String("p2p.private-peer-ids", config.P2P.PrivatePeerIDs, "comma-delimited private peer IDs") // consensus flags cmd.Flags().Bool( "consensus.create-empty-blocks", config.Consensus.CreateEmptyBlocks, "set this to false to only produce blocks when there are txs or when the AppHash changes") cmd.Flags().String( "consensus.create-empty-blocks-interval", config.Consensus.CreateEmptyBlocksInterval.String(), "the possible interval between empty blocks") // db flags cmd.Flags().String( "db-backend", config.DBBackend, "database backend: goleveldb | cleveldb | boltdb | rocksdb | badgerdb") cmd.Flags().String( "db-dir", config.DBPath, "database directory") } // 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 cfg.ServiceProvider) *cobra.Command { cmd := &cobra.Command{ Use: "start", Aliases: []string{"node", "run"}, Short: "Run the tendermint node", RunE: func(cmd *cobra.Command, args []string) error { if err := checkGenesisHash(config); err != nil { return err } n, err := nodeProvider(config, logger) if err != nil { return fmt.Errorf("failed to create node: %w", err) } if err := n.Start(); err != nil { return fmt.Errorf("failed to start node: %w", err) } logger.Info("started node", "node", n.String()) // Stop upon receiving SIGTERM or CTRL-C. tmos.TrapSignal(logger, func() { if n.IsRunning() { if err := n.Stop(); err != nil { logger.Error("unable to stop the node", "error", err) } } }) // Run forever. select {} }, } AddNodeFlags(cmd) return cmd } func checkGenesisHash(config *cfg.Config) error { if len(genesisHash) == 0 || config.Genesis == "" { return nil } // Calculate SHA-256 hash of the genesis file. f, err := os.Open(config.GenesisFile()) if err != nil { return fmt.Errorf("can't open genesis file: %w", err) } defer f.Close() h := sha256.New() if _, err := io.Copy(h, f); err != nil { return fmt.Errorf("error when hashing genesis file: %w", err) } actualHash := h.Sum(nil) // Compare with the flag. if !bytes.Equal(genesisHash, actualHash) { return fmt.Errorf( "--genesis_hash=%X does not match %s hash: %X", genesisHash, config.GenesisFile(), actualHash) } return nil }