// Copyright Fuzamei Corp. 2018 All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package commands import ( "fmt" "os" "strconv" "time" "errors" "github.com/33cn/chain33/rpc/jsonclient" rpctypes "github.com/33cn/chain33/rpc/types" . "github.com/33cn/chain33/system/dapp/commands/types" "github.com/33cn/chain33/types" "github.com/spf13/cobra" ) // WalletCmd wallet command func WalletCmd() *cobra.Command { cmd := &cobra.Command{ Use: "wallet", Short: "Wallet management", Args: cobra.MinimumNArgs(1), } cmd.AddCommand( LockCmd(), UnlockCmd(), WalletStatusCmd(), SetPwdCmd(), WalletListTxsCmd(), MergeBalanceCmd(), AutoMineCmd(), SignRawTxCmd(), NoBalanceCmd(), SetFeeCmd(), SendTxCmd(), ) return cmd } // LockCmd lock the wallet func LockCmd() *cobra.Command { cmd := &cobra.Command{ Use: "lock", Short: "Lock wallet", Run: lock, } return cmd } func lock(cmd *cobra.Command, args []string) { rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") var res rpctypes.Reply ctx := jsonclient.NewRpcCtx(rpcLaddr, "Chain33.Lock", nil, &res) ctx.Run() } // UnlockCmd unlock the wallet func UnlockCmd() *cobra.Command { cmd := &cobra.Command{ Use: "unlock", Short: "Unlock wallet", Run: unLock, } addUnlockFlags(cmd) return cmd } func addUnlockFlags(cmd *cobra.Command) { cmd.Flags().StringP("pwd", "p", "", "password needed to unlock") cmd.MarkFlagRequired("pwd") cmd.Flags().Int64P("time_out", "t", 0, "time out for unlock operation(0 for unlimited)") cmd.Flags().StringP("scope", "s", "wallet", "unlock scope(wallet/ticket)") } func unLock(cmd *cobra.Command, args []string) { rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") pwd, _ := cmd.Flags().GetString("pwd") timeOut, _ := cmd.Flags().GetInt64("time_out") scope, _ := cmd.Flags().GetString("scope") var walletOrTicket bool switch scope { case "wallet": walletOrTicket = false case "ticket": walletOrTicket = true default: fmt.Println("unlock scope code wrong: wallet or ticket") return } params := types.WalletUnLock{ Passwd: pwd, Timeout: timeOut, WalletOrTicket: walletOrTicket, } var res rpctypes.Reply ctx := jsonclient.NewRpcCtx(rpcLaddr, "Chain33.UnLock", params, &res) ctx.Run() } // WalletStatusCmd get wallet status func WalletStatusCmd() *cobra.Command { cmd := &cobra.Command{ Use: "status", Short: "Get wallet status", Run: walletStatus, } return cmd } func walletStatus(cmd *cobra.Command, args []string) { rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") var res rpctypes.WalletStatus ctx := jsonclient.NewRpcCtx(rpcLaddr, "Chain33.GetWalletStatus", nil, &res) ctx.Run() } // SetPwdCmd set password func SetPwdCmd() *cobra.Command { cmd := &cobra.Command{ Use: "set_pwd", Short: "Set password", Run: setPwd, } addSetPwdFlags(cmd) return cmd } func addSetPwdFlags(cmd *cobra.Command) { cmd.Flags().StringP("old", "o", "", "old password") cmd.MarkFlagRequired("old") cmd.Flags().StringP("new", "n", "", "new password") cmd.MarkFlagRequired("new") } func setPwd(cmd *cobra.Command, args []string) { rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") oldPwd, _ := cmd.Flags().GetString("old") newPwd, _ := cmd.Flags().GetString("new") params := types.ReqWalletSetPasswd{ OldPass: oldPwd, NewPass: newPwd, } var res rpctypes.Reply ctx := jsonclient.NewRpcCtx(rpcLaddr, "Chain33.SetPasswd", params, &res) ctx.Run() } // WalletListTxsCmd get wallet transactions func WalletListTxsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "list_txs", Short: "List transactions in wallet", Run: walletListTxs, } addWalletListTxsFlags(cmd) return cmd } func addWalletListTxsFlags(cmd *cobra.Command) { cmd.Flags().StringP("from", "f", "", "from which transaction begin") cmd.MarkFlagRequired("from") cmd.Flags().Int32P("count", "c", 0, "number of transactions") cmd.MarkFlagRequired("count") cmd.Flags().Int32P("direction", "d", 1, "query direction (0: pre page, 1: next page)") } func walletListTxs(cmd *cobra.Command, args []string) { rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") txHash, _ := cmd.Flags().GetString("from") count, _ := cmd.Flags().GetInt32("count") direction, _ := cmd.Flags().GetInt32("dir") params := rpctypes.ReqWalletTransactionList{ FromTx: txHash, Count: count, Direction: direction, } var res rpctypes.WalletTxDetails ctx := jsonclient.NewRpcCtx(rpcLaddr, "Chain33.WalletTxList", params, &res) ctx.SetResultCb(parseWalletTxListRes) ctx.Run() } func parseWalletTxListRes(arg interface{}) (interface{}, error) { res := arg.(*rpctypes.WalletTxDetails) var result WalletTxDetailsResult for _, v := range res.TxDetails { amountResult := strconv.FormatFloat(float64(v.Amount)/float64(types.Coin), 'f', 4, 64) wtxd := &WalletTxDetailResult{ Tx: DecodeTransaction(v.Tx), Receipt: v.Receipt, Height: v.Height, Index: v.Index, Blocktime: v.BlockTime, Amount: amountResult, Fromaddr: v.FromAddr, Txhash: v.TxHash, ActionName: v.ActionName, } result.TxDetails = append(result.TxDetails, wtxd) } return result, nil } // MergeBalanceCmd merge all balance to an account func MergeBalanceCmd() *cobra.Command { cmd := &cobra.Command{ Use: "merge", Short: "Merge accounts' balance into address", Run: mergeBalance, } addMergeBalanceFlags(cmd) return cmd } func addMergeBalanceFlags(cmd *cobra.Command) { cmd.Flags().StringP("to", "t", "", "destination account address") cmd.MarkFlagRequired("to") } func mergeBalance(cmd *cobra.Command, args []string) { rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") toAddr, _ := cmd.Flags().GetString("to") params := types.ReqWalletMergeBalance{ To: toAddr, } var res rpctypes.ReplyHashes ctx := jsonclient.NewRpcCtx(rpcLaddr, "Chain33.MergeBalance", params, &res) ctx.Run() } // AutoMineCmd set auto mining: 为了兼容现在的命令行, 这个命令就放到wallet,实际上依赖 ticket func AutoMineCmd() *cobra.Command { cmd := &cobra.Command{ Use: "auto_mine", Short: "Set auto mine on/off", Run: autoMine, } addAutoMineFlags(cmd) return cmd } func addAutoMineFlags(cmd *cobra.Command) { cmd.Flags().Int32P("flag", "f", 0, `auto mine(0: off, 1: on)`) cmd.MarkFlagRequired("flag") } func autoMine(cmd *cobra.Command, args []string) { rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") flag, _ := cmd.Flags().GetInt32("flag") if flag != 0 && flag != 1 { cmd.UsageFunc()(cmd) return } params := struct { Flag int32 }{ Flag: flag, } var res rpctypes.Reply ctx := jsonclient.NewRpcCtx(rpcLaddr, "ticket.SetAutoMining", params, &res) ctx.Run() } // NoBalanceCmd sign raw tx func NoBalanceCmd() *cobra.Command { cmd := &cobra.Command{ Use: "nobalance", Short: "Create nobalance transaction", Run: noBalanceTx, } addNoBalanceFlags(cmd) return cmd } func addNoBalanceFlags(cmd *cobra.Command) { cmd.Flags().StringP("data", "d", "", "raw transaction data") cmd.MarkFlagRequired("data") cmd.Flags().StringP("key", "k", "", "private key of pay fee account (optional)") cmd.Flags().StringP("payaddr", "a", "", "account address of pay fee (optional)") cmd.Flags().StringP("expire", "e", "120s", "transaction expire time") } // SignRawTxCmd sign raw tx func SignRawTxCmd() *cobra.Command { cmd := &cobra.Command{ Use: "sign", Short: "Sign transaction", Run: signRawTx, } addSignRawTxFlags(cmd) return cmd } func addSignRawTxFlags(cmd *cobra.Command) { cmd.Flags().StringP("data", "d", "", "raw transaction data") cmd.MarkFlagRequired("data") cmd.Flags().Int32P("index", "i", 0, "transaction index to be signed") cmd.Flags().StringP("key", "k", "", "private key (optional)") cmd.Flags().StringP("addr", "a", "", "account address (optional)") cmd.Flags().StringP("expire", "e", "120s", "transaction expire time") // A duration string is a possibly signed sequence of // decimal numbers, each with optional fraction and a unit suffix, // such as "300ms", "-1.5h" or "2h45m". // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". } func noBalanceTx(cmd *cobra.Command, args []string) { rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") data, _ := cmd.Flags().GetString("data") addr, _ := cmd.Flags().GetString("payaddr") privkey, _ := cmd.Flags().GetString("key") expire, _ := cmd.Flags().GetString("expire") expireTime, err := time.ParseDuration(expire) if err != nil { fmt.Fprintln(os.Stderr, err) return } if expireTime < time.Minute*2 && expireTime != time.Second*0 { expire = "120s" fmt.Println("expire time must longer than 2 minutes, changed expire time into 2 minutes") } params := types.NoBalanceTx{ PayAddr: addr, TxHex: data, Expire: expire, Privkey: privkey, } ctx := jsonclient.NewRpcCtx(rpcLaddr, "Chain33.CreateNoBalanceTransaction", params, nil) ctx.RunWithoutMarshal() } func parseTxHeight(expire string) error { if len(expire) == 0 { return errors.New("expire string should not be empty") } if expire[0] == 'H' && expire[1] == ':' { txHeight, err := strconv.Atoi(expire[2:]) if err != nil { return err } if txHeight <= 0 { fmt.Printf("txHeight should be grate to 0") return errors.New("txHeight should be grate to 0") } return nil } return errors.New("Invalid expire format. Should be one of {time:\"3600s/1min/1h\" block:\"123\" txHeight:\"H:123\"}") } func parseExpireOpt(expire string) (string, error) { //时间格式123s/1m/1h expireTime, err := time.ParseDuration(expire) if err == nil { if expireTime < time.Minute*2 && expireTime != time.Second*0 { expire = "120s" fmt.Println("expire time must longer than 2 minutes, changed expire time into 2 minutes") } return expire, nil } //区块高度格式,123 blockInt, err := strconv.Atoi(expire) if err == nil { if blockInt <= 0 { fmt.Printf("block height should be grate to 0") return "", errors.New("block height should be grate to 0") } return expire, nil } //Txheight格式,H:123 err = parseTxHeight(expire) if err != nil { return "", err } return expire, err } func signRawTx(cmd *cobra.Command, args []string) { rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") data, _ := cmd.Flags().GetString("data") key, _ := cmd.Flags().GetString("key") addr, _ := cmd.Flags().GetString("addr") index, _ := cmd.Flags().GetInt32("index") expire, _ := cmd.Flags().GetString("expire") expire, err := parseExpireOpt(expire) if err != nil { fmt.Fprintln(os.Stderr, err) return } params := types.ReqSignRawTx{ Addr: addr, Privkey: key, TxHex: data, Expire: expire, Index: index, } ctx := jsonclient.NewRpcCtx(rpcLaddr, "Chain33.SignRawTx", params, nil) ctx.RunWithoutMarshal() } // SetFeeCmd set tx fee func SetFeeCmd() *cobra.Command { cmd := &cobra.Command{ Use: "set_fee", Short: "Set transaction fee", Run: setFee, } addSetFeeFlags(cmd) return cmd } func addSetFeeFlags(cmd *cobra.Command) { cmd.Flags().Float64P("amount", "a", 0, "tx fee amount") cmd.MarkFlagRequired("amount") } func setFee(cmd *cobra.Command, args []string) { rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") amount, _ := cmd.Flags().GetFloat64("amount") amountInt64 := int64(amount * 1e4) params := types.ReqWalletSetFee{ Amount: amountInt64 * 1e4, } var res rpctypes.Reply ctx := jsonclient.NewRpcCtx(rpcLaddr, "Chain33.SetTxFee", params, &res) ctx.Run() } // SendTxCmd send raw tx func SendTxCmd() *cobra.Command { cmd := &cobra.Command{ Use: "send", Short: "Send a transaction", Run: sendTx, } addSendTxFlags(cmd) return cmd } func addSendTxFlags(cmd *cobra.Command) { cmd.Flags().StringP("data", "d", "", "transaction content") cmd.MarkFlagRequired("data") cmd.Flags().StringP("token", "t", types.BTY, "token name. (BTY supported)") } func sendTx(cmd *cobra.Command, args []string) { rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") data, _ := cmd.Flags().GetString("data") token, _ := cmd.Flags().GetString("token") params := rpctypes.RawParm{ Token: token, Data: data, } ctx := jsonclient.NewRpcCtx(rpcLaddr, "Chain33.SendTransaction", params, nil) ctx.RunWithoutMarshal() }