lotus/chain/wallet/ledger/ledger.go

140 lines
3.2 KiB
Go
Raw Normal View History

package ledgerwallet
import (
"context"
"encoding/json"
"fmt"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/query"
ledgerfil "github.com/whyrusleeping/ledger-filecoin-go"
)
type LedgerWallet struct {
ds datastore.Datastore
}
func NewWallet(ds datastore.Datastore) *LedgerWallet {
return &LedgerWallet{ds}
}
type LedgerKeyInfo struct {
Address address.Address
Path []uint32
}
var _ (api.WalletAPI) = (*LedgerWallet)(nil)
func (lw LedgerWallet) WalletSign(ctx context.Context, signer address.Address, toSign []byte, meta api.MsgMeta) (*crypto.Signature, error) {
ki, err := lw.getKeyInfo(signer)
if err != nil {
return nil, err
}
fl, err := ledgerfil.FindLedgerFilecoinApp()
if err != nil {
return nil, err
}
defer fl.Close()
if meta.Type != api.MTChainMsg {
return nil, fmt.Errorf("ledger can only sign chain messages")
}
// TODO: assert meta matches the 'toSign' bits
sig, err := fl.SignSECP256K1(ki.Path, meta.Extra)
if err != nil {
return nil, err
}
return &crypto.Signature{
Type: crypto.SigTypeSecp256k1,
Data: sig.SignatureBytes(),
}, nil
}
func (lw LedgerWallet) getKeyInfo(addr address.Address) (*LedgerKeyInfo, error) {
kib, err := lw.ds.Get(keyForAddr(addr))
if err != nil {
return nil, err
}
var out LedgerKeyInfo
if err := json.Unmarshal(kib, &out); err != nil {
return nil, err
}
return &out, nil
}
func (lw LedgerWallet) WalletDelete(ctx context.Context, k address.Address) error {
return lw.ds.Delete(keyForAddr(k))
}
func (lw LedgerWallet) WalletExport(ctx context.Context, k address.Address) (*types.KeyInfo, error) {
return nil, fmt.Errorf("cannot export keys from ledger wallets")
}
func (lw LedgerWallet) WalletHas(ctx context.Context, k address.Address) (bool, error) {
_, err := lw.ds.Get(keyForAddr(k))
if err == nil {
return true, nil
}
if err == datastore.ErrNotFound {
return false, nil
}
return false, err
}
func (lw LedgerWallet) WalletImport(ctx context.Context, kinfo *types.KeyInfo) (address.Address, error) {
var ki LedgerKeyInfo
if err := json.Unmarshal(kinfo.PrivateKey, &ki); err != nil {
return address.Undef, err
}
if ki.Address == address.Undef {
return address.Undef, fmt.Errorf("no address given in imported key info")
}
if err := lw.ds.Put(keyForAddr(ki.Address), kinfo.PrivateKey); err != nil {
return address.Undef, err
}
return ki.Address, nil
}
func (lw LedgerWallet) WalletList(ctx context.Context) ([]address.Address, error) {
res, err := lw.ds.Query(query.Query{Prefix: "ledgerkey/"})
if err != nil {
return nil, err
}
var out []address.Address
for {
res, ok := res.NextSync()
if !ok {
break
}
var ki LedgerKeyInfo
if err := json.Unmarshal(res.Value, &ki); err != nil {
return nil, err
}
out = append(out, ki.Address)
}
return out, nil
}
func (lw LedgerWallet) WalletNew(ctx context.Context, t crypto.SigType) (address.Address, error) {
return address.Undef, fmt.Errorf("cannot create new address on ledger")
}
func keyForAddr(addr address.Address) datastore.Key {
return datastore.NewKey("ledgerkey/" + addr.String())
}