<blockquote>
<p>If you were a miner, what are the steps you would take to create the extra (21,000,012.5th) bitcoin?</p>
<p>Where in the source code is this exactly (link)?</p>
</blockquote>
<p>There are two components to CVE-2018-17144. There is a crash bug and an inflation bug. Both are triggered by almost the same scenario: a transaction contains an input multiple times.</p>
<p>In general, how this would work is as follows: lets suppose a miner has an unspent output A for 1 BTC. They create a transaction with that input in twice, so input 1 spends from output A and input 2 <em>also</em> spends from output A. The output of that transaction has a value of 2 BTC. Note how the output's value is larger than the value of output A, but if you had output A twice, the value is correct.</p>
<p>The miner would then take this transaction and include it in a block that he is mining. Once the miner find a block with his transaction included in it, he broadcasts it to the Bitcoin network.</p>
<p>When a Bitcoin Core 0.14.x node receives this block, it will validate the block, but it will skip the duplicate input check because of <a href="https://github.com/bitcoin/bitcoin/blob/v0.14.2/src/validation.cpp#L2883" rel="noreferrer">the <code>false</code> parameter on this line</a>. So the transaction the miner made will pass this step of validation, and the other transaction validation steps, including input script validation, until it reaches <a href="https://github.com/bitcoin/bitcoin/blob/v0.14.2/src/validation.cpp#L1328" rel="noreferrer">this loop</a>. In this loop, the inputs to the transaction are being marked as spent in the UTXO database. The first time the duplicated input is seen, it is marked as spent. But the second time it is seen, the coin is already marked as spent so <code>coins-&gt;vout[nPos].IsNull()</code> will be true. This means that it will go into <a href="https://github.com/bitcoin/bitcoin/blob/v0.14.2/src/validation.cpp#L1332" rel="noreferrer">this if statement</a> and subsequently hit the <a href="https://github.com/bitcoin/bitcoin/blob/v0.14.2/src/validation.cpp#L1333" rel="noreferrer"><code>assert</code> statement that follows</a>. The assert causes the software to crash.</p>
<p>For Bitcoin Core 0.15.0 - 0.16.2, the behavior is different. This is due to the change in how the UTXO database is structured. Everything is largely the same until the <a href="https://github.com/bitcoin/bitcoin/blob/v0.15.0/src/validation.cpp#L1183" rel="noreferrer">same loop</a> is reached. Here, instead of returning whether the output was spent, <code>SpendCoin</code> actually returns whether the input <em>exists</em> in the database. So the first time, it will pass as expected, but the second time, instead of returning <code>false</code>, it still returns <code>true</code>.</p>
<p>Looking at <a href="https://github.com/bitcoin/bitcoin/blob/v0.15.0/src/coins.cpp#L101" rel="noreferrer"><code>SpendCoin</code></a>, you can see that it only returns false when it is unable to fetch the coin (object representing a UTXO) from the database. With the new database structure, this makes sense as the output should be removed from the database when it is spent. But, if you <a href="https://github.com/bitcoin/bitcoin/blob/v0.15.0/src/coins.cpp#L109" rel="noreferrer">look a few lines down</a>, you see that it only deletes the coin when it is marked as <code>FRESH</code>. In the case the coin was <code>FRESH</code>, <code>SpendCoin</code> would delete the object on the first pass so the second pass the coin would not be found and thus it would return false. This triggers the <a href="https://github.com/bitcoin/bitcoin/blob/v0.15.0/src/validation.cpp#L1186" rel="noreferrer"><code>assert</code> following the function call</a> causing the node to shutdown.</p>
<p>If the coin was not <code>FRESH</code>, the coin object itself is not deleted, but its contents are cleared. This means that the second time the input is seen, if the coin was not <code>FRESH</code>, <code>SpendCoin</code> would still return true as the object still exists in memory, which means that it passes the assert that follows the <code>SpendCoin</code> (which caused the crash when the coin was not <code>FRESH</code>). Then validation continues as normal, and the output this transaction created is added to the UTXO database, which means that money that shouldn't exist now exists in the UTXO database.</p>
<p>So now the question is, when are UTXOs marked as <code>FRESH</code>? They are marked <code>FRESH</code> when they are added to the UTXO database. But the UTXO database is still only in memory (as a cache). When it is saved to disk, the entries in memory are then no longer marked as <code>FRESH</code>. This saving to disk happens after every block (as well as at other times, but that is not important).</p>
<p>Thus, if a miner has an output that was part of a transaction that has already confirmed, and he spends the output twice in the same transaction (so the transaction has two inputs that refer to the same output), and this transaction is not broadcast to the network but instead included in a block that he mines, he is able to create a new output that has twice the value of the output that he spent, thereby creating coins.</p>
<blockquote>
<p>Why can't this be done by a non-miner?</p>
</blockquote>
<p>The reason that this cannot be done by a non-miner is because transactions that are received outside of blocks are still checked for duplicate inputs. The transaction will be rejected as invalid and not added to the node's mempool, so the transaction will never get into a block. It is only transactions with duplicate inputs that get into blocks that trigger this vulnerability, and thus only miners can do this as they must knowingly insert an invalid transaction into their block.</p>
<blockquote>
<p>Also, which forks are/were vulnerable?</p>
</blockquote>
<p>Any fork whose software includes commit <a href="https://github.com/bitcoin/bitcoin/pull/9049/commits/eecffe50efc3944d713c701fa375dacbf17fb7cf" rel="noreferrer"><code>eecffe50efc3944d713c701fa375dacbf17fb7cf</code></a>. This would mean any software forked from or pulled in changes from Bitcoin Core after November 10th, 2016.</p>

CC BY-SA 4.0
2018-09-22T18:16:23.860Z
Would a node that receives the block as a compact block check for the duplicate?
Murch
CC BY-SA 4.0
2018-09-22T19:04:12.857Z
No, it would not. The transaction with the duplicate input would not be in the mempool so it would be sent along with the block (as part of a `blocktxn` message). Such transactions are not run through the mempool acceptance code. Instead the block is reconstructed and run through the normal block acceptance code.
Ava Chow
CC BY-SA 4.0
2018-09-26T07:54:18.737Z
Great write-up, thank you! It seems to me having a bool flag such as `fCheckDuplicateInputs` seems pretty unfortunate. It is quite easy to set it to an invalid value. I'm just wondering if we shouldn't be more explicit and verbose in those critical parts
tsusanka
CC BY-SA 4.0
2018-09-22T01:23:49.920Z
2018-09-22T01:23:49.920Z
2020-06-16T11:10:03.350Z
Ava Chow
<p>If you were a miner, what are the steps you would take to create the extra (21,000,012.5th) bitcoin? </p>

<p>Where in the source code is this exactly (link)?</p>

<p>Why can't this be done by a non-miner?</p>

<p>Also, which forks are/were vulnerable?</p>

CC BY-SA 4.0
2023-11-29T19:19:10.170Z
21 upvotes, very meta, good job everyone
hedgedandlevered
<reward-schedule><error><weaknesses><source-code>
How does the most recently found critical vulnerability (CVE-2018-17144) work?
hedgedandlevered
# How does the most recently found critical vulnerability (CVE-2018-17144) work?

(Asked by: username_0 on 2018-09-22)

If you were a miner, what are the steps you would take to create the extra (21,000,012.5th) bitcoin?

Where in the source code is this exactly (link)?

Why can't this be done by a non-miner?

Also, which forks are/were vulnerable?

**Tags:** reward-schedule, error, weaknesses, source-code

> **Comments:**
> **username_0**: 21 upvotes, very meta, good job everyone  

---

## 1 answers

### Answer by username_1 on 2018-09-22. Score: 30 | Accepted answer

> If you were a miner, what are the steps you would take to create the extra (21,000,012.5th) bitcoin?
>
> Where in the source code is this exactly (link)?

There are two components to CVE-2018-17144. There is a crash bug and an inflation bug. Both are triggered by almost the same scenario: a transaction contains an input multiple times.

In general, how this would work is as follows: lets suppose a miner has an unspent output A for 1 BTC. They create a transaction with that input in twice, so input 1 spends from output A and input 2 *also* spends from output A. The output of that transaction has a value of 2 BTC. Note how the output's value is larger than the value of output A, but if you had output A twice, the value is correct.

The miner would then take this transaction and include it in a block that he is mining. Once the miner find a block with his transaction included in it, he broadcasts it to the Bitcoin network.

When a Bitcoin Core 0.14.x node receives this block, it will validate the block, but it will skip the duplicate input check because of [the `false` parameter on this line](https://github.com/bitcoin/bitcoin/blob/v0.14.2/src/validation.cpp#L2883). So the transaction the miner made will pass this step of validation, and the other transaction validation steps, including input script validation, until it reaches [this loop](https://github.com/bitcoin/bitcoin/blob/v0.14.2/src/validation.cpp#L1328). In this loop, the inputs to the transaction are being marked as spent in the UTXO database. The first time the duplicated input is seen, it is marked as spent. But the second time it is seen, the coin is already marked as spent so `coins->vout[nPos].IsNull()` will be true. This means that it will go into [this if statement](https://github.com/bitcoin/bitcoin/blob/v0.14.2/src/validation.cpp#L1332) and subsequently hit the [`assert` statement that follows](https://github.com/bitcoin/bitcoin/blob/v0.14.2/src/validation.cpp#L1333). The assert causes the software to crash.

For Bitcoin Core 0.15.0 - 0.16.2, the behavior is different. This is due to the change in how the UTXO database is structured. Everything is largely the same until the [same loop](https://github.com/bitcoin/bitcoin/blob/v0.15.0/src/validation.cpp#L1183) is reached. Here, instead of returning whether the output was spent, `SpendCoin` actually returns whether the input *exists* in the database. So the first time, it will pass as expected, but the second time, instead of returning `false`, it still returns `true`.

Looking at [`SpendCoin`](https://github.com/bitcoin/bitcoin/blob/v0.15.0/src/coins.cpp#L101), you can see that it only returns false when it is unable to fetch the coin (object representing a UTXO) from the database. With the new database structure, this makes sense as the output should be removed from the database when it is spent. But, if you [look a few lines down](https://github.com/bitcoin/bitcoin/blob/v0.15.0/src/coins.cpp#L109), you see that it only deletes the coin when it is marked as `FRESH`. In the case the coin was `FRESH`, `SpendCoin` would delete the object on the first pass so the second pass the coin would not be found and thus it would return false. This triggers the [`assert` following the function call](https://github.com/bitcoin/bitcoin/blob/v0.15.0/src/validation.cpp#L1186) causing the node to shutdown.

If the coin was not `FRESH`, the coin object itself is not deleted, but its contents are cleared. This means that the second time the input is seen, if the coin was not `FRESH`, `SpendCoin` would still return true as the object still exists in memory, which means that it passes the assert that follows the `SpendCoin` (which caused the crash when the coin was not `FRESH`). Then validation continues as normal, and the output this transaction created is added to the UTXO database, which means that money that shouldn't exist now exists in the UTXO database.

So now the question is, when are UTXOs marked as `FRESH`? They are marked `FRESH` when they are added to the UTXO database. But the UTXO database is still only in memory (as a cache). When it is saved to disk, the entries in memory are then no longer marked as `FRESH`. This saving to disk happens after every block (as well as at other times, but that is not important).

Thus, if a miner has an output that was part of a transaction that has already confirmed, and he spends the output twice in the same transaction (so the transaction has two inputs that refer to the same output), and this transaction is not broadcast to the network but instead included in a block that he mines, he is able to create a new output that has twice the value of the output that he spent, thereby creating coins.

> Why can't this be done by a non-miner?

The reason that this cannot be done by a non-miner is because transactions that are received outside of blocks are still checked for duplicate inputs. The transaction will be rejected as invalid and not added to the node's mempool, so the transaction will never get into a block. It is only transactions with duplicate inputs that get into blocks that trigger this vulnerability, and thus only miners can do this as they must knowingly insert an invalid transaction into their block.

> Also, which forks are/were vulnerable?

Any fork whose software includes commit [`eecffe50efc3944d713c701fa375dacbf17fb7cf`](https://github.com/bitcoin/bitcoin/pull/9049/commits/eecffe50efc3944d713c701fa375dacbf17fb7cf). This would mean any software forked from or pulled in changes from Bitcoin Core after November 10th, 2016.

> **Comments:**
> **username_2**: Would a node that receives the block as a compact block check for the duplicate?  
> **username_1**: No, it would not. The transaction with the duplicate input would not be in the mempool so it would be sent along with the block (as part of a `blocktxn` message). Such transactions are not run through the mempool acceptance code. Instead the block is reconstructed and run through the normal block acceptance code.  
> **username_3**: Great write-up, thank you! It seems to me having a bool flag such as `fCheckDuplicateInputs` seems pretty unfortunate. It is quite easy to set it to an invalid value. I'm just wondering if we shouldn't be more explicit and verbose in those critical parts  

---

