TL;DR
At 15:44:01 UTC on August 7, 2026, block 4,487,776 arrived on Ravencoin carrying no ProgPoW mining work behind it, and the network accepted it. The nHeight field inside a KAWPOW block header was never compared against the block's actual position in the chain, and that field decides whether a node runs the full ProgPoW check or a cheap shortcut reserved for old blocks below a checkpoint. A block that claimed to be old got graded as old, for the price of a plain hash search. Nobody has published a theft figure, and the coinbase take was small. The damage was structural: 96 forged blocks in a 2,089-block sample, nodes that could no longer restart or resync, Upbit and other venues freezing RVN transfers, and a roughly three-day reorganization that two mining pools chose over the project's objection. The whole failure is one branch of code taking a block's own word for whether it needed to be checked.
The nHeight Field Inside a KAWPOW Header
KAWPOW is Ravencoin's proof-of-work algorithm, a ProgPoW variant adopted in 2020 to keep GPU mining economical and ASIC mining awkward. ProgPoW is memory-hard: to hash a candidate block you first build a large dataset, the DAG, then walk it in a pattern that changes on a schedule. Mining cost is dominated by that memory traffic. You cannot shrink it into cheap silicon.
The schedule is where nHeight comes in. Ravencoin's header carries the block's own height as a field, and that field is not decorative. It feeds the proof-of-work hash, and it selects both the DAG epoch and the ProgPoW period that sets the access pattern. A miner needs the height to mine at all. Bitcoin puts height in the coinbase transaction, Ravencoin lifted it into the header, and so far this is ordinary. The problem is in the code that decides whether a block is real.
Why Did a Checkpoint Shortcut Become a Consensus Bypass?
Checkpoints are a sync optimization. A node with hardcoded checkpoints already knows the correct block hash at, say, height 1,000,000, so re-verifying proof of work below it is wasted work. Ravencoin's version of that shortcut is still sitting in upstream master:
// If we are checking a KAWPOW block below a know checkpoint height. We can validate the proof of work using the mix_hash
if (fCheckPOW && block.nTime >= nKAWPOWActivationTime) {
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(GetParams().Checkpoints());
if (fCheckPOW && pcheckpoint && block.nHeight <= (uint32_t)pcheckpoint->nHeight) {
if (!CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) {
return state.DoS(/* ... */ "proof of work failed with mix_hash only check");
}
return true; // <-- returns before the real checks
}
}
uint256 mix_hash;
// Check proof of work matches claimed amount
if (fCheckPOW && !CheckProofOfWork(block.GetHashFull(mix_hash), block.nBits, consensusParams)) { /* ... */ }
if (fCheckPOW && block.nTime >= nKAWPOWActivationTime) {
if (mix_hash != block.mix_hash) { /* ... */ }
}Read the branch condition: block.nHeight <= pcheckpoint->nHeight. The height being tested is the value the block asserts about itself. Nothing in this function, and nothing that ran before it, checks that assertion against where the block actually sits.
Take the branch and two checks disappear. GetHashFull(mix_hash), which recomputes the real ProgPoW mix and is the expensive part, is never called, and the comparison of that mix against the supplied mix_hash is never reached. What survives is a difficulty check over the plain header, with the attacker's mix_hash taken on faith as bytes.
A hash search with no DAG, no memory-hard step, no GPU. Same nominal difficulty, a fraction of the cost. Any block could opt into it by writing a small number into one field.
This is CWE-807, reliance on untrusted inputs in a security decision, in about as pure a form as the class gets. The decision is "does this block need real proof of work," and the input driving it comes from the party being checked. The comment above it carries its own small tell: "below a know checkpoint height," typo included, sitting there since KAWPOW activation in 2020 (upstream master was last touched in May 2024).
The Attack, Step by Step
| # | Step | Mechanism |
|---|---|---|
| 1 | Pick a position | Any current chain tip. Height 4,487,776 was first, mined 2026-08-07 15:44:01 UTC. |
| 2 | Write a false nHeight | Any value at or below the last hardcoded checkpoint. |
| 3 | Search for a cheap hash | Grind the plain header until block.GetHash() meets nBits. No DAG, no ProgPoW walk. |
| 4 | Attach any mix_hash | The fast path never recomputes the mix, so the field only has to exist. |
| 5 | Broadcast | CheckBlockHeader takes the checkpoint branch and returns true. Block accepted. |
| 6 | Collect | The coinbase pays at the current subsidy, and the forged block becomes an ancestor of honest blocks. |
The path stops being linear once the block lands, because the same field poisons three systems at once.
The Damage Beyond the Forged Blocks
The subtler damage came from the gap between the height a block declares and the height the node stores.
Nodes died on restart. The block index records the chain-derived height, not the header's claim. On reload the node recomputes the hash from the stored value, gets a different one, fails the proof-of-work check, and LoadBlockIndexGuts aborts the whole load with Error loading block database. The data on disk is intact. The message is a false diagnosis, the worst kind of outage: an operator reads database corruption and starts deleting things.
Catch-up sync became impossible. Headers are served rebuilt from the index, so an affected header goes out with a hash that does not match the one its own child points at. The receiving node reports non-continuous headers sequence, scores the peer down, and bans it. With forged blocks arriving at roughly one in twenty-two, no headers batch could pass, and a node that fell behind would work through its peer list banning honest peers.
How Much Was Actually Taken?
No published figure exists, and we are not going to invent one.
The coinbase arithmetic is small. GetBlockSubsidy starts at 5,000 RVN and halves every 2,100,000 blocks, so block 4,487,776 sits after two halvings at 1,250 RVN. 2Miners measured 96 forged blocks between heights 4,489,527 and 4,491,615: 120,000 RVN, roughly $350 at the post-crash price.
Note what that window is. It starts about 1,750 blocks above the first forged block, so the 96 count is a mid-campaign rate sample rather than a running total. Extrapolating that rate across four days puts the take in the low four figures, which is our arithmetic and not a measurement.
The monetizable path was never the block reward. It sits in the release notes as a warning to exchanges: a deposit is unrecoverable when the coins it spends trace back to a coinbase above block 4,487,775, because those coins do not exist on the clean chain. Mine free blocks, let the coinbase mature, deposit, sell, and the reorg erases the coins after the proceeds have left. Whether anyone ran that play is not in the public record, and it cannot be checked from an explorer, since the explorers serve the clean chain and the forged coinbase outputs are not on it. We flag the mechanism because it is written down, not because we can show it was used.
The collateral is measurable. RVN fell to roughly $0.0029 at a market capitalization near $46 million to $47 million, on reported daily declines of 17% to 20.4% depending on the snapshot, then printed a record low of $0.002754 on August 12. Upbit halted deposits and withdrawals on August 10 while leaving trading open. Reports name different second venues, Bitvavo in Decrypt and CoinDesk, Bitget in crypto.news, and both may be right since the suspensions landed hours apart.
Reorg depth is the other disputed number, and the disagreement is just clocks. Coverage on August 10 called the rollback three days, CoinDesk on August 11 called it four. Both are right for their date, because the depth grows every hour the clean chain has not overtaken the exploited one.
The clean chain is checkable, so we checked it. On the branch the public Ravencoin blockbook serves, block 4,487,775 is timestamped 2026-08-07 15:43:57 UTC, four seconds before the first forged block. The block now at 4,487,776 is a replacement, mined 2026-08-10 14:09:42 UTC, about two hours after 2Miners published its patch. Its coinbase pays exactly 1,250 RVN, settling the subsidy arithmetic above, to an address with hundreds of thousands of prior transactions: a pool payout address. As of 2026-08-12 01:12 UTC that chain stood at height 4,490,115, roughly 1,500 blocks short of the exploited branch's known extent. By 2026-08-14 00:58 UTC it had reached height 4,492,958, past the last measured forged block at 4,491,615. No all-clear and no exchange resumption notice had been published as of the same date.
The Fix Came From a Mining Pool
Because nobody else shipped one.
2Miners released v4.6.1.1-hf1 on August 10 with a blunt explanation: upstream development is inactive, no fix had been published, and waiting was not an option. The build rejects height-mismatched blocks from 4,487,776 onward, checkpoints 4,487,775, and rebuilds the chainstate automatically. A follow-up v4.8.0 on August 11 rebased that work onto upstream's development branch.
The project's own response was a release-page note dated August 13 from core contributor Hans Schmidt, pointing operators at the mining pool's binaries and stating that no single core build yet patched both open bugs. Schmidt frames the actor as a self-titled security researcher who exploited the mining bug on mainnet and disclosed only after a second attacker had copied the technique, scare quotes his.
That second open bug shows why patch adoption is a security control rather than a chore. An asset transfer quantity overflow lets anyone holding a single unit of an asset mint 2^64 minus that quantity copies. A fix existed, deployed as a BIP9 soft fork on bit 11, and it could not activate: most of the hash rate ran 4.6.1, a build that does not know bit 11 exists and signalled nothing. The patch was written, published, and inert (the network voted by not upgrading).
The pools also overruled the project on recovery. Ravencoin asked for a more recent recovery point to reduce the impact on users, services and exchanges. 2Miners and RavenMiner, holding most of the hash rate, declined and rewound to the first forged block instead.
Ravencoin's Third Exploited Consensus Failure
The lineage matters because the class repeats.
In September 2018 a double-spend attack rewrote 22 blocks, answered with a cap on reorganization depth. In May 2020 an attacker found that a reissue_asset output could carry RVN value as well as asset quantity, and injected roughly 500,000 RVN every two hours for seven weeks. The CryptoScope team, who found it, put the total at about 301.8 million RVN, near 1.4% of the 21 billion supply cap, fixed at block 1,304,352 on July 4, 2020. Much of the press reported 315 million. We use 301.8 million, from the people who reconstructed the transactions.
The 2020 bug was an output field carrying a value the validator did not expect. This one is a header field carrying a value the validator never questioned.
Why This Generalizes Past a $47 Million Chain
Two patterns here have nothing to do with Ravencoin.
Self-declared fields become security inputs by accident. nHeight started as a mining convenience: the miner needs the height for the DAG epoch, so put it in the header. It became a security input the moment a validation branch read it, and that transition is invisible in a diff, because the field was already there and the branch looks like an optimization. The review question is not "is this field validated" but "what decisions read it, and does the party who wrote it benefit from any of them." Forks also inherit assumptions along with code, which is how reused infrastructure carries reused failure modes.
Fast paths are where invariants go to die. The checkpoint shortcut existed to skip expensive work on blocks whose validity was settled by other means. That reasoning was sound for its case and silently wrong everywhere else, because the guard testing "is this that case" asked the block. Same shape as the Verus bridge state-root parser divergence: a cheaper path that was supposed to agree with the expensive one, and did not.
We usually argue that audits miss what happens after deploy: the approval left standing, the quorum nobody set. This failure runs the other way, and deserves naming honestly rather than reaching for that line. It was a code bug in consensus validation, in an open-source file, reachable by reading twelve lines. A competent review of CheckBlockHeader finds it. Runtime monitoring would not have prevented it, so we will not claim otherwise.
Where monitoring earns its place is the gap before anyone reacted. First forged block at 15:44 UTC on August 7, first patch published 12:04 UTC on August 10: two days and twenty hours, and the public record does not name who noticed. No independent researcher is credited, and the upstream note says the disclosure came from the actor who ran the exploit, only after a second attacker had copied it.
The signal itself was loud: blocks arriving with heights that did not match their position, roughly one in twenty-two. Every exchange, custodian, and bridge with RVN exposure kept crediting deposits through it. That delay in noticing the chain had stopped being the chain is what runtime monitoring closes.
Operator Takeaways
- Run your own validating node for every chain you credit deposits on. A pool's chain tip and an explorer's API are both someone else's opinion. On Ravencoin the pools and the project disagreed about which chain was real, and a node you control is how you learn that on hour one instead of day three.
- Alert on divergence, not just on liveness. "Is my node up" returned green through this entire incident, right up to the first restart. Page on your tip disagreeing with a second independent node, on reorg depth crossing a threshold, and on header-validation rejections at all.
- Treat every self-declared header or transaction field as attacker-controlled, and review the optimizations that read it. Checkpoint shortcuts, cached results, and "we already validated this" flags all carry a precondition. Write it down, then prove the guard establishes it instead of asking the input.
- Make patch adoption a monitored metric. The asset overflow fix could not activate because the hash rate ran a build unable to signal it. Track the versions your peers report, and know your soft-fork signalling percentage before you need it.
- Save raw transactions before you follow a reorg. Export raw hex for every affected deposit and withdrawal first. A transaction that is not in the new chain and not in your mempool is gone from a node that has already reorganized.
- Raise confirmation requirements while a chain's consensus is in dispute. Resuming deposits before your node has followed the clean chain past the divergence is how you inherit someone else's reorg.
Frequently Asked Questions
Was RVN stolen in the Ravencoin exploit? No theft figure has been published, and the forged coinbase was worth a few hundred dollars in the measured sample. The exposure is to exchange deposits confirmed after block 4,487,775, which the reorg reverses.
What exactly was the Ravencoin KAWPOW vulnerability?
The nHeight field in a KAWPOW header was never checked against the block's real position in the chain. CheckBlockHeader uses that self-reported height to decide whether to skip full ProgPoW verification below a checkpoint, so a block could claim to be old, take the cheap path, and be accepted with no memory-hard work behind it.
Which Ravencoin version fixes it? 2Miners' v4.6.1.1-hf1, superseded by v4.8.0, which also carries the asset transfer overflow fix. Both reject height-mismatched blocks from 4,487,776 and checkpoint 4,487,775. The first start replays the chain, about 2.5 hours on a 32-core machine.
Does this affect other Bitcoin forks or ProgPoW chains? Not automatically, since the bug needs both a self-declared height in the header and a validation branch reading it. Any chain with a checkpoint fast path should confirm the guard derives from chain state, not from the block being checked.
Sources / References
- 2Miners, Ravencoin 4.6.1.1-hf1 release notes
- 2Miners, Ravencoin 4.8.0 release notes
- RavenProject/Ravencoin,
CheckBlockHeader - RavenProject/Ravencoin,
GetBlockSubsidy - RavenProject/Ravencoin,
masterHEAD - RavenProject/Ravencoin, core release-page note on the two open bugs (Hans Schmidt, Aug 13)
- Project Raven network notice
- CoinDesk, Ravencoin could roll back four days of transactions
- Decrypt, Ravencoin crashes as critical exploit threatens rollback
- crypto.news, Ravencoin falls 19% as consensus flaw splits network
- cryip.co, Ravencoin's fix is coming from a mining pool
- CryptoScope, The anatomy of Ravencoin exploit finding
- CryptoPotato, Hackers mint 315 million fake RVN
- CWE-807: Reliance on Untrusted Inputs in a Security Decision



