-
Notifications
You must be signed in to change notification settings - Fork 37.7k
[WIP] Consensus: Don't allow a coin to be spent and FRESH. #18113
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
Conversation
f95e620
to
5150e82
Compare
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
5150e82
to
9288d74
Compare
Fixed a whitespace oops that our linters didn't like. |
Concept ACK, will review (this was discussed in part in the meeting log of https://bitcoincore.reviews/17487.html) |
src/coins.h
Outdated
//! If a FRESH coin is spent in this cache, then it can be deleted entirely | ||
//! and doesn't ever need to be flushed to the parent. This is a performance | ||
//! optimization. Note that a coin cannot be both FRESH and empty. | ||
//! Marking a coin FRESH which is does exist in the parent view will |
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.
grammar issue- "which is does exist"
maybe- "which actually does exist"
also, I find this sentence a bit confusing, mostly because of the phrasing of "will" vs "might". If a coin exists in the parent view & gets incorrectly marked as FRESH, is there a way it could still be deleted from the parent when the cache is flushed?
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.
grammar issue- "which is does exist"
maybe- "which actually does exist"
Or simply "exists"?
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
src/coins.cpp
Outdated
// re-added if it also appears in the newly connected blocks). It can | ||
// also happen in the case of duplicate txids. In practice, BIP30 and | ||
// BIP34 ensure that the only duplicate transactions before block | ||
// 1,983,702 are the two pairs of coinbase txs at heights 91812/91842 |
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.
Block 1,983,702 -- about 26 years from now? Is that a timestamp rolling over or something?
I thought BIP34 (block height in coinbase) will prevent this from ever happening since it was enforced:
Block number 227,835 (timestamp 2013-03-24 15:49:13 GMT) was the last version 1 block.
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.
Take a look at the comments here:
Line 1989 in a421e0a
// Once BIP34 activated it was not possible to create new duplicate coinbases and thus other than starting |
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.
Oh THAT large comment in connectBlock()
thanks ;-)
src/coins.h
Outdated
//! Failing to mark a coin as DIRTY when it is potentially different from the | ||
//! parent view will cause a consensus failure, since the coin's state won't get | ||
//! written to the parent when the cache is flushed. | ||
//! Note that all empty coins in the cache MUST be DIRTY. |
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.
What is an "empty" coin?
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.
I think this references emptyCoin
(coins.h:132
) which calls an empty constructor of Coin
(coins.h:53
).
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.
I think this references
emptyCoin
(coins.h:132
) which calls an empty contractor ofCoin
(coins.h:53
).
I think this is suppose to say coinEmpty
(coins.cpp:132
).
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.
You're right that this is unclear. Empty here means that the coin has been spent (see Coin.Clear() above which nulls out a coin when it is spent). I've changed this to "Note that a spent coin MUST be DIRTY", which I think is much clearer.
Reading about it in details now. Not sure if I can suggest anything in next few hours because this looks more complex than I thought. Will join the Bitcoin Core PR Review Club Meeting on IRC today to understand more and research later in the week : https://bitcoincore.reviews/18113.html |
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.
Concept ACK
Code looks good but I still need to think about reorgs a little more.
src/coins.cpp
Outdated
@@ -75,8 +75,26 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi | |||
} | |||
if (!possible_overwrite) { |
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.
I think you could clean up and clarify this parameter here as well. First of all, it is sometimes called possible_overwrite
and sometimes potential_overwrite
in the code base, making it harder to grep etc. And I think part of the comment below could be moved here or there could be an additional comment here to explain what this does. I still found it confusing at first after reading comments in coins.h
and validation.cpp
.
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.
Or, I should rather say, the combination of the comments confused me more. From validation.cpp
where AddCoin
is called: "The potential_overwrite parameter to AddCoin is only allowed to be false if we know for sure that the coin did not already exist in the cache." When it is false we end up in this code branch and your comment starts with "If the coin exists in this cache..."
src/coins.h
Outdated
//! Failing to mark a coin as DIRTY when it is potentially different from the | ||
//! parent view will cause a consensus failure, since the coin's state won't get | ||
//! written to the parent when the cache is flushed. | ||
//! Note that all empty coins in the cache MUST be DIRTY. |
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.
I think this references emptyCoin
(coins.h:132
) which calls an empty constructor of Coin
(coins.h:53
).
src/coins.cpp
Outdated
entry.flags |= CCoinsCacheEntry::FRESH; | ||
} | ||
// The parent cache does not have an entry, while the child does. | ||
|
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.
nit: empty line could be removed
Code review ACK 9288d74 Agree the terminology between FRESH and DIRTY is a bit confusing as people brought up in the PR review meeting this morning. |
Code review ACK 9288d74 |
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.
ACK 9288d74 modulo terminology and timing
-
I think "FRESH" and "DIRTY" generally imply mutually exclusive states to people, and non-intuitive meanings/behavior could cause issues in the future. Happy to re-review/re-ACK any proposed further improvements.
-
As this touches consensus, perhaps best to merge after the impending 0.20 branch-off to give the changes some time.
Given that there are four valid states but eight possible combinations (see @amitiuttarwar's summarization), one option is to refactor the code to use an enum with four variants named appropriately. This removes the possibility of getting into an invalid states. However, I'm not sure how practical this refactor would be or how difficult it would be to come up with meaningful names. |
interesting idea but then I think renaming the flags to |
Concept ACK Reiterating my comments from the PR review club, I think this PR might benefit from being split up. It's attempting two separate things, one of which is a consensus change. I think review on that important change might be more focused if this was split into a name and comment update, and then just the logic change. |
9288d74
to
2ba4885
Compare
2ba4885
to
0786c60
Compare
🐙 This pull request conflicts with the target branch and needs rebase. |
When fetching a coin from a parent cache, don't add it to the child cache if it's already spent, since there's no benefit to adding it. This adds some invariant properties to the coins cache: - a spent coin cannot be FRESH - a spent coin must be DIRTY
0786c60
to
c9849da
Compare
I think there is a typo in the last two comments |
Thanks Marco. I fixed those comments. |
This change prevents coins from existing in a coins cache which are both FRESH and spent. FRESH coins should be removed from a cache when they are spent, and this PR also stops us from fetching spent coins from a parent cache and marking them FRESH.