mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-20 13:34:32 +01:00
multi: create and list all default internal accounts
This commit is contained in:
parent
ceb31f9034
commit
1541b2ef1b
5 changed files with 108 additions and 57 deletions
|
@ -105,6 +105,21 @@ const (
|
|||
KeyFamilyTowerID KeyFamily = 9
|
||||
)
|
||||
|
||||
// VersionZeroKeyFamilies is a slice of all the known key families for first
|
||||
// version of the key derivation schema defined in this package.
|
||||
var VersionZeroKeyFamilies = []KeyFamily{
|
||||
KeyFamilyMultiSig,
|
||||
KeyFamilyRevocationBase,
|
||||
KeyFamilyHtlcBase,
|
||||
KeyFamilyPaymentBase,
|
||||
KeyFamilyDelayBase,
|
||||
KeyFamilyRevocationRoot,
|
||||
KeyFamilyNodeKey,
|
||||
KeyFamilyStaticBackup,
|
||||
KeyFamilyTowerSession,
|
||||
KeyFamilyTowerID,
|
||||
}
|
||||
|
||||
// KeyLocator is a two-tuple that can be used to derive *any* key that has ever
|
||||
// been used under the key derivation mechanisms described in this file.
|
||||
// Version 0 of our key derivation schema uses the following BIP43-like
|
||||
|
|
|
@ -16,25 +16,11 @@ import (
|
|||
"github.com/btcsuite/btcwallet/wallet"
|
||||
"github.com/btcsuite/btcwallet/walletdb"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
_ "github.com/btcsuite/btcwallet/walletdb/bdb" // Required in order to create the default database.
|
||||
)
|
||||
|
||||
// versionZeroKeyFamilies is a slice of all the known key families for first
|
||||
// version of the key derivation schema defined in this package.
|
||||
var versionZeroKeyFamilies = []KeyFamily{
|
||||
KeyFamilyMultiSig,
|
||||
KeyFamilyRevocationBase,
|
||||
KeyFamilyHtlcBase,
|
||||
KeyFamilyPaymentBase,
|
||||
KeyFamilyDelayBase,
|
||||
KeyFamilyRevocationRoot,
|
||||
KeyFamilyNodeKey,
|
||||
KeyFamilyStaticBackup,
|
||||
KeyFamilyTowerSession,
|
||||
KeyFamilyTowerID,
|
||||
}
|
||||
|
||||
var (
|
||||
testHDSeed = chainhash.Hash{
|
||||
0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
|
||||
|
@ -139,9 +125,7 @@ func TestKeyRingDerivation(t *testing.T) {
|
|||
cleanUp, wallet, err := createTestBtcWallet(
|
||||
CoinTypeBitcoin,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
keyRing := NewBtcWalletKeyRing(wallet, CoinTypeBitcoin)
|
||||
|
||||
|
@ -151,9 +135,7 @@ func TestKeyRingDerivation(t *testing.T) {
|
|||
cleanUp, wallet, err := createTestBtcWallet(
|
||||
CoinTypeLitecoin,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
keyRing := NewBtcWalletKeyRing(wallet, CoinTypeLitecoin)
|
||||
|
||||
|
@ -163,9 +145,7 @@ func TestKeyRingDerivation(t *testing.T) {
|
|||
cleanUp, wallet, err := createTestBtcWallet(
|
||||
CoinTypeTestnet,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
keyRing := NewBtcWalletKeyRing(wallet, CoinTypeTestnet)
|
||||
|
||||
|
@ -189,14 +169,11 @@ func TestKeyRingDerivation(t *testing.T) {
|
|||
success := t.Run(fmt.Sprintf("%v", keyRingName), func(t *testing.T) {
|
||||
// First, we'll ensure that we're able to derive keys
|
||||
// from each of the known key families.
|
||||
for _, keyFam := range versionZeroKeyFamilies {
|
||||
for _, keyFam := range VersionZeroKeyFamilies {
|
||||
// First, we'll ensure that we can derive the
|
||||
// *next* key in the keychain.
|
||||
keyDesc, err := keyRing.DeriveNextKey(keyFam)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to derive next for "+
|
||||
"keyFam=%v: %v", keyFam, err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assertEqualKeyLocator(t,
|
||||
KeyLocator{
|
||||
Family: keyFam,
|
||||
|
@ -212,10 +189,7 @@ func TestKeyRingDerivation(t *testing.T) {
|
|||
Index: 0,
|
||||
}
|
||||
firstKeyDesc, err := keyRing.DeriveKey(keyLoc)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to derive first key for "+
|
||||
"keyFam=%v: %v", keyFam, err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
if !keyDesc.PubKey.IsEqual(firstKeyDesc.PubKey) {
|
||||
t.Fatalf("mismatched keys: expected %x, "+
|
||||
"got %x",
|
||||
|
@ -240,10 +214,7 @@ func TestKeyRingDerivation(t *testing.T) {
|
|||
Index: uint32(i),
|
||||
}
|
||||
keyDesc, err := keyRing.DeriveKey(keyLoc)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to derive first key for "+
|
||||
"keyFam=%v: %v", keyFam, err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
// Ensure that the key locator matches
|
||||
// up as well.
|
||||
|
@ -260,11 +231,7 @@ func TestKeyRingDerivation(t *testing.T) {
|
|||
Index: randKeyIndex,
|
||||
}
|
||||
keyDesc, err = keyRing.DeriveKey(keyLoc)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to derive key_index=%v "+
|
||||
"for keyFam=%v: %v",
|
||||
randKeyIndex, keyFam, err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assertEqualKeyLocator(
|
||||
t, keyLoc, keyDesc.KeyLocator,
|
||||
)
|
||||
|
@ -293,9 +260,7 @@ func TestSecretKeyRingDerivation(t *testing.T) {
|
|||
cleanUp, wallet, err := createTestBtcWallet(
|
||||
CoinTypeBitcoin,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
keyRing := NewBtcWalletKeyRing(wallet, CoinTypeBitcoin)
|
||||
|
||||
|
@ -305,9 +270,7 @@ func TestSecretKeyRingDerivation(t *testing.T) {
|
|||
cleanUp, wallet, err := createTestBtcWallet(
|
||||
CoinTypeLitecoin,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
keyRing := NewBtcWalletKeyRing(wallet, CoinTypeLitecoin)
|
||||
|
||||
|
@ -317,9 +280,7 @@ func TestSecretKeyRingDerivation(t *testing.T) {
|
|||
cleanUp, wallet, err := createTestBtcWallet(
|
||||
CoinTypeTestnet,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
keyRing := NewBtcWalletKeyRing(wallet, CoinTypeTestnet)
|
||||
|
||||
|
@ -342,7 +303,7 @@ func TestSecretKeyRingDerivation(t *testing.T) {
|
|||
// For, each key family, we'll ensure that we're able
|
||||
// to obtain the private key of a randomly select child
|
||||
// index within the key family.
|
||||
for _, keyFam := range versionZeroKeyFamilies {
|
||||
for _, keyFam := range VersionZeroKeyFamilies {
|
||||
randKeyIndex := uint32(rand.Int31())
|
||||
keyLoc := KeyLocator{
|
||||
Family: keyFam,
|
||||
|
|
|
@ -310,6 +310,14 @@ func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispat
|
|||
return subServer, macPermissions, nil
|
||||
}
|
||||
|
||||
// internalScope returns the internal key scope.
|
||||
func (w *WalletKit) internalScope() waddrmgr.KeyScope {
|
||||
return waddrmgr.KeyScope{
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
Coin: w.cfg.ChainParams.HDCoinType,
|
||||
}
|
||||
}
|
||||
|
||||
// ListUnspent returns useful information about each unspent output owned by the
|
||||
// wallet, as reported by the underlying `ListUnspentWitness`; the information
|
||||
// returned is: outpoint, amount in satoshis, address, address type,
|
||||
|
@ -1245,7 +1253,9 @@ func (w *WalletKit) FinalizePsbt(_ context.Context,
|
|||
|
||||
// marshalWalletAccount converts the properties of an account into its RPC
|
||||
// representation.
|
||||
func marshalWalletAccount(account *waddrmgr.AccountProperties) (*Account, error) {
|
||||
func marshalWalletAccount(internalScope waddrmgr.KeyScope,
|
||||
account *waddrmgr.AccountProperties) (*Account, error) {
|
||||
|
||||
var addrType AddressType
|
||||
switch account.KeyScope {
|
||||
case waddrmgr.KeyScopeBIP0049Plus:
|
||||
|
@ -1259,6 +1269,10 @@ func marshalWalletAccount(account *waddrmgr.AccountProperties) (*Account, error)
|
|||
switch *account.AddrSchema {
|
||||
case waddrmgr.KeyScopeBIP0049AddrSchema:
|
||||
addrType = AddressType_NESTED_WITNESS_PUBKEY_HASH
|
||||
|
||||
case waddrmgr.ScopeAddrMap[waddrmgr.KeyScopeBIP0049Plus]:
|
||||
addrType = AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported address schema %v",
|
||||
*account.AddrSchema)
|
||||
|
@ -1267,6 +1281,9 @@ func marshalWalletAccount(account *waddrmgr.AccountProperties) (*Account, error)
|
|||
case waddrmgr.KeyScopeBIP0084:
|
||||
addrType = AddressType_WITNESS_PUBKEY_HASH
|
||||
|
||||
case internalScope:
|
||||
addrType = AddressType_WITNESS_PUBKEY_HASH
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("account %v has unsupported "+
|
||||
"key scope %v", account.AccountName, account.KeyScope)
|
||||
|
@ -1340,7 +1357,9 @@ func (w *WalletKit) ListAccounts(ctx context.Context,
|
|||
continue
|
||||
}
|
||||
|
||||
rpcAccount, err := marshalWalletAccount(account)
|
||||
rpcAccount, err := marshalWalletAccount(
|
||||
w.internalScope(), account,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1431,7 +1450,7 @@ func (w *WalletKit) ImportAccount(ctx context.Context,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
rpcAccount, err := marshalWalletAccount(accountProps)
|
||||
rpcAccount, err := marshalWalletAccount(w.internalScope(), accountProps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -299,7 +299,7 @@ func (b *BtcWallet) Start() error {
|
|||
}
|
||||
}
|
||||
|
||||
_, err := b.wallet.Manager.FetchScopedKeyManager(b.chainKeyScope)
|
||||
scope, err := b.wallet.Manager.FetchScopedKeyManager(b.chainKeyScope)
|
||||
if err != nil {
|
||||
// If the scope hasn't yet been created (it wouldn't been
|
||||
// loaded by default if it was), then we'll manually create the
|
||||
|
@ -307,7 +307,7 @@ func (b *BtcWallet) Start() error {
|
|||
err := walletdb.Update(b.db, func(tx walletdb.ReadWriteTx) error {
|
||||
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||
|
||||
_, err := b.wallet.Manager.NewScopedKeyManager(
|
||||
scope, err = b.wallet.Manager.NewScopedKeyManager(
|
||||
addrmgrNs, b.chainKeyScope, lightningAddrSchema,
|
||||
)
|
||||
return err
|
||||
|
@ -317,6 +317,43 @@ func (b *BtcWallet) Start() error {
|
|||
}
|
||||
}
|
||||
|
||||
// Now that the wallet is unlocked, we'll go ahead and make sure we
|
||||
// create accounts for all the key families we're going to use. This
|
||||
// will make it possible to list all the account/family xpubs in the
|
||||
// wallet list RPC.
|
||||
err = walletdb.Update(b.db, func(tx walletdb.ReadWriteTx) error {
|
||||
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||
|
||||
for _, keyFam := range keychain.VersionZeroKeyFamilies {
|
||||
// If this is the multi-sig key family, then we can
|
||||
// return early as this is the default account that's
|
||||
// created.
|
||||
if keyFam == keychain.KeyFamilyMultiSig {
|
||||
continue
|
||||
}
|
||||
|
||||
// Otherwise, we'll check if the account already exists,
|
||||
// if so, we can once again bail early.
|
||||
_, err := scope.AccountName(addrmgrNs, uint32(keyFam))
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// If we reach this point, then the account hasn't yet
|
||||
// been created, so we'll need to create it before we
|
||||
// can proceed.
|
||||
err = scope.NewRawAccount(addrmgrNs, uint32(keyFam))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Establish an RPC connection in addition to starting the goroutines
|
||||
// in the underlying wallet.
|
||||
if err := b.chain.Start(); err != nil {
|
||||
|
@ -556,6 +593,18 @@ func (b *BtcWallet) ListAccounts(name string,
|
|||
account := account
|
||||
res = append(res, &account.AccountProperties)
|
||||
}
|
||||
|
||||
accounts, err = b.wallet.Accounts(waddrmgr.KeyScope{
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
Coin: b.cfg.CoinType,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, account := range accounts.Accounts {
|
||||
account := account
|
||||
res = append(res, &account.AccountProperties)
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
|
|
|
@ -3019,6 +3019,13 @@ func (r *rpcServer) WalletBalance(ctx context.Context,
|
|||
default:
|
||||
}
|
||||
|
||||
// There now also are the accounts for the internal channel
|
||||
// related keys. We skip those as they'll never have any direct
|
||||
// balance.
|
||||
if account.KeyScope.Purpose == keychain.BIP0043Purpose {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get total balance, from txs that have >= 0 confirmations.
|
||||
totalBal, err := r.server.cc.Wallet.ConfirmedBalance(
|
||||
0, account.AccountName,
|
||||
|
|
Loading…
Add table
Reference in a new issue