#15481 Restrict timestamp when mining a diff-adjustment block to prev-600 (mining)



  • This PR was proposed in preparation for Matt Corallo’s Great Consensus Cleanup softfork proposal. (Mailing list post), (proposed BIP).
  • The proposal was discussed at the recent Bitcoin Core devtech meetup.
  • This PR is connected with the timewarp fix part of the BIP: By further restricting nTime fields on difficulty adjustment blocks, we propose fixing the long-standing “timewarp” inflation vulnerability in Bitcoin’s difficulty adjustment without risking existing mining hardware becoming unusable. This limits the worst-case difficulty adjustment target in case of attack from the current exponential growth, to once every roughly 600 seconds.
  • The expected time period of 2016 blocks (one retargetting period) isn’t exactly two weeks. See this thread from Pieter Wuille on why exactly that is.
  • The PR doesn’t enforce that consensus change. Rather, it changes the mining behaviour of a Bitcoin Core node to be compliant with the proposed changes.
  • There are two changes:
    • One to getblocktemplate (the RPC that a miner calls in order to get a template block to work on). This PR changes the returned mintime field returned by getblocktemplate, such that if the next block is a difficulty adjustment block, it is constrained by the prev-600 rule.
    • One to UpdateTime(), which is called when creating a new block (either for mining on the node for regtest, or for getblocktemplate).
  • At the time of writing, this PR is ‘Chasing Concept ACK’. That means that we want contributors to review the concept before reviewing the implementation.


  • What could a miner do by abusing the timestamp field in a difficulty adjustment block prior to the Great Consensus Cleanup change?
  • After the Great Consensus Cleanup change, what can a miner do by abusing the block timestamp?
  • Why not just enforce that difficulty adjustment blocks to have a timestamp after the previous block, rather than after prev-600?
  • Which function would need to be changed to enforce this timestamp restriction as a consensus rule?
  • The curtime return value of getblocktemplate is now constrained by the prev-600 rule. Why?

Meeting Log

13:00 < jnewbery> hi
13:00 < kanzure> hi
13:00 < pinheadmz> hi
13:00 < sosthene> hi
13:00 < ccdle12> hi
13:00 < jonatack> hi !
13:00 < rockstardev> hi... @jnewbery I'll follow up with details after PR review
13:01 < amiti> hi
13:01 < ariard> hi
13:01 < jnewbery> Before we start, can you raise your hand o/ if you managed to clone/build the PR and read the notes/questions?
13:02 < nehan> hi
13:02 < pinheadmz> read everything, did not test locally
13:02 < ccdle12> same as above
13:03 < jnewbery> ok, let's give everyone a couple of minutes to catch up. Notes are at https://bitcoin-core-review-club.github.io/15481.html
13:03 < ariard> read notes/BIP and skim over commits
13:03 < jnewbery> I'd encourage everyone to spend at least a few minutes preparing before the meeting by reading the notes. It'll really increase the benefit you get from the meeting if you come with questions prepared.
13:03 < ariard> https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-August/016316.html
13:03 < ariard> Getting around to fixing the timewarp attack
13:04 < jonatack> * yup built, tested, read
13:04 < PaulTroon> I looked over Jon's shoulder :-)
13:04 < jnewbery> ok, so let's hit some of those questions
13:05 < jnewbery> pinheadmz: What could a miner do by abusing the timestamp field in a difficulty adjustment block prior to the Great Consensus Cleanup change?
13:05 < pinheadmz> force a downward difficultly adjustment
13:06 < ariard> and so increase the block production rate?
13:06 < jnewbery> exactly right
13:06 < pinheadmz> making mining easier
13:06 < jnewbery> either push difficulty down or keep it low
13:06 < jnewbery> the idea is that the retarget algorithm only looks at 2015 out of 2016 blocks, so the retargeting periods don't overlap
13:07 < jnewbery> the timewarp exploits this by pushing the difficulty adjustment block way into the future, and then the next block back into the present
13:07 < jnewbery> that makes it appear to the retarget algorithm that it took a long time for the prior retarget period, so difficulty stays low or is adjusted down
13:08 < sosthene> Is it the same vulnerability that was exploited against Verge was attacked last year?
13:08 < pinheadmz> can we clarify which blocks by # ? Like, block 2015 (the ACTUAL last block for retarget algo) is the one thats post-dated 2 hours into the future?
13:09 < jnewbery> sosthene: I think similar. As far as I'm aware verge had some system that used multiple POW algorithms
13:09 < lightlike> jnewbery: "way in the future": is it possible, i read somewhere that you cannot go more than 2 hours into the future with the current rules?
13:09 < pinheadmz> lightlike: correct, 2 hours
13:09 < jnewbery> lightlike: correct, so you couldn't use this attack at the tip, but you could redo a blockchain from genesis that does this because you have 10 years to play with
13:09 < rockstardev> @lightlike: https://bitcoin.stackexchange.com/a/20490/13744
13:10 < jnewbery> (in fact there are stome checkpoints, so you could only do it from block 295,000 if you wanted your blockchain to be accepted by Bitcoin Core)
13:10 < pinheadmz> and that 2 hours is measured against netwoek-adjusted wall-clock time? not MTP
13:10 < nehan> jnewbery: in matt's bip, it's called "timewarp inflation vulnerability". How does inflation come into it?
13:10 < jnewbery> pinheadmz: yes, your local time
13:10 < ariard> you could redo a blockchain but with less work, so you can't override current chain right?
13:11 < kanzure> inflation because subsidy goes out quicker than scheduled
13:11 < jnewbery> nehan: because if you can increase the rate of block production, you can increase the rate of new coin emission
13:11 < nehan> ok, so it refers to schedule and not # of coins
13:11 < jnewbery> nehan: exactly
13:11 < jnewbery> it doesn't affect the 21m cap
13:12 < pinheadmz> since retarget algo is based on median, how much effect can post-dating block #2015 by 2 hours really have?
13:12 < jnewbery> ariard: yes, you'd still need to do more work than the current blockchain if you wanted others to reorg to your chain
13:12 < nehan> i'd appreciate more explanation about the bug, like what pinheadmz asked for (which blocks)
13:12 < pinheadmz> or does the attack have to involve more than just the last block
13:13 < jnewbery> so if I wanted to create a blockchain with many blocks, I'd do the following:
13:13 < jnewbery> create block 1 with a timestamp 1 second after the genesis block
13:13 < jnewbery> create block 2 with timestamp one second after that
13:13 < jnewbery> ...
13:14 < jnewbery> create block 2015 with timestamp genesis+2015
13:14 < jnewbery> create block 2016 with timestamp 2 weeks in the future
13:14 < jnewbery> sorry, timestamp genesis+2 weeks
13:14 < jnewbery> create block 2017 with timestmp genesis+2017
13:14 < jnewbery> ...
13:15 < nehan> jnewbery: is that within the 2 hour time bound?
13:15 < jnewbery> nehan: correction, not 2 weeks in the future from clock time, 2 weeks in the future from genesis
13:15 < nehan> it seems like two weeks + genesis - 2016s + geneses > 2 hours
13:15 < jnewbery> so the retarget algorithm compares the timestamp of block 2016 with block 1 and thinks "hmmm, it took 2 weeks to mine these blocks, keep difficulty at 1"
13:16 < jnewbery> but because 2017 is back to genesis+2017 seconds, the time hasn't advanced two weeks
13:16 < ariard> the hard limit of 2 hours is based on which clock ? Median-Time-Past of X blocks ?
13:16 < jnewbery> nehan: the 2 hours check is against clock time on the node
13:16 < nehan> hasn't it for block 2016 though? why would nodes accept 2016's with such a far off timestamp?
13:16 < jnewbery> the two hour rule is really weird
13:16 < nehan> or is it averaged over blocks?
13:17 < jnewbery> it's the only 'consensus' rule that isn't based on blockchain data but on local data
13:17 < ariard> okay if you're a malicious miner just tune your clock time like you want?
13:17 < jnewbery> it's almost better to not think of it as a consensus rule
13:17 < jnewbery> ariard: sure, but other people wouldn't accept your block
13:18 < jnewbery> because they're all comparing to their (presumably correct) clocks
13:18 < ccdle12> so what is the significance of updating previous block nTime to -600 seconds? (why -600 as an arbitrary number)
13:19 < ariard> the 600 seconds is to avoid messing with miner software using timestamp field for pow
13:19 < jnewbery> so in summary: it allows someone to create arbitrarily long blockchains without the difficulty increasing. The Median Time Passed of the blockchain creeps forward slowly, but one block out of every 2016 is sent forward in time to artificially keep the difficulty down
13:19 < pinheadmz> jnewbery: in your example, why not just create blocks 1-2016 at ten minute intervals? the diff will still stay unchanged?
13:20 < fl> ariard: using the node's local clock, it increases the attack surface from inaccurate time attestations in blocks to requiring the persistent manipulation of timeserver/nodes' local time
13:20 < jnewbery> ariard: exactly right. Some miners apparently use the timestamp field as extra nonce and roll it forward up to 10 minutes
13:20 < rockstardev> that's a good one... interesting to learn that miners do that with timestamp
13:20 < jnewbery> pinheadmz: because at blocks every 10 minutes the timestamp will advance to current time after ~580000 blocks
13:20 < fl> there's a bit of a dearth of nonce space IIRC
13:21 < jnewbery> the attack is to make a longer blockchain, but without increasing the difficulty
13:21  * pinheadmz thumbs up
13:21 < jnewbery> so more coinbase subsidy is realeased
13:21 < lightlike> jnewbery: why would other nodes accept the longer blockchain? I thought the one with the most cumulated work is accepted, not with the largest block count.
13:22 < fl> jnewbery wouldn't a timewarped fork chain require an eclipse attack to prevent nodes from learning about a non attacking chain? or is the desire just to make controlled supply more metered?
13:22 < jnewbery> lightlike: correct, so the full attack would be: I control 51% of the mining power, so I can create a chain with more work than anyone else. I do that, but also use timeward to make that most-work chain really long
13:23 < pinheadmz> does this attack have to start from genesis?
13:23 < fl> pinheadmz no
13:23 < lightlike> jnewbery: ok, thanks, I think I got it now :-)
13:23 < jnewbery> it means that a 51% attack can now increase the rate at which new coins are minted
13:23 < fl> timewarping can start at any block
13:23 < jnewbery> fl: yes, exactly correct
13:24 < fl> jnewbery to which comment?
13:24 < jnewbery> fl: correct that a timewarp can start at any block
13:24 < fl> ty
13:24 < jnewbery> I used genesis as an example because I thought it might simplify the concept, but you could start anywhere
13:24 < hugohn> q: why is the 600sec limit in miner.cpp? don't miners run their own sw?
13:25 < jnewbery> obviously if you start a more recent block, it'll be more work
13:25 < jnewbery> I created a 500,000 long mainnet blockchain by starting at genesis with an S7 a few years ago. At the time I believe it was the longest mainnet Bitcoin blockchain (but not most work, obviously)
13:26 < pinheadmz> jnewbery: thats awesome! ha
13:26 < nehan> jnewbery: i still don't understand why, in your example, other nodes would accept block 2016 (its timestamp is far in the future) but i am happy to take it offline
13:26 < ariard> fl: yes on increasing the attack surface to require the persistent manipulation of timeserver but I wonder if eclipse attacks can destroy this assumption and instead relying on public data wouldn't be better...
13:27 < fl> What would be the effect of sending a timewarped-spoofed chain to a node, would chainstate get erased?
13:27 < jnewbery> block 2016 is in the future from the prior block, but it's in the past when your node is validating
13:27 < wallet42> jnewbery: awesome! wen polo?
13:27 < nehan> jnewbery: aha, because you're starting at genesis, which was a long time ago. got it, thanks!
13:27 < jnewbery> yes, my use of the word future was probably confusing
13:28 < jnewbery> hugohn: I believe most miners use the Bitcoin Core software to get block templates, which they then do work on
13:28 < jnewbery> the `getblocktemplate` RPC will give a block template with the timestamp filled in
13:29 < jnewbery> Here's the code: https://github.com/bitcoin/bitcoin/blob/0221420d1a0550cd849e0f3a5ada3738d5931bdd/src/rpc/mining.cpp#L292
13:29 < jnewbery> You can see that it also returns `curtime` and `mintime`
13:29 < jnewbery> this PR means the value returned for those fields may change. Why?
13:30 < ariard> fl: headers-sync first would happen first and your spoffed chain not queried as new chainstate ?
13:31 < jnewbery> ariard fl: assuming your peer is on the most work tip, and your spoofed chain is below block 295,000, it wouldn't accept the headers because of the checkpoint
13:31 < fl> ah yeah
13:32 < fl> forgot
13:32 < jnewbery> you could create a timewarped chain after block 295,000 but even back at that height, it's quite a lot of work
13:33 < ariard> jnewbery: yes and after 295,000 headers are the counter-measures against resource consumption to track spoofed-chain?
13:33 < jnewbery> anyone want to take a shot at why `curtime` and `mintime` might be changed?
13:33 < fl> i.e. unless timewarped chain had more chainwork after 295e3 nodes wouldn't switch tips, correct?
13:33 < jnewbery> fl: right, we wouldn't reorg unless the new chain contained more work
13:34 < jonatack> to cap the mintime at not less than nTime - 600 ?
13:34 < hugohn> so this is not a consensus change right? as the 600-limit is only in miner.cpp
13:34 < fl> jnewbery but a timewarp spoof chain target node would have some some resource consumption based on header-sync assesment I think
13:35 < jnewbery> ok, so mintime might change because of the code change here: https://github.com/bitcoin/bitcoin/pull/15481/files#diff-9651347c8e00bed3ddc7631de569406dR656
13:35 < pinheadmz> Is there any way timewarp can benefit one single attacker? Seems like it lowers difficulty for EVERYONE, doesn't give one party a specific advantage
13:35 < jnewbery> if the last block of the retarget period was more than 10 minute in the future, then `mintime` would be that timestamp-600
13:36 < fl> pinheadmz it would benefit miners with low block propogation latency
13:36 < jnewbery> hugohn: correct, not a consensus change. This is just miner behaviour change
13:36 < jnewbery> pinheadmz: potentially you could combine it with selfish mining if you're not sharing your chain
13:37 < jnewbery> but really, the dangerous attack is from a miner (or group of miners) that control 51%
13:37 < jnewbery> why might `curtime` change?
13:37 < ariard> fl: starting from checkpoints that's only 2,3 GB of headers with current height of 581487
13:38 < jonatack> to close the vuln without potentially breaking miner machines
13:38 < wallet42> 23 MB
13:38 < jnewbery> fl: I believe we won't download the full block if it's not the most work chain - just the header
13:38 < wallet42> (581487−295000)×80
13:38 < nehan> jnewbery: block->nTime is set on line 41 in UpdateTime(), and it might have been changed at line 37
13:39 < ariard> wallet42: oh sorry right
13:39 < jnewbery> so if you're trying to use this to DOS nodes, you need to do a ton of work just to waste 80 bytes
13:39 < fl> jnewbery I agree just header data would get computed on, thought I would mention that there is some marginal increase in resource consumption
13:39 < jnewbery> if you want to use timewarp to ratchet down the difficulty, you still need to get to the next difficulty retarget block after 295000, and then you can only lower it by 75%
13:40 < jnewbery> fl: yes, but it's asymetric. The attacker needs to do a *lot* of work for a minimal amount of bandwidth/CPU/memory wastage on the civtim
13:40 < jnewbery> *victim
13:40 < fl> issuance rate and preserving DMMS are the more important network properties anti-timewarping attack measures benefit
13:41 < fl> IMO
13:41 < wallet42> hm but once you lowered it enough, you can generate 80 bytes very easily no?
13:41 < jnewbery> nehan: yes exactly right. The update time change here: https://github.com/bitcoin/bitcoin/pull/15481/files#diff-4a59b408ad3778278c3aeffa7da33c3cR36 is used when constructing the block template
13:41 < jnewbery> and `curtime` is taken from that block: https://github.com/bitcoin/bitcoin/blob/0221420d1a0550cd849e0f3a5ada3738d5931bdd/src/rpc/mining.cpp#L667
13:42 < jnewbery> ok, last question: Which function would need to be changed to enforce this timestamp restriction as a consensus rule?
13:42 < fl> jnewbery the resource consumption on a spoof timewarp chain is negligible but in your case where 51% miner stretches out the chain, they could start stuffing more and more data into blocks increasing node operation costs more
13:42 < nehan> jnewbery: but *why* was that change made?
13:43 < jnewbery> nehan: because the aim of this PR is to make miners not mine a difficulty retarget block more than 600 seconds before the prior block. When the miner callers GBT we want the block template returned to them to have a timestamp that is constrained by that rule
13:43 < fl> well not more data into blocks but more data per time
13:44 < jnewbery> and `curtime` is just taken from the block
13:44 < jnewbery> the help text would be technically wrong after this PR: https://github.com/bitcoin/bitcoin/blob/0221420d1a0550cd849e0f3a5ada3738d5931bdd/src/rpc/mining.cpp#L361
13:45 < jnewbery> by the way, GBT is defined in a BIP here: https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki
13:45 < jnewbery> Anyone brave enough to try to answer "Which function would need to be changed to enforce this timestamp restriction as a consensus rule?" :)
13:46 < jnewbery> hint: it's a consensus rule, so it's probably in validation.cpp
13:46 < lightlike> jnewbery: Change CheckBlock() in validation to not accepts new block that break it?
13:47 < jnewbery> lightlike: almost!
13:47 < jnewbery> the change would actually be in ContextualCheckBlockHeader()
13:47 < jnewbery> Header, because this is a field in the block header
13:48 < jnewbery> Contextual, because it's a context-dependant check (ie it depends on where the block is in the blockchain)
13:48 < jnewbery> Matt has a reference implementation for the Consensus Cleanup softfork: https://github.com/bitcoin/bitcoin/pull/15482
13:49 < jnewbery> the relevant change is here: https://github.com/bitcoin/bitcoin/pull/15482/files#diff-24efdb00bfbe56b140fb006b562cc70bR3251
13:49 < hugohn> q: what happens if there is a large hashrate drop between the second-to-last block and the last block in a period (so the gap between those 2 blocks is truly more than 2 minutes)? Won't this change make it so that we won't account for this hash rate drop in retargeting? then that means the next period's difficulty will be a lot longer than it has to be?
13:49 < hugohn> *more than 10 minutes
13:49 < jnewbery> so the code changes this week were very minor, but I hope you agree that there's a lot of interesting discussion behind them
13:50 < hugohn> *harder than it has to be
13:50 < jnewbery> hugohn: the change imposes a new minimum on the block timestamp, not a new maximum
13:51 < jnewbery> if the hashrate is dropping, then we'd expect the timestamps to become more spread out
13:51 < jnewbery> last 10 minutes. Any questions?
13:51 < pinheadmz> <timewarp attack> actually we have 2 hours still :-)
13:52 < fl> I dunno my local clock says...
13:53 < jnewbery> ok, thanks everyone!
13:53 < wallet42> thanks @jnewbery!
13:53 < lightlike> thanks!
13:53 < jonatack> V interesting discussion. No Concept ACK from you on the PR?
13:53 < pinheadmz> thanks
13:53 < fl> jnewbery thanks great pr review
13:53 < ariard> thanks jnewbery, was really interesting!
13:53 < rockstardev> pinheadmz: good one
13:54 < jnewbery> I'm out of office and won't make the next couple of review clubs. I'm going to try to find someone else to host, so keep an eye out on the website for details.
13:56 < jnewbery> jonatack: I believe Matt has some updates to make to his consensus cleanup proposal. I'm going to hold off on concept reviewing until after that
13:57 < jonatack> jnewbery: thanks