-
Notifications
You must be signed in to change notification settings - Fork 36.6k
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
BIP-113: Mempool-only median time-past as endpoint for lock-time calculations #6566
Conversation
needs rebase |
d7d811a
to
8167805
Compare
8167805
to
59ca129
Compare
59ca129
to
6bd1f3c
Compare
@maaku awesome. |
// Only accept nLockTime-using transactions that can be mined in the next | ||
// block; we don't want our mempool filled up with transactions that can't | ||
// be mined yet. | ||
if (!CheckFinalTx(tx)) | ||
if (!IsFinalTx(tx, chainActive.Height() + 1, nLockTimeCutoff)) |
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.
Minor nit: Other than removing the assertion on cs_main, is there any advantage to calling IsFinalTx directly rather than CheckFinalTx? If that difference could be important I would suggest having two versions of CheckFinalTx, one that does not have the assertion and one that has the assertion and calls the other one, since the rest of the logic seems to just be getting duplicated.
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.
IsFinalTx is compatible with libconsensus, CheckFinalTx (by using globals) is not.
Consensus functions cannot access globals like chainActive or GetAdjustedTime() directly.
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.
This isn't consensus code though. @CodeShark is right, we can save some lines by calling CheckFinalTx with an explicit flags parameter. I've made the change.
utACK |
int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST) | ||
? pindexPrev->GetMedianTimePast() | ||
: block.GetBlockTime(); | ||
if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) { | ||
return state.DoS(10, error("%s: contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); | ||
} |
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.
style nit: braces not required for this if
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.
Braces already existed. Principle of smallest diff :)
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 nothing about brace usage for single-statement blocks in the developer-guide, so let's not nit about this. Also personally I like using braces everywhere where possible to avoid famous bugs like
if (something)
do_something_else();
goto handle_error;
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.
OK, agreed.
utACK |
29059c1
to
d0e1799
Compare
Nits addressed and rebased. |
Looks like Travis randomly borked. Needs another force push. |
d0e1799
to
3bd1769
Compare
Travis is happy. |
edit: nvm
concept ACK, though I'd prefer to see a plan for merging the soft-fork before accepting this. |
@dcousens These rules will be eventually enforced through a soft-fork. |
@dcousens As discussed at the RC meeting on the 15th, we aim to roll this out with CLTV using standard ISM. |
Ack. Please apply soon :) |
@@ -162,7 +163,12 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) | |||
mi != mempool.mapTx.end(); ++mi) | |||
{ | |||
const CTransaction& tx = mi->GetTx(); | |||
if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, pblock->nTime)) | |||
|
|||
int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) |
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 don't see why this can't be moved to a const like nMedianTimePast, and skip calculating it for every mempool tx?
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.
Any compiler more recent than the 1960's will evaluate an expression consisting entirely of literals at compile time. In this case I prefer the clarity of specifying what is being tested in-line.
@gmaxwell The max is only in CheckFinalTx (not IsFinal), which is never used for consensus-critical code... |
utACK, although prefer if at least this nit is done before merge. |
…t for lock-time calculations The lock-time code currently uses CBlock::nTime as the cutoff point for time based locked transactions. This has the unfortunate outcome of creating a perverse incentive for miners to lie about the time of a block in order to collect more fees by including transactions that by wall clock determination have not yet matured. By using CBlockIndex::GetMedianTimePast from the prior block instead, the self-interested miner no longer gains from generating blocks with fraudulent timestamps. Users can compensate for this change by simply adding an hour (3600 seconds) to their time-based lock times. If enforced, this would be a soft-fork change. This commit only adds the functionality on an unexecuted code path, without changing the behaviour of Bitcoin Core.
…me constraints Transactions are not allowed in the memory pool or selected for inclusion in a block until their lock times exceed chainActive.Tip()->GetMedianTimePast(). However blocks including transactions which are only mature under the old rules are still accepted; this is *not* the soft-fork required to actually rely on the new constraint in production.
3bd1769
to
dea8d21
Compare
I believe that nits have been addressed and this is ready for merge. @laanwj ? |
int nLockTimeFlags = 0; | ||
int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST) | ||
? pindexPrev->GetMedianTimePast() | ||
: block.GetBlockTime(); |
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.
nLockTimeCutoff assignment could be move outside the loop.
Is it because nLockTimeFlags will eventually be decided by the version of the transaction ?
It seems to me BIP 113 is actually a hardfork being treated as a softfork, at least how it currently stands, and this PR will potentially produce invalid blocks. Suggest reverting it and thinking through revising the BIP to be an actual softfork. |
In my understanding - but this is just a rough check - it seems that Luke is right. A transaction whose locktime is between a block's nTime and its GetMedianTimePast() will be accepted post-BIP113 and not before. That looks like going from invalid to valid, which is a hard fork. |
Could you explain why it is a hard fork?
|
A valid block cannot have a nTime less than GetMedianTimePast + 1.
|
Assume you have a perfect sequence of blocks, where block at height N has timestamp N*600. Block 12 has timestamp 7200. Its mediantimepast() is 3600. It is valid as 7200 is >= 3601. A transaction has nLockTime 5000. It is invalid currently in block 12, as 5000 is below 7200. Post BIP113, it is valid, as it is above 3600. |
@sipa No, we're being silly. The requirement is that the txlocktime be above $thing. Under the old rule $thing was blocktime, under this rule it is MTP. Since blocktime is already required to be greater than MTP, it's fine. 5000 < 7200 so that transaction is valid now. It is > 3600 so it wouldn't be accepted by MTP. |
So the requirement is : blocktime < txlocktime |
Indeed, I was wrong. I assumed that the tx timestamp was supposed to be above the block timestamp - not sure why I made that assumption - and even looking at the code didn't convince me otherwise. I'm now convinced this is not a problem. |
The requirement is that the txlocktime be under $thing. Not above ! |
The requirement is the the blocktime is above the txlocktime, not the other way around. |
Got it. This is what I said (txlocktime be under blocktime == blocktime be above txlocktime), but got confused by gmaxwell remark who refuted your point by saying wrongly "txlocktime be above blocktime". I hate comparisons. :( |
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.
^^
fa8d4d9 scripted-diff: Clarify CheckFinalTxAtTip name (MarcoFalke) fa4e30b policy: Remove unused locktime flags (MarcoFalke) Pull request description: The locktime flags have many issues: * They are passed in by a default argument, which is fragile. It has already lead to bugs like the one fixed in commit e30b6ea. * They are negative (signed), which doesn't make sense for flags (unsigned in general). According to the review comments when the code was added: "The max on the flags is a fairly weird operation." (#6566 (comment)) * No call site relies on the default argument and they all pass in a single compile-time constant, rendering most of the code dead and untested. * The dead code calls `GetAdjustedTime` (network adjusted time), which has its own issues. See #4521 Fix all issues by removing them ACKs for top commit: ajtowns: ACK fa8d4d9 theStack: Code-review ACK fa8d4d9 glozow: ACK fa8d4d9, agree the default arg `flags` is a massive footgun and just setting max flags is weird. Adding `AtTip` to the names makes sense to me, since they're both testing for *next* block and only ever used for {,re}addition to mempool. Tree-SHA512: 79f4a52f34909eb598d88bbae7afe8abe5f85f45c128483d16aa83dacd0e5579e561b725d01b1e9a931d1821012a51ad2bc6fb2867f8d09ee541f9d234d696f8
fa8d4d9 scripted-diff: Clarify CheckFinalTxAtTip name (MarcoFalke) fa4e30b policy: Remove unused locktime flags (MarcoFalke) Pull request description: The locktime flags have many issues: * They are passed in by a default argument, which is fragile. It has already lead to bugs like the one fixed in commit e30b6ea. * They are negative (signed), which doesn't make sense for flags (unsigned in general). According to the review comments when the code was added: "The max on the flags is a fairly weird operation." (bitcoin#6566 (comment)) * No call site relies on the default argument and they all pass in a single compile-time constant, rendering most of the code dead and untested. * The dead code calls `GetAdjustedTime` (network adjusted time), which has its own issues. See bitcoin#4521 Fix all issues by removing them ACKs for top commit: ajtowns: ACK fa8d4d9 theStack: Code-review ACK fa8d4d9 glozow: ACK fa8d4d9, agree the default arg `flags` is a massive footgun and just setting max flags is weird. Adding `AtTip` to the names makes sense to me, since they're both testing for *next* block and only ever used for {,re}addition to mempool. Tree-SHA512: 79f4a52f34909eb598d88bbae7afe8abe5f85f45c128483d16aa83dacd0e5579e561b725d01b1e9a931d1821012a51ad2bc6fb2867f8d09ee541f9d234d696f8
The lock-time code currently uses CBlock::nTime as the cutoff point for time based locked transactions. This has the unfortunate outcome of creating a perverse incentive for miners to lie about the time of a block in order to collect more fees by including transactions that by wall clock determination have not yet matured. By using CBlockIndex::GetMedianTimePast from the prior block instead, the self-interested miner no longer gains from generating blocks with fraudulent timestamps. Users can compensate for this change by simply adding an hour (3600 seconds) to their time-based lock times.
Transactions are not allowed in the memory pool or selected for inclusion in a block until their lock times exceed chainActive.Tip()->GetMedianTimePast(). However blocks including transactions which are only mature under the old rules are still accepted; this is not the soft-fork required to actually rely on the new constraint in production.
Note: This pull request is NO LONGER dependent on #6312 or #6564. If this pull request is merged first, those PR's will have to be updated to include median time-past codes for relative lock-time as well.