Basic multi-wallet support
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
This commit is contained in:
parent
7ee6bafd23
commit
6a232e7214
@ -17,13 +17,14 @@ import (
|
|||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LedgerWallet struct {
|
type LedgerWallet struct {
|
||||||
ds datastore.Datastore
|
ds datastore.Datastore
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWallet(ds datastore.Datastore) *LedgerWallet {
|
func NewWallet(ds dtypes.MetadataDS) *LedgerWallet {
|
||||||
return &LedgerWallet{ds}
|
return &LedgerWallet{ds}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +128,7 @@ func (lw LedgerWallet) WalletImport(ctx context.Context, kinfo *types.KeyInfo) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (lw LedgerWallet) WalletList(ctx context.Context) ([]address.Address, error) {
|
func (lw LedgerWallet) WalletList(ctx context.Context) ([]address.Address, error) {
|
||||||
res, err := lw.ds.Query(query.Query{Prefix: "ledgerkey/"})
|
res, err := lw.ds.Query(query.Query{Prefix: "/ledgerkey/"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -153,6 +154,14 @@ func (lw LedgerWallet) WalletNew(ctx context.Context, t crypto.SigType) (address
|
|||||||
return address.Undef, fmt.Errorf("cannot create new address on ledger")
|
return address.Undef, fmt.Errorf("cannot create new address on ledger")
|
||||||
}
|
}
|
||||||
|
|
||||||
func keyForAddr(addr address.Address) datastore.Key {
|
func (lw *LedgerWallet) Get() api.WalletAPI {
|
||||||
return datastore.NewKey("ledgerkey/" + addr.String())
|
if lw == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return lw
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyForAddr(addr address.Address) datastore.Key {
|
||||||
|
return datastore.NewKey("/ledgerkey/" + addr.String())
|
||||||
}
|
}
|
||||||
|
160
chain/wallet/multi.go
Normal file
160
chain/wallet/multi.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package wallet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.uber.org/fx"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
ledgerwallet "github.com/filecoin-project/lotus/chain/wallet/ledger"
|
||||||
|
"github.com/filecoin-project/lotus/chain/wallet/remotewallet"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MultiWallet struct {
|
||||||
|
fx.In // "constructed" with fx.In instead of normal constructor
|
||||||
|
|
||||||
|
Local *LocalWallet `optional:"true"`
|
||||||
|
Remote *remotewallet.RemoteWallet `optional:"true"`
|
||||||
|
Ledger *ledgerwallet.LedgerWallet `optional:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type getif interface {
|
||||||
|
api.WalletAPI
|
||||||
|
|
||||||
|
// workaround for the fact that iface(*struct(nil)) != nil
|
||||||
|
Get() api.WalletAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
func firstNonNil(wallets ...getif) api.WalletAPI {
|
||||||
|
for _, w := range wallets {
|
||||||
|
if w.Get() != nil {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func nonNil(wallets ...getif) []api.WalletAPI {
|
||||||
|
var out []api.WalletAPI
|
||||||
|
for _, w := range wallets {
|
||||||
|
if w.Get() == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MultiWallet) find(ctx context.Context, address address.Address, wallets ...getif) (api.WalletAPI, error) {
|
||||||
|
ws := nonNil(wallets...)
|
||||||
|
|
||||||
|
for _, w := range ws {
|
||||||
|
have, err := w.WalletHas(ctx, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if have {
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MultiWallet) WalletNew(ctx context.Context, sigType crypto.SigType) (address.Address, error) {
|
||||||
|
w := firstNonNil(m.Remote, m.Local)
|
||||||
|
if w == nil {
|
||||||
|
return address.Undef, xerrors.Errorf("no wallet backends configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.WalletNew(ctx, sigType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MultiWallet) WalletHas(ctx context.Context, address address.Address) (bool, error) {
|
||||||
|
w, err := m.find(ctx, address, m.Remote, m.Ledger, m.Local)
|
||||||
|
return w != nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MultiWallet) WalletList(ctx context.Context) ([]address.Address, error) {
|
||||||
|
var out []address.Address
|
||||||
|
seen := map[address.Address]struct{}{}
|
||||||
|
|
||||||
|
ws := nonNil(m.Remote, m.Ledger, m.Local)
|
||||||
|
for _, w := range ws {
|
||||||
|
l, err := w.WalletList(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, a := range l {
|
||||||
|
if _, ok := seen[a]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[a] = struct{}{}
|
||||||
|
|
||||||
|
out = append(out, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MultiWallet) WalletSign(ctx context.Context, signer address.Address, toSign []byte, meta api.MsgMeta) (*crypto.Signature, error) {
|
||||||
|
w, err := m.find(ctx, signer, m.Remote, m.Ledger, m.Local)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if w == nil {
|
||||||
|
return nil, xerrors.Errorf("key not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.WalletSign(ctx, signer, toSign, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MultiWallet) WalletExport(ctx context.Context, address address.Address) (*types.KeyInfo, error) {
|
||||||
|
w, err := m.find(ctx, address, m.Remote, m.Local)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if w == nil {
|
||||||
|
return nil, xerrors.Errorf("key not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.WalletExport(ctx, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MultiWallet) WalletImport(ctx context.Context, info *types.KeyInfo) (address.Address, error) {
|
||||||
|
w := firstNonNil(m.Remote, m.Ledger, m.Local)
|
||||||
|
if w == nil {
|
||||||
|
return address.Undef, xerrors.Errorf("no wallet backends configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.WalletImport(ctx, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MultiWallet) WalletDelete(ctx context.Context, address address.Address) error {
|
||||||
|
for {
|
||||||
|
w, err := m.find(ctx, address, m.Remote, m.Ledger, m.Local)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if w == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.WalletDelete(ctx, address); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ api.WalletAPI = MultiWallet{}
|
@ -40,3 +40,11 @@ func SetupRemoteWallet(info string) func(mctx helpers.MetricsCtx, lc fx.Lifecycl
|
|||||||
return &RemoteWallet{wapi}, nil
|
return &RemoteWallet{wapi}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *RemoteWallet) Get() api.WalletAPI {
|
||||||
|
if w == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
@ -300,6 +300,14 @@ func (w *LocalWallet) WalletDelete(ctx context.Context, addr address.Address) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *LocalWallet) Get() api.WalletAPI {
|
||||||
|
if w == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
var _ api.WalletAPI = &LocalWallet{}
|
var _ api.WalletAPI = &LocalWallet{}
|
||||||
|
|
||||||
func swapMainnetForTestnetPrefix(addr string) (string, error) {
|
func swapMainnetForTestnetPrefix(addr string) (string, error) {
|
||||||
@ -312,3 +320,16 @@ func swapMainnetForTestnetPrefix(addr string) (string, error) {
|
|||||||
aChars[0] = prefixRunes[0]
|
aChars[0] = prefixRunes[0]
|
||||||
return string(aChars), nil
|
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
|
||||||
|
@ -44,6 +44,7 @@ import (
|
|||||||
"github.com/filecoin-project/lotus/chain/metrics"
|
"github.com/filecoin-project/lotus/chain/metrics"
|
||||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
ledgerwallet "github.com/filecoin-project/lotus/chain/wallet/ledger"
|
||||||
"github.com/filecoin-project/lotus/chain/wallet/remotewallet"
|
"github.com/filecoin-project/lotus/chain/wallet/remotewallet"
|
||||||
sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage"
|
sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage"
|
||||||
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
|
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
|
||||||
@ -251,8 +252,8 @@ func Online() Option {
|
|||||||
Override(new(*stmgr.StateManager), stmgr.NewStateManagerWithUpgradeSchedule),
|
Override(new(*stmgr.StateManager), stmgr.NewStateManagerWithUpgradeSchedule),
|
||||||
Override(new(stmgr.StateManagerAPI), From(new(*stmgr.StateManager))),
|
Override(new(stmgr.StateManagerAPI), From(new(*stmgr.StateManager))),
|
||||||
Override(new(*wallet.LocalWallet), wallet.NewWallet),
|
Override(new(*wallet.LocalWallet), wallet.NewWallet),
|
||||||
Override(new(api.WalletAPI), From(new(*wallet.LocalWallet))),
|
|
||||||
Override(new(wallet.Default), From(new(*wallet.LocalWallet))),
|
Override(new(wallet.Default), From(new(*wallet.LocalWallet))),
|
||||||
|
Override(new(api.WalletAPI), From(new(wallet.MultiWallet))),
|
||||||
Override(new(*messagesigner.MessageSigner), messagesigner.NewMessageSigner),
|
Override(new(*messagesigner.MessageSigner), messagesigner.NewMessageSigner),
|
||||||
|
|
||||||
Override(new(dtypes.ChainGCLocker), blockstore.NewGCLocker),
|
Override(new(dtypes.ChainGCLocker), blockstore.NewGCLocker),
|
||||||
@ -459,8 +460,16 @@ func ConfigFullNode(c interface{}) Option {
|
|||||||
If(cfg.Metrics.HeadNotifs,
|
If(cfg.Metrics.HeadNotifs,
|
||||||
Override(HeadMetricsKey, metrics.SendHeadNotifs(cfg.Metrics.Nickname)),
|
Override(HeadMetricsKey, metrics.SendHeadNotifs(cfg.Metrics.Nickname)),
|
||||||
),
|
),
|
||||||
|
|
||||||
If(cfg.Wallet.RemoteBackend != "",
|
If(cfg.Wallet.RemoteBackend != "",
|
||||||
Override(new(api.WalletAPI), remotewallet.SetupRemoteWallet(cfg.Wallet.RemoteBackend)),
|
Override(new(*remotewallet.RemoteWallet), remotewallet.SetupRemoteWallet(cfg.Wallet.RemoteBackend)),
|
||||||
|
),
|
||||||
|
If(cfg.Wallet.EnableLedger,
|
||||||
|
Override(new(*ledgerwallet.LedgerWallet), ledgerwallet.NewWallet),
|
||||||
|
),
|
||||||
|
If(cfg.Wallet.DisableLocal,
|
||||||
|
Unset(new(*wallet.LocalWallet)),
|
||||||
|
Override(new(wallet.Default), wallet.NilDefault),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,8 @@ type Client struct {
|
|||||||
|
|
||||||
type Wallet struct {
|
type Wallet struct {
|
||||||
RemoteBackend string
|
RemoteBackend string
|
||||||
|
EnableLedger bool
|
||||||
|
DisableLocal bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func defCommon() Common {
|
func defCommon() Common {
|
||||||
|
Loading…
Reference in New Issue
Block a user