418 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			418 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package cli
 | |
| 
 | |
| import (
 | |
| 	"bufio"
 | |
| 	"encoding/hex"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"io/ioutil"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/filecoin-project/go-address"
 | |
| 	"github.com/filecoin-project/go-state-types/crypto"
 | |
| 	types "github.com/filecoin-project/lotus/chain/types"
 | |
| 	"github.com/filecoin-project/lotus/chain/wallet"
 | |
| 	"golang.org/x/xerrors"
 | |
| 
 | |
| 	"github.com/urfave/cli/v2"
 | |
| )
 | |
| 
 | |
| var walletCmd = &cli.Command{
 | |
| 	Name:  "wallet",
 | |
| 	Usage: "Manage wallet",
 | |
| 	Subcommands: []*cli.Command{
 | |
| 		walletNew,
 | |
| 		walletList,
 | |
| 		walletBalance,
 | |
| 		walletExport,
 | |
| 		walletImport,
 | |
| 		walletGetDefault,
 | |
| 		walletSetDefault,
 | |
| 		walletSign,
 | |
| 		walletVerify,
 | |
| 		walletDelete,
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var walletNew = &cli.Command{
 | |
| 	Name:      "new",
 | |
| 	Usage:     "Generate a new key of the given type",
 | |
| 	ArgsUsage: "[bls|secp256k1 (default secp256k1)]",
 | |
| 	Action: func(cctx *cli.Context) error {
 | |
| 		api, closer, err := GetFullNodeAPI(cctx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer closer()
 | |
| 		ctx := ReqContext(cctx)
 | |
| 
 | |
| 		t := cctx.Args().First()
 | |
| 		if t == "" {
 | |
| 			t = "secp256k1"
 | |
| 		}
 | |
| 
 | |
| 		nk, err := api.WalletNew(ctx, wallet.ActSigType(t))
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		fmt.Println(nk.String())
 | |
| 
 | |
| 		return nil
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var walletList = &cli.Command{
 | |
| 	Name:  "list",
 | |
| 	Usage: "List wallet address",
 | |
| 	Action: func(cctx *cli.Context) error {
 | |
| 		api, closer, err := GetFullNodeAPI(cctx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer closer()
 | |
| 		ctx := ReqContext(cctx)
 | |
| 
 | |
| 		addrs, err := api.WalletList(ctx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		for _, addr := range addrs {
 | |
| 			fmt.Println(addr.String())
 | |
| 		}
 | |
| 		return nil
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var walletBalance = &cli.Command{
 | |
| 	Name:      "balance",
 | |
| 	Usage:     "Get account balance",
 | |
| 	ArgsUsage: "[address]",
 | |
| 	Action: func(cctx *cli.Context) error {
 | |
| 		api, closer, err := GetFullNodeAPI(cctx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer closer()
 | |
| 		ctx := ReqContext(cctx)
 | |
| 
 | |
| 		var addr address.Address
 | |
| 		if cctx.Args().First() != "" {
 | |
| 			addr, err = address.NewFromString(cctx.Args().First())
 | |
| 		} else {
 | |
| 			addr, err = api.WalletDefaultAddress(ctx)
 | |
| 		}
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		balance, err := api.WalletBalance(ctx, addr)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		if balance.Equals(types.NewInt(0)) {
 | |
| 			fmt.Printf("%s (warning: may display 0 if chain sync in progress)\n", types.FIL(balance))
 | |
| 		} else {
 | |
| 			fmt.Printf("%s\n", types.FIL(balance))
 | |
| 		}
 | |
| 
 | |
| 		return nil
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var walletGetDefault = &cli.Command{
 | |
| 	Name:  "default",
 | |
| 	Usage: "Get default wallet address",
 | |
| 	Action: func(cctx *cli.Context) error {
 | |
| 		api, closer, err := GetFullNodeAPI(cctx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer closer()
 | |
| 		ctx := ReqContext(cctx)
 | |
| 
 | |
| 		addr, err := api.WalletDefaultAddress(ctx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		fmt.Printf("%s\n", addr.String())
 | |
| 		return nil
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var walletSetDefault = &cli.Command{
 | |
| 	Name:      "set-default",
 | |
| 	Usage:     "Set default wallet address",
 | |
| 	ArgsUsage: "[address]",
 | |
| 	Action: func(cctx *cli.Context) error {
 | |
| 		api, closer, err := GetFullNodeAPI(cctx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer closer()
 | |
| 		ctx := ReqContext(cctx)
 | |
| 
 | |
| 		if !cctx.Args().Present() {
 | |
| 			return fmt.Errorf("must pass address to set as default")
 | |
| 		}
 | |
| 
 | |
| 		addr, err := address.NewFromString(cctx.Args().First())
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		return api.WalletSetDefault(ctx, addr)
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var walletExport = &cli.Command{
 | |
| 	Name:      "export",
 | |
| 	Usage:     "export keys",
 | |
| 	ArgsUsage: "[address]",
 | |
| 	Action: func(cctx *cli.Context) error {
 | |
| 		api, closer, err := GetFullNodeAPI(cctx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer closer()
 | |
| 		ctx := ReqContext(cctx)
 | |
| 
 | |
| 		if !cctx.Args().Present() {
 | |
| 			return fmt.Errorf("must specify key to export")
 | |
| 		}
 | |
| 
 | |
| 		addr, err := address.NewFromString(cctx.Args().First())
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		ki, err := api.WalletExport(ctx, addr)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		b, err := json.Marshal(ki)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		fmt.Println(hex.EncodeToString(b))
 | |
| 		return nil
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var walletImport = &cli.Command{
 | |
| 	Name:      "import",
 | |
| 	Usage:     "import keys",
 | |
| 	ArgsUsage: "[<path> (optional, will read from stdin if omitted)]",
 | |
| 	Flags: []cli.Flag{
 | |
| 		&cli.StringFlag{
 | |
| 			Name:  "format",
 | |
| 			Usage: "specify input format for key",
 | |
| 			Value: "hex-lotus",
 | |
| 		},
 | |
| 		&cli.BoolFlag{
 | |
| 			Name:  "as-default",
 | |
| 			Usage: "import the given key as your new default key",
 | |
| 		},
 | |
| 	},
 | |
| 	Action: func(cctx *cli.Context) error {
 | |
| 		api, closer, err := GetFullNodeAPI(cctx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer closer()
 | |
| 		ctx := ReqContext(cctx)
 | |
| 
 | |
| 		var inpdata []byte
 | |
| 		if !cctx.Args().Present() || cctx.Args().First() == "-" {
 | |
| 			reader := bufio.NewReader(os.Stdin)
 | |
| 			fmt.Print("Enter private key: ")
 | |
| 			indata, err := reader.ReadBytes('\n')
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			inpdata = indata
 | |
| 
 | |
| 		} else {
 | |
| 			fdata, err := ioutil.ReadFile(cctx.Args().First())
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			inpdata = fdata
 | |
| 		}
 | |
| 
 | |
| 		var ki types.KeyInfo
 | |
| 		switch cctx.String("format") {
 | |
| 		case "hex-lotus":
 | |
| 			data, err := hex.DecodeString(strings.TrimSpace(string(inpdata)))
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 
 | |
| 			if err := json.Unmarshal(data, &ki); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		case "json-lotus":
 | |
| 			if err := json.Unmarshal(inpdata, &ki); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		case "gfc-json":
 | |
| 			var f struct {
 | |
| 				KeyInfo []struct {
 | |
| 					PrivateKey []byte
 | |
| 					SigType    int
 | |
| 				}
 | |
| 			}
 | |
| 			if err := json.Unmarshal(inpdata, &f); err != nil {
 | |
| 				return xerrors.Errorf("failed to parse go-filecoin key: %s", err)
 | |
| 			}
 | |
| 
 | |
| 			gk := f.KeyInfo[0]
 | |
| 			ki.PrivateKey = gk.PrivateKey
 | |
| 			switch gk.SigType {
 | |
| 			case 1:
 | |
| 				ki.Type = wallet.KTSecp256k1
 | |
| 			case 2:
 | |
| 				ki.Type = wallet.KTBLS
 | |
| 			default:
 | |
| 				return fmt.Errorf("unrecognized key type: %d", gk.SigType)
 | |
| 			}
 | |
| 		default:
 | |
| 			return fmt.Errorf("unrecognized format: %s", cctx.String("format"))
 | |
| 		}
 | |
| 
 | |
| 		addr, err := api.WalletImport(ctx, &ki)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		if cctx.Bool("as-default") {
 | |
| 			if err := api.WalletSetDefault(ctx, addr); err != nil {
 | |
| 				return fmt.Errorf("failed to set default key: %w", err)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		fmt.Printf("imported key %s successfully!\n", addr)
 | |
| 		return nil
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var walletSign = &cli.Command{
 | |
| 	Name:      "sign",
 | |
| 	Usage:     "sign a message",
 | |
| 	ArgsUsage: "<signing address> <hexMessage>",
 | |
| 	Action: func(cctx *cli.Context) error {
 | |
| 		api, closer, err := GetFullNodeAPI(cctx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer closer()
 | |
| 		ctx := ReqContext(cctx)
 | |
| 
 | |
| 		if !cctx.Args().Present() || cctx.NArg() != 2 {
 | |
| 			return fmt.Errorf("must specify signing address and message to sign")
 | |
| 		}
 | |
| 
 | |
| 		addr, err := address.NewFromString(cctx.Args().First())
 | |
| 
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		msg, err := hex.DecodeString(cctx.Args().Get(1))
 | |
| 
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		sig, err := api.WalletSign(ctx, addr, msg)
 | |
| 
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		sigBytes := append([]byte{byte(sig.Type)}, sig.Data...)
 | |
| 
 | |
| 		fmt.Println(hex.EncodeToString(sigBytes))
 | |
| 		return nil
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var walletVerify = &cli.Command{
 | |
| 	Name:      "verify",
 | |
| 	Usage:     "verify the signature of a message",
 | |
| 	ArgsUsage: "<signing address> <hexMessage> <signature>",
 | |
| 	Action: func(cctx *cli.Context) error {
 | |
| 		api, closer, err := GetFullNodeAPI(cctx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer closer()
 | |
| 		ctx := ReqContext(cctx)
 | |
| 
 | |
| 		if !cctx.Args().Present() || cctx.NArg() != 3 {
 | |
| 			return fmt.Errorf("must specify signing address, message, and signature to verify")
 | |
| 		}
 | |
| 
 | |
| 		addr, err := address.NewFromString(cctx.Args().First())
 | |
| 
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		msg, err := hex.DecodeString(cctx.Args().Get(1))
 | |
| 
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		sigBytes, err := hex.DecodeString(cctx.Args().Get(2))
 | |
| 
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		var sig crypto.Signature
 | |
| 		if err := sig.UnmarshalBinary(sigBytes); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		if api.WalletVerify(ctx, addr, msg, &sig) {
 | |
| 			fmt.Println("valid")
 | |
| 			return nil
 | |
| 		}
 | |
| 		fmt.Println("invalid")
 | |
| 		return NewCliError("CLI Verify called with invalid signature")
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var walletDelete = &cli.Command{
 | |
| 	Name:      "delete",
 | |
| 	Usage:     "Delete an account from the wallet",
 | |
| 	ArgsUsage: "<address> ",
 | |
| 	Action: func(cctx *cli.Context) error {
 | |
| 		api, closer, err := GetFullNodeAPI(cctx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer closer()
 | |
| 		ctx := ReqContext(cctx)
 | |
| 
 | |
| 		if !cctx.Args().Present() || cctx.NArg() != 1 {
 | |
| 			return fmt.Errorf("must specify address to delete")
 | |
| 		}
 | |
| 
 | |
| 		addr, err := address.NewFromString(cctx.Args().First())
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		return api.WalletDelete(ctx, addr)
 | |
| 	},
 | |
| }
 |