walletrpc: expose wallet import related RPCs

This commit is contained in:
Wilmer Paulino committed 2021-04-05 15:41:10 -07:00
1 parent 2d163b788b
commit 5301c7e83f
6 files changed
+1601 -146

No files matched your search

+210
View File
@@ -12,11 +12,14 @@ import (
"path/filepath"
"time"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/btcsuite/btcutil/psbt"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/wtxmgr"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/lightningnetwork/lnd/input"
@@ -128,6 +131,18 @@ var (
Entity: "onchain",
Action: "write",
}},
"/walletrpc.WalletKit/ListAccounts": {{
Entity: "onchain",
Action: "read",
}},
"/walletrpc.WalletKit/ImportAccount": {{
Entity: "onchain",
Action: "write",
}},
"/walletrpc.WalletKit/ImportPublicKey": {{
Entity: "onchain",
Action: "write",
}},
}
// DefaultWalletKitMacFilename is the default name of the wallet kit
@@ -1215,3 +1230,198 @@ func (w *WalletKit) FinalizePsbt(_ context.Context,
RawFinalTx: finalTxBytes.Bytes(),
}, nil
}
// marshalWalletAccount converts the properties of an account into its RPC
// representation.
func marshalWalletAccount(account *waddrmgr.AccountProperties) (*Account, error) {
var addrType AddressType
switch account.KeyScope {
case waddrmgr.KeyScopeBIP0049Plus:
// No address schema present represents the traditional BIP-0049
// address derivation scheme.
if account.AddrSchema == nil {
addrType = AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH
break
}
switch account.AddrSchema {
case &waddrmgr.KeyScopeBIP0049AddrSchema:
addrType = AddressType_NESTED_WITNESS_PUBKEY_HASH
default:
return nil, fmt.Errorf("unsupported address schema %v",
*account.AddrSchema)
}
case waddrmgr.KeyScopeBIP0084:
addrType = AddressType_WITNESS_PUBKEY_HASH
default:
return nil, fmt.Errorf("account %v has unsupported "+
"key scope %v", account.AccountName, account.KeyScope)
}
rpcAccount := &Account{
Name: account.AccountName,
AddressType: addrType,
ExternalKeyCount: account.ExternalKeyCount,
InternalKeyCount: account.InternalKeyCount,
WatchOnly: account.IsWatchOnly,
}
// The remaining fields can only be done on accounts other than the
// default imported one existing within each key scope.
if account.AccountName != waddrmgr.ImportedAddrAccountName {
nonHardenedIndex := account.AccountPubKey.ChildIndex() -
hdkeychain.HardenedKeyStart
rpcAccount.ExtendedPublicKey = account.AccountPubKey.String()
rpcAccount.MasterKeyFingerprint = account.MasterKeyFingerprint
rpcAccount.DerivationPath = fmt.Sprintf("%v/%v'",
account.KeyScope, nonHardenedIndex)
}
return rpcAccount, nil
}
// ListAccounts retrieves all accounts belonging to the wallet by default. A
// name and key scope filter can be provided to filter through all of the wallet
// accounts and return only those matching.
func (w *WalletKit) ListAccounts(ctx context.Context,
req *ListAccountsRequest) (*ListAccountsResponse, error) {
// Map the supported address types into their corresponding key scope.
var keyScopeFilter *waddrmgr.KeyScope
switch req.AddressType {
case AddressType_UNKNOWN:
break
case AddressType_WITNESS_PUBKEY_HASH:
keyScope := waddrmgr.KeyScopeBIP0084
keyScopeFilter = &keyScope
case AddressType_NESTED_WITNESS_PUBKEY_HASH,
AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH:
keyScope := waddrmgr.KeyScopeBIP0049Plus
keyScopeFilter = &keyScope
default:
return nil, fmt.Errorf("unhandled address type %v", req.AddressType)
}
accounts, err := w.cfg.Wallet.ListAccounts(req.Name, keyScopeFilter)
if err != nil {
return nil, err
}
rpcAccounts := make([]*Account, 0, len(accounts))
for _, account := range accounts {
// Don't include the default imported accounts created by the
// wallet in the response if they don't have any keys imported.
if account.AccountName == waddrmgr.ImportedAddrAccountName &&
account.ImportedKeyCount == 0 {
continue
}
rpcAccount, err := marshalWalletAccount(account)
if err != nil {
return nil, err
}
rpcAccounts = append(rpcAccounts, rpcAccount)
}
return &ListAccountsResponse{Accounts: rpcAccounts}, nil
}
// parseAddrType parses an address type from its RPC representation to a
// *waddrmgr.AddressType.
func parseAddrType(addrType AddressType,
required bool) (*waddrmgr.AddressType, error) {
switch addrType {
case AddressType_UNKNOWN:
if required {
return nil, errors.New("an address type must be specified")
}
return nil, nil
case AddressType_WITNESS_PUBKEY_HASH:
addrTyp := waddrmgr.WitnessPubKey
return &addrTyp, nil
case AddressType_NESTED_WITNESS_PUBKEY_HASH:
addrTyp := waddrmgr.NestedWitnessPubKey
return &addrTyp, nil
case AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH:
addrTyp := waddrmgr.WitnessPubKey
return &addrTyp, nil
default:
return nil, fmt.Errorf("unhandled address type %v", addrType)
}
}
// ImportAccount imports an account backed by an account extended public key.
// The master key fingerprint denotes the fingerprint of the root key
// corresponding to the account public key (also known as the key with
// derivation path m/). This may be required by some hardware wallets for proper
// identification and signing.
//
// The address type can usually be inferred from the key's version, but may be
// required for certain keys to map them into the proper scope.
//
// For BIP-0044 keys, an address type must be specified as we intend to not
// support importing BIP-0044 keys into the wallet using the legacy
// pay-to-pubkey-hash (P2PKH) scheme. A nested witness address type will force
// the standard BIP-0049 derivation scheme, while a witness address type will
// force the standard BIP-0084 derivation scheme.
//
// For BIP-0049 keys, an address type must also be specified to make a
// distinction between the standard BIP-0049 address schema (nested witness
// pubkeys everywhere) and our own BIP-0049Plus address schema (nested pubkeys
// externally, witness pubkeys internally).
func (w *WalletKit) ImportAccount(ctx context.Context,
req *ImportAccountRequest) (*ImportAccountResponse, error) {
accountPubKey, err := hdkeychain.NewKeyFromString(req.ExtendedPublicKey)
if err != nil {
return nil, err
}
addrType, err := parseAddrType(req.AddressType, false)
if err != nil {
return nil, err
}
err = w.cfg.Wallet.ImportAccount(
req.Name, accountPubKey, req.MasterKeyFingerprint, addrType,
)
if err != nil {
return nil, err
}
return &ImportAccountResponse{}, nil
}
// ImportPublicKey imports a single derived public key into the wallet. The
// address type can usually be inferred from the key's version, but in the case
// of legacy versions (xpub, tpub), an address type must be specified as we
// intend to not support importing BIP-44 keys into the wallet using the legacy
// pay-to-pubkey-hash (P2PKH) scheme.
func (w *WalletKit) ImportPublicKey(ctx context.Context,
req *ImportPublicKeyRequest) (*ImportPublicKeyResponse, error) {
pubKey, err := btcec.ParsePubKey(req.PublicKey, btcec.S256())
if err != nil {
return nil, err
}
addrType, err := parseAddrType(req.AddressType, true)
if err != nil {
return nil, err
}
if err := w.cfg.Wallet.ImportPublicKey(pubKey, *addrType); err != nil {
return nil, err
}
return &ImportPublicKeyResponse{}, nil
}