-
Notifications
You must be signed in to change notification settings - Fork 575
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
wallet+waddrmgr: sync and store up to MaxReorgDepth blocks #618
Conversation
cc @halseth |
for reference: #555 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super stoked about this PR! This will be a massive improvement in startup times, and I also find the code easier to reason about than before 😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overall approach looks pretty solid, some minor comments left inline
Pushed a significant rewrite that addresses some comments @cfromknecht and I discussed offline where it was possible for us to miss scanning relevant blocks if the birthday was before the reorg safe height. I also included some nice refactors along the way. PTAL @cfromknecht @halseth! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wpaulino latest diff looking solid, dig the recovery refactor as well 👌
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🌪 tested and indeed sped up the wallet initial sync!!
In this commit, we modify the wallet's block hash index to only store up to MaxReorgDepth blocks. This allows us to reduce consumed storage, as we'd be mostly storing duplicate data. We choose to store up to MaxReorgDepth to ensure we can recover from a potential long reorg.
In this commit, we add a migration that will be used by existing wallets to ensure they can adhere to the new requirement of storing up to MaxReorgDepth entries within the block hash index.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, this more or less looks good to me now 👍 Only a few questions and nits, once those are addressed I think this one is good to go.
wallet/wallet.go
Outdated
|
||
// Clear the batch of all processed blocks to reuse the same | ||
// memory for future batches. | ||
blocks = blocks[:0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are blocks
actually used anywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They're used in the database transaction above.
"%v", err) | ||
} | ||
|
||
c.bestBlock.Hash = *bestHash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no mutex needed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still haz mutex?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's meant to be there since BlockStamp
can be called at any point, which acquires the lock.
if err != nil { | ||
return err | ||
} | ||
blocks = append(blocks, &waddrmgr.BlockStamp{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should blocks before our birthday be added to the slice? If yes, add comment what this achieves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's already a comment at the top of the loop explaining the rationale?
Since the recovery process itself acts as rescan, we'll also update our wallet's synced state along the way to reflect the blocks we process and prevent rescanning them later on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, just some minor comments
"%v", err) | ||
} | ||
|
||
c.bestBlock.Hash = *bestHash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still haz mutex?
This ensures the wallet can properly do an initial sync, a recovery, or detect if it's on a stale branch before attempting to process new blocks at tip. Since the rescan will be triggered synchronously as well, we'll need to catch the wallet's quit chan when handling rescan batches in order to allow for clean shutdowns.
IsCurrent allows us to determine if the chain backend considers itself "current" with the chain.
This serves as groundwork for only storing up to MaxReorgDepth blocks upon initial sync. To do so, we want to make sure the chain backend considers itself current so that we can only fetch the latest MaxReorgDepth blocks from it.
We do this as the wallet will no longer store blocks all the way from genesis to the tip of the chain. Instead, in order to find a reasonable birthday block, we resort to performing a binary search for a block timestamp that's within +/-2 hours of the birthday timestamp.
This commit serves as another building point to allow the wallet to not store blocks all the way from genesis to the tip of chain. We modify the wallet's recovery logic to now start from either its birthday block, or the current reorg safe height if it's before the birthday, to ensure the wallet properly only stores MaxReorgDepth blocks. We also refactor things a bit in hopes of making the logic a bit more readable.
Currently, wallet rescans start from its known tip of the chain. Since we no longer store blocks all the way from genesis to the tip of the chain, performing a rescan would cause us to scan blocks all the way from genesis, which we want to avoid. To prevent this, we set the wallet's tip to be the current reorg safe height. This ensures that we're unable to scan any blocks before it, and that we maintain MaxReorgDepth blocks stored.
We use the recently introduced locateBirthdayBlock function within birthdaySanityCheck as it serves as a more optimized alternative that achieves the same purpose.
If a block arrives while the wallet is rescanning, users would be shown a log message that is confusing and not very helpful.
One could argue that the behavior before this commit was incorrect, as the ChainClient interface expects a call to NotifyBlocks before notifying blocks at tip, so we decide to fix this. Since we now wait for the chain backend to be considered "current" before proceeding to sync the wallet with it, any blocks that were processed while waiting would result in being notified and scanned twice, once by processing it at tip, and another while rescanning the wallet, which is not desirable.
In this PR, we modify the wallet's block hash index to only store up to MaxReorgDepth blocks. This allows us to reduce consumed storage, as we'd be mostly storing duplicate data. We choose to store up to MaxReorgDepth to ensure we can recover from a potential long reorg.
We also add a migration that will be used by existing wallets to ensure they can adhere to the new requirement of storing up to MaxReorgDepth entries within the block hash index.
As a result of only storing up to MaxReorgDepth, we can also speed up the initial sync process for the wallet as we can just fetch the latest MaxReorgDepth blocks of the chain.
Depends on #617 to address some
bitcoind
issues.Partly addresses #513, except we do not batch the requests to the chain backend.
Fixes #616.
NOTE: Since this is changing the initial sync logic, we should make sure to test manually and also through the set of tests within
lnd
to ensure there are no regressions.