Yesterday, BitMEX Research tweeted that their ForkMonitor had picked up a double spend in the Bitcoin blockchain. Understandably, the word “double-spend” has people worried on social media, so I quickly want to shed some light on what actually happened and what this means for Bitcoin.

Source: Link

Stale blocks

In Nakamoto Consensus, all nodes automatically switch to the heaviest valid chain. This allows them to synchronize their local ledger state (who owns what) with that of everyone else.

Occasionally, two mining pools find a new block at about the same time, and these blocks have the same accumulated difficulty. Then some nodes switch to one block, and other nodes switch to the second block. For a short time, the Bitcoin network is then bifurcated. But odds are that the bifurcation is resolved once the next block is found.

For example, imagine that some miners were mining on block 666833-A and some miners were mining on block 666833-B. Once one of them found block 666834, this blockchain will have a higher accumulated difficulty, and all nodes still on 666833-A will automatically abandon their blockchain and switch to the new canonical one.

This is exactly what happened in the Bitcoin network, as both SlushPool and F2Pool found block 666833 at the same time. The tie was broken after Binance’s mining pool found block 666834 and F2Pool’s block became stale.

Now stale blocks occur relatively frequently and are absolutely nothing to worry about. But what about the suspected double-spend?

What is a double-spend?

There are two popular definitions for what a double spend is, a technical and a practical one.

Technically speaking, a double spend occurs when a payment received by some party (transaction T1) is first included in the blockchain and then removed at a later point. This can only happen if the block containing that transaction itself has become stale.

Further, it is not enough that the new blockchain does not include T1, otherwise it could still be included in the next block. In a double spend, one of the inputs to T1 must have been spent by a conflicting transaction T2. Since the input has been spent in T2, T1 then becomes invalid and is removed from the network.

We call this a double-spend not because a user has been double spent, but an input to the transaction has. But practically speaking, it also matters if T1->T2 actually changed the ownership of coins from one user to another.

In other words, the recipient of the T1 coins would have to send other funds or products in exchange. Here is an example that actually happened:

  1. An attacker deposits $1m in ETC on an exchange.
  2. They exchange the ETC for $1m in BTC and withdraw it.
  3. The attacker then double-spends the exchange by creating the competing transaction T2 where he spends the same coins that originally went to the exchange back to themselves.
  4. The attacker now has $1m in ETC plus $1m in BTC. The exchange is short $1m in BTC.

Step 3 is the equivalent of bouncing a check and that is extremely difficult to do in the Bitcoin network. Reversing T1 requires reversing all blocks on top of T1, which would require the attacker to control a large amount of hash power. So the attack can only be executed by miners or by someone renting hash power on a market like NiceHash.

Note that none of the largest coins can be attacked in this way. If someone rented all SHA256 hash power from Nicehash (~500 PH/s), that would be 0.4% of Bitcoin’s current hash rate (123 EH/s).

Essentially all double-spend events, including this recent one, are completely harmless. Even in the event that a large miner (instead of an accidental race between two mining pools) deliberately orphaned the block containing T1, they couldn’t have monetized the attack as long as the recipient followed the common 6 confirmation rule of thumb. In this case, we simply saw a conflicting T1 and a T2 both be confirmed at different times.

As we’ll now see, there are good reasons for a Bitcoin user to broadcast such conflicting transactions.

Replacing your own transactions

Whenever users broadcast a transaction to the network, they participate in a first-price auction. By attaching a fee to their transaction, they hope miners will include it in a block. Rational miners tend to fill their blocks to the brim with the highest paying transactions because that gives them the most profit.

But unlike regular auctions, the block space market has a quirk: if a bid didn’t win the block space it was bidding for, it’s not withdrawn but automatically enters the next auction. That can be a big problem if the bid remains too low to be included, leaving the transaction (and the respective coins) stuck for a long time.

So users need a way to bump fees on an already published transaction. A useful side effect is that users can bid lower on their transaction fee rates because they always have the option to bump the fee later. That makes block space auctions more efficient.

Finally, fee bumping can be necessary to close payment channels, e.g. in the Lightning Network. Uncooperative closes have hard deadlines, and missing that deadline because of insufficient fees could result in a loss of funds for users.

Users can bump their transactions in three ways:

  1. Child-Pays-For-Parent (CPFP).
  2. Transaction accelerators.
  3. Replace-By-Fee (RBF).

Child-Pays-For-Parent allows the receiving user to speed up a transaction if the sender has attached an insufficient fee. To do that, he broadcasts a secondary transaction that uses an output from the unconfirmed transaction as input and pays a larger-than-usual fee. If a miner wants the fee from the child transaction, whose validity depends on the inclusion of the parent transaction, he is now incentivized to include both. (For an example of a CPFP transaction, see here).

Transaction accelerators are off-chain markets for confirming transactions that paid an insufficient fee in the on-chain market. These are typically offered by mining pools and exist in free as well as paid variants. A user can paste their transaction ID into the accelerator and then the mining pool will include it with higher priority. Since no signatures are necessary, they can be used by both the sender and the receiver, as well as anyone else (examples: ViaBTC and BTC.com).

Replace-By-Fee allows a user to create a replacement transaction that spends at least one of the same inputs as the stuck transaction but pays an additional fee. (If it spent none of the same inputs, it would be a separate transaction altogether.)

Apart from bumping the fee on a stuck transaction, transaction replacement is useful for power use-cases like iterative payment batching. While regular batching combines many payments into the same transaction and then publishes them, it’s equally possible to first publish a transaction and then iterate on the transaction by adding more inputs and outputs after it has been broadcast but before a miner has included it in a block.

The transaction in question

Getting to the actual point, what actually happened in blocks 666833-1, 666833-2, and 666834?

As Bitcoin engineer and researcher 0xB10C shows in this diagram, a user (let’s call her Alice) broadcasted three competing transactions, each of them updating the fee rate compared to the previous one.

The first one had a fee rate of 1 sat/b, which was too low to be included in a block. So about 23 hours later, she decided to replace the transaction using RBF with a higher fee rate of 9.4 sat/b. Note that Alice had waited almost an entire day at this point and her transaction still wasn’t mined before she broadcasted the replacement transaction. The new fee rate, however, was still too low, and so she replaced it again with yet a higher fee rate another three hours later.

This analysis was later confirmed by BitMEXResearch as well.

So was it a double-spend?

We established that users can use RBF to replace transactions before they have been confirmed. The updated transactions will often have different outputs from the first transaction, as users are trying to cancel the previously stuck transaction and send it back to themselves. This is almost certainly what happened with Alice’s replacement transactions 2 and 3 (more on that in the Addendum).

As a result, different versions of a transaction can float in the network at the same time. When miners include two conflicting (but not harmful!) transactions, a previously included transaction can become invalid. That makes it initially seem like a double spend.

While it is theoretically possible for double-spending to occur on the Bitcoin network, it is not usually a serious concern. Here are some useful heuristics for when you should panic:

The value of the invalidated transaction is in the tens to hundreds of millions of dollars. This is the minimum it would take for miners to profitably “bounce a check”, considering that such a double-spend could seriously damage Bitcoin’s reputation and market value and the hardware used by miners is worth tens of billions of dollars. For comparison, today’s transaction in question was about $25.

Exchanges or other large entities would not accept such a large transaction as final without waiting for a more reasonable number of confirmations. So an attacker couldn’t actually bounce a check by replacing a single block, they likely would have to replace 10+ blocks. This requires most of the existing miners to collude with each other and risk destroying their investment in mining hardware at the same time.

Like many debates, this comes down to definitions of the word double-spend. I offered two of them today, one of which is technical and refers to the double-spending of an INPUT, not another user. An input double-spend has clearly taken place, but it has zero effect on other users.

A double-spend in the practical sense is like bouncing a check after paying another user. We can say with very high certainty that this has NOT taken place for several reasons.

  1. The presence of other, far more likely explanations, such as a user (not a miner!) invalidating their own transaction.
  2. The absence of other factors that we would expect in a harmful double-spend, namely a MUCH larger invalid transaction and a much larger number of stale blocks.

Since the media is quick to pick up on terms like “double-spend”, I think we should be careful not to use it when none of the heuristics point toward a double spend in the harmful sense having taken place. Instead, I would suggest saying “a transaction has been invalidated”, which has a much broader meaning and includes the many ways a user might deliberately cancel a transaction.

Addendum – What did Alice try to do?

So we have established that this was certainly not a double-spend where miners deliberately replace one blockchain with a higher-difficulty one to bounce a very large check. It had no miner involvement and was simply a stale block.

However, many people were still curious what the nature of the different transactions published by Alice was. Why did she replace her original transaction, to begin with? Was this maybe an attempt to double-spend a merchant who accepts “zero-conf”?

We can immediately rule out the latter because almost an entire day passed between the initial transaction and the first replacement transaction. But it might still be interesting to explore who Alice spent the coins to. While we can’t know who controls the outputs respectively, we can use some heuristics to solve the puzzle.

  1. Inputs can be ignored because we know those must be controlled by the user.
  2. A transaction with one output is almost always an internal transfer (meaning between wallets of the same user). It rarely happens that you have exactly the right input in your wallet to pay someone else.
  3. A transaction with two outputs often pays one third-party and spends the rest back to the user (a so-called change output).

Putting the puzzle pieces together, we can see that transactions 2 and 3 both have only one output, meaning Alice probably controls these outputs. Transaction 1 shares one output with transaction 3, so the same would apply there.

This leaves only the second output of transaction 1, 3JaLvP… to be unaccounted for. To answer this question, we need to make a small detour. Why did the lower fee transaction (1 sat/b) win? Does Slushpool hate money?

The answer is no, Slushpool behaved exactly like they are supposed to. The reason is either Alice or her counterparty employed the other technique that we previously discussed, CPFP, to get their transaction confirmed.

In other words, she made a previously unknown fourth transaction that included the stuck output from transaction a13c2bd… to address 3JaLvP… as its only input in the same block.

As we can see here, the new transaction paid a much higher fee rate (46.3 sat/byte) that was enough to pay for both transactions. Since the inclusion of transaction 1290376… is conditional on the inclusion of transaction a13c2bd…, Slushpool had to include both.

By spending the funds from 3JaLvP…, we can know with very high certainty that the funds did reach their target destination and were not, in fact, double-spent. We know this because only someone who controls the private key to 3JaLvP… could have signed the CPFP transaction that eventually got it confirmed.

There is a very small chance that Alice was trying to send to a third party, Bob, who got tired of waiting for the money to arrive. So he spent the unconfirmed output to him using CPFP. But it’s far more likely that this was simply Alice sending money between her own wallets AND/OR experimenting with different ways to bump the transaction fee.

Hence, we can conclude that all outputs were indeed controlled by Alice.

AUTHOR(S)

Hasu

Crypto researcher writing w/@zhusu for @DeribitInsights and uncommoncore.co, podcast: uncommoncore.co/podcast/

THANKS TO

Thank you to BitMEX Research for running ForkMonitor as a valuable community service. Thank you to 0xB10C for the diagram. Thank you to James Prestwich and BitMEX Research for providing feedback on this article.

RECENT ARTICLES