walletrpc: use bytes to represent master key fingerprint

The integer representation is not common and using bytes allows users to
easily confirm whether their master key fingerprint is correct.
This commit is contained in:
Wilmer Paulino 2021-05-04 16:08:57 -07:00
parent f7b130b5ca
commit e079a9583c
No known key found for this signature in database
GPG Key ID: 6DF57B9F9514972F
5 changed files with 54 additions and 30 deletions

@ -4,7 +4,6 @@ package main
import ( import (
"encoding/base64" "encoding/base64"
"encoding/binary"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
@ -988,13 +987,14 @@ func importAccount(ctx *cli.Context) error {
return err return err
} }
var masterKeyFingerprint uint32 var mkfpBytes []byte
if ctx.IsSet("master_key_fingerprint") { if ctx.IsSet("master_key_fingerprint") {
mkfp, err := hex.DecodeString(ctx.String("master_key_fingerprint")) mkfpBytes, err = hex.DecodeString(
ctx.String("master_key_fingerprint"),
)
if err != nil { if err != nil {
return fmt.Errorf("invalid master key fingerprint: %v", err) return fmt.Errorf("invalid master key fingerprint: %v", err)
} }
masterKeyFingerprint = binary.LittleEndian.Uint32(mkfp)
} }
walletClient, cleanUp := getWalletClient(ctx) walletClient, cleanUp := getWalletClient(ctx)
@ -1004,7 +1004,7 @@ func importAccount(ctx *cli.Context) error {
req := &walletrpc.ImportAccountRequest{ req := &walletrpc.ImportAccountRequest{
Name: ctx.Args().Get(1), Name: ctx.Args().Get(1),
ExtendedPublicKey: ctx.Args().Get(0), ExtendedPublicKey: ctx.Args().Get(0),
MasterKeyFingerprint: masterKeyFingerprint, MasterKeyFingerprint: mkfpBytes,
AddressType: addrType, AddressType: addrType,
DryRun: dryRun, DryRun: dryRun,
} }

@ -723,8 +723,9 @@ type Account struct {
// //
//The fingerprint of the root key from which the account public key was //The fingerprint of the root key from which the account public key was
//derived from. This will always be zero for the default imported account in //derived from. This will always be zero for the default imported account in
//which single public keys are imported into. //which single public keys are imported into. The bytes are in big-endian
MasterKeyFingerprint uint32 `protobuf:"varint,4,opt,name=master_key_fingerprint,json=masterKeyFingerprint,proto3" json:"master_key_fingerprint,omitempty"` //order.
MasterKeyFingerprint []byte `protobuf:"bytes,4,opt,name=master_key_fingerprint,json=masterKeyFingerprint,proto3" json:"master_key_fingerprint,omitempty"`
// //
//The derivation path corresponding to the account public key. This will //The derivation path corresponding to the account public key. This will
//always be empty for the default imported account in which single public keys //always be empty for the default imported account in which single public keys
@ -797,11 +798,11 @@ func (x *Account) GetExtendedPublicKey() string {
return "" return ""
} }
func (x *Account) GetMasterKeyFingerprint() uint32 { func (x *Account) GetMasterKeyFingerprint() []byte {
if x != nil { if x != nil {
return x.MasterKeyFingerprint return x.MasterKeyFingerprint
} }
return 0 return nil
} }
func (x *Account) GetDerivationPath() string { func (x *Account) GetDerivationPath() string {
@ -951,8 +952,9 @@ type ImportAccountRequest struct {
// //
//The fingerprint of the root key (also known as the key with derivation path //The fingerprint of the root key (also known as the key with derivation path
//m/) from which the account public key was derived from. This may be required //m/) from which the account public key was derived from. This may be required
//by some hardware wallets for proper identification and signing. //by some hardware wallets for proper identification and signing. The bytes
MasterKeyFingerprint uint32 `protobuf:"varint,3,opt,name=master_key_fingerprint,json=masterKeyFingerprint,proto3" json:"master_key_fingerprint,omitempty"` //must be in big-endian order.
MasterKeyFingerprint []byte `protobuf:"bytes,3,opt,name=master_key_fingerprint,json=masterKeyFingerprint,proto3" json:"master_key_fingerprint,omitempty"`
// //
//An address type is only required when the extended account public key has a //An address type is only required when the extended account public key has a
//legacy version (xpub, tpub, etc.), such that the wallet cannot detect what //legacy version (xpub, tpub, etc.), such that the wallet cannot detect what
@ -1013,11 +1015,11 @@ func (x *ImportAccountRequest) GetExtendedPublicKey() string {
return "" return ""
} }
func (x *ImportAccountRequest) GetMasterKeyFingerprint() uint32 { func (x *ImportAccountRequest) GetMasterKeyFingerprint() []byte {
if x != nil { if x != nil {
return x.MasterKeyFingerprint return x.MasterKeyFingerprint
} }
return 0 return nil
} }
func (x *ImportAccountRequest) GetAddressType() AddressType { func (x *ImportAccountRequest) GetAddressType() AddressType {
@ -2841,7 +2843,7 @@ var file_walletrpc_walletkit_proto_rawDesc = []byte{
0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x78, 0x74, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x78, 0x74,
0x65, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x34, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x34,
0x0a, 0x16, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x69, 0x6e, 0x0a, 0x16, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x69, 0x6e,
0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14,
0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70,
0x72, 0x69, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x65, 0x72, 0x69, 0x76, 0x61, 0x74, 0x69, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x65, 0x72, 0x69, 0x76, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64,
@ -2872,7 +2874,7 @@ var file_walletrpc_walletkit_proto_rawDesc = []byte{
0x52, 0x11, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x52, 0x11, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
0x4b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65,
0x79, 0x5f, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x79, 0x5f, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x14, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x46, 0x69, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x46, 0x69,
0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x0c, 0x61, 0x64, 0x64, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x0c, 0x61, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x16, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x16, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72,

@ -324,9 +324,10 @@ message Account {
/* /*
The fingerprint of the root key from which the account public key was The fingerprint of the root key from which the account public key was
derived from. This will always be zero for the default imported account in derived from. This will always be zero for the default imported account in
which single public keys are imported into. which single public keys are imported into. The bytes are in big-endian
order.
*/ */
uint32 master_key_fingerprint = 4; bytes master_key_fingerprint = 4;
/* /*
The derivation path corresponding to the account public key. This will The derivation path corresponding to the account public key. This will
@ -377,9 +378,10 @@ message ImportAccountRequest {
/* /*
The fingerprint of the root key (also known as the key with derivation path The fingerprint of the root key (also known as the key with derivation path
m/) from which the account public key was derived from. This may be required m/) from which the account public key was derived from. This may be required
by some hardware wallets for proper identification and signing. by some hardware wallets for proper identification and signing. The bytes
must be in big-endian order.
*/ */
uint32 master_key_fingerprint = 3; bytes master_key_fingerprint = 3;
/* /*
An address type is only required when the extended account public key has a An address type is only required when the extended account public key has a

@ -861,9 +861,9 @@
"description": "The public key backing the account that all keys are derived from\nrepresented as an extended key. This will always be empty for the default\nimported account in which single public keys are imported into." "description": "The public key backing the account that all keys are derived from\nrepresented as an extended key. This will always be empty for the default\nimported account in which single public keys are imported into."
}, },
"master_key_fingerprint": { "master_key_fingerprint": {
"type": "integer", "type": "string",
"format": "int64", "format": "byte",
"description": "The fingerprint of the root key from which the account public key was\nderived from. This will always be zero for the default imported account in\nwhich single public keys are imported into." "description": "The fingerprint of the root key from which the account public key was\nderived from. This will always be zero for the default imported account in\nwhich single public keys are imported into. The bytes are in big-endian\norder."
}, },
"derivation_path": { "derivation_path": {
"type": "string", "type": "string",
@ -1057,9 +1057,9 @@
"description": "A public key that corresponds to a wallet account represented as an extended\nkey. It must conform to a derivation path of the form\nm/purpose'/coin_type'/account'." "description": "A public key that corresponds to a wallet account represented as an extended\nkey. It must conform to a derivation path of the form\nm/purpose'/coin_type'/account'."
}, },
"master_key_fingerprint": { "master_key_fingerprint": {
"type": "integer", "type": "string",
"format": "int64", "format": "byte",
"description": "The fingerprint of the root key (also known as the key with derivation path\nm/) from which the account public key was derived from. This may be required\nby some hardware wallets for proper identification and signing." "description": "The fingerprint of the root key (also known as the key with derivation path\nm/) from which the account public key was derived from. This may be required\nby some hardware wallets for proper identification and signing. The bytes\nmust be in big-endian order."
}, },
"address_type": { "address_type": {
"$ref": "#/definitions/walletrpcAddressType", "$ref": "#/definitions/walletrpcAddressType",

@ -5,6 +5,7 @@ package walletrpc
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -1253,8 +1254,8 @@ func marshalWalletAccount(account *waddrmgr.AccountProperties) (*Account, error)
break break
} }
switch account.AddrSchema { switch *account.AddrSchema {
case &waddrmgr.KeyScopeBIP0049AddrSchema: case waddrmgr.KeyScopeBIP0049AddrSchema:
addrType = AddressType_NESTED_WITNESS_PUBKEY_HASH addrType = AddressType_NESTED_WITNESS_PUBKEY_HASH
default: default:
return nil, fmt.Errorf("unsupported address schema %v", return nil, fmt.Errorf("unsupported address schema %v",
@ -1283,7 +1284,13 @@ func marshalWalletAccount(account *waddrmgr.AccountProperties) (*Account, error)
nonHardenedIndex := account.AccountPubKey.ChildIndex() - nonHardenedIndex := account.AccountPubKey.ChildIndex() -
hdkeychain.HardenedKeyStart hdkeychain.HardenedKeyStart
rpcAccount.ExtendedPublicKey = account.AccountPubKey.String() rpcAccount.ExtendedPublicKey = account.AccountPubKey.String()
rpcAccount.MasterKeyFingerprint = account.MasterKeyFingerprint if account.MasterKeyFingerprint != 0 {
var mkfp [4]byte
binary.BigEndian.PutUint32(
mkfp[:], account.MasterKeyFingerprint,
)
rpcAccount.MasterKeyFingerprint = mkfp[:]
}
rpcAccount.DerivationPath = fmt.Sprintf("%v/%v'", rpcAccount.DerivationPath = fmt.Sprintf("%v/%v'",
account.KeyScope, nonHardenedIndex) account.KeyScope, nonHardenedIndex)
} }
@ -1396,14 +1403,27 @@ func (w *WalletKit) ImportAccount(ctx context.Context,
if err != nil { if err != nil {
return nil, err return nil, err
} }
var mkfp uint32
switch len(req.MasterKeyFingerprint) {
// No master key fingerprint provided, which is fine as it's not
// required.
case 0:
// Expected length.
case 4:
mkfp = binary.BigEndian.Uint32(req.MasterKeyFingerprint)
default:
return nil, errors.New("invalid length for master key " +
"fingerprint, expected 4 bytes in big-endian")
}
addrType, err := parseAddrType(req.AddressType, false) addrType, err := parseAddrType(req.AddressType, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
accountProps, extAddrs, intAddrs, err := w.cfg.Wallet.ImportAccount( accountProps, extAddrs, intAddrs, err := w.cfg.Wallet.ImportAccount(
req.Name, accountPubKey, req.MasterKeyFingerprint, addrType, req.Name, accountPubKey, mkfp, addrType, req.DryRun,
req.DryRun,
) )
if err != nil { if err != nil {
return nil, err return nil, err