One Payment Hash, One Payment Secret: Lightning's BOLT11 Cleanup, Explained
A reported attack let Lightning withdrawals look unpaid while the Bitcoin actually moved. The fix is a spec tightening: BOLT11 invoices now carry exactly one payment hash and exactly one payment secret. The real problem isn't the protocol. It's that a lot of node software never enforced the field in the first place.
Did that Lightning withdrawal actually fail? Or does your node just think it did?
That's the question a handful of node operators have been asking, and the answer is uncomfortable. A reported attack let Bitcoin withdrawals appear unpaid on the sender's side while the funds moved anyway. The response isn't a fork or a new opcode. It's a spec tightening: BOLT11 invoices now carry exactly one payment hash and exactly one payment secret. No ambiguity, no extra fields, no room to interpret.
Sounds dull. It's also the difference between a ledger and a fiction.
What changed, precisely
BOLT11 is the invoice format Lightning has used since 2018. On mainnet the string starts withlnbc, bech32 encoded, and it packs the destination, the amount, a timestamp, and an expiry into something a wallet can scan.
Two fields matter here. The payment hash is 32 bytes, the SHA256 of the preimage the receiver has to reveal to claim the money. The payment secret is another 32 bytes of randomness, and its whole job is to prove the payer actually saw the invoice.
The new rule is strict. One hash per invoice. One secret per invoice. Before this, an implementation could end up referencing the same invoice through more than one hash in its internal bookkeeping, or it could settle a payment against an invoice whose secret it never bothered to check. That second crack is the one the attack walked through.
Here's the part that gets missed. Nothing about the wire format broke. The protocol did what it was told. The failure lives in the layer above, where software decided what to write down.
Why the payment secret was there all along
Payment secrets didn't get added for decoration. They shipped years ago to close an invoice probing hole. Without a secret tied to a specific invoice, a node can be coaxed into revealing whether a payment hash belongs to it, which is a slow leak of routing and balance information nobody wants.
The catch is that a check only works if the receiver runs it. Plenty of wallets generated the secret, stuffed it into the invoice, and then never validated it on the inbound side. Dormant gap. Harmless right up until someone decides to poke it.
So let's be blunt about the takeaway. Lightning's biggest risk was never capacity. It's accounting.
Public channel capacity gets all the headlines because it's a big number that goes up and to the right. But the network's real fragility is bookkeeping. Two honest nodes can disagree about whether a payment happened, and neither one is lying. One saw an HTLC resolve. The other saw a status field stick at pending and never got the memo.
Anyone who calls this a documentation update hasn't run a node through a bad reconciliation.
What operators are actually doing
According to people running nodes in production, the guidance coming down is blunt and slightly awkward: go look at how your software records withdrawals. Not how it sends them. How it records them.
The gap sits in the write path. A payment goes out, HTLCs resolve, the preimage gets revealed, and somewhere between the wire and the database a status field stays at pending or flips to failed. On Lightning the money is gone. In your ledger it's a ghost. Now repeat that a few thousand times and try to file a tax return.
Routing node operators are watching this closely for one reason. Reconciliation. If you're running hundreds of channels and your accounting drifts by even a fraction of a percent, you can't tell whether you're profitable. You're guessing. And guessing at scale is how a routing business quietly dies.
Traders are framing it the same way they frame exchange reserve data. The number isn't the story. Whether you trust the number is the story.
There's a nasty second-order effect too. Exchanges and custodians that batch Lightning withdrawals into a single on-chain operation now have to prove each invoice settled individually. If the internal flag says failed and the chain says otherwise, you get a support ticket from a customer who is, annoyingly, correct.
What to check this week
Three audits, and they're cheap to run. First, invoice generation. Confirm your stack emits exactly one payment hash and exactly one payment secret, every time, with no fallback path that skips the secret. Second, inbound validation. Confirm the receiving node actually checks the secret before it settles. Third, the withdrawal record. Confirm that a settled payment can never be logged as failed, and that a failed one can never be silently retried.
Then ask your node software's maintainers when they last touched the payment-secret validation path. If the answer is some version of it's always worked, push back. That's not an answer. That's an assumption with a changelog.
Watch the release notes for the major implementations over the next few cycles. BOLT11 tightening usually lands as a validation change, not a headline feature, which means it'll be buried three lines down in a patch summary. Read those lines. Also watch whether any wallet vendors backfill the secret check retroactively or just quietly require an upgrade.
And here's the angle that should bother anyone building machine payments. If agents have wallets, who holds the keys? More to the point, who reconciles the ledger when the agent can't distinguish a settled payment from a failed one? An autonomous process firing off hundreds of invoices an hour can't run on a status field that lies. The compute layer needs a payment rail. It needs a receipt just as badly.
This isn't a partnership announcement. It's a convergence. Lightning just got asked to behave like settlement infrastructure instead of a demo, and the answer so far is a one-line rule that half the stack wasn't enforcing.
That's the actual news. Not the attack. The fact that it worked.