-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
blockchain: fix inconsistent utxocache and database on reorg #2123
blockchain: fix inconsistent utxocache and database on reorg #2123
Conversation
Pull Request Test Coverage Report for Build 8184050728Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
de1be7d
to
65ecf4a
Compare
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.
Thanks a lot for the fix! LGTM 🎉
existance/non-existance New test instance BlockDisconnectExpectUTXO tests that a utxo exists/doesn't exist after a specific block has been disconnected.
65ecf4a
to
f76a887
Compare
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.
Nice work tracking down this fix! Have some questions around some lingering logic that looks redundant after this change, and a panic I get in the unit tests w/ the first commit (new tests), but w/o the second (the fix).
The assumption in the previous code was incorrect in that we were assuming that the chainLock is held throughout the entire chain reorg. This is not the case since the chainLock is let go of during the callback to the subscribers. Because of this, we need to ensure that the utxo set is consistent on each block disconnect. To achieve this, additional flushes are added during block disconnects. Also the utxocache is no longer avoided during block connects and when we're checking for the validity of the block connects and disconnects as we can just use the cache instead of trying to avoid it.
f76a887
to
a254998
Compare
Allowing the caller to fetch from either the database or the cache resulted in inconsistencies if the cache were ever to be dirty. Removing this option eliminates this problem.
Since no code is now depending on accepting new blocks without the cache, we get rid of the option to do so.
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 🍖
During reorgs, the assumption being made was that the utxocache is ignored and we're directly accessing the database for utxo set modifications.
However, after a block disconnect, the
BlockChain.chainLock
, the lock that prevents modifications to the utxo set, was being freed for the callback functions for the blockchain notifications.btcd/blockchain/chain.go
Lines 853 to 858 in 13152b3
If a caller calls either of the two methods below, the utxocache could be loaded with utxos that are fetched from the database:
btcd/blockchain/utxoviewpoint.go
Lines 687 to 693 in 13152b3
btcd/blockchain/utxoviewpoint.go
Lines 722 to 732 in 13152b3
Since the UTXOs fetched here are considered unmodified and therefore wouldn't be saved in the database when the
utxoCache
is flushed at the end of a reorganization, if a caller makes a call to the above two functions during a reorg, they could be mistakenly think that a utxo exists when it was removed from the utxo set.Example:
There's a chain like so where a utxo created in
b1001
is spent inb1002
. Blocksb1002
andb1001
are being disconnected out by a longer chain.Mempool tries to fetch
b1001.tx[1].out[0]
three times in total.2
,8
and11
.At
6
the utxob1001.tx[1].out[0]
get loaded up to the cache. The cache never gets reset and that utxo gets fetched at12
. When the reorg finishes, the mempool can no longer fetchb1001.tx[1].out[0]