2020-10-10 03:17:04 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/go-address"
|
2020-10-10 11:41:19 +00:00
|
|
|
"github.com/urfave/cli/v2"
|
|
|
|
ledgerfil "github.com/whyrusleeping/ledger-filecoin-go"
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/api"
|
2020-10-10 03:17:04 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
|
|
"github.com/filecoin-project/lotus/chain/wallet"
|
|
|
|
ledgerwallet "github.com/filecoin-project/lotus/chain/wallet/ledger"
|
2020-10-10 11:41:19 +00:00
|
|
|
lcli "github.com/filecoin-project/lotus/cli"
|
2020-10-10 03:17:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var ledgerCmd = &cli.Command{
|
|
|
|
Name: "ledger",
|
|
|
|
Usage: "Ledger interactions",
|
|
|
|
Flags: []cli.Flag{},
|
|
|
|
Subcommands: []*cli.Command{
|
|
|
|
ledgerListAddressesCmd,
|
|
|
|
ledgerKeyInfoCmd,
|
|
|
|
ledgerSignTestCmd,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-10-10 20:59:40 +00:00
|
|
|
const hdHard = 0x80000000
|
|
|
|
|
2020-10-10 03:17:04 +00:00
|
|
|
var ledgerListAddressesCmd = &cli.Command{
|
|
|
|
Name: "list",
|
2020-10-10 11:41:19 +00:00
|
|
|
Flags: []cli.Flag{
|
|
|
|
&cli.BoolFlag{
|
2020-10-10 12:06:03 +00:00
|
|
|
Name: "print-balances",
|
|
|
|
Usage: "print balances",
|
2020-10-10 11:41:19 +00:00
|
|
|
Aliases: []string{"b"},
|
|
|
|
},
|
|
|
|
},
|
2020-10-10 03:17:04 +00:00
|
|
|
Action: func(cctx *cli.Context) error {
|
2020-10-10 11:41:19 +00:00
|
|
|
var api api.FullNode
|
|
|
|
if cctx.Bool("print-balances") {
|
|
|
|
a, closer, err := lcli.GetFullNodeAPI(cctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
api = a
|
|
|
|
|
|
|
|
defer closer()
|
|
|
|
}
|
|
|
|
ctx := lcli.ReqContext(cctx)
|
2020-10-10 03:17:04 +00:00
|
|
|
|
|
|
|
fl, err := ledgerfil.FindLedgerFilecoinApp()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-10 20:59:40 +00:00
|
|
|
end := 20
|
|
|
|
for i := 0; i < end; i++ {
|
2020-10-10 11:41:19 +00:00
|
|
|
if err := ctx.Err(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-10 21:13:22 +00:00
|
|
|
p := []uint32{hdHard | 44, hdHard | 461, hdHard, 0, uint32(i)}
|
2020-10-10 03:17:04 +00:00
|
|
|
pubk, err := fl.GetPublicKeySECP256K1(p)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
addr, err := address.NewSecp256k1Address(pubk)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-10 11:41:19 +00:00
|
|
|
if cctx.Bool("print-balances") && api != nil { // api check makes linter happier
|
|
|
|
b, err := api.WalletBalance(ctx, addr)
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("getting balance: %w", err)
|
|
|
|
}
|
2020-10-10 20:59:40 +00:00
|
|
|
if !b.IsZero() {
|
|
|
|
end = i + 21 // BIP32 spec, stop after 20 empty addresses
|
|
|
|
}
|
2020-10-10 11:41:19 +00:00
|
|
|
|
|
|
|
fmt.Printf("%s %s %s\n", addr, printHDPath(p), types.FIL(b))
|
|
|
|
} else {
|
|
|
|
fmt.Printf("%s %s\n", addr, printHDPath(p))
|
|
|
|
}
|
|
|
|
|
2020-10-10 03:17:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseHDPath(s string) ([]uint32, error) {
|
|
|
|
parts := strings.Split(s, "/")
|
|
|
|
if parts[0] != "m" {
|
|
|
|
return nil, fmt.Errorf("expected HD path to start with 'm'")
|
|
|
|
}
|
|
|
|
|
|
|
|
var out []uint32
|
|
|
|
for _, p := range parts[1:] {
|
|
|
|
var hard bool
|
|
|
|
if strings.HasSuffix(p, "'") {
|
|
|
|
p = p[:len(p)-1]
|
|
|
|
hard = true
|
|
|
|
}
|
|
|
|
|
|
|
|
v, err := strconv.ParseUint(p, 10, 32)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-10-10 20:59:40 +00:00
|
|
|
if v >= hdHard {
|
2020-10-10 03:17:04 +00:00
|
|
|
return nil, fmt.Errorf("path element %s too large", p)
|
|
|
|
}
|
|
|
|
|
|
|
|
if hard {
|
2020-10-10 20:59:40 +00:00
|
|
|
v += hdHard
|
2020-10-10 03:17:04 +00:00
|
|
|
}
|
|
|
|
out = append(out, uint32(v))
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func printHDPath(pth []uint32) string {
|
2020-10-10 20:59:40 +00:00
|
|
|
s := "m"
|
2020-10-10 03:17:04 +00:00
|
|
|
for _, p := range pth {
|
2020-10-10 20:59:40 +00:00
|
|
|
s += "/"
|
|
|
|
|
|
|
|
hard := p&hdHard != 0
|
|
|
|
p &^= hdHard // remove hdHard bit
|
|
|
|
|
2020-10-10 03:17:04 +00:00
|
|
|
s += fmt.Sprint(p)
|
|
|
|
if hard {
|
|
|
|
s += "'"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-10 20:59:40 +00:00
|
|
|
return s
|
2020-10-10 03:17:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var ledgerKeyInfoCmd = &cli.Command{
|
|
|
|
Name: "key-info",
|
2020-10-10 11:41:19 +00:00
|
|
|
Flags: []cli.Flag{
|
|
|
|
&cli.BoolFlag{
|
2020-10-10 12:06:03 +00:00
|
|
|
Name: "verbose",
|
2020-10-10 11:41:19 +00:00
|
|
|
Aliases: []string{"v"},
|
|
|
|
},
|
|
|
|
},
|
2020-10-10 03:17:04 +00:00
|
|
|
Action: func(cctx *cli.Context) error {
|
|
|
|
if !cctx.Args().Present() {
|
|
|
|
return cli.ShowCommandHelp(cctx, cctx.Command.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
fl, err := ledgerfil.FindLedgerFilecoinApp()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
p, err := parseHDPath(cctx.Args().First())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
pubk, _, addr, err := fl.GetAddressPubKeySECP256K1(p)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-10-10 20:59:40 +00:00
|
|
|
|
2020-10-10 11:41:19 +00:00
|
|
|
if cctx.Bool("verbose") {
|
|
|
|
fmt.Println(addr)
|
|
|
|
fmt.Println(pubk)
|
|
|
|
}
|
2020-10-10 03:17:04 +00:00
|
|
|
|
|
|
|
a, err := address.NewFromString(addr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var pd ledgerwallet.LedgerKeyInfo
|
|
|
|
pd.Address = a
|
|
|
|
pd.Path = p
|
|
|
|
|
|
|
|
b, err := json.Marshal(pd)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var ki types.KeyInfo
|
|
|
|
ki.Type = wallet.KTSecp256k1
|
|
|
|
ki.PrivateKey = b
|
|
|
|
|
|
|
|
out, err := json.Marshal(ki)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println(string(out))
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var ledgerSignTestCmd = &cli.Command{
|
|
|
|
Name: "sign",
|
|
|
|
Action: func(cctx *cli.Context) error {
|
|
|
|
if !cctx.Args().Present() {
|
|
|
|
return cli.ShowCommandHelp(cctx, cctx.Command.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
fl, err := ledgerfil.FindLedgerFilecoinApp()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
p, err := parseHDPath(cctx.Args().First())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
addr, err := address.NewFromString("f1xc3hws5n6y5m3m44gzb3gyjzhups6wzmhe663ji")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
m := &types.Message{
|
|
|
|
To: addr,
|
|
|
|
From: addr,
|
|
|
|
}
|
|
|
|
|
|
|
|
b, err := m.ToStorageBlock()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
sig, err := fl.SignSECP256K1(p, b.RawData())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println(sig.SignatureBytes())
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|