account+rpcserver: bump account key index on recovery

Fixes #372.
This commit fixes the issue that if the lnd node the trader client is
connected to was also restored from seed, it is starting at account key
derivation index 0. So when recovering accounts we need to make sure we
re-derive the right number of keys from the wallet in order to allow
properly creating new accounts with the recovered node.
This commit is contained in:
Oliver Gugger 2022-06-14 19:07:23 +02:00
parent 19eed4453d
commit 1ba717c741
No known key found for this signature in database
GPG key ID: 8E4256593F177720
2 changed files with 75 additions and 1 deletions

View file

@ -9,11 +9,13 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/pool/poolscript"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
)
var (
@ -523,3 +525,57 @@ func GenerateRecoveryKeys(ctx context.Context, accountTarget uint32,
}
return acctKeys, nil
}
// AdvanceAccountDerivationIndex asserts that the internal wallet has at least
// the given minimum index for deriving account keys.
func AdvanceAccountDerivationIndex(ctx context.Context, minimumIndex uint32,
wallet lndclient.WalletKitClient, chainParams *chaincfg.Params) error {
allAccounts, err := wallet.ListAccounts(
ctx, "", walletrpc.AddressType_UNKNOWN,
)
if err != nil {
return fmt.Errorf("error listing accounts: %v", err)
}
poolAccountsPath := fmt.Sprintf("m/%d'/%d'/%d'",
keychain.BIP0043Purpose, chainParams.HDCoinType,
poolscript.AccountKeyFamily)
// Before we change anything in the wallet, let's check if maybe we
// already have enough keys?
for _, acct := range allAccounts {
if acct.DerivationPath == poolAccountsPath {
// If we did find our account in the list and already
// have enough keys derived, we don't need to do
// anything here. This is the key _count_, so it must be
// strictly greater than the minimum _index_ that we
// want.
if acct.ExternalKeyCount > minimumIndex {
log.Debugf("Account %s already has %d "+
"external keys (want minimum index "+
"%d), not deriving any keys",
poolAccountsPath, acct.ExternalKeyCount,
minimumIndex)
return nil
}
break
}
}
for {
key, err := wallet.DeriveNextKey(
ctx, int32(poolscript.AccountKeyFamily),
)
if err != nil {
return fmt.Errorf("error deriving next key: %v", err)
}
log.Debugf("Derived next account key with index %d", key.Index)
if key.Index >= minimumIndex {
return nil
}
}
}

View file

@ -1185,16 +1185,34 @@ func (s *rpcServer) RecoverAccounts(ctx context.Context,
// nice since it allows us to try recovery multiple times until it
// actually works.
numRecovered := len(recoveredAccounts)
var maxIndex uint32
for _, acct := range recoveredAccounts {
// We need to know the highest index we ever used so we can make
// sure lnd's wallet is also at that index and the next account
// will be derived from the proper index.
if acct.TraderKey.Index > maxIndex {
maxIndex = acct.TraderKey.Index
}
err = s.accountManager.RecoverAccount(ctx, acct)
if err != nil {
// If something goes wrong for one account we still want
// to continue with the others.
numRecovered--
rpcLog.Errorf("error storing recovered account: %v", err)
rpcLog.Errorf("Error storing recovered account: %v", err)
}
}
// Try to ratchet forward lnd's derivation index for accounts.
err = account.AdvanceAccountDerivationIndex(
ctx, maxIndex, s.lndServices.WalletKit,
s.lndServices.ChainParams,
)
if err != nil {
rpcLog.Errorf("Error advancing lnd's wallet to index %d: %v",
maxIndex, err)
}
return &poolrpc.RecoverAccountsResponse{
NumRecoveredAccounts: uint32(numRecovered),
}, nil