From afc53d1c52f457ba24dc2a82d0f78e87865113b5 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 5 Jan 2022 11:04:32 +0100 Subject: [PATCH] lnwallet: initialize first 255 accounts This fixes lightninglabs/loop#437 by adding all accounts that are used in liquidity products such as Loop or Pool. Since both of these products use key families below 255, we can get by with that number. The alternative to creating way too many accounts (which increases the default wallet size by ~250kB) would be to hard code the exact accounts used by Loop (99) and Pool (210). But that sounds like a bad idea given that there could always be more accounts being added to those (or other) products. By making sure the first 255 accounts exist, we have a lot more flexibility in those products for choosing key families. --- lntest/itest/lnd_remote_signer_test.go | 125 +++++++++++-------------- lnwallet/btcwallet/btcwallet.go | 16 ++-- 2 files changed, 60 insertions(+), 81 deletions(-) diff --git a/lntest/itest/lnd_remote_signer_test.go b/lntest/itest/lnd_remote_signer_test.go index 3ec897547..4a4162c6f 100644 --- a/lntest/itest/lnd_remote_signer_test.go +++ b/lntest/itest/lnd_remote_signer_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcutil/hdkeychain" "github.com/btcsuite/btcwallet/waddrmgr" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnrpc" @@ -39,76 +40,6 @@ var ( Xpub: "tpubDDWAWrSLRSFrG1KdqXMQQyTKYGSKLKaY7gxpvK7RdV3e3Dkhvu" + "W2GgsFvsPN4RGmuoYtUgZ1LHZE8oftz7T4mzc1BxGt5rt8zJcVQi" + "KTPPV", - }, { - Purpose: keychain.BIP0043Purpose, - CoinType: harnessNetParams.HDCoinType, - Account: uint32(keychain.KeyFamilyMultiSig), - Xpub: "tpubDDXFHr67Ro2tHKVWG2gNjjijKUH1Lyv5NKFYdJnuaLGVNBVwyV" + - "5AbykhR43iy8wYozEMbw2QfmAqZhb8gnuL5mm9sZh8YsR6FjGAbe" + - "w1xoT", - }, { - Purpose: keychain.BIP0043Purpose, - CoinType: harnessNetParams.HDCoinType, - Account: uint32(keychain.KeyFamilyRevocationBase), - Xpub: "tpubDDXFHr67Ro2tKkccDqNfDqZpd5wCs2n6XRV2Uh185DzCTbkDaE" + - "d9v7P837zZTYBNVfaRriuxgGVgxbGjDui4CKxyzBzwz4aAZxjn2P" + - "hNcQy", - }, { - Purpose: keychain.BIP0043Purpose, - CoinType: harnessNetParams.HDCoinType, - Account: uint32(keychain.KeyFamilyHtlcBase), - Xpub: "tpubDDXFHr67Ro2tNH4KH41i4oTsWfRjFigoH1Ee7urvHow51opH9x" + - "J7mu1qSPMPVtkVqQZ5tE4NTuFJPrbDqno7TQietyUDmPTwyVviJb" + - "GCwXk", - }, { - Purpose: keychain.BIP0043Purpose, - CoinType: harnessNetParams.HDCoinType, - Account: uint32(keychain.KeyFamilyPaymentBase), - Xpub: "tpubDDXFHr67Ro2tQj5Zvav2ALhkU6dRQAhEtNPnYJVBC8hs2U1A9e" + - "cqxRY3XTiJKBDD7e8tudhmTRs8aGWJAiAXJN5kXy3Hi6cmiwGWjX" + - "K5Cv5", - }, { - Purpose: keychain.BIP0043Purpose, - CoinType: harnessNetParams.HDCoinType, - Account: uint32(keychain.KeyFamilyDelayBase), - Xpub: "tpubDDXFHr67Ro2tSSR2LLBJtotxx2U45cuESLWKA72YT9td3SzVKH" + - "AptzDEx5chsUNZ4WRMY5h6HJxRSebjRatxQKX1uUsux1LvKS1wsf" + - "NJ2PH", - }, { - Purpose: keychain.BIP0043Purpose, - CoinType: harnessNetParams.HDCoinType, - Account: uint32(keychain.KeyFamilyRevocationRoot), - Xpub: "tpubDDXFHr67Ro2tTwzfWvNoMoPpZbxdMEfe1WhbXJxvXikGixPa4g" + - "gSRZeGx6T5yxVHTVT3rjVh35Veqsowj7emX8SZfXKDKDKcLduXCe" + - "WPUU3", - }, { - Purpose: keychain.BIP0043Purpose, - CoinType: harnessNetParams.HDCoinType, - Account: uint32(keychain.KeyFamilyNodeKey), - Xpub: "tpubDDXFHr67Ro2tYEDS2EByRedfsUoEwBtrzVbS1qdPrX6sAkUYGL" + - "rZWvMmQv8KZDZ4zd9r8WzM9bJ2nGp7XuNVC4w2EBtWg7i76gbrmu" + - "EWjQh", - }, { - Purpose: keychain.BIP0043Purpose, - CoinType: harnessNetParams.HDCoinType, - Account: uint32(keychain.KeyFamilyStaticBackup), - Xpub: "tpubDDXFHr67Ro2tYpwnFJEQaM8eAPM2UV5uY6gFgXeSzS5aC5T9Tf" + - "zXuawYKBbQMZJn8qHXLafY4tAutoda1aKP5h6Nbgy3swPbnhWbFj" + - "S5wnX", - }, { - Purpose: keychain.BIP0043Purpose, - CoinType: harnessNetParams.HDCoinType, - Account: uint32(keychain.KeyFamilyTowerSession), - Xpub: "tpubDDXFHr67Ro2tddKpAjUegXqt7EGxRXnHkeLbUkfuFMGbLJYgRp" + - "G4ew5pMmGg2nmcGmHFQ29w3juNhd8N5ZZ8HwJdymC4f5ukQLJ4yg" + - "9rEr3", - }, { - Purpose: keychain.BIP0043Purpose, - CoinType: harnessNetParams.HDCoinType, - Account: uint32(keychain.KeyFamilyTowerID), - Xpub: "tpubDDXFHr67Ro2tgE89V8ZdgMytC2Jq1iT9ttGhdzR1X7haQJNBmX" + - "t8kau6taC6DGASYzbrjmo9z9w6JQFcaLNqbhS2h2PVSzKf79j265" + - "Zi8hF", }} ) @@ -181,7 +112,7 @@ func testRemoteSigner(net *lntest.NetworkHarness, t *harnessTest) { password := []byte("itestpassword") var ( signerNodePubKey = nodePubKey - watchOnlyAccounts = accounts + watchOnlyAccounts = deriveCustomScopeAccounts(t.t) signer *lntest.HarnessNode err error ) @@ -261,3 +192,55 @@ func testRemoteSigner(net *lntest.NetworkHarness, t *harnessTest) { } } } + +// deriveCustomScopeAccounts derives the first 255 default accounts of the custom lnd +// internal key scope. +func deriveCustomScopeAccounts(t *testing.T) []*lnrpc.WatchOnlyAccount { + allAccounts := make([]*lnrpc.WatchOnlyAccount, 0, 255+len(accounts)) + allAccounts = append(allAccounts, accounts...) + + extendedRootKey, err := hdkeychain.NewKeyFromString(rootKey) + require.NoError(t, err) + + path := []uint32{ + keychain.BIP0043Purpose + hdkeychain.HardenedKeyStart, + harnessNetParams.HDCoinType + hdkeychain.HardenedKeyStart, + } + coinTypeKey, err := derivePath(extendedRootKey, path) + require.NoError(t, err) + for idx := uint32(0); idx <= 255; idx++ { + accountPath := []uint32{idx + hdkeychain.HardenedKeyStart} + accountKey, err := derivePath(coinTypeKey, accountPath) + require.NoError(t, err) + + accountXPub, err := accountKey.Neuter() + require.NoError(t, err) + + allAccounts = append(allAccounts, &lnrpc.WatchOnlyAccount{ + Purpose: keychain.BIP0043Purpose, + CoinType: harnessNetParams.HDCoinType, + Account: idx, + Xpub: accountXPub.String(), + }) + } + + return allAccounts +} + +// derivePath derives the given path from an extended key. +func derivePath(key *hdkeychain.ExtendedKey, path []uint32) ( + *hdkeychain.ExtendedKey, error) { + + var ( + currentKey = key + err error + ) + for _, pathPart := range path { + currentKey, err = currentKey.Derive(pathPart) + if err != nil { + return nil, err + } + } + + return currentKey, nil +} diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index bc4922eb6..579ca130d 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -323,17 +323,13 @@ func (b *BtcWallet) Start() error { 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 - } - + // Generate all accounts that we could ever need. This includes + // all lnd key families as well as some key families used in + // external liquidity tools. + for keyFam := uint32(1); keyFam <= 255; keyFam++ { // Otherwise, we'll check if the account already exists, // if so, we can once again bail early. - _, err := scope.AccountName(addrmgrNs, uint32(keyFam)) + _, err := scope.AccountName(addrmgrNs, keyFam) if err == nil { continue } @@ -341,7 +337,7 @@ func (b *BtcWallet) Start() error { // 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)) + err = scope.NewRawAccount(addrmgrNs, keyFam) if err != nil { return err }