walletrpc: expose wallet import related RPCs
This commit is contained in:
6 files changed
+1601
-146
No files matched your search
@@ -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
|
||||
}
|
||||
Reference in new issue
Block a user