203 lines
12 KiB
Markdown
203 lines
12 KiB
Markdown
# Post-quantum certificate anchor for QBFT
|
|
|
|
This is the code that puts a post-quantum validator certificate under the block hash in Hyperledger
|
|
Besu's QBFT consensus, as an overlay on a named upstream commit.
|
|
|
|
It is published so that the claim can be checked rather than believed. Everything below that is not
|
|
demonstrable from these files is marked as not demonstrable from these files.
|
|
|
|
---
|
|
|
|
## Scope boundary, stated first and not in a footnote
|
|
|
|
**Consensus on Aere Network chain 2800 is classical secp256k1 ECDSA QBFT with a post-quantum
|
|
checkpoint enforced every 32 blocks.** This code is what adds the checkpoint, in two steps that
|
|
should not be confused:
|
|
|
|
1. **The binding.** From block 13,014,000 the block hash of every anchored header (every 32nd
|
|
block) commits to a Falcon-512 certificate signed by the validators. That is a binding, not a
|
|
replacement: a rewrite of anchored history must also forge the post-quantum signatures.
|
|
2. **The enforced minimum.** Since 2026-08-14 a node does not finalize an anchor block unless
|
|
its certificate holds at least three valid Falcon-512 seals (f+1 of nine, the guarantee that at
|
|
least one honest validator signed; eight or nine are carried in practice). Blocks between
|
|
anchors carry no Falcon seals and are finalized by ECDSA alone, so the post-quantum guarantee
|
|
is a checkpoint about every 16 seconds, not a per-block quorum.
|
|
|
|
Correction 2026-08-19: the fleet was also configured on 2026-08-15 with `aere.falcon.forkBlock`
|
|
at 14,050,000, intended as a per-block blocking Falcon quorum. In the shipped code that rule
|
|
(`FalconSealValidationRule`) is retired at `aere.pq.anchorBlock` (13,014,000) in favour of the
|
|
anchor rules (`PqAnchorConfig.legacyFalconRuleRetirementBlock`), so at 14,050,000 it was already
|
|
inert, and the chain itself confirms it: blocks between anchors carry no Falcon seals, which a
|
|
per-block quorum rule would have rejected. The earlier statement in this repository that from
|
|
14,050,000 every block required a 2f+1 Falcon quorum to finalize is withdrawn.
|
|
|
|
"Hybrid" is the honest word only in the checkpoint sense above, and "post-quantum consensus"
|
|
without that qualification overstates: block proposal, validator identity and message transport
|
|
are classical elliptic-curve cryptography, and a cryptographically relevant quantum computer would
|
|
break those exactly as on any other ECDSA chain. What it cannot do is rewrite history past the
|
|
last anchor without also forging Falcon-512 signatures. None of this has been audited by a third
|
|
party.
|
|
|
|
The threat it addresses is not "harvest now, decrypt later". A signature is public; there is nothing
|
|
to harvest. The threat is **retroactive rewriting**: validator keys recovered later can be used to
|
|
re-sign old blocks, and a chain whose history is authenticated only by ECDSA cannot distinguish the
|
|
rewrite from the original. Binding a post-quantum certificate into the hash of every anchored header
|
|
means a rewrite must also forge the post-quantum signatures.
|
|
|
|
The threat is not ours to claim. It is the long-range attack, and the peer-reviewed treatment is
|
|
Azouvi, Danezis and Nikolaenko, "Winkle: Foiling Long-Range Attacks in Proof-of-Stake Systems"
|
|
(IACR 2019/1440; AFT 2020, pp. 189-201). Two things should be said plainly about that citation,
|
|
because we checked it rather than repeated it. **Winkle does not mention quantum adversaries at
|
|
all**: it treats old validator keys becoming compromised, by any means. The quantum framing is
|
|
ours. And **Winkle's own defence is not ours**: it adds a secondary layer of client-based
|
|
validation, where clients sign a hash of the previously sequenced block. A second published defence
|
|
for the same threat, Azouvi and Vukolic, "Pikachu: Securing PoS Blockchains from
|
|
Long-Range Attacks by Checkpointing into Bitcoin PoW using Taproot" (arXiv 2208.05408, 2022),
|
|
checkpoints into Bitcoin's proof of work.
|
|
|
|
### Where this sits among the known defences
|
|
|
|
The literature calls this attack **posterior corruption**, also known as the founders' attack, the
|
|
long-range attack, history revision, or costless simulation. Tas, Tse, Gai, Kannan, Maddah-Ali and
|
|
Yu survey the mitigations in "Bitcoin-Enhanced Proof-of-Stake Security: Possibilities and
|
|
Impossibilities" (arXiv 2207.08392) and count four: social consensus by a trusted committee;
|
|
key-evolving signatures, where validators forget their old keys; verifiable delay functions; and
|
|
timestamping onto an existing proof-of-work chain.
|
|
|
|
That paper also proves an impossibility, and it deserves to be stated correctly rather than
|
|
paraphrased into something scarier or softer than it is. **Slashable safety**, meaning safety plus the
|
|
ability to punish the attacker, is not achievable by a proof-of-stake chain without an external
|
|
trusted source. Plain safety against posterior corruption is a weaker property, and key-evolving
|
|
signatures are shown to provide it under an honest majority. So the impossibility is not "you cannot
|
|
defend yourself"; it is "you cannot make the attacker pay, on your own."
|
|
|
|
**This anchor does not claim slashable safety and does not compete with checkpointing on that
|
|
ground.** What it addresses is the case where key-evolving signatures stop working. Their guarantee
|
|
rests on a validator's old key being *gone* once deleted. Against an adversary with a
|
|
cryptographically relevant quantum computer, an old secp256k1 key does not need to have been kept or
|
|
stolen. QBFT headers carry validator addresses rather than public keys, but every seal a validator
|
|
ever wrote is an ECDSA signature from which its public key can be recovered, and those seals are in
|
|
the chain forever. From the public key, a quantum adversary derives the private one. Deleting the
|
|
private key buys nothing, because the chain reconstructs it. The certificate under the block hash is what still has to be forged, and forging it requires
|
|
a secret the adversary cannot derive.
|
|
|
|
Put plainly: this is the post-quantum analogue of what key-evolving signatures give classically, for
|
|
the setting in which their central assumption no longer holds. It is narrower than checkpointing and
|
|
it borrows nothing from another chain, and both of those are true at the same time.
|
|
|
|
---
|
|
|
|
## The problem this solves, which is not obvious
|
|
|
|
In QBFT, `extraData` is an RLP list. The block hash is **not** computed over the stored bytes. It is
|
|
computed over a re-encoding of the decoded list, with the seal fields removed, so that every node
|
|
agrees on a hash before the seals exist.
|
|
|
|
That has a consequence that is easy to miss: **anything the decoder does not know about is dropped
|
|
before hashing.** Append a certificate as a new element and it survives in storage, travels between
|
|
nodes, and is entirely absent from the hash. Two nodes can hold different certificates for the same
|
|
block and both consider it valid. The certificate would be decoration.
|
|
|
|
Four obvious designs were tried and all four died on that:
|
|
|
|
| Attempt | Why it died |
|
|
|---|---|
|
|
| new RLP element after the seals | dropped by the re-encode, never reaches keccak |
|
|
| extend the seal list | changes the seal encoding, so every existing node rejects the header |
|
|
| a second `extraData`-like field | not in the header schema; a header with it is not a header |
|
|
| commit in the state root | the state root is computed before the certificate exists |
|
|
|
|
The design that works uses a field that is **already under keccak**: `vanityData`, element 0 of the
|
|
list, 32 bytes, historically arbitrary. At an anchor height it carries the digest of the certificate
|
|
instead of the usual vanity string. The certificate itself still rides outside the hash, but it is
|
|
now pinned: change one byte of it and the digest no longer matches, and the header is rejected.
|
|
|
|
`PqAnchor.java` and `PqAnchorDigestRule.java` are where that lives. Read those two first.
|
|
|
|
---
|
|
|
|
## What is in here
|
|
|
|
- `consensus/common/.../bft/` — the anchor itself: configuration, the digest, the seal cache and
|
|
store, the producer that attaches seals, the Falcon registry that maps a validator to a key.
|
|
- `consensus/qbft/.../headervalidationrules/` — the validation rules: the digest must match, the
|
|
seals must verify, and the rules must actually be wired into the validator chain.
|
|
- `app/.../controller/` — where the rules are built and where the node refuses to start on a
|
|
configuration that would produce headers its own fleet rejects.
|
|
- tests — including the negative controls. A test that cannot fail is not a test, and several of
|
|
these exist specifically to prove the guards can fail.
|
|
|
|
`BASE.txt` names the upstream commit. Applying these files to any other tree overwrites whatever
|
|
upstream added since, silently. That is stated there in more detail because it is a real hazard.
|
|
|
|
**One build file changes, and it is one line.** `consensus/common/build.gradle` gains
|
|
`implementation 'org.bouncycastle:bcprov-jdk18on'`. Falcon verification happens inside the consensus
|
|
module, so the module needs the library on its own compile path. No version is stated, because
|
|
upstream pins it: `platform/build.gradle` declares `bcprov-jdk18on:1.83`, and four upstream modules
|
|
already take the dependency in exactly this versionless form. So the line adds a compile-path entry
|
|
and no new artifact, and it does not move any version. That is the entire build change, and it is
|
|
called out here rather than left to be found in the diff.
|
|
|
|
No cryptography is implemented in this overlay. Falcon signature verification calls Bouncy Castle's
|
|
implementation; what is ours is the framing, the registry that maps a validator to a key, the digest,
|
|
and the validation rules.
|
|
|
|
---
|
|
|
|
## What is proven, and by what
|
|
|
|
- **The certificate is byte-identical across nodes.** Measured on a test network, six nodes.
|
|
- **A stripped certificate is rejected.** 12 attempts, 12 rejections.
|
|
- **The guards compile and pass together**, and the negative control was run: with the guards
|
|
removed the proofs go red. A guard that has never failed cannot be trusted, so each was made to
|
|
fail on purpose.
|
|
- **Arming does not halt the chain across a validator-set change.** On a test network of ten
|
|
processes, 1,110 blocks were produced across the arming height while four validator-set votes were
|
|
driven through it, and the block rate did not change. The negative control for that run was
|
|
separate and blunt: three nodes restarted without their Falcon key produced zero blocks in ninety
|
|
seconds while every node was alive; with the keys restored, eighty-nine.
|
|
- **The wiring is tested.** An earlier version of this code registered a rule that could not be seen
|
|
from outside, because Besu's `BlockHeaderValidator.Builder` wraps detached rules in a lambda. The
|
|
rule was present and untested for that reason alone. `QbftAnchorRuleWiringTest` exists because of
|
|
that, and its negative control is measured: comment out the registration line and it goes red.
|
|
|
|
## What is not proven here
|
|
|
|
- **This overlay has not been audited by a third party.** No external security review of this code
|
|
exists. If you are reading it as an auditor, you are the first.
|
|
- **A rehearsal with a deliberately un-upgraded node has not been run.** Every rehearsal so far
|
|
upgraded every node.
|
|
- **Nothing here demonstrates what is configured on any live network.** These files show what the
|
|
code does when armed. They are not evidence about any running fleet, and should not be read as any.
|
|
|
|
## One claim we retracted, on purpose
|
|
|
|
An earlier version of our public material said that no public chain has a block hash covering a
|
|
post-quantum validator certificate. That does not survive a hostile reading. Cellframe's ESBoCS
|
|
signs blocks with keys that resolve to Dilithium, Falcon or SPHINCS+, and hashes the block with the
|
|
signatures attached. The capability exists in their code today.
|
|
|
|
The defensible statement is narrower: no public chain has a post-quantum validator certificate under
|
|
the block hash **that is proven and independently verifiable**. This repository is our half of that
|
|
sentence. Someone else has to do the verifying, which is why it is here.
|
|
|
|
---
|
|
|
|
## Terminology, used precisely
|
|
|
|
**A certificate here is signed by f+1 validators, not by a quorum.** With f Byzantine faults
|
|
tolerated, f+1 signatures guarantee that at least one honest validator signed. That is a real
|
|
property and it is not the same as a quorum, and we do not call it one. Anyone counting will notice,
|
|
and they should.
|
|
|
|
The seal threshold is a **floor, not a cap**: nodes attach as many verified seals as arrive in time,
|
|
which is at least the threshold and often more. A separate cap bounds how many are written, because
|
|
each seal costs bytes in every header forever.
|
|
|
|
---
|
|
|
|
## Licence
|
|
|
|
Apache 2.0, matching upstream Hyperledger Besu. See `../LICENSE` and `../NOTICE`. Files that modify
|
|
upstream carry the change notice required by section 4(b); files that are new are ours.
|