lotus/chain/wallet/wallet.go

365 lines
8.4 KiB
Go
Raw Normal View History

2019-07-25 22:15:33 +00:00
package wallet
2019-07-05 14:29:17 +00:00
import (
2019-08-16 04:40:59 +00:00
"context"
2019-07-13 00:41:32 +00:00
"sort"
"strings"
2019-10-08 09:17:03 +00:00
"sync"
2019-07-08 12:51:45 +00:00
2020-11-25 04:58:17 +00:00
"github.com/filecoin-project/go-address"
2020-09-07 03:49:10 +00:00
"github.com/filecoin-project/go-state-types/crypto"
logging "github.com/ipfs/go-log/v2"
"golang.org/x/xerrors"
2020-09-05 19:36:32 +00:00
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/sigs"
2020-11-25 04:58:17 +00:00
_ "github.com/filecoin-project/lotus/lib/sigs/bls" // enable bls signatures
_ "github.com/filecoin-project/lotus/lib/sigs/secp" // enable secp signatures
2019-07-05 14:29:17 +00:00
)
2019-10-18 11:39:31 +00:00
var log = logging.Logger("wallet")
2019-07-05 14:29:17 +00:00
const (
2020-06-05 23:04:23 +00:00
KNamePrefix = "wallet-"
KTrashPrefix = "trash-"
KDefault = "default"
2019-07-05 14:29:17 +00:00
)
2020-09-04 20:17:28 +00:00
type LocalWallet struct {
keys map[address.Address]*Key
keystore types.KeyStore
2019-10-08 09:17:03 +00:00
lk sync.Mutex
2019-07-05 14:29:17 +00:00
}
2020-09-05 19:36:32 +00:00
type Default interface {
GetDefault() (address.Address, error)
SetDefault(a address.Address) error
}
2020-09-04 20:17:28 +00:00
func NewWallet(keystore types.KeyStore) (*LocalWallet, error) {
w := &LocalWallet{
keys: make(map[address.Address]*Key),
keystore: keystore,
}
return w, nil
2019-07-05 14:29:17 +00:00
}
2020-09-04 20:17:28 +00:00
func KeyWallet(keys ...*Key) *LocalWallet {
2019-11-28 22:50:58 +00:00
m := make(map[address.Address]*Key)
for _, key := range keys {
m[key.Address] = key
}
2020-09-04 20:17:28 +00:00
return &LocalWallet{
2019-11-28 22:50:58 +00:00
keys: m,
}
}
func (w *LocalWallet) WalletSign(ctx context.Context, addr address.Address, msg []byte, meta api.MsgMeta) (*crypto.Signature, error) {
2019-07-05 14:29:17 +00:00
ki, err := w.findKey(addr)
if err != nil {
return nil, err
}
2019-08-06 23:08:34 +00:00
if ki == nil {
return nil, xerrors.Errorf("signing using key '%s': %w", addr.String(), types.ErrKeyInfoNotFound)
2019-08-06 23:08:34 +00:00
}
2019-07-05 14:29:17 +00:00
2020-02-13 03:50:37 +00:00
return sigs.Sign(ActSigType(ki.Type), ki.PrivateKey, msg)
2019-07-05 14:29:17 +00:00
}
2020-09-04 20:17:28 +00:00
func (w *LocalWallet) findKey(addr address.Address) (*Key, error) {
2019-10-08 09:17:03 +00:00
w.lk.Lock()
defer w.lk.Unlock()
k, ok := w.keys[addr]
if ok {
return k, nil
}
2019-11-28 22:50:58 +00:00
if w.keystore == nil {
log.Warn("findKey didn't find the key in in-memory wallet")
return nil, nil
}
ki, err := w.tryFind(addr)
if err != nil {
if xerrors.Is(err, types.ErrKeyInfoNotFound) {
2019-07-31 16:58:19 +00:00
return nil, nil
}
return nil, xerrors.Errorf("getting from keystore: %w", err)
}
k, err = NewKey(ki)
if err != nil {
return nil, xerrors.Errorf("decoding from keystore: %w", err)
2019-07-05 14:29:17 +00:00
}
w.keys[k.Address] = k
return k, nil
2019-07-05 14:29:17 +00:00
}
func (w *LocalWallet) tryFind(addr address.Address) (types.KeyInfo, error) {
ki, err := w.keystore.Get(KNamePrefix + addr.String())
if err == nil {
return ki, err
}
if !xerrors.Is(err, types.ErrKeyInfoNotFound) {
return types.KeyInfo{}, err
}
// We got an ErrKeyInfoNotFound error
// Try again, this time with the testnet prefix
2020-10-09 07:10:40 +00:00
tAddress, err := swapMainnetForTestnetPrefix(addr.String())
if err != nil {
return types.KeyInfo{}, err
}
2020-10-09 07:10:40 +00:00
ki, err = w.keystore.Get(KNamePrefix + tAddress)
if err != nil {
return types.KeyInfo{}, err
}
// We found it with the testnet prefix
// Add this KeyInfo with the mainnet prefix address string
err = w.keystore.Put(KNamePrefix+addr.String(), ki)
if err != nil {
return types.KeyInfo{}, err
}
return ki, nil
}
2020-10-08 22:51:04 +00:00
func (w *LocalWallet) WalletExport(ctx context.Context, addr address.Address) (*types.KeyInfo, error) {
2019-10-08 09:17:03 +00:00
k, err := w.findKey(addr)
if err != nil {
return nil, xerrors.Errorf("failed to find key to export: %w", err)
}
if k == nil {
return nil, xerrors.Errorf("key not found")
}
2019-10-08 09:17:03 +00:00
2019-10-08 09:46:36 +00:00
return &k.KeyInfo, nil
2019-07-05 14:29:17 +00:00
}
2020-09-04 20:17:28 +00:00
func (w *LocalWallet) WalletImport(ctx context.Context, ki *types.KeyInfo) (address.Address, error) {
2019-10-08 09:46:36 +00:00
w.lk.Lock()
defer w.lk.Unlock()
2019-10-08 09:17:03 +00:00
2019-10-08 09:46:36 +00:00
k, err := NewKey(*ki)
2019-10-08 09:17:03 +00:00
if err != nil {
return address.Undef, xerrors.Errorf("failed to make key: %w", err)
}
2019-10-08 09:46:36 +00:00
if err := w.keystore.Put(KNamePrefix+k.Address.String(), k.KeyInfo); err != nil {
return address.Undef, xerrors.Errorf("saving to keystore: %w", err)
}
2019-10-08 09:17:03 +00:00
return k.Address, nil
2019-07-05 14:29:17 +00:00
}
2020-09-04 20:17:28 +00:00
func (w *LocalWallet) WalletList(ctx context.Context) ([]address.Address, error) {
all, err := w.keystore.List()
if err != nil {
return nil, xerrors.Errorf("listing keystore: %w", err)
}
sort.Strings(all)
seen := map[address.Address]struct{}{}
out := make([]address.Address, 0, len(all))
for _, a := range all {
if strings.HasPrefix(a, KNamePrefix) {
name := strings.TrimPrefix(a, KNamePrefix)
addr, err := address.NewFromString(name)
if err != nil {
return nil, xerrors.Errorf("converting name to address: %w", err)
}
if _, ok := seen[addr]; ok {
continue // got duplicate with a different prefix
}
seen[addr] = struct{}{}
out = append(out, addr)
}
2019-07-13 00:41:32 +00:00
}
sort.Slice(out, func(i, j int) bool {
return out[i].String() < out[j].String()
})
return out, nil
2019-07-13 00:41:32 +00:00
}
2020-09-04 20:17:28 +00:00
func (w *LocalWallet) GetDefault() (address.Address, error) {
w.lk.Lock()
defer w.lk.Unlock()
ki, err := w.keystore.Get(KDefault)
if err != nil {
return address.Undef, xerrors.Errorf("failed to get default key: %w", err)
}
k, err := NewKey(ki)
if err != nil {
return address.Undef, xerrors.Errorf("failed to read default key from keystore: %w", err)
}
return k.Address, nil
}
2020-09-04 20:17:28 +00:00
func (w *LocalWallet) SetDefault(a address.Address) error {
w.lk.Lock()
defer w.lk.Unlock()
ki, err := w.keystore.Get(KNamePrefix + a.String())
if err != nil {
return err
}
2019-10-18 11:39:31 +00:00
if err := w.keystore.Delete(KDefault); err != nil {
if !xerrors.Is(err, types.ErrKeyInfoNotFound) {
log.Warnf("failed to unregister current default key: %s", err)
}
}
if err := w.keystore.Put(KDefault, ki); err != nil {
return err
}
return nil
}
func (w *LocalWallet) WalletNew(ctx context.Context, typ types.KeyType) (address.Address, error) {
2019-10-08 09:17:03 +00:00
w.lk.Lock()
defer w.lk.Unlock()
k, err := GenerateKey(typ)
if err != nil {
return address.Undef, err
}
if err := w.keystore.Put(KNamePrefix+k.Address.String(), k.KeyInfo); err != nil {
return address.Undef, xerrors.Errorf("saving to keystore: %w", err)
2019-07-05 14:29:17 +00:00
}
w.keys[k.Address] = k
_, err = w.keystore.Get(KDefault)
if err != nil {
if !xerrors.Is(err, types.ErrKeyInfoNotFound) {
return address.Undef, err
}
if err := w.keystore.Put(KDefault, k.KeyInfo); err != nil {
return address.Undef, xerrors.Errorf("failed to set new key as default: %w", err)
}
}
return k.Address, nil
2019-07-05 14:29:17 +00:00
}
2020-09-04 20:17:28 +00:00
func (w *LocalWallet) WalletHas(ctx context.Context, addr address.Address) (bool, error) {
k, err := w.findKey(addr)
if err != nil {
return false, err
}
return k != nil, nil
}
2020-11-25 04:58:17 +00:00
func (w *LocalWallet) walletDelete(ctx context.Context, addr address.Address) error {
2020-06-05 23:04:23 +00:00
k, err := w.findKey(addr)
2020-10-21 21:41:45 +00:00
2020-06-05 23:04:23 +00:00
if err != nil {
return xerrors.Errorf("failed to delete key %s : %w", addr, err)
}
if k == nil {
return nil // already not there
}
2020-06-05 23:04:23 +00:00
2020-10-21 21:41:45 +00:00
w.lk.Lock()
defer w.lk.Unlock()
if err := w.keystore.Delete(KTrashPrefix + k.Address.String()); err != nil && !xerrors.Is(err, types.ErrKeyInfoNotFound) {
return xerrors.Errorf("failed to purge trashed key %s: %w", addr, err)
}
2020-06-05 23:04:23 +00:00
if err := w.keystore.Put(KTrashPrefix+k.Address.String(), k.KeyInfo); err != nil {
return xerrors.Errorf("failed to mark key %s as trashed: %w", addr, err)
}
if err := w.keystore.Delete(KNamePrefix + k.Address.String()); err != nil {
return xerrors.Errorf("failed to delete key %s: %w", addr, err)
}
2020-10-09 07:10:40 +00:00
tAddr, err := swapMainnetForTestnetPrefix(addr.String())
if err != nil {
return xerrors.Errorf("failed to swap prefixes: %w", err)
}
// TODO: Does this always error in the not-found case? Just ignoring an error return for now.
_ = w.keystore.Delete(KNamePrefix + tAddr)
2020-10-21 21:41:45 +00:00
delete(w.keys, addr)
2020-11-25 04:58:17 +00:00
return nil
}
2020-11-26 13:36:40 +00:00
func (w *LocalWallet) deleteDefault() {
w.lk.Lock()
defer w.lk.Unlock()
if err := w.keystore.Delete(KDefault); err != nil {
if !xerrors.Is(err, types.ErrKeyInfoNotFound) {
log.Warnf("failed to unregister current default key: %s", err)
}
}
}
2020-11-25 04:58:17 +00:00
func (w *LocalWallet) WalletDelete(ctx context.Context, addr address.Address) error {
if err := w.walletDelete(ctx, addr); err != nil {
return xerrors.Errorf("wallet delete: %w", err)
}
if def, err := w.GetDefault(); err == nil {
if def == addr {
w.deleteDefault()
}
}
2020-06-05 23:04:23 +00:00
return nil
}
func (w *LocalWallet) Get() api.Wallet {
if w == nil {
return nil
}
return w
}
var _ api.Wallet = &LocalWallet{}
2020-10-09 07:10:40 +00:00
func swapMainnetForTestnetPrefix(addr string) (string, error) {
aChars := []rune(addr)
prefixRunes := []rune(address.TestnetPrefix)
if len(prefixRunes) != 1 {
return "", xerrors.Errorf("unexpected prefix length: %d", len(prefixRunes))
}
aChars[0] = prefixRunes[0]
return string(aChars), nil
}
type nilDefault struct{}
func (n nilDefault) GetDefault() (address.Address, error) {
return address.Undef, nil
}
func (n nilDefault) SetDefault(a address.Address) error {
return xerrors.Errorf("not supported; local wallet disabled")
}
var NilDefault nilDefault
var _ Default = NilDefault