diff --git a/cmd/harmony/bls.go b/cmd/harmony/bls.go index 4d5e79f9d..518a4b3c4 100644 --- a/cmd/harmony/bls.go +++ b/cmd/harmony/bls.go @@ -1,8 +1,13 @@ package main import ( + "fmt" + "os" "sync" + nodeconfig "github.com/harmony-one/harmony/internal/configs/node" + + "github.com/harmony-one/harmony/internal/blsgen" "github.com/harmony-one/harmony/multibls" ) @@ -11,42 +16,97 @@ var ( onceLoadBLSKey sync.Once ) -//// TODO: refactor this -//func loadBLSKeys() (multibls.PrivateKeys, error) { -// config, err := parseBLSLoadingConfig() -// if err != nil { -// return nil, err -// } -// keys, err := blsgen.LoadKeys(config) -// if err != nil { -// return nil, err -// } -// if len(keys) == 0 { -// return nil, fmt.Errorf("0 bls keys loaded") -// } -// if len(keys) >= *maxBLSKeysPerNode { -// return nil, fmt.Errorf("bls keys exceed maximum count %v", *maxBLSKeysPerNode) -// } -// return keys, err -//} -// -//func parseBLSLoadingConfig() (blsgen.Config, error) { -// var ( -// config blsgen.Config -// err error -// ) -// if len(*blsKeyFile) != 0 { -// config.MultiBlsKeys = strings.Split(*blsKeyFile, ",") -// } -// config.BlsDir = blsFolder -// -// config, err = parseBLSPass(config, *blsPass) -// if err != nil { -// return blsgen.Config{}, err -// } -// config, err = parseAwsConfigSrc(config, *awsConfigSource) -// if err != nil { -// return blsgen.Config{}, err -// } -// return config, nil -//} +// setupConsensusKeys load bls keys and set the keys to nodeConfig. Return the loaded public keys. +func setupConsensusKeys(hc harmonyConfig, config *nodeconfig.ConfigType) multibls.PublicKeys { + onceLoadBLSKey.Do(func() { + var err error + multiBLSPriKey, err = loadBLSKeys(hc.BLSKeys) + if err != nil { + fmt.Fprintf(os.Stderr, "ERROR when loading bls key: %v\n", err) + os.Exit(100) + } + fmt.Printf("Successfully loaded %v BLS keys\n", len(multiBLSPriKey)) + }) + config.ConsensusPriKey = multiBLSPriKey + return multiBLSPriKey.GetPublicKeys() +} + +func loadBLSKeys(raw blsConfig) (multibls.PrivateKeys, error) { + config, err := parseBLSLoadingConfig(raw) + if err != nil { + return nil, err + } + keys, err := blsgen.LoadKeys(config) + if err != nil { + return nil, err + } + if len(keys) == 0 { + return nil, fmt.Errorf("0 bls keys loaded") + } + if len(keys) >= raw.MaxKeys { + return nil, fmt.Errorf("bls keys exceed maximum count %v", raw.MaxKeys) + } + return keys, err +} + +func parseBLSLoadingConfig(raw blsConfig) (blsgen.Config, error) { + var ( + config blsgen.Config + err error + ) + if len(raw.KeyFiles) != 0 { + config.MultiBlsKeys = raw.KeyFiles + } + config.BlsDir = &raw.KeyDir + + config, err = parseBLSPassConfig(config, raw) + if err != nil { + return blsgen.Config{}, err + } + config, err = parseBLSKmsConfig(config, raw) + if err != nil { + return blsgen.Config{}, err + } + return config, nil +} + +func parseBLSPassConfig(cfg blsgen.Config, raw blsConfig) (blsgen.Config, error) { + if !raw.PassEnabled { + cfg.PassSrcType = blsgen.PassSrcNil + return blsgen.Config{}, nil + } + switch raw.PassSrcType { + case "auto": + cfg.PassSrcType = blsgen.PassSrcAuto + case "file": + cfg.PassSrcType = blsgen.PassSrcFile + case "prompt": + cfg.PassSrcType = blsgen.PassSrcPrompt + default: + return blsgen.Config{}, fmt.Errorf("unknown pass source type [%v]", raw.PassSrcType) + } + cfg.PassFile = &raw.PassFile + cfg.PersistPassphrase = raw.SavePassphrase + + return cfg, nil +} + +func parseBLSKmsConfig(cfg blsgen.Config, raw blsConfig) (blsgen.Config, error) { + if !raw.KMSEnabled { + cfg.AwsCfgSrcType = blsgen.AwsCfgSrcNil + return cfg, nil + } + switch raw.KMSConfigSrcType { + case "shared": + cfg.AwsCfgSrcType = blsgen.AwsCfgSrcShared + case "file": + cfg.AwsCfgSrcType = blsgen.AwsCfgSrcFile + case "prompt": + cfg.AwsCfgSrcType = blsgen.AwsCfgSrcPrompt + default: + return blsgen.Config{}, fmt.Errorf("unknown aws config source type [%v]", raw.KMSConfigSrcType) + } + cfg.AwsConfigFile = &raw.KMSConfigFile + + return cfg, nil +} diff --git a/cmd/harmony/config.go b/cmd/harmony/config.go index 324d1cbf9..12fec4c75 100644 --- a/cmd/harmony/config.go +++ b/cmd/harmony/config.go @@ -3,8 +3,13 @@ package main import ( "fmt" "io/ioutil" + "os" "strings" + "github.com/harmony-one/harmony/internal/cli" + + "github.com/spf13/cobra" + nodeconfig "github.com/harmony-one/harmony/internal/configs/node" "github.com/pelletier/go-toml" ) @@ -17,6 +22,7 @@ type harmonyConfig struct { Network networkConfig P2P p2pConfig RPC rpcConfig + WS wsConfig Consensus consensusConfig BLSKeys blsConfig TxPool txPoolConfig @@ -24,6 +30,7 @@ type harmonyConfig struct { Log logConfig Devnet *devnetConfig `toml:",omitempty"` Revert *revertConfig `toml:",omitempty"` + Legacy *legacyConfig `toml:",omitempty"` } type networkConfig struct { @@ -36,6 +43,7 @@ type networkConfig struct { } type p2pConfig struct { + IP string Port int KeyFile string } @@ -51,6 +59,7 @@ type generalConfig struct { type consensusConfig struct { DelayCommit string BlockTime string + MinPeers int } type blsConfig struct { @@ -97,135 +106,29 @@ type rpcConfig struct { Port int } +type wsConfig struct { + Enabled bool + IP string + Port int +} + type devnetConfig struct { NumShards int ShardSize int HmyNodeSize int } -// TODO: make this revert to a seperate command +// TODO: make this revert to a separate command type revertConfig struct { RevertBeacon bool RevertTo int RevertBefore int } -var defaultConfig = harmonyConfig{ - Version: tomlConfigVersion, - General: generalConfig{ - NodeType: "validator", - IsStaking: true, - ShardID: -1, - IsArchival: false, - DataDir: "./", - }, - Network: getDefaultNetworkConfig(nodeconfig.Mainnet), - P2P: p2pConfig{ - Port: nodeconfig.DefaultP2PPort, - KeyFile: "./.hmykey", - }, - RPC: rpcConfig{ - Enabled: false, - IP: "127.0.0.1", - Port: nodeconfig.DefaultRPCPort, - }, - BLSKeys: blsConfig{ - KeyDir: "./hmy/blskeys", - KeyFiles: nil, - MaxKeys: 10, - - PassEnabled: true, - PassSrcType: blsPassTypeAuto, - PassFile: "", - SavePassphrase: false, - KMSEnabled: true, - KMSConfigSrcType: kmsConfigTypeShared, - KMSConfigFile: "", - }, - Consensus: consensusConfig{ - DelayCommit: "0ms", - BlockTime: "8s", - }, - TxPool: txPoolConfig{ - BlacklistFile: "./.hmy/blacklist.txt", - BroadcastInvalidTx: false, - }, - Pprof: pprofConfig{ - Enabled: false, - ListenAddr: "127.0.0.1:6060", - }, - Log: logConfig{ - Folder: "./latest", - FileName: "harmony.log", - RotateSize: 100, - Verbosity: 3, - }, -} - -var defaultDevnetConfig = devnetConfig{ - NumShards: 2, - ShardSize: 10, - HmyNodeSize: 10, -} - -var defaultRevertConfig = revertConfig{ - RevertBeacon: false, - RevertBefore: -1, - RevertTo: -1, +type legacyConfig struct { + WebHookConfig string } -var defaultLogContext = logContext{ - IP: "127.0.0.1", - Port: 9000, -} - -func getDefaultHmyConfigCopy() harmonyConfig { - config := defaultConfig - return config -} - -func getDefaultDevnetConfigCopy() devnetConfig { - config := defaultDevnetConfig - return config -} - -func getDefaultRevertConfigCopy() revertConfig { - config := defaultRevertConfig - return config -} - -func getDefaultLogContextCopy() logContext { - config := defaultLogContext - return config -} - -const ( - nodeTypeValidator = "validator" - nodeTypeExplorer = "explorer" -) - -const ( - blsPassTypeAuto = "auto" - blsPassTypeFile = "file" - blsPassTypePrompt = "prompt" - - kmsConfigTypeShared = "shared" - kmsConfigTypePrompt = "prompt" - kmsConfigTypeFile = "file" - - legacyBLSPassTypeDefault = "default" - legacyBLSPassTypeStdin = "stdin" - legacyBLSPassTypeDynamic = "no-prompt" - legacyBLSPassTypePrompt = "prompt" - legacyBLSPassTypeStatic = "file" - legacyBLSPassTypeNone = "none" - - legacyBLSKmsTypeDefault = "default" - legacyBLSKmsTypePrompt = "prompt" - legacyBLSKmsTypeFile = "file" - legacyBLSKmsTypeNone = "none" -) - // TODO: use specific type wise validation instead of general string types assertion. func validateHarmonyConfig(config harmonyConfig) error { var accepts []string @@ -303,6 +206,28 @@ func parseNetworkType(nt string) nodeconfig.NetworkType { } } +var dumpConfigCmd = &cobra.Command{ + Use: "dumpconfig [config_file]", + Short: "dump the config file for harmony binary configurations", + Long: "dump the config file for harmony binary configurations", + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + config := getDefaultHmyConfigCopy() + nt := getNetworkType(cmd) + config.Network = getDefaultNetworkConfig(nt) + fmt.Println(args[0]) + + if err := writeHarmonyConfigToFile(config, args[0]); err != nil { + fmt.Println(err) + os.Exit(128) + } + }, +} + +func registerDumpConfigFlags() error { + return cli.RegisterFlags(dumpConfigCmd, []cli.Flag{networkTypeFlag}) +} + func loadHarmonyConfig(file string) (harmonyConfig, error) { b, err := ioutil.ReadFile(file) if err != nil { @@ -316,7 +241,7 @@ func loadHarmonyConfig(file string) (harmonyConfig, error) { return config, nil } -func writeHarmonyConfigToFile(config *harmonyConfig, file string) error { +func writeHarmonyConfigToFile(config harmonyConfig, file string) error { b, err := toml.Marshal(config) if err != nil { return err diff --git a/cmd/harmony/config_test.go b/cmd/harmony/config_test.go index 29201a6af..3b7afc059 100644 --- a/cmd/harmony/config_test.go +++ b/cmd/harmony/config_test.go @@ -1,4 +1,5 @@ -//package main +package main + // //import ( // "fmt" diff --git a/cmd/harmony/const.go b/cmd/harmony/const.go index 8dd859624..79156c39a 100644 --- a/cmd/harmony/const.go +++ b/cmd/harmony/const.go @@ -9,3 +9,126 @@ const ( const ( mainnetDnsZone = "t.hmny.io" ) + +var defaultConfig = harmonyConfig{ + Version: tomlConfigVersion, + General: generalConfig{ + NodeType: "validator", + IsStaking: true, + ShardID: -1, + IsArchival: false, + DataDir: "./", + }, + Network: getDefaultNetworkConfig(defNetworkType), + P2P: p2pConfig{ + IP: "127.0.0.1", + Port: nodeconfig.DefaultP2PPort, + KeyFile: "./.hmykey", + }, + RPC: rpcConfig{ + Enabled: true, + IP: "127.0.0.1", + Port: nodeconfig.DefaultRPCPort, + }, + WS: wsConfig{ + Enabled: true, + IP: "127.0.0.1", + Port: nodeconfig.DefaultWSPort, + }, + BLSKeys: blsConfig{ + KeyDir: "./hmy/blskeys", + KeyFiles: nil, + MaxKeys: 10, + + PassEnabled: true, + PassSrcType: blsPassTypeAuto, + PassFile: "", + SavePassphrase: false, + KMSEnabled: true, + KMSConfigSrcType: kmsConfigTypeShared, + KMSConfigFile: "", + }, + Consensus: consensusConfig{ + DelayCommit: "0ms", + BlockTime: "8s", + MinPeers: 32, + }, + TxPool: txPoolConfig{ + BlacklistFile: "./.hmy/blacklist.txt", + BroadcastInvalidTx: false, + }, + Pprof: pprofConfig{ + Enabled: false, + ListenAddr: "127.0.0.1:6060", + }, + Log: logConfig{ + Folder: "./latest", + FileName: "harmony.log", + RotateSize: 100, + Verbosity: 3, + }, +} + +var defaultDevnetConfig = devnetConfig{ + NumShards: 2, + ShardSize: 10, + HmyNodeSize: 10, +} + +var defaultRevertConfig = revertConfig{ + RevertBeacon: false, + RevertBefore: 0, + RevertTo: 0, +} + +var defaultLogContext = logContext{ + IP: "127.0.0.1", + Port: 9000, +} + +func getDefaultHmyConfigCopy() harmonyConfig { + config := defaultConfig + return config +} + +func getDefaultDevnetConfigCopy() devnetConfig { + config := defaultDevnetConfig + return config +} + +func getDefaultRevertConfigCopy() revertConfig { + config := defaultRevertConfig + return config +} + +func getDefaultLogContextCopy() logContext { + config := defaultLogContext + return config +} + +const ( + nodeTypeValidator = "validator" + nodeTypeExplorer = "explorer" +) + +const ( + blsPassTypeAuto = "auto" + blsPassTypeFile = "file" + blsPassTypePrompt = "prompt" + + kmsConfigTypeShared = "shared" + kmsConfigTypePrompt = "prompt" + kmsConfigTypeFile = "file" + + legacyBLSPassTypeDefault = "default" + legacyBLSPassTypeStdin = "stdin" + legacyBLSPassTypeDynamic = "no-prompt" + legacyBLSPassTypePrompt = "prompt" + legacyBLSPassTypeStatic = "file" + legacyBLSPassTypeNone = "none" + + legacyBLSKmsTypeDefault = "default" + legacyBLSKmsTypePrompt = "prompt" + legacyBLSKmsTypeFile = "file" + legacyBLSKmsTypeNone = "none" +) diff --git a/cmd/harmony/flags.go b/cmd/harmony/flags.go index ab10cb6b7..265eb4134 100644 --- a/cmd/harmony/flags.go +++ b/cmd/harmony/flags.go @@ -37,6 +37,7 @@ var ( } p2pFlags = []cli.Flag{ + p2pIPFlag, p2pPortFlag, p2pKeyFileFlag, @@ -51,6 +52,12 @@ var ( legacyPublicRPCFlag, } + wsFlags = []cli.Flag{ + wsEnabledFlag, + wsIPFlag, + wsPortFlag, + } + blsFlags = append(newBLSFlags, legacyBLSFlags...) newBLSFlags = []cli.Flag{ @@ -78,9 +85,11 @@ var ( consensusFlags = []cli.Flag{ consensusDelayCommitFlag, consensusBlockTimeFlag, + consensusMinPeersFlag, legacyDelayCommitFlag, legacyBlockTimeFlag, + legacyConsensusMinPeersFlag, } txPoolFlags = []cli.Flag{ @@ -140,6 +149,7 @@ var ( legacyMiscFlags = []cli.Flag{ legacyPortFlag, legacyIPFlag, + legacyWebHookConfigFlag, } ) @@ -271,7 +281,7 @@ var ( Name: "dns", DefValue: true, Usage: "use dns for syncing", - Deprecated: "set to false only to use self discovery peers for syncing", + Deprecated: "only set to false to use self discovered peers for syncing", } legacyNetworkTypeFlag = cli.StringFlag{ Name: "network_type", @@ -301,7 +311,7 @@ func applyNetworkFlags(cmd *cobra.Command, cfg *harmonyConfig) { } else if cli.IsFlagChanged(cmd, legacyDNSFlag) { val := cli.GetBoolFlagValue(cmd, legacyDNSFlag) if val { - cfg.Network.DNSZone = mainnetDnsZone + cfg.Network.DNSZone = nodeconfig.GetDefaultDNSZone(defNetworkType) } else { cfg.Network.LegacySyncing = true } @@ -316,9 +326,14 @@ func applyNetworkFlags(cmd *cobra.Command, cfg *harmonyConfig) { // p2p flags var ( + p2pIPFlag = cli.StringFlag{ + Name: "p2p.ip", + Usage: "ip to listen for p2p protocols", + DefValue: defaultConfig.P2P.IP, + } p2pPortFlag = cli.IntFlag{ Name: "p2p.port", - Usage: "port to listen for p2p communication", + Usage: "port to listen for p2p protocols", DefValue: defaultConfig.P2P.Port, } p2pKeyFileFlag = cli.StringFlag{ @@ -335,6 +350,10 @@ var ( ) func applyP2PFlags(cmd *cobra.Command, config *harmonyConfig) { + if cli.IsFlagChanged(cmd, p2pIPFlag) { + config.P2P.IP = cli.GetStringFlagValue(cmd, p2pIPFlag) + } + if cli.IsFlagChanged(cmd, p2pPortFlag) { config.P2P.Port = cli.GetIntFlagValue(cmd, p2pPortFlag) } @@ -391,6 +410,37 @@ func applyRPCFlags(cmd *cobra.Command, config *harmonyConfig) { } } +// ws flags +var ( + wsEnabledFlag = cli.BoolFlag{ + Name: "ws", + Usage: "enable websocket endpoint", + DefValue: defaultConfig.WS.Enabled, + } + wsIPFlag = cli.StringFlag{ + Name: "ws.ip", + Usage: "ip endpoint for websocket", + DefValue: defaultConfig.WS.IP, + } + wsPortFlag = cli.IntFlag{ + Name: "ws.port", + Usage: "port for websocket endpoint", + DefValue: defaultConfig.WS.Port, + } +) + +func applyWSFlags(cmd *cobra.Command, config *harmonyConfig) { + if cli.IsFlagChanged(cmd, wsEnabledFlag) { + config.WS.Enabled = cli.GetBoolFlagValue(cmd, wsEnabledFlag) + } + if cli.IsFlagChanged(cmd, wsIPFlag) { + config.WS.IP = cli.GetStringFlagValue(cmd, wsIPFlag) + } + if cli.IsFlagChanged(cmd, wsPortFlag) { + config.WS.Port = cli.GetIntFlagValue(cmd, wsPortFlag) + } +} + // bls flags var ( blsDirFlag = cli.StringFlag{ @@ -616,6 +666,13 @@ var ( DefValue: defaultConfig.Consensus.BlockTime, Hidden: true, } + // TODO: hard code value? + consensusMinPeersFlag = cli.IntFlag{ + Name: "consensus.min-peers", + Usage: "minimal number of peers in shard", + DefValue: defaultConfig.Consensus.MinPeers, + Hidden: true, + } legacyDelayCommitFlag = cli.StringFlag{ Name: "delay_commit", Usage: "how long to delay sending commit messages in consensus, ex: 500ms, 1s", @@ -628,6 +685,12 @@ var ( DefValue: 8, Deprecated: "use --consensus.block-time", } + legacyConsensusMinPeersFlag = cli.IntFlag{ + Name: "min_peers", + Usage: "Minimal number of Peers in shard", + DefValue: defaultConfig.Consensus.MinPeers, + Hidden: true, + } ) func applyConsensusFlags(cmd *cobra.Command, config *harmonyConfig) { @@ -643,6 +706,12 @@ func applyConsensusFlags(cmd *cobra.Command, config *harmonyConfig) { sec := cli.GetIntFlagValue(cmd, legacyBlockTimeFlag) config.Consensus.BlockTime = fmt.Sprintf("%ds", sec) } + + if cli.IsFlagChanged(cmd, consensusMinPeersFlag) { + config.Consensus.MinPeers = cli.GetIntFlagValue(cmd, consensusMinPeersFlag) + } else if cli.IsFlagChanged(cmd, legacyConsensusMinPeersFlag) { + config.Consensus.MinPeers = cli.GetIntFlagValue(cmd, legacyConsensusMinPeersFlag) + } } // transaction pool flags @@ -961,6 +1030,12 @@ var ( DefValue: defaultConfig.RPC.IP, Deprecated: "use --http.ip", } + legacyWebHookConfigFlag = cli.StringFlag{ + Name: "webhook_yaml", + Usage: "path for yaml config reporting double signing", + DefValue: "", + Hidden: true, + } ) // Note: this function need to be called before parse other flags @@ -970,14 +1045,18 @@ func applyLegacyMiscFlags(cmd *cobra.Command, config *harmonyConfig) { legacyPort := cli.GetIntFlagValue(cmd, legacyPortFlag) config.P2P.Port = legacyPort config.RPC.Port = legacyPort + config.WS.Port = legacyPort } if cli.IsFlagChanged(cmd, legacyIPFlag) { - config.RPC.IP = cli.GetStringFlagValue(cmd, legacyIPFlag) + legacyIP := cli.GetStringFlagValue(cmd, legacyIPFlag) + config.RPC.IP = legacyIP config.RPC.Enabled = true + config.P2P.IP = legacyIP + config.WS.IP = legacyIP } - if cli.HasFlagsChanged(cmd, legacyMiscFlags) { + if cli.HasFlagsChanged(cmd, []cli.Flag{legacyPortFlag, legacyIPFlag}) { logIP := cli.GetStringFlagValue(cmd, legacyIPFlag) logPort := cli.GetIntFlagValue(cmd, legacyPortFlag) config.Log.FileName = fmt.Sprintf("validator-%v-%v.log", logIP, logPort) @@ -988,4 +1067,10 @@ func applyLegacyMiscFlags(cmd *cobra.Command, config *harmonyConfig) { } config.Log.Context = logCtx } + + if cli.IsFlagChanged(cmd, legacyWebHookConfigFlag) { + config.Legacy = &legacyConfig{ + WebHookConfig: cli.GetStringFlagValue(cmd, legacyWebHookConfigFlag), + } + } } diff --git a/cmd/harmony/main.go b/cmd/harmony/main.go index f289f6949..0ca1c38e9 100644 --- a/cmd/harmony/main.go +++ b/cmd/harmony/main.go @@ -1,7 +1,6 @@ package main import ( - "flag" "fmt" "io/ioutil" "math/big" @@ -10,32 +9,32 @@ import ( _ "net/http/pprof" "os" "os/signal" - "path" + "path/filepath" "runtime" "strconv" "strings" "syscall" "time" - "github.com/harmony-one/harmony/internal/cli" + "github.com/ethereum/go-ethereum/log" + "github.com/harmony-one/harmony/numeric" + "github.com/spf13/cobra" ethCommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" "github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/harmony/api/service/syncing" "github.com/harmony-one/harmony/consensus" "github.com/harmony-one/harmony/consensus/quorum" "github.com/harmony-one/harmony/core" + "github.com/harmony-one/harmony/internal/cli" "github.com/harmony-one/harmony/internal/common" nodeconfig "github.com/harmony-one/harmony/internal/configs/node" shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding" - viperconfig "github.com/harmony-one/harmony/internal/configs/viper" "github.com/harmony-one/harmony/internal/genesis" "github.com/harmony-one/harmony/internal/shardchain" "github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/multibls" "github.com/harmony-one/harmony/node" - "github.com/harmony-one/harmony/numeric" "github.com/harmony-one/harmony/p2p" "github.com/harmony-one/harmony/shard" "github.com/harmony-one/harmony/webhooks" @@ -48,71 +47,315 @@ var ( initialAccounts = []*genesis.DeployAccount{} ) -func printVersion() { - fmt.Fprintln(os.Stdout, nodeconfig.GetVersion()) - os.Exit(0) +var rootCmd = &cobra.Command{ + // TODO: elaborate the usage + Use: "harmony", + Short: "run harmony node", + Long: "run harmony node", + Run: runHarmonyNode, } -//ip = flag.String("ip", "127.0.0.1", "ip of the node") -//port = flag.String("port", "9000", "port of the node.") -//logFolder = flag.String("log_folder", "latest", "the folder collecting the logs of this execution") -//logMaxSize = flag.Int("log_max_size", 100, "the max size in megabytes of the log file before it gets rotated") -//// Remove this flag -freshDB = flag.Bool("fresh_db", false, "true means the existing disk based db will be removed") -//pprof = flag.String("pprof", "", "what address and port the pprof profiling server should listen on") -//versionFlag = flag.Bool("version", false, "Output version info") -//dnsZone = flag.String("dns_zone", "", "if given and not empty, use peers from the zone (default: use libp2p peer discovery instead)") -//dnsFlag = flag.Bool("dns", true, "[deprecated] equivalent to -dns_zone t.hmny.io") -//dnsPort = flag.String("dns_port", "9000", "port of dns node") -////Leader needs to have a minimal number of peers to start consensus -//minPeers = flag.Int("min_peers", 32, "Minimal number of Peers in shard") -//// Key file to store the private key -//keyFile = flag.String("key", "./.hmykey", "the p2p key file of the harmony node") -//// isArchival indicates this node is an archival node that will save and archive current blockchain -//isArchival = flag.Bool("is_archival", false, "false will enable cached state pruning") -//// delayCommit is the commit-delay timer, used by Harmony nodes -//// TODO: check whether this field can be removed -//delayCommit = flag.String("delay_commit", "0ms", "how long to delay sending commit messages in consensus, ex: 500ms, 1s") -//// nodeType indicates the type of the node: validator, explorer -//nodeType = flag.String("node_type", "validator", "node type: validator, explorer") -//// networkType indicates the type of the network -//networkType = flag.String("network_type", "mainnet", "type of the network: mainnet, testnet, pangaea, partner, stressnet, devnet, localnet") -//// blockPeriod indicates the how long the leader waits to propose a new block. -//blockPeriod = flag.Int("block_period", 8, "how long in second the leader waits to propose a new block") -//// staking indicates whether the node is operating in staking mode. -//stakingFlag = flag.Bool("staking", false, "whether the node should operate in staking mode") -//// shardID indicates the shard ID of this node -//shardID = flag.Int("shard_id", -1, "the shard ID of this node") -//// Sharding configuration parameters for devnet -//devnetNumShards = flag.Uint("dn_num_shards", 2, "number of shards for -network_type=devnet (default: 2)") -//devnetShardSize = flag.Int("dn_shard_size", 10, "number of nodes per shard for -network_type=devnet (default 10)") -//devnetHarmonySize = flag.Int("dn_hmy_size", -1, "number of Harmony-operated nodes per shard for -network_type=devnet; negative (default) means equal to -dn_shard_size") -//// logging verbosity -//verbosity = flag.Int("verbosity", 5, "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail (default: 5)") -//// dbDir is the database directory. -//dbDir = flag.String("db_dir", "", "blockchain database directory") -//publicRPC = flag.Bool("public_rpc", false, "Enable Public RPC Access (default: false)") -//// Bad block revert -//// TODO: Seperate revert to a different command -//doRevertBefore = flag.Int("do_revert_before", 0, "If the current block is less than do_revert_before, revert all blocks until (including) revert_to block") -//revertTo = flag.Int("revert_to", 0, "The revert will rollback all blocks until and including block number revert_to") -//revertBeacon = flag.Bool("revert_beacon", false, "Whether to revert beacon chain or the chain this node is assigned to") -//// Blacklist of addresses -//blacklistPath = flag.String("blacklist", "./.hmy/blacklist.txt", "Path to newline delimited file of blacklisted wallet addresses") -//broadcastInvalidTx = flag.Bool("broadcast_invalid_tx", false, "Broadcast invalid transactions to sync pool state (default: false)") -//webHookYamlPath = flag.String( -// "webhook_yaml", "", "path for yaml config reporting double signing", -//) +var configFlag = cli.StringFlag{ + Name: "config", + Usage: "load node config from the config toml file.", + Shorthand: "c", + DefValue: "", +} func init() { - if err := registerRootCmdFlags(); err != nil { - // Only happens when code is wrong - panic(err) - } cli.SetParseErrorHandle(func(err error) { os.Exit(128) // 128 - invalid command line arguments }) + rootCmd.AddCommand(dumpConfigCmd) + rootCmd.AddCommand(versionCmd) + + if err := registerRootCmdFlags(); err != nil { + os.Exit(2) + } + if err := registerDumpConfigFlags(); err != nil { + os.Exit(2) + } +} + +func main() { + rootCmd.Execute() +} + +func registerRootCmdFlags() error { + var flags []cli.Flag + + flags = append(flags, configFlag) + flags = append(flags, generalFlags...) + flags = append(flags, networkFlags...) + flags = append(flags, p2pFlags...) + flags = append(flags, rpcFlags...) + flags = append(flags, wsFlags...) + flags = append(flags, blsFlags...) + flags = append(flags, consensusFlags...) + flags = append(flags, txPoolFlags...) + flags = append(flags, pprofFlags...) + flags = append(flags, logFlags...) + flags = append(flags, devnetFlags...) + flags = append(flags, revertFlags...) + flags = append(flags, legacyMiscFlags...) + + return cli.RegisterFlags(rootCmd, flags) +} + +func runHarmonyNode(cmd *cobra.Command, args []string) { + prepareRootCmd(cmd) + cfg, err := getHarmonyConfig(cmd) + if err != nil { + fmt.Println(err) + cmd.Help() + os.Exit(128) + } + setupNodeLog(cfg) + setupPprof(cfg) + + setupNodeAndRun(cfg) +} + +func prepareRootCmd(cmd *cobra.Command) { + // HACK Force usage of go implementation rather than the C based one. Do the right way, see the + // notes one line 66,67 of https://golang.org/src/net/net.go that say can make the decision at + // build time. + os.Setenv("GODEBUG", "netdns=go") + // Don't set higher than num of CPU. It will make go scheduler slower. + runtime.GOMAXPROCS(runtime.NumCPU()) + // Set up randomization seed. + rand.Seed(int64(time.Now().Nanosecond())) +} + +func getHarmonyConfig(cmd *cobra.Command) (harmonyConfig, error) { + var ( + config harmonyConfig + err error + ) + if cli.IsFlagChanged(cmd, configFlag) { + configFile := cli.GetStringFlagValue(cmd, configFlag) + config, err = loadHarmonyConfig(configFile) + } else { + config = getDefaultHmyConfigCopy() + nt := getNetworkType(cmd) + config.Network = getDefaultNetworkConfig(nt) + } + if err != nil { + return harmonyConfig{}, err + } + // Misc flags shall be applied first since legacy ip / port is overwritten + // by new ip / port flags + applyLegacyMiscFlags(cmd, &config) + applyGeneralFlags(cmd, &config) + applyNetworkFlags(cmd, &config) + applyP2PFlags(cmd, &config) + applyRPCFlags(cmd, &config) + applyWSFlags(cmd, &config) + applyBLSFlags(cmd, &config) + applyConsensusFlags(cmd, &config) + applyTxPoolFlags(cmd, &config) + applyPprofFlags(cmd, &config) + applyLogFlags(cmd, &config) + applyDevnetFlags(cmd, &config) + applyRevertFlags(cmd, &config) + + if err := validateHarmonyConfig(config); err != nil { + return harmonyConfig{}, err + } + + return config, nil +} + +func setupNodeLog(config harmonyConfig) { + logPath := filepath.Join(config.Log.Folder, config.Log.FileName) + rotateSize := config.Log.RotateSize + verbosity := config.Log.Verbosity + + utils.AddLogFile(logPath, rotateSize) + utils.SetLogVerbosity(log.Lvl(verbosity)) + if config.Log.Context != nil { + ip := config.Log.Context.IP + port := config.Log.Context.Port + utils.SetLogContext(ip, strconv.Itoa(port)) + } +} + +func setupPprof(config harmonyConfig) { + enabled := config.Pprof.Enabled + addr := config.Pprof.ListenAddr + + if enabled { + go func() { + http.ListenAndServe(addr, nil) + }() + } +} + +func setupNodeAndRun(hc harmonyConfig) { + var err error + bootNodes := hc.Network.BootNodes + p2p.BootNodes, err = p2p.StringsToAddrs(bootNodes) + if err != nil { + utils.FatalErrMsg(err, "cannot parse bootnode list %#v", + bootNodes) + } + // TODO: seperate use of port rpc / p2p + nodeconfig.GetDefaultConfig().Port = strconv.Itoa(hc.RPC.Port) + nodeconfig.GetDefaultConfig().IP = hc.RPC.IP + + nodeconfig.SetShardingSchedule(shard.Schedule) + nodeconfig.SetPublicRPC(true) + nodeconfig.SetVersion(getHarmonyVersion()) + nodeconfigSetShardSchedule(hc) + + if hc.General.NodeType == "validator" { + var err error + if hc.General.IsStaking { + err = setupStakingNodeAccount(hc) + } else { + err = setupLegacyNodeAccount(hc) + } + if err != nil { + fmt.Fprintf(os.Stderr, "cannot set up node account: %s\n", err) + os.Exit(1) + } + } + if hc.General.NodeType == "validator" { + fmt.Printf("%s mode; node key %s -> shard %d\n", + map[bool]string{false: "Legacy", true: "Staking"}[hc.General.IsStaking], + nodeconfig.GetDefaultConfig().ConsensusPriKey.GetPublicKeys().SerializeToHexStr(), + initialAccounts[0].ShardID) + } + if hc.General.NodeType != "validator" && hc.General.ShardID >= 0 { + for _, initialAccount := range initialAccounts { + utils.Logger().Info(). + Uint32("original", initialAccount.ShardID). + Int("override", hc.General.ShardID). + Msg("ShardID Override") + initialAccount.ShardID = uint32(hc.General.ShardID) + } + } + + nodeConfig, err := createGlobalConfig(hc) + if err != nil { + fmt.Fprintf(os.Stderr, "ERROR cannot configure node: %s\n", err) + os.Exit(1) + } + currentNode := setupConsensusAndNode(hc, nodeConfig) + nodeconfig.GetDefaultConfig().ShardID = nodeConfig.ShardID + + // Prepare for graceful shutdown from os signals + osSignal := make(chan os.Signal) + signal.Notify(osSignal, os.Interrupt, syscall.SIGTERM) + go func() { + for sig := range osSignal { + if sig == syscall.SIGTERM || sig == os.Interrupt { + const msg = "Got %s signal. Gracefully shutting down...\n" + utils.Logger().Printf(msg, sig) + fmt.Printf(msg, sig) + currentNode.ShutDown() + } + } + }() + + if nodeConfig.ShardID != shard.BeaconChainShardID { + utils.Logger().Info(). + Uint32("shardID", currentNode.Blockchain().ShardID()). + Uint32("shardID", nodeConfig.ShardID).Msg("SupportBeaconSyncing") + currentNode.SupportBeaconSyncing() + } + + if hc.Revert != nil && hc.Revert.RevertBefore != -1 && hc.Revert.RevertTo != -1 { + chain := currentNode.Blockchain() + if hc.Revert.RevertBeacon { + chain = currentNode.Beaconchain() + } + curNum := chain.CurrentBlock().NumberU64() + if curNum < uint64(hc.Revert.RevertBefore) && curNum >= uint64(hc.Revert.RevertTo) { + // Remove invalid blocks + for chain.CurrentBlock().NumberU64() >= uint64(hc.Revert.RevertTo) { + curBlock := chain.CurrentBlock() + rollbacks := []ethCommon.Hash{curBlock.Hash()} + chain.Rollback(rollbacks) + lastSig := curBlock.Header().LastCommitSignature() + sigAndBitMap := append(lastSig[:], curBlock.Header().LastCommitBitmap()...) + chain.WriteCommitSig(curBlock.NumberU64()-1, sigAndBitMap) + } + } + } + + startMsg := "==== New Harmony Node ====" + if hc.General.NodeType == nodeTypeExplorer { + startMsg = "==== New Explorer Node ====" + } + + utils.Logger().Info(). + Str("BLSPubKey", nodeConfig.ConsensusPriKey.GetPublicKeys().SerializeToHexStr()). + Uint32("ShardID", nodeConfig.ShardID). + Str("ShardGroupID", nodeConfig.GetShardGroupID().String()). + Str("BeaconGroupID", nodeConfig.GetBeaconGroupID().String()). + Str("ClientGroupID", nodeConfig.GetClientGroupID().String()). + Str("Role", currentNode.NodeConfig.Role().String()). + Str("multiaddress", + fmt.Sprintf("/ip4/%s/tcp/%s/p2p/%s", hc.RPC.IP, hc.P2P.Port, myHost.GetID().Pretty()), + ). + Msg(startMsg) + + nodeconfig.SetPeerID(myHost.GetID()) + + currentNode.SupportSyncing() + currentNode.ServiceManagerSetup() + currentNode.RunServices() + + if err := currentNode.StartRPC(strconv.Itoa(hc.RPC.Port)); err != nil { + utils.Logger().Warn(). + Err(err). + Msg("StartRPC failed") + } + if err := currentNode.BootstrapConsensus(); err != nil { + fmt.Println("could not bootstrap consensus", err.Error()) + os.Exit(-1) + } + + if err := currentNode.Start(); err != nil { + fmt.Println("could not begin network message handling for node", err.Error()) + os.Exit(-1) + } +} + +func nodeconfigSetShardSchedule(config harmonyConfig) { + switch config.Network.NetworkType { + case nodeconfig.Mainnet: + shard.Schedule = shardingconfig.MainnetSchedule + case nodeconfig.Testnet: + shard.Schedule = shardingconfig.TestnetSchedule + case nodeconfig.Pangaea: + shard.Schedule = shardingconfig.PangaeaSchedule + case nodeconfig.Localnet: + shard.Schedule = shardingconfig.LocalnetSchedule + case nodeconfig.Partner: + shard.Schedule = shardingconfig.PartnerSchedule + case nodeconfig.Stressnet: + shard.Schedule = shardingconfig.StressNetSchedule + case nodeconfig.Devnet: + var dnConfig devnetConfig + if config.Devnet != nil { + dnConfig = *config.Devnet + } else { + dnConfig = getDefaultDevnetConfigCopy() + } + + // TODO (leo): use a passing list of accounts here + devnetConfig, err := shardingconfig.NewInstance( + uint32(dnConfig.NumShards), dnConfig.ShardSize, dnConfig.HmyNodeSize, numeric.OneDec(), genesis.HarmonyAccounts, genesis.FoundationalNodeAccounts, nil, shardingconfig.VLBPE) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "ERROR invalid devnet sharding config: %s", + err) + os.Exit(1) + } + shard.Schedule = shardingconfig.NewFixedSchedule(devnetConfig) + } } func findAccountsByPubKeys(config shardingconfig.Instance, pubKeys multibls.PublicKeys) { @@ -125,9 +368,9 @@ func findAccountsByPubKeys(config shardingconfig.Instance, pubKeys multibls.Publ } } -func setupLegacyNodeAccount() error { +func setupLegacyNodeAccount(hc harmonyConfig) error { genesisShardingConfig := shard.Schedule.InstanceForEpoch(big.NewInt(core.GenesisEpoch)) - multiBLSPubKey := setupConsensusKeys(nodeconfig.GetDefaultConfig()) + multiBLSPubKey := setupConsensusKeys(hc, nodeconfig.GetDefaultConfig()) reshardingEpoch := genesisShardingConfig.ReshardingEpoch() if len(reshardingEpoch) > 0 { @@ -157,8 +400,8 @@ func setupLegacyNodeAccount() error { return nil } -func setupStakingNodeAccount() error { - pubKeys := setupConsensusKeys(nodeconfig.GetDefaultConfig()) +func setupStakingNodeAccount(hc harmonyConfig) error { + pubKeys := setupConsensusKeys(hc, nodeconfig.GetDefaultConfig()) shardID, err := nodeconfig.GetDefaultConfig().ShardIDFromConsensusKey() if err != nil { return errors.Wrap(err, "cannot determine shard to join") @@ -178,51 +421,36 @@ func setupStakingNodeAccount() error { return nil } -// setupConsensusKeys load bls keys and set the keys to nodeConfig. Return the loaded public keys. -func setupConsensusKeys(config *nodeconfig.ConfigType) multibls.PublicKeys { - onceLoadBLSKey.Do(func() { - var err error - multiBLSPriKey, err = loadBLSKeys() - if err != nil { - fmt.Fprintf(os.Stderr, "ERROR when loading bls key: %v\n", err) - os.Exit(100) - } - fmt.Printf("Successfully loaded %v BLS keys\n", len(multiBLSPriKey)) - }) - config.ConsensusPriKey = multiBLSPriKey - return multiBLSPriKey.GetPublicKeys() -} - -func createGlobalConfig() (*nodeconfig.ConfigType, error) { +func createGlobalConfig(hc harmonyConfig) (*nodeconfig.ConfigType, error) { var err error if len(initialAccounts) == 0 { - initialAccounts = append(initialAccounts, &genesis.DeployAccount{ShardID: uint32(*shardID)}) + initialAccounts = append(initialAccounts, &genesis.DeployAccount{ShardID: uint32(hc.General.ShardID)}) } nodeConfig := nodeconfig.GetShardConfig(initialAccounts[0].ShardID) - if *nodeType == "validator" { + if hc.General.NodeType == nodeTypeValidator { // Set up consensus keys. - setupConsensusKeys(nodeConfig) + setupConsensusKeys(hc, nodeConfig) } else { // set dummy bls key for consensus object nodeConfig.ConsensusPriKey = multibls.GetPrivateKeys(&bls.SecretKey{}) } // Set network type - netType := nodeconfig.NetworkType(*networkType) + netType := nodeconfig.NetworkType(hc.Network.NetworkType) nodeconfig.SetNetworkType(netType) // sets for both global and shard configs - nodeConfig.SetArchival(*isArchival) + nodeConfig.SetArchival(hc.General.IsArchival) // P2P private key is used for secure message transfer between p2p nodes. - nodeConfig.P2PPriKey, _, err = utils.LoadKeyFromFile(*keyFile) + nodeConfig.P2PPriKey, _, err = utils.LoadKeyFromFile(hc.P2P.KeyFile) if err != nil { return nil, errors.Wrapf(err, "cannot load or create P2P key at %#v", - *keyFile) + hc.P2P.KeyFile) } selfPeer := p2p.Peer{ - IP: *ip, - Port: *port, + IP: hc.P2P.IP, + Port: strconv.Itoa(hc.P2P.Port), ConsensusPubKey: nodeConfig.ConsensusPriKey[0].Pub.Object, } @@ -231,9 +459,10 @@ func createGlobalConfig() (*nodeconfig.ConfigType, error) { return nil, errors.Wrap(err, "cannot create P2P network host") } - nodeConfig.DBDir = *dbDir + nodeConfig.DBDir = hc.General.DataDir - if p := *webHookYamlPath; p != "" { + if hc.Legacy != nil && len(hc.Legacy.WebHookConfig) != 0 { + p := hc.Legacy.WebHookConfig config, err := webhooks.NewWebHooksFromPath(p) if err != nil { fmt.Fprintf( @@ -247,10 +476,10 @@ func createGlobalConfig() (*nodeconfig.ConfigType, error) { return nodeConfig, nil } -func setupConsensusAndNode(nodeConfig *nodeconfig.ConfigType) *node.Node { +func setupConsensusAndNode(hc harmonyConfig, nodeConfig *nodeconfig.ConfigType) *node.Node { // Consensus object. // TODO: consensus object shouldn't start here - decider := quorum.NewDecider(quorum.SuperMajorityVote, uint32(*shardID)) + decider := quorum.NewDecider(quorum.SuperMajorityVote, uint32(hc.General.ShardID)) currentConsensus, err := consensus.New( myHost, nodeConfig.ShardID, p2p.Peer{}, nodeConfig.ConsensusPriKey, decider, @@ -263,15 +492,15 @@ func setupConsensusAndNode(nodeConfig *nodeconfig.ConfigType) *node.Node { _, _ = fmt.Fprintf(os.Stderr, "Error :%v \n", err) os.Exit(1) } - commitDelay, err := time.ParseDuration(*delayCommit) + commitDelay, err := time.ParseDuration(hc.Consensus.DelayCommit) if err != nil || commitDelay < 0 { - _, _ = fmt.Fprintf(os.Stderr, "ERROR invalid commit delay %#v", *delayCommit) + _, _ = fmt.Fprintf(os.Stderr, "ERROR invalid commit delay %#v", hc.Consensus.DelayCommit) os.Exit(1) } currentConsensus.SetCommitDelay(commitDelay) - currentConsensus.MinPeers = *minPeers + currentConsensus.MinPeers = hc.Consensus.MinPeers - blacklist, err := setupBlacklist() + blacklist, err := setupBlacklist(hc) if err != nil { utils.Logger().Warn().Msgf("Blacklist setup error: %s", err.Error()) } @@ -279,44 +508,37 @@ func setupConsensusAndNode(nodeConfig *nodeconfig.ConfigType) *node.Node { // Current node. chainDBFactory := &shardchain.LDBFactory{RootDir: nodeConfig.DBDir} - currentNode := node.New(myHost, currentConsensus, chainDBFactory, blacklist, *isArchival) - currentNode.BroadcastInvalidTx = *broadcastInvalidTx + currentNode := node.New(myHost, currentConsensus, chainDBFactory, blacklist, hc.General.IsArchival) + currentNode.BroadcastInvalidTx = hc.TxPool.BroadcastInvalidTx - switch { - case *networkType == nodeconfig.Localnet: - epochConfig := shard.Schedule.InstanceForEpoch(ethCommon.Big0) - selfPort, err := strconv.ParseUint(*port, 10, 16) - if err != nil { - utils.Logger().Fatal(). - Err(err). - Str("self_port_string", *port). - Msg("cannot convert self port string into port number") - } - currentNode.SyncingPeerProvider = node.NewLocalSyncingPeerProvider( - 6000, uint16(selfPort), epochConfig.NumShards(), uint32(epochConfig.NumNodesPerShard())) - case *dnsZone != "": - currentNode.SyncingPeerProvider = node.NewDNSSyncingPeerProvider(*dnsZone, syncing.GetSyncingPort(*dnsPort)) - case *dnsFlag: - currentNode.SyncingPeerProvider = node.NewDNSSyncingPeerProvider("t.hmny.io", syncing.GetSyncingPort(*dnsPort)) - default: + if hc.Network.LegacySyncing { currentNode.SyncingPeerProvider = node.NewLegacySyncingPeerProvider(currentNode) - + } else { + if hc.Network.NetworkType == nodeconfig.Localnet { + epochConfig := shard.Schedule.InstanceForEpoch(ethCommon.Big0) + selfPort := hc.Network.DNSPort + currentNode.SyncingPeerProvider = node.NewLocalSyncingPeerProvider( + 6000, uint16(selfPort), epochConfig.NumShards(), uint32(epochConfig.NumNodesPerShard())) + } else { + currentNode.SyncingPeerProvider = node.NewDNSSyncingPeerProvider(hc.Network.DNSZone, syncing.GetSyncingPort(strconv.Itoa(hc.Network.DNSPort))) + } } // TODO: refactor the creation of blockchain out of node.New() currentConsensus.ChainReader = currentNode.Blockchain() - currentNode.NodeConfig.DNSZone = *dnsZone + currentNode.NodeConfig.DNSZone = hc.Network.DNSZone currentNode.NodeConfig.SetBeaconGroupID( nodeconfig.NewGroupIDByShardID(shard.BeaconChainShardID), ) nodeconfig.GetDefaultConfig().DBDir = nodeConfig.DBDir - switch *nodeType { - case "explorer": + switch hc.General.NodeType { + case nodeTypeExplorer: nodeconfig.SetDefaultRole(nodeconfig.ExplorerNode) currentNode.NodeConfig.SetRole(nodeconfig.ExplorerNode) - case "validator": + + case nodeTypeValidator: nodeconfig.SetDefaultRole(nodeconfig.Validator) currentNode.NodeConfig.SetRole(nodeconfig.Validator) } @@ -327,7 +549,7 @@ func setupConsensusAndNode(nodeConfig *nodeconfig.ConfigType) *node.Node { // This needs to be executed after consensus setup if err := currentNode.InitConsensusWithValidators(); err != nil { utils.Logger().Warn(). - Int("shardID", *shardID). + Int("shardID", hc.General.ShardID). Err(err). Msg("InitConsensusWithMembers failed") } @@ -345,14 +567,19 @@ func setupConsensusAndNode(nodeConfig *nodeconfig.ConfigType) *node.Node { // update consensus information based on the blockchain currentConsensus.SetMode(currentConsensus.UpdateConsensusInformation()) // Setup block period and block due time. - currentConsensus.BlockPeriod = time.Duration(*blockPeriod) * time.Second + // TODO: move the error check to validation + currentConsensus.BlockPeriod, err = time.ParseDuration(hc.Consensus.BlockTime) + if err != nil { + fmt.Printf("Unknown block period: %v\n", hc.Consensus.BlockTime) + os.Exit(128) + } currentConsensus.NextBlockDue = time.Now() return currentNode } -func setupBlacklist() (map[ethCommon.Address]struct{}, error) { - utils.Logger().Debug().Msgf("Using blacklist file at `%s`", *blacklistPath) - dat, err := ioutil.ReadFile(*blacklistPath) +func setupBlacklist(hc harmonyConfig) (map[ethCommon.Address]struct{}, error) { + utils.Logger().Debug().Msgf("Using blacklist file at `%s`", hc.TxPool.BlacklistFile) + dat, err := ioutil.ReadFile(hc.TxPool.BlacklistFile) if err != nil { return nil, err } @@ -369,120 +596,3 @@ func setupBlacklist() (map[ethCommon.Address]struct{}, error) { } return addrMap, nil } - -func main() { - if *nodeType == "validator" { - var err error - if *stakingFlag { - err = setupStakingNodeAccount() - } else { - err = setupLegacyNodeAccount() - } - if err != nil { - fmt.Fprintf(os.Stderr, "cannot set up node account: %s\n", err) - os.Exit(1) - } - } - if *nodeType == "validator" { - fmt.Printf("%s mode; node key %s -> shard %d\n", - map[bool]string{false: "Legacy", true: "Staking"}[*stakingFlag], - nodeconfig.GetDefaultConfig().ConsensusPriKey.GetPublicKeys().SerializeToHexStr(), - initialAccounts[0].ShardID) - } - if *nodeType != "validator" && *shardID >= 0 { - for _, initialAccount := range initialAccounts { - utils.Logger().Info(). - Uint32("original", initialAccount.ShardID). - Int("override", *shardID). - Msg("ShardID Override") - initialAccount.ShardID = uint32(*shardID) - } - } - - nodeConfig, err := createGlobalConfig() - if err != nil { - fmt.Fprintf(os.Stderr, "ERROR cannot configure node: %s\n", err) - os.Exit(1) - } - currentNode := setupConsensusAndNode(nodeConfig) - nodeconfig.GetDefaultConfig().ShardID = nodeConfig.ShardID - - // Prepare for graceful shutdown from os signals - osSignal := make(chan os.Signal) - signal.Notify(osSignal, os.Interrupt, syscall.SIGTERM) - go func() { - for sig := range osSignal { - if sig == syscall.SIGTERM || sig == os.Interrupt { - const msg = "Got %s signal. Gracefully shutting down...\n" - utils.Logger().Printf(msg, sig) - fmt.Printf(msg, sig) - currentNode.ShutDown() - } - } - }() - - if nodeConfig.ShardID != shard.BeaconChainShardID { - utils.Logger().Info(). - Uint32("shardID", currentNode.Blockchain().ShardID()). - Uint32("shardID", nodeConfig.ShardID).Msg("SupportBeaconSyncing") - currentNode.SupportBeaconSyncing() - } - - if uint64(*doRevertBefore) != 0 && uint64(*revertTo) != 0 { - chain := currentNode.Blockchain() - if *revertBeacon { - chain = currentNode.Beaconchain() - } - curNum := chain.CurrentBlock().NumberU64() - if curNum < uint64(*doRevertBefore) && curNum >= uint64(*revertTo) { - // Remove invalid blocks - for chain.CurrentBlock().NumberU64() >= uint64(*revertTo) { - curBlock := chain.CurrentBlock() - rollbacks := []ethCommon.Hash{curBlock.Hash()} - chain.Rollback(rollbacks) - lastSig := curBlock.Header().LastCommitSignature() - sigAndBitMap := append(lastSig[:], curBlock.Header().LastCommitBitmap()...) - chain.WriteCommitSig(curBlock.NumberU64()-1, sigAndBitMap) - } - } - } - - startMsg := "==== New Harmony Node ====" - if *nodeType == "explorer" { - startMsg = "==== New Explorer Node ====" - } - - utils.Logger().Info(). - Str("BLSPubKey", nodeConfig.ConsensusPriKey.GetPublicKeys().SerializeToHexStr()). - Uint32("ShardID", nodeConfig.ShardID). - Str("ShardGroupID", nodeConfig.GetShardGroupID().String()). - Str("BeaconGroupID", nodeConfig.GetBeaconGroupID().String()). - Str("ClientGroupID", nodeConfig.GetClientGroupID().String()). - Str("Role", currentNode.NodeConfig.Role().String()). - Str("multiaddress", - fmt.Sprintf("/ip4/%s/tcp/%s/p2p/%s", *ip, *port, myHost.GetID().Pretty()), - ). - Msg(startMsg) - - nodeconfig.SetPeerID(myHost.GetID()) - - currentNode.SupportSyncing() - currentNode.ServiceManagerSetup() - currentNode.RunServices() - - if err := currentNode.StartRPC(*port); err != nil { - utils.Logger().Warn(). - Err(err). - Msg("StartRPC failed") - } - - if err := currentNode.BootstrapConsensus(); err != nil { - fmt.Println("could not bootstrap consensus", err.Error()) - os.Exit(-1) - } - - if err := currentNode.Start(); err != nil { - fmt.Println("could not begin network message handling for node", err.Error()) - os.Exit(-1) - } -} diff --git a/cmd/harmony/rootcmd.go b/cmd/harmony/rootcmd.go deleted file mode 100644 index 7f01766a9..000000000 --- a/cmd/harmony/rootcmd.go +++ /dev/null @@ -1,219 +0,0 @@ -package main - -import ( - "fmt" - "math/rand" - "net/http" - "os" - "path/filepath" - "runtime" - "strconv" - "strings" - "time" - - shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding" - "github.com/harmony-one/harmony/internal/genesis" - "github.com/harmony-one/harmony/numeric" - - "github.com/harmony-one/harmony/p2p" - - nodeconfig "github.com/harmony-one/harmony/internal/configs/node" - "github.com/harmony-one/harmony/shard" - - _ "net/http/pprof" - - "github.com/ethereum/go-ethereum/log" - "github.com/harmony-one/harmony/internal/cli" - "github.com/harmony-one/harmony/internal/utils" - "github.com/spf13/cobra" -) - -var rootCmd = &cobra.Command{ - // TODO: elaborate the usage - Use: "harmony", - Short: "run harmony node", - Long: "run harmony node", - Run: runHarmonyNode, -} - -var configFlag = cli.StringFlag{ - Name: "config", - Usage: "load node config from the config toml file.", - Shorthand: "c", - DefValue: "", -} - -func registerRootCmdFlags() error { - var flags []cli.Flag - flags = append(flags, configFlag) - flags = append(flags, generalFlags...) - flags = append(flags, networkFlags...) - flags = append(flags, p2pFlags...) - flags = append(flags, rpcFlags...) - flags = append(flags, blsFlags...) - flags = append(flags, consensusFlags...) - flags = append(flags, txPoolFlags...) - flags = append(flags, pprofFlags...) - flags = append(flags, logFlags...) - flags = append(flags, devnetFlags...) - flags = append(flags, revertFlags...) - flags = append(flags, legacyMiscFlags...) - - return cli.RegisterFlags(rootCmd, flags) -} - -func runHarmonyNode(cmd *cobra.Command, args []string) { - prepareRootCmd(cmd) - - cfg, err := getHarmonyConfig(cmd) - if err != nil { - fmt.Println(err) - cmd.Help() - os.Exit(128) - } - - setupNodeLog(cfg) - setupPprof(cfg) - if err := setupP2P; err != nil { - fmt.Println(err) - cmd.Help() - os.Exit(128) - } -} - -func prepareRootCmd(cmd *cobra.Command) { - // TODO: can we remove this? - // HACK Force usage of go implementation rather than the C based one. Do the right way, see the - // notes one line 66,67 of https://golang.org/src/net/net.go that say can make the decision at - // build time. - os.Setenv("GODEBUG", "netdns=go") - // Don't set higher than num of CPU. It will make go scheduler slower. - runtime.GOMAXPROCS(runtime.NumCPU()) - // Set up randomization seed. - rand.Seed(int64(time.Now().Nanosecond())) -} - -func getHarmonyConfig(cmd *cobra.Command) (harmonyConfig, error) { - var ( - config harmonyConfig - err error - ) - if cli.IsFlagChanged(cmd, configFlag) { - configFile := cli.GetStringFlagValue(cmd, configFlag) - config, err = loadHarmonyConfig(configFile) - } else { - config = getDefaultHmyConfigCopy() - nt := getNetworkType(cmd) - config.Network = getDefaultNetworkConfig(nt) - } - if err != nil { - return harmonyConfig{}, err - } - // Misc flags shall be applied first since legacy ip / port shall be overwritten - // by new ip / port flags - applyLegacyMiscFlags(cmd, &config) - applyGeneralFlags(cmd, &config) - applyNetworkFlags(cmd, &config) - applyP2PFlags(cmd, &config) - applyRPCFlags(cmd, &config) - applyBLSFlags(cmd, &config) - applyConsensusFlags(cmd, &config) - applyTxPoolFlags(cmd, &config) - applyPprofFlags(cmd, &config) - applyLogFlags(cmd, &config) - applyDevnetFlags(cmd, &config) - applyRevertFlags(cmd, &config) - - if err := validateHarmonyConfig(config); err != nil { - return harmonyConfig{}, err - } - - return config, nil -} - -func setupNodeLog(config harmonyConfig) { - logPath := filepath.Join(config.Log.Folder, config.Log.FileName) - rotateSize := config.Log.RotateSize - verbosity := config.Log.Verbosity - - utils.AddLogFile(logPath, rotateSize) - utils.SetLogVerbosity(log.Lvl(verbosity)) - if config.Log.Context != nil { - ip := config.Log.Context.IP - port := config.Log.Context.Port - utils.SetLogContext(ip, strconv.Itoa(port)) - } -} - -func setupPprof(config harmonyConfig) { - enabled := config.Pprof.Enabled - addr := config.Pprof.ListenAddr - - if enabled { - go func() { - http.ListenAndServe(addr, nil) - }() - } -} - -func setupP2P(config harmonyConfig) error { - var err error - bootNodes := config.Network.BootNodes - p2p.BootNodes, err = p2p.StringsToAddrs(strings.Join(bootNodes, ",")) - - return err -} - -func runNodeWithConfig(config harmonyConfig) { - -} - -func setupNodeConfig(config harmonyConfig) { - // TODO: seperate use of port rpc / p2p - nodeconfig.GetDefaultConfig().Port = strconv.Itoa(config.RPC.Port) - nodeconfig.GetDefaultConfig().IP = config.RPC.IP - - // Set sharding schedule - // TODO: module interface should have the same signature. - nodeconfig.SetShardingSchedule(shard.Schedule) - - // TODO: Whether public rpc is defined by user input ip flag. Refactor this. - nodeconfig.SetPublicRPC(true) - - nodeconfig.SetVersion(getHarmonyVersion()) - -} - -func setShardSchedule(config harmonyConfig) { - switch config.Network.NetworkType { - case nodeconfig.Mainnet: - shard.Schedule = shardingconfig.MainnetSchedule - case nodeconfig.Testnet: - shard.Schedule = shardingconfig.TestnetSchedule - case nodeconfig.Pangaea: - shard.Schedule = shardingconfig.PangaeaSchedule - case nodeconfig.Localnet: - shard.Schedule = shardingconfig.LocalnetSchedule - case nodeconfig.Partner: - shard.Schedule = shardingconfig.PartnerSchedule - case nodeconfig.Stressnet: - shard.Schedule = shardingconfig.StressNetSchedule - case nodeconfig.Devnet: - var dnConfig devnetConfig - if config.Devnet != nil { - dnConfig = *config.Devnet - } else { - dnConfig = getDefaultDevnetConfigCopy() - } - - // TODO (leo): use a passing list of accounts here - devnetConfig, err := shardingconfig.NewInstance( - uint32(dnConfig.NumShards), dnConfig.ShardSize, dnConfig.HmyNodeSize, numeric.OneDec(), genesis.HarmonyAccounts, genesis.FoundationalNodeAccounts, nil, shardingconfig.VLBPE) - if err != nil { - _, _ = fmt.Fprintf(os.Stderr, "ERROR invalid devnet sharding config: %s", - err) - os.Exit(1) - } - shard.Schedule = shardingconfig.NewFixedSchedule(devnetConfig) - } -} diff --git a/cmd/harmony/utils.go b/cmd/harmony/utils.go deleted file mode 100644 index 06ab7d0f9..000000000 --- a/cmd/harmony/utils.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/cmd/harmony/version.go b/cmd/harmony/version.go index 1eea78b64..38b864ff2 100644 --- a/cmd/harmony/version.go +++ b/cmd/harmony/version.go @@ -2,6 +2,9 @@ package main import ( "fmt" + "os" + + "github.com/spf13/cobra" ) const ( @@ -16,8 +19,21 @@ var ( commit string ) +var versionCmd = &cobra.Command{ + Use: "version", + Short: "print version of the harmony binary", + Long: "print version of the harmony binary", + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + printVersion() + os.Exit(0) + }, +} + func getHarmonyVersion() string { return fmt.Sprintf(versionFormat, "harmony", version, commit, builtBy, builtAt) } -// TODO add version command here +func printVersion() { + fmt.Println(getHarmonyVersion()) +} diff --git a/internal/configs/node/network.go b/internal/configs/node/network.go index 5a0ded095..fc7786c98 100644 --- a/internal/configs/node/network.go +++ b/internal/configs/node/network.go @@ -43,9 +43,12 @@ const ( // very bad design. Will refactor later // TODO: refactor all 9000-3000 = 6000 stuff DefaultDNSPort = 9000 - // DefaultRPCPort is the default rpc port. The actual port used is 9000+500, which is a terrible - // design. + // DefaultRPCPort is the default rpc port. The actual port used is 9000+500 + // TODO: modify this DefaultRPCPort = 9000 + // DefaultWSPort is the default port for web socket endpoint. The actual port used is + // TODO: modify this + DefaultWSPort = 9000 ) // GetDefaultBootNodes get the default bootnode with the given network type @@ -79,6 +82,7 @@ func GetDefaultDNSZone(networkType NetworkType) string { case Stressnet: return stressnetDNSZone } + return "" } // GetDefaultDNSPort get the default DNS port for the given network type