Aere Network public source. Everything here can be checked against the live chain (chain id 2800, https://rpc.aere.network). Scope note, stated up front rather than buried: consensus on chain 2800 is classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at the signature, precompile, account and transport layers. Nothing here makes the consensus post-quantum, and no document in it should be read as claiming so.
22 KiB
AIP-8: Post-Quantum-Authorized Transaction Envelope (EIP-2718 Type 0x2A)
Preamble
| Field | Value |
|---|---|
| AIP | 8 |
| Title | Post-Quantum-Authorized Transaction Envelope (EIP-2718 Type 0x2A) |
| Author | AERE Foundation |
| Type | Standards Track |
| Category | Core |
| Status | Draft |
| Created | 2026-07-18 |
| Requires | 7 |
| Ratification | Foundation-ratified (pre-decentralization) |
Abstract
This AIP specifies a new EIP-2718 typed transaction, TransactionType = 0x2A, whose
sender authorization is a NIST post-quantum signature (Falcon-512 or ML-DSA-44)
rather than a secp256k1 ECDSA signature. The transaction carries a scheme selector, a
raw PQC public key, and a PQC signature; the sender address is derived from the public
key, and the transaction is valid only if the signature verifies against the exact same
native precompiles that are already live on AERE mainnet chain 2800 (Falcon-512 0x0AE1,
ML-DSA-44 0x0AE3, activated at block 9,189,161 per AIP-7). It makes ordinary value and
contract-call transactions authorizable by a quantum-resistant key with no smart-account
indirection. This is a base-layer execution change requiring a coordinated Besu hard
fork; it is NOT yet live. It is explicit that this does not change how AERE blocks are
proposed, signed, or committed: consensus remains classical secp256k1 ECDSA QBFT and is
not post-quantum. A deploy-today reference path that needs no fork
(AerePQCTxAccount.sol, an ERC-4337 account) accompanies this AIP.
Motivation
AERE has live, mainnet-proven post-quantum signature verification at the precompile layer (AIP-7) and usable post-quantum authorization at the account layer (AerePQCAccount, AereThresholdAccount, AerePQCSocialRecoveryModule). What is still missing is the most basic object of all: an ordinary externally-originated transaction whose right to spend an account is a post-quantum signature.
Today every transaction on chain 2800, including one that calls a post-quantum contract, is itself authorized by a secp256k1 ECDSA signature. The account's ability to move its native balance rests on ECDSA. A cryptographically-relevant quantum computer running Shor's algorithm breaks secp256k1: it recovers the private key from the public key, and on any account that has ever sent a transaction the public key is already on-chain. Account- layer PQC (a 4337 smart account) closes this for smart-contract wallets, but it cannot protect a plain externally-owned account (EOA), and it adds a bundler/EntryPoint and a deployment step. A base-layer post-quantum transaction type closes the gap for the EOA itself: the account is a hash of a PQC public key, and only a PQC signature can spend it.
The goal is a transaction type that (1) reuses the audited, mainnet-live precompile verify paths verbatim (no new cryptography), (2) is a pure function of the transaction bytes for validation (like ECDSA sender recovery, so mempool validation reads no state), (3) fits far under the Fusaka EIP-7825 per-transaction gas cap, and (4) is a strictly additive, client-only hard fork with no re-genesis, exactly like AIP-7.
Specification
The keywords MUST, SHOULD, and MAY are to be interpreted as in RFC 2119.
Transaction type byte
The transaction type is 0x2A.
EIP-2718 constrains a valid TransactionType to the range [0x00, 0x7f]: a first byte
>= 0xc0 is ambiguous with the RLP list header of a legacy transaction and is excluded,
and EIP-2718 recommends implementers stay within 0x00..0x7f. Within that range the
Ethereum L1 assignments are 0x01 (EIP-2930), 0x02 (EIP-1559), 0x03 (EIP-4844), and
0x04 (EIP-7702); AERE recognizes all four. 0x2A is chosen because it is unassigned by
any Ethereum L1 EIP and sits well below the 0x64..0x7f band that existing L2s have
informally colonized (Arbitrum 0x64..0x6c, zkSync Era 0x71+, Optimism deposit
0x7e, Celo 0x7b..0x7d), which minimizes the chance that an imported third-party tool
misparses an AERE PQ transaction as some other chain's type. 0x2A is reserved henceforth
in the AERE chain specification for this purpose. If Ethereum L1 later assigns 0x2A, AERE
will re-evaluate to preserve equivalence, as AIP-7 commits to for the precompile band.
Signature-scheme selector
A one-byte pq_scheme selects the NIST scheme and the precompile that validates it. The
values match AIP-7 and AerePQCAttestation:
pq_scheme |
Scheme | Precompile | Public-key length | Signature envelope |
|---|---|---|---|---|
0x01 |
Falcon-512 | 0x0AE1 |
897 bytes (header 0x09) |
`nonce(40) |
0x03 |
ML-DSA-44 | 0x0AE3 |
1312 bytes | sig, exactly 2420 bytes |
pq_scheme 0x02 (Falcon-1024, 0x0AE2, 1793-byte pk) and 0x04 (SLH-DSA-128s, 0x0AE4,
32-byte pk, 7856-byte sig) MAY be enabled by the same rules; they are omitted from the
mandatory set because Falcon-512 (NIST level 1, smallest signature) and ML-DSA-44 (FIPS 204,
fixed-size, conservative) span the practical trade-off between signature size and margin.
Any other value MUST be rejected as an invalid transaction.
RLP wire format
A type-0x2A transaction is the concatenation of the type byte and the RLP encoding of a
12-element list:
0x2A || rlp([
chain_id, # scalar; MUST equal 2800 for AERE mainnet
nonce, # scalar; the PQC-derived sender's account nonce
max_priority_fee_per_gas, # scalar (EIP-1559 semantics)
max_fee_per_gas, # scalar (EIP-1559 semantics)
gas_limit, # scalar
to, # 20-byte address, or empty (0x) for contract creation
value, # scalar
data, # byte string
access_list, # EIP-2930 [[address, [storageKey, ...]], ...]
pq_scheme, # single byte, 0x01 or 0x03 (see selector table)
pq_public_key, # raw NIST public key for pq_scheme
pq_signature # signature envelope for pq_scheme (see selector table)
])
The fee, gas, to, value, data, and access_list fields are identical in meaning to
EIP-1559 (type 0x02) with an EIP-2930 access list, so existing execution and fee-market
logic is unchanged. The three trailing fields replace the ECDSA (y_parity, r, s) triple.
The transaction hash is keccak256(0x2A || rlp([...all 12 fields...])), as for any typed
transaction.
Sender derivation
The sender is derived from the PQC public key and the scheme, not from signature recovery:
sender = keccak256(pq_scheme || pq_public_key)[12:32] # last 20 bytes
Binding the address to pq_scheme as well as the key means the same raw key bytes under a
different scheme selector yield a different account, so there is no cross-scheme aliasing. A
user migrates to post-quantum custody by moving assets to their sender address; from then
on only a valid PQC signature can spend it. Unlike ECDSA, the public key is transmitted in
full on every transaction (the verify precompiles are stateless), which is safe: Falcon and
ML-DSA remain secure with the public key exposed. This is the point of the design, not a
weakness.
Signing message and verification
pq_signature MUST be a valid signature, under pq_public_key and pq_scheme, over the
32-byte digest:
sig_hash = keccak256(0x2A || rlp([
chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit,
to, value, data, access_list, pq_scheme, pq_public_key
])) # the 12-field list WITHOUT pq_signature
Validation MUST assemble the precompile input for pq_scheme exactly as
AerePQCAttestation / AerePQCKeyRegistry do, with sig_hash as the signed message, and
accept the transaction only if the corresponding precompile returns the 32-byte
"valid" word 0x…01:
- Falcon-512:
input = pk(897) || sm, wheresm = sigLen(2, big-endian) || nonce(40) || sig_hash(32) || esigandsigLen == esig.length; call0x0AE1. - ML-DSA-44:
input = pk(1312) || sig(2420) || sig_hash(32); call0x0AE3.
The client MUST use the same audited Bouncy Castle 1.83 verify path the precompile wraps, so
that transaction validation and an in-EVM STATICCALL to the precompile agree bit-for-bit
on every (pq_public_key, sig_hash, pq_signature) triple. A transaction whose signature
does not verify has no valid sender and is invalid: it MUST be rejected from the mempool and
MUST NOT be included in a block, exactly as an ECDSA transaction with an unrecoverable
signature is.
Nonce and replay handling
- Cross-chain replay is prevented by
chain_idinside both the transaction andsig_hash: a signature for chain 2800 does not verify for any other chain id. - Intra-chain replay and ordering are enforced by
nonce, the account nonce of the PQC-derivedsender, tracked in world state and incremented on inclusion, identical to an ECDSA account. A given(sender, nonce)can be mined at most once. - Cross-protocol replay (reusing a PQ tx signature as an AerePQCAttestation,
AerePQCKeyRegistry, or ERC-4337 authorization, or vice-versa) is prevented because those
surfaces sign structurally different messages: attestation/registry/account challenges are
keccak256(abi.encode(DOMAIN, ...))orabi.encodePacked(domain, hash)byte strings that can never equal the0x2A || rlp(...)preimage ofsig_hash. The leading type byte0x2Ain the signed preimage is the domain tag for this transaction type, mirroring how EIP-1559 signskeccak256(0x02 || rlp(...)).
Gas accounting
Intrinsic gas for a type-0x2A transaction is:
intrinsic = 21000
+ calldata_gas(entire type-0x2A payload, including pq_public_key + pq_signature)
+ access_list_gas # EIP-2930: 2400 per address, 1900 per storage key
+ PQ_VERIFY_GAS[pq_scheme] # fork constant, MUST equal the live precompile verify gas
PQ_VERIFY_GAS MUST equal the marginal verify-op gas the corresponding precompile charges,
which AIP-7 measured on the pre-activation scratch fork (chain 28099) and carried to
mainnet:
| Scheme | PQ_VERIFY_GAS (measured, AIP-7) |
|---|---|
Falcon-512 (0x0AE1) |
40,000 |
ML-DSA-44 (0x0AE3) |
55,000 |
Falcon-1024 (0x0AE2) |
75,000 |
SLH-DSA-128s (0x0AE4) |
350,000 |
The dominant additional cost versus an EIP-1559 transaction is the calldata for the public key and signature. Approximate PQ-material sizes and the resulting calldata component (to be measured on the scratch fork; calldata is priced at EIP-2028 rates, subject to the EIP-7623 floor):
| Scheme | pk bytes | sig-envelope bytes | PQ material | approx. calldata gas |
|---|---|---|---|---|
| Falcon-512 | 897 | ~707 (40 + 1 + ~666) | ~1,605 B | ~24,000 (to be measured) |
| ML-DSA-44 | 1,312 | 2,420 | ~3,732 B | ~56,000 (to be measured) |
A complete Falcon-512 PQ transaction therefore lands near 21,000 + ~24,000 + 40,000 ≈ 85,000 gas of intrinsic cost before execution, consistent with AIP-7's measured
end-to-end Falcon-512 verify-and-record transaction of 86,336 gas; a ML-DSA-44 PQ
transaction lands near 21,000 + ~56,000 + 55,000 ≈ 132,000. Both are a tiny fraction of
the EIP-7825 per-transaction cap of 16,777,216 (2^24), leaving the full block-execution
budget for the call itself.
PQ_VERIFY_GAS is folded into intrinsic gas so the sender pays for the verification work
that validators perform. Because signature validity is a precondition of transaction
validity, a transaction with an invalid signature is never included and thus is never
charged on-chain, exactly as with a bad ECDSA signature; it simply cannot be mined.
Base-layer activation sketch (Besu fork)
The change is a strictly additive execution-layer fork of the AERE Besu build (base
hyperledger/besu:26.4.0, the same tree that carries the AIP-7 precompiles). No re-genesis,
no state migration. The implementation touches, at minimum:
- TransactionType. Add
PQ_AUTHORIZED((byte) 0x2A)toorg.hyperledger.besu.datatypes.TransactionType. - Codec. Add a
PQAuthorizedTransactionDecoder/PQAuthorizedTransactionEncoderimplementing the 12-field RLP layout above and register them in theTransactionDecoder/TransactionEncoderdispatch. Extend theTransactionmodel to carrypqScheme,pqPublicKey, andpqSignature, and override sender computation for this type tokeccak256(pqScheme || pqPublicKey)[12:], defined only when the PQC signature verifies. - PQ signature verifier. A
PQAuthorizedSignatureVerifierthat calls the exact same Bouncy Castle 1.83 BCPQC verify path (AereFalconSupport.verify, ML-DSAverifyInternal) the0x0AE1/0x0AE3precompiles wrap. Zero new cryptography; reuse the precompile adapters through the same classloader-isolation boundary AIP-7 established. - Validation. In
MainnetTransactionValidator(viaTransactionValidatorFactory), for type0x2A: reconstructsig_hash, verify the PQC signature (reject with an INVALID reason on failure), derive the sender, and validate nonce, balance, and intrinsic gas as usual. - Gas. Extend the
GasCalculatorwithpqAuthorizationIntrinsicGasCost(scheme, payload)returning the calldata component plusPQ_VERIFY_GAS[scheme], wired intotransactionIntrinsicGasCost. - Mempool and wire. Teach
TransactionPooland the eth/68Transactions/PooledTransactionscodecs to carry the larger typed body; expose the type in the JSON-RPC transaction serialization (eth_getTransactionByHash, receipts). - Fork gating. Add an
AerePQTxmilestone toProtocolScheduleBuilder/ the chain spec, activated at a future block or timestamp distinct from the AIP-7 precompile activation. Before activation, a0x2Afirst byte is an unknown type and is rejected; after activation it is valid. Because chain 2800 runs seven QBFT validators under one operator on one client, activation is a flag-day client upgrade across the validator set, exactly as in AIP-7. A non-upgraded validator would reject the type and fork off, so the upgrade is mandatory at the fork block. - Differential testing. Gate ship on a test that, for the NIST KAT vectors in
pqc-fork/katand randomized cases, asserts the transaction-validation verify agrees bit-for-bit with aSTATICCALLto the matching precompile over the same(pk, sig_hash, sig), plus round-trip encode/decode and a full mine-and-receipt path on an isolated testnet.
Consensus is untouched: QBFT proposal, the committed-seal ECDSA signatures, and block
structure are unchanged; type 0x2A is simply another transaction a block may contain, with
its own receipt type.
Rationale
Full public key in every transaction, not a registry keyId. An alternative shrinks
calldata by replacing pq_public_key with a keyId into AerePQCKeyRegistry
(0x1eCa…3691). It is rejected for the base type because it makes transaction validation
depend on world state (a registry lookup), which breaks the property that a transaction's
validity is a pure function of its own bytes, the same property ECDSA sender recovery has,
and which mempool validation relies on. The stateless full-key form keeps validation pure
and matches how the live precompiles already work. A registry-indexed companion type could be
specified later once the state-dependency implications for the mempool are worked through.
Sender = hash of (scheme, key), reusing the 20-byte address space. This keeps the PQ
account indistinguishable from any other address to every downstream contract, tool, and
explorer: balances, CALL, msg.sender, and logs all behave normally. The only thing that
changes is who may originate a transaction from that address.
Type 0x2A over a higher value. Staying in the low half of the valid range and away
from the L2-colonized 0x64..0x7f band reduces cross-ecosystem tool confusion, at no cost.
A base-layer type at all, given 4337 exists. ERC-4337 already gives post-quantum authorization for smart-contract wallets and is the recommended path today (see the reference implementation). But a 4337 account cannot protect a plain EOA, requires a deployed contract and a bundler, and pays EVM-execution overhead for validation. The base type is the only way to make an ordinary account post-quantum, and it prices verification at the native precompile cost rather than in-EVM. The trade-off is that it requires a hard fork and touches the entire transaction-decoding surface of the ecosystem, which is exactly why it is filed as a Draft with a shipping 4337 alternative rather than activated immediately.
Backwards Compatibility
Additive. Existing legacy, 0x01, 0x02, 0x03, and 0x04 transactions are unaffected.
Before the AerePQTx fork block, a 0x2A transaction is an unknown type and is rejected by
all nodes; after it, all nodes accept it. There is no change to existing accounts, contracts,
receipts of other types, or the genesis state root.
Ecosystem tooling that decodes raw transactions (wallets, explorers, indexers, the RPC
transaction serializer, hardware-wallet firmware, signing libraries) MUST learn type 0x2A
to build, sign, display, or index these transactions. Tools that only handle types they
recognize will treat a 0x2A transaction as opaque or unsupported rather than misdecode it,
because the type byte is self-describing. This ecosystem surface is the principal cost of the
change and the reason the ERC-4337 path is preferred for immediate use.
Because AERE runs all validators under one operator and one client, activation is a mandatory flag-day client upgrade across the validator set at the fork block, as in AIP-7. A validator that had not upgraded would reject the type and fork.
Security Considerations
This change does not make AERE consensus post-quantum. Validators continue to sign
classical secp256k1 QBFT committed seals; blocks are proposed and committed exactly as today.
Type 0x2A makes an individual transaction's sender authorization post-quantum. It does
not make block production, finality, or the validator set post-quantum, and any claim to that
effect on the basis of this AIP would be false and must not be made.
A valid signature is not authorization beyond spend rights. A "valid" precompile decision
establishes only that pq_signature is a valid signature by pq_public_key over sig_hash.
Freshness and ordering come from nonce; scope comes from the tx fields themselves. There is
no key rotation or recovery at this layer: losing the PQC private key loses the account, the
same as losing an ECDSA key. Users who need rotation or social recovery should hold assets in
an ERC-7579 account guarded by AerePQCSocialRecoveryModule instead of, or in addition to, a
PQ EOA.
Single operator, one client (no client-diversity safety net). Chain 2800's seven QBFT validators are one operator on one execution client. A consensus bug in the PQ-transaction validation path (for example a decoder edge case, or a divergence between the tx-validation verify and the precompile verify) would not be caught by a second live client, because there is none. This raises the bar for external audit and for the mandatory bit-for-bit differential testing in the activation sketch.
Determinism. Transaction validation, like a precompile, must be deterministic and read no node-local state or block context beyond the transaction and the account nonce/balance. Falcon verification MUST use the integer-only verify path (never the floating-point signing path). Malformed structure MUST be rejected deterministically as an invalid transaction, not faulted.
Public-key exposure is intended. The public key is on-chain from the account's first transaction. This is safe for Falcon and ML-DSA and is the mechanism by which the account is quantum-resistant; it is called out only to preempt the ECDSA-era intuition that an unspent address "hides" its key.
Not live; external audit pending. This is a Draft specification and a reference- implementation sketch. Nothing here is deployed on chain 2800. The precompiles it reuses carry an internal self-audit only (AIP-7). This transaction type MUST NOT secure material value until it is implemented, differentially tested against the live precompiles, and covered by an external security audit.
Reference Implementation and On-Chain Deployment
Not yet live. The base-layer fork above is a specification and a Besu implementation sketch, not a deployed change.
Deploy-today reference path (no fork required). The same user-visible guarantee, "a
transaction is authorized by a post-quantum signature and nothing else", is delivered today,
with no base-layer change, by an ERC-4337 v0.7 smart account whose sole owner is a NIST PQC
key and whose validateUserOp verifies via the live precompiles:
- Contract:
contracts/contracts/pqc/AerePQCTxAccount.sol(this repository). - Tests:
contracts/test/AerePQCTxAccount.test.js(MockPQCPrecompile across all four schemes plus a genuine Falcon-512 path through the NIST-KAT-proven AereFalcon512Verifier). - Verified 2026-07-18: compiled and tested under the canonical Hardhat toolchain,
npx hardhat compilethennpx hardhat test test/AerePQCTxAccount.test.js. The contract compiles clean and all 20 tests pass: the mock verifier across all four schemes, the domain-separation and single-use replay checks, and two genuine Falcon-512 paths through the NIST-KAT-proven verifier. This bullet records an actual machine run; it should be read as a verified result, not an untested present-tense assertion. - It calls the live precompiles (
0x0AE1..0x0AE4) directly with the identical wire encoding proven on mainnet by AerePQCAttestation (0x465d9E3b476BF98Aa1393079e240Db5D2a9bEA6A) and AerePQCKeyRegistry (0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691), and targets AereEntryPointV2 (0x8D6f40598d552fF0Cb358b6012cF4227B86aF770). Addresses are copied verbatim fromsdk-js/src/addresses.ts.
The precompile band, its activation block (9,189,161), and the wire encoding are verifiable
on https://rpc.aere.network (chain id 2800) as documented in AIP-7.
Copyright
Released to the public domain (CC0). No rights reserved.