# 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) || esig` (esig starts `0x29`) | | `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`, where `sm = sigLen(2, big-endian) || nonce(40) || sig_hash(32) || esig` and `sigLen == esig.length`; call `0x0AE1`. - ML-DSA-44: `input = pk(1312) || sig(2420) || sig_hash(32)`; call `0x0AE3`. 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_id` inside both the transaction and `sig_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-derived `sender`, 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, ...))` or `abi.encodePacked(domain, hash)` byte strings that can never equal the `0x2A || rlp(...)` preimage of `sig_hash`. The leading type byte `0x2A` in the signed preimage is the domain tag for this transaction type, mirroring how EIP-1559 signs `keccak256(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: 1. **TransactionType.** Add `PQ_AUTHORIZED((byte) 0x2A)` to `org.hyperledger.besu.datatypes.TransactionType`. 2. **Codec.** Add a `PQAuthorizedTransactionDecoder` / `PQAuthorizedTransactionEncoder` implementing the 12-field RLP layout above and register them in the `TransactionDecoder` / `TransactionEncoder` dispatch. Extend the `Transaction` model to carry `pqScheme`, `pqPublicKey`, and `pqSignature`, and override sender computation for this type to `keccak256(pqScheme || pqPublicKey)[12:]`, defined only when the PQC signature verifies. 3. **PQ signature verifier.** A `PQAuthorizedSignatureVerifier` that calls the exact same Bouncy Castle 1.83 BCPQC verify path (`AereFalconSupport.verify`, ML-DSA `verifyInternal`) the `0x0AE1` / `0x0AE3` precompiles wrap. Zero new cryptography; reuse the precompile adapters through the same classloader-isolation boundary AIP-7 established. 4. **Validation.** In `MainnetTransactionValidator` (via `TransactionValidatorFactory`), for type `0x2A`: reconstruct `sig_hash`, verify the PQC signature (reject with an INVALID reason on failure), derive the sender, and validate nonce, balance, and intrinsic gas as usual. 5. **Gas.** Extend the `GasCalculator` with `pqAuthorizationIntrinsicGasCost(scheme, payload)` returning the calldata component plus `PQ_VERIFY_GAS[scheme]`, wired into `transactionIntrinsicGasCost`. 6. **Mempool and wire.** Teach `TransactionPool` and the eth/68 `Transactions` / `PooledTransactions` codecs to carry the larger typed body; expose the type in the JSON-RPC transaction serialization (`eth_getTransactionByHash`, receipts). 7. **Fork gating.** Add an `AerePQTx` milestone to `ProtocolScheduleBuilder` / the chain spec, activated at a future block or timestamp distinct from the AIP-7 precompile activation. Before activation, a `0x2A` first 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. 8. **Differential testing.** Gate ship on a test that, for the NIST KAT vectors in `pqc-fork/kat` and randomized cases, asserts the transaction-validation verify agrees bit-for-bit with a `STATICCALL` to 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 compile` then `npx 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 from `sdk-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.