[cmd] finished implementation of command line flags and commands

pull/3278/head
Jacky Wang 4 years ago
parent b66be54a7e
commit 93574bc4ee
No known key found for this signature in database
GPG Key ID: 1085CE5F4FF5842C
  1. 138
      cmd/harmony/bls.go
  2. 157
      cmd/harmony/config.go
  3. 3
      cmd/harmony/config_test.go
  4. 123
      cmd/harmony/const.go
  5. 95
      cmd/harmony/flags.go
  6. 598
      cmd/harmony/main.go
  7. 219
      cmd/harmony/rootcmd.go
  8. 1
      cmd/harmony/utils.go
  9. 18
      cmd/harmony/version.go
  10. 8
      internal/configs/node/network.go

@ -1,8 +1,13 @@
package main package main
import ( import (
"fmt"
"os"
"sync" "sync"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/harmony-one/harmony/internal/blsgen"
"github.com/harmony-one/harmony/multibls" "github.com/harmony-one/harmony/multibls"
) )
@ -11,42 +16,97 @@ var (
onceLoadBLSKey sync.Once onceLoadBLSKey sync.Once
) )
//// TODO: refactor this // setupConsensusKeys load bls keys and set the keys to nodeConfig. Return the loaded public keys.
//func loadBLSKeys() (multibls.PrivateKeys, error) { func setupConsensusKeys(hc harmonyConfig, config *nodeconfig.ConfigType) multibls.PublicKeys {
// config, err := parseBLSLoadingConfig() onceLoadBLSKey.Do(func() {
// if err != nil { var err error
// return nil, err multiBLSPriKey, err = loadBLSKeys(hc.BLSKeys)
// } if err != nil {
// keys, err := blsgen.LoadKeys(config) fmt.Fprintf(os.Stderr, "ERROR when loading bls key: %v\n", err)
// if err != nil { os.Exit(100)
// return nil, err }
// } fmt.Printf("Successfully loaded %v BLS keys\n", len(multiBLSPriKey))
// if len(keys) == 0 { })
// return nil, fmt.Errorf("0 bls keys loaded") config.ConsensusPriKey = multiBLSPriKey
// } return multiBLSPriKey.GetPublicKeys()
// if len(keys) >= *maxBLSKeysPerNode { }
// return nil, fmt.Errorf("bls keys exceed maximum count %v", *maxBLSKeysPerNode)
// } func loadBLSKeys(raw blsConfig) (multibls.PrivateKeys, error) {
// return keys, err config, err := parseBLSLoadingConfig(raw)
//} if err != nil {
// return nil, err
//func parseBLSLoadingConfig() (blsgen.Config, error) { }
// var ( keys, err := blsgen.LoadKeys(config)
// config blsgen.Config if err != nil {
// err error return nil, err
// ) }
// if len(*blsKeyFile) != 0 { if len(keys) == 0 {
// config.MultiBlsKeys = strings.Split(*blsKeyFile, ",") return nil, fmt.Errorf("0 bls keys loaded")
// } }
// config.BlsDir = blsFolder if len(keys) >= raw.MaxKeys {
// return nil, fmt.Errorf("bls keys exceed maximum count %v", raw.MaxKeys)
// config, err = parseBLSPass(config, *blsPass) }
// if err != nil { return keys, err
// return blsgen.Config{}, err }
// }
// config, err = parseAwsConfigSrc(config, *awsConfigSource) func parseBLSLoadingConfig(raw blsConfig) (blsgen.Config, error) {
// if err != nil { var (
// return blsgen.Config{}, err config blsgen.Config
// } err error
// return config, nil )
//} 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
}

@ -3,8 +3,13 @@ package main
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os"
"strings" "strings"
"github.com/harmony-one/harmony/internal/cli"
"github.com/spf13/cobra"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node" nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/pelletier/go-toml" "github.com/pelletier/go-toml"
) )
@ -17,6 +22,7 @@ type harmonyConfig struct {
Network networkConfig Network networkConfig
P2P p2pConfig P2P p2pConfig
RPC rpcConfig RPC rpcConfig
WS wsConfig
Consensus consensusConfig Consensus consensusConfig
BLSKeys blsConfig BLSKeys blsConfig
TxPool txPoolConfig TxPool txPoolConfig
@ -24,6 +30,7 @@ type harmonyConfig struct {
Log logConfig Log logConfig
Devnet *devnetConfig `toml:",omitempty"` Devnet *devnetConfig `toml:",omitempty"`
Revert *revertConfig `toml:",omitempty"` Revert *revertConfig `toml:",omitempty"`
Legacy *legacyConfig `toml:",omitempty"`
} }
type networkConfig struct { type networkConfig struct {
@ -36,6 +43,7 @@ type networkConfig struct {
} }
type p2pConfig struct { type p2pConfig struct {
IP string
Port int Port int
KeyFile string KeyFile string
} }
@ -51,6 +59,7 @@ type generalConfig struct {
type consensusConfig struct { type consensusConfig struct {
DelayCommit string DelayCommit string
BlockTime string BlockTime string
MinPeers int
} }
type blsConfig struct { type blsConfig struct {
@ -97,135 +106,29 @@ type rpcConfig struct {
Port int Port int
} }
type wsConfig struct {
Enabled bool
IP string
Port int
}
type devnetConfig struct { type devnetConfig struct {
NumShards int NumShards int
ShardSize int ShardSize int
HmyNodeSize int HmyNodeSize int
} }
// TODO: make this revert to a seperate command // TODO: make this revert to a separate command
type revertConfig struct { type revertConfig struct {
RevertBeacon bool RevertBeacon bool
RevertTo int RevertTo int
RevertBefore int RevertBefore int
} }
var defaultConfig = harmonyConfig{ type legacyConfig struct {
Version: tomlConfigVersion, WebHookConfig string
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,
} }
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. // TODO: use specific type wise validation instead of general string types assertion.
func validateHarmonyConfig(config harmonyConfig) error { func validateHarmonyConfig(config harmonyConfig) error {
var accepts []string 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) { func loadHarmonyConfig(file string) (harmonyConfig, error) {
b, err := ioutil.ReadFile(file) b, err := ioutil.ReadFile(file)
if err != nil { if err != nil {
@ -316,7 +241,7 @@ func loadHarmonyConfig(file string) (harmonyConfig, error) {
return config, nil return config, nil
} }
func writeHarmonyConfigToFile(config *harmonyConfig, file string) error { func writeHarmonyConfigToFile(config harmonyConfig, file string) error {
b, err := toml.Marshal(config) b, err := toml.Marshal(config)
if err != nil { if err != nil {
return err return err

@ -1,4 +1,5 @@
//package main package main
// //
//import ( //import (
// "fmt" // "fmt"

@ -9,3 +9,126 @@ const (
const ( const (
mainnetDnsZone = "t.hmny.io" 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"
)

@ -37,6 +37,7 @@ var (
} }
p2pFlags = []cli.Flag{ p2pFlags = []cli.Flag{
p2pIPFlag,
p2pPortFlag, p2pPortFlag,
p2pKeyFileFlag, p2pKeyFileFlag,
@ -51,6 +52,12 @@ var (
legacyPublicRPCFlag, legacyPublicRPCFlag,
} }
wsFlags = []cli.Flag{
wsEnabledFlag,
wsIPFlag,
wsPortFlag,
}
blsFlags = append(newBLSFlags, legacyBLSFlags...) blsFlags = append(newBLSFlags, legacyBLSFlags...)
newBLSFlags = []cli.Flag{ newBLSFlags = []cli.Flag{
@ -78,9 +85,11 @@ var (
consensusFlags = []cli.Flag{ consensusFlags = []cli.Flag{
consensusDelayCommitFlag, consensusDelayCommitFlag,
consensusBlockTimeFlag, consensusBlockTimeFlag,
consensusMinPeersFlag,
legacyDelayCommitFlag, legacyDelayCommitFlag,
legacyBlockTimeFlag, legacyBlockTimeFlag,
legacyConsensusMinPeersFlag,
} }
txPoolFlags = []cli.Flag{ txPoolFlags = []cli.Flag{
@ -140,6 +149,7 @@ var (
legacyMiscFlags = []cli.Flag{ legacyMiscFlags = []cli.Flag{
legacyPortFlag, legacyPortFlag,
legacyIPFlag, legacyIPFlag,
legacyWebHookConfigFlag,
} }
) )
@ -271,7 +281,7 @@ var (
Name: "dns", Name: "dns",
DefValue: true, DefValue: true,
Usage: "use dns for syncing", 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{ legacyNetworkTypeFlag = cli.StringFlag{
Name: "network_type", Name: "network_type",
@ -301,7 +311,7 @@ func applyNetworkFlags(cmd *cobra.Command, cfg *harmonyConfig) {
} else if cli.IsFlagChanged(cmd, legacyDNSFlag) { } else if cli.IsFlagChanged(cmd, legacyDNSFlag) {
val := cli.GetBoolFlagValue(cmd, legacyDNSFlag) val := cli.GetBoolFlagValue(cmd, legacyDNSFlag)
if val { if val {
cfg.Network.DNSZone = mainnetDnsZone cfg.Network.DNSZone = nodeconfig.GetDefaultDNSZone(defNetworkType)
} else { } else {
cfg.Network.LegacySyncing = true cfg.Network.LegacySyncing = true
} }
@ -316,9 +326,14 @@ func applyNetworkFlags(cmd *cobra.Command, cfg *harmonyConfig) {
// p2p flags // p2p flags
var ( var (
p2pIPFlag = cli.StringFlag{
Name: "p2p.ip",
Usage: "ip to listen for p2p protocols",
DefValue: defaultConfig.P2P.IP,
}
p2pPortFlag = cli.IntFlag{ p2pPortFlag = cli.IntFlag{
Name: "p2p.port", Name: "p2p.port",
Usage: "port to listen for p2p communication", Usage: "port to listen for p2p protocols",
DefValue: defaultConfig.P2P.Port, DefValue: defaultConfig.P2P.Port,
} }
p2pKeyFileFlag = cli.StringFlag{ p2pKeyFileFlag = cli.StringFlag{
@ -335,6 +350,10 @@ var (
) )
func applyP2PFlags(cmd *cobra.Command, config *harmonyConfig) { func applyP2PFlags(cmd *cobra.Command, config *harmonyConfig) {
if cli.IsFlagChanged(cmd, p2pIPFlag) {
config.P2P.IP = cli.GetStringFlagValue(cmd, p2pIPFlag)
}
if cli.IsFlagChanged(cmd, p2pPortFlag) { if cli.IsFlagChanged(cmd, p2pPortFlag) {
config.P2P.Port = cli.GetIntFlagValue(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 // bls flags
var ( var (
blsDirFlag = cli.StringFlag{ blsDirFlag = cli.StringFlag{
@ -616,6 +666,13 @@ var (
DefValue: defaultConfig.Consensus.BlockTime, DefValue: defaultConfig.Consensus.BlockTime,
Hidden: true, 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{ legacyDelayCommitFlag = cli.StringFlag{
Name: "delay_commit", Name: "delay_commit",
Usage: "how long to delay sending commit messages in consensus, ex: 500ms, 1s", Usage: "how long to delay sending commit messages in consensus, ex: 500ms, 1s",
@ -628,6 +685,12 @@ var (
DefValue: 8, DefValue: 8,
Deprecated: "use --consensus.block-time", 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) { func applyConsensusFlags(cmd *cobra.Command, config *harmonyConfig) {
@ -643,6 +706,12 @@ func applyConsensusFlags(cmd *cobra.Command, config *harmonyConfig) {
sec := cli.GetIntFlagValue(cmd, legacyBlockTimeFlag) sec := cli.GetIntFlagValue(cmd, legacyBlockTimeFlag)
config.Consensus.BlockTime = fmt.Sprintf("%ds", sec) 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 // transaction pool flags
@ -961,6 +1030,12 @@ var (
DefValue: defaultConfig.RPC.IP, DefValue: defaultConfig.RPC.IP,
Deprecated: "use --http.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 // 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) legacyPort := cli.GetIntFlagValue(cmd, legacyPortFlag)
config.P2P.Port = legacyPort config.P2P.Port = legacyPort
config.RPC.Port = legacyPort config.RPC.Port = legacyPort
config.WS.Port = legacyPort
} }
if cli.IsFlagChanged(cmd, legacyIPFlag) { 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.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) logIP := cli.GetStringFlagValue(cmd, legacyIPFlag)
logPort := cli.GetIntFlagValue(cmd, legacyPortFlag) logPort := cli.GetIntFlagValue(cmd, legacyPortFlag)
config.Log.FileName = fmt.Sprintf("validator-%v-%v.log", logIP, logPort) 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 config.Log.Context = logCtx
} }
if cli.IsFlagChanged(cmd, legacyWebHookConfigFlag) {
config.Legacy = &legacyConfig{
WebHookConfig: cli.GetStringFlagValue(cmd, legacyWebHookConfigFlag),
}
}
} }

@ -1,7 +1,6 @@
package main package main
import ( import (
"flag"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
@ -10,32 +9,32 @@ import (
_ "net/http/pprof" _ "net/http/pprof"
"os" "os"
"os/signal" "os/signal"
"path" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
"time" "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" 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/bls/ffi/go/bls"
"github.com/harmony-one/harmony/api/service/syncing" "github.com/harmony-one/harmony/api/service/syncing"
"github.com/harmony-one/harmony/consensus" "github.com/harmony-one/harmony/consensus"
"github.com/harmony-one/harmony/consensus/quorum" "github.com/harmony-one/harmony/consensus/quorum"
"github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core"
"github.com/harmony-one/harmony/internal/cli"
"github.com/harmony-one/harmony/internal/common" "github.com/harmony-one/harmony/internal/common"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node" nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding" 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/genesis"
"github.com/harmony-one/harmony/internal/shardchain" "github.com/harmony-one/harmony/internal/shardchain"
"github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/multibls" "github.com/harmony-one/harmony/multibls"
"github.com/harmony-one/harmony/node" "github.com/harmony-one/harmony/node"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/p2p" "github.com/harmony-one/harmony/p2p"
"github.com/harmony-one/harmony/shard" "github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/webhooks" "github.com/harmony-one/harmony/webhooks"
@ -48,71 +47,315 @@ var (
initialAccounts = []*genesis.DeployAccount{} initialAccounts = []*genesis.DeployAccount{}
) )
func printVersion() { var rootCmd = &cobra.Command{
fmt.Fprintln(os.Stdout, nodeconfig.GetVersion()) // TODO: elaborate the usage
os.Exit(0) Use: "harmony",
Short: "run harmony node",
Long: "run harmony node",
Run: runHarmonyNode,
} }
//ip = flag.String("ip", "127.0.0.1", "ip of the node") var configFlag = cli.StringFlag{
//port = flag.String("port", "9000", "port of the node.") Name: "config",
//logFolder = flag.String("log_folder", "latest", "the folder collecting the logs of this execution") Usage: "load node config from the config toml file.",
//logMaxSize = flag.Int("log_max_size", 100, "the max size in megabytes of the log file before it gets rotated") Shorthand: "c",
//// Remove this flag DefValue: "",
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",
//)
func init() { func init() {
if err := registerRootCmdFlags(); err != nil {
// Only happens when code is wrong
panic(err)
}
cli.SetParseErrorHandle(func(err error) { cli.SetParseErrorHandle(func(err error) {
os.Exit(128) // 128 - invalid command line arguments 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) { 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)) genesisShardingConfig := shard.Schedule.InstanceForEpoch(big.NewInt(core.GenesisEpoch))
multiBLSPubKey := setupConsensusKeys(nodeconfig.GetDefaultConfig()) multiBLSPubKey := setupConsensusKeys(hc, nodeconfig.GetDefaultConfig())
reshardingEpoch := genesisShardingConfig.ReshardingEpoch() reshardingEpoch := genesisShardingConfig.ReshardingEpoch()
if len(reshardingEpoch) > 0 { if len(reshardingEpoch) > 0 {
@ -157,8 +400,8 @@ func setupLegacyNodeAccount() error {
return nil return nil
} }
func setupStakingNodeAccount() error { func setupStakingNodeAccount(hc harmonyConfig) error {
pubKeys := setupConsensusKeys(nodeconfig.GetDefaultConfig()) pubKeys := setupConsensusKeys(hc, nodeconfig.GetDefaultConfig())
shardID, err := nodeconfig.GetDefaultConfig().ShardIDFromConsensusKey() shardID, err := nodeconfig.GetDefaultConfig().ShardIDFromConsensusKey()
if err != nil { if err != nil {
return errors.Wrap(err, "cannot determine shard to join") return errors.Wrap(err, "cannot determine shard to join")
@ -178,51 +421,36 @@ func setupStakingNodeAccount() error {
return nil return nil
} }
// setupConsensusKeys load bls keys and set the keys to nodeConfig. Return the loaded public keys. func createGlobalConfig(hc harmonyConfig) (*nodeconfig.ConfigType, error) {
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) {
var err error var err error
if len(initialAccounts) == 0 { 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) nodeConfig := nodeconfig.GetShardConfig(initialAccounts[0].ShardID)
if *nodeType == "validator" { if hc.General.NodeType == nodeTypeValidator {
// Set up consensus keys. // Set up consensus keys.
setupConsensusKeys(nodeConfig) setupConsensusKeys(hc, nodeConfig)
} else { } else {
// set dummy bls key for consensus object // set dummy bls key for consensus object
nodeConfig.ConsensusPriKey = multibls.GetPrivateKeys(&bls.SecretKey{}) nodeConfig.ConsensusPriKey = multibls.GetPrivateKeys(&bls.SecretKey{})
} }
// Set network type // Set network type
netType := nodeconfig.NetworkType(*networkType) netType := nodeconfig.NetworkType(hc.Network.NetworkType)
nodeconfig.SetNetworkType(netType) // sets for both global and shard configs 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. // 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 { if err != nil {
return nil, errors.Wrapf(err, "cannot load or create P2P key at %#v", return nil, errors.Wrapf(err, "cannot load or create P2P key at %#v",
*keyFile) hc.P2P.KeyFile)
} }
selfPeer := p2p.Peer{ selfPeer := p2p.Peer{
IP: *ip, IP: hc.P2P.IP,
Port: *port, Port: strconv.Itoa(hc.P2P.Port),
ConsensusPubKey: nodeConfig.ConsensusPriKey[0].Pub.Object, 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") 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) config, err := webhooks.NewWebHooksFromPath(p)
if err != nil { if err != nil {
fmt.Fprintf( fmt.Fprintf(
@ -247,10 +476,10 @@ func createGlobalConfig() (*nodeconfig.ConfigType, error) {
return nodeConfig, nil return nodeConfig, nil
} }
func setupConsensusAndNode(nodeConfig *nodeconfig.ConfigType) *node.Node { func setupConsensusAndNode(hc harmonyConfig, nodeConfig *nodeconfig.ConfigType) *node.Node {
// Consensus object. // Consensus object.
// TODO: consensus object shouldn't start here // 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( currentConsensus, err := consensus.New(
myHost, nodeConfig.ShardID, p2p.Peer{}, nodeConfig.ConsensusPriKey, decider, 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) _, _ = fmt.Fprintf(os.Stderr, "Error :%v \n", err)
os.Exit(1) os.Exit(1)
} }
commitDelay, err := time.ParseDuration(*delayCommit) commitDelay, err := time.ParseDuration(hc.Consensus.DelayCommit)
if err != nil || commitDelay < 0 { 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) os.Exit(1)
} }
currentConsensus.SetCommitDelay(commitDelay) currentConsensus.SetCommitDelay(commitDelay)
currentConsensus.MinPeers = *minPeers currentConsensus.MinPeers = hc.Consensus.MinPeers
blacklist, err := setupBlacklist() blacklist, err := setupBlacklist(hc)
if err != nil { if err != nil {
utils.Logger().Warn().Msgf("Blacklist setup error: %s", err.Error()) utils.Logger().Warn().Msgf("Blacklist setup error: %s", err.Error())
} }
@ -279,44 +508,37 @@ func setupConsensusAndNode(nodeConfig *nodeconfig.ConfigType) *node.Node {
// Current node. // Current node.
chainDBFactory := &shardchain.LDBFactory{RootDir: nodeConfig.DBDir} chainDBFactory := &shardchain.LDBFactory{RootDir: nodeConfig.DBDir}
currentNode := node.New(myHost, currentConsensus, chainDBFactory, blacklist, *isArchival) currentNode := node.New(myHost, currentConsensus, chainDBFactory, blacklist, hc.General.IsArchival)
currentNode.BroadcastInvalidTx = *broadcastInvalidTx currentNode.BroadcastInvalidTx = hc.TxPool.BroadcastInvalidTx
switch { if hc.Network.LegacySyncing {
case *networkType == nodeconfig.Localnet: currentNode.SyncingPeerProvider = node.NewLegacySyncingPeerProvider(currentNode)
} else {
if hc.Network.NetworkType == nodeconfig.Localnet {
epochConfig := shard.Schedule.InstanceForEpoch(ethCommon.Big0) epochConfig := shard.Schedule.InstanceForEpoch(ethCommon.Big0)
selfPort, err := strconv.ParseUint(*port, 10, 16) selfPort := hc.Network.DNSPort
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( currentNode.SyncingPeerProvider = node.NewLocalSyncingPeerProvider(
6000, uint16(selfPort), epochConfig.NumShards(), uint32(epochConfig.NumNodesPerShard())) 6000, uint16(selfPort), epochConfig.NumShards(), uint32(epochConfig.NumNodesPerShard()))
case *dnsZone != "": } else {
currentNode.SyncingPeerProvider = node.NewDNSSyncingPeerProvider(*dnsZone, syncing.GetSyncingPort(*dnsPort)) currentNode.SyncingPeerProvider = node.NewDNSSyncingPeerProvider(hc.Network.DNSZone, syncing.GetSyncingPort(strconv.Itoa(hc.Network.DNSPort)))
case *dnsFlag: }
currentNode.SyncingPeerProvider = node.NewDNSSyncingPeerProvider("t.hmny.io", syncing.GetSyncingPort(*dnsPort))
default:
currentNode.SyncingPeerProvider = node.NewLegacySyncingPeerProvider(currentNode)
} }
// TODO: refactor the creation of blockchain out of node.New() // TODO: refactor the creation of blockchain out of node.New()
currentConsensus.ChainReader = currentNode.Blockchain() currentConsensus.ChainReader = currentNode.Blockchain()
currentNode.NodeConfig.DNSZone = *dnsZone currentNode.NodeConfig.DNSZone = hc.Network.DNSZone
currentNode.NodeConfig.SetBeaconGroupID( currentNode.NodeConfig.SetBeaconGroupID(
nodeconfig.NewGroupIDByShardID(shard.BeaconChainShardID), nodeconfig.NewGroupIDByShardID(shard.BeaconChainShardID),
) )
nodeconfig.GetDefaultConfig().DBDir = nodeConfig.DBDir nodeconfig.GetDefaultConfig().DBDir = nodeConfig.DBDir
switch *nodeType { switch hc.General.NodeType {
case "explorer": case nodeTypeExplorer:
nodeconfig.SetDefaultRole(nodeconfig.ExplorerNode) nodeconfig.SetDefaultRole(nodeconfig.ExplorerNode)
currentNode.NodeConfig.SetRole(nodeconfig.ExplorerNode) currentNode.NodeConfig.SetRole(nodeconfig.ExplorerNode)
case "validator":
case nodeTypeValidator:
nodeconfig.SetDefaultRole(nodeconfig.Validator) nodeconfig.SetDefaultRole(nodeconfig.Validator)
currentNode.NodeConfig.SetRole(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 // This needs to be executed after consensus setup
if err := currentNode.InitConsensusWithValidators(); err != nil { if err := currentNode.InitConsensusWithValidators(); err != nil {
utils.Logger().Warn(). utils.Logger().Warn().
Int("shardID", *shardID). Int("shardID", hc.General.ShardID).
Err(err). Err(err).
Msg("InitConsensusWithMembers failed") Msg("InitConsensusWithMembers failed")
} }
@ -345,14 +567,19 @@ func setupConsensusAndNode(nodeConfig *nodeconfig.ConfigType) *node.Node {
// update consensus information based on the blockchain // update consensus information based on the blockchain
currentConsensus.SetMode(currentConsensus.UpdateConsensusInformation()) currentConsensus.SetMode(currentConsensus.UpdateConsensusInformation())
// Setup block period and block due time. // 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() currentConsensus.NextBlockDue = time.Now()
return currentNode return currentNode
} }
func setupBlacklist() (map[ethCommon.Address]struct{}, error) { func setupBlacklist(hc harmonyConfig) (map[ethCommon.Address]struct{}, error) {
utils.Logger().Debug().Msgf("Using blacklist file at `%s`", *blacklistPath) utils.Logger().Debug().Msgf("Using blacklist file at `%s`", hc.TxPool.BlacklistFile)
dat, err := ioutil.ReadFile(*blacklistPath) dat, err := ioutil.ReadFile(hc.TxPool.BlacklistFile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -369,120 +596,3 @@ func setupBlacklist() (map[ethCommon.Address]struct{}, error) {
} }
return addrMap, nil 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)
}
}

@ -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)
}
}

@ -1 +0,0 @@
package main

@ -2,6 +2,9 @@ package main
import ( import (
"fmt" "fmt"
"os"
"github.com/spf13/cobra"
) )
const ( const (
@ -16,8 +19,21 @@ var (
commit string 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 { func getHarmonyVersion() string {
return fmt.Sprintf(versionFormat, "harmony", version, commit, builtBy, builtAt) return fmt.Sprintf(versionFormat, "harmony", version, commit, builtBy, builtAt)
} }
// TODO add version command here func printVersion() {
fmt.Println(getHarmonyVersion())
}

@ -43,9 +43,12 @@ const (
// very bad design. Will refactor later // very bad design. Will refactor later
// TODO: refactor all 9000-3000 = 6000 stuff // TODO: refactor all 9000-3000 = 6000 stuff
DefaultDNSPort = 9000 DefaultDNSPort = 9000
// DefaultRPCPort is the default rpc port. The actual port used is 9000+500, which is a terrible // DefaultRPCPort is the default rpc port. The actual port used is 9000+500
// design. // TODO: modify this
DefaultRPCPort = 9000 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 // GetDefaultBootNodes get the default bootnode with the given network type
@ -79,6 +82,7 @@ func GetDefaultDNSZone(networkType NetworkType) string {
case Stressnet: case Stressnet:
return stressnetDNSZone return stressnetDNSZone
} }
return ""
} }
// GetDefaultDNSPort get the default DNS port for the given network type // GetDefaultDNSPort get the default DNS port for the given network type

Loading…
Cancel
Save