Wednesday 21 October 2015

Working around transaction malleability

In my last post I wrote about how someone is exploiting transaction malleability to produce duplicate transactions that are seen as valid by the Bitcoin network. This is causing a few different issues, where the most important for my use-case is the inability to safely spend unconfirmed change from previous transactions.

Normally one would be able to spend the outputs of a transaction without waiting for it to be confirmed by a miner. When the subsequent transactions are broadcast, the other nodes, miners included, will know that the previous transaction with the outputs now being spent is pending confirmation. Miners can chose to include a chain of transactions spending each others' outputs in the same block, effectively confirming them all at once.

With duplicate valid transactions being broadcast by a third-party, it's not clear which one will end up getting confirmed, rendering both the unconfirmed transactions and their outputs potentially invalid, precluding reliable spending of them. Thus the change (and other outputs) become locked up until the previous transaction is confirmed in at least one block (or more if one wants to be safe from orphaned blocks).

Malleability strikes

I'll use a set of transactions made last night as an example. I'm sending 1.2 mBTC (which happens to be most of the funds in this wallet) to a couple of my own addresses:

Transaction: c83daec40c929b93bcc74b2cf1e434df6dd683e03f0734d659eb598fd6be859f

Input: tx 3d9ae35ec8e5b4c9b9cdad8c1fe7bd9afc465646485a9d5932fb71cbff586294 (output index 0) => 13ZsFbiWrDeyyBwwtFpmssA47Tv7v7tZNX
Signature: 304402205e60723c766054f37de49e312e5ca30458d80b2998319acdd6b9987c9b585f7c02206b282675dee89634b4470ea314ccfd18171732d35473517cd49b2889c9d1b46901

Input: tx f2312492b3383adc7d3aa68762e3988eba21b0787810eaf5504b7382247c814c (output index 0) => 191SwwonaRnUxDREcFVKgaRyUn6RuKPGjj
Signature: 30440220015dceb8395f0e5f52c47739659306e6ed552bd5f3fca2a6730587f4c82e2a6e022064a9bc869b35d6c773c7bf5423f40bd057666f14b2e6ef6dca5de1394d39a04501

Input: tx f2312492b3383adc7d3aa68762e3988eba21b0787810eaf5504b7382247c814c (output index 1) => 1Ky3S6nfazgAzGJB9UoS7Djn2mh3tSrMT
Signature: 30440220581b87bb9c49bcaa834081a1454bc68ee5c4541bbe98622f8cc4518e551b756302206cf4c02c961866913d11dd0f69d03d9c38a7874a17e68706af1399d11ec2e9a801

Output: 100000 satoshis (1.000000 mBTC) to script: OP_DUP OP_HASH160 1K1RhsKXz3amFrpoWB8H4B3bgYTafBsU9L OP_EQUALVERIFY OP_CHECKSIG
Output: 20000 satoshis (0.200000 mBTC) to script: OP_DUP OP_HASH160 1LwuLQK93PDTB1fR4CJ5DgpnsgrUXJiaTb OP_EQUALVERIFY OP_CHECKSIG 


Within seconds of me broadcasting the above transaction, the following equivalent transaction is produced by an unknown third-party:

Transaction: fe99ee388fca957b324039b8cc2c3938b2a0e3bfff9cb98bec0c7a422b4242b2

Input: tx 3d9ae35ec8e5b4c9b9cdad8c1fe7bd9afc465646485a9d5932fb71cbff586294 (output index 0) => 13ZsFbiWrDeyyBwwtFpmssA47Tv7v7tZNX
Signature: 304502205e60723c766054f37de49e312e5ca30458d80b2998319acdd6b9987c9b585f7c02210094d7d98a211769cb4bb8f15ceb3302e6a397aa135ad54ebeeb37360306648cd801

Input: tx f2312492b3383adc7d3aa68762e3988eba21b0787810eaf5504b7382247c814c (output index 0) => 191SwwonaRnUxDREcFVKgaRyUn6RuKPGjj
Signature: 30450220015dceb8395f0e5f52c47739659306e6ed552bd5f3fca2a6730587f4c82e2a6e0221009b56437964ca29388c3840abdc0bf42e63486dd1fc61b0cdf5747d5382fca0fc01

Input: tx f2312492b3383adc7d3aa68762e3988eba21b0787810eaf5504b7382247c814c (output index 1) => 1Ky3S6nfazgAzGJB9UoS7Djn2mh3tSrMT
Signature: 30450220581b87bb9c49bcaa834081a1454bc68ee5c4541bbe98622f8cc4518e551b7563022100930b3fd369e7996ec2ee22f0962fc2628207559c9762193510bec4bbb173579901

Output: 100000 satoshis (1.000000 mBTC) to script: OP_DUP OP_HASH160 1K1RhsKXz3amFrpoWB8H4B3bgYTafBsU9L OP_EQUALVERIFY OP_CHECKSIG
Output: 20000 satoshis (0.200000 mBTC) to script: OP_DUP OP_HASH160 1LwuLQK93PDTB1fR4CJ5DgpnsgrUXJiaTb OP_EQUALVERIFY OP_CHECKSIG


I won't go into how it's different from my original (read my previous post on the topic for that); suffice to say it is a valid, but conflicting transaction, and nodes and miners have no way to tell which is the original of the two.

If I want to spend the outputs of that transaction before it is confirmed, the dilemma is the following: the two conflicting transactions have different hashes, producing unique and different outputs:

  • c83daec40c929b93bcc74b2cf1e434df6dd683e03f0734d659eb598fd6be859f, index 0 has 1 mBTC and index 1 has 0.2 mBTC
  • fe99ee388fca957b324039b8cc2c3938b2a0e3bfff9cb98bec0c7a422b4242b2, index 0 has 1 mBTC and index 1 has 0.2 mBTC

Eventually one of those will be confirmed and the other one will be thrown away as invalid. If we use the wrong one in our next transaction, parts of the network will accept it as a valid transaction for a while (essentially whatever nodes saw its predecessor first are going to see this one as valid too), but it will never end up getting confirmed by a miner. This is frustrating, and can result in us believing we sent a payment (or recorded a document checksum on the blockchain) when in reality the transaction ended up getting dropped by the whole network.

Living with uncertainty

Using a transaction fingerprint that isn't affected by signature modifications, it is easy to recognize two or more transactions as originating from a malleability attack. This information can be used to postpone spending the change until one is confirmed and the situation is cleared up.

Another option when this is encountered is to accept that out of this set of transactions, only one will eventually get confirmed, and new transactions created spending the outputs of the others are certain to get dropped. What I did was to modify my wallet software to create its own duplicate transactions when a situation like this is encountered. The following are the suggested transactions for splitting the still unconfirmed 1 mBTC I just sent to 1K1RhsKXz3amFrpoWB8H4B3bgYTafBsU9L into payments of 0.5 and 0.4 mBTC to two new addresses (paying a 0.1 mBTC fee):

Transaction 0: 4ebe551a5830a9a17b149fa75ae037e0511a730ae9919183a9c03045bed043b1

Input: tx c83daec40c929b93bcc74b2cf1e434df6dd683e03f0734d659eb598fd6be859f (output index 0) => 1K1RhsKXz3amFrpoWB8H4B3bgYTafBsU9L
Signature: 30430220197d47182020132ac30fb0ef5d0a86913adc4aaf756a806e2997fe2e4ea79eaf021f60ba102e4400538f286bf877250d72708cead8561f1c21be4eba83221c314c01

Output: 50000 satoshis (0.500000 mBTC) to script: OP_DUP OP_HASH160 1NK66CAL8CajEZBFLXke3VtckxnML3PYpv OP_EQUALVERIFY OP_CHECKSIG
Output: 40000 satoshis (0.400000 mBTC) to script: OP_DUP OP_HASH160 1LivLKSyh9hU9enjptHveRqxp1uJ8NfDYd OP_EQUALVERIFY OP_CHECKSIG 


Transaction 1: aa37ea6c35e6688b52f71908bd3fb7700b83d6e88e32349914a7be964e5d69a8

Input: tx fe99ee388fca957b324039b8cc2c3938b2a0e3bfff9cb98bec0c7a422b4242b2 (output index 0) => 1K1RhsKXz3amFrpoWB8H4B3bgYTafBsU9L
Signature: 304402202dc4cb51339bd2b434df13e3a9bbe65d845750ff03f2622a38e58dd47418c92c0220422e8389316d3dd5fff3a1ad66bb186460303a7616d52fb9f89ee5170740981a01

Output: 50000 satoshis (0.500000 mBTC) to script: OP_DUP OP_HASH160 1NK66CAL8CajEZBFLXke3VtckxnML3PYpv OP_EQUALVERIFY OP_CHECKSIG
Output: 40000 satoshis (0.400000 mBTC) to script: OP_DUP OP_HASH160 1LivLKSyh9hU9enjptHveRqxp1uJ8NfDYd OP_EQUALVERIFY OP_CHECKSIG

As you can see, two basically equivalent transactions are generated, one for each of the potentially valid outputs to 1K1RhsKXz3amFrpoWB8H4B3bgYTafBsU9L. These would then, ideally, each be sent to the nodes of the network that see their respective predecessors as valid transactions as these would readily accept this next one as well. For simplicity we do the slightly abusive alternative of just broadcasting both of them, and letting our connected nodes (based on which predecessor they saw first) filter out the "good" one.

In this particular example, fe99ee388fca957b324039b8cc2c3938b2a0e3bfff9cb98bec0c7a422b4242b2, the conflicting transaction broadcast by the unknown third-party, ended up becoming the confirmed one, mined in block 379791. As it turns out, our second follow-up transaction (aa37ea6c35e6688b52f71908bd3fb7700b83d6e88e32349914a7be964e5d69a8) got confirmed in the same block. The first follow-up gets dropped by the network along with its predecessor, as intended.

Limitations

This approach relies on creating a potentially high number of redundant transactions to be able to spend unconfirmed change. In the case above, it's only two transaction, but the nature of transaction malleability allows third-parties to create a lot more duplicates than that if they are so inclined. Furthermore, you'd now have a number of actually distinct duplicate transactions (the change-spending transactions above have different inputs, which is a new level of duplication than what malleability allows for). If a third-party decided to create duplicates of the new change-spending transactions and we were to try to spend the unconfirmed outputs of these too, we'd have a veritable explosion of duplicate transactions.

With the current magnitude of the malleability attacks, where a single duplicate transaction is broadcast for each original transaction, creating duplicate spending transactions for the respective outputs will enable several levels of chained transaction between block -- without requiring excessive numbers of speculative transactions to be broadcast. That might not be the case if the malleability attacks intensified in the future. For now though, the workaround outlined above seems like an appropriate and sufficient remedy.

No comments:

Post a Comment