How to Protect Against Replay Attacks
Jeff Garzik recently reverted opt-in replay protection on the btc1 GitHub. This is causing some anxiety as many believe this lack of replay protection will cause all sorts of horrible things on the network.
In this article, I’m going to explain a few ways to protect against replay attacks regardless of whether replay protection is implemented. This will require explaining how Bitcoin transactions work and how UTXOs are consumed and hopefully, you, dear reader, will have a better grasp of what you will need to do to be safe going forward.
In order to understand what we need to do to guard against replay attacks, we first need to dig a little bit into how Bitcoin works.
With Bitcoin, you actually don’t have a single big balance, even if all the payments have been sent to a single address. What you have are a bunch of little payments to you called UTXOs.
UTXO stands for unspent transaction output, and you can think of them like bank checks that someone has sent to you. You can only spend what you have and these checks add up to the balance your wallet shows.
When you spend some amount of Bitcoins, what your wallet does is it uses one or more of these checks that have been sent to you and spends them as inputs. So if Alice sent you 5 bitcoins and Bob sent you 3 bitcoins and you’re sending 8 to Charlie, your wallet knows to use both the “5 bitcoin” check and the “3 bitcoin” check as inputs. You send 8 bitcoins to Charlie and he now has an “8 bitcoin” check (note this example ignores fees).
An important feature of UTXOs is that the entire UTXO has to be spent any time a UTXO is used as an input. That is, if you’re spending a check your friend sent you, you have to spend all of it.
So if you only have a single check (UTXO) that’s 100 Bitcoins and only want to send 0.001 to someone, what do you do? This is where you use the fact that you can send Bitcoins to multiple people in a single transaction. You can send 0.001 to someone and send 99.9985 to yourself (and give 0.0005 as a fee to the miner). You will now have a new check (UTXO) that’s 99.9985 Bitcoins that comprise of your balance.
This way, once a UTXO is spent, it can be ignored. It’s either spent or unspent with no in-between state, which makes the code a lot simpler.
The global set of UTXOs, or the complete set of everyone’s unspent checks, is what the full nodes keep track of in order to validate that something is not being double spent. Again, once a UTXO is spent, it’s no longer a UTXO, but a spent transaction output and can be ignored as a valid input to a transaction.
At the beginning of the hard fork, the same UTXOs are on both chains. For a replay attack to work, the entire transaction has to be valid on both chains. Assuming no strong replay protection, all transactions using UTXOs valid on both chains will be vulnerable to replay attacks.
However, as the two chains diverge in transaction composition, the UTXO sets will start to differ. For example, the coinbase transactions (miner reward transactions that occur once per block, not the company) on both chains will certainly be different. Coinbase transactions post-hard fork will not be replayable. That is, a coinbase transaction on 2x will not be replayable on 1x and neither will a coinbase transaction on 1x be replayable on 2x.
Of course, coinbase transactions (which we call Level 0 transactions) have outputs and those will be a part of the UTXO set. Any transaction utilizing Level 0 UTXOs (which we call Level 1 transactions) as inputs will also not be replayable on the other chain. In addition, any transactions that utilize the outputs of Level 1 transactions (which we call Level 2 transactions) will also not be replayable on the other chain. This goes on and on with Level 3 transactions all the way to Level N.
Note that if even a single Level N transaction is an input to a transaction, the transaction will not be replayable on the other chain. That is, a Level N transaction on 2x is not replayable on 1x and a Level N transaction on 1x is not replayable on 2x. We call all unspent outputs of Level N transactions replay-protected UTXOs.
This particular method is a bit cumbersome as coinbase transactions have a special rule that makes them unspendable for 100 blocks. This is normally about 17 hours, but it could be much longer depending on how hashing power splits.
The other way in which you can create replay-protected UTXOs without requiring a coinbase transaction or one of its descendants is to create two different transactions, both sent to yourself spending the same UTXO.
This is how such a procedure would work:
- Say you have a UTXO, u
- Spend u in two different transactions, the first (tx1) going to address1 and the second (tx2) going to address2
- Submit tx1 to chain 1 and tx2 to chain 2 at the same time with sufficient fees.
If everything works out, you’ll have tx mined in chain 1 and tx2 mined in chain 2. Of course tx1 may be replayed on chain 2, but since tx2 was submitted first, it will look like a double spend to nodes on chain 2 and nodes will likely reject it. Same with tx2 being replayed on chain 1. It too will look like a double spend to nodes that receive tx1 first.
As long as the replay attack doesn’t happen right away and the fees are high enough on both transactions, you should have an output on each chain that you can use as a replay-protected UTXO.
Even if somehow you get replayed, that’s okay as the addresses are controlled by you so you don’t lose anything but the tx fees and you can try again
The final way to create a replay-protected UTXO is to utilize something called locktime. This is not a feature that’s found in too many wallets, but you can make a transaction illegal to be included in the Bitcoin blockchain until a certain block number. That is, if you specify a block number of 500,000 any block before block number 500,000 that includes such a transaction would be rejected by the network.
The key to making this work is if one chain is longer than the other by a decent amount (at least 6, but better if it’s 20–30). Say chain 1 is at block 500,010 and chain 2 is at 500,000.
- Say you have a UTXO, u
- Spend u on chain 1 with a locktime of 500,011 in tx1.
- As soon as tx1 is included in a block (hopefully before block 500,011 on chain 2), create another transaction (tx2) that spends u on chain 2 to a different address.
- If tx2 gets confirmed before block 500,011 on chain 2, we now have replay-protected UTXOs on both chains.
Natural Replay Protection
Replay-protected UTXOs turn out to be a really effective way to protect against replay attacks.
All you need is a single replay-protected UTXO. The UTXO can have a very small value, as the amount does not matter. What both sides will probably end up doing is offer faucets that give a tiny amount of replay-protected UTXO to you and you can utilize that to make sure your transaction will not be replayable.
At the very least, we can expect exchanges, merchants and other Bitcoin businesses to utilize this so they don’t accidentally send an equivalent amount of the other coin to their customers. In other words, you’re going to be able to get replay-protected UTXOs from exchanges, faucets, merchants, etc very soon after the hard fork since it’s in their interest to not be replay-attacked.
That said, it’s of course much more convenient and less taxing for the ecosystem if there were strong replay protection built-in, as less developer time will have to go into creating unreplayable UTXOs among other things.
What This Means For You
If you’re a holder and don’t plan to do any sort of transactions for a while, then this won’t matter until there’s actually some transacting you want to do.
Even if Segwit2x does not offer any replay protection, we can expect there will be replay-protection faucets to arise so you can transact safely. You’ll want to grab a few satoshis from such faucets and move the entire balance (including your newly gotten satoshis) of your wallet to another address in your wallet. This will take away some of your privacy as all your addresses will now be linked, but this transaction will not be replayable.
If privacy is a concern, you’ll want to get a lot more replay-protected UTXOs from a lot of faucets. Unfortunately, this will take a lot of time depending on how many addresses are in your wallet, not to mention expensive from a fee perspective. Your wallet will also need some sort of “coin control” feature in order to make this work (Bitcoin Core and Armory definitely have coin control, Electrum sort of does). This, by the way, is something wallets should offer will make Bitcoin privacy stronger if more wallets offered it and more users utilized it.
There may also be wallets that allow you to attempt to create replay-protected UTXOs. This will require wallets to either specify locktime or to make locktime the last block it knows about (Bitcoin Core wallet does this). In other words, you’re going to have to wait for some features from your favorite wallet to mitigate this problem.
Replay protection would be a nice thing to have for a hard fork, but don’t fret. There are solutions that will emerge including faucets, wallets that let you specify locktime and similar things. You will have options as far as protecting your Bitcoin goes. All you need to do is pay attention and wait for the tools to arrive.
Now if you’re a wallet/exchange/payment processor/merchant, that’s another story…
Want to get curated Technical Bitcoin News? Sign up for the Bitcoin Tech Talk newsletter!
Are you a developer that wants to get into Bitcoin and blockchain? Sign up for Programming Blockchain Seminar in Stanford, London or Seoul!