Skip to content

Commit

Permalink
account+rpcserver: bump account key index on recovery
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
guggero committed Jun 14, 2022
1 parent 19eed44 commit 1542dfc
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 1 deletion.
52 changes: 52 additions & 0 deletions account/recovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"encoding/hex"
"fmt"
"github.com/btcsuite/btcd/chaincfg"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"io/ioutil"
"os"

Expand Down Expand Up @@ -523,3 +525,53 @@ 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)

var poolAccountsAccount *walletrpc.Account
for idx, acct := range allAccounts {
if acct.DerivationPath == poolAccountsPath {
poolAccountsAccount = allAccounts[idx]
}
}

// If we did find our account in the list and already have enough keys
// derived, we don't need to do anything here.
if poolAccountsAccount != nil &&
poolAccountsAccount.ExternalKeyCount > minimumIndex {

log.Debugf("Account %s already has %d external keys (want "+
"minimum index %d), not deriving any keys",
poolAccountsPath, poolAccountsAccount.ExternalKeyCount,
minimumIndex)
return nil
}

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
}
}
}
20 changes: 19 additions & 1 deletion rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 1542dfc

Please sign in to comment.