diff --git a/Makefile b/Makefile index 1b765be43..12051b23f 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,8 @@ else BIN_DIR=$(GOOS)-$(GOARCH) endif NKND_BUILD_PARAM=-ldflags "-s -w -X github.com/nknorg/nkn/v2/config.Version=$(VERSION)" -NKNC_BUILD_PARAM=-ldflags "-s -w -X github.com/nknorg/nkn/v2/cmd/nknc/common.Version=$(VERSION)" +#NKNC_BUILD_PARAM=-ldflags "-s -w -X github.com/nknorg/nkn/v2/cmd/nknc/common.Version=$(VERSION)" +NKNC_BUILD_PARAM=$(NKND_BUILD_PARAM) NKND_OUTPUT=$(BUILD_DIR)/$(BIN_DIR)/nknd$(EXT) NKNC_OUTPUT=$(BUILD_DIR)/$(BIN_DIR)/nknc$(EXT) NKND_MAIN=./cmd/nknd/ diff --git a/cmd/nknc/asset/asset.go b/cmd/nknc/asset/asset.go deleted file mode 100644 index c8d1bb86c..000000000 --- a/cmd/nknc/asset/asset.go +++ /dev/null @@ -1,242 +0,0 @@ -package asset - -import ( - "encoding/hex" - "fmt" - "os" - - api "github.com/nknorg/nkn/v2/api/common" - "github.com/nknorg/nkn/v2/api/httpjson/client" - nknc "github.com/nknorg/nkn/v2/cmd/nknc/common" - "github.com/nknorg/nkn/v2/common" - "github.com/nknorg/nkn/v2/config" - "github.com/nknorg/nkn/v2/util/password" - "github.com/nknorg/nkn/v2/vault" - - "github.com/urfave/cli" -) - -const ( - RANDBYTELEN = 4 -) - -func parseAddress(c *cli.Context) common.Uint160 { - if address := c.String("to"); address != "" { - pg, err := common.ToScriptHash(address) - if err != nil { - fmt.Println("invalid receiver address") - os.Exit(1) - } - return pg - } - fmt.Println("missing flag [--to]") - os.Exit(1) - return common.EmptyUint160 -} - -func assetAction(c *cli.Context) error { - if c.NumFlags() == 0 { - cli.ShowSubcommandHelp(c) - return nil - } - - value := c.String("value") - if value == "" { - fmt.Println("asset amount is required with [--value]") - return nil - } - - var txnFee common.Fixed64 - fee := c.String("fee") - var err error - if fee == "" { - txnFee = common.Fixed64(0) - } else { - txnFee, err = common.StringToFixed64(fee) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - } - - nonce := c.Uint64("nonce") - - var resp []byte - switch { - case c.Bool("issue"): - walletName := c.String("wallet") - passwd := c.String("password") - myWallet, err := vault.OpenWallet(walletName, getPassword(passwd)) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - - name := c.String("name") - if name == "" { - fmt.Println("asset name is required with [--name]") - return nil - } - - symbol := c.String("symbol") - if symbol == "" { - fmt.Println("asset symbol is required with [--symbol]") - return nil - } - - totalSupply, err := common.StringToFixed64(value) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - - precision := uint32(c.Uint("precision")) - if precision > config.MaxAssetPrecision { - err := fmt.Errorf("precision is larger than %v", config.MaxAssetPrecision) - fmt.Fprintln(os.Stderr, err) - return err - } - txn, err := api.MakeIssueAssetTransaction(myWallet, name, symbol, totalSupply, precision, nonce, txnFee) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - - buff, err := txn.Marshal() - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - - resp, err = client.Call(nknc.Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - case c.Bool("transfer"): - walletName := c.String("wallet") - passwd := c.String("password") - myWallet, err := vault.OpenWallet(walletName, getPassword(passwd)) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - receipt := parseAddress(c) - amount, err := common.StringToFixed64(value) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - - if nonce == 0 { - remoteNonce, _, err := client.GetNonceByAddr(nknc.Address(), myWallet.Address, true) - if err != nil { - return err - } - nonce = remoteNonce - } - - txn, err := api.MakeTransferTransaction(myWallet, receipt, nonce, amount, txnFee) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - - buff, err := txn.Marshal() - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - - resp, err = client.Call(nknc.Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - default: - cli.ShowSubcommandHelp(c) - return nil - } - - nknc.FormatOutput(resp) - - return nil -} - -func NewCommand() *cli.Command { - return &cli.Command{ - Name: "asset", - Usage: "asset registration, issuance and transfer", - Description: "With nknc asset, you could control assert through transaction.", - ArgsUsage: "[args]", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "issue, i", - Usage: "issue asset", - }, - cli.BoolFlag{ - Name: "transfer, t", - Usage: "transfer asset", - }, - cli.StringFlag{ - Name: "wallet, w", - Usage: "wallet name", - Value: config.Parameters.WalletFile, - }, - cli.StringFlag{ - Name: "password, p", - Usage: "wallet password", - }, - cli.StringFlag{ - Name: "to", - Usage: "asset to whom", - }, - cli.StringFlag{ - Name: "value, v", - Usage: "asset amount in transfer asset or totalSupply in inssue assset", - Value: "", - }, - cli.StringFlag{ - Name: "fee, f", - Usage: "transaction fee", - Value: "", - }, - cli.Uint64Flag{ - Name: "nonce", - Usage: "nonce", - }, - cli.StringFlag{ - Name: "name", - Usage: "asset name", - }, - cli.StringFlag{ - Name: "symbol", - Usage: "asset symbol", - }, - cli.UintFlag{ - Name: "precision", - Usage: "asset precision", - }, - }, - Action: assetAction, - OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error { - nknc.PrintError(c, err, "asset") - return cli.NewExitError("", 1) - }, - } -} - -func getPassword(passwd string) []byte { - var tmp []byte - var err error - if passwd != "" { - tmp = []byte(passwd) - } else { - tmp, err = password.GetPassword("") - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - } - return tmp -} diff --git a/cmd/nknc/commands/asset.go b/cmd/nknc/commands/asset.go new file mode 100644 index 000000000..5c4bb977d --- /dev/null +++ b/cmd/nknc/commands/asset.go @@ -0,0 +1,177 @@ +package commands + +import ( + "encoding/hex" + "fmt" + "os" + + api "github.com/nknorg/nkn/v2/api/common" + "github.com/nknorg/nkn/v2/api/httpjson/client" + "github.com/nknorg/nkn/v2/common" + "github.com/nknorg/nkn/v2/config" + "github.com/nknorg/nkn/v2/vault" + "github.com/spf13/cobra" +) + +const ( + RANDBYTELEN = 4 +) + +// assetCmd represents the asset command +var assetCmd = &cobra.Command{ + Use: "asset", + Short: "asset registration, issuance and transfer", + Long: "", + RunE: func(cmd *cobra.Command, args []string) error { + return assetAction(cmd) + }, +} + +var ( + issue bool + transfer bool + to string + value string + assetname string + symbol string + precision uint32 +) + +func init() { + rootCmd.AddCommand(assetCmd) + + assetCmd.Flags().BoolVarP(&issue, "issue", "i", false, "issue asset") + assetCmd.Flags().BoolVarP(&transfer, "transfer", "t", false, "transfer asset") + assetCmd.Flags().StringVarP(&walletFile, "wallet", "w", config.Parameters.WalletFile, "wallet name") + assetCmd.Flags().StringVar(&walletPassword, "password", "", "wallet password") + assetCmd.Flags().StringVar(&to, "to", "", "asset to whom") + assetCmd.Flags().StringVarP(&value, "value", "v", "", "asset amount in transfer asset or totalSupply in inssue assset") + assetCmd.Flags().StringVarP(&fee, "fee", "f", "", "transaction fee") + assetCmd.Flags().Uint64Var(&nonce, "nonce", 0, "nonce") + assetCmd.Flags().StringVar(&assetname, "name", "", "asset name") + assetCmd.Flags().StringVar(&symbol, "symbol", "", "asset symbol") + assetCmd.Flags().Uint32Var(&precision, "precision", 0, "asset precision") + + assetCmd.Flags().MarkHidden("password") +} + +func parseAddress() common.Uint160 { + if address := to; address != "" { + pg, err := common.ToScriptHash(address) + if err != nil { + fmt.Println("invalid receiver address") + os.Exit(1) + } + return pg + } + fmt.Println("missing flag [--to]") + os.Exit(1) + return common.EmptyUint160 +} + +func assetAction(cmd *cobra.Command) error { + var err error + var txnFee common.Fixed64 + + if fee == "" { + txnFee = common.Fixed64(0) + } else { + txnFee, err = common.StringToFixed64(fee) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + } + + var resp []byte + switch { + case issue: + myWallet, err := vault.OpenWallet(walletFile, GetPassword(walletPassword)) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + if assetname == "" { + return fmt.Errorf("asset name is required with [--name]") + } + + if symbol == "" { + return fmt.Errorf("asset symbol is required with [--symbol]") + } + + totalSupply, err := common.StringToFixed64(value) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + + if precision > config.MaxAssetPrecision { + err := fmt.Errorf("precision is larger than %v", config.MaxAssetPrecision) + fmt.Fprintln(os.Stderr, err) + return err + } + + txn, err := api.MakeIssueAssetTransaction(myWallet, assetname, symbol, totalSupply, precision, nonce, txnFee) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + + buff, err := txn.Marshal() + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + + resp, err = client.Call(Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + case transfer: + myWallet, err := vault.OpenWallet(walletFile, GetPassword(walletPassword)) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + receipt := parseAddress() + amount, err := common.StringToFixed64(value) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + + if nonce == 0 { + remoteNonce, _, err := client.GetNonceByAddr(Address(), myWallet.Address, true) + if err != nil { + return err + } + nonce = remoteNonce + } + + txn, err := api.MakeTransferTransaction(myWallet, receipt, nonce, amount, txnFee) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + + buff, err := txn.Marshal() + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + + resp, err = client.Call(Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + default: + return cmd.Usage() + } + + FormatOutput(resp) + + return nil +} diff --git a/cmd/nknc/commands/debug.go b/cmd/nknc/commands/debug.go new file mode 100644 index 000000000..b5a293707 --- /dev/null +++ b/cmd/nknc/commands/debug.go @@ -0,0 +1,42 @@ +package commands + +import ( + "fmt" + "os" + + "github.com/nknorg/nkn/v2/api/httpjson/client" + "github.com/spf13/cobra" +) + +// debugCmd represents the debug command +var debugCmd = &cobra.Command{ + Use: "debug", + Short: "blockchain node debugging", + Long: "With nknc debug, you could debug blockchain node.", + Run: func(cmd *cobra.Command, args []string) { + debugAction() + }, +} + +var ( + level int +) + +func init() { + rootCmd.AddCommand(debugCmd) + + debugCmd.Flags().IntVarP(&level, "level", "l", -1, "log level 0-6") + debugCmd.MarkFlagRequired("level") +} + +func debugAction() (err error) { + if level != -1 { + resp, err := client.Call(Address(), "setdebuginfo", 0, map[string]interface{}{"level": level}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + FormatOutput(resp) + } + return nil +} diff --git a/cmd/nknc/commands/id.go b/cmd/nknc/commands/id.go new file mode 100644 index 000000000..41d5f5444 --- /dev/null +++ b/cmd/nknc/commands/id.go @@ -0,0 +1,125 @@ +package commands + +import ( + "context" + "encoding/hex" + "fmt" + "os" + + api "github.com/nknorg/nkn/v2/api/common" + "github.com/nknorg/nkn/v2/api/httpjson/client" + "github.com/nknorg/nkn/v2/common" + "github.com/nknorg/nkn/v2/config" + "github.com/nknorg/nkn/v2/vault" + "github.com/spf13/cobra" +) + +// idCmd represents the id command +var idCmd = &cobra.Command{ + Use: "id", + Short: "generate id for nknd", + Long: "With nknc id, you could generate ID.", + Run: func(cmd *cobra.Command, args []string) { + generateIDAction(cmd) + }, +} + +var ( + genid bool + pubkeyHex string +) + +func init() { + rootCmd.AddCommand(idCmd) + + idCmd.Flags().BoolVar(&genid, "genid", false, "generate id") + idCmd.Flags().StringVar(&pubkeyHex, "pubkey", "", "pubkey to generate id for, leave empty for local wallet pubkey") + idCmd.Flags().StringVarP(&walletFile, "wallet", "w", config.Parameters.WalletFile, "wallet name") + idCmd.Flags().StringVarP(&walletPassword, "password", "p", "", "wallet password") + idCmd.Flags().StringVar(®fee, "regfee", "", "registration fee") + idCmd.Flags().StringVarP(&fee, "fee", "f", "", "transaction fee") + idCmd.Flags().Uint64Var(&nonce, "nonce", 0, "nonce") +} + +func generateIDAction(cmd *cobra.Command) error { + pubkey, err := hex.DecodeString(pubkeyHex) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + myWallet, err := vault.OpenWallet(walletFile, GetPassword(walletPassword)) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + var txnFee common.Fixed64 + if regfee == "" { + txnFee = common.Fixed64(0) + } else { + txnFee, err = common.StringToFixed64(regfee) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + } + + var regFee common.Fixed64 + fee = regfee + if fee == "" { + regFee = common.Fixed64(0) + } else { + regFee, err = common.StringToFixed64(fee) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + } + + var resp []byte + switch { + case genid: + account, err := myWallet.GetDefaultAccount() + if err != nil { + return err + } + + walletAddr, err := account.ProgramHash.ToAddress() + if err != nil { + return err + } + + remoteNonce, height, err := client.GetNonceByAddr(Address(), walletAddr, true) + if err != nil { + return err + } + + if nonce == 0 { + nonce = remoteNonce + } + + txn, err := api.MakeGenerateIDTransaction(context.Background(), pubkey, myWallet, regFee, nonce, txnFee, height+1) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + + buff, err := txn.Marshal() + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + + resp, err = client.Call(Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + default: + return cmd.Usage() + } + FormatOutput(resp) + + return nil +} diff --git a/cmd/nknc/commands/info.go b/cmd/nknc/commands/info.go new file mode 100644 index 000000000..49a3701de --- /dev/null +++ b/cmd/nknc/commands/info.go @@ -0,0 +1,368 @@ +package commands + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "os" + + "github.com/nknorg/nkn/v2/api/httpjson/client" + "github.com/nknorg/nkn/v2/pb" + "github.com/nknorg/nkn/v2/util/log" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "google.golang.org/protobuf/proto" +) + +// infoCmd represents the info command +var infoCmd = &cobra.Command{ + Use: "info", + Short: "show blockchain information", + Long: "", + RunE: func(cmd *cobra.Command, args []string) error { + showusage := true + cmd.Flags().Visit(func(name *pflag.Flag) { + showusage = false + }) + if showusage { + return cmd.Usage() + } + if err := infoAction(); err != nil { + log.Error(err) + } + return nil + }, +} + +var ( + pretty bool + blockhash string + txhash string + latestblockhash bool + height int + header int + headerhash string + blockcount bool + connections bool + neighbor bool + ring bool + state bool + nodeversion bool + balance string + idstr string +) + +func init() { + rootCmd.AddCommand(infoCmd) + + infoCmd.Flags().BoolVarP(&pretty, "pretty", "p", false, "pretty print") + infoCmd.Flags().StringVarP(&blockhash, "blockhash", "b", "", "hash for querying a block") + infoCmd.Flags().StringVarP(&txhash, "txhash", "t", "", "hash for querying a transaction") + infoCmd.Flags().BoolVar(&latestblockhash, "latestblockhash", false, "latest block hash") + infoCmd.Flags().IntVar(&height, "height", -1, "block height for querying a block") + infoCmd.Flags().IntVar(&header, "header", -1, "get block header by height") + infoCmd.Flags().StringVar(&headerhash, "headerhash", "", "get block header by hash") + infoCmd.Flags().BoolVarP(&blockcount, "blockcount", "c", false, "block number in blockchain") + infoCmd.Flags().BoolVar(&connections, "connections", false, "connection count") + infoCmd.Flags().BoolVar(&neighbor, "neighbor", false, "neighbor information of current node") + infoCmd.Flags().BoolVar(&ring, "ring", false, "chord ring information of current node") + infoCmd.Flags().BoolVarP(&state, "state", "s", false, "current node state") + infoCmd.Flags().BoolVarP(&nodeversion, "nodeversion", "v", false, "version of connected remote node") + infoCmd.Flags().StringVar(&balance, "balance", "", "balance of a address") + infoCmd.Flags().Uint64Var(&nonce, "nonce", 0, "nonce of a address") + infoCmd.Flags().StringVar(&idstr, "id", "", "id from publickey") + +} + +func infoAction() (err error) { + var resp []byte + var output [][]byte + + version := nodeversion + id := idstr + + if height >= 0 { + resp, err = client.Call(Address(), "getblock", 0, map[string]interface{}{"height": height}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + if pretty { + if p, err := PrettyPrinter(resp).PrettyBlock(); err == nil { + resp = p // replace resp if pretty success + } else { + fmt.Fprintln(os.Stderr, "Fallback to original resp due to PrettyPrint fail: ", err) + } + } + output = append(output, resp) + } + + if len(blockhash) > 0 { + resp, err = client.Call(Address(), "getblock", 0, map[string]interface{}{"hash": blockhash}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + if pretty { + if p, err := PrettyPrinter(resp).PrettyBlock(); err == nil { + resp = p // replace resp if pretty success + } else { + fmt.Fprintln(os.Stderr, "Fallback to original resp due to PrettyPrint fail: ", err) + } + } + output = append(output, resp) + } + + if header >= 0 { + resp, err = client.Call(Address(), "getheader", 0, map[string]interface{}{"height": header}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + output = append(output, resp) + } + + if len(headerhash) > 0 { + resp, err = client.Call(Address(), "getheader", 0, map[string]interface{}{"hash": headerhash}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + output = append(output, resp) + } + + if latestblockhash { + resp, err = client.Call(Address(), "getlatestblockhash", 0, map[string]interface{}{}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + output = append(output, resp) + } + + if blockcount { + resp, err = client.Call(Address(), "getblockcount", 0, map[string]interface{}{}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + output = append(output, resp) + } + + if connections { + resp, err = client.Call(Address(), "getconnectioncount", 0, map[string]interface{}{}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + output = append(output, resp) + } + + if neighbor { + resp, err := client.Call(Address(), "getneighbor", 0, map[string]interface{}{}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + output = append(output, resp) + } + + if ring { + resp, err := client.Call(Address(), "getchordringinfo", 0, map[string]interface{}{}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + output = append(output, resp) + } + + if state { + resp, err := client.Call(Address(), "getnodestate", 0, map[string]interface{}{}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + output = append(output, resp) + } + + if txhash != "" { + resp, err = client.Call(Address(), "gettransaction", 0, map[string]interface{}{"hash": txhash}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + if pretty { + if p, err := PrettyPrinter(resp).PrettyTxn(); err == nil { + resp = p // replace resp if pretty success + } else { + fmt.Fprintln(os.Stderr, "Output origin resp due to PrettyPrint fail: ", err) + } + } + output = append(output, resp) + } + + if version { + resp, err = client.Call(Address(), "getversion", 0, map[string]interface{}{}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + output = append(output, resp) + + } + + if balance != "" { + resp, err := client.Call(Address(), "getbalancebyaddr", 0, map[string]interface{}{"address": balance}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + output = append(output, resp) + } + + if nonce > 0 { + resp, err := client.Call(Address(), "getNoncebyaddr", 0, map[string]interface{}{"address": fmt.Sprintf("%d", nonce)}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + output = append(output, resp) + } + + if id != "" { + resp, err := client.Call(Address(), "getid", 0, map[string]interface{}{"publickey": id}) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + output = append(output, resp) + } + + for _, v := range output { + FormatOutput(v) + } + + return nil +} + +type PrettyPrinter []byte + +// TxnUnmarshal function +func TxnUnmarshal(m map[string]interface{}) (interface{}, error) { + typ, ok := m["txType"] + if !ok { + return nil, fmt.Errorf("No such key [txType]") + } + + pbHexStr, ok := m["payloadData"] + if !ok { + return m, nil + } + + buf, err := hex.DecodeString(pbHexStr.(string)) + if err != nil { + return nil, err + } + + switch typ { + case pb.PayloadType_name[int32(pb.PayloadType_SIG_CHAIN_TXN_TYPE)]: + sigChainTxn := &pb.SigChainTxn{} + if err = proto.Unmarshal(buf, sigChainTxn); err == nil { // bin to pb struct of SigChainTxnType txn + m["payloadData"] = sigChainTxn.ToMap() + } + case pb.PayloadType_name[int32(pb.PayloadType_COINBASE_TYPE)]: + coinBaseTxn := &pb.Coinbase{} + if err = proto.Unmarshal(buf, coinBaseTxn); err == nil { // bin to pb struct of Coinbase txn + m["payloadData"] = coinBaseTxn.ToMap() + } + case pb.PayloadType_name[int32(pb.PayloadType_TRANSFER_ASSET_TYPE)]: + trans := &pb.TransferAsset{} + if err = proto.Unmarshal(buf, trans); err == nil { // bin to pb struct of Coinbase txn + m["payloadData"] = trans.ToMap() + } + case pb.PayloadType_name[int32(pb.PayloadType_GENERATE_ID_TYPE)]: + genID := &pb.GenerateID{} + if err = proto.Unmarshal(buf, genID); err == nil { // bin to pb struct of Coinbase txn + m["payloadData"] = genID.ToMap() + } + case pb.PayloadType_name[int32(pb.PayloadType_REGISTER_NAME_TYPE)]: + regName := &pb.RegisterName{} + if err = proto.Unmarshal(buf, regName); err == nil { // bin to pb struct of Coinbase txn + m["payloadData"] = regName.ToMap() + } + case pb.PayloadType_name[int32(pb.PayloadType_SUBSCRIBE_TYPE)]: + sub := &pb.Subscribe{} + if err = proto.Unmarshal(buf, sub); err == nil { // bin to pb struct of Coinbase txn + m["payloadData"] = sub.ToMap() + } + case pb.PayloadType_name[int32(pb.PayloadType_UNSUBSCRIBE_TYPE)]: + sub := &pb.Unsubscribe{} + if err = proto.Unmarshal(buf, sub); err == nil { // bin to pb struct of Coinbase txn + m["payloadData"] = sub.ToMap() + } + case pb.PayloadType_name[int32(pb.PayloadType_NANO_PAY_TYPE)]: + pay := &pb.NanoPay{} + if err = proto.Unmarshal(buf, pay); err == nil { // bin to pb struct of Coinbase txn + m["payloadData"] = pay.ToMap() + } + case pb.PayloadType_name[int32(pb.PayloadType_TRANSFER_NAME_TYPE)]: + fallthrough //TODO + case pb.PayloadType_name[int32(pb.PayloadType_DELETE_NAME_TYPE)]: + fallthrough //TODO + case pb.PayloadType_name[int32(pb.PayloadType_ISSUE_ASSET_TYPE)]: + fallthrough //TODO + default: + return nil, fmt.Errorf("Unknow txType[%s] for pretty print", typ) + } + + return m, nil +} + +func (resp PrettyPrinter) PrettyTxn() ([]byte, error) { + m := map[string]interface{}{} + err := json.Unmarshal(resp, &m) + if err != nil { + return nil, err + } + + v, ok := m["result"] + if !ok { + return nil, fmt.Errorf("response No such key [result]") + } + + if m["result"], err = TxnUnmarshal(v.(map[string]interface{})); err != nil { + return nil, err + } + + return json.Marshal(m) +} + +func (resp PrettyPrinter) PrettyBlock() ([]byte, error) { + m := map[string]interface{}{} + err := json.Unmarshal(resp, &m) + if err != nil { + return nil, err + } + + ret, ok := m["result"] + if !ok { + return nil, fmt.Errorf("response No such key [result]") + } + + txns, ok := ret.(map[string]interface{})["transactions"] + if !ok { + return nil, fmt.Errorf("result No such key [transactions]") + } + + lst := make([]interface{}, 0) + for _, t := range txns.([]interface{}) { + if m, err := TxnUnmarshal(t.(map[string]interface{})); err == nil { + lst = append(lst, m) + } else { + lst = append(lst, t) // append origin txn if TxnUnmarshal fail + } + } + m["transactions"] = lst + + return json.Marshal(m) +} diff --git a/cmd/nknc/commands/name.go b/cmd/nknc/commands/name.go new file mode 100644 index 000000000..09e52353f --- /dev/null +++ b/cmd/nknc/commands/name.go @@ -0,0 +1,128 @@ +package commands + +import ( + "encoding/hex" + "fmt" + "os" + + api "github.com/nknorg/nkn/v2/api/common" + "github.com/nknorg/nkn/v2/api/httpjson/client" + "github.com/nknorg/nkn/v2/common" + "github.com/nknorg/nkn/v2/config" + "github.com/nknorg/nkn/v2/vault" + "github.com/spf13/cobra" +) + +// nameCmd represents the name command +var nameCmd = &cobra.Command{ + Use: "name", + Short: "name registration", + Long: "With nknc name, you could register name for your address.", + RunE: func(cmd *cobra.Command, args []string) error { + return nameAction(cmd) + }, +} + +var ( + reg bool + del bool + get bool + name string + regfee string +) + +func init() { + rootCmd.AddCommand(nameCmd) + + nameCmd.Flags().BoolVarP(®, "reg", "r", false, "register name for your address") + nameCmd.Flags().BoolVarP(&del, "del", "d", false, "delete name of your address") + nameCmd.Flags().BoolVarP(&get, "get", "g", false, "get register name info") + nameCmd.Flags().BoolVarP(&transfer, "transfer", "t", false, "transfer name to another address") + nameCmd.Flags().StringVar(&name, "name", "", "name") + nameCmd.Flags().StringVarP(&walletFile, "wallet", "w", config.Parameters.WalletFile, "wallet name") + nameCmd.Flags().StringVar(&walletPassword, "password", "", "wallet password") + nameCmd.Flags().StringVarP(&fee, "fee", "f", "", "transaction fee") + nameCmd.Flags().Uint64Var(&nonce, "nonce", 0, "nonce") + nameCmd.Flags().StringVar(®fee, "regfee", "", "regfee") + nameCmd.Flags().StringVar(&to, "to", "", "transfer name to addr") + + nameCmd.MarkFlagRequired("password") +} + +func nameAction(cmd *cobra.Command) error { + myWallet, err := vault.OpenWallet(walletFile, GetPassword(walletPassword)) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + var txnFee common.Fixed64 + if fee == "" { + txnFee = common.Fixed64(0) + } else { + txnFee, _ = common.StringToFixed64(fee) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + } + + var regFee common.Fixed64 + if regfee == "" { + regFee = common.Fixed64(0) + } else { + regFee, _ = common.StringToFixed64(regfee) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + } + + var resp []byte + switch { + case reg: + if name == "" { + return fmt.Errorf("name is required with [--name]") + } + txn, _ := api.MakeRegisterNameTransaction(myWallet, name, nonce, regFee, txnFee) + buff, _ := txn.Marshal() + resp, err = client.Call(Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) + case transfer: + if name == "" { + return fmt.Errorf("name is required with [--name]") + } + if to == "" { + return fmt.Errorf("transfer is required with [--to]") + } + var toBytes []byte + toBytes, err = hex.DecodeString(to) + if err != nil { + return err + } + txn, _ := api.MakeTransferNameTransaction(myWallet, name, nonce, txnFee, toBytes) + buff, _ := txn.Marshal() + resp, err = client.Call(Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) + case del: + if name == "" { + return fmt.Errorf("name is required with [--name]") + } + + txn, _ := api.MakeDeleteNameTransaction(myWallet, name, nonce, txnFee) + buff, _ := txn.Marshal() + resp, err = client.Call(Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) + case get: + if name == "" { + return fmt.Errorf("name is required with [--name]") + } + resp, err = client.Call(Address(), "getregistrant", 0, map[string]interface{}{"name": name}) + default: + return cmd.Usage() + } + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + FormatOutput(resp) + + return nil +} diff --git a/cmd/nknc/commands/pruning.go b/cmd/nknc/commands/pruning.go new file mode 100644 index 000000000..e982d4b62 --- /dev/null +++ b/cmd/nknc/commands/pruning.go @@ -0,0 +1,106 @@ +package commands + +import ( + "fmt" + + "github.com/nknorg/nkn/v2/chain/store" + "github.com/spf13/cobra" +) + +// pruningCmd represents the pruning command +var pruningCmd = &cobra.Command{ + Use: "pruning", + Short: "state trie pruning for nknd", + Long: "state trie pruning for nknd", + RunE: func(cmd *cobra.Command, args []string) error { + return pruningAction(cmd) + }, +} + +var ( + currentheight bool + startheights bool + pruningB bool + seq bool + lowmem bool + dumpnodes bool + verifystate bool +) + +func init() { + rootCmd.AddCommand(pruningCmd) + + pruningCmd.Flags().BoolVar(¤theight, "currentheight", false, "current block height of offline db") + pruningCmd.Flags().BoolVar(&startheights, "startheights", false, "start heights of refcount and pruning") + pruningCmd.Flags().BoolVar(&pruningB, "pruning", false, "prune state trie") + pruningCmd.Flags().BoolVar(&seq, "seq", false, "prune state trie sequential mode") + pruningCmd.Flags().BoolVar(&lowmem, "lowmem", false, "prune state trie low memory mode") + pruningCmd.Flags().BoolVar(&dumpnodes, "dumpnodes", false, "dump nodes of tri") + pruningCmd.Flags().BoolVar(&verifystate, "verifystate", false, "verify state of ledger") +} + +func pruningAction(cmd *cobra.Command) error { + switch { + case currentheight: + cs, err := store.NewLedgerStore() + if err != nil { + return err + } + _, h, err := cs.GetCurrentBlockHashFromDB() + if err != nil { + return err + } + fmt.Println(h) + case startheights: + cs, err := store.NewLedgerStore() + if err != nil { + return err + } + refCountStartHeight, pruningStartHeight := cs.GetPruningStartHeight() + fmt.Println(refCountStartHeight, pruningStartHeight) + case pruningB: + cs, err := store.NewLedgerStore() + if err != nil { + return err + } + + if seq { + err := cs.SequentialPrune() + if err != nil { + return err + } + } else if lowmem { + err := cs.PruneStatesLowMemory(true) + if err != nil { + return err + } + } else { + err := cs.PruneStates() + if err != nil { + return err + } + } + case dumpnodes: + cs, err := store.NewLedgerStore() + if err != nil { + return err + } + err = cs.TrieTraverse(true) + if err != nil { + return err + } + case verifystate: + cs, err := store.NewLedgerStore() + if err != nil { + return err + } + err = cs.VerifyState() + if err != nil { + return err + } + default: + return cmd.Usage() + } + + return nil +} diff --git a/cmd/nknc/commands/pubsub.go b/cmd/nknc/commands/pubsub.go new file mode 100644 index 000000000..91c26ac90 --- /dev/null +++ b/cmd/nknc/commands/pubsub.go @@ -0,0 +1,131 @@ +package commands + +import ( + "encoding/hex" + "fmt" + "os" + + api "github.com/nknorg/nkn/v2/api/common" + "github.com/nknorg/nkn/v2/api/httpjson/client" + "github.com/nknorg/nkn/v2/common" + "github.com/nknorg/nkn/v2/config" + "github.com/nknorg/nkn/v2/vault" + "github.com/spf13/cobra" +) + +// pubsubCmd represents the pubsub command +var pubsubCmd = &cobra.Command{ + Use: "pubsub", + Short: "manage topic subscriptions", + Long: "With nknc pubsub, you could manage topic subscriptions.", + RunE: func(cmd *cobra.Command, args []string) error { + return subscribeAction(cmd) + }, +} + +var ( + sub bool + unsub bool + identifier string + topic string + duration uint64 + offset uint64 + limit uint64 + subscriber string + meta string +) + +func init() { + rootCmd.AddCommand(pubsubCmd) + + pubsubCmd.Flags().BoolVarP(&sub, "sub", "s", false, "subscribe to topic") + pubsubCmd.Flags().BoolVarP(&unsub, "unsub", "u", false, "unsubscribe from topic") + pubsubCmd.Flags().BoolVarP(&get, "get", "g", false, "get subscribes list of specified topic or get details info of a specified subscriber") + pubsubCmd.Flags().StringVar(&identifier, "id", "", "identifier") + pubsubCmd.Flags().StringVar(&topic, "topic", "", "topic") + pubsubCmd.Flags().Uint64Var(&duration, "duration", 0, "duration") + pubsubCmd.Flags().Uint64Var(&offset, "offset", 0, "get subscribes skip previous offset items") + pubsubCmd.Flags().Uint64Var(&limit, "limit", 0, "limit subscribes amount which start from offset") + pubsubCmd.Flags().StringVar(&subscriber, "subscriber", "", "specified subscriber which you want details") + pubsubCmd.Flags().StringVar(&meta, "meta", "", "meta") + pubsubCmd.Flags().StringVarP(&walletFile, "wallet", "w", config.Parameters.WalletFile, "wallet name") + pubsubCmd.Flags().StringVarP(&walletPassword, "password", "p", "", "wallet password") + pubsubCmd.Flags().StringVarP(&fee, "fee", "f", "", "transaction fee") + pubsubCmd.Flags().Uint64Var(&nonce, "nonce", 0, "nonce") + +} + +func subscribeAction(cmd *cobra.Command) error { + myWallet, err := vault.OpenWallet(walletFile, GetPassword(walletPassword)) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + var txnFee common.Fixed64 + if fee == "" { + txnFee = common.Fixed64(0) + } else { + txnFee, _ = common.StringToFixed64(fee) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + } + + var resp []byte + switch { + case sub: + if identifier == "" { + return fmt.Errorf("identifier is required with [--id]") + } + + if topic == "" { + return fmt.Errorf("topic is required with [--topic]") + } + + if meta == "" { + return fmt.Errorf("meta is required with [--meta]") + } + + txn, _ := api.MakeSubscribeTransaction(myWallet, identifier, topic, uint32(duration), meta, nonce, txnFee) + buff, _ := txn.Marshal() + resp, err = client.Call(Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) + case unsub: + id := identifier + if id == "" { + return fmt.Errorf("identifier is required with [--id]") + } + + if topic == "" { + return fmt.Errorf("topic is required with [--topic]") + } + + txn, _ := api.MakeUnsubscribeTransaction(myWallet, id, topic, nonce, txnFee) + buff, _ := txn.Marshal() + resp, err = client.Call(Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) + case get: + if topic == "" { + return fmt.Errorf("topic is required with [--topic]") + } + + params := map[string]interface{}{"topic": topic} + if subscriber == "" { // get list of topic + params["offset"] = offset + params["limit"] = limit + resp, err = client.Call(Address(), "getsubscribers", 0, params) + } else { // get details of specified subscriber + params["subscriber"] = subscriber + resp, err = client.Call(Address(), "getsubscription", 0, params) + } + default: + return cmd.Usage() + } + if err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + FormatOutput(resp) + + return nil +} diff --git a/cmd/nknc/commands/root.go b/cmd/nknc/commands/root.go new file mode 100644 index 000000000..ff31f9812 --- /dev/null +++ b/cmd/nknc/commands/root.go @@ -0,0 +1,88 @@ +package commands + +import ( + "bytes" + "encoding/json" + "fmt" + "math/rand" + "net" + "os" + "strconv" + "time" + + "github.com/howeyc/gopass" + "github.com/nknorg/nkn/v2/config" + "github.com/spf13/cobra" +) + +// Globals +var ( + walletFile string + walletPassword string + nonce uint64 + fee string + ip string + port string +) + +var rootCmd = &cobra.Command{ + Use: "nknc", + Version: config.Version, + Short: "nknc - A cli tool for the NKN blockchain", + Long: "", +} + +// RootCmd function  +func RootCmd() *cobra.Command { + return rootCmd +} + +// Execute function  +func Execute() { + cobra.CheckErr(rootCmd.Execute()) +} + +// init function  +func init() { + rand.Seed(time.Now().UnixNano()) + + rootCmd.CompletionOptions.DisableDefaultCmd = true + rootCmd.PersistentFlags().StringVar(&ip, "ip", "localhost", "node's ip address") + rootCmd.PersistentFlags().StringVar(&port, "port", strconv.Itoa(int(config.Parameters.HttpJsonPort)), "node's ip address") +} + +// Address function  +func Address() string { + return "http://" + net.JoinHostPort(ip, port) +} + +// FormatOutput function  +func FormatOutput(o []byte) error { + var out bytes.Buffer + err := json.Indent(&out, o, "", "\t") + if err != nil { + return err + } + out.Write([]byte("\n")) + _, err = out.WriteTo(os.Stdout) + + return err +} + +// GetPassword function  +func GetPassword(passwd string) []byte { + var tmp []byte + var err error + + if passwd != "" { + tmp = []byte(passwd) + } else { + fmt.Printf("%s:", "Password") + tmp, err = gopass.GetPasswd() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + } + return tmp +} diff --git a/cmd/nknc/service/service.go b/cmd/nknc/commands/service.go similarity index 53% rename from cmd/nknc/service/service.go rename to cmd/nknc/commands/service.go index 72961fcfc..777625a4a 100644 --- a/cmd/nknc/service/service.go +++ b/cmd/nknc/commands/service.go @@ -1,4 +1,4 @@ -package service +package commands import ( "encoding/json" @@ -7,21 +7,35 @@ import ( "net/http" "time" - nknc "github.com/nknorg/nkn/v2/cmd/nknc/common" "github.com/nknorg/nkn/v2/util/log" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) const requestTimeout = 5 * time.Second -func serviceAction(c *cli.Context) error { - if c.NumFlags() == 0 { - cli.ShowSubcommandHelp(c) - return nil - } +// serviceCmd represents the service command +var serviceCmd = &cobra.Command{ + Use: "service", + Short: "NKN-based service", + Long: "NKN-based service.", + RunE: func(cmd *cobra.Command, args []string) error { + return serviceAction(cmd) + }, +} + +var ( + list bool +) + +func init() { + rootCmd.AddCommand(serviceCmd) + serviceCmd.Flags().BoolVarP(&list, "list", "l", false, "show nkn service list") +} + +func serviceAction(cmd *cobra.Command) error { switch { - case c.Bool("list"): + case list: var netClient = &http.Client{ Timeout: requestTimeout, } @@ -46,29 +60,8 @@ func serviceAction(c *cli.Context) error { fmt.Println(out["post_stream"].(map[string]interface{})["posts"].([]interface{})[0].(map[string]interface{})["raw"]) default: - cli.ShowSubcommandHelp(c) - return nil + return cmd.Usage() } return nil } - -func NewCommand() *cli.Command { - return &cli.Command{ - Name: "service", - Usage: "NKN-based service", - Description: "NKN-based service.", - ArgsUsage: "[args]", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "list, ls", - Usage: "show nkn service list", - }, - }, - Action: serviceAction, - OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error { - nknc.PrintError(c, err, "service") - return cli.NewExitError("", 1) - }, - } -} diff --git a/cmd/nknc/wallet/wallet.go b/cmd/nknc/commands/wallet.go similarity index 51% rename from cmd/nknc/wallet/wallet.go rename to cmd/nknc/commands/wallet.go index 85014a29d..a57ed056d 100644 --- a/cmd/nknc/wallet/wallet.go +++ b/cmd/nknc/commands/wallet.go @@ -1,4 +1,4 @@ -package wallet +package commands import ( "encoding/hex" @@ -6,81 +6,56 @@ import ( "os" "github.com/nknorg/nkn/v2/api/httpjson/client" - nknc "github.com/nknorg/nkn/v2/cmd/nknc/common" "github.com/nknorg/nkn/v2/common" - "github.com/nknorg/nkn/v2/crypto" "github.com/nknorg/nkn/v2/config" + "github.com/nknorg/nkn/v2/crypto" "github.com/nknorg/nkn/v2/util/password" "github.com/nknorg/nkn/v2/vault" - - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func showAccountInfo(wallet *vault.Wallet, verbose bool) { - const format = "%-37s %s\n" - account, _ := wallet.GetDefaultAccount() - fmt.Printf(format, "Address", "Public Key") - fmt.Printf(format, "-------", "----------") - address, _ := account.ProgramHash.ToAddress() - publicKey := account.PublicKey - fmt.Printf(format, address, hex.EncodeToString(publicKey)) - if verbose { - fmt.Printf("\nSecret Seed\n-----------\n%s\n", hex.EncodeToString(crypto.GetSeedFromPrivateKey(account.PrivateKey))) - } +// walletCmd represents the wallet command +var walletCmd = &cobra.Command{ + Use: "wallet", + Short: "user wallet operation", + Long: "With nknc wallet, you could control your asset.", + RunE: func(cmd *cobra.Command, args []string) error { + return walletAction() + }, } -func getPassword(passwd string) []byte { - var tmp []byte - var err error - if passwd != "" { - tmp = []byte(passwd) - } else { - tmp, err = password.GetPassword("") - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - } - return tmp -} +var ( + create bool + restore string + changepassword bool + reset bool + listflag string +) -func getConfirmedPassword(passwd string) []byte { - var tmp []byte - var err error - if passwd != "" { - tmp = []byte(passwd) - } else { - tmp, err = password.GetConfirmedPassword() - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - } - return tmp +func init() { + rootCmd.AddCommand(walletCmd) + + walletCmd.Flags().BoolVarP(&create, "create", "c", false, "create wallet") + walletCmd.Flags().StringVarP(&listflag, "list", "l", "", "list wallet information [account, balance, verbose, nonce]") + walletCmd.Flags().StringVarP(&restore, "restore", "r", "", "restore wallet with [hex, bin] format PrivateKey") + walletCmd.Flags().BoolVar(&changepassword, "changepassword", false, "change wallet password") + walletCmd.Flags().BoolVar(&reset, "reset", false, "reset wallet") + walletCmd.Flags().StringVarP(&walletFile, "name", "n", config.Parameters.WalletFile, "wallet file") + walletCmd.Flags().StringVarP(&walletPassword, "password", "p", "", "wallet password") } -func walletAction(c *cli.Context) error { - if c.NumFlags() == 0 { - cli.ShowSubcommandHelp(c) - return nil - } - // wallet file name - name := c.String("name") - if name == "" { - return fmt.Errorf("invalid wallet name") - } +func walletAction() error { // get password from the command line or from environment variable - passwd := c.String("password") - if passwd == "" { - passwd = os.Getenv("NKN_WALLET_PASSWORD") + if walletPassword == "" { + walletPassword = os.Getenv("NKN_WALLET_PASSWORD") } // create wallet - if c.Bool("create") { - if common.FileExisted(name) { - return fmt.Errorf("CAUTION: '%s' already exists!\n", name) + if create { + if common.FileExisted(walletFile) { + return fmt.Errorf("CAUTION: '%s' already exists!\n", walletFile) } - wallet, err := vault.NewWallet(name, getConfirmedPassword(passwd)) + wallet, err := vault.NewWallet(walletFile, getConfirmedPassword(walletPassword)) if err != nil { return err } @@ -89,15 +64,15 @@ func walletAction(c *cli.Context) error { } var hexFmt bool - switch format := c.String("restore"); format { + switch format := restore; format { case "": // Not restore mode break case "hex": hexFmt = true fallthrough case "bin": - if common.FileExisted(name) { - return fmt.Errorf("CAUTION: '%s' already exists!\n", name) + if common.FileExisted(walletFile) { + return fmt.Errorf("CAUTION: '%s' already exists!\n", walletFile) } key, err := password.GetPassword("Input you Secret Seed") @@ -110,23 +85,23 @@ func walletAction(c *cli.Context) error { return fmt.Errorf("Invalid hex. %v\n", err) } } - wallet, err := vault.RestoreWallet(name, getPassword(passwd), key) + wallet, err := vault.RestoreWallet(walletFile, GetPassword(walletPassword), key) if err != nil { return err } - fmt.Printf("Restore %s wallet to %s\n", wallet.Address, name) + fmt.Printf("Restore %s wallet to %s\n", wallet.Address, walletFile) return nil default: return fmt.Errorf("--restore [hex | bin]") } // list wallet info - if item := c.String("list"); item != "" { + if item := listflag; item != "" { if item != "account" && item != "balance" && item != "verbose" && item != "nonce" && item != "id" { return fmt.Errorf("--list [account | balance | verbose | nonce | id]") } else { - wallet, err := vault.OpenWallet(name, getPassword(passwd)) + wallet, err := vault.OpenWallet(walletFile, GetPassword(walletPassword)) if err != nil { return err } @@ -140,41 +115,41 @@ func walletAction(c *cli.Context) error { case "balance": account, _ := wallet.GetDefaultAccount() address, _ := account.ProgramHash.ToAddress() - resp, err := client.Call(nknc.Address(), "getbalancebyaddr", 0, map[string]interface{}{"address": address}) + resp, err := client.Call(Address(), "getbalancebyaddr", 0, map[string]interface{}{"address": address}) if err != nil { return err } - nknc.FormatOutput(resp) + FormatOutput(resp) case "nonce": account, _ := wallet.GetDefaultAccount() address, _ := account.ProgramHash.ToAddress() - resp, err := client.Call(nknc.Address(), "getnoncebyaddr", 0, map[string]interface{}{"address": address}) + resp, err := client.Call(Address(), "getnoncebyaddr", 0, map[string]interface{}{"address": address}) if err != nil { return err } - nknc.FormatOutput(resp) + FormatOutput(resp) case "id": account, _ := wallet.GetDefaultAccount() publicKey := account.PubKey() pk := hex.EncodeToString(publicKey) - resp, err := client.Call(nknc.Address(), "getid", 0, map[string]interface{}{"publickey": pk}) + resp, err := client.Call(Address(), "getid", 0, map[string]interface{}{"publickey": pk}) if err != nil { return err } - nknc.FormatOutput(resp) + FormatOutput(resp) } } return nil } // change password - if c.Bool("changepassword") { - fmt.Printf("Wallet File: '%s'\n", name) + if changepassword { + fmt.Printf("Wallet File: '%s'\n", walletFile) passwd, err := password.GetPassword("") if err != nil { return err } - wallet, err := vault.OpenWallet(name, passwd) + wallet, err := vault.OpenWallet(walletFile, passwd) if err != nil { return err } @@ -195,47 +170,30 @@ func walletAction(c *cli.Context) error { return nil } -func NewCommand() *cli.Command { - return &cli.Command{ - Name: "wallet", - Usage: "user wallet operation", - Description: "With nknc wallet, you could control your asset.", - ArgsUsage: "[args]", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "create, c", - Usage: "create wallet", - }, - cli.StringFlag{ - Name: "list, l", - Usage: "list wallet information [account, balance, verbose, nonce]", - }, - cli.StringFlag{ - Name: "restore, r", - Usage: "restore wallet with [hex, bin] format PrivateKey", - }, - cli.BoolFlag{ - Name: "changepassword", - Usage: "change wallet password", - }, - cli.BoolFlag{ - Name: "reset", - Usage: "reset wallet", - }, - cli.StringFlag{ - Name: "name, n", - Usage: "wallet name", - Value: config.Parameters.WalletFile, - }, - cli.StringFlag{ - Name: "password, p", - Usage: "wallet password", - }, - }, - Action: walletAction, - OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error { - nknc.PrintError(c, err, "wallet") - return cli.NewExitError("", 1) - }, +func showAccountInfo(wallet *vault.Wallet, verbose bool) { + const format = "%-37s %s\n" + account, _ := wallet.GetDefaultAccount() + fmt.Printf(format, "Address", "Public Key") + fmt.Printf(format, "-------", "----------") + address, _ := account.ProgramHash.ToAddress() + publicKey := account.PublicKey + fmt.Printf(format, address, hex.EncodeToString(publicKey)) + if verbose { + fmt.Printf("\nSecret Seed\n-----------\n%s\n", hex.EncodeToString(crypto.GetSeedFromPrivateKey(account.PrivateKey))) + } +} + +func getConfirmedPassword(passwd string) []byte { + var tmp []byte + var err error + if passwd != "" { + tmp = []byte(passwd) + } else { + tmp, err = password.GetConfirmedPassword() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } } + return tmp } diff --git a/cmd/nknc/common/common.go b/cmd/nknc/common/common.go deleted file mode 100644 index baaf2b22c..000000000 --- a/cmd/nknc/common/common.go +++ /dev/null @@ -1,76 +0,0 @@ -package common - -import ( - "bytes" - "encoding/json" - "fmt" - "net" - "os" - "strconv" - - "github.com/nknorg/nkn/v2/config" - "github.com/nknorg/nkn/v2/util/password" - - "github.com/urfave/cli" -) - -var ( - Ip string - Port string - Version string -) - -func NewIpFlag() cli.Flag { - return cli.StringFlag{ - Name: "ip", - Usage: "node's ip address", - Value: "localhost", - Destination: &Ip, - } -} - -func NewPortFlag() cli.Flag { - return cli.StringFlag{ - Name: "port", - Usage: "node's RPC port", - Value: strconv.Itoa(int(config.Parameters.HttpJsonPort)), - Destination: &Port, - } -} - -func Address() string { - return "http://" + net.JoinHostPort(Ip, Port) -} - -func PrintError(c *cli.Context, err error, cmd string) { - fmt.Println("Incorrect Usage:", err) - fmt.Println("") - cli.ShowCommandHelp(c, cmd) -} - -func FormatOutput(o []byte) error { - var out bytes.Buffer - err := json.Indent(&out, o, "", "\t") - if err != nil { - return err - } - out.Write([]byte("\n")) - _, err = out.WriteTo(os.Stdout) - - return err -} - -func GetPassword(passwd string) []byte { - var tmp []byte - var err error - if passwd != "" { - tmp = []byte(passwd) - } else { - tmp, err = password.GetPassword("") - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - } - return tmp -} diff --git a/cmd/nknc/debug/debug.go b/cmd/nknc/debug/debug.go deleted file mode 100644 index 0ff9a157e..000000000 --- a/cmd/nknc/debug/debug.go +++ /dev/null @@ -1,48 +0,0 @@ -package debug - -import ( - "fmt" - "os" - - "github.com/nknorg/nkn/v2/api/httpjson/client" - nknc "github.com/nknorg/nkn/v2/cmd/nknc/common" - - "github.com/urfave/cli" -) - -func debugAction(c *cli.Context) (err error) { - if c.NumFlags() == 0 { - cli.ShowSubcommandHelp(c) - return nil - } - level := c.Int("level") - if level != -1 { - resp, err := client.Call(nknc.Address(), "setdebuginfo", 0, map[string]interface{}{"level": level}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - nknc.FormatOutput(resp) - } - return nil -} - -func NewCommand() *cli.Command { - return &cli.Command{Name: "debug", - Usage: "blockchain node debugging", - Description: "With nknc debug, you could debug blockchain node.", - ArgsUsage: "[args]", - Flags: []cli.Flag{ - cli.IntFlag{ - Name: "level, l", - Usage: "log level 0-6", - Value: -1, - }, - }, - Action: debugAction, - OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error { - nknc.PrintError(c, err, "debug") - return cli.NewExitError("", 1) - }, - } -} diff --git a/cmd/nknc/id/id.go b/cmd/nknc/id/id.go deleted file mode 100644 index 229fac485..000000000 --- a/cmd/nknc/id/id.go +++ /dev/null @@ -1,159 +0,0 @@ -package id - -import ( - "context" - "encoding/hex" - "fmt" - "os" - - api "github.com/nknorg/nkn/v2/api/common" - "github.com/nknorg/nkn/v2/api/httpjson/client" - nknc "github.com/nknorg/nkn/v2/cmd/nknc/common" - "github.com/nknorg/nkn/v2/common" - "github.com/nknorg/nkn/v2/config" - "github.com/nknorg/nkn/v2/vault" - - "github.com/urfave/cli" -) - -func generateIDAction(c *cli.Context) error { - if c.NumFlags() == 0 { - cli.ShowSubcommandHelp(c) - return nil - } - - pubkeyHex := c.String("pubkey") - pubkey, err := hex.DecodeString(pubkeyHex) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - - walletName := c.String("wallet") - passwd := c.String("password") - myWallet, err := vault.OpenWallet(walletName, nknc.GetPassword(passwd)) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - - var txnFee common.Fixed64 - fee := c.String("fee") - if fee == "" { - txnFee = common.Fixed64(0) - } else { - txnFee, err = common.StringToFixed64(fee) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - } - - var regFee common.Fixed64 - fee = c.String("regfee") - if fee == "" { - regFee = common.Fixed64(0) - } else { - regFee, err = common.StringToFixed64(fee) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - } - - nonce := c.Uint64("nonce") - - var resp []byte - switch { - case c.Bool("genid"): - account, err := myWallet.GetDefaultAccount() - if err != nil { - return err - } - - walletAddr, err := account.ProgramHash.ToAddress() - if err != nil { - return err - } - - remoteNonce, height, err := client.GetNonceByAddr(nknc.Address(), walletAddr, true) - if err != nil { - return err - } - - if nonce == 0 { - nonce = remoteNonce - } - - txn, err := api.MakeGenerateIDTransaction(context.Background(), pubkey, myWallet, regFee, nonce, txnFee, height+1) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - - buff, err := txn.Marshal() - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - - resp, err = client.Call(nknc.Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - default: - cli.ShowSubcommandHelp(c) - return nil - } - nknc.FormatOutput(resp) - - return nil -} - -func NewCommand() *cli.Command { - return &cli.Command{ - Name: "id", - Usage: "generate id for nknd", - Description: "With nknc id, you could generate ID.", - ArgsUsage: "[args]", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "genid", - Usage: "generate id", - }, - cli.StringFlag{ - Name: "pubkey", - Usage: "pubkey to generate id for, leave empty for local wallet pubkey", - }, - cli.StringFlag{ - Name: "wallet, w", - Usage: "wallet name", - Value: config.Parameters.WalletFile, - }, - cli.StringFlag{ - Name: "password, p", - Usage: "wallet password", - }, - cli.StringFlag{ - Name: "regfee", - Usage: "registration fee", - Value: "", - }, - cli.StringFlag{ - Name: "fee, f", - Usage: "transaction fee", - Value: "", - }, - cli.Uint64Flag{ - Name: "nonce", - Usage: "nonce", - }, - }, - Action: generateIDAction, - OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error { - nknc.PrintError(c, err, "id") - return cli.NewExitError("", 1) - }, - } -} diff --git a/cmd/nknc/info/info.go b/cmd/nknc/info/info.go deleted file mode 100644 index b3be7ca1a..000000000 --- a/cmd/nknc/info/info.go +++ /dev/null @@ -1,281 +0,0 @@ -package info - -import ( - "fmt" - "os" - - "github.com/nknorg/nkn/v2/api/httpjson/client" - nknc "github.com/nknorg/nkn/v2/cmd/nknc/common" - - "github.com/urfave/cli" -) - -func infoAction(c *cli.Context) (err error) { - if c.NumFlags() == 0 { - cli.ShowSubcommandHelp(c) - return nil - } - blockhash := c.String("blockhash") - txhash := c.String("txhash") - latestblockhash := c.Bool("latestblockhash") - height := c.Int("height") - header := c.Int("header") - headerHash := c.String("headerhash") - blockcount := c.Bool("blockcount") - connections := c.Bool("connections") - neighbor := c.Bool("neighbor") - ring := c.Bool("ring") - state := c.Bool("state") - version := c.Bool("nodeversion") - balance := c.String("balance") - nonce := c.String("nonce") - id := c.String("id") - pretty := c.Bool("pretty") - - var resp []byte - var output [][]byte - if height >= 0 { - resp, err = client.Call(nknc.Address(), "getblock", 0, map[string]interface{}{"height": height}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - if pretty { - if p, err := PrettyPrinter(resp).PrettyBlock(); err == nil { - resp = p // replace resp if pretty success - } else { - fmt.Fprintln(os.Stderr, "Fallback to original resp due to PrettyPrint fail: ", err) - } - } - output = append(output, resp) - } - - if len(blockhash) > 0 { - resp, err = client.Call(nknc.Address(), "getblock", 0, map[string]interface{}{"hash": blockhash}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - if pretty { - if p, err := PrettyPrinter(resp).PrettyBlock(); err == nil { - resp = p // replace resp if pretty success - } else { - fmt.Fprintln(os.Stderr, "Fallback to original resp due to PrettyPrint fail: ", err) - } - } - output = append(output, resp) - } - - if header >= 0 { - resp, err = client.Call(nknc.Address(), "getheader", 0, map[string]interface{}{"height": header}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - output = append(output, resp) - } - - if len(headerHash) > 0 { - resp, err = client.Call(nknc.Address(), "getheader", 0, map[string]interface{}{"hash": headerHash}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - output = append(output, resp) - } - - if latestblockhash { - resp, err = client.Call(nknc.Address(), "getlatestblockhash", 0, map[string]interface{}{}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - output = append(output, resp) - } - - if blockcount { - resp, err = client.Call(nknc.Address(), "getblockcount", 0, map[string]interface{}{}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - output = append(output, resp) - } - - if connections { - resp, err = client.Call(nknc.Address(), "getconnectioncount", 0, map[string]interface{}{}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - output = append(output, resp) - } - - if neighbor { - resp, err := client.Call(nknc.Address(), "getneighbor", 0, map[string]interface{}{}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - output = append(output, resp) - } - - if ring { - resp, err := client.Call(nknc.Address(), "getchordringinfo", 0, map[string]interface{}{}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - output = append(output, resp) - } - - if state { - resp, err := client.Call(nknc.Address(), "getnodestate", 0, map[string]interface{}{}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - output = append(output, resp) - } - - if txhash != "" { - resp, err = client.Call(nknc.Address(), "gettransaction", 0, map[string]interface{}{"hash": txhash}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - if pretty { - if p, err := PrettyPrinter(resp).PrettyTxn(); err == nil { - resp = p // replace resp if pretty success - } else { - fmt.Fprintln(os.Stderr, "Output origin resp due to PrettyPrint fail: ", err) - } - } - output = append(output, resp) - } - - if version { - resp, err = client.Call(nknc.Address(), "getversion", 0, map[string]interface{}{}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - output = append(output, resp) - - } - - if balance != "" { - resp, err := client.Call(nknc.Address(), "getbalancebyaddr", 0, map[string]interface{}{"address": balance}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - output = append(output, resp) - } - - if nonce != "" { - resp, err := client.Call(nknc.Address(), "getnoncebyaddr", 0, map[string]interface{}{"address": nonce}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - output = append(output, resp) - } - - if id != "" { - resp, err := client.Call(nknc.Address(), "getid", 0, map[string]interface{}{"publickey": id}) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - output = append(output, resp) - } - - for _, v := range output { - nknc.FormatOutput(v) - } - - return nil -} - -func NewCommand() *cli.Command { - return &cli.Command{ - Name: "info", - Usage: "show blockchain information", - Description: "With nknc info, you could look up blocks, transactions, etc.", - ArgsUsage: "[args]", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "pretty, p", - Usage: "pretty print", - }, - cli.StringFlag{ - Name: "blockhash, b", - Usage: "hash for querying a block", - }, - cli.StringFlag{ - Name: "txhash, t", - Usage: "hash for querying a transaction", - }, - cli.BoolFlag{ - Name: "latestblockhash", - Usage: "latest block hash", - }, - cli.IntFlag{ - Name: "height", - Usage: "block height for querying a block", - Value: -1, - }, - cli.IntFlag{ - Name: "header", - Usage: "get block header by height", - Value: -1, - }, - cli.StringFlag{ - Name: "headerhash", - Usage: "get block header by hash", - }, - cli.BoolFlag{ - Name: "blockcount, c", - Usage: "block number in blockchain", - }, - cli.BoolFlag{ - Name: "connections", - Usage: "connection count", - }, - cli.BoolFlag{ - Name: "neighbor", - Usage: "neighbor information of current node", - }, - cli.BoolFlag{ - Name: "ring", - Usage: "chord ring information of current node", - }, - cli.BoolFlag{ - Name: "state, s", - Usage: "current node state", - }, - cli.BoolFlag{ - Name: "nodeversion, v", - Usage: "version of connected remote node", - }, - cli.StringFlag{ - Name: "balance", - Usage: "balance of a address", - }, - cli.StringFlag{ - Name: "nonce", - Usage: "nonce of a address", - }, - cli.StringFlag{ - Name: "id", - Usage: "id from publickey", - }, - }, - Action: infoAction, - OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error { - nknc.PrintError(c, err, "info") - return cli.NewExitError("", 1) - }, - } -} diff --git a/cmd/nknc/info/pretty.go b/cmd/nknc/info/pretty.go deleted file mode 100644 index a8b005887..000000000 --- a/cmd/nknc/info/pretty.go +++ /dev/null @@ -1,131 +0,0 @@ -package info - -import ( - "encoding/hex" - "encoding/json" - "fmt" - - "github.com/golang/protobuf/proto" - "github.com/nknorg/nkn/v2/pb" -) - -type PrettyPrinter []byte - -func TxnUnmarshal(m map[string]interface{}) (interface{}, error) { - typ, ok := m["txType"] - if !ok { - return nil, fmt.Errorf("No such key [txType]") - } - - pbHexStr, ok := m["payloadData"] - if !ok { - return m, nil - } - - buf, err := hex.DecodeString(pbHexStr.(string)) - if err != nil { - return nil, err - } - - switch typ { - case pb.PayloadType_name[int32(pb.PayloadType_SIG_CHAIN_TXN_TYPE)]: - sigChainTxn := &pb.SigChainTxn{} - if err = proto.Unmarshal(buf, sigChainTxn); err == nil { // bin to pb struct of SigChainTxnType txn - m["payloadData"] = sigChainTxn.ToMap() - } - case pb.PayloadType_name[int32(pb.PayloadType_COINBASE_TYPE)]: - coinBaseTxn := &pb.Coinbase{} - if err = proto.Unmarshal(buf, coinBaseTxn); err == nil { // bin to pb struct of Coinbase txn - m["payloadData"] = coinBaseTxn.ToMap() - } - case pb.PayloadType_name[int32(pb.PayloadType_TRANSFER_ASSET_TYPE)]: - trans := &pb.TransferAsset{} - if err = proto.Unmarshal(buf, trans); err == nil { // bin to pb struct of Coinbase txn - m["payloadData"] = trans.ToMap() - } - case pb.PayloadType_name[int32(pb.PayloadType_GENERATE_ID_TYPE)]: - genID := &pb.GenerateID{} - if err = proto.Unmarshal(buf, genID); err == nil { // bin to pb struct of Coinbase txn - m["payloadData"] = genID.ToMap() - } - case pb.PayloadType_name[int32(pb.PayloadType_REGISTER_NAME_TYPE)]: - regName := &pb.RegisterName{} - if err = proto.Unmarshal(buf, regName); err == nil { // bin to pb struct of Coinbase txn - m["payloadData"] = regName.ToMap() - } - case pb.PayloadType_name[int32(pb.PayloadType_SUBSCRIBE_TYPE)]: - sub := &pb.Subscribe{} - if err = proto.Unmarshal(buf, sub); err == nil { // bin to pb struct of Coinbase txn - m["payloadData"] = sub.ToMap() - } - case pb.PayloadType_name[int32(pb.PayloadType_UNSUBSCRIBE_TYPE)]: - sub := &pb.Unsubscribe{} - if err = proto.Unmarshal(buf, sub); err == nil { // bin to pb struct of Coinbase txn - m["payloadData"] = sub.ToMap() - } - case pb.PayloadType_name[int32(pb.PayloadType_NANO_PAY_TYPE)]: - pay := &pb.NanoPay{} - if err = proto.Unmarshal(buf, pay); err == nil { // bin to pb struct of Coinbase txn - m["payloadData"] = pay.ToMap() - } - case pb.PayloadType_name[int32(pb.PayloadType_TRANSFER_NAME_TYPE)]: - fallthrough //TODO - case pb.PayloadType_name[int32(pb.PayloadType_DELETE_NAME_TYPE)]: - fallthrough //TODO - case pb.PayloadType_name[int32(pb.PayloadType_ISSUE_ASSET_TYPE)]: - fallthrough //TODO - default: - return nil, fmt.Errorf("Unknow txType[%s] for pretty print", typ) - } - - return m, nil -} - -func (resp PrettyPrinter) PrettyTxn() ([]byte, error) { - m := map[string]interface{}{} - err := json.Unmarshal(resp, &m) - if err != nil { - return nil, err - } - - v, ok := m["result"] - if !ok { - return nil, fmt.Errorf("response No such key [result]") - } - - if m["result"], err = TxnUnmarshal(v.(map[string]interface{})); err != nil { - return nil, err - } - - return json.Marshal(m) -} - -func (resp PrettyPrinter) PrettyBlock() ([]byte, error) { - m := map[string]interface{}{} - err := json.Unmarshal(resp, &m) - if err != nil { - return nil, err - } - - ret, ok := m["result"] - if !ok { - return nil, fmt.Errorf("response No such key [result]") - } - - txns, ok := ret.(map[string]interface{})["transactions"] - if !ok { - return nil, fmt.Errorf("result No such key [transactions]") - } - - lst := make([]interface{}, 0) - for _, t := range txns.([]interface{}) { - if m, err := TxnUnmarshal(t.(map[string]interface{})); err == nil { - lst = append(lst, m) - } else { - lst = append(lst, t) // append origin txn if TxnUnmarshal fail - } - } - m["transactions"] = lst - - return json.Marshal(m) -} diff --git a/cmd/nknc/main.go b/cmd/nknc/main.go new file mode 100644 index 000000000..4a3317ddd --- /dev/null +++ b/cmd/nknc/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "log" + + cmd "github.com/nknorg/nkn/v2/cmd/nknc/commands" +) + +func main() { + defer func() { + if r := recover(); r != nil { + log.Fatalf("Panic: %+v", r) + } + }() + + cmd.Execute() +} diff --git a/cmd/nknc/name/name.go b/cmd/nknc/name/name.go deleted file mode 100644 index 81dbd62b2..000000000 --- a/cmd/nknc/name/name.go +++ /dev/null @@ -1,170 +0,0 @@ -package name - -import ( - "encoding/hex" - "fmt" - "os" - - api "github.com/nknorg/nkn/v2/api/common" - "github.com/nknorg/nkn/v2/api/httpjson/client" - nknc "github.com/nknorg/nkn/v2/cmd/nknc/common" - "github.com/nknorg/nkn/v2/common" - "github.com/nknorg/nkn/v2/config" - "github.com/nknorg/nkn/v2/vault" - - "github.com/urfave/cli" -) - -func nameAction(c *cli.Context) error { - if c.NumFlags() == 0 { - cli.ShowSubcommandHelp(c) - return nil - } - - walletName := c.String("wallet") - passwd := c.String("password") - myWallet, err := vault.OpenWallet(walletName, nknc.GetPassword(passwd)) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - - var txnFee common.Fixed64 - fee := c.String("fee") - if fee == "" { - txnFee = common.Fixed64(0) - } else { - txnFee, _ = common.StringToFixed64(fee) - } - - regFeeString := c.String("regfee") - var regFee common.Fixed64 - if regFeeString == "" { - regFee = common.Fixed64(0) - } else { - regFee, _ = common.StringToFixed64(regFeeString) - } - - nonce := c.Uint64("nonce") - - var resp []byte - switch { - case c.Bool("reg"): - name := c.String("name") - if name == "" { - fmt.Println("name is required with [--name]") - return nil - } - txn, _ := api.MakeRegisterNameTransaction(myWallet, name, nonce, regFee, txnFee) - buff, _ := txn.Marshal() - resp, err = client.Call(nknc.Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) - case c.Bool("transfer"): - name := c.String("name") - if name == "" { - fmt.Println("name is required with [--name]") - return nil - } - to := c.String("to") - if to == "" { - fmt.Println("transfer is required with [--to]") - return nil - } - var toBytes []byte - toBytes, err = hex.DecodeString(to) - if err != nil { - return err - } - txn, _ := api.MakeTransferNameTransaction(myWallet, name, nonce, txnFee, toBytes) - buff, _ := txn.Marshal() - resp, err = client.Call(nknc.Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) - case c.Bool("del"): - name := c.String("name") - if name == "" { - fmt.Println("name is required with [--name]") - return nil - } - - txn, _ := api.MakeDeleteNameTransaction(myWallet, name, nonce, txnFee) - buff, _ := txn.Marshal() - resp, err = client.Call(nknc.Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) - case c.Bool("get"): - name := c.String("name") - if name == "" { - fmt.Println("name is required with [--name]") - return nil - } - resp, err = client.Call(nknc.Address(), "getregistrant", 0, map[string]interface{}{"name": name}) - default: - cli.ShowSubcommandHelp(c) - return nil - } - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - nknc.FormatOutput(resp) - - return nil -} - -func NewCommand() *cli.Command { - return &cli.Command{ - Name: "name", - Usage: "name registration", - Description: "With nknc name, you could register name for your address.", - ArgsUsage: "[args]", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "reg, r", - Usage: "register name for your address", - }, - cli.BoolFlag{ - Name: "del, d", - Usage: "delete name of your address", - }, - cli.BoolFlag{ - Name: "get, g", - Usage: "get register name info", - }, - cli.BoolFlag{ - Name: "transfer, t", - Usage: "transfer name to another address", - }, - cli.StringFlag{ - Name: "name", - Usage: "name", - }, - cli.StringFlag{ - Name: "wallet, w", - Usage: "wallet name", - Value: config.Parameters.WalletFile, - }, - cli.StringFlag{ - Name: "password, p", - Usage: "wallet password", - }, - cli.StringFlag{ - Name: "fee, f", - Usage: "transaction fee", - Value: "", - }, - cli.Uint64Flag{ - Name: "nonce", - Usage: "nonce", - }, - cli.StringFlag{ - Name: "regfee", - Usage: "regfee", - }, - cli.StringFlag{ - Name: "to", - Usage: "transfer name to addr", - }, - }, - Action: nameAction, - OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error { - nknc.PrintError(c, err, "name") - return cli.NewExitError("", 1) - }, - } -} diff --git a/cmd/nknc/nknc.go b/cmd/nknc/nknc.go deleted file mode 100644 index fdb8620f7..000000000 --- a/cmd/nknc/nknc.go +++ /dev/null @@ -1,73 +0,0 @@ -package main - -import ( - "fmt" - "log" - "math/rand" - "os" - "sort" - "syscall" - "time" - - "github.com/nknorg/nkn/v2/cmd/nknc/asset" - "github.com/nknorg/nkn/v2/cmd/nknc/common" - "github.com/nknorg/nkn/v2/cmd/nknc/debug" - "github.com/nknorg/nkn/v2/cmd/nknc/id" - "github.com/nknorg/nkn/v2/cmd/nknc/info" - "github.com/nknorg/nkn/v2/cmd/nknc/name" - "github.com/nknorg/nkn/v2/cmd/nknc/pruning" - "github.com/nknorg/nkn/v2/cmd/nknc/pubsub" - "github.com/nknorg/nkn/v2/cmd/nknc/service" - "github.com/nknorg/nkn/v2/cmd/nknc/wallet" - "github.com/urfave/cli" -) - -func init() { - rand.Seed(time.Now().UnixNano()) -} - -func main() { - defer func() { - if r := recover(); r != nil { - log.Fatalf("Panic: %+v", r) - } - }() - - app := cli.NewApp() - app.Name = "nknc" - app.Version = common.Version - app.HelpName = "nknc" - app.Usage = "command line tool for blockchain" - app.UsageText = "nknc [global options] command [command options] [args]" - app.HideHelp = false - app.HideVersion = false - //global options - app.Flags = []cli.Flag{ - common.NewIpFlag(), - common.NewPortFlag(), - } - //commands - app.Commands = []cli.Command{ - *debug.NewCommand(), - *info.NewCommand(), - *wallet.NewCommand(), - *asset.NewCommand(), - *name.NewCommand(), - *pubsub.NewCommand(), - *id.NewCommand(), - *pruning.NewCommand(), - *service.NewCommand(), - } - sort.Sort(cli.CommandsByName(app.Commands)) - sort.Sort(cli.FlagsByName(app.Flags)) - - if err := app.Run(os.Args); err != nil { - switch err.(type) { - case syscall.Errno: - os.Exit(int(err.(syscall.Errno))) - default: - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - } -} diff --git a/cmd/nknc/pruning/pruning.go b/cmd/nknc/pruning/pruning.go deleted file mode 100644 index 79533f775..000000000 --- a/cmd/nknc/pruning/pruning.go +++ /dev/null @@ -1,123 +0,0 @@ -package pruning - -import ( - "fmt" - - "github.com/nknorg/nkn/v2/chain/store" - "github.com/urfave/cli" -) - -func pruningAction(c *cli.Context) error { - if c.NumFlags() == 0 { - cli.ShowSubcommandHelp(c) - return nil - } - - switch { - case c.Bool("currentheight"): - cs, err := store.NewLedgerStore() - if err != nil { - return err - } - _, h, err := cs.GetCurrentBlockHashFromDB() - if err != nil { - return err - } - fmt.Println(h) - case c.Bool("startheights"): - cs, err := store.NewLedgerStore() - if err != nil { - return err - } - refCountStartHeight, pruningStartHeight := cs.GetPruningStartHeight() - fmt.Println(refCountStartHeight, pruningStartHeight) - case c.Bool("pruning"): - cs, err := store.NewLedgerStore() - if err != nil { - return err - } - - if c.Bool("seq") { - err := cs.SequentialPrune() - if err != nil { - return err - } - } else if c.Bool("lowmem") { - err := cs.PruneStatesLowMemory(true) - if err != nil { - return err - } - } else { - err := cs.PruneStates() - if err != nil { - return err - } - } - case c.Bool("dumpnodes"): - cs, err := store.NewLedgerStore() - if err != nil { - return err - } - err = cs.TrieTraverse(true) - if err != nil { - return err - } - case c.Bool("verifystate"): - cs, err := store.NewLedgerStore() - if err != nil { - return err - } - err = cs.VerifyState() - if err != nil { - return err - } - default: - cli.ShowSubcommandHelp(c) - return nil - } - - return nil -} - -func NewCommand() *cli.Command { - return &cli.Command{ - Name: "pruning", - Usage: "state trie pruning for nknd", - Description: "state trie pruning for nknd.", - ArgsUsage: "[args]", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "currentheight", - Usage: "current block height of offline db", - }, - cli.BoolFlag{ - Name: "startheights", - Usage: "start heights of refcount and pruning", - }, - cli.BoolFlag{ - Name: "pruning", - Usage: "prune state trie", - }, - cli.BoolFlag{ - Name: "seq", - Usage: "prune state trie sequential mode", - }, - cli.BoolFlag{ - Name: "lowmem", - Usage: "prune state trie low memory mode", - }, - cli.BoolFlag{ - Name: "dumpnodes", - Usage: "dump nodes of trie", - }, - cli.BoolFlag{ - Name: "verifystate", - Usage: "verify state of ledger", - }, - }, - Action: pruningAction, - OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error { - return cli.NewExitError("", 1) - }, - } -} diff --git a/cmd/nknc/pubsub/pubsub.go b/cmd/nknc/pubsub/pubsub.go deleted file mode 100644 index 2c13365cf..000000000 --- a/cmd/nknc/pubsub/pubsub.go +++ /dev/null @@ -1,186 +0,0 @@ -package pubsub - -import ( - "encoding/hex" - "fmt" - "os" - - api "github.com/nknorg/nkn/v2/api/common" - "github.com/nknorg/nkn/v2/api/httpjson/client" - nknc "github.com/nknorg/nkn/v2/cmd/nknc/common" - "github.com/nknorg/nkn/v2/common" - "github.com/nknorg/nkn/v2/config" - "github.com/nknorg/nkn/v2/vault" - - "github.com/urfave/cli" -) - -func subscribeAction(c *cli.Context) error { - if c.NumFlags() == 0 { - cli.ShowSubcommandHelp(c) - return nil - } - - walletName := c.String("wallet") - passwd := c.String("password") - myWallet, err := vault.OpenWallet(walletName, nknc.GetPassword(passwd)) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - - var txnFee common.Fixed64 - fee := c.String("fee") - if fee == "" { - txnFee = common.Fixed64(0) - } else { - txnFee, _ = common.StringToFixed64(fee) - } - - nonce := c.Uint64("nonce") - - var resp []byte - switch { - case c.Bool("sub"): - id := c.String("identifier") - if id == "" { - fmt.Println("identifier is required with [--id]") - return nil - } - - topic := c.String("topic") - if topic == "" { - fmt.Println("topic is required with [--topic]") - return nil - } - - duration := c.Uint64("duration") - - meta := c.String("meta") - if meta == "" { - fmt.Println("meta is required with [--meta]") - return nil - } - - txn, _ := api.MakeSubscribeTransaction(myWallet, id, topic, uint32(duration), meta, nonce, txnFee) - buff, _ := txn.Marshal() - resp, err = client.Call(nknc.Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) - case c.Bool("unsub"): - id := c.String("identifier") - if id == "" { - fmt.Println("identifier is required with [--id]") - return nil - } - - topic := c.String("topic") - if topic == "" { - fmt.Println("topic is required with [--topic]") - return nil - } - - txn, _ := api.MakeUnsubscribeTransaction(myWallet, id, topic, nonce, txnFee) - buff, _ := txn.Marshal() - resp, err = client.Call(nknc.Address(), "sendrawtransaction", 0, map[string]interface{}{"tx": hex.EncodeToString(buff)}) - case c.Bool("get"): - topic := c.String("topic") - if topic == "" { - fmt.Println("topic is required with [--topic]") - return nil - } - - params := map[string]interface{}{"topic": topic} - subscriber := c.String("subscriber") - if subscriber == "" { // get list of topic - params["offset"] = c.Uint64("offset") - params["limit"] = c.Uint64("limit") - resp, err = client.Call(nknc.Address(), "getsubscribers", 0, params) - } else { // get details of specified subscriber - params["subscriber"] = subscriber - resp, err = client.Call(nknc.Address(), "getsubscription", 0, params) - } - default: - cli.ShowSubcommandHelp(c) - return nil - } - if err != nil { - fmt.Fprintln(os.Stderr, err) - return err - } - nknc.FormatOutput(resp) - - return nil -} - -func NewCommand() *cli.Command { - return &cli.Command{ - Name: "pubsub", - Usage: "manage topic subscriptions", - Description: "With nknc pubsub, you could manage topic subscriptions.", - ArgsUsage: "[args]", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "sub, s", - Usage: "subscribe to topic", - }, - cli.BoolFlag{ - Name: "unsub, u", - Usage: "unsubscribe from topic", - }, - cli.BoolFlag{ - Name: "get, g", - Usage: "get subscribes list of specified topic or get details info of a specified subscriber", - }, - cli.StringFlag{ - Name: "identifier, id", - Usage: "identifier", - }, - cli.StringFlag{ - Name: "topic", - Usage: "topic", - }, - cli.Uint64Flag{ - Name: "duration", - Usage: "duration", - }, - cli.Uint64Flag{ - Name: "offset", - Usage: "get subscribes skip previous offset items", - }, - cli.Uint64Flag{ - Name: "limit", - Usage: "limit subscribes amount which start from offset", - }, - cli.StringFlag{ - Name: "subscriber", - Usage: "specified subscriber which you want details", - }, - cli.StringFlag{ - Name: "meta", - Usage: "meta", - }, - cli.StringFlag{ - Name: "wallet, w", - Usage: "wallet name", - Value: config.Parameters.WalletFile, - }, - cli.StringFlag{ - Name: "password, p", - Usage: "wallet password", - }, - cli.StringFlag{ - Name: "fee, f", - Usage: "transaction fee", - Value: "", - }, - cli.Uint64Flag{ - Name: "nonce", - Usage: "nonce", - }, - }, - Action: subscribeAction, - OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error { - nknc.PrintError(c, err, "pubsub") - return cli.NewExitError("", 1) - }, - } -} diff --git a/cmd/nknd/portmapper.go b/cmd/nknd/commands/portmapper.go similarity index 99% rename from cmd/nknd/portmapper.go rename to cmd/nknd/commands/portmapper.go index 7247c1f1b..f0fe8c1cd 100644 --- a/cmd/nknd/portmapper.go +++ b/cmd/nknd/commands/portmapper.go @@ -1,4 +1,4 @@ -package main +package commands import ( "github.com/nknorg/nkn/v2/config" diff --git a/cmd/nknd/nknd.go b/cmd/nknd/commands/root.go similarity index 74% rename from cmd/nknd/nknd.go rename to cmd/nknd/commands/root.go index 9d8c58b77..2374b3202 100644 --- a/cmd/nknd/nknd.go +++ b/cmd/nknd/commands/root.go @@ -1,4 +1,4 @@ -package main +package commands import ( "bytes" @@ -17,8 +17,6 @@ import ( "runtime" "time" - "github.com/rdegges/go-ipify" - "github.com/nknorg/nkn/v2/api/certs" api "github.com/nknorg/nkn/v2/api/common" "github.com/nknorg/nkn/v2/api/httpjson" @@ -43,94 +41,69 @@ import ( nnetnode "github.com/nknorg/nnet/node" "github.com/nknorg/nnet/overlay" "github.com/nknorg/nnet/overlay/chord" - "github.com/urfave/cli" + "github.com/rdegges/go-ipify" + "github.com/spf13/cobra" ) const ( NetVersionNum = 29 // This is temporary and will be removed soon after mainnet is stabilized ) +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "nknd", + Version: config.Version, + Short: "nknd - The official NKN daemon for the NKN blockchain", + Long: "", + RunE: func(cmd *cobra.Command, args []string) error { + if err := nknMain(); err != nil { + log.Error(err) + } + return nil + }, +} + var ( createMode bool ) -func init() { - runtime.GOMAXPROCS(runtime.NumCPU()) - rand.Seed(time.Now().UnixNano()) -} - -func InitLedger(account *vault.Account) error { - var err error - store, err := store.NewLedgerStore() - if err != nil { - return err - } - blockChain, err := chain.NewBlockchainWithGenesisBlock(store) +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + err := rootCmd.Execute() if err != nil { - return err - } - chain.DefaultLedger = &chain.Ledger{ - Blockchain: blockChain, - Store: store, - } - - return nil -} - -func printMemStats() { - var m runtime.MemStats - runtime.ReadMemStats(&m) - log.Infof("Alloc = %v TotalAlloc = %v Sys = %v NumGC = %v\n", m.Alloc/1024, m.TotalAlloc/1024, m.Sys/1024, m.NumGC) - log.Infof("HeapAlloc = %v HeapSys = %v HeapIdle = %v HeapInuse = %v HeapReleased = %v HeapObjects = %v\n", m.HeapAlloc/1024, m.HeapSys/1024, m.HeapIdle/1024, m.HeapInuse/1024, m.HeapReleased/1024, m.HeapObjects/1024) - log.Infof("StackInuse = %v StackSys = %v MCacheInuse = %v MCacheSys = %v\n", m.StackInuse/1024, m.StackSys/1024, m.MCacheInuse/1024, m.MCacheSys/1024) -} - -func JoinNet(nn *nnet.NNet) error { - seeds := config.Parameters.SeedList - rand.Shuffle(len(seeds), func(i int, j int) { - seeds[i], seeds[j] = seeds[j], seeds[i] - }) - - for _, seed := range seeds { - randAddrs, err := client.FindSuccessorAddrs(seed, util.RandomBytes(config.NodeIDBytes)) - if err != nil { - log.Warningf("Can't get successor address from [%s]", seed) - continue - } - - for _, randAddr := range randAddrs { - if randAddr == nn.GetLocalNode().Addr { - log.Warning("Skipping self...") - continue - } - err = nn.Join(randAddr) - if err != nil { - log.Error(err) - continue - } - return nil - } + os.Exit(1) } - return errors.New("Failed to join the network") } -// AskMyIP request to seeds randomly, in order to obtain self's externIP and corresponding chordID -func AskMyIP(seeds []string) (string, error) { - rand.Shuffle(len(seeds), func(i int, j int) { - seeds[i], seeds[j] = seeds[j], seeds[i] - }) +func init() { + runtime.GOMAXPROCS(runtime.NumCPU()) + rand.Seed(time.Now().UnixNano()) - for _, seed := range seeds { - addr, err := client.GetMyExtIP(seed, []byte{}) - if err == nil { - return addr, err - } - log.Warningf("Ask my ID from %s error: %v", seed, err) - } - return "", errors.New("Tried all seeds but can't got my external IP and nknID") + rootCmd.CompletionOptions.DisableDefaultCmd = true + rootCmd.Flags().BoolVarP(&createMode, "create", "c", false, "Create Mode") + rootCmd.Flags().StringVar(&config.SeedList, "seed", "", "Seed node address to join, multiple seeds should be split by comma") + rootCmd.Flags().StringVar(&password.Passwd, "passwd", "", "Password of Your wallet private Key") + rootCmd.Flags().BoolVar(&config.SkipNAT, "no-nat", false, "Skip NAT traversal for UPnP and NAT-PMP") + rootCmd.Flags().BoolVar(&config.Debug, "debug", false, "Provide runtime profiling data of NKN") + rootCmd.Flags().StringVar(&config.StatePruningMode, "pruning", "", "state pruning mode: none, lowmem") + rootCmd.Flags().StringVar(&config.SyncMode, "sync", "", "sync mode: full, fast, light") + rootCmd.Flags().StringVar(&config.PprofPort, "pprof-port", "", "The port used for pprof in debug mode") + rootCmd.Flags().StringVar(&config.ConfigFile, "config", "", "config file name") + rootCmd.Flags().StringVar(&config.LogPath, "log", "", "directory where your log file will be generated") + rootCmd.Flags().StringVar(&config.ChainDBPath, "chaindb", "", "directory where your blockchain data will be stored") + rootCmd.Flags().StringVar(&config.WalletFile, "wallet", "", "wallet file") + rootCmd.Flags().StringVar(&config.BeneficiaryAddr, "beneficiaryaddr", "", "beneficiary address where your mining reward will go to") + rootCmd.Flags().StringVar(&config.GenesisBlockProposer, "genesisblockproposer", "", "public key of genesis block proposer") + rootCmd.Flags().BoolVar(&config.AllowEmptyBeneficiaryAddress, "allow-empty-beneficiary-address", false, "beneficiary address is forced unless --allow-empty-beneficiary-address is true") + rootCmd.Flags().StringVar(&config.WebGuiListenAddress, "web-gui-listen-address", "", "web gui will listen this address (default: 127.0.0.1)") + rootCmd.Flags().BoolVar(&config.WebGuiCreateWallet, "web-gui-create-wallet", false, "web gui create/open wallet") + rootCmd.Flags().StringVar(&config.PasswordFile, "password-file", "", "read password from file, save password to file when --web-gui-create-wallet arguments be true and password file does not exist") + + rootCmd.Flags().MarkHidden("passwd") } -func nknMain(c *cli.Context) error { +func nknMain() error { if config.Debug { //pprof go func() { @@ -267,13 +240,16 @@ func nknMain(c *cli.Context) error { return err } - nn.MustApplyMiddleware(overlay.NetworkStopped{func(network overlay.Network) bool { - select { - case signalChan <- os.Interrupt: - default: - } - return true - }, 0}) + nn.MustApplyMiddleware(overlay.NetworkStopped{ + Func: func(network overlay.Network) bool { + select { + case signalChan <- os.Interrupt: + default: + } + return true + }, + Priority: 0, + }) localNode, err := node.NewLocalNode(wallet, nn) if err != nil { @@ -299,12 +275,15 @@ func nknMain(c *cli.Context) error { // start websocket server ws := websocket.NewServer(localNode, wallet) - nn.MustApplyMiddleware(chord.SuccessorAdded{func(remoteNode *nnetnode.RemoteNode, index int) bool { - if index == 0 { - ws.NotifyWrongClients() - } - return true - }, 0}) + nn.MustApplyMiddleware(chord.SuccessorAdded{ + Func: func(remoteNode *nnetnode.RemoteNode, index int) bool { + if index == 0 { + ws.NotifyWrongClients() + } + return true + }, + Priority: 0, + }) err = nn.Start(createMode) if err != nil { @@ -351,6 +330,77 @@ func nknMain(c *cli.Context) error { return nil } +func InitLedger(account *vault.Account) error { + var err error + store, err := store.NewLedgerStore() + if err != nil { + return err + } + blockChain, err := chain.NewBlockchainWithGenesisBlock(store) + if err != nil { + return err + } + chain.DefaultLedger = &chain.Ledger{ + Blockchain: blockChain, + Store: store, + } + + return nil +} + +func printMemStats() { + var m runtime.MemStats + runtime.ReadMemStats(&m) + log.Infof("Alloc = %v TotalAlloc = %v Sys = %v NumGC = %v\n", m.Alloc/1024, m.TotalAlloc/1024, m.Sys/1024, m.NumGC) + log.Infof("HeapAlloc = %v HeapSys = %v HeapIdle = %v HeapInuse = %v HeapReleased = %v HeapObjects = %v\n", m.HeapAlloc/1024, m.HeapSys/1024, m.HeapIdle/1024, m.HeapInuse/1024, m.HeapReleased/1024, m.HeapObjects/1024) + log.Infof("StackInuse = %v StackSys = %v MCacheInuse = %v MCacheSys = %v\n", m.StackInuse/1024, m.StackSys/1024, m.MCacheInuse/1024, m.MCacheSys/1024) +} + +func JoinNet(nn *nnet.NNet) error { + seeds := config.Parameters.SeedList + rand.Shuffle(len(seeds), func(i int, j int) { + seeds[i], seeds[j] = seeds[j], seeds[i] + }) + + for _, seed := range seeds { + randAddrs, err := client.FindSuccessorAddrs(seed, util.RandomBytes(config.NodeIDBytes)) + if err != nil { + log.Warningf("Can't get successor address from [%s]", seed) + continue + } + + for _, randAddr := range randAddrs { + if randAddr == nn.GetLocalNode().Addr { + log.Warning("Skipping self...") + continue + } + err = nn.Join(randAddr) + if err != nil { + log.Error(err) + continue + } + return nil + } + } + return errors.New("failed to join the network") +} + +// AskMyIP request to seeds randomly, in order to obtain self's externIP and corresponding chordID +func AskMyIP(seeds []string) (string, error) { + rand.Shuffle(len(seeds), func(i int, j int) { + seeds[i], seeds[j] = seeds[j], seeds[i] + }) + + for _, seed := range seeds { + addr, err := client.GetMyExtIP(seed, []byte{}) + if err == nil { + return addr, err + } + log.Warningf("Ask my ID from %s error: %v", seed, err) + } + return "", errors.New("tried all seeds but can't got my external IP and nknID") +} + type NetVer struct { Ver int `json:"version"` } @@ -375,139 +425,18 @@ func GetRemoteVersionNum() (int, error) { // This is temporary and will be removed soon after mainnet is stabilized func netVersion(timer *time.Timer) { - for { - select { - case <-timer.C: - verNum, err := GetRemoteVersionNum() - if err != nil { - log.Warningf("Get the remote version number error: %v", err) - timer.Reset(30 * time.Minute) - break - } - if verNum > NetVersionNum { - log.Fatal("Your current nknd is deprecated, Please download the latest NKN software from https://github.com/nknorg/nkn/releases") - } - + for range timer.C { + verNum, err := GetRemoteVersionNum() + if err != nil { + log.Warningf("Get the remote version number error: %v", err) timer.Reset(30 * time.Minute) + continue } - } -} - -func main() { - defer func() { - if r := recover(); r != nil { - log.Fatalf("Panic: %+v", r) + if verNum > NetVersionNum { + log.Fatal("Your current nknd is deprecated, Please download the latest NKN software from https://github.com/nknorg/nkn/releases") } - }() - // This is temporary and will be removed soon after mainnet is stabilized - timer := time.NewTimer(1 * time.Second) - go netVersion(timer) - - app := cli.NewApp() - app.Name = "nknd" - app.Version = config.Version - app.HelpName = "nknd" - app.Usage = "full node of NKN blockchain" - app.Flags = []cli.Flag{ - cli.BoolFlag{ - Name: "create, c", - Usage: "Create Mode", - Destination: &createMode, - }, - cli.StringFlag{ - Name: "seed", - Usage: "Seed node address to join, multiple seeds should be split by comma", - Destination: &config.SeedList, - }, - cli.StringFlag{ - Name: "passwd, p", - Usage: "Password of Your wallet private Key", - Hidden: true, - Destination: &password.Passwd, - }, - cli.BoolFlag{ - Name: "no-nat", - Usage: "Skip NAT traversal for UPnP and NAT-PMP", - Destination: &config.SkipNAT, - }, - cli.BoolFlag{ - Name: "debug", - Usage: "Provide runtime profiling data of NKN", - Destination: &config.Debug, - }, - cli.StringFlag{ - Name: "pruning", - Usage: "state pruning mode: none, lowmem", - Destination: &config.StatePruningMode, - }, - cli.StringFlag{ - Name: "sync", - Usage: "sync mode: full, fast, light", - Destination: &config.SyncMode, - }, - cli.StringFlag{ - Name: "pprof-port", - Usage: "The port used for pprof in debug mode", - Destination: &config.PprofPort, - }, - cli.StringFlag{ - Name: "config", - Usage: "config file name", - Destination: &config.ConfigFile, - }, - cli.StringFlag{ - Name: "log", - Usage: "directory where your log file will be generated", - Destination: &config.LogPath, - }, - cli.StringFlag{ - Name: "chaindb", - Usage: "directory where your blockchain data will be stored", - Destination: &config.ChainDBPath, - }, - cli.StringFlag{ - Name: "wallet", - Usage: "wallet file", - Destination: &config.WalletFile, - }, - cli.StringFlag{ - Name: "beneficiaryaddr", - Usage: "beneficiary address where your mining reward will go to", - Destination: &config.BeneficiaryAddr, - }, - cli.StringFlag{ - Name: "genesisblockproposer", - Usage: "public key of genesis block proposer", - Destination: &config.GenesisBlockProposer, - }, - cli.BoolFlag{ - Name: "allow-empty-beneficiary-address", - Usage: "beneficiary address is forced unless --allow-empty-beneficiary-address is true", - Destination: &config.AllowEmptyBeneficiaryAddress, - }, - cli.StringFlag{ - Name: "web-gui-listen-address", - Usage: "web gui will listen this address (default: 127.0.0.1)", - Destination: &config.WebGuiListenAddress, - }, - cli.BoolFlag{ - Name: "web-gui-create-wallet", - Usage: "web gui create/open wallet", - Destination: &config.WebGuiCreateWallet, - }, - cli.StringFlag{ - Name: "password-file", - Usage: "read password from file, save password to file when --web-gui-create-wallet arguments be true and password file does not exist", - Destination: &config.PasswordFile, - }, - } - app.Action = nknMain - - // app.Run will shutdown graceful. - if err := app.Run(os.Args); err != nil { - log.Errorf("%v", err) - os.Exit(1) + timer.Reset(30 * time.Minute) } } @@ -638,7 +567,7 @@ func GetOrCreateID(seeds []string, wallet *vault.Wallet, txnFee common.Fixed64, } break } else if len(id) != config.NodeIDBytes { - return nil, fmt.Errorf("Got ID %x from neighbors with wrong size, expecting %d bytes", id, config.NodeIDBytes) + return nil, fmt.Errorf("got ID %x from neighbors with wrong size, expecting %d bytes", id, config.NodeIDBytes) } else if bytes.Equal(id, crypto.Sha256ZeroHash) { log.Info("Waiting for ID generation to complete") break diff --git a/cmd/nknd/main.go b/cmd/nknd/main.go new file mode 100644 index 000000000..5f482fbb4 --- /dev/null +++ b/cmd/nknd/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "log" + + cmd "github.com/nknorg/nkn/v2/cmd/nknd/commands" +) + +func main() { + defer func() { + if r := recover(); r != nil { + log.Fatalf("Panic: %+v", r) + } + }() + + cmd.Execute() +} diff --git a/go.mod b/go.mod index f15f19c46..f3ad96a4b 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,9 @@ require ( github.com/pbnjay/memory v0.0.0-20190104145345-974d429e7ae4 github.com/pborman/uuid v1.2.0 github.com/rdegges/go-ipify v0.0.0-20150526035502-2d94a6a86c40 + github.com/spf13/cobra v1.4.0 + github.com/spf13/pflag v1.0.5 github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 - github.com/urfave/cli v1.22.1 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 google.golang.org/protobuf v1.26.0 diff --git a/go.sum b/go.sum index a23579366..b4c1baae9 100644 --- a/go.sum +++ b/go.sum @@ -63,8 +63,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY= github.com/cpu/goacmedns v0.0.2/go.mod h1:4MipLkI+qScwqtVxcNO6okBhbgRrr7/tKXUSgSL0teQ= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -198,6 +198,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/itchyny/base58-go v0.0.5 h1:uv3ieMgCtuE9HtN0Gux375+GOApFnifLkyvSseHBaH0= github.com/itchyny/base58-go v0.0.5/go.mod h1:SrMWPE3DFuJJp1M/RUhu4fccp/y9AlB8AL3o3duPToU= github.com/jackpal/gateway v1.0.4/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= @@ -329,10 +331,9 @@ github.com/rdegges/go-ipify v0.0.0-20150526035502-2d94a6a86c40 h1:31Y7UZ1yTYBU4E github.com/rdegges/go-ipify v0.0.0-20150526035502-2d94a6a86c40/go.mod h1:j4c6zEU0eMG1oiZPUy+zD4ykX0NIpjZAEOEAviTWC18= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -340,6 +341,10 @@ github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:s github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -363,7 +368,6 @@ github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vultr/govultr v0.4.2/go.mod h1:TUuUizMOFc7z+PNMssb6iGjKjQfpw5arIaOLfocVudQ= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -638,8 +642,9 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=