Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

keychain+lnwallet: when fetching priv keys or signing try to use cache #5629

Merged
merged 4 commits into from
Aug 27, 2021

Conversation

Roasbeef
Copy link
Member

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%

@Roasbeef Roasbeef added wallet The wallet (lnwallet) which LND uses optimization labels Aug 13, 2021
@Roasbeef Roasbeef added this to the v0.14.0 milestone Aug 13, 2021
@Roasbeef Roasbeef changed the title Priv key caching keychain+lnwallet: when fetching priv keys or signing try to use cache Aug 13, 2021
Copy link
Collaborator

@guggero guggero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Niiiice 😎 This should give us quite the performance boost.

LGTM after dependent PR is merged.

// use to cache private keys to avoid DB and EC operations within the
// wallet. With the default sisize, we'll allocate up to 320 KB to
// caching private keys (ignoring pointer overhead, etc).
privKeyCacheSize = 10_000
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: unused.

cleanUp, wallet, err := createTestBtcWallet(
CoinTypeBitcoin,
)
if err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use require?

for i := 0; i < t.N; i++ {
privKey, err = keyRing.DerivePrivKey(keyDesc)
}
require.NoError(t, err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move those inside the loop?

Copy link
Collaborator

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice optimization, LTGM!

"github.com/stretchr/testify/require"
)

func BenchmarkDerivePrivKey(t *testing.B) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: godoc


// privKeyCacheSize is the default size of the the LRU cache that we'll
// use to cache private keys to avoid DB and EC operations within the
// wallet. With the default sisize, we'll allocate up to 320 KB to
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/s/size

Branch: 0,
Index: keyDesc.Index,
}
privKey, err := scope.DeriveFromKeyPathCache(keyPath)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If err can be anything other that ErrAccountNotCached, do we want to log/handle it? Likewise below.

@Roasbeef Roasbeef force-pushed the priv-key-caching branch 2 times, most recently from eb7b515 to 6d1da54 Compare August 26, 2021 01:49
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 lightningnetwork#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%
```
@Roasbeef Roasbeef merged commit d9644eb into lightningnetwork:master Aug 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
optimization wallet The wallet (lnwallet) which LND uses
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Potential private key derivation optimization
3 participants