Liquid Network

How One Cache Bug Drained 4000 BTC From Liquid Network

19 min read
MH
Written by Mohamed Habbat

Four thousand bitcoin left Liquid. Nobody stole a key.

I work in the crypto self-custody space and I have watched people treat L-BTC as bitcoin with a faster block time. On 6 September 2026 the difference became a number. 4,002.67 BTC left the Liquid Federation's Bitcoin wallet under a valid 11-of-15 multisig signature, because the sidechain's own software accepted L-BTC that nobody had deposited.

TL;DR

Elements, the Bitcoin Core fork that runs Liquid, remembers which rangeproofs, the proofs that a hidden amount is not negative, it has already verified. The label it used for that memory left out two things the proof depends on: the asset and the output script. Someone primed the cache with a harmless transaction, then reused the same proof bytes one block later where the hidden amount was invalid. Nodes holding the cached label accepted the block, nodes without it rejected it, and the federation's signing nodes were on the accepting side. They paid out 4,002.67 BTC (about USD 319 million at USD 79,807 per BTC). Anonymous actors calling themselves whitehats returned 3,400 BTC about 26 hours later and still hold 598.5 BTC. Bitcoin mainnet and Lightning were never affected. Liquid is paused pending a coordinated restart. Elements 23.3.4, tagged as a pre-release on 9 September, carries the fix plus a second patch that hardens the cache keys. There is still no post-mortem.

4,002 BTC left the federation wallet on 6 September 2026

The Liquid Federation keeps the bitcoin behind L-BTC in one reused multisig address on Bitcoin mainnet. It held 4,205.29 BTC just before 14:28 UTC on 6 September and 197.47 BTC by that evening (federation wallet on mempool.space). The payout transaction sent 3,996.01834922 BTC to one outside address, 2.65138358 BTC to a second, and 3.99601658 BTC to a third, unlinked, address (Bitcoin block 965,783).

At USD 79,807 per BTC, the CoinGecko price at 14:00 UTC that day, the outflow was worth about USD 319.4 million. The liquid.network explorer counted 4,205.02 L-BTC in circulation at the last block before the attack, so for about 26 hours the reserve covered 4.7 percent of the L-BTC it was meant to back. The federation transaction was an ordinary multisig spend, valid under Bitcoin's rules, and the bitcoin price barely moved (CoinDesk).

The bug lived in Elements, not in Bitcoin Core

Liquid is a sidechain, a separate blockchain that runs next to Bitcoin, holds bitcoin in escrow, and issues a matching token, L-BTC, on its own ledger. Blockstream launched it in September 2018 on Elements, an open-source fork of the Bitcoin Core codebase with features Bitcoin does not have: hidden amounts, issued assets, and blocks signed by a fixed group instead of mined (Liquid technical overview).

That group is the federation. Fifteen members each run a server, called a functionary, plus a hardware key module. The functionaries take turns proposing a block every minute, and a block counts once 11 of the 15 have signed it. The same 11-of-15 threshold controls the Bitcoin wallet, because each hardware module holds one of the 15 keys (what a functionary is). Blockstream writes the software. The members run it.

Bitcoin Core has no hidden amounts, no rangeproofs, and no cache of verified rangeproofs. The bug was consensus code, the rules every node uses to decide whether a block is valid, and it was Liquid's consensus, not Bitcoin's.

How the Liquid peg works

A peg-in moves bitcoin onto Liquid. You send BTC to a federation-controlled address, wait 102 Bitcoin confirmations, and claim the same amount of L-BTC on the sidechain (peg-in and peg-out docs). A peg-out is the reverse. You burn L-BTC on Liquid, and after two Liquid confirmations (what a Liquid peg-out is) the functionaries' wallet software, called the watchman, pays BTC from the multisig in a batch round of 11 to 35 minutes, per the same peg-in and peg-out docs.

Not everyone can peg out. The hardware modules only sign a payout to an address derived from a Peg-out Authorization Key, a PAK, and only federation members hold one. Ordinary users peg out through a member such as an exchange or SideSwap, which burns the L-BTC from its own wallet with its own PAK proof and points the BTC at the customer's address. PAK was never designed to check whether the L-BTC being burned was real.

Hidden amounts and one worked example

Liquid hides how much each output holds. Instead of a number, an output carries a commitment, a locked box produced with a secret blinding factor that still obeys addition (confidential transactions docs). A verifier checks that inputs minus outputs equal zero without learning any amount.

Say Alice spends one input of 5 L-BTC, pays Bob 3, and keeps 2 as change. With blinding factors 7, 4, and 3, the verifier finds that 5 minus 3 minus 2 and 7 minus 4 minus 3 both equal zero. It never saw 5, 3, or 2.

Now Alice cheats with outputs of 1,003 and -998. The sum is still 5, so the balance check passes. On the curve, -998 is a gigantic positive number nobody will ever spend, and Alice has minted 998 L-BTC from 5. The rangeproof stops this. Every output carries one, and it proves the hidden value lies between 0 and 2 to the power of 64, which -998 cannot satisfy (Greg Maxwell's original design note). A node that skips the rangeproof, or wrongly remembers an earlier one as passed, accepts the minting. The real attack did it with about 4,000 L-BTC.

A cache label that forgot two things

Rangeproofs are slow to check, so Elements caches them. After a proof passes, the node stores a label that means "seen this, it passed" and skips the check next time. That label is the cache key. A rangeproof is only meaningful together with the asset it commits to and the output script it is attached to, but the cache key was a hash of two fields only, the proof bytes and the value commitment. It left out the asset and the script.

At 13:52:10 UTC, two transactions in Liquid block 4,050,335 carried a genuine rangeproof that passed on every node that saw them. That was the priming. At 13:53:10 UTC, block 4,050,336 carried a transaction that reused the same proof bytes and commitment, now attached to an OP_RETURN output, a data-only output nobody can spend, where the hidden value was invalid (root-cause gist). Primed nodes found the label and skipped the check. Value appeared from nothing.

This mechanism is independent analysis, corroborated by several engineers including Cashu developer callebtc in a plain-language thread he flags as simplified. Blockstream has published no post-mortem.

How the Liquid cache-replay attack worked A harmless priming transaction gets its range proof checked and cached as valid under a key that ignores asset and script; the attack transaction reuses the same proof bytes under L-BTC with a hidden negative amount, so a node with a cache hit accepts it without checking and the federation pays out 3,996 BTC in a peg-out, while a node without the cached entry runs the real check, rejects the block, and stops at block 4050335. YES NO Priming transaction proof P for amount box C under asset A, script S Node checks the proof slow range check runs result: valid Cache stores result key = P + C only asset and script ignored Attack transaction same P and C, now under L-BTC hidden negative balances a big positive Cache hit? seen these bytes? Real check runs, proof invalid asset and script do not match Accepted without checking cache says: seen it, valid unbacked L-BTC now exists Block rejected node stuck at block 4050335 3,996 BTC paid by the federation peg-out: fake L-BTC swapped for real BTC LEGEND Step Decision Outcome Focal

Primed nodes accepted the block and fresh nodes rejected it

The cache lives in memory and is salted per process. A node that had seen the primer transactions held the label. A node without that entry ran the real rangeproof check, failed it, and rejected block 4,050,336. Bitcoin Core developer Antoine Poinsot spotted the split at 20:23 UTC: "Looks like Liquid block 4'050'336 was rejected by @mempool but accepted by @Blockstream" (Poinsot). The liquid.network explorer, run by mempool.space, is still stuck at height 4,050,335. The blockstream.info explorer accepted it and followed the federation's chain to height 4,051,232, where the last block carries a timestamp of 04:49 UTC on 7 September. Nothing has been produced since.

Consensus means every node applies the same rules and reaches the same answer. Here the answer depended on what each node had in memory. The federation's signing nodes were on the accepting side, so they matured the peg-out and the watchmen paid real BTC. Running your own node is the only vote you get in a dispute like this; the Swiss node setup guide covers the Bitcoin side.

One bug or two

The gist by @1440000bytes, an AI-assisted report, is the most detailed public write-up and the most contested. Its first six revisions named the two-field cache key as the whole bug and commit c26d719c29 as the fix. Revision 7, on 7 September, reversed course. It claims the fix's new four-field key has no length separators, so two different inputs can hash to the same label, and that the accepting nodes were running that unreleased patched code. Simanta Gautam of Alpen Labs called the gist's framing of c26d719 as the fix "wrong & misleading" (Gautam) without elaborating. mempool.space developer mononaut wrote that "apparently the Liquid functionaries were running this bleeding-edge code" (mononaut).

On 8 September Hambly opened PR #1600, which switches both proof caches to length-prefixed hashing so that "distinct argument tuples with byte-identical raw concatenations no longer collide to the same cache key" and adds a -norangeproofcache option (PR #1600). That confirms the missing separators were a real weakness worth closing. It does not confirm that anyone exploited them, and Blockstream has still not said which code accepted the block. Treat "the fix is what got exploited" as an open question.

A fix that sat public for five days before any release

Byron Hambly of Blockstream authored the fix on 3 August 2026. It landed on the master branch on 1 September inside PR #1592, a batch of small cleanups described as "Fixes a number of small issues picked up during LLM scans" (PR #1592). Pablo Greco opened PR #1599 on 4 September to carry it into the elements-23.3.x release branch "in preparation for 23.3.4rc2" (PR #1599). That backport landed at 17:21 UTC on 6 September, just under three hours after the payout and an hour before the first public alert. Until 9 September the latest release was elements-23.3.3 from 13 April 2026, and no release-candidate tag contained the fix. Elements 23.3.4 was published at 03:14 UTC on 9 September, flagged as a pre-release, with PR #1599 and the PR #1600 hardening in its changelog (Elements 23.3.4).

So a consensus-critical fix sat on public branches for five days with no release to upgrade to. Bitcoin Core withholds details of medium and high severity bugs until two weeks after the last affected release goes end of life (Bitcoin Core security advisories). Whether the attackers read the diff is suspected, not proven; their own message "the chain is under risk at latest commit right now" reads like someone with the repository open. The fairer criticism is that the fix rode in as a minor cleanup and nobody may have flagged it as consensus-critical at merge time. The CVE-2023-50428 post covers how Bitcoin Core draws its own bug-versus-policy line.

For engineers

Commit c26d719c29a40da280a825b25657e9c3d8bc7d99, "fix: range proof cache bind to asset and scriptpubkey", Byron Hambly, authored 2026-08-03, committed to master 2026-09-01 via PR #1592. Eight lines across src/script/sigcache.cpp (+3, -3) and src/script/sigcache.h (+1, -1). ComputeEntryRangeProof previously hashed the proof bytes then the value commitment. It now also writes asset_commitment and scriptPubKey, matching what secp256k1_rangeproof_verify already binds. The two-field key dates from commit 0b5066143d (2019-03-19), so every release through elements-23.3.3 carries it. Cherry-picks: 6253d7e103 on elements-23.x (PR #1595, merged 2026-09-03) and 212c43f475 on elements-23.3.x (PR #1599, merged 2026-09-06 17:21 UTC). The four fields are written with no length delimiters (commit c26d719c29). PR #1600, also by Hambly, opened 17:19 UTC and merged 19:06 UTC on 8 September on elements-23.3.x: the rangeproof and surjection-proof cache hashers move from raw CSHA256 concatenation to CHashWriter, which length-prefixes every field, both caches keep per-process salted midstates with distinct domain separators, unit tests cover field-boundary collisions and script sensitivity, and a -norangeproofcache startup flag disables the cache without recompiling. The same PR bumps the version to 23.3.4.

Timeline in UTC from dry run to return

Times are block-header timestamps. One analyst's gist puts the Liquid wall clock about 73 minutes behind the headers; measured peg-out latencies fit the header times, so I use them (defiprime timeline).

Time (UTC)WhereWhat happened
2026-08-03 10:53gitByron Hambly authors the fix
2026-09-04 19:36gitPR #1599 opened to backport the fix
2026-09-06 13:52:10Liquid 4,050,335Primer transactions carry the rangeproof to be replayed
2026-09-06 13:53:10Liquid 4,050,336Attack transaction. Nodes split on the block
2026-09-06 14:01:10 and 14:06:10Liquid 4,050,344 and 4,050,349Peg-outs of 2.65138358 and 3,996.01834922 L-BTC through SideSwap
2026-09-06 14:28:56Bitcoin 965,783Federation pays 4,002.67 BTC to outside addresses
2026-09-06 17:21gitPR #1599 merged into elements-23.3.x
2026-09-06 18:30:10Bitcoin 965,818Attackers: we are whitehats, contact us on chain
2026-09-06 20:25:20XLiquid statement. Bridge nodes disabled, sidechain paused
2026-09-07 03:30:05Bitcoin 965,875Attackers: fix the bug first, patch every node
2026-09-07 09:41:26Bitcoin 965,912Blockstream, PGP-signed: bridge nodes patched, safe to return
2026-09-07 16:09:25Bitcoin 965,9503,400 BTC returned to the federation wallet
2026-09-08 19:06gitPR #1600 merged: length-prefixed cache keys, -norangeproofcache flag
2026-09-09 03:14gitElements 23.3.4 published as a pre-release with the fix

Who did it and what SideSwap signed

Nobody knows who did it. The only self-description is the OP_RETURN text "we are whitehats. contact us on chain" (Bitcoin block 965,818). They tested with a 2.65 BTC peg-out, took the large one, and opened negotiations about four hours later. Ledger CTO Charles Guillemet was not persuaded: "If this was ever a negotiated reward under an encrypted contract signed on-chain, it looks more like extortion than white-hat hacking!" (Guillemet).

SideSwap processed the peg-out. At 14:05 UTC a customer sent 4,000 L-BTC to its peg-out service, the service burned the L-BTC with a valid peg-out authorisation, and at 14:28 UTC the federation paid 3,996 BTC to the customer's Bitcoin address. "Our service had no way to tell those coins from any other L-BTC" (SideSwap statement). Liquid's own statement confirms the SideSwap PAK "was not compromised, nor were any others" (Liquid statement).

The money trail and the on-chain negotiation

In the same Bitcoin block as the payout, 3,995.99999857 BTC went to a consolidation address the attackers had funded with 2.4975 BTC half an hour earlier (consolidation address). The 2.65 BTC leg moved separately at 14:46 to a different address and never joined the consolidation address, which held 3,998.4975 BTC after the payout block: the 3,996 BTC leg plus its own 2.4975 BTC seed.

Then the dialogue, each message an OP_RETURN carrying 1,000 satoshis to the other side. Block 965,818 at 18:30:10: "we are whitehats. contact us on chain". Block 965,822 at 19:31:47, from Blockstream: "Please contact security@blockstream.com". Block 965,869 at 02:20:18 on 7 September: "sending most back to bc1qdlld6..., is that ok". Block 965,875 at 03:30:05: "Please fix the bug first. The chain is under risk at latest commit right now. Make sure every node is patched. Then we will transfer the money back safely after confirming the fix." Blockstream's PGP-signed "Yes, thank you." sits in the same block. Block 965,912 at 09:41:26, from Blockstream, PGP-signed: "Bridge nodes are patched, safe to return the funds." Block 965,930 at 12:43:33: "plz confirm again that we are sending the coins back to bc1qdlld6...". Block 965,950 at 16:09:25: 15 inputs, 3,400 BTC to the federation wallet, 598.49955894 BTC back to the consolidation address, 1,768 satoshis in fees (return transaction). Sjors Provoost published a chronology of the exchange with signature checks (Provoost).

After the return, Blockstream sent six more PGP-signed, encrypted messages (four in block 965,956, one each in blocks 965,962 and 965,967) and the actors answered with two encrypted messages in block 965,962, so the talks about the remaining coins continue in private.

3,400 BTC came back and 598 BTC did not

At the reference price, 3,400 BTC is about USD 271.3 million and 598.49955894 BTC is about USD 47.8 million. The federation wallet now holds 3,597.47 BTC against 4,205.02 L-BTC counted at the last pre-attack block, 85.5 percent by my arithmetic on the two explorer counters (liquid.network reserves API).

No bounty has been agreed in any signed message from either side. Whether Blockstream, the federation, SideSwap, or L-BTC holders absorb the gap is not announced. The Block reported the attackers' conditional offer (The Block) and Decrypt reported the return (Decrypt). For scale, Poly Network's attacker returned everything in 15 days in 2021; 3,400 BTC in about 26 hours is the fastest large return I know of.

What this means for federated sidechains

The 11-of-15 federation did what it was designed to do. No key leaked and the PAK whitelist held. What failed was the validation code every functionary ran. Che Kohler of The Bitcoin Manual put the critic's case: "you're not just trusting the honesty of fifteen vetted institutions; you're trusting the correctness of every line of code those institutions collectively rely on to decide what counts as a legitimate transaction" (Kohler). Samson Mow of JAN3 put the defender's case: "The vulnerability is a node level issue and is not related to PAKs or HSMs" (Mow).

The honesty assumption held and the correctness assumption did not, and a software monoculture means one bug decides for all fifteen. Blockstream's write-up of the 2020 Liquid timelock incident already blamed "external challenges in coordinating the updates on the functionary servers" for a delayed fix (Blockstream, 2020). Lightning keeps your bitcoin on mainnet under your own keys (Lightning chapter), and Bitcoin's own consensus changes go through the slow public process in the BIP 110 post.

If you are in Switzerland and hold L-BTC

No Swiss retail on-ramp offers L-BTC. Relai, Pocket Bitcoin, Mt Pelerin, and Swissquote sell on-chain bitcoin, and I found no L-BTC listing at Bitcoin Suisse, Sygnum, or Bity. If you bought through any of them and hold on a hardware wallet, do not move coins because of this news. As after the Coldcard incident in July, mainnet cold storage needs no action, and the cold storage guide applies unchanged. SIX Digital Exchange in Zurich was a founding federation member in 2018 and Peach Bitcoin joined in 2024; I found no post from either about the incident by 7 September evening.

L-BTC on an exchange is a claim on the exchange. Its deposit and withdrawal pause does not change your balance. Do not send L-BTC to a deposit address while the bridge is off, and take a dated screenshot of the balance and the notice.

L-BTC in Aqua, the Blockstream app, Jade, or SideSwap never put your keys at risk, because the bug was in node validation, not in wallets. You cannot spend, swap, or peg out until the bridge nodes return. Do not open a new peg-in. Do not buy discounted L-BTC over the counter on the promise of a resume; that is a bet on Blockstream's balance sheet with no disclosed terms. Every incident brings fake recovery offers, and the scams chapter covers the pattern.

On tax, no Swiss source mentions pegged tokens or hack losses, so do not expect a rule. For a private investor a peg-in or peg-out is at most a Tausch (swap) of BTC for L-BTC at 1:1, and capital gains on private assets are tax-free under Art. 16 Abs. 3 DBG. A Kapitalverlust im Privatvermögen (capital loss on private assets) is not deductible, per the ESTV and SSK Dossier Steuerinformationen "Kryptowährung" §3.1 (ESTV Dossier). For Vermögenssteuer (wealth tax) on 31 December 2026, the same Dossier's fallback when no year-end price exists is the original purchase price in CHF. The Swiss tax guide covers the grey zone around wrapped bitcoin.

What Blockstream has not published yet

As of 9 September 2026 there is a release but no restart and no post-mortem. Blockstream told the press that updated software has been deployed and that federation members are preparing a coordinated restart, and Samson Mow added that a chain split still has to be resolved and that nobody should send bitcoin to a Liquid peg-in address until the restart is confirmed (Unchained). The split is the one described above: the federation's chain reached 4,051,232, the mempool.space node stopped at 4,050,335, and restarting means deciding which history counts. Blockstream has not said which code version each functionary ran when block 4,050,336 was accepted, and has said nothing about the 598.5 BTC, a bounty, or who covers the shortfall for exchange customers. The consolidation address still holds 598.50 BTC. The only official words are the status page entry (Blockstream status), the on-chain signed messages, and the 23.3.4 changelog.

Watch the status page for a resolved entry and the release for the pre-release flag to drop. If the consolidation address moves, that is news too. Anything else is rumour.

Not financial, legal, or tax advice. Figures as of 9 September 2026, 12:00 UTC; Blockstream has published no post-mortem.

Sources

Frequently Asked Questions

What is the Liquid Network?
Liquid is a Bitcoin sidechain that Blockstream launched in September 2018. Fifteen federation members run the servers, called functionaries, that sign blocks 11-of-15, one block per minute, with finality after two confirmations. It runs on Elements, a fork of Bitcoin Core. L-BTC is meant to be backed one to one by BTC held in the federation multisig on Bitcoin mainnet. The technical overview documents the design.
Was Bitcoin itself hacked?
No. The bug sat in Elements, the software Liquid runs on, not in Bitcoin Core. The federation's Bitcoin transaction in block 965,783 carried valid signatures from the required functionaries. Bitcoin consensus, mining, Lightning, and every mainnet wallet worked as before. If you hold BTC in your own wallet, nothing about this incident touches you. See the self-custody guide.
Is my L-BTC safe?
Your L-BTC still exists on the Liquid chain, but the reserve behind it fell from 4,205.29 BTC to 197.47 BTC on 6 September 2026. Blockstream then disabled the bridge nodes, so peg-outs are paused and exchanges paused L-BTC deposits and withdrawals. 3,400 BTC returned on 7 September, lifting the reserve to 3,597.47 BTC. About 598.5 BTC is still outstanding. Check the Blockstream status page before acting.
Was it a hack or a bug?
Both words fit. Elements cached the result of a successful rangeproof check under a label that omitted the asset and the output script. Someone reused a cached pass in a context where the proof was invalid, created L-BTC with no BTC behind it, and pegged out real BTC. No key was compromised, per the Liquid statement, and the federation's Bitcoin payout carried valid signatures.
Who did it?
Nobody knows. The actors are anonymous and call themselves whitehats in a Bitcoin OP_RETURN message in block 965,818 that reads: we are whitehats, contact us on chain. They negotiated with Blockstream through PGP-signed on-chain messages and returned 3,400 BTC. Ledger CTO Charles Guillemet and others have questioned the whitehat label. The defiprime timeline lists every message.
Will the funds be returned?
Most already were. Blockstream's PGP-signed on-chain message, Bridge nodes are patched, safe to return the funds, was mined at 09:41 UTC on 7 September 2026 in block 965,912. At 16:09 UTC, 3,400 BTC (about USD 271 million) arrived at the federation wallet in block 965,950. About 598.5 BTC (about USD 48 million) remains with the actors. No bounty has been agreed in any signed message. Decrypt reported the return.
Is Liquid still paused?
As of 9 September 2026, yes. Block production stopped at Liquid height 4,051,232 at 04:49 UTC on 7 September, so no peg-ins, peg-outs, or transactions clear. SideSwap paused swaps, peg-ins, and peg-outs. Exchanges paused L-BTC rails. Blockstream says updated software is deployed and federation members are preparing a coordinated restart, with no date given. Elements 23.3.4, tagged as a pre-release at 03:14 UTC on 9 September, contains the fix. There is still no post-mortem.
What is a rangeproof?
Liquid hides transaction amounts inside Pedersen commitments. A rangeproof is a zero-knowledge proof that a hidden amount lies between 0 and 2 to the power of 64, so a sender cannot balance a huge positive output with a hidden negative one. Elements cached successful rangeproof checks, and the cache label omitted the asset commitment and the output script. Cashu developer callebtc wrote a plain-language thread on it.
Why did Liquid nodes disagree about the block?
Independent analysis says nodes that had already cached the relevant rangeproof result accepted block 4,050,336, while nodes without that cache entry re-verified the proof and rejected it. The mempool.space Liquid explorer rejected the block and Blockstream's accepted it. One revised report claims the fix itself carries a second flaw and that functionaries ran unreleased code; an engineer disputes that reading and Blockstream has confirmed nothing. The root-cause gist is contested.
What should Swiss holders of L-BTC do now?
Do not sell L-BTC into a thin market at a discount out of panic, and do not buy discounted L-BTC on the promise of a resume. Elements 23.3.4 with the fix was tagged on 9 September; wait for the confirmed restart and a post-mortem. If your L-BTC sits on an exchange, withdrawals are paused; keep dated screenshots. BTC you control on mainnet is unaffected. No Swiss retail on-ramp offers L-BTC. See how to buy Bitcoin in Switzerland and the Swiss tax guide.