Two blocks on the main chain carry a coinbase transaction that is byte-identical to one in an earlier block. When the later block connected, it overwrote the earlier one's index entry, and 50 BTF that had never been spent stopped being reachable. It happened twice.
duplicate coinbase c9e56f7131 at height 859 (first seen at height 841)
duplicate coinbase e635f4441f at height 860 (first seen at height 845)
100 BTF in total. Both belong to the same miner — the payout key is the same on all four blocks — so nobody's wallet was drained by a stranger. If you did not mine in that range, nothing of yours is affected.
This is not historical. Nothing in how a coinbase is built has changed since. It can happen again at any time, to any miner.
Why it is possible
main.cpp builds the coinbase as:
txNew.vin[0].scriptSig << nBits << ++bnExtraNonce;
txNew.vout[0].scriptPubKey << key.GetPubKey() << OP_CHECKSIG;
Nothing in there belongs to the block. No height, no previous hash, no timestamp. The transaction is a function of exactly three things: the current difficulty, a counter, and the payout key. nBits holds still for long stretches, and bnExtraNonce is a local that restarts at zero whenever the miner does — so the same value comes round again. Repeat all three and you have produced the same transaction, with the same hash, in a different block.
A transaction id is supposed to name one transaction. Here two blocks can name the same one, and the index keeps whichever arrived last.
One part is not explained yet. For these to collide, the same payout key had to be in use on both sides of a restarted counter, and the current code generates a fresh key per miner run. Either the key is not being regenerated when it should be, or something repeats it. That question is open and is being looked at separately. It does not block the fix below, which removes the possibility regardless of the answer.
What is being done
1. Put the block height in the coinbase (BIP34-style). Two coinbases at different heights can then never be identical, whatever happens to the counter or the key. Nothing validates coinbase content today and the script has 88 bytes of room, so this changes only what a miner produces — nodes that do not upgrade accept the blocks exactly as before. No coordination, no activation height.
2. Then enforce BIP30 — refuse a block containing a transaction id that already exists with unspent outputs — from an activation height announced in advance.
The order matters and the second step cannot come first. Enforcing BIP30 retroactively rejects block 859 and forks every node off the chain. That is measured, not assumed: a node with the check enabled and no activation height stops there and will not pass it.
About the coins
The 100 BTF are not recoverable. Undoing it would mean rewriting the chain from height 859, which costs more than it returns and breaks everyone who is synced.
How it was found
Porting the BIP30 check from later Bitcoin. Syncing the chain from genesis with it enabled rejected a real block, which is the whole reason a consensus change gets tested against the live chain before it ships rather than after.
Script execution limits from the same review are in #57 and are unaffected — that branch syncs all 3910 blocks with zero rejections.
Two blocks on the main chain carry a coinbase transaction that is byte-identical to one in an earlier block. When the later block connected, it overwrote the earlier one's index entry, and 50 BTF that had never been spent stopped being reachable. It happened twice.
100 BTF in total. Both belong to the same miner — the payout key is the same on all four blocks — so nobody's wallet was drained by a stranger. If you did not mine in that range, nothing of yours is affected.
This is not historical. Nothing in how a coinbase is built has changed since. It can happen again at any time, to any miner.
Why it is possible
main.cppbuilds the coinbase as:Nothing in there belongs to the block. No height, no previous hash, no timestamp. The transaction is a function of exactly three things: the current difficulty, a counter, and the payout key.
nBitsholds still for long stretches, andbnExtraNonceis a local that restarts at zero whenever the miner does — so the same value comes round again. Repeat all three and you have produced the same transaction, with the same hash, in a different block.A transaction id is supposed to name one transaction. Here two blocks can name the same one, and the index keeps whichever arrived last.
One part is not explained yet. For these to collide, the same payout key had to be in use on both sides of a restarted counter, and the current code generates a fresh key per miner run. Either the key is not being regenerated when it should be, or something repeats it. That question is open and is being looked at separately. It does not block the fix below, which removes the possibility regardless of the answer.
What is being done
1. Put the block height in the coinbase (BIP34-style). Two coinbases at different heights can then never be identical, whatever happens to the counter or the key. Nothing validates coinbase content today and the script has 88 bytes of room, so this changes only what a miner produces — nodes that do not upgrade accept the blocks exactly as before. No coordination, no activation height.
2. Then enforce BIP30 — refuse a block containing a transaction id that already exists with unspent outputs — from an activation height announced in advance.
The order matters and the second step cannot come first. Enforcing BIP30 retroactively rejects block 859 and forks every node off the chain. That is measured, not assumed: a node with the check enabled and no activation height stops there and will not pass it.
About the coins
The 100 BTF are not recoverable. Undoing it would mean rewriting the chain from height 859, which costs more than it returns and breaks everyone who is synced.
How it was found
Porting the BIP30 check from later Bitcoin. Syncing the chain from genesis with it enabled rejected a real block, which is the whole reason a consensus change gets tested against the live chain before it ships rather than after.
Script execution limits from the same review are in #57 and are unaffected — that branch syncs all 3910 blocks with zero rejections.