Has Bitcoin Ever Hard Forked?
What is a hard fork? It's a change to the protocol that is not compatible with older versions; that is, older client versions would not accept blocks created by newer client versions, considering them invalid. The occurs due to a loosening of the consensus rules on block validity, such that some blocks that would have previously been considered invalid are now considered valid.
Nearly all of the definitions and visualizations you'll find regarding hard and soft forks refer to consensus changes that either require nodes to update their software in order to stay in consensus, or didn't require a software update to stay in consensus.
The question of Bitcoin hard forking is complicated to answer because it's somewhat open to interpretation. From one standpoint, Bitcoin has been hard forked dozens of times to intentionally create alternative protocols / networks / blockchains that are not Bitcoin. But for the purpose of this post I'm focused on the question of hard forks within the Bitcoin protocol itself. Hard fork consensus changes within a protocol may or may not create chain splits, further complicating the analysis.
As a result of recent experiments I've run, I've come to the conclusion that there's a nuanced difference between theoretical incompatibility and practical incompatibility, and the term "hard fork" does not do a good job distinguishing between them.
Consensus Changes
BitMEX has cataloged 21 known consensus changes throughout the 13 year history of Bitcoin. Note that I said "known consensus changes" because it's certainly possible that there have been code changes that affected consensus, but did so in such a way that nobody ever noticed because the right conditions were never hit to create a consensus failure between different nodes.
BitMEX's list has 3 events labelled as "hard fork." I myself believe there to be 7 consensus changes that could be argued to be hard forks but will make a case for why most should not be considered as such. To demonstrate how a consensus change can simultaneously be a hard fork and yet not be a hard fork, consider the following example.
The Hard Fork That Isn't
Let's say that in the next release of every Bitcoin implementation, the block subsidy algorithm is changed to one that has a tail emission of 0.01 BTC in perpetuity after block height 2,520,000 (roughly 30 years from the time of writing) to ensure that miners always get a reward regardless of demand for block space (which results in transaction fees.) For the sake of argument let's even assume that almost every node on the network updates their code to this new rule, whether due to agreement or ignorance.
Say a decade goes by with this rule in place but then the block space market takes off and aggregate transaction fees consistently eclipse the block subsidy, causing Bitcoin proponents to no longer worry about the long term sustainability of paying for the network's thermodynamic security. As such, in the year 2032 it is agreed that Bitcoin should switch back to the original block subsidy schedule with 33 halvings going all the way down to 0 satoshis of block subsidy.
The rule change gets reverted, but the loosened rule itself was never triggered! Despite being "active" for a decade, it was removed over 20 years before it would have ever been enforced. Did Bitcoin hard fork? I would argue that no, while Bitcoin had the potential to hard fork, it never did. The same logic holds true for nearly all of the supposed "hard forks" throughout Bitcoin's history.
I further break down these incompatible "hard fork" consensus changes along two axes:
- Was it a theoretical change or a practical one that actually triggered?
- Was it an explicit, intended rule change or an unintended implicit change?
I posit that for a change to be considered a hard fork it should require that all old client versions will fall out of consensus if they are not updated to understand the new protocol rule. As such, inactive / non-triggered hard fork logic should not count as a hard fork.
Theoretical Incompatible Consensus Changes
There are 5 code changes that I'd categorize as being potential hard forks that never came to fruition.
Bitcoin v0.2 - Satoshi changed the "best chain" selection logic from the chain with the highest block height to the one with the greatest cumulative proof of work. This is technically not a loosening of validity rules, but it did create a potential for a consensus failure / chain split scenario in which both chains would be considered valid by both old and new clients and the consensus failure would be around which chain was the "best chain." Regardless, this conflicting logic was never triggered on the network and remains a potential theoretical chain split that could have happened back in 2010 but is no longer feasible.
Bitcoin v0.3.6 and earlier - OP_VER pushed the version number of the verifier onto the stack. Any use of scripts observing that number would have caused a fork, one per release. As such, every released before v0.3.6 could have triggered a hard fork. However, OP_VER was never actually used.
Bitcoin v0.3.7 - Separation of the evaluation of the scriptSig and scriptPubKey. This fixed a critical bug that enabled anyone to spend anyone's bitcoin. This bug was never actually exploited and the change could have triggered a hard fork if incompatible data had existed in the blockchain.
Bitcoin Core v0.15 - A critical inflation bug was accidentally added. This was fixed over a year later in September 2018. The inflation condition was never actually triggered or exploited.
BIP90 (debatable) - since it only relaxed rules related to soft fork activations that happened in the distant past, it does not share many of the characteristics or risks normally associated with consensus forks. The only way I see this becoming a practical hard fork would be if there was a massive blockchain reorganization that wiped out nearly the entire history of Bitcoin, which I'd argue would effectively destroy the value proposition of the network and thus we wouldn't really care at that point.
I consider the above changes as being potential hard fork changes that could have created incompatibilities in the blockchain that prevented non-upgraded clients from validating and progressing forward with their local blockchain copy. But, because these looser rules were never actually triggered back when nodes were allowing them on the network, the "loopholes" were closed as the nodes on the network upgraded and tightened their rule sets.
Practical Incompatible Implicit Consensus Changes
BIP50 - Bitcoin Core versions prior to 0.8.0 used Berkley DB which had limits on the number of database locks that could occur. Bitcoin Core 0.8.0 switched to LevelDB instead, which did not implement the same locking limits as BDB. This was an unintentional implicit loosening of the validation rules enforced by Bitcoin Core.
After v0.8.0 was released in March 2013, a miner that was running it created a block that caused a chainsplit and temporary hard fork. Within ~5 hours most miners switched back to running v0.7.2 and reorganized the forked chain. Shortly thereafter v0.8.1 was released, with LevelDB and a virtual lock limit that prevented blocks from having more than 4,500 unique transaction IDs as inputs, thus recreating a "lock limit rule" of 10,000. This rule expired on May 15, 2013. Based upon my old node version syncing tests, complex blocks that were incompatible with the old lock limit were produced later:
- Block 258,355 on September 16, 2013 is incompatible with Bitcoin Core v0.5 and earlier. This was 2 years after the v0.5 release.
- Block 364,670 on July 10, 2015 is incompatible with Bitcoin Core v0.6 and v0.7. This was 3 years after the v0.7 release.
This Berkley DB locking issue actually causes a number of problems when trying to sync older Bitcoin Core versions, which I explored in depth in an earlier post:
This was a very unique forking situation. The "rule" was never intended to be a rule in the first place, it was an aberration in one Bitcoin client (albeit the most widely adopted one.) It did create a chain split / hard fork, but that chain split was overwritten within a matter of hours and no longer exists in the blockchain's history. While it did create incompatibilities with old versions of Bitcoin Core, you can fix the incompatibility without changing the code of the old Bitcoin Core versions - it just requires adding 3 lines to a DB_CONFIG file in the node's data directory.
At the time this issue was discovered, there were 2 other full implementations (libbitcoin and BitcoinJ) of which neither had this problem.
I'd classify this compatibility issue as 2 things:
- A historical temporary hard fork
- A forward-compatibility issue in one Bitcoin client
Whether this compatibility issue is a network / protocol fork comes down to if you consider the dominant client (Bitcoin Core) to effectively be the protocol. I do not; and as far as I'm aware none of the other half dozen Bitcoin implementations in existence have ever suffered from database lock issues; none of them use BerkleyDB.
Practical Incompatible Explicit Consensus Changes
Bitcoin v0.3.6 - the addition of the OP_NOP functions.
I believe this to be an incontrovertible, intentional hard fork of the protocol. Interestingly enough, this code change was instituted in order to facilitate future soft forks! It added a bunch of "no operation" opcodes that had no functional purpose rather than waiting to be redefined to have an actual function.
With further research it turns out that this change itself was only a theoretical hard fork at the time of the 0.3.6 release because none of new opcodes were actually used for anything. 2 years later OP_NOP1 was almost redefined to be OP_EVAL via BIP12, but a serious vulnerability was found in the code before it was ever released and thus the soft fork attempt was abandoned.
It wasn't until BIP65 came along and redefined OP_NOP2 as OP_CHECKLOCKTIMEVERIFY which activated on December 8, 2015 and had its first transactions spent using that rule on the same day. I initially thought that this was the point in the blockchain at which pre-v0.3.6 clients would fail to validate new blocks.
However, closer analysis of the blockchain data shows that a few folks (probably developers) were actually spending UTXOs with OP_NOPs in their redeem scripts back when OP_NOPs still had no definition! The first transaction that spends an OP_NOPX in its redeem script is this one in block 163,685. And some additional spelunking shows that this transaction was made by Luke-Jr when he created a proof of concept for BIP-17.
As such, the time between the v0.3.6 release and the first spend of an OP_NOP redeem script, thus triggering a hard fork condition for older clients, was 18 months.
In Conclusion
There is a strong case that the Bitcoin protocol has only implemented 1 practical permanent hard fork. The forking condition appears to have triggered back when Bitcoin was a little over 3 years old, the exchange rate was $5, and there were surely far fewer nodes on the network.
However, the forking question is further muddled by the fact that one might naively assume that if a network has no hard forks, you should be able to sync old client versions successfully from scratch with no modifications. We've seen that this is not the case due to Bitcoin Core unintentionally enforcing implicit rules regarding database operations.
In contrast, many other networks have frequent systematic hard forks that can bring quick functionality upgrades, but they also tend to immediately kick users off the network if they aren't paying attention or disagree with the changes. While Bitcoin has implemented incompatible protocol changes, we can see that they tend to only kick users off the network who have not updated their software in several years. At time of writing, you can fully sync any version of Bitcoin Core released after January 2013 with its default configuration.
As Bitcoin has matured, developers have become more adept at avoiding adding breaking changes to consensus logic. As such, I think it's fair to state that Bitcoin's conservative nature when it comes to consensus changes makes them extremely low impact and thus it's a very node operator friendly network in comparison to other protocols.