Bitcoin relies on the concept of time for operations such as the difficulty adjustment and transaction- or script-level timelocks. Since time is relative and clocks are notoriously difficult to keep synchronized, it is impossible to define a source of truth that does not depend on synchronization and authorities such as NTP servers. In a decentralized system, we must accept that nodes can have a different but equally valid view of the current time.
The Bitcoin network comes to consensus on time by requiring miners to commit to a timestamp in the block header. To prevent miners from being able to use excessively deviating timestamps in their blocks, nodes verify that the timestamp is later than the Median Time Past (calculated on the previous 11 blocks), and earlier than 2 hours from the current time.
Prior to this PR, the current time was calculated based on the network-adjusted time. When connecting to a peer, the peer’s current time is compared to the system’s current time, and the time difference is stored. The network-adjusted time is then calculated by adding the median of all peers’ offsets to the system’s current time, unless it exceeds the -maxtimeadjustment value in which case a warning is emitted.
With this PR, the upper bound of the validity of a block header’s timestamp is no longer calculated based on the network-adjusted time, but on the unadjusted system’s current time. The network-adjusted time is still calculated and used to warn users of a potential clock misconfiguration.
Note: the description in timedata.cppmentions that “the condition to update nTimeOffset includes checking whether the number of elements in vTimeOffsets is odd, which will never happen after there are 200 elements.”. This comment has become outdated since #6545, which made the maximum number of elements explicit in an earlier check.
Is it necessary for block headers to have a timestamp? If so, why?
What is the difference between Median Time Past (MTP) and network-adjusted time? Which of these are relevant to the PR?
Why are limits enforced on how far “off” a block header’s timestamp is allowed to be from a node’s internal clock? And since we don’t require exact agreement on time, can these limits be made more strict?
Prior to this PR, why would an attacker try to manipulate a node’s network-adjusted time?
Prior to this PR, how could an attacker try to manipulate a node’s network-adjusted time? Which network message(s) would they use? Hint: network messages are processed in net_processing.cpp
Does this PR remove any attack vectors? Does it introduce new ones?
Does this PR change consensus behaviour? If so, is this a soft fork, a hard fork, or neither? Why?
After this PR, does it still matter for a non-mining node to have its system time (roughly) agree with that of the network? Why (not)?
Code
Which operations were relying on network-adjusted time prior to this PR?
Does this PR introduce any difference in how and when it warns for a clock that appears out-of-sync with the network?
<stickies-v> for those of you who were able to review, would you give it a Concept ACK, Approach ACK, Tested ACK, or NACK? what was your review approach?
<stickies-v> monlovesmango: well yes this PR affects how a block header's timestamps affects its validity, but if it didn't have the timestamp to begin with we wouldn't have to validate it :D
<pablomartin> seq order: blocks should be in chronologial order... preventing timestamp manipulation: helps prevent miners from manipulating the block creation process, miners must set the timestamp within certain limits
<sr_gi> Our only sense of how long blocks take to get mined is based on the block timestamp, so in order to re-adjust we need to know how long it took (sorry if it was pointed out, I'm missing the chat log :'))
<monlovesmango> mtp is calculated with past block timestamps whereas network-adjusted time is calculated based on first 199 outgoing peers' median time
<alfonsoromanz> MTP is the median time of last 11 blocks and network adjusted time is a time that is calculated by adding the median of the offsets between current node time and a sample of 200 peers, to try to get nodes closer to each other in terms of clock synchronization. For this PR only network adjusted time is relevant.
<stickies-v> MTP is uniquely defined for all network participants that are on the same chain (i.e. there's consensus on time), whereas they can (and generally do) have a different network-adjusted time
<sr_gi> MTP is used as the lower bound for the timestamps of the subsequent block, whereas the network-adjusted time is our view of time corrected by the view of our peers (at least before this PR)
<stickies-v> 4. Why are limits enforced on how far "off" a block header's timestamp is allowed to be from a node's internal clock? And since we don't require exact agreement on time, can these limits be made more strict?
<sr_gi> The restrictions are in place to prevent tampering with the difficulty adjustments. If timestamps are to far off the future, when computing the next difficulty it would look like blocks took longer to be mined, wrongly correcting the difficulty down, the same applies, in the opposite way, if timestamps are too far into the past. These limits could
<sr_gi> be made more strict, given we are giving some room for discrepancies (maxtimeadjustment) but it we made them too strict valid blocks could be rejected, creating a consensus issue.
<stickies-v> we don't need the time to be exactly correct, it's mostly important that it keeps moving in the right direction and that it keeps moving at the same pace as the wall clock on average
<monlovesmango> the only thing I could think of was to convince a node that a valid block is invalid and then broadcast a fake block to the node (that would be valid to the node)
<stickies-v> monlovesmango: yes! broadcasting a fake block would require a lot of hashpower of course, but forking nodes off the valid chain is possible like that
<stickies-v> 6. Prior to this PR, how could an attacker try to manipulate a node's network-adjusted time? Which network message(s) would they use? *Hint: network messages are processed in `net_processing.cpp`*
<lightlike> if I rejected a block because it's too much in the future (>2h), and then some time passes and it's now valid, will my node accept it a bit later? Or will it be remembered as being invalid indefinitely?
<michaelfolkson> I find it very difficult to assess the Concept ACK on this. As I think you say in a comment stickies-v it is making some esoteric attacks harder to pull off whilst potentially making some other esoteric attacks easier to pull off
<michaelfolkson> So you have to essentially assess whether you're more worried about an attacker controlling your local time or an attacker controlling all your peers
<monlovesmango> michaelfolkson: i agree. in my gut it feels much easier to attack an individual with malware (to change system time) and little recourse for correction than to orchestrate being one of the first outgoing peers
<lightlike> stickies-v: then how do I recover if my time is really wrong (>2h) for some reason, and I rejected a valid block, and now I fixed my time and wanna get back to the right chain?
<michaelfolkson> And then there's also the weighing up of dangers of forking yourself off the network versus contagion with forking peers off the network? (for admittedly esoteric attacks)
<monlovesmango> dergoegge: correct, adjusted time doesn't protect against NTP based attacks. just think this attack is much harder to pull off than a targeted malware attack. but really don't know.
<stickies-v> lightlike: that is a good point, and i see now that we have a `BlockValidationResult::BLOCK_TIME_FUTURE` so probably those blocks can be re-evaluated?
<michaelfolkson> It is hard. I don't feel I understand the trade-offs well enough to assess the Concept ACK. I get the fact it is cleaner from a consensus code perspective post this PR but also not sure how much of a "big win" that really is
<monlovesmango> if we want non technical folks to run a node, this probably would be a hindrance, but only in cases where system time is off which is very rare)
<lightlike> re: question 6: An attacker would need to send us multiple version messages with manipulated timestamps. They would need us to make >50% of outbound connections to nodes controlled to them, which is hopefullly hard (but much easier than completely eclipsing the node).
<sr_gi> I think one relevant detail is that we only take one sample per peer, given that happens during the handshake (i.e. exchange of version messages), hence why a single peer cannot affect your computation
<michaelfolkson> It is one of those where technically it is a hard fork right? But in reality we're dealing with highly unlikely esoteric attacks and code cleanup
<sr_gi> I think it does not change consensus behavior but it can lead to consensus discrepancies, e.g a chain split due to what blocks are valid wrt time
<sr_gi> monlovesmango you're right, but it only applies back to the amount of distinct peers because we sample on handshake. If you happen to do so on any other message that can be sent without disconnecting a single peer may affect multiple samples
<michaelfolkson> dergoegge: Theoretically it is possible that you reject a block after this PR that you would have accepted before this PR? Emphasize "theoretically"
<sr_gi> michaelfolkson I think that's a bit unfair. The root of why would you reject a block (both after and before this PR) is because your view of time deviates from "the network's", that can be achieved either by peers messing with your corrected time, or by your NTP server messing with you
<monlovesmango> "peers messing with your corrected time, or by your NTP server messing with you" agree, but imo NTP server messing with you seems easier to orchestrate
<sr_gi> I think the matter really boils down to how likely is is that after the change non-adjusted and adjusted clients may conflict, and that's a fair concern
<lightlike> I think it's important to note that adjusted time can only change time by a max of 70 minutes, which is smaller than the 120 minutes block rule. So I think that any meaningful attack would require the attacker to be a miner, that would date its own block almost 2 hours into the future, and then adjust the victim's time into the past.
<michaelfolkson> The chain split risk is really the important question versus whether this is theoretically a hard fork or not. If the chain split risk reduces or is unimpacted then we're happy. And we're in the realm of edge cases anyway
<sr_gi> I will I think, if you're far off, you'll adjust the time up to a certain extend, but not beyond `maxtimeadjustment`, but blocks times will still be off for you, so you won't accept them. That's my intuition at least, but take it with a grain of salt
<stickies-v> does anyone see any problems with switching those callsites to system time? (besides the attack vectors discussed in yesterday's meeting)?
<grndslm> just a thought.... i thought the wonder of bitcoin was that there was no oracle, but seems like usine time servers is introducing the first oracle..
<lightlike> I think the warnings after this PR are fetch-only (you have to call a RPC to see it). Before the PR it was push (entry in the log, + popup in the gui).
<lightlike> only VERSION messages from outbound peers though, and we have another unconditional logging msg for these already. So disk-filling attacks via log messages are-not an issue here.
<stickies-v> Ayelen: the issue you linked is more about making it explicit that we never go above 200 (which, from what i understand from my perusal of historical PRs, was never intended, but eventually kept to address other, undisclosed, vulnerabilities)
<lightlike> also, for many years timedata was also from taken from inbound peers, not just outbounds. So it was pretty easily to manipulate for an attacker, that could make multiple connections to you. Stopping at 199 samples was a slightly crude way to prevent that, because once the node was running for a few hours/days it was "safe" from this attack.