Skip to content

Commit

Permalink
keychain+lnwallet: when fetching priv keys or signing try to use cache
Browse files Browse the repository at this point in the history
In this commit, we start to optimistically use the new private key cache
that was added to btcwallet. As is, btcwallet will cache the decrypted
account keys for each scope in memory. However, the existing methods
to derive a child key from those account keys requires a write database
transaction, and will re-derive the private key using BIP-32 each time.

The newly added `DeriveFromKeyPathCache` lets us skip all this and
directly use a cache assuming the account info is already cached. The
new logic will try to use this method, but if it fails fall back to the
existing `DeriveFromKeyPath` method. All calls after this will use this
new cached key.

Fixes #5125.

Basic benchmark before the btcwallet change and after:
```
benchmark                    old ns/op     new ns/op     delta
BenchmarkDerivePrivKey-8     22840583      125           -100.00%

benchmark                    old allocs     new allocs     delta
BenchmarkDerivePrivKey-8     89             2              -97.75%

benchmark                    old bytes     new bytes     delta
BenchmarkDerivePrivKey-8     10225         24            -99.77%
```
  • Loading branch information
Roasbeef committed Aug 26, 2021
1 parent 2206eba commit d6524ea
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 6 deletions.
28 changes: 22 additions & 6 deletions keychain/btcwallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,14 +252,30 @@ func (b *BtcWalletKeyRing) DerivePrivKey(keyDesc KeyDescriptor) (

var key *btcec.PrivateKey

db := b.wallet.Database()
err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
scope, err := b.keyScope()
if err != nil {
return nil, err
}

scope, err := b.keyScope()
if err != nil {
return err
// First, attempt to see if we can read the key directly from
// btcwallet's internal cache, if we can then we can skip all the
// operations below (fast path).
if keyDesc.PubKey == nil {
keyPath := waddrmgr.DerivationPath{
InternalAccount: uint32(keyDesc.Family),
Account: uint32(keyDesc.Family),
Branch: 0,
Index: keyDesc.Index,
}
privKey, err := scope.DeriveFromKeyPathCache(keyPath)
if err == nil {
return privKey, nil
}
}

db := b.wallet.Database()
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)

// If the account doesn't exist, then we may need to create it
// for the first time in order to derive the keys that we
Expand Down
14 changes: 14 additions & 0 deletions lnwallet/btcwallet/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@ func (b *BtcWallet) deriveKeyByLocator(keyLoc keychain.KeyLocator) (*btcec.Priva
return nil, err
}

// First try to read the key from the cached store, if this fails, then
// we'll fall through to the method below that requires a database
// transaction.
path := waddrmgr.DerivationPath{
InternalAccount: uint32(keyLoc.Family),
Account: uint32(keyLoc.Family),
Branch: 0,
Index: keyLoc.Index,
}
privKey, err := scopedMgr.DeriveFromKeyPathCache(path)
if err == nil {
return privKey, nil
}

var key *btcec.PrivateKey
err = walletdb.Update(b.db, func(tx walletdb.ReadWriteTx) error {
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
Expand Down

0 comments on commit d6524ea

Please sign in to comment.