A 184 Billion Bitcoin Bug
In August 2010, something happened that you really wouldn't expect to see on the Bitcoin blockchain: a single transaction created over 184 billion BTC — out of thin air.
Block 74638, mined on August 15, 2010, included a transaction with outputs totaling 184,467,440,737.09551616 BTC. That’s 9,000× more than the 21 million BTC that should ever exist.
How was this even possible? In a nutshell: integer overflow. A classic bug, familiar to C/C++ developers, that made it into Bitcoin’s transaction validation logic.
Let’s walk through what happened, how it was fixed, and why this bug remains one of the most fascinating examples of how fragile even the most secure systems can be.
A Block That Shouldn’t Exist
Block 74638 (still available on the blockchain) contained a transaction with three outputs — two of roughly 92.2 billion BTC each and a much smaller third output — totaling about 184 billion BTC. It passed all the internal validation checks in Bitcoin Core at the time.
This should have been impossible. According to the protocol, the sum of all outputs must not exceed the sum of the inputs. But due to an integer overflow bug, that check was bypassed.
How? The numbers got too big — and instead of throwing an error, they wrapped around.
What Is an Integer Overflow?
To understand how this happened, you need to know how computers represent numbers.
A 32-bit signed integer can store values from –2,147,483,648 to 2,147,483,647. If a calculation produces a result outside this range, it wraps around — much like a clock going back to 1 aster 12.
The outcome can sometimes be catastrophic.
Let’s say we’re writing code to simulate a bank wire transfer. You want to make sure users can’t transfer more money than they have in their balance.
#include <stdio.h>
#include <stdbool.h>
#include <limits.h>
bool wire_transfer(unsigned balance, unsigned amount) {
unsigned new_balance = balance - amount; // wraps modulo 2^32
if ((int)new_balance < 0) { // vulnerable check
puts("deny: insufficient funds");
return false;
}
printf("approve: new balance = %u\n", new_balance);
return true;
}
int main(void) {
unsigned balance = 100u;
unsigned amount = 2147483647u;
wire_transfer(balance, amount);
}
You implement a check:
If the transfer amount is more than the balance, deny the transaction.
Simple enough, right?
But now imagine a user submits a very large positive amount — so large that doing balance - amount
in an unsigned type underflows and wraps around to a huge value.
Let’s say:
- The account balance is 100
- The transfer amount is 2,147,483,647 (the max 32-bit signed integer, used here just as a large number)
Internally, the system computes:
new_balance = balance - amount
Which becomes:
new_balance = 100 - 2,147,483,647
Because new_balance
is an unsigned value, the subtraction wraps modulo 2³² and yields a very large positive number instead of anything negative. The naive test if ((int)new_balance < 0)
therefore never sees a straightforward “negative” result and can be bypassed, so the transfer is approved even though the user lacked funds.
Now your code thinks:
“Hey, the new balance is still positive — go ahead and approve the transfer.”
And the user just got away with transferring more than they actually own. This is a overly simplified echo of Bitcoin’s 2010 “value overflow” incident:
- A transaction’s outputs summed to more than the inputs
- The sum overflowed, wrapping to a small-looking total
- Nodes accepted the transaction as valid
This way, a single transaction that created ~184 billion BTC before the network was patched and the chain was rolled back.
Enough pseudocode, make it real!
So, CTransaction
was (and still is) the class that rappresents a basic transaction that gets broadcasted to the Bitcoin network, and ConnectInputs
was the vulnerable method, responsible for the transaction verification.
Before the patch, ConnectInputs()
added each referenced output value into a running total (nValueIn
) without checking that the single output or the running total stayed within sane monetary bounds. That made it possible for badly formed transactions to make the mathematical sum of outputs exceed the allowed money supply, overflow the accumulator, and appear as a small wrapped value to the later checks.
nValueIn += txPrev.vout[prevout.n].nValue;
The commit adds three concrete checks inside ConnectInputs() when iterating inputs:
// Check for negative or overflow input values
if (txPrev.vout[prevout.n].nValue < 0)
return error("ConnectInputs() : txin.nValue negative");
if (txPrev.vout[prevout.n].nValue > MAX_MONEY)
return error("ConnectInputs() : txin.nValue too high");
if (nValueIn > MAX_MONEY)
return error("ConnectInputs() : txin total too high");
- Per-output non-negativity — reject if an input’s referenced output value is negative
- Per-output upper bound — reject if that single output value is greater than MAX_MONEY
- Running-total bound — after adding the output to nValueIn, reject if nValueIn > MAX_MONEY
All three checks are performed while tallying inputs so an attacker can’t rely on arithmetic wraparound to hide a too-large total. The patch prevents that attack in two ways:
-
Limit each output — by rejecting any single
txout.nValue
that exceedsMAX_MONEY
, you stop obviously bogus outputs from ever contributing to a sum that could overflow into a plausibly-small value. -
Limit the running total — by checking
nValueOut
after each addition and rejecting when it goes aboveMAX_MONEY
, the code ensures the cumulative sum never exceeds the allowed money supply. Because the allowed maximum (MAX_MONEY
) is orders of magnitude below the overflow threshold of theint64
accumulator used, this check prevents a wrapped value from ever being treated as valid.
Implementation note: the code uses an
int64
accumulator, initializes it to zero, and only adds validated, non-negative per-output values. With the per-output bound andMAX_MONEY
well belowINT64_MAX
, accidental signed overflow of the accumulator is safely avoided.
Where in the code the checks landed
CheckTransaction()
(transaction-level validation) — added per-output checks and a runningnValueOut
limit test.ConnectInputs()
(when a transaction is applied in a block) — added checks on input values and a check that total inputs don’t exceedMAX_MONEY
.
As far as I can tell, this commit also marks the first appearance of MAX_MONEY
in the Bitcoin codebase — an interesting detail that adds yet another layer of mystery to Bitcoin’s famously limited supply.
Security in Bitcoin: Code vs Consensus
After the discovery of the vulnerability, a patch was rolled out by Satoshi Nakamoto himself just a few hours later, and the forged block was invalidated once miners upgraded to the new Bitcoin client.
But what does this really mean? Isn’t the blockchain immutable?
Yes — but immutability in Bitcoin comes from consensus! This means that once the updated nodes started rejecting the hacked block, and because the Bitcoin chain is programmed to honor the longest valid chain, the majority of miners began building on top of the previous valid block. As a result, the compromised block was discarded from the canonical history, even though it had initially looked “valid” under the old code.
This incident teaches us a powerful lesson about Bitcoin’s security: consensus validation sits on top of code security itself.
Thanks for reading,
Francesco