Our priority at Coinbase is to be the most trusted and easy to use cryptocurrency company in the world. As an active member of the community, we believe the ability to fork digital currencies is a very important tool for innovation in the ecosystem. However, forks occasionally pose unique security risks for the funds of our customers. The most recent example is the Bitcoin Cash Hard Fork that occurred on November 15, 2018. Unlike typical fork proposals, it was met with contention when an opposing subgroup of the Bitcoin Cash community disagreed with the proposed fork changes. As such, the group in disagreement proposed its own, incompatible set of protocol changes that resulted in a network split. The competing hard forked chain, called Bitcoin SV, made the decision to not implement replay protection. Without replay protection, an exchange’s hot wallet is vulnerable to replay attacks. In order to protect our customers’ funds, we opted to pause all service of Bitcoin Cash until we could engineer a solution to this specific risk. To overcome this unique problem, we implemented our own replay protection by using a strategy called “dust mixing,” thereby ensuring that all customer funds are isolated to a specific chain and not vulnerable to replay attacks. This post will go into detail about hard forks, replay attacks, and dust mixing.
A hard fork is a backwards-incompatible software change that either updates the behavior of the network’s existing rules or creates entirely new rules, such that older versions of the software will no longer be able to function on the hard-forked network. Think of a hard fork as the same as a fork of a repository on github, but in addition to making a copy of the codebase, a blockchain hard fork also makes a copy of the database.
An example of one such change was prompted by the Bitcoin block size limit debate that resulted in a hard fork that created Bitcoin Cash. In August of 2017, a subgroup of the Bitcoin community in favor of a block size increase decided to implement a hard fork of the Bitcoin network, which created Bitcoin Cash. Once the software upgrade was released, a sufficient number of node operators and miners adopted the upgrade, resulting in a permanent chain split. Up to the point of the chain split, there was one shared global ledger of the transactions on the Bitcoin network. After the chain split, the two software implementations began to maintain their own, differing versions of the global ledger. We know these differing global ledgers as Bitcoin and Bitcoin Cash.
There are primarily two types of hard forks, ones with replay protection and ones without replay protection. Hard forks with replay protection ensure that transactions created on one chain are not valid on the other, forked chain. Hard forks without replay protection make coin holders of both sides of the fork vulnerable to what are called “replay attacks.” There are many ways that networks can implement replay protection during a hard fork, including the use of new opcodes, blacklisted addresses, or slight modifications to the transaction signing scheme such that transactions are only valid on one side of the fork. For reference, Bitcoin Cash implemented replay protection when it forked from Bitcoin by including a fork-id specified during the signing of a transaction.
Hard forks without replay protection do not ensure that transactions generated on one chain are invalid on the other. Consequently, transactions generated on one side of the fork are valid on the other side of the fork, and can be replayed on the other chain. When transactions intended to exist only on one chain are replayed on the other chain, it is called a replay attack.
Anatomy of a Replay Attack
Replay attacks can occur when a single transaction is considered to be valid on both chains. To understand fully how this happens, let’s take a step back.
Nodes on a blockchain network learn of new transactions by connecting to a number of other nodes on the same network. All nodes both listen for new transactions from their peers and tell their peers of new transactions they hear about. When a node learns of a new transaction it performs some mathematical computation to ensure that the transaction is valid. If it deems the transaction to be valid, then it propagates the transactions to its peers and places the transaction into a pool of transactions to be considered for the next block, called the mempool.
Transaction propagation gets messy when nodes from both sides of a hard fork are running different versions of software, but still communicating with each other. When BSV forked from BCH, BSV nodes were still able to connect to BCH nodes, and vice versa. As a result, any transaction valid on the BCH chain was shared with BSV nodes, and any transaction valid on the BSV chain was shared with BCH nodes.
Without replay protection of any kind, BSV and BCH nodes run the same process to determine if new transactions are valid and ought to be added to their mempool. As a result, both BSV and BCH nodes are connecting to each other, propagating transactions from both chains, and adding transactions from both chains to their respective mempools to potentially be included in blocks on both chains.
Alice has 1 BCH she wants to send Bob. Alice signs a transaction sending Bob 1 BCH. Her transaction is broadcast to a BCH node and quickly propagated to all BCH nodes. Due to the fact that BCH and BSV nodes are still able to connect to each other, her transaction is also propagated to at least one BSV node, eventually being propagated to the entire BSV network. Eventually, the transaction is included in a valid block in each of the two chains, meaning that Alice sent to Bob not only 1 BCH, but also 1 BSV.
Replay attacks are especially dangerous for exchange operators. Without replay protection, a savvy attacker could use replay attacks to drain funds from an exchange’s hot wallet. The attack works like this:
After the fork, Alice creates an exchange account and buys BCH. She then sends the BCH from the exchange to another address off exchange. If the exchange is not protecting sends against replay, Alice’s “send” not only sends BCH to the external address, but also sends BSV to the same external address, although Alice paid only for the BCH.
In order to protect our hot wallet and our customers’ funds during the BCH fork, we designed and implemented our own version of replay protection. This approach allowed us to isolate BCH transactions sent by Coinbase users to the BCH chain.
The solution we decided upon is called “dust mixing”. Remember, replay attacks occur when transactions are valid on both chains. One way to ensure that a transaction is valid on one chain but invalid on the other is for the transaction’s inputs to include an input that exists only on one chain. At the time of a fork, all of the unspent outputs on both sides of the fork are identical. At that point, these outputs could be replayed on both chains because they existed on both chains. However, as the chains diverge, new outputs are created on each chain from the mining reward, which is known as the “coinbase reward.” As the two chains continue to be extended by their respective miners, different blocks are proposed on the two chains, resulting in coinbase rewards that exist only on one chain. Any output that originates from a post-fork coinbase reward therefore also only exists on a single chain. As a result, transactions that consume such outputs are only valid on a single chain. Dust mixing refers to the practice by exchange operators of including at least one small chain-isolated input to each newly generated post-fork transaction.
At the time of the BCH/BSV fork, we obtained a BCH coinbase reward from a miner. We used the coinbase reward to generate a large set of chain-isolated dust outputs.
For each newly generated post-fork BCH transaction, we make sure to include at least one input that is guaranteed to be isolated to the BCH chain (i.e. a descendant of a BCH coinbase reward). Any left over change outputs of Coinbase generated BCH transactions are added back into the pool of chain isolated outputs in our hot wallet, and can be used as an input to subsequent transactions to produce additional dust outputs required to service BCH sends off our platform.
Our mission is to create an open financial system by being the most trusted and easiest to use digital currency platform in the world. Our security focused approach to hard fork management is a direct result of that mission. Members of the crypto team at Coinbase are constantly working on solving difficult problems like the one described in this blog post. We are actively hiring, so if you are interested, please check out open engineering positions here.
Unless otherwise noted, all images provided herein are by Coinbase.