Bitcoin Replace-By-Fee guide: fix stuck transactions, do doublespends

In this article we will study what is Replace-By-Fee transactions and how to use them. Surprise, with RBF you can try to do doublespends!

Imagine situation when Alice sends coins to Bob. She creates and signs the transaction, and broadcasts it to the network for confirmation. Unfortunately, the transaction hangs unconfirmed for a long time, as it has low fee (or an ok fee under other circumstances, but at this point of time unconfirmed transactions queue is too big and miners prefer transactions with higher fees). What can Alice do? Her options: a) wait for a confirmation (might take a while) b) wait till transaction gets cancelled (most miners will exclude it from mempool) and freed coins would be spendable again (i.e. create transactions using those coins and most pools won’t reject it as doublespend, as old transaction got cleared from mempool) c) replace transaction, increasing the fee (RBF).

Thus, RBF — replace of existing transaction with a new one, increasing the fee. “Replace” means that new transaction will use same inputs (i.e. coins) as old one, and this would not be considered as doublespend (i.e. cheating). As result, only one transaction will be confirmed, mined and added to blockchain (most probably the one with higher fee).

RBF was introduced in BIP-0125

But kind of transaction is potentially replaceable? Good question. Transaction replacement was introduced by the Bitcoin designer himself Satoshi Nakamoto, but disabled at some point. It was then improved/upgraded to RBF and shipped in Bitcoin Core 0.12+. For replaceability, there’s a designated int field called nSequence, which signals which transaction is an older version and which one is younger. So, for the transaction to be replaceable nSequence should be lower than MAX (0xffffffff — 1). By default, most wallets set nSequence to maximum and you have to manually enable transactions replacement ability in settings. Here’s how it looks in Electrum:

Enable RBF in Electrum wallet

An example of decoded transaction:

... 
"inputs": [
{
"addresses": [ "...." ],
"output_index": 0,
"output_value": 1010000,
"prev_hash": "...",
"script": "...",
"script_type":
"pay-to-pubkey-hash",
"sequence": 0
},
...

Sequence is indicated for each transaction input.

In Electrum after you enable RBF you’ll be able to increase transaction fee right in GUI. Right click the transaction and you’ll see it. But now we are going to manually create RBF transaction. Why? First of all, to better understand what’s going on. Second, for more versatility. In case of manual RBF we can specify totally different destination addresses, which can be considered a doublespend.

This is how it works. Alice wants to pay Bob for goods. Alice creates, signs and broadcasts transaction (which transfers coins to Bob), but intentionally sets low (or zero) fee. If Bob is not cautious enough he accepts unconfirmed transaction as payment and sends goods to Alice. After that Alice replaces the transaction with output addresses which Alice controls. Basically, Alice returns her funds. Bob got scammed. How can Bob avoid that? First, if Bob wants to accept unconfirmed transactions (for speed), don’t accept replaceable transactions (where nSequence is lower than MAX). Of course, ideal case when Bob waits for enough confirmations.


Now let’s try to do an RBF with doublespend.

This is a real live example that can be verified on blockchain.

We shall use neat opensource tool github.com/OutCast3k/coinbin which can create transactions with arbitrary inputs and outputs. It works almost completely inside browser tab without server interaction. Live version is available on coinb.in but you are encouraged to download it and run from localhost.

Let’s create New → Transaction. Get a private key in WIF format and load unspent outs for that key. In Outputs tab set destination address, set amount. In Advanced Options dont forget to tick “Make this a RBF transaction” — in that case, nSequence won’t be set to MAX. Click Submit.

Construct transaction

Transaction is created. Note the Transaction Fee — it’s the difference between sum of available coins (inputs) and sum of all sent coins (outputs). This difference is a miner fee, and it’s set to zero in this example. Click Sign, copypaste the transaction hex, same private key and finally sign the transaction:

Sign transaction

Broadcast the transaction (you can use any endpoint):

Broadcast transaction

This is how the transaction looks:

Unconfirmed transaction!

Now, let’s use RBF. Create new transaction, and take outs from the same WIF. Let’s specify totally different destination address! One of the destinations we are going to set to 1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg — this is donate address of coinb.in creator. Set the Amount so the fee is significant for miner for a quick confirmation.

Constructing RBF transaction

Note the Inputs tab — if the source wallet was used multiple times and has lots of transactions, we would have to manually specify only inputs we used in transaction we are going to replace (basically remove all other inputs and leave only the ones we need).If we have hundreds of inputs that might be quite a tedious work.

Sign it:

Sign transaction

Broadcast:

Broadcast transaction

This is how it looks now on blockchain.info. New transaction has all chances to be included in block before the ancestor.

Unconfirmed transactions! Smells like doublespend

Wait. Got a confirmation:

Confirmed RBF transaction

We have just replaced transaction using RBF, notoriously, we sent the coins to a different from original destination address.


A few words about security and doublespends

Not always nSequence=MAX guarantees that transaction is final and can’t be replaced. You could try NOT ticking “Make this a RBF transaction”, but:

  • Not all nodes will agree to relay the new tx version, referring to mempool-conflict
  • Some nodes will accept this transaction
  • Websites like blockchain.info will basically scream that this is a DOUBLE SPEND
  • Not all pools will mine this new transaction, as it is a doublespend. Some pools will mine it, but it might take more time (comparing to when all pools mine your transaction)
  • If new transaction is finally included in block — this is absolutely legit and does not contradict to consensus rules. If someone got scammed this way — there’s no place he can complain about it.

Thus, we come to a conclusion that any transaction without at least two confirmations is unreliable. Why two, not one? Short answer: orphaned blocks.

References:


Bonus!

Real life story. Once upon a time a website using our bitcoin payment gateway got hacked, and hacker requested a 2 BTC payout from gateway. Gateway at that time had hardcoded low fee (0.0001), so hacker’s transaction got stuck unconfirmed. It gave us enough time to study the situation, and replace the transaction with different (our’s) destination address. We increased the fee to 0.1 to speed up. Hacker got nothing. Lesson — use RBF to do good!

Real life story 2. I was refilling bitmedia.io acount with bitcoin. Account refill there works with depositing to same deposit address, and total amount of user’s balance gets summed up of all incoming to that address transactions. Only 6 confirmation transactions are counted. I wanted to top up faster, so I used RBF several times. When the transaction got finally confirmed, the final credited balance was multiplied by the times I used RBF! I instantly submitted a support ticket, and as a bug bounty I got to keep extra 0.3 BTC. Lesson — check how your systems behave with RBF! I wonder how many systems in the wild are vulnerable to this…

Originally published at habrahabr.ru.