From de0fedb512e6c0be3475157d47b44697f2110035 Mon Sep 17 00:00:00 2001 From: Jin Date: Thu, 14 Dec 2023 09:33:22 +0800 Subject: [PATCH 1/9] Otpimize config --- services/common/flags.go | 670 +++++++++++++++++++++++++++++ services/common/load.go | 884 +++++---------------------------------- 2 files changed, 769 insertions(+), 785 deletions(-) create mode 100644 services/common/flags.go diff --git a/services/common/flags.go b/services/common/flags.go new file mode 100644 index 00000000..92208919 --- /dev/null +++ b/services/common/flags.go @@ -0,0 +1,670 @@ +// Copyright (c) 2017-2018 The qitmeer developers +// Copyright (c) 2015-2016 The Decred developers +// Copyright (c) 2013-2016 The btcsuite developers + +package common + +import ( + "path/filepath" + "time" + + "github.com/Qitmeer/qng/common/profiling" + "github.com/Qitmeer/qng/common/util" + "github.com/Qitmeer/qng/config" + "github.com/Qitmeer/qng/core/types" + "github.com/urfave/cli/v2" +) + +const ( + defaultConfigFilename = "qng.conf" + defaultDataDirname = "data" + defaultLogLevel = "info" + defaultDebugPrintOrigins = false + defaultLogDirname = "logs" + defaultLogFilename = "qng.log" + defaultGenerate = false + defaultBlockMinSize = 0 + defaultBlockMaxSize = types.MaxBlockPayload / 2 + defaultMaxRPCClients = 10 + defaultMaxRPCWebsockets = 25 + defaultMaxRPCConcurrentReqs = 20 + defaultMaxPeers = 50 + defaultMiningStateSync = false + defaultMaxInboundPeersPerHost = 25 // The default max total of inbound peer for host + defaultTrickleInterval = 10 * time.Second + defaultInvalidTxIndex = false + defaultTxhashIndex = false + defaultMempoolExpiry = int64(time.Hour) + defaultRPCUser = "test" + defaultRPCPass = "test" + defaultMinBlockPruneSize = 2000 + defaultMinBlockDataCache = 2000 + defaultMinRelayTxFee = int64(1e4) +) +const ( + defaultSigCacheMaxSize = 100000 +) +const ( + defaultMaxOrphanTxSize = 5000 +) + +var ( + defaultHomeDir = util.AppDataDir("qng", false) + defaultConfigFile = filepath.Join(defaultHomeDir, defaultConfigFilename) + defaultDataDir = filepath.Join(defaultHomeDir, defaultDataDirname) + defaultDbType = "ffldb" + defaultLogDir = filepath.Join(defaultHomeDir, defaultLogDirname) + defaultLogRotatorSize = int64(1024 * 10) + defaultRPCKeyFile = filepath.Join(defaultHomeDir, "rpc.key") + defaultRPCCertFile = filepath.Join(defaultHomeDir, "rpc.cert") + defaultDAGType = "phantom" +) + +var ( + cfg = DefaultConfig("") + + RPCListeners cli.StringSlice + Modules cli.StringSlice + MiningAddrs cli.StringSlice + BlockMinSize uint + BlockMaxSize uint + BlockPrioritySize uint + AddPeers cli.StringSlice + BootstrapNodes cli.StringSlice + Whitelist cli.StringSlice + Blacklist cli.StringSlice + GBTNotify cli.StringSlice + + Flags = []cli.Flag{ + &cli.StringFlag{ + Name: "appdata", + Aliases: []string{"A"}, + Usage: "Path to application home directory", + Value: defaultHomeDir, + Destination: &cfg.HomeDir, + }, + &cli.BoolFlag{ + Name: "ShowVersion", + Aliases: []string{"V"}, + Usage: "Display version information and exit", + Destination: &cfg.ShowVersion, + }, + &cli.StringFlag{ + Name: "configfile", + Aliases: []string{"C"}, + Usage: "Path to configuration file", + Value: defaultConfigFile, + Destination: &cfg.ConfigFile, + }, + &cli.StringFlag{ + Name: "datadir", + Aliases: []string{"b"}, + Usage: "Directory to store data", + Value: defaultDataDir, + Destination: &cfg.DataDir, + }, + &cli.StringFlag{ + Name: "logdir", + Usage: "Directory to log output.", + Value: defaultLogDir, + Destination: &cfg.LogDir, + }, + &cli.Int64Flag{ + Name: "logrotatorsize", + Usage: "Directory to log output.", + Value: defaultLogRotatorSize, + Destination: &cfg.LogRotatorSize, + }, + &cli.BoolFlag{ + Name: "nofilelogging", + Usage: "Disable file logging.", + Destination: &cfg.NoFileLogging, + }, + &cli.StringFlag{ + Name: "listen", + Usage: "Add an IP to listen for connections", + Destination: &cfg.Listener, + }, + &cli.StringFlag{ + Name: "port", + Usage: "Default p2p port.", + Destination: &cfg.DefaultPort, + }, + &cli.StringSliceFlag{ + Name: "rpclisten", + Usage: "Add an interface/port to listen for RPC connections", + Destination: &RPCListeners, + }, + &cli.IntFlag{ + Name: "maxpeers", + Usage: "Max number of inbound and outbound peers", + Value: defaultMaxPeers, + Destination: &cfg.MaxPeers, + }, + &cli.BoolFlag{ + Name: "nolisten", + Usage: "Disable listening for incoming connections", + Destination: &cfg.DisableListen, + }, + &cli.StringFlag{ + Name: "rpcuser", + Aliases: []string{"u"}, + Usage: "Username for RPC connections", + Value: defaultRPCUser, + Destination: &cfg.RPCUser, + }, + &cli.StringFlag{ + Name: "rpcpass", + Aliases: []string{"P"}, + Usage: "Password for RPC connections", + Value: defaultRPCPass, + Destination: &cfg.RPCPass, + }, + &cli.StringFlag{ + Name: "rpccert", + Usage: "File containing the certificate file", + Value: defaultRPCCertFile, + Destination: &cfg.RPCCert, + }, + &cli.StringFlag{ + Name: "rpckey", + Usage: "File containing the certificate key", + Value: defaultRPCKeyFile, + Destination: &cfg.RPCKey, + }, + &cli.IntFlag{ + Name: "rpcmaxclients", + Usage: "Max number of RPC clients for standard connections", + Value: defaultMaxRPCClients, + Destination: &cfg.RPCMaxClients, + }, + &cli.BoolFlag{ + Name: "norpc", + Usage: "Disable built-in RPC server -- NOTE: The RPC server is disabled by default if no rpcuser/rpcpass or rpclimituser/rpclimitpass is specified", + Destination: &cfg.DisableRPC, + }, + &cli.BoolFlag{ + Name: "notls", + Usage: "Disable TLS for the RPC server -- NOTE: This is only allowed if the RPC server is bound to localhost", + Destination: &cfg.DisableTLS, + }, + &cli.StringSliceFlag{ + Name: "modules", + Usage: "Modules is a list of API modules(See GetNodeInfo) to expose via the HTTP RPC interface. If the module list is empty, all RPC API endpoints designated public will be exposed.", + Destination: &Modules, + }, + &cli.BoolFlag{ + Name: "nocheckpoints", + Usage: "Disable built-in checkpoints. Don't do this unless you know what you're doing.", + Destination: &cfg.DisableCheckpoints, + }, + &cli.BoolFlag{ + Name: "addrindex", + Usage: "Maintain a full address-based transaction index which makes the getrawtransactions RPC available", + Destination: &cfg.AddrIndex, + }, + &cli.BoolFlag{ + Name: "dropaddrindex", + Usage: "Deletes the address-based transaction index from the database on start up and then exits.", + Destination: &cfg.DropAddrIndex, + }, + &cli.BoolFlag{ + Name: "light", + Usage: "start as a qitmeer light node", + Destination: &cfg.LightNode, + }, + &cli.UintFlag{ + Name: "sigcachemaxsize", + Usage: "The maximum number of entries in the signature verification cache", + Value: defaultSigCacheMaxSize, + Destination: &cfg.SigCacheMaxSize, + }, + &cli.BoolFlag{ + Name: "testnet", + Usage: "Use the test network", + Destination: &cfg.TestNet, + }, + &cli.BoolFlag{ + Name: "mixnet", + Usage: "Use the test mix pow network", + Destination: &cfg.MixNet, + }, + &cli.BoolFlag{ + Name: "privnet", + Usage: "Use the private network", + Destination: &cfg.PrivNet, + }, + &cli.StringFlag{ + Name: "dbtype", + Usage: "Database backend to use for the Block Chain", + Value: defaultDbType, + Destination: &cfg.DbType, + }, + &cli.StringFlag{ + Name: "profile", + Usage: "Enable HTTP profiling on given [addr:]port -- NOTE port must be between 1024 and 65536", + Destination: &cfg.Profile, + }, + &cli.StringFlag{ + Name: "cpuprofile", + Usage: "Write CPU profile to the specified file", + Destination: &cfg.CPUProfile, + }, + &cli.BoolFlag{ + Name: "trackheap", + Usage: "tracks the size of the heap and dumps a profile", + Destination: &cfg.TrackHeap, + }, + &cli.IntFlag{ + Name: "trackheaplimit", + Usage: "track heap when limit in gigabytes (default:7G)", + Destination: &cfg.TrackHeapLimit, + Value: profiling.DefaultTrackHeapLimit, + }, + &cli.StringFlag{ + Name: "debuglevel", + Aliases: []string{"d"}, + Usage: "Logging level {trace, debug, info, warn, error, critical}", + Value: defaultLogLevel, + Destination: &cfg.DebugLevel, + }, + &cli.BoolFlag{ + Name: "printorigin", + Usage: "Print log debug location (file:line)", + Destination: &cfg.DebugPrintOrigins, + }, + &cli.BoolFlag{ + Name: "norelaypriority", + Usage: "Do not require free or low-fee transactions to have high priority for relaying", + Destination: &cfg.NoRelayPriority, + }, + &cli.Float64Flag{ + Name: "limitfreerelay", + Usage: "Limit relay of transactions with no transaction fee to the given amount in thousands of bytes per minute", + Destination: &cfg.FreeTxRelayLimit, + }, + &cli.BoolFlag{ + Name: "acceptnonstd", + Usage: "Accept and relay non-standard transactions to the network regardless of the default settings for the active network.", + Destination: &cfg.AcceptNonStd, + Value: true, + }, + &cli.IntFlag{ + Name: "maxorphantx", + Usage: "Max number of orphan transactions to keep in memory", + Destination: &cfg.MaxOrphanTxs, + }, + &cli.Int64Flag{ + Name: "mintxfee", + Usage: "The minimum transaction fee in AtomMEER/kB.", + Value: defaultMinRelayTxFee, + Destination: &cfg.MinTxFee, + }, + &cli.Int64Flag{ + Name: "mempoolexpiry", + Usage: "Do not keep transactions in the mempool more than mempoolexpiry", + Value: defaultMempoolExpiry, + Destination: &cfg.MempoolExpiry, + }, + &cli.BoolFlag{ + Name: "persistmempool", + Usage: "Whether to save the mempool on shutdown and load on restart", + Destination: &cfg.Persistmempool, + }, + &cli.BoolFlag{ + Name: "nomempoolbar", + Usage: "Whether to show progress bar when load mempool from file", + Destination: &cfg.NoMempoolBar, + }, + &cli.BoolFlag{ + Name: "miner", + Usage: "Enable miner module", + Destination: &cfg.Miner, + }, + &cli.BoolFlag{ + Name: "generate", + Usage: "Generate (mine) coins using the CPU", + Destination: &cfg.Generate, + }, + &cli.StringSliceFlag{ + Name: "miningaddr", + Usage: "Add the specified payment address to the list of addresses to use for generated blocks -- At least one address is required if the generate option is set", + Destination: &MiningAddrs, + }, + &cli.IntFlag{ + Name: "miningtimeoffset", + Usage: "Offset the mining timestamp of a block by this many seconds (positive values are in the past)", + Destination: &cfg.MiningTimeOffset, + }, + &cli.UintFlag{ + Name: "blockminsize", + Usage: "Mininum block size in bytes to be used when creating a block", + Value: defaultBlockMinSize, + Destination: &BlockMinSize, + }, + &cli.UintFlag{ + Name: "blockmaxsize", + Usage: "Maximum block size in bytes to be used when creating a block", + Value: defaultBlockMaxSize, + Destination: &BlockMaxSize, + }, + &cli.UintFlag{ + Name: "blockprioritysize", + Usage: "Size in bytes for high-priority/low-fee transactions when creating a block", + Destination: &BlockPrioritySize, + }, + &cli.IntFlag{ + Name: "rpcmaxwebsockets", + Usage: "Max number of RPC websocket connections", + Value: defaultMaxRPCWebsockets, + Destination: &cfg.RPCMaxWebsockets, + }, + &cli.IntFlag{ + Name: "rpcmaxconcurrentreqs", + Usage: "Max number of concurrent RPC requests that may be processed concurrently", + Value: defaultMaxRPCConcurrentReqs, + Destination: &cfg.RPCMaxConcurrentReqs, + }, + &cli.BoolFlag{ + Name: "blocksonly", + Usage: "Do not accept transactions from remote peers", + Destination: &cfg.BlocksOnly, + }, + &cli.BoolFlag{ + Name: "miningstatesync", + Usage: "Synchronizing the mining state with other nodes", + Destination: &cfg.MiningStateSync, + }, + &cli.StringSliceFlag{ + Name: "addpeer", + Aliases: []string{"a"}, + Usage: "Add a peer to connect with at startup", + Destination: &AddPeers, + }, + &cli.BoolFlag{ + Name: "upnp", + Usage: "Use UPnP to map our listening port outside of NAT", + Destination: &cfg.Upnp, + }, + &cli.IntFlag{ + Name: "maxinbound", + Usage: "The max total of inbound peer for host", + Value: defaultMaxInboundPeersPerHost, + Destination: &cfg.MaxInbound, + }, + &cli.BoolFlag{ + Name: "banning", + Usage: "Enable banning of misbehaving peers", + Destination: &cfg.Banning, + Value: true, + }, + &cli.StringFlag{ + Name: "dagtype", + Aliases: []string{"G"}, + Usage: "DAG type {phantom,spectre}", + Value: defaultDAGType, + Destination: &cfg.DAGType, + }, + &cli.BoolFlag{ + Name: "cleanup", + Aliases: []string{"L"}, + Usage: "Cleanup the block database", + Destination: &cfg.Cleanup, + }, + &cli.BoolFlag{ + Name: "buildledger", + Usage: "Generate the genesis ledger for the next qitmeer version", + Destination: &cfg.BuildLedger, + }, + &cli.StringFlag{ + Name: "zmqpubhashblock", + Usage: "Enable publish hash block in
", + Destination: &cfg.Zmqpubhashblock, + }, + &cli.StringFlag{ + Name: "zmqpubrawblock", + Usage: "Enable publish raw block in
", + Destination: &cfg.Zmqpubrawblock, + }, + &cli.StringFlag{ + Name: "zmqpubhashtx", + Usage: "Enable publish hash transaction in
", + Destination: &cfg.Zmqpubhashtx, + }, + &cli.StringFlag{ + Name: "zmqpubrawtx", + Usage: "Enable publish raw transaction in
", + Destination: &cfg.Zmqpubrawtx, + }, + &cli.BoolFlag{ + Name: "invalidtxindex", + Usage: "invalid transaction index.", + Destination: &cfg.InvalidTxIndex, + }, + &cli.BoolFlag{ + Name: "txhashindex", + Usage: "Cache transaction full hash.", + Destination: &cfg.TxHashIndex, + }, + &cli.BoolFlag{ + Name: "ntp", + Usage: "Auto sync time.", + Destination: &cfg.NTP, + }, + &cli.StringSliceFlag{ + Name: "bootstrapnode", + Usage: "The address of bootstrap node.", + Destination: &BootstrapNodes, + }, + &cli.BoolFlag{ + Name: "nodiscovery", + Usage: "Enable only local network p2p and do not connect to cloud bootstrap nodes.", + Destination: &cfg.NoDiscovery, + }, + &cli.StringFlag{ + Name: "metadatadir", + Usage: "meta data dir for p2p", + Destination: &cfg.MetaDataDir, + }, + &cli.IntFlag{ + Name: "p2pudpport", + Usage: "The udp port used by P2P", + Destination: &cfg.P2PUDPPort, + }, + &cli.IntFlag{ + Name: "p2ptcpport", + Usage: "The tcp port used by P2P.", + Destination: &cfg.P2PTCPPort, + }, + &cli.StringFlag{ + Name: "externalip", + Usage: "The IP address advertised by libp2p. This may be used to advertise an external IP.", + Destination: &cfg.HostIP, + }, + &cli.StringFlag{ + Name: "externaldns", + Usage: "The DNS address advertised by libp2p. This may be used to advertise an external DNS.", + Destination: &cfg.HostDNS, + }, + &cli.StringFlag{ + Name: "relaynode", + Usage: "The address of relay node that routes traffic between two peers over a qitmeer “relay” peer.", + Destination: &cfg.RelayNode, + }, + &cli.StringSliceFlag{ + Name: "whitelist", + Usage: "Add an IP network or IP,PeerID that will not be banned or ignore dual channel mode detection. (eg. 192.168.1.0/24 or ::1 or [peer id])", + Destination: &Whitelist, + }, + &cli.StringSliceFlag{ + Name: "blacklist", + Usage: "Add some IP network or IP that will be banned. (eg. 192.168.1.0/24 or ::1)", + Destination: &Blacklist, + }, + &cli.IntFlag{ + Name: "maxbadresp", + Usage: "maxbadresp is the maximum number of bad responses from a peer before we stop talking to it.", + Destination: &cfg.MaxBadResp, + }, + &cli.BoolFlag{ + Name: "circuit", + Usage: "All peers will ignore dual channel mode detection", + Destination: &cfg.Circuit, + Value: true, + }, + &cli.StringFlag{ + Name: "evmenv", + Usage: "meer EVM environment", + Destination: &cfg.EVMEnv, + }, + &cli.BoolFlag{ + Name: "estimatefee", + Usage: "Enable estimate fee", + Destination: &cfg.Estimatefee, + }, + &cli.StringSliceFlag{ + Name: "gbtnotify", + Usage: "HTTP URL list to be notified of new block template", + Destination: &GBTNotify, + }, + &cli.BoolFlag{ + Name: "acctmode", + Usage: "Enable support account system mode", + Destination: &cfg.AcctMode, + }, + &cli.Uint64Flag{ + Name: "dagcachesize", + Usage: "DAG block cache size", + Value: defaultMinBlockPruneSize, + Destination: &cfg.DAGCacheSize, + }, + &cli.Uint64Flag{ + Name: "bdcachesize", + Usage: "Block data cache size", + Value: defaultMinBlockDataCache, + Destination: &cfg.BlockDataCacheSize, + }, + &cli.StringFlag{ + Name: "amanaenv", + Usage: "Amana environment", + Destination: &cfg.AmanaEnv, + }, + &cli.BoolFlag{ + Name: "amana", + Usage: "Enable Amana", + Destination: &cfg.Amana, + }, + &cli.BoolFlag{ + Name: "consistency", + Usage: "Detect data consistency through P2P", + Destination: &cfg.Consistency, + Value: true, + }, + &cli.BoolFlag{ + Name: "metrics", + Usage: "Enable metrics collection and reporting", + Destination: &cfg.Metrics, + }, + &cli.BoolFlag{ + Name: "metrics.expensive", + Usage: "Enable expensive metrics collection and reporting", + Destination: &cfg.MetricsExpensive, + }, + &cli.Uint64Flag{ + Name: "minfreedisk", + Usage: "Minimum free disk space in MB, once reached triggers auto shut down (default = 512M, 0 = disabled)", + Value: 512, + Destination: &cfg.Minfreedisk, + }, + &cli.IntFlag{ + Name: "cache", + Usage: "Megabytes of memory allocated to internal caching (default = 1024 mainnet full node)", + Value: 1024, + Destination: &cfg.Cache, + }, + &cli.IntFlag{ + Name: "cache.database", + Usage: "Percentage of cache memory allowance to use for database io", + Value: 50, + Destination: &cfg.CacheDatabase, + }, + &cli.IntFlag{ + Name: "cache.snapshot", + Usage: "Percentage of cache memory allowance to use for snapshot caching (default = 10% full mode, 20% archive mode)", + Value: 0, + Destination: &cfg.CacheSnapshot, + }, + &cli.BoolFlag{ + Name: "devnextgdb", + Usage: "Enable next generation databases that only exist in development mode", + Destination: &cfg.DevNextGDB, + }, + &cli.BoolFlag{ + Name: "autocollectevm", + Usage: "auto collect miner coinbase utxo to evm", + Destination: &cfg.AutoCollectEvm, + }, + &cli.StringFlag{ + Name: "walletpass", + Usage: "wallet password", + Destination: &cfg.WalletPass, + }, + &cli.IntFlag{ + Name: "evmtrietimeout", + Usage: "Set the interval time(seconds) for flush evm trie to disk", + Destination: &cfg.EVMTrieTimeout, + }, + &cli.StringFlag{ + Name: "state.scheme", + Usage: "Scheme to use for storing ethereum state ('hash' or 'path')", + Destination: &cfg.StateScheme, + }, + } +) + +func DefaultConfig(homeDir string) *config.Config { + cfg := &config.Config{ + HomeDir: defaultHomeDir, + ConfigFile: defaultConfigFile, + DebugLevel: defaultLogLevel, + DebugPrintOrigins: defaultDebugPrintOrigins, + DataDir: defaultDataDir, + LogDir: defaultLogDir, + DbType: defaultDbType, + RPCKey: defaultRPCKeyFile, + RPCCert: defaultRPCCertFile, + RPCMaxClients: defaultMaxRPCClients, + RPCMaxWebsockets: defaultMaxRPCWebsockets, + RPCMaxConcurrentReqs: defaultMaxRPCConcurrentReqs, + Generate: defaultGenerate, + MaxPeers: defaultMaxPeers, + MinTxFee: defaultMinRelayTxFee, + BlockMinSize: defaultBlockMinSize, + BlockMaxSize: defaultBlockMaxSize, + SigCacheMaxSize: defaultSigCacheMaxSize, + MiningStateSync: defaultMiningStateSync, + DAGType: defaultDAGType, + Banning: true, + MaxInbound: defaultMaxInboundPeersPerHost, + InvalidTxIndex: defaultInvalidTxIndex, + TxHashIndex: defaultTxhashIndex, + NTP: false, + MempoolExpiry: defaultMempoolExpiry, + AcceptNonStd: true, + RPCUser: defaultRPCUser, + RPCPass: defaultRPCPass, + } + if len(homeDir) > 0 { + hd, err := filepath.Abs(homeDir) + if err != nil { + panic(err) + } + cfg.HomeDir = hd + cfg.ConfigFile = filepath.Join(cfg.HomeDir, defaultConfigFilename) + cfg.DataDir = filepath.Join(cfg.HomeDir, defaultDataDirname) + cfg.LogDir = filepath.Join(cfg.HomeDir, defaultLogDirname) + cfg.RPCKey = filepath.Join(cfg.HomeDir, "rpc.key") + cfg.RPCCert = filepath.Join(cfg.HomeDir, "rpc.cert") + } + return cfg +} diff --git a/services/common/load.go b/services/common/load.go index bd9f772a..8b37f290 100644 --- a/services/common/load.go +++ b/services/common/load.go @@ -6,633 +6,22 @@ package common import ( "fmt" - "net" - "os" - "path/filepath" - "runtime" - "strconv" - "strings" - "time" - - "github.com/Qitmeer/qng/common/profiling" "github.com/Qitmeer/qng/common/roughtime" "github.com/Qitmeer/qng/common/util" "github.com/Qitmeer/qng/config" "github.com/Qitmeer/qng/core/address" - "github.com/Qitmeer/qng/core/types" "github.com/Qitmeer/qng/log" "github.com/Qitmeer/qng/params" "github.com/Qitmeer/qng/version" gp "github.com/howeyc/gopass" "github.com/jessevdk/go-flags" "github.com/urfave/cli/v2" -) - -const ( - defaultConfigFilename = "qng.conf" - defaultDataDirname = "data" - defaultLogLevel = "info" - defaultDebugPrintOrigins = false - defaultLogDirname = "logs" - defaultLogFilename = "qng.log" - defaultGenerate = false - defaultBlockMinSize = 0 - defaultBlockMaxSize = types.MaxBlockPayload / 2 - defaultMaxRPCClients = 10 - defaultMaxRPCWebsockets = 25 - defaultMaxRPCConcurrentReqs = 20 - defaultMaxPeers = 50 - defaultMiningStateSync = false - defaultMaxInboundPeersPerHost = 25 // The default max total of inbound peer for host - defaultTrickleInterval = 10 * time.Second - defaultInvalidTxIndex = false - defaultTxhashIndex = false - defaultMempoolExpiry = int64(time.Hour) - defaultRPCUser = "test" - defaultRPCPass = "test" - defaultMinBlockPruneSize = 2000 - defaultMinBlockDataCache = 2000 - defaultMinRelayTxFee = int64(1e4) -) -const ( - defaultSigCacheMaxSize = 100000 -) -const ( - defaultMaxOrphanTxSize = 5000 -) - -var ( - defaultHomeDir = util.AppDataDir("qng", false) - defaultConfigFile = filepath.Join(defaultHomeDir, defaultConfigFilename) - defaultDataDir = filepath.Join(defaultHomeDir, defaultDataDirname) - defaultDbType = "ffldb" - defaultLogDir = filepath.Join(defaultHomeDir, defaultLogDirname) - defaultLogRotatorSize = int64(1024 * 10) - defaultRPCKeyFile = filepath.Join(defaultHomeDir, "rpc.key") - defaultRPCCertFile = filepath.Join(defaultHomeDir, "rpc.cert") - defaultDAGType = "phantom" -) - -var ( - cfg = DefaultConfig("") - - RPCListeners cli.StringSlice - Modules cli.StringSlice - MiningAddrs cli.StringSlice - BlockMinSize uint - BlockMaxSize uint - BlockPrioritySize uint - AddPeers cli.StringSlice - BootstrapNodes cli.StringSlice - Whitelist cli.StringSlice - Blacklist cli.StringSlice - GBTNotify cli.StringSlice - - Flags = []cli.Flag{ - &cli.StringFlag{ - Name: "appdata", - Aliases: []string{"A"}, - Usage: "Path to application home directory", - Value: defaultHomeDir, - Destination: &cfg.HomeDir, - }, - &cli.BoolFlag{ - Name: "ShowVersion", - Aliases: []string{"V"}, - Usage: "Display version information and exit", - Destination: &cfg.ShowVersion, - }, - &cli.StringFlag{ - Name: "configfile", - Aliases: []string{"C"}, - Usage: "Path to configuration file", - Value: defaultConfigFile, - Destination: &cfg.ConfigFile, - }, - &cli.StringFlag{ - Name: "datadir", - Aliases: []string{"b"}, - Usage: "Directory to store data", - Value: defaultDataDir, - Destination: &cfg.DataDir, - }, - &cli.StringFlag{ - Name: "logdir", - Usage: "Directory to log output.", - Value: defaultLogDir, - Destination: &cfg.LogDir, - }, - &cli.Int64Flag{ - Name: "logrotatorsize", - Usage: "Directory to log output.", - Value: defaultLogRotatorSize, - Destination: &cfg.LogRotatorSize, - }, - &cli.BoolFlag{ - Name: "nofilelogging", - Usage: "Disable file logging.", - Destination: &cfg.NoFileLogging, - }, - &cli.StringFlag{ - Name: "listen", - Usage: "Add an IP to listen for connections", - Destination: &cfg.Listener, - }, - &cli.StringFlag{ - Name: "port", - Usage: "Default p2p port.", - Destination: &cfg.DefaultPort, - }, - &cli.StringSliceFlag{ - Name: "rpclisten", - Usage: "Add an interface/port to listen for RPC connections", - Destination: &RPCListeners, - }, - &cli.IntFlag{ - Name: "maxpeers", - Usage: "Max number of inbound and outbound peers", - Value: defaultMaxPeers, - Destination: &cfg.MaxPeers, - }, - &cli.BoolFlag{ - Name: "nolisten", - Usage: "Disable listening for incoming connections", - Destination: &cfg.DisableListen, - }, - &cli.StringFlag{ - Name: "rpcuser", - Aliases: []string{"u"}, - Usage: "Username for RPC connections", - Value: defaultRPCUser, - Destination: &cfg.RPCUser, - }, - &cli.StringFlag{ - Name: "rpcpass", - Aliases: []string{"P"}, - Usage: "Password for RPC connections", - Value: defaultRPCPass, - Destination: &cfg.RPCPass, - }, - &cli.StringFlag{ - Name: "rpccert", - Usage: "File containing the certificate file", - Value: defaultRPCCertFile, - Destination: &cfg.RPCCert, - }, - &cli.StringFlag{ - Name: "rpckey", - Usage: "File containing the certificate key", - Value: defaultRPCKeyFile, - Destination: &cfg.RPCKey, - }, - &cli.IntFlag{ - Name: "rpcmaxclients", - Usage: "Max number of RPC clients for standard connections", - Value: defaultMaxRPCClients, - Destination: &cfg.RPCMaxClients, - }, - &cli.BoolFlag{ - Name: "norpc", - Usage: "Disable built-in RPC server -- NOTE: The RPC server is disabled by default if no rpcuser/rpcpass or rpclimituser/rpclimitpass is specified", - Destination: &cfg.DisableRPC, - }, - &cli.BoolFlag{ - Name: "notls", - Usage: "Disable TLS for the RPC server -- NOTE: This is only allowed if the RPC server is bound to localhost", - Destination: &cfg.DisableTLS, - }, - &cli.StringSliceFlag{ - Name: "modules", - Usage: "Modules is a list of API modules(See GetNodeInfo) to expose via the HTTP RPC interface. If the module list is empty, all RPC API endpoints designated public will be exposed.", - Destination: &Modules, - }, - &cli.BoolFlag{ - Name: "nocheckpoints", - Usage: "Disable built-in checkpoints. Don't do this unless you know what you're doing.", - Destination: &cfg.DisableCheckpoints, - }, - &cli.BoolFlag{ - Name: "addrindex", - Usage: "Maintain a full address-based transaction index which makes the getrawtransactions RPC available", - Destination: &cfg.AddrIndex, - }, - &cli.BoolFlag{ - Name: "dropaddrindex", - Usage: "Deletes the address-based transaction index from the database on start up and then exits.", - Destination: &cfg.DropAddrIndex, - }, - &cli.BoolFlag{ - Name: "light", - Usage: "start as a qitmeer light node", - Destination: &cfg.LightNode, - }, - &cli.UintFlag{ - Name: "sigcachemaxsize", - Usage: "The maximum number of entries in the signature verification cache", - Value: defaultSigCacheMaxSize, - Destination: &cfg.SigCacheMaxSize, - }, - &cli.BoolFlag{ - Name: "testnet", - Usage: "Use the test network", - Destination: &cfg.TestNet, - }, - &cli.BoolFlag{ - Name: "mixnet", - Usage: "Use the test mix pow network", - Destination: &cfg.MixNet, - }, - &cli.BoolFlag{ - Name: "privnet", - Usage: "Use the private network", - Destination: &cfg.PrivNet, - }, - &cli.StringFlag{ - Name: "dbtype", - Usage: "Database backend to use for the Block Chain", - Value: defaultDbType, - Destination: &cfg.DbType, - }, - &cli.StringFlag{ - Name: "profile", - Usage: "Enable HTTP profiling on given [addr:]port -- NOTE port must be between 1024 and 65536", - Destination: &cfg.Profile, - }, - &cli.StringFlag{ - Name: "cpuprofile", - Usage: "Write CPU profile to the specified file", - Destination: &cfg.CPUProfile, - }, - &cli.BoolFlag{ - Name: "trackheap", - Usage: "tracks the size of the heap and dumps a profile", - Destination: &cfg.TrackHeap, - }, - &cli.IntFlag{ - Name: "trackheaplimit", - Usage: "track heap when limit in gigabytes (default:7G)", - Destination: &cfg.TrackHeapLimit, - Value: profiling.DefaultTrackHeapLimit, - }, - &cli.StringFlag{ - Name: "debuglevel", - Aliases: []string{"d"}, - Usage: "Logging level {trace, debug, info, warn, error, critical}", - Value: defaultLogLevel, - Destination: &cfg.DebugLevel, - }, - &cli.BoolFlag{ - Name: "printorigin", - Usage: "Print log debug location (file:line)", - Destination: &cfg.DebugPrintOrigins, - }, - &cli.BoolFlag{ - Name: "norelaypriority", - Usage: "Do not require free or low-fee transactions to have high priority for relaying", - Destination: &cfg.NoRelayPriority, - }, - &cli.Float64Flag{ - Name: "limitfreerelay", - Usage: "Limit relay of transactions with no transaction fee to the given amount in thousands of bytes per minute", - Destination: &cfg.FreeTxRelayLimit, - }, - &cli.BoolFlag{ - Name: "acceptnonstd", - Usage: "Accept and relay non-standard transactions to the network regardless of the default settings for the active network.", - Destination: &cfg.AcceptNonStd, - Value: true, - }, - &cli.IntFlag{ - Name: "maxorphantx", - Usage: "Max number of orphan transactions to keep in memory", - Destination: &cfg.MaxOrphanTxs, - }, - &cli.Int64Flag{ - Name: "mintxfee", - Usage: "The minimum transaction fee in AtomMEER/kB.", - Value: defaultMinRelayTxFee, - Destination: &cfg.MinTxFee, - }, - &cli.Int64Flag{ - Name: "mempoolexpiry", - Usage: "Do not keep transactions in the mempool more than mempoolexpiry", - Value: defaultMempoolExpiry, - Destination: &cfg.MempoolExpiry, - }, - &cli.BoolFlag{ - Name: "persistmempool", - Usage: "Whether to save the mempool on shutdown and load on restart", - Destination: &cfg.Persistmempool, - }, - &cli.BoolFlag{ - Name: "nomempoolbar", - Usage: "Whether to show progress bar when load mempool from file", - Destination: &cfg.NoMempoolBar, - }, - &cli.BoolFlag{ - Name: "miner", - Usage: "Enable miner module", - Destination: &cfg.Miner, - }, - &cli.BoolFlag{ - Name: "generate", - Usage: "Generate (mine) coins using the CPU", - Destination: &cfg.Generate, - }, - &cli.StringSliceFlag{ - Name: "miningaddr", - Usage: "Add the specified payment address to the list of addresses to use for generated blocks -- At least one address is required if the generate option is set", - Destination: &MiningAddrs, - }, - &cli.IntFlag{ - Name: "miningtimeoffset", - Usage: "Offset the mining timestamp of a block by this many seconds (positive values are in the past)", - Destination: &cfg.MiningTimeOffset, - }, - &cli.UintFlag{ - Name: "blockminsize", - Usage: "Mininum block size in bytes to be used when creating a block", - Value: defaultBlockMinSize, - Destination: &BlockMinSize, - }, - &cli.UintFlag{ - Name: "blockmaxsize", - Usage: "Maximum block size in bytes to be used when creating a block", - Value: defaultBlockMaxSize, - Destination: &BlockMaxSize, - }, - &cli.UintFlag{ - Name: "blockprioritysize", - Usage: "Size in bytes for high-priority/low-fee transactions when creating a block", - Destination: &BlockPrioritySize, - }, - &cli.IntFlag{ - Name: "rpcmaxwebsockets", - Usage: "Max number of RPC websocket connections", - Value: defaultMaxRPCWebsockets, - Destination: &cfg.RPCMaxWebsockets, - }, - &cli.IntFlag{ - Name: "rpcmaxconcurrentreqs", - Usage: "Max number of concurrent RPC requests that may be processed concurrently", - Value: defaultMaxRPCConcurrentReqs, - Destination: &cfg.RPCMaxConcurrentReqs, - }, - &cli.BoolFlag{ - Name: "blocksonly", - Usage: "Do not accept transactions from remote peers", - Destination: &cfg.BlocksOnly, - }, - &cli.BoolFlag{ - Name: "miningstatesync", - Usage: "Synchronizing the mining state with other nodes", - Destination: &cfg.MiningStateSync, - }, - &cli.StringSliceFlag{ - Name: "addpeer", - Aliases: []string{"a"}, - Usage: "Add a peer to connect with at startup", - Destination: &AddPeers, - }, - &cli.BoolFlag{ - Name: "upnp", - Usage: "Use UPnP to map our listening port outside of NAT", - Destination: &cfg.Upnp, - }, - &cli.IntFlag{ - Name: "maxinbound", - Usage: "The max total of inbound peer for host", - Value: defaultMaxInboundPeersPerHost, - Destination: &cfg.MaxInbound, - }, - &cli.BoolFlag{ - Name: "banning", - Usage: "Enable banning of misbehaving peers", - Destination: &cfg.Banning, - Value: true, - }, - &cli.StringFlag{ - Name: "dagtype", - Aliases: []string{"G"}, - Usage: "DAG type {phantom,spectre}", - Value: defaultDAGType, - Destination: &cfg.DAGType, - }, - &cli.BoolFlag{ - Name: "cleanup", - Aliases: []string{"L"}, - Usage: "Cleanup the block database", - Destination: &cfg.Cleanup, - }, - &cli.BoolFlag{ - Name: "buildledger", - Usage: "Generate the genesis ledger for the next qitmeer version", - Destination: &cfg.BuildLedger, - }, - &cli.StringFlag{ - Name: "zmqpubhashblock", - Usage: "Enable publish hash block in
", - Destination: &cfg.Zmqpubhashblock, - }, - &cli.StringFlag{ - Name: "zmqpubrawblock", - Usage: "Enable publish raw block in
", - Destination: &cfg.Zmqpubrawblock, - }, - &cli.StringFlag{ - Name: "zmqpubhashtx", - Usage: "Enable publish hash transaction in
", - Destination: &cfg.Zmqpubhashtx, - }, - &cli.StringFlag{ - Name: "zmqpubrawtx", - Usage: "Enable publish raw transaction in
", - Destination: &cfg.Zmqpubrawtx, - }, - &cli.BoolFlag{ - Name: "invalidtxindex", - Usage: "invalid transaction index.", - Destination: &cfg.InvalidTxIndex, - }, - &cli.BoolFlag{ - Name: "txhashindex", - Usage: "Cache transaction full hash.", - Destination: &cfg.TxHashIndex, - }, - &cli.BoolFlag{ - Name: "ntp", - Usage: "Auto sync time.", - Destination: &cfg.NTP, - }, - &cli.StringSliceFlag{ - Name: "bootstrapnode", - Usage: "The address of bootstrap node.", - Destination: &BootstrapNodes, - }, - &cli.BoolFlag{ - Name: "nodiscovery", - Usage: "Enable only local network p2p and do not connect to cloud bootstrap nodes.", - Destination: &cfg.NoDiscovery, - }, - &cli.StringFlag{ - Name: "metadatadir", - Usage: "meta data dir for p2p", - Destination: &cfg.MetaDataDir, - }, - &cli.IntFlag{ - Name: "p2pudpport", - Usage: "The udp port used by P2P", - Destination: &cfg.P2PUDPPort, - }, - &cli.IntFlag{ - Name: "p2ptcpport", - Usage: "The tcp port used by P2P.", - Destination: &cfg.P2PTCPPort, - }, - &cli.StringFlag{ - Name: "externalip", - Usage: "The IP address advertised by libp2p. This may be used to advertise an external IP.", - Destination: &cfg.HostIP, - }, - &cli.StringFlag{ - Name: "externaldns", - Usage: "The DNS address advertised by libp2p. This may be used to advertise an external DNS.", - Destination: &cfg.HostDNS, - }, - &cli.StringFlag{ - Name: "relaynode", - Usage: "The address of relay node that routes traffic between two peers over a qitmeer “relay” peer.", - Destination: &cfg.RelayNode, - }, - &cli.StringSliceFlag{ - Name: "whitelist", - Usage: "Add an IP network or IP,PeerID that will not be banned or ignore dual channel mode detection. (eg. 192.168.1.0/24 or ::1 or [peer id])", - Destination: &Whitelist, - }, - &cli.StringSliceFlag{ - Name: "blacklist", - Usage: "Add some IP network or IP that will be banned. (eg. 192.168.1.0/24 or ::1)", - Destination: &Blacklist, - }, - &cli.IntFlag{ - Name: "maxbadresp", - Usage: "maxbadresp is the maximum number of bad responses from a peer before we stop talking to it.", - Destination: &cfg.MaxBadResp, - }, - &cli.BoolFlag{ - Name: "circuit", - Usage: "All peers will ignore dual channel mode detection", - Destination: &cfg.Circuit, - Value: true, - }, - &cli.StringFlag{ - Name: "evmenv", - Usage: "meer EVM environment", - Destination: &cfg.EVMEnv, - }, - &cli.BoolFlag{ - Name: "estimatefee", - Usage: "Enable estimate fee", - Destination: &cfg.Estimatefee, - }, - &cli.StringSliceFlag{ - Name: "gbtnotify", - Usage: "HTTP URL list to be notified of new block template", - Destination: &GBTNotify, - }, - &cli.BoolFlag{ - Name: "acctmode", - Usage: "Enable support account system mode", - Destination: &cfg.AcctMode, - }, - &cli.Uint64Flag{ - Name: "dagcachesize", - Usage: "DAG block cache size", - Value: defaultMinBlockPruneSize, - Destination: &cfg.DAGCacheSize, - }, - &cli.Uint64Flag{ - Name: "bdcachesize", - Usage: "Block data cache size", - Value: defaultMinBlockDataCache, - Destination: &cfg.BlockDataCacheSize, - }, - &cli.StringFlag{ - Name: "amanaenv", - Usage: "Amana environment", - Destination: &cfg.AmanaEnv, - }, - &cli.BoolFlag{ - Name: "amana", - Usage: "Enable Amana", - Destination: &cfg.Amana, - }, - &cli.BoolFlag{ - Name: "consistency", - Usage: "Detect data consistency through P2P", - Destination: &cfg.Consistency, - Value: true, - }, - &cli.BoolFlag{ - Name: "metrics", - Usage: "Enable metrics collection and reporting", - Destination: &cfg.Metrics, - }, - &cli.BoolFlag{ - Name: "metrics.expensive", - Usage: "Enable expensive metrics collection and reporting", - Destination: &cfg.MetricsExpensive, - }, - &cli.Uint64Flag{ - Name: "minfreedisk", - Usage: "Minimum free disk space in MB, once reached triggers auto shut down (default = 512M, 0 = disabled)", - Value: 512, - Destination: &cfg.Minfreedisk, - }, - &cli.IntFlag{ - Name: "cache", - Usage: "Megabytes of memory allocated to internal caching (default = 1024 mainnet full node)", - Value: 1024, - Destination: &cfg.Cache, - }, - &cli.IntFlag{ - Name: "cache.database", - Usage: "Percentage of cache memory allowance to use for database io", - Value: 50, - Destination: &cfg.CacheDatabase, - }, - &cli.IntFlag{ - Name: "cache.snapshot", - Usage: "Percentage of cache memory allowance to use for snapshot caching (default = 10% full mode, 20% archive mode)", - Value: 0, - Destination: &cfg.CacheSnapshot, - }, - &cli.BoolFlag{ - Name: "devnextgdb", - Usage: "Enable next generation databases that only exist in development mode", - Destination: &cfg.DevNextGDB, - }, - &cli.BoolFlag{ - Name: "autocollectevm", - Usage: "auto collect miner coinbase utxo to evm", - Destination: &cfg.AutoCollectEvm, - }, - &cli.StringFlag{ - Name: "walletpass", - Usage: "wallet password", - Destination: &cfg.WalletPass, - }, - &cli.IntFlag{ - Name: "evmtrietimeout", - Usage: "Set the interval time(seconds) for flush evm trie to disk", - Destination: &cfg.EVMTrieTimeout, - }, - &cli.StringFlag{ - Name: "state.scheme", - Usage: "Scheme to use for storing ethereum state ('hash' or 'path')", - Destination: &cfg.StateScheme, - }, - } + "net" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" ) // loadConfig initializes and parses the config using a config file and command @@ -739,6 +128,77 @@ func LoadConfig(ctx *cli.Context, parsefile bool) (*config.Config, error) { return nil, err } + err = SetupConfig(cfg) + if err != nil { + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, err + } + return cfg, nil +} + +// newConfigParser returns a new command line flags parser. +func newConfigParser(cfg *config.Config, options flags.Options) *flags.Parser { + parser := flags.NewParser(cfg, options) + return parser +} + +// parseAndSetDebugLevels attempts to parse the specified debug level and set +// the levels accordingly. An appropriate error is returned if anything is +// invalid. +func ParseAndSetDebugLevels(debugLevel string) error { + // When the specified string doesn't have any delimters, treat it as + // the log level for all subsystems. + if !strings.Contains(debugLevel, ",") && !strings.Contains(debugLevel, "=") { + // Validate debug log level. + lvl, err := log.LvlFromString(debugLevel) + if err != nil { + str := "the specified debug level [%v] is invalid" + return fmt.Errorf(str, debugLevel) + } + // Change the logging level for all subsystems. + log.Glogger().Verbosity(lvl) + return nil + } + // TODO support log for subsystem + return nil +} + +// normalizeAddress returns addr with the passed default port appended if +// there is not already a port specified. +func normalizeAddress(addr, defaultPort string) string { + _, _, err := net.SplitHostPort(addr) + if err != nil { + return net.JoinHostPort(addr, defaultPort) + } + return addr +} + +// normalizeAddresses returns a new slice with all the passed peer addresses +// normalized with the given default port, and all duplicates removed. +func normalizeAddresses(addrs []string, defaultPort string) []string { + for i, addr := range addrs { + addrs[i] = normalizeAddress(addr, defaultPort) + } + + return removeDuplicateAddresses(addrs) +} + +// removeDuplicateAddresses returns a new slice with all duplicate entries in +// addrs removed. +func removeDuplicateAddresses(addrs []string) []string { + result := make([]string, 0, len(addrs)) + seen := map[string]struct{}{} + for _, val := range addrs { + if _, ok := seen[val]; !ok { + result = append(result, val) + seen[val] = struct{}{} + } + } + return result +} + +func SetupConfig(cfg *config.Config) error { // assign active network params while we're at it numNets := 0 if cfg.TestNet { @@ -756,12 +216,7 @@ func LoadConfig(ctx *cli.Context, parsefile bool) (*config.Config, error) { } // Multiple networks can't be selected simultaneously. if numNets > 1 { - str := "%s: the testnet and simnet params can't be " + - "used together -- choose one of the three" - err := fmt.Errorf(str, funcName) - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, usageMessage) - return nil, err + return fmt.Errorf("SetupConfig: the testnet and simnet params can't be used together -- choose one of the three") } // default p2p port @@ -772,7 +227,7 @@ func LoadConfig(ctx *cli.Context, parsefile bool) (*config.Config, error) { if cfg.P2PTCPPort <= 0 { P2PTCPPort, err := strconv.Atoi(params.ActiveNetParams.DefaultPort) if err != nil { - return nil, err + return err } cfg.P2PTCPPort = P2PTCPPort } @@ -782,14 +237,12 @@ func LoadConfig(ctx *cli.Context, parsefile bool) (*config.Config, error) { } // if err := params.ActiveNetParams.PowConfig.Check(); err != nil { - fmt.Fprintln(os.Stderr, err) - return nil, err + return err } // Add default port to all rpc listener addresses if needed and remove // duplicate addresses. - cfg.RPCListeners = normalizeAddresses(cfg.RPCListeners, - params.ActiveNetParams.RpcPort) + cfg.RPCListeners = normalizeAddresses(cfg.RPCListeners, params.ActiveNetParams.RpcPort) // Only allow TLS to be disabled if the RPC is bound to localhost // addresses. @@ -803,21 +256,14 @@ func LoadConfig(ctx *cli.Context, parsefile bool) (*config.Config, error) { for _, addr := range cfg.RPCListeners { host, _, err := net.SplitHostPort(addr) if err != nil { - str := "%s: RPC listen interface '%s' is " + - "invalid: %v" - err := fmt.Errorf(str, funcName, addr, err) - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, usageMessage) - return nil, err + str := "SetupConfig: RPC listen interface '%s' is invalid: %v" + return fmt.Errorf(str, addr, err) } if _, ok := allowedTLSListeners[host]; !ok { - str := "%s: the --notls option may not be used " + + str := "SetupConfig: the --notls option may not be used " + "when binding RPC to non localhost " + "addresses: %s" - err := fmt.Errorf(str, funcName, addr) - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, usageMessage) - return nil, err + return fmt.Errorf(str, addr) } } } @@ -826,7 +272,7 @@ func LoadConfig(ctx *cli.Context, parsefile bool) (*config.Config, error) { if !cfg.DisableRPC && len(cfg.RPCListeners) == 0 { addrs, err := net.LookupHost("localhost") if err != nil { - return nil, err + return err } cfg.RPCListeners = make([]string, 0, len(addrs)) for _, addr := range addrs { @@ -836,12 +282,8 @@ func LoadConfig(ctx *cli.Context, parsefile bool) (*config.Config, error) { } if cfg.RPCMaxConcurrentReqs < 0 { - str := "%s: The rpcmaxwebsocketconcurrentrequests option may " + - "not be less than 0 -- parsed [%d]" - err := fmt.Errorf(str, funcName, cfg.RPCMaxConcurrentReqs) - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, usageMessage) - return nil, err + str := "SetupConfig: The rpcmaxwebsocketconcurrentrequests option may not be less than 0 -- parsed [%d]" + return fmt.Errorf(str, cfg.RPCMaxConcurrentReqs) } // Append the network type to the data directory so it is "namespaced" @@ -850,8 +292,10 @@ func LoadConfig(ctx *cli.Context, parsefile bool) (*config.Config, error) { // All data is specific to a network, so namespacing the data directory // means each individual piece of serialized data does not have to // worry about changing names per network and such. - cfg.DataDir = util.CleanAndExpandPath(cfg.DataDir) - cfg.DataDir = filepath.Join(cfg.DataDir, params.ActiveNetParams.Name) + if len(cfg.DataDir) > 0 { + cfg.DataDir = util.CleanAndExpandPath(cfg.DataDir) + cfg.DataDir = filepath.Join(cfg.DataDir, params.ActiveNetParams.Name) + } // Set logging file if presented if !cfg.NoFileLogging { @@ -867,10 +311,7 @@ func LoadConfig(ctx *cli.Context, parsefile bool) (*config.Config, error) { // Parse, validate, and set debug log level(s). if err := ParseAndSetDebugLevels(cfg.DebugLevel); err != nil { - err := fmt.Errorf("%s: %v", funcName, err.Error()) - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, usageMessage) - return nil, err + return err } // DebugPrintOrigins @@ -880,32 +321,21 @@ func LoadConfig(ctx *cli.Context, parsefile bool) (*config.Config, error) { // --addrindex and --dropaddrindex do not mix. if cfg.AddrIndex && cfg.DropAddrIndex { - err := fmt.Errorf("%s: the --addrindex and --dropaddrindex "+ - "options may not be activated at the same time", - funcName) - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, usageMessage) - return nil, err + return fmt.Errorf("SetupConfig: the --addrindex and --dropaddrindex options may not be activated at the same time") } // Check mining addresses are valid and saved parsed versions. for _, strAddr := range cfg.MiningAddrs { addr, err := address.DecodeAddress(strAddr) if err != nil { - str := "%s: mining address '%s' failed to decode: %v" - err := fmt.Errorf(str, funcName, strAddr, err) - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, usageMessage) - return nil, err + str := "SetupConfig: mining address '%s' failed to decode: %v" + return fmt.Errorf(str, strAddr, err) } // TODO, check network by using IsForNetwork() if !address.IsForNetwork(addr, params.ActiveNetParams.Params) { - str := "%s: mining address '%s' is on the wrong network" - err := fmt.Errorf(str, funcName, strAddr) - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, usageMessage) - return nil, err + str := "SetupConfig: mining address '%s' is on the wrong network" + return fmt.Errorf(str, strAddr) } cfg.SetMiningAddrs(addr) } @@ -916,16 +346,8 @@ func LoadConfig(ctx *cli.Context, parsefile bool) (*config.Config, error) { // Ensure there is at least one mining address when the generate or miner flag is // set. if len(cfg.MiningAddrs) == 0 { - var str string if cfg.Generate { - str = "%s: the generate flag is set, but there are no mining " + - "addresses specified " - } - if len(str) > 0 { - err := fmt.Errorf(str, funcName) - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, usageMessage) - return nil, err + return fmt.Errorf("SetupConfig: the generate flag is set, but there are no mining addresses specified") } } @@ -936,113 +358,5 @@ func LoadConfig(ctx *cli.Context, parsefile bool) (*config.Config, error) { s3, _ := gp.GetPasswdPrompt("please input your pass unlock your wallet:", true, os.Stdin, os.Stdout) cfg.WalletPass = string(s3) } - return cfg, nil -} - -// newConfigParser returns a new command line flags parser. -func newConfigParser(cfg *config.Config, options flags.Options) *flags.Parser { - parser := flags.NewParser(cfg, options) - return parser -} - -// parseAndSetDebugLevels attempts to parse the specified debug level and set -// the levels accordingly. An appropriate error is returned if anything is -// invalid. -func ParseAndSetDebugLevels(debugLevel string) error { - // When the specified string doesn't have any delimters, treat it as - // the log level for all subsystems. - if !strings.Contains(debugLevel, ",") && !strings.Contains(debugLevel, "=") { - // Validate debug log level. - lvl, err := log.LvlFromString(debugLevel) - if err != nil { - str := "the specified debug level [%v] is invalid" - return fmt.Errorf(str, debugLevel) - } - // Change the logging level for all subsystems. - log.Glogger().Verbosity(lvl) - return nil - } - // TODO support log for subsystem return nil } - -// normalizeAddress returns addr with the passed default port appended if -// there is not already a port specified. -func normalizeAddress(addr, defaultPort string) string { - _, _, err := net.SplitHostPort(addr) - if err != nil { - return net.JoinHostPort(addr, defaultPort) - } - return addr -} - -// normalizeAddresses returns a new slice with all the passed peer addresses -// normalized with the given default port, and all duplicates removed. -func normalizeAddresses(addrs []string, defaultPort string) []string { - for i, addr := range addrs { - addrs[i] = normalizeAddress(addr, defaultPort) - } - - return removeDuplicateAddresses(addrs) -} - -// removeDuplicateAddresses returns a new slice with all duplicate entries in -// addrs removed. -func removeDuplicateAddresses(addrs []string) []string { - result := make([]string, 0, len(addrs)) - seen := map[string]struct{}{} - for _, val := range addrs { - if _, ok := seen[val]; !ok { - result = append(result, val) - seen[val] = struct{}{} - } - } - return result -} - -func DefaultConfig(homeDir string) *config.Config { - cfg := &config.Config{ - HomeDir: defaultHomeDir, - ConfigFile: defaultConfigFile, - DebugLevel: defaultLogLevel, - DebugPrintOrigins: defaultDebugPrintOrigins, - DataDir: defaultDataDir, - LogDir: defaultLogDir, - DbType: defaultDbType, - RPCKey: defaultRPCKeyFile, - RPCCert: defaultRPCCertFile, - RPCMaxClients: defaultMaxRPCClients, - RPCMaxWebsockets: defaultMaxRPCWebsockets, - RPCMaxConcurrentReqs: defaultMaxRPCConcurrentReqs, - Generate: defaultGenerate, - MaxPeers: defaultMaxPeers, - MinTxFee: defaultMinRelayTxFee, - BlockMinSize: defaultBlockMinSize, - BlockMaxSize: defaultBlockMaxSize, - SigCacheMaxSize: defaultSigCacheMaxSize, - MiningStateSync: defaultMiningStateSync, - DAGType: defaultDAGType, - Banning: true, - MaxInbound: defaultMaxInboundPeersPerHost, - InvalidTxIndex: defaultInvalidTxIndex, - TxHashIndex: defaultTxhashIndex, - NTP: false, - MempoolExpiry: defaultMempoolExpiry, - AcceptNonStd: true, - RPCUser: defaultRPCUser, - RPCPass: defaultRPCPass, - } - if len(homeDir) > 0 { - hd, err := filepath.Abs(homeDir) - if err != nil { - panic(err) - } - cfg.HomeDir = hd - cfg.ConfigFile = filepath.Join(cfg.HomeDir, defaultConfigFilename) - cfg.DataDir = filepath.Join(cfg.HomeDir, defaultDataDirname) - cfg.LogDir = filepath.Join(cfg.HomeDir, defaultLogDirname) - cfg.RPCKey = filepath.Join(cfg.HomeDir, "rpc.key") - cfg.RPCCert = filepath.Join(cfg.HomeDir, "rpc.cert") - } - return cfg -} From ddb7b9931d08f8b2886b8fc228b2315e8061acbd Mon Sep 17 00:00:00 2001 From: Jin Date: Thu, 14 Dec 2023 13:49:16 +0800 Subject: [PATCH 2/9] mock node for block chain test --- cmd/qng/commands.go | 3 +- cmd/qng/qng.go | 1 - meerevm/meer/config.go | 4 +- node/full.go | 2 +- p2p/options.go | 46 +++++++++-------- p2p/service.go | 15 ++++-- p2p/utils.go | 13 +++++ services/acct/acctmgr.go | 3 ++ services/common/load.go | 1 + testutils/simulator/blockchain_test.go | 24 +++++++++ testutils/simulator/mocknode.go | 71 ++++++++++++++++++++++++++ 11 files changed, 153 insertions(+), 30 deletions(-) create mode 100644 testutils/simulator/blockchain_test.go create mode 100644 testutils/simulator/mocknode.go diff --git a/cmd/qng/commands.go b/cmd/qng/commands.go index edab275c..030cee06 100644 --- a/cmd/qng/commands.go +++ b/cmd/qng/commands.go @@ -129,10 +129,9 @@ func consensusCmd() *cli.Command { } func loadConfig(ctx *cli.Context) error { - cfg, err := common.LoadConfig(ctx, false) + _, err := common.LoadConfig(ctx, false) if err != nil { return err } - config.Cfg = cfg return nil } diff --git a/cmd/qng/qng.go b/cmd/qng/qng.go index 9e77ae53..473bca71 100644 --- a/cmd/qng/qng.go +++ b/cmd/qng/qng.go @@ -78,7 +78,6 @@ func qitmeerd(ctx *cli.Context) error { if err != nil { return err } - config.Cfg = cfg defer func() { if log.LogWrite() != nil { log.LogWrite().Close() diff --git a/meerevm/meer/config.go b/meerevm/meer/config.go index 1352e17e..def0742f 100644 --- a/meerevm/meer/config.go +++ b/meerevm/meer/config.go @@ -79,7 +79,9 @@ func MakeConfig(cfg *config.Config) (*eth.Config, error) { nodeConf.HTTPModules = append(nodeConf.HTTPModules, "eth") nodeConf.WSModules = append(nodeConf.WSModules, "eth") nodeConf.IPCPath = "" - nodeConf.KeyStoreDir = filepath.Join(datadir, "keystore") + if len(datadir) > 0 { + nodeConf.KeyStoreDir = filepath.Join(datadir, "keystore") + } //nodeConf.HTTPHost = node.DefaultHTTPHost //nodeConf.WSHost = node.DefaultWSHost nodeConf.HTTPPort, nodeConf.WSPort, nodeConf.AuthPort = getDefaultRPCPort() diff --git a/node/full.go b/node/full.go index d796b3f9..6d97beb9 100644 --- a/node/full.go +++ b/node/full.go @@ -189,7 +189,7 @@ func (qm *QitmeerFull) GetPeerServer() *p2p.Service { func (qm *QitmeerFull) GetRpcServer() *rpc.RpcServer { var service *rpc.RpcServer if err := qm.Services().FetchService(&service); err != nil { - log.Error(err.Error()) + log.Warn(err.Error()) return nil } return service diff --git a/p2p/options.go b/p2p/options.go index fc619597..9fea18b9 100644 --- a/p2p/options.go +++ b/p2p/options.go @@ -7,13 +7,13 @@ package p2p import ( "fmt" "github.com/Qitmeer/qng/version" + ds "github.com/ipfs/go-ds-leveldb" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoreds" "net" "path" "time" - ds "github.com/ipfs/go-ds-leveldb" "github.com/libp2p/go-libp2p" ma "github.com/multiformats/go-multiaddr" ) @@ -41,17 +41,19 @@ const ( // buildOptions for the libp2p host. func (s *Service) buildOptions(ip net.IP, priKey crypto.PrivKey) []libp2p.Option { cfg := s.cfg - listen, err := multiAddressBuilder(ip.String(), cfg.TCPPort) - if err != nil { - log.Error(fmt.Sprintf("Failed to p2p listen: %v", err)) - return nil - } options := []libp2p.Option{ privKeyOption(priKey), - libp2p.ListenAddrs(listen), libp2p.UserAgent(s.cfg.UserAgent), libp2p.ConnectionGater(s), } + if !s.cfg.DisableListen { + listen, err := multiAddressBuilder(ip.String(), cfg.TCPPort) + if err != nil { + log.Error(fmt.Sprintf("Failed to p2p listen: %v", err)) + return nil + } + options = append(options, libp2p.ListenAddrs(listen)) + } if !s.cfg.NoDiscovery { options = append(options, s.KademliaDHTOption()) } @@ -101,7 +103,7 @@ func (s *Service) buildOptions(ip net.IP, priKey crypto.PrivKey) []libp2p.Option log.Error(fmt.Sprintf("Invalid local ip provided: %s", cfg.LocalIP)) return options } - listen, err = multiAddressBuilder(cfg.LocalIP, cfg.TCPPort) + listen, err := multiAddressBuilder(cfg.LocalIP, cfg.TCPPort) if err != nil { log.Error(fmt.Sprintf("Failed to p2p listen: %v", err)) return nil @@ -109,20 +111,24 @@ func (s *Service) buildOptions(ip net.IP, priKey crypto.PrivKey) []libp2p.Option options = append(options, libp2p.ListenAddrs(listen)) } - dsPath := path.Join(s.cfg.DataDir, PeerStore) - peerDS, err := ds.NewDatastore(dsPath, nil) - if err != nil { - log.Error(err.Error()) - return nil - } - log.Info(fmt.Sprintf("Start Peers from:%s", dsPath)) + if len(s.cfg.DataDir) > 0 { + dsPath := path.Join(s.cfg.DataDir, PeerStore) + log.Info(fmt.Sprintf("Start Peers from:%s", dsPath)) + peerDS, err := ds.NewDatastore(dsPath, nil) + if err != nil { + log.Error(err.Error()) + return nil + } - ps, err := pstoreds.NewPeerstore(s.Context(), peerDS, pstoreds.DefaultOpts()) - if err != nil { - log.Error(err.Error()) - return nil + ps, err := pstoreds.NewPeerstore(s.Context(), peerDS, pstoreds.DefaultOpts()) + if err != nil { + log.Error(err.Error()) + return nil + } + options = append(options, libp2p.Peerstore(ps)) + } else { + options = append(options, libp2p.DefaultPeerstore) } - options = append(options, libp2p.Peerstore(ps)) return options } diff --git a/p2p/service.go b/p2p/service.go index f920d1e1..0fdb6c9d 100644 --- a/p2p/service.go +++ b/p2p/service.go @@ -677,7 +677,7 @@ func NewService(cfg *config.Config, consensus model.Consensus, param *params.Par if len(cfg.Listener) > 0 { ipAddr = net.ParseIP(cfg.Listener) } - if ipAddr == nil { + if ipAddr == nil && !cfg.DisableListen { ipAddr = IpAddr() } @@ -697,15 +697,18 @@ func NewService(cfg *config.Config, consensus model.Consensus, param *params.Par return nil, err } opts := s.buildOptions(ipAddr, s.privKey) - h, err := libp2p.New(opts...) + if cfg.DisableListen { + opts = append(opts, libp2p.DisableMetrics()) + s.host, err = libp2p.NewWithoutDefaults(opts...) + } else { + s.host, err = libp2p.New(opts...) + } if err != nil { log.Error("Failed to create p2p host") return nil, err } - s.host = h - - s.cfg.BootstrapNodeAddr = filterBootStrapAddrs(h.ID().String(), s.cfg.BootstrapNodeAddr) + s.cfg.BootstrapNodeAddr = filterBootStrapAddrs(s.host.ID().String(), s.cfg.BootstrapNodeAddr) psOpts := []pubsub.Option{ pubsub.WithMessageSigning(false), @@ -753,6 +756,8 @@ func logIPAddr(id peer.ID, addrs ...multiaddr.Multiaddr) { } if correctAddr != nil { log.Info(fmt.Sprintf("Node started p2p server:multiAddr=%s", correctAddr.String()+"/p2p/"+id.String())) + } else { + log.Warn("QNG P2P server will be useless, neither dialing nor listening") } } diff --git a/p2p/utils.go b/p2p/utils.go index 5571f22d..e6a8a97c 100644 --- a/p2p/utils.go +++ b/p2p/utils.go @@ -51,6 +51,13 @@ func privKey(cfg *common.Config) (crypto.PrivKey, error) { // Determines a private key for p2p networking from the p2p service's // configuration struct. If no key is found, it generates a new one. func PrivateKey(dataDir string, privateKeyPath string, readWritePermissions os.FileMode) (crypto.PrivKey, error) { + if len(dataDir) <= 0 && len(privateKeyPath) <= 0 { + priv, _, err := crypto.GenerateSecp256k1Key(rand.Reader) + if err != nil { + return nil, err + } + return priv, nil + } defaultKeyPath := path.Join(dataDir, keyPath) _, err := os.Stat(defaultKeyPath) @@ -121,6 +128,12 @@ func retrievePrivKeyFromFile(path string) (crypto.PrivKey, error) { // Retrieves node p2p metadata from a set of configuration values // from the p2p service. func metaDataFromConfig(cfg *common.Config) (*pb.MetaData, error) { + if len(cfg.DataDir) <= 0 && len(cfg.MetaDataDir) <= 0 { + return &pb.MetaData{ + SeqNumber: 0, + Subnets: bitfield.NewBitvector64(), + }, nil + } defaultKeyPath := path.Join(cfg.DataDir, metaDataPath) metaDataPath := cfg.MetaDataDir diff --git a/services/acct/acctmgr.go b/services/acct/acctmgr.go index e42595ca..f4dd4ab4 100644 --- a/services/acct/acctmgr.go +++ b/services/acct/acctmgr.go @@ -131,6 +131,9 @@ func (a *AccountManager) initDB(first bool) error { } func (a *AccountManager) cleanDB() { + if len(a.cfg.DataDir) <= 0 { + return + } if a.db == nil { db, err := loadDB("ffldb", a.cfg.DataDir, false) if err != nil { diff --git a/services/common/load.go b/services/common/load.go index 8b37f290..54f376a6 100644 --- a/services/common/load.go +++ b/services/common/load.go @@ -358,5 +358,6 @@ func SetupConfig(cfg *config.Config) error { s3, _ := gp.GetPasswdPrompt("please input your pass unlock your wallet:", true, os.Stdin, os.Stdout) cfg.WalletPass = string(s3) } + config.Cfg = cfg return nil } diff --git a/testutils/simulator/blockchain_test.go b/testutils/simulator/blockchain_test.go new file mode 100644 index 00000000..74785a32 --- /dev/null +++ b/testutils/simulator/blockchain_test.go @@ -0,0 +1,24 @@ +package simulator + +import ( + "fmt" + "testing" + "time" +) + +func TestMockNode(t *testing.T) { + node, err := StartMockNode() + if err != nil { + t.Error(err) + } + for i := 0; i < 10; i++ { + time.Sleep(time.Second) + fmt.Println("wait for:", i) + } + defer func() { + err = node.Stop() + if err != nil { + t.Error(err) + } + }() +} diff --git a/testutils/simulator/mocknode.go b/testutils/simulator/mocknode.go new file mode 100644 index 00000000..4cb03b11 --- /dev/null +++ b/testutils/simulator/mocknode.go @@ -0,0 +1,71 @@ +package simulator + +import ( + "github.com/Qitmeer/qng/common/system" + "github.com/Qitmeer/qng/config" + _ "github.com/Qitmeer/qng/database/legacydb/ffldb" + "github.com/Qitmeer/qng/log" + _ "github.com/Qitmeer/qng/meerevm/common" + "github.com/Qitmeer/qng/node" + "github.com/Qitmeer/qng/params" + "github.com/Qitmeer/qng/services/common" + "github.com/Qitmeer/qng/version" + "os" + "runtime" +) + +func DefaultConfig() *config.Config { + cfg := common.DefaultConfig(os.TempDir()) + cfg.DataDir = "" + cfg.DevNextGDB = true + cfg.NoFileLogging = true + cfg.PrivNet = true + cfg.DisableRPC = true + cfg.DisableListen = true + cfg.NoDiscovery = true + + cfg.DebugPrintOrigins = true + cfg.DebugLevel = "trace" + return cfg +} + +func StartMockNode() (*node.Node, error) { + cfg := DefaultConfig() + err := common.SetupConfig(cfg) + if err != nil { + return nil, err + } + + defer func() { + if log.LogWrite() != nil { + log.LogWrite().Close() + } + }() + interrupt := system.InterruptListener() + + // Show version and home dir at startup. + log.Info("System info", "QNG Version", version.String(), "Go version", runtime.Version()) + log.Info("System info", "Home dir", cfg.HomeDir) + + if cfg.NoFileLogging { + log.Info("File logging disabled") + } + + // Create node and start it. + n, err := node.NewNode(cfg, params.ActiveNetParams.Params, interrupt) + if err != nil { + log.Error("Unable to start server", "listeners", cfg.Listener, "error", err) + return nil, err + } + err = n.RegisterService() + if err != nil { + return nil, err + } + err = n.Start() + if err != nil { + log.Error("Uable to start server", "error", err) + n.Stop() + return nil, err + } + return n, nil +} From c5924373a490c63a8f2cd424769fce44c51b4392 Mon Sep 17 00:00:00 2001 From: Jin Date: Thu, 14 Dec 2023 19:50:50 +0800 Subject: [PATCH 3/9] mock node support API --- testutils/simulator/blockchain_test.go | 64 +++++++++--- testutils/simulator/mocknode.go | 106 +++++++++++++++++--- testutils/simulator/testwallet.go | 132 +++++++++++++++++++++++++ testutils/testwallet.go | 6 +- 4 files changed, 278 insertions(+), 30 deletions(-) create mode 100644 testutils/simulator/testwallet.go diff --git a/testutils/simulator/blockchain_test.go b/testutils/simulator/blockchain_test.go index 74785a32..75e23ccc 100644 --- a/testutils/simulator/blockchain_test.go +++ b/testutils/simulator/blockchain_test.go @@ -1,24 +1,64 @@ package simulator import ( - "fmt" + "encoding/json" + "github.com/Qitmeer/qng/config" + qjson "github.com/Qitmeer/qng/core/json" + "github.com/Qitmeer/qng/core/types/pow" "testing" - "time" ) func TestMockNode(t *testing.T) { - node, err := StartMockNode() + node, err := StartMockNode(nil) if err != nil { t.Error(err) } - for i := 0; i < 10; i++ { - time.Sleep(time.Second) - fmt.Println("wait for:", i) + defer node.Stop() + + nodeinfo, _ := node.GetPublicBlockChainAPI().GetNodeInfo() + + jsonString, err := json.Marshal(nodeinfo.(*qjson.InfoNodeResult)) + if err != nil { + t.Fatal(err) + } + t.Log(string(jsonString)) +} + +func TestGenerateBlocks(t *testing.T) { + node, err := StartMockNode(nil) + if err != nil { + t.Error(err) + } + defer node.Stop() + + targetBlockNum := uint32(5) + ret, err := node.GetPrivateMinerAPI().Generate(targetBlockNum, pow.MEERXKECCAKV1) + if err != nil { + t.Fatal(err) + } + if len(ret) != int(targetBlockNum) { + t.Fatalf("generate block number error: %d != %d ", len(ret), targetBlockNum) + } + info, err := node.GetPublicMinerAPI().GetMinerInfo() + if err != nil { + t.Fatal(err) + } + t.Log(info) + + blockCount, _ := node.GetPublicBlockAPI().GetBlockCount() + if blockCount.(uint) != uint(targetBlockNum+1) { + t.Fatalf("block count error: %d != %d ", blockCount.(uint), targetBlockNum+1) + } +} + +func TestOverrideCfg(t *testing.T) { + node, err := StartMockNode(func(cfg *config.Config) error { + cfg.DebugLevel = "trace" + cfg.DebugPrintOrigins = true + return nil + }) + if err != nil { + t.Error(err) } - defer func() { - err = node.Stop() - if err != nil { - t.Error(err) - } - }() + defer node.Stop() } diff --git a/testutils/simulator/mocknode.go b/testutils/simulator/mocknode.go index 4cb03b11..4712ba46 100644 --- a/testutils/simulator/mocknode.go +++ b/testutils/simulator/mocknode.go @@ -3,12 +3,14 @@ package simulator import ( "github.com/Qitmeer/qng/common/system" "github.com/Qitmeer/qng/config" + "github.com/Qitmeer/qng/core/blockchain" _ "github.com/Qitmeer/qng/database/legacydb/ffldb" "github.com/Qitmeer/qng/log" _ "github.com/Qitmeer/qng/meerevm/common" "github.com/Qitmeer/qng/node" "github.com/Qitmeer/qng/params" "github.com/Qitmeer/qng/services/common" + "github.com/Qitmeer/qng/services/miner" "github.com/Qitmeer/qng/version" "os" "runtime" @@ -23,28 +25,34 @@ func DefaultConfig() *config.Config { cfg.DisableRPC = true cfg.DisableListen = true cfg.NoDiscovery = true - - cfg.DebugPrintOrigins = true - cfg.DebugLevel = "trace" + cfg.Miner = true return cfg } -func StartMockNode() (*node.Node, error) { - cfg := DefaultConfig() +var mockNodeGlobalID uint + +type MockNode struct { + id uint + n *node.Node + wallet *testWallet + overrideCfg func(cfg *config.Config) error + // + publicMinerAPI *miner.PublicMinerAPI + privateMinerAPI *miner.PrivateMinerAPI + publicBlockAPI *blockchain.PublicBlockAPI + publicBlockChainAPI *node.PublicBlockChainAPI +} + +func (mn *MockNode) Start(cfg *config.Config) error { err := common.SetupConfig(cfg) if err != nil { - return nil, err + return err } - defer func() { - if log.LogWrite() != nil { - log.LogWrite().Close() - } - }() interrupt := system.InterruptListener() // Show version and home dir at startup. - log.Info("System info", "QNG Version", version.String(), "Go version", runtime.Version()) + log.Info("System info", "QNG Version", version.String(), "Go version", runtime.Version(), "ID", mn.id) log.Info("System info", "Home dir", cfg.HomeDir) if cfg.NoFileLogging { @@ -55,17 +63,85 @@ func StartMockNode() (*node.Node, error) { n, err := node.NewNode(cfg, params.ActiveNetParams.Params, interrupt) if err != nil { log.Error("Unable to start server", "listeners", cfg.Listener, "error", err) - return nil, err + return err } + mn.n = n err = n.RegisterService() if err != nil { - return nil, err + return err } err = n.Start() if err != nil { log.Error("Uable to start server", "error", err) n.Stop() + return err + } + + return nil +} + +func (mn *MockNode) Stop() { + if log.LogWrite() != nil { + log.LogWrite().Close() + } + if mn.n != nil { + err := mn.n.Stop() + if err != nil { + log.Error(err.Error()) + } + } +} + +func (mn *MockNode) GetPublicMinerAPI() *miner.PublicMinerAPI { + if mn.publicMinerAPI == nil { + mn.publicMinerAPI = miner.NewPublicMinerAPI(mn.n.GetQitmeerFull().GetMiner()) + } + return mn.publicMinerAPI +} + +func (mn *MockNode) GetPrivateMinerAPI() *miner.PrivateMinerAPI { + if mn.privateMinerAPI == nil { + mn.privateMinerAPI = miner.NewPrivateMinerAPI(mn.n.GetQitmeerFull().GetMiner()) + } + return mn.privateMinerAPI +} + +func (mn *MockNode) GetPublicBlockAPI() *blockchain.PublicBlockAPI { + if mn.publicBlockAPI == nil { + mn.publicBlockAPI = blockchain.NewPublicBlockAPI(mn.n.GetQitmeerFull().GetBlockChain()) + } + return mn.publicBlockAPI +} + +func (mn *MockNode) GetPublicBlockChainAPI() *node.PublicBlockChainAPI { + if mn.publicBlockChainAPI == nil { + mn.publicBlockChainAPI = node.NewPublicBlockChainAPI(mn.n.GetQitmeerFull()) + } + return mn.publicBlockChainAPI +} + +func StartMockNode(overrideCfg func(cfg *config.Config) error) (*MockNode, error) { + mn := &MockNode{id: mockNodeGlobalID} + cfg := DefaultConfig() + if overrideCfg != nil { + err := overrideCfg(cfg) + if err != nil { + return nil, err + } + } + + mockNodeGlobalID++ + err := mn.Start(cfg) + if err != nil { return nil, err } - return n, nil + wallet, err := newTestWallet(uint32(mn.id)) + if err != nil { + return nil, err + } + mn.wallet = wallet + if len(mn.n.Config.MiningAddrs) <= 0 { + mn.n.Config.SetMiningAddrs(wallet.miningAddr()) + } + return mn, nil } diff --git a/testutils/simulator/testwallet.go b/testutils/simulator/testwallet.go new file mode 100644 index 00000000..aa3907a2 --- /dev/null +++ b/testutils/simulator/testwallet.go @@ -0,0 +1,132 @@ +// Copyright (c) 2020 The qitmeer developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package simulator + +import ( + "encoding/binary" + "github.com/Qitmeer/qng/common/hash" + "github.com/Qitmeer/qng/core/address" + "github.com/Qitmeer/qng/core/types" + "github.com/Qitmeer/qng/crypto/bip32" + "github.com/Qitmeer/qng/params" + "github.com/Qitmeer/qng/testutils" + "github.com/ethereum/go-ethereum/common" +) + +var ( + // the default seed used in the testWallet + defaultSeed = [hash.HashSize]byte{ + 0x7e, 0x44, 0x5a, 0xa5, 0xff, 0xd8, 0x34, 0xcb, + 0x2d, 0x3b, 0x2d, 0xb5, 0x0f, 0x89, 0x97, 0xdd, + 0x21, 0xaf, 0x29, 0xbe, 0xc3, 0xd2, 0x96, 0xaa, + 0xa0, 0x66, 0xd9, 0x02, 0xb9, 0x3f, 0x48, 0x4b, + } +) + +// testWallet is a simple in-memory wallet works for a test harness instance's +// node. the purpose of testWallet is to provide basic wallet functionality for +// the integrated-test, such as send tx & verify balance etc. +// testWallet works as a HD (BIP-32) wallet +type testWallet struct { + // the node id which wallet is targeted for + nodeId uint32 + // the bip32 master extended private key from a seed + hdMaster *bip32.Key + // the next hd child number from the master + hdChildNumer uint32 + // addrs are all addresses which belong to the master private key. + // the keys of address map are their hd child numbers. + addrs map[uint32]types.Address + pkAddrs map[uint32]*address.SecpPubKeyAddress + ethAddrs map[uint32]common.Address + // privkeys cached all private keys which derived from the master private key. + // the keys of the private key map are their hd child number. + privkeys map[uint32][]byte +} + +func newTestWallet(nodeId uint32) (*testWallet, error) { + params := params.ActiveNetParams.Params + // The final seed is seed || nodeId, the purpose to make sure that each harness + // node use a deterministic private key based on the its node id. + var finalSeed [hash.HashSize + 4]byte + // t.Logf("seed is %v",hexutil.Encode(seed[:])) + copy(finalSeed[:], defaultSeed[:]) + // t.Logf("finalseed is %v",hexutil.Encode(finalSeed[:])) + binary.LittleEndian.PutUint32(finalSeed[hash.HashSize:], nodeId) + version := bip32.Bip32Version{ + PrivKeyVersion: params.HDPrivateKeyID[:], + PubKeyVersion: params.HDPublicKeyID[:], + } + // t.Logf("finalseed is %v",hexutil.Encode(finalSeed[:])) + hdMaster, err := bip32.NewMasterKey2(finalSeed[:], version) + if err != nil { + return nil, err + } + child0, err := hdMaster.NewChildKey(0) + if err != nil { + return nil, err + } + key0 := child0.Key + privkeys := make(map[uint32][]byte) + privkeys[0] = key0 + addr0, err := testutils.PrivateKeyToAddr(key0, params) + if err != nil { + return nil, err + } + pkAddr0, err := testutils.PrivateKeyToPkAddress(key0, params) + if err != nil { + return nil, err + } + ethAddr0, err := testutils.PrivateKeyToETHAddress(key0) + if err != nil { + return nil, err + } + addrs := make(map[uint32]types.Address) + pkAddrs := make(map[uint32]*address.SecpPubKeyAddress) + ethAddrs := make(map[uint32]common.Address) + addrs[0] = addr0 + pkAddrs[0] = pkAddr0 + ethAddrs[0] = ethAddr0 + return &testWallet{ + nodeId: nodeId, + hdMaster: hdMaster, + hdChildNumer: 1, + privkeys: privkeys, + addrs: addrs, + ethAddrs: ethAddrs, + pkAddrs: pkAddrs, + }, nil +} + +// newAddress create a new address from the wallet's key chain. +func (w *testWallet) newAddress() (types.Address, error) { + num := w.hdChildNumer + childx, err := w.hdMaster.NewChildKey(num) + if err != nil { + return nil, err + } + w.privkeys[num] = childx.Key + addrx, err := testutils.PrivateKeyToAddr(childx.Key, params.ActiveNetParams.Params) + if err != nil { + return nil, err + } + pkAddrx, err := testutils.PrivateKeyToPkAddress(childx.Key, params.ActiveNetParams.Params) + if err != nil { + return nil, err + } + ethAddrx, err := testutils.PrivateKeyToETHAddress(childx.Key) + if err != nil { + return nil, err + } + w.addrs[num] = addrx + w.pkAddrs[num] = pkAddrx + w.ethAddrs[num] = ethAddrx + w.hdChildNumer++ + return addrx, nil +} + +func (w *testWallet) miningAddr() types.Address { + return w.pkAddrs[0] +} diff --git a/testutils/testwallet.go b/testutils/testwallet.go index df432343..aea17853 100644 --- a/testutils/testwallet.go +++ b/testutils/testwallet.go @@ -187,7 +187,7 @@ func newTestWalletWithSeed(t *testing.T, params *params.Params, seed *[hash.Hash key0 := child0.Key privkeys := make(map[uint32][]byte) privkeys[0] = key0 - addr0, err := privKeyToAddr(key0, params) + addr0, err := PrivateKeyToAddr(key0, params) if err != nil { return nil, err } @@ -229,7 +229,7 @@ func (w *testWallet) newAddress() (types.Address, error) { return nil, err } w.privkeys[num] = childx.Key - addrx, err := privKeyToAddr(childx.Key, w.netParams) + addrx, err := PrivateKeyToAddr(childx.Key, w.netParams) if err != nil { return nil, err } @@ -257,7 +257,7 @@ func (m *testWallet) NewAddress() (types.Address, error) { } // convert the serialized private key into the p2pkh address -func privKeyToAddr(privKey []byte, params *params.Params) (types.Address, error) { +func PrivateKeyToAddr(privKey []byte, params *params.Params) (types.Address, error) { _, pubKey := secp256k1.PrivKeyFromBytes(privKey) serializedKey := pubKey.SerializeCompressed() addr, err := address.NewSecpPubKeyAddress(serializedKey, params) From e06a854b700c9193db395e71324efbf83d31def8 Mon Sep 17 00:00:00 2001 From: Jin Date: Fri, 15 Dec 2023 23:18:49 +0800 Subject: [PATCH 4/9] Optimize hash --- common/hash/hash.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/hash/hash.go b/common/hash/hash.go index c69f1afa..f07197a2 100644 --- a/common/hash/hash.go +++ b/common/hash/hash.go @@ -167,6 +167,12 @@ func MustHexToHash(i string) Hash { return nh } +// convert hex string to a hash pointer. Must means it panics for invalid input. +func MustHexToHashPointer(i string) *Hash { + nh := MustHexToHash(i) + return &nh +} + // convert hex string to a byte-reversed hash, Must means it panics for invalid input. func MustHexToDecodedHash(i string) Hash { h, err := NewHashFromStr(i) From aabbe0608dd11c7c63e16fe3249fc9291b05fa03 Mon Sep 17 00:00:00 2001 From: Jin Date: Fri, 15 Dec 2023 23:19:51 +0800 Subject: [PATCH 5/9] Optimize wallet and acct --- services/acct/acctmgr.go | 11 ++++------- services/acct/dbhelper.go | 9 +++++++++ services/wallet/wallet.go | 6 ++---- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/services/acct/acctmgr.go b/services/acct/acctmgr.go index f4dd4ab4..a4c1228e 100644 --- a/services/acct/acctmgr.go +++ b/services/acct/acctmgr.go @@ -42,7 +42,7 @@ func (a *AccountManager) Start() error { if a.cfg.AcctMode { err := a.initDB(true) if err != nil { - return fmt.Errorf("Serious error, you can try to delete the data file(%s):%s", getDBPath(a.cfg.DataDir), err.Error()) + return fmt.Errorf("Serious error, you can try to delete the data file(%s):%s", getDBPath(getDataDir(a.cfg)), err.Error()) } } else { a.cleanDB() @@ -66,7 +66,7 @@ func (a *AccountManager) Stop() error { func (a *AccountManager) initDB(first bool) error { log.Info("AccountManager enable account mode") var err error - a.db, err = loadDB("ffldb", a.cfg.DataDir, true) + a.db, err = loadDB("ffldb", getDataDir(a.cfg), true) if err != nil { return err } @@ -131,11 +131,8 @@ func (a *AccountManager) initDB(first bool) error { } func (a *AccountManager) cleanDB() { - if len(a.cfg.DataDir) <= 0 { - return - } if a.db == nil { - db, err := loadDB("ffldb", a.cfg.DataDir, false) + db, err := loadDB("ffldb", getDataDir(a.cfg), false) if err != nil { return } @@ -167,7 +164,7 @@ func (a *AccountManager) cleanDB() { } } - err := removeDB(getDBPath(a.cfg.DataDir)) + err := removeDB(getDBPath(getDataDir(a.cfg))) if err != nil { log.Error(err.Error()) } diff --git a/services/acct/dbhelper.go b/services/acct/dbhelper.go index b22f783a..c09dee4c 100644 --- a/services/acct/dbhelper.go +++ b/services/acct/dbhelper.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "github.com/Qitmeer/qng/common/hash" + "github.com/Qitmeer/qng/config" "github.com/Qitmeer/qng/core/serialization" "github.com/Qitmeer/qng/core/types" "github.com/Qitmeer/qng/database/legacydb" @@ -71,6 +72,14 @@ func getDBPath(dataDir string) string { return filepath.Join(dataDir, dbNamePrefix) } +func getDataDir(cfg *config.Config) string { + dataDir := cfg.DataDir + if len(dataDir) <= 0 { + dataDir = cfg.HomeDir + } + return dataDir +} + // info func DBGetACCTInfo(dbTx legacydb.Tx) (*AcctInfo, error) { meta := dbTx.Metadata() diff --git a/services/wallet/wallet.go b/services/wallet/wallet.go index f4bbe2d7..c3497b15 100644 --- a/services/wallet/wallet.go +++ b/services/wallet/wallet.go @@ -58,10 +58,8 @@ func (a *WalletManager) APIs() []api.API { func New(cfg *config.Config, evm *meer.MeerChain, _am *acct.AccountManager, _tm *tx.TxManager, _events *event.Feed) (*WalletManager, error) { conf := evm.ETHChain().Config().Node - keydir, err := conf.KeyDirConfig() - if err != nil { - return nil, err - } + keydir := evm.ETHChain().Node().KeyStoreDir() + n, p := keystore.StandardScryptN, keystore.StandardScryptP if conf.UseLightweightKDF { n, p = keystore.LightScryptN, keystore.LightScryptP From 2b75c5f338cf77fa58f1a58f87ad2184380a0448 Mon Sep 17 00:00:00 2001 From: Jin Date: Sat, 16 Dec 2023 11:14:48 +0800 Subject: [PATCH 6/9] Optimize full node --- node/full.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/node/full.go b/node/full.go index 6d97beb9..953cbc33 100644 --- a/node/full.go +++ b/node/full.go @@ -213,6 +213,15 @@ func (qm *QitmeerFull) GetAccountManager() *acct.AccountManager { return service } +func (qm *QitmeerFull) GetWalletManager() *wallet.WalletManager { + var service *wallet.WalletManager + if err := qm.Services().FetchService(&service); err != nil { + log.Error(err.Error()) + return nil + } + return service +} + func (qm *QitmeerFull) GetMiner() *miner.Miner { var service *miner.Miner if err := qm.Services().FetchService(&service); err != nil { From 69de69c32d603d6b1f4b6c79155da25313df8113 Mon Sep 17 00:00:00 2001 From: Jin Date: Sun, 17 Dec 2023 23:02:11 +0800 Subject: [PATCH 7/9] Support ImportRawKey for wallet --- script/cli.sh | 24 ++++++++++++++++++++++-- services/wallet/api.go | 13 +++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/script/cli.sh b/script/cli.sh index 9f3b8604..7c184e9b 100755 --- a/script/cli.sh +++ b/script/cli.sh @@ -548,6 +548,20 @@ function sendToAddress() { get_result "$data" } +function importRawKey() { + local privkey=$1 + local password=$2 + local data='{"jsonrpc":"2.0","method":"wallet_importRawKey","params":["'$privkey'","'$password'"],"id":null}' + get_result "$data" +} + +function listAccount() { + local privkey=$1 + local password=$2 + local data='{"jsonrpc":"2.0","method":"wallet_listAccount","params":[],"id":null}' + get_result "$data" +} + function add_balance() { local address=$1 local data='{"jsonrpc":"2.0","method":"addBalance","params":["'$address'"],"id":null}' @@ -900,7 +914,8 @@ function usage(){ echo " unlock (accountIndex) password timeout" echo " lock (address)" echo " sendtoaddress fromAddress addressAmounts({\"RmN6q2ZdNaCtgpq2BE5ZaUbfQxXwRU1yTYf\":{\"amount\":100000000,\"coinid\":0}}) locktime" - + echo " importrawkey(privkey password)" + echo " listaccount" } # ------------------- @@ -1580,7 +1595,12 @@ elif [ "$1" == "lock" ]; then elif [ "$1" == "sendtoaddress" ]; then shift sendToAddress "$@" - +elif [ "$1" == "importrawkey" ]; then + shift + importRawKey "$@" +elif [ "$1" == "listaccount" ]; then + shift + listAccount "$@" elif [ "$1" == "list_command" ]; then usage diff --git a/services/wallet/api.go b/services/wallet/api.go index b6a560b8..9aa66a43 100644 --- a/services/wallet/api.go +++ b/services/wallet/api.go @@ -4,6 +4,8 @@ import ( "encoding/hex" ejson "encoding/json" "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "time" "github.com/Qitmeer/qng/core/address" @@ -11,6 +13,17 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" ) +// ImportRawKey stores the given hex encoded ECDSA key into the key directory, +// encrypting it with the passphrase. +func (api *PrivateWalletManagerAPI) ImportRawKey(privkey string, password string) (common.Address, error) { + key, err := crypto.HexToECDSA(privkey) + if err != nil { + return common.Address{}, err + } + acc, err := api.a.qks.KeyStore.ImportECDSA(key, password) + return acc.Address, err +} + func (api *PrivateWalletManagerAPI) ListAccount() (interface{}, error) { accs := api.a.qks.KeyStore.Accounts() res := []map[string]interface{}{} From db10ce482ad8466e8785d98f56bef0ceacbfe02b Mon Sep 17 00:00:00 2001 From: Jin Date: Mon, 18 Dec 2023 08:35:49 +0800 Subject: [PATCH 8/9] Optimize address API --- services/address/address_test.go | 19 ++++++++++++++++++ services/address/api.go | 33 +++++++++++++++++++------------- 2 files changed, 39 insertions(+), 13 deletions(-) create mode 100644 services/address/address_test.go diff --git a/services/address/address_test.go b/services/address/address_test.go new file mode 100644 index 00000000..491d7966 --- /dev/null +++ b/services/address/address_test.go @@ -0,0 +1,19 @@ +package address + +import ( + "encoding/hex" + "testing" +) + +func TestNewAddresses(t *testing.T) { + privateKeyHex := "fff2cefe258ca60ae5f5abec99b5d63e2a561c40d784ee50b04eddf8efc84b0d" + privateKey, addr, eaddr, err := NewAddresses(privateKeyHex) + if err != nil { + t.Fatal(err) + } + pkHex := hex.EncodeToString(privateKey.Serialize()) + if pkHex != privateKeyHex { + t.Fatalf("%s != %s (expect)", pkHex, privateKeyHex) + } + t.Logf("privateKey:%s addr:%s pkAddr:%s evmAddr:%s", pkHex, addr.PKHAddress().String(), addr.String(), eaddr.String()) +} diff --git a/services/address/api.go b/services/address/api.go index a5c6ab6b..572172bb 100644 --- a/services/address/api.go +++ b/services/address/api.go @@ -16,6 +16,7 @@ import ( "github.com/Qitmeer/qng/rpc" "github.com/Qitmeer/qng/rpc/api" "github.com/Qitmeer/qng/rpc/client/cmds" + ecommon "github.com/ethereum/go-ethereum/common" "sync" ) @@ -94,30 +95,36 @@ func NewPrivateAddressAPI(ai *AddressApi) *PrivateAddressAPI { } func (api *PrivateAddressAPI) GetAddresses(privateKeyHex string) (interface{}, error) { - privkeyByte, err := hex.DecodeString(privateKeyHex) + privateKey, addr, eaddr, err := NewAddresses(privateKeyHex) if err != nil { return nil, err } + result := qjson.OrderedResult{ + qjson.KV{Key: "PrivateKey", Val: hex.EncodeToString(privateKey.Serialize())}, + qjson.KV{Key: "PKHAddress", Val: addr.PKHAddress().String()}, + qjson.KV{Key: "PKAddress", Val: addr.String()}, + qjson.KV{Key: "MeerEVM Address", Val: eaddr.String()}, + } + return result, nil +} + +func NewAddresses(privateKeyHex string) (ecc.PrivateKey, *address.SecpPubKeyAddress, ecommon.Address, error) { + privkeyByte, err := hex.DecodeString(privateKeyHex) + if err != nil { + return nil, nil, ecommon.Address{}, err + } if len(privkeyByte) != 32 { - return nil, fmt.Errorf("error length:%d", len(privkeyByte)) + return nil, nil, ecommon.Address{}, fmt.Errorf("error length:%d", len(privkeyByte)) } privateKey, pubKey := ecc.Secp256k1.PrivKeyFromBytes(privkeyByte) - serializedKey := pubKey.SerializeCompressed() addr, err := address.NewSecpPubKeyAddress(serializedKey, params.ActiveNetParams.Params) if err != nil { - return nil, err + return nil, nil, ecommon.Address{}, err } eaddr, err := common.NewMeerEVMAddress(pubKey.SerializeUncompressed()) if err != nil { - return nil, err - } - result := qjson.OrderedResult{ - qjson.KV{Key: "PrivateKey", Val: hex.EncodeToString(privateKey.Serialize())}, - qjson.KV{Key: "PKHAddress", Val: addr.PKHAddress().String()}, - qjson.KV{Key: "PKAddress", Val: addr.String()}, - qjson.KV{Key: "MeerEVM Address", Val: eaddr.String()}, + return nil, nil, ecommon.Address{}, err } - - return result, nil + return privateKey, addr, eaddr, nil } From 1c8a27b821df8dbce1b59f61ef115fdfcbfcd427 Mon Sep 17 00:00:00 2001 From: Jin Date: Mon, 18 Dec 2023 09:43:11 +0800 Subject: [PATCH 9/9] testutils --- common/hash/hash.go | 6 - services/address/address_test.go | 9 +- testutils/simulator/evm_test.go | 1 + testutils/simulator/mocknode.go | 103 +++++++++++--- testutils/simulator/testprivatekey/builder.go | 96 +++++++++++++ .../simulator/testprivatekey/builder_test.go | 52 +++++++ testutils/simulator/testwallet.go | 132 ------------------ testutils/simulator/tx_test.go | 1 + testutils/simulator/utils.go | 66 +++++++++ testutils/simulator/wallet_test.go | 40 ++++++ 10 files changed, 348 insertions(+), 158 deletions(-) create mode 100644 testutils/simulator/evm_test.go create mode 100644 testutils/simulator/testprivatekey/builder.go create mode 100644 testutils/simulator/testprivatekey/builder_test.go delete mode 100644 testutils/simulator/testwallet.go create mode 100644 testutils/simulator/tx_test.go create mode 100644 testutils/simulator/utils.go create mode 100644 testutils/simulator/wallet_test.go diff --git a/common/hash/hash.go b/common/hash/hash.go index f07197a2..c69f1afa 100644 --- a/common/hash/hash.go +++ b/common/hash/hash.go @@ -167,12 +167,6 @@ func MustHexToHash(i string) Hash { return nh } -// convert hex string to a hash pointer. Must means it panics for invalid input. -func MustHexToHashPointer(i string) *Hash { - nh := MustHexToHash(i) - return &nh -} - // convert hex string to a byte-reversed hash, Must means it panics for invalid input. func MustHexToDecodedHash(i string) Hash { h, err := NewHashFromStr(i) diff --git a/services/address/address_test.go b/services/address/address_test.go index 491d7966..6cd3accd 100644 --- a/services/address/address_test.go +++ b/services/address/address_test.go @@ -2,11 +2,18 @@ package address import ( "encoding/hex" + "github.com/Qitmeer/qng/params" + "github.com/Qitmeer/qng/testutils/simulator/testprivatekey" "testing" ) func TestNewAddresses(t *testing.T) { - privateKeyHex := "fff2cefe258ca60ae5f5abec99b5d63e2a561c40d784ee50b04eddf8efc84b0d" + params.ActiveNetParams = ¶ms.PrivNetParam + pb, err := testprivatekey.NewBuilder(0) + if err != nil { + t.Fatal(err) + } + privateKeyHex := hex.EncodeToString(pb.Get(0)) privateKey, addr, eaddr, err := NewAddresses(privateKeyHex) if err != nil { t.Fatal(err) diff --git a/testutils/simulator/evm_test.go b/testutils/simulator/evm_test.go new file mode 100644 index 00000000..71a17022 --- /dev/null +++ b/testutils/simulator/evm_test.go @@ -0,0 +1 @@ +package simulator diff --git a/testutils/simulator/mocknode.go b/testutils/simulator/mocknode.go index 4712ba46..422b04e9 100644 --- a/testutils/simulator/mocknode.go +++ b/testutils/simulator/mocknode.go @@ -1,6 +1,7 @@ package simulator import ( + "fmt" "github.com/Qitmeer/qng/common/system" "github.com/Qitmeer/qng/config" "github.com/Qitmeer/qng/core/blockchain" @@ -9,15 +10,21 @@ import ( _ "github.com/Qitmeer/qng/meerevm/common" "github.com/Qitmeer/qng/node" "github.com/Qitmeer/qng/params" + "github.com/Qitmeer/qng/services/acct" + "github.com/Qitmeer/qng/services/address" "github.com/Qitmeer/qng/services/common" "github.com/Qitmeer/qng/services/miner" + "github.com/Qitmeer/qng/services/tx" + "github.com/Qitmeer/qng/services/wallet" + "github.com/Qitmeer/qng/testutils/simulator/testprivatekey" "github.com/Qitmeer/qng/version" "os" + "path" "runtime" ) func DefaultConfig() *config.Config { - cfg := common.DefaultConfig(os.TempDir()) + cfg := common.DefaultConfig(path.Join(os.TempDir(), "qng_test")) cfg.DataDir = "" cfg.DevNextGDB = true cfg.NoFileLogging = true @@ -26,6 +33,7 @@ func DefaultConfig() *config.Config { cfg.DisableListen = true cfg.NoDiscovery = true cfg.Miner = true + cfg.AcctMode = true return cfg } @@ -34,13 +42,21 @@ var mockNodeGlobalID uint type MockNode struct { id uint n *node.Node - wallet *testWallet + pb *testprivatekey.Builder overrideCfg func(cfg *config.Config) error // - publicMinerAPI *miner.PublicMinerAPI - privateMinerAPI *miner.PrivateMinerAPI - publicBlockAPI *blockchain.PublicBlockAPI - publicBlockChainAPI *node.PublicBlockChainAPI + publicMinerAPI *miner.PublicMinerAPI + privateMinerAPI *miner.PrivateMinerAPI + publicBlockAPI *blockchain.PublicBlockAPI + publicBlockChainAPI *node.PublicBlockChainAPI + publicTxAPI *tx.PublicTxAPI + privateTxAPI *tx.PrivateTxAPI + publicAccountManagerAPI *acct.PublicAccountManagerAPI + privateWalletManagerAPI *wallet.PrivateWalletManagerAPI +} + +func (mn *MockNode) ID() uint { + return mn.id } func (mn *MockNode) Start(cfg *config.Config) error { @@ -77,7 +93,7 @@ func (mn *MockNode) Start(cfg *config.Config) error { return err } - return nil + return mn.setup() } func (mn *MockNode) Stop() { @@ -90,6 +106,31 @@ func (mn *MockNode) Stop() { log.Error(err.Error()) } } + // remove temp dir + log.Info("Try remove home dir", "path", mn.n.Config.HomeDir) + err := os.RemoveAll(mn.n.Config.HomeDir) + if err != nil { + log.Error(err.Error()) + } +} + +func (mn *MockNode) setup() error { + // init + coinbasePKHex := mn.pb.GetHex(testprivatekey.CoinbaseIdx) + _, err := mn.GetPrivateWalletManagerAPI().ImportRawKey(coinbasePKHex, testprivatekey.Password) + if err != nil { + return err + } + accounts, err := mn.GetPrivateWalletManagerAPI().ListAccount() + if err != nil { + return err + } + log.Info(fmt.Sprintf("%v", accounts)) + if len(mn.n.Config.MiningAddrs) <= 0 { + _, addr, _, _ := address.NewAddresses(coinbasePKHex) + mn.n.Config.SetMiningAddrs(addr) + } + return nil } func (mn *MockNode) GetPublicMinerAPI() *miner.PublicMinerAPI { @@ -120,8 +161,40 @@ func (mn *MockNode) GetPublicBlockChainAPI() *node.PublicBlockChainAPI { return mn.publicBlockChainAPI } +func (mn *MockNode) GetPublicTxAPI() *tx.PublicTxAPI { + if mn.publicTxAPI == nil { + mn.publicTxAPI = tx.NewPublicTxAPI(mn.n.GetQitmeerFull().GetTxManager()) + } + return mn.publicTxAPI +} + +func (mn *MockNode) GetPrivateTxAPI() *tx.PrivateTxAPI { + if mn.privateTxAPI == nil { + mn.privateTxAPI = tx.NewPrivateTxAPI(mn.n.GetQitmeerFull().GetTxManager()) + } + return mn.privateTxAPI +} + +func (mn *MockNode) GetPublicAccountManagerAPI() *acct.PublicAccountManagerAPI { + if mn.publicAccountManagerAPI == nil { + mn.publicAccountManagerAPI = acct.NewPublicAccountManagerAPI(mn.n.GetQitmeerFull().GetAccountManager()) + } + return mn.publicAccountManagerAPI +} + +func (mn *MockNode) GetPrivateWalletManagerAPI() *wallet.PrivateWalletManagerAPI { + if mn.privateWalletManagerAPI == nil { + mn.privateWalletManagerAPI = wallet.NewPrivateWalletAPI(mn.n.GetQitmeerFull().GetWalletManager()) + } + return mn.privateWalletManagerAPI +} + func StartMockNode(overrideCfg func(cfg *config.Config) error) (*MockNode, error) { - mn := &MockNode{id: mockNodeGlobalID} + pb, err := testprivatekey.NewBuilder(uint32(mockNodeGlobalID)) + if err != nil { + return nil, err + } + mn := &MockNode{id: mockNodeGlobalID, pb: pb} cfg := DefaultConfig() if overrideCfg != nil { err := overrideCfg(cfg) @@ -129,19 +202,11 @@ func StartMockNode(overrideCfg func(cfg *config.Config) error) (*MockNode, error return nil, err } } - - mockNodeGlobalID++ - err := mn.Start(cfg) - if err != nil { - return nil, err - } - wallet, err := newTestWallet(uint32(mn.id)) + err = mn.Start(cfg) if err != nil { return nil, err } - mn.wallet = wallet - if len(mn.n.Config.MiningAddrs) <= 0 { - mn.n.Config.SetMiningAddrs(wallet.miningAddr()) - } + + mockNodeGlobalID++ return mn, nil } diff --git a/testutils/simulator/testprivatekey/builder.go b/testutils/simulator/testprivatekey/builder.go new file mode 100644 index 00000000..5cd2ef76 --- /dev/null +++ b/testutils/simulator/testprivatekey/builder.go @@ -0,0 +1,96 @@ +// Copyright (c) 2020 The qitmeer developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package testprivatekey + +import ( + "encoding/binary" + "encoding/hex" + "github.com/Qitmeer/qng/common/hash" + "github.com/Qitmeer/qng/crypto/bip32" + "github.com/Qitmeer/qng/params" +) + +const ( + CoinbaseIdx = 0 + Password = "12345" +) + +var ( + // the default seed used in the testWallet + defaultSeed = [hash.HashSize]byte{ + 0x7e, 0x44, 0x5a, 0xa5, 0xff, 0xd8, 0x34, 0xcb, + 0x2d, 0x3b, 0x2d, 0xb5, 0x0f, 0x89, 0x97, 0xdd, + 0x21, 0xaf, 0x29, 0xbe, 0xc3, 0xd2, 0x96, 0xaa, + 0xa0, 0x66, 0xd9, 0x02, 0xb9, 0x3f, 0x48, 0x4b, + } +) + +type Builder struct { + // the bip32 master extended private key from a seed + hdMaster *bip32.Key + // the next hd child number from the master + hdChildNumer uint32 + // privkeys cached all private keys which derived from the master private key. + // the keys of the private key map are their hd child number. + privkeys map[uint32][]byte +} + +func NewBuilder(id uint32) (*Builder, error) { + params := params.ActiveNetParams.Params + // The final seed is seed || nodeId, the purpose to make sure that each harness + // node use a deterministic private key based on the its node id. + var finalSeed [hash.HashSize + 4]byte + // t.Logf("seed is %v",hexutil.Encode(seed[:])) + copy(finalSeed[:], defaultSeed[:]) + // t.Logf("finalseed is %v",hexutil.Encode(finalSeed[:])) + binary.LittleEndian.PutUint32(finalSeed[hash.HashSize:], id) + version := bip32.Bip32Version{ + PrivKeyVersion: params.HDPrivateKeyID[:], + PubKeyVersion: params.HDPublicKeyID[:], + } + // t.Logf("finalseed is %v",hexutil.Encode(finalSeed[:])) + hdMaster, err := bip32.NewMasterKey2(finalSeed[:], version) + if err != nil { + return nil, err + } + child0, err := hdMaster.NewChildKey(0) + if err != nil { + return nil, err + } + key0 := child0.Key + privkeys := make(map[uint32][]byte) + privkeys[0] = key0 + + return &Builder{ + hdMaster: hdMaster, + hdChildNumer: 1, + privkeys: privkeys, + }, nil +} + +func (b *Builder) Build() ([]byte, error) { + num := b.hdChildNumer + childx, err := b.hdMaster.NewChildKey(num) + if err != nil { + return nil, err + } + b.privkeys[num] = childx.Key + b.hdChildNumer++ + return childx.Key, nil +} + +func (b *Builder) Get(idx int) []byte { + if idx >= len(b.privkeys) { + return nil + } + return b.privkeys[uint32(idx)] +} + +func (b *Builder) GetHex(idx int) string { + if idx >= len(b.privkeys) { + return "" + } + return hex.EncodeToString(b.privkeys[uint32(idx)]) +} diff --git a/testutils/simulator/testprivatekey/builder_test.go b/testutils/simulator/testprivatekey/builder_test.go new file mode 100644 index 00000000..6dcef032 --- /dev/null +++ b/testutils/simulator/testprivatekey/builder_test.go @@ -0,0 +1,52 @@ +package testprivatekey + +import ( + "github.com/Qitmeer/qng/common/util/hexutil" + "github.com/Qitmeer/qng/params" + "testing" +) + +var ( + expect = struct { + ver string + key string + chaincode string + priv0 string + priv1 string + }{ + "0x040bee6e", + "0x38015593945529cc0bd761108ad2fbd98a3f5f8e030c5acd3747ce3e54d95c16", + "0x4eb4e56ada09795313734db329c362923c5b6fac75b924780e68b9c9b18a24b3", + "0xe0b26a52b1a9676a365d6452fb04a1c05b58e959683862d73105e58d4416baba", + "0xfff2cefe258ca60ae5f5abec99b5d63e2a561c40d784ee50b04eddf8efc84b0d", + } +) + +func TestPrivateKeyBuild(t *testing.T) { + params.ActiveNetParams = ¶ms.PrivNetParam + pb, err := NewBuilder(0) + if err != nil { + t.Fatal(err) + } + + if hexutil.Encode(pb.hdMaster.Key) != expect.key { + t.Fatalf("hd master key not matched, expect %v but got %v", pb.hdMaster.Key, expect.key) + } + if hexutil.Encode(pb.hdMaster.Version) != expect.ver { + t.Fatalf("hd master version not matched, expect %v but got %v", pb.hdMaster.Version, expect.ver) + } + if hexutil.Encode(pb.hdMaster.ChainCode) != expect.chaincode { + t.Fatalf("hd master chain code not matched, expect %v but got %v", pb.hdMaster.ChainCode, expect.chaincode) + } + + _, err = pb.Build() + if err != nil { + t.Fatalf("failed get new address : %v", err) + } + if hexutil.Encode(pb.Get(0)) != expect.priv0 { + t.Fatalf("hd key0 priv key not matched, expect %x but got %v", pb.Get(0), expect.priv0) + } + if hexutil.Encode(pb.Get(1)) != expect.priv1 { + t.Fatalf("hd key0 priv key not matched, expect %x but got %v", pb.Get(1), expect.priv1) + } +} diff --git a/testutils/simulator/testwallet.go b/testutils/simulator/testwallet.go deleted file mode 100644 index aa3907a2..00000000 --- a/testutils/simulator/testwallet.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2020 The qitmeer developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package simulator - -import ( - "encoding/binary" - "github.com/Qitmeer/qng/common/hash" - "github.com/Qitmeer/qng/core/address" - "github.com/Qitmeer/qng/core/types" - "github.com/Qitmeer/qng/crypto/bip32" - "github.com/Qitmeer/qng/params" - "github.com/Qitmeer/qng/testutils" - "github.com/ethereum/go-ethereum/common" -) - -var ( - // the default seed used in the testWallet - defaultSeed = [hash.HashSize]byte{ - 0x7e, 0x44, 0x5a, 0xa5, 0xff, 0xd8, 0x34, 0xcb, - 0x2d, 0x3b, 0x2d, 0xb5, 0x0f, 0x89, 0x97, 0xdd, - 0x21, 0xaf, 0x29, 0xbe, 0xc3, 0xd2, 0x96, 0xaa, - 0xa0, 0x66, 0xd9, 0x02, 0xb9, 0x3f, 0x48, 0x4b, - } -) - -// testWallet is a simple in-memory wallet works for a test harness instance's -// node. the purpose of testWallet is to provide basic wallet functionality for -// the integrated-test, such as send tx & verify balance etc. -// testWallet works as a HD (BIP-32) wallet -type testWallet struct { - // the node id which wallet is targeted for - nodeId uint32 - // the bip32 master extended private key from a seed - hdMaster *bip32.Key - // the next hd child number from the master - hdChildNumer uint32 - // addrs are all addresses which belong to the master private key. - // the keys of address map are their hd child numbers. - addrs map[uint32]types.Address - pkAddrs map[uint32]*address.SecpPubKeyAddress - ethAddrs map[uint32]common.Address - // privkeys cached all private keys which derived from the master private key. - // the keys of the private key map are their hd child number. - privkeys map[uint32][]byte -} - -func newTestWallet(nodeId uint32) (*testWallet, error) { - params := params.ActiveNetParams.Params - // The final seed is seed || nodeId, the purpose to make sure that each harness - // node use a deterministic private key based on the its node id. - var finalSeed [hash.HashSize + 4]byte - // t.Logf("seed is %v",hexutil.Encode(seed[:])) - copy(finalSeed[:], defaultSeed[:]) - // t.Logf("finalseed is %v",hexutil.Encode(finalSeed[:])) - binary.LittleEndian.PutUint32(finalSeed[hash.HashSize:], nodeId) - version := bip32.Bip32Version{ - PrivKeyVersion: params.HDPrivateKeyID[:], - PubKeyVersion: params.HDPublicKeyID[:], - } - // t.Logf("finalseed is %v",hexutil.Encode(finalSeed[:])) - hdMaster, err := bip32.NewMasterKey2(finalSeed[:], version) - if err != nil { - return nil, err - } - child0, err := hdMaster.NewChildKey(0) - if err != nil { - return nil, err - } - key0 := child0.Key - privkeys := make(map[uint32][]byte) - privkeys[0] = key0 - addr0, err := testutils.PrivateKeyToAddr(key0, params) - if err != nil { - return nil, err - } - pkAddr0, err := testutils.PrivateKeyToPkAddress(key0, params) - if err != nil { - return nil, err - } - ethAddr0, err := testutils.PrivateKeyToETHAddress(key0) - if err != nil { - return nil, err - } - addrs := make(map[uint32]types.Address) - pkAddrs := make(map[uint32]*address.SecpPubKeyAddress) - ethAddrs := make(map[uint32]common.Address) - addrs[0] = addr0 - pkAddrs[0] = pkAddr0 - ethAddrs[0] = ethAddr0 - return &testWallet{ - nodeId: nodeId, - hdMaster: hdMaster, - hdChildNumer: 1, - privkeys: privkeys, - addrs: addrs, - ethAddrs: ethAddrs, - pkAddrs: pkAddrs, - }, nil -} - -// newAddress create a new address from the wallet's key chain. -func (w *testWallet) newAddress() (types.Address, error) { - num := w.hdChildNumer - childx, err := w.hdMaster.NewChildKey(num) - if err != nil { - return nil, err - } - w.privkeys[num] = childx.Key - addrx, err := testutils.PrivateKeyToAddr(childx.Key, params.ActiveNetParams.Params) - if err != nil { - return nil, err - } - pkAddrx, err := testutils.PrivateKeyToPkAddress(childx.Key, params.ActiveNetParams.Params) - if err != nil { - return nil, err - } - ethAddrx, err := testutils.PrivateKeyToETHAddress(childx.Key) - if err != nil { - return nil, err - } - w.addrs[num] = addrx - w.pkAddrs[num] = pkAddrx - w.ethAddrs[num] = ethAddrx - w.hdChildNumer++ - return addrx, nil -} - -func (w *testWallet) miningAddr() types.Address { - return w.pkAddrs[0] -} diff --git a/testutils/simulator/tx_test.go b/testutils/simulator/tx_test.go new file mode 100644 index 00000000..71a17022 --- /dev/null +++ b/testutils/simulator/tx_test.go @@ -0,0 +1 @@ +package simulator diff --git a/testutils/simulator/utils.go b/testutils/simulator/utils.go new file mode 100644 index 00000000..0b8cc5b6 --- /dev/null +++ b/testutils/simulator/utils.go @@ -0,0 +1,66 @@ +package simulator + +import ( + "github.com/Qitmeer/qng/common/hash" + "github.com/Qitmeer/qng/core/types/pow" + "strconv" + "testing" +) + +// GenerateBlock will generate a number of blocks by the input number +// It will return the hashes of the generated blocks or an error +func GenerateBlock(t *testing.T, node *MockNode, num uint64) []*hash.Hash { + result := make([]*hash.Hash, 0) + blocks, err := node.GetPrivateMinerAPI().Generate(uint32(num), pow.MEERXKECCAKV1) + if err != nil { + t.Errorf("generate block failed : %v", err) + return nil + } + + for _, b := range blocks { + bh := hash.MustHexToDecodedHash(b) + result = append(result, &bh) + t.Logf("%v: generate block [%v] ok", node.ID(), b) + } + return result +} + +// AssertBlockOrderAndHeight will verify the current block order, total block number +// and current main-chain height of the appointed test node and assert it ok or +// cause the test failed. +func AssertBlockOrderAndHeight(t *testing.T, node *MockNode, order, total, height uint) { + // order + c, err := node.GetPublicBlockAPI().GetBlockCount() + if err != nil { + t.Errorf("test failed : %v", err) + } else { + expect := order + if c.(uint) != expect { + t.Errorf("test failed, expect %v , but got %v", expect, c) + } + } + // total block + tal, err := node.GetPublicBlockAPI().GetBlockTotal() + if err != nil { + t.Errorf("test failed : %v", err) + } else { + expect := total + if tal != expect { + t.Errorf("test failed, expect %v , but got %v", expect, tal) + } + } + // main height + h, err := node.GetPublicBlockAPI().GetMainChainHeight() + if err != nil { + t.Errorf("test failed : %v", err) + } else { + expect := height + hi, err := strconv.ParseUint(h.(string), 10, 64) + if err != nil { + t.Errorf("test failed : %v", err) + } + if hi != uint64(expect) { + t.Errorf("test failed, expect %v , but got %v", expect, h) + } + } +} diff --git a/testutils/simulator/wallet_test.go b/testutils/simulator/wallet_test.go new file mode 100644 index 00000000..f2ed33cb --- /dev/null +++ b/testutils/simulator/wallet_test.go @@ -0,0 +1,40 @@ +package simulator + +import ( + "encoding/hex" + "github.com/Qitmeer/qng/config" + "github.com/Qitmeer/qng/testutils/simulator/testprivatekey" + "github.com/ethereum/go-ethereum/common" + "testing" +) + +func TestImportRawKey(t *testing.T) { + node, err := StartMockNode(func(cfg *config.Config) error { + cfg.DebugLevel = "trace" + cfg.DebugPrintOrigins = true + return nil + }) + if err != nil { + t.Fatal(err) + } + defer node.Stop() + + pk, err := node.pb.Build() + if err != nil { + t.Fatal(err) + } + pkHex := hex.EncodeToString(pk) + eaddr, err := node.GetPrivateWalletManagerAPI().ImportRawKey(pkHex, testprivatekey.Password) + if err != nil { + t.Fatal(err) + } + accounts, err := node.GetPrivateWalletManagerAPI().ListAccount() + if err != nil { + t.Fatal(err) + } + accountsM := accounts.([]map[string]interface{}) + ret := accountsM[1]["address"].(common.Address) + if ret.Cmp(eaddr) != 0 { + t.Fatalf("%s != %s", ret.String(), eaddr.String()) + } +}