# AIP-3: Sub-Second Block Period (500 ms QBFT) ## Preamble | Field | Value | | --- | --- | | AIP | 3 | | Title | Sub-Second Block Period (500 ms QBFT) | | Author | AERE Foundation | | Type | Standards Track | | Category | Core | | Status | Final | | Created | 2026-07-11 | | Requires | None | | Supersedes | None | | Superseded-By | None | | Ratification | Foundation-ratified (pre-decentralization) | ## Abstract This AIP documents the mid-chain transition of AERE's block period from one second to 500 milliseconds, performed as a QBFT configuration transition rather than a chain restart, so that all history, state, and validator keys were preserved. It is a retro-filed record of a change that is already live on chain 2800. ## Motivation AERE targets settlement-grade latency. A one-second block period puts a hard floor of about one second on inclusion latency and worst-case finality. Halving the block period to 500 ms roughly halves both, which materially improves the feel of interactive flows (swaps, payments, wallet confirmations) without any change to the EVM or to application code. The change had to be done without re-genesising the chain. AERE had already been forced through one full re-genesis (genesis-v2 on 2026-05-07), and losing history again was not acceptable. QBFT supports timed configuration transitions, so the block period could be reduced at a scheduled block with continuity of state and validator identity. ## Specification ### Mechanism Hyperledger Besu QBFT reads the initial block period from `config.qbft` and reads scheduled overrides from a **separate** `transitions` object at the config root. The transition key is `xblockperiodmilliseconds` (note: milliseconds, and note the `x` prefix, which marks the option experimental in Besu). The correct, and easily mis-typed, form is: ```json { "config": { "qbft": { "blockperiodseconds": 1, "epochlength": 30000, "requesttimeoutseconds": 4 }, "transitions": { "qbft": [ { "block": 2137652, "xblockperiodmilliseconds": 500 } ] } } } ``` `transitions` is a sibling of `qbft`, not a child of it. Besu reads `transitions` from the config root (`JsonGenesisConfigOptions.java:47`) and then the sub key `qbft` (`TransitionsConfigOptions.getQbftForks()`). A `transitions` object nested inside `config.qbft` is never read, parses without any error or warning, and is silently inert. ### Activation The 500 ms period is in force from block 2,137,652 on chain 2800. Before that block the network produced one-second blocks; from that block onward it produces 500 ms blocks, with the cadence settling into a steady alternating pattern by block 2,137,662. State, account balances, contract code, and the validator set were unchanged across the transition. Block 2,137,652 is the boundary because it is the first block on chain 2800 whose timestamp equals its parent's. That is impossible while a one second minimum period is in force, and it is the reason the block number is not a free choice: a node configured to transition later than 2,137,652 applies `TimestampMoreRecentThanParent` to block 2,137,652 (`QbftBlockHeaderValidationRulesetFactory.java:80-83`), rejects it, and stops syncing. Full evidence in `aerenew/docs/TRANZITIA-QBFT-SETTLED-2026-07-20.md`. ### Operational note for fresh nodes A node synced from genesis against this config applies the transition automatically. A brand-new isolated chain started from a config like this needs `--sync-min-peers=1` to begin producing, which is an operational detail of bootstrapping, not part of the consensus rule. ## Rationale A configuration transition was chosen over a re-genesis because it preserves the chain's entire history and, critically, the validator keys and account state. Re-genesis was rejected: it destroys history and forces every integrator to re-point. QBFT's timed transition is purpose-built for exactly this kind of parameter change and avoids a client fork. 500 ms was chosen as a conservative first step below one second. It roughly halves latency while staying comfortably within what a small, low-latency, Foundation-operated validator set can sustain. Going lower (for example 250 ms or 100 ms) is possible in principle but was deliberately not attempted here, because tighter periods increase the rate of missed-proposal rounds and stress peer-to-peer timing, and there is no need to push that limit with the current validator topology. ## Backwards Compatibility None at the application layer. Block time is not part of the EVM; contracts do not observe it directly. Note the second-order effect: any code that assumed a one-second block period to convert block counts to wall-clock time (for example a naive per-block reward constant) is now wrong by 2x. AERE hit exactly this: the original staking contract computed rewards against a hardcoded blocks-per-year constant and under-paid once blocks sped up. That was fixed in AereStakingV2 (`0x1D95eF6D17aeAB732dF914Ba2d018c270BC155FC`) by moving reward accrual to timestamps, which is block-time-immune. New code MUST use `block.timestamp`, not block height, for wall-clock math. ## Security Considerations Reducing the block period narrows the timing margin for each consensus round. With the current seven validators under a single operator this is low-risk because network latency between the nodes is small and predictable, and QBFT finalizes within the round among a small set. The honest caveats: - **Small, single-operator validator set.** Sub-second finality here is a property of a seven-node, one-operator topology, not of a large decentralized set. It should not be read as a claim about behavior under a geographically dispersed, independently operated validator set. Reassessing the block period will be part of decentralizing the validator set. - **No consensus-layer change beyond the parameter.** This is a timing parameter, not a new consensus algorithm. It does not alter QBFT's fault tolerance assumptions. ## Reference Implementation and On-Chain Deployment This is a chain-configuration change, so there is no contract address. The authoritative artifacts are the Besu QBFT genesis/config carrying the `xblockperiodmilliseconds: 500` transition at block 2,137,652 (see Erratum 1), and the chain history itself: block intervals before 2,137,652 are 1000 ms and after are approximately 500 ms, verifiable via `eth_getBlockByNumber` timestamp deltas on `https://rpc.aere.network`. Chain ID is 2800. ## Errata **Erratum 1, 2026-07-20. The Mechanism JSON example was self-contradictory, and the activation block was wrong.** As originally published, the prose in Mechanism correctly described a `transitions` block, but the JSON example nested `transitions` **inside** `qbft`, which is a form Besu never reads. The example was: ```json { "_note": "WRONG-FORM, historical record of the original defect. Do not copy.", "config": { "qbft": { "blockperiodseconds": 1, "transitions": { "qbft": [ { "block": 2138451, "xblockperiodmilliseconds": 500 } ] } } } } ``` That fence is retained above only as the historical record of the defect. It is marked WRONG-FORM so the regression test skips it. It must not be copied. Two things were corrected in the body: 1. **Path.** `transitions` moved from inside `config.qbft` to the config root, as a sibling of `qbft`. Settled from the Besu 26.4.0 source (`JsonGenesisConfigOptions.java:47`, `TransitionsConfigOptions.getQbftForks()`). 2. **Activation block.** `2,138,451` corrected to `2,137,652`. The original figure was not measured. Live block timestamps show the one second cadence ending at block 2,137,651, and block 2,137,652 carries the same timestamp as its parent, which a one second period forbids. The `2,138,451` figure is roughly 800 blocks too late and would halt a from-genesis sync at 2,137,652. The key name `xblockperiodmilliseconds` was correct as originally published and is unchanged. The 500 ms value is unchanged. Nothing about the deployed network changed; only this document's description of it was wrong. Evidence and Besu source citations: `aerenew/docs/TRANZITIA-QBFT-SETTLED-2026-07-20.md`. ## Post-Acceptance Outcome Record *Appended 2026-07-20 under AIP-1 section 5. Nothing above this heading was edited.* **Measured block interval: 516.4 ms. Configured target: 500 ms.** The transition worked: history, state and validator keys were preserved, and intervals before block 2,137,652 (see Erratum 1) are 1000 ms while intervals after are approximately 500 ms, as the sections above describe. But the realized mean interval is **516.4 ms (measured)**, roughly 3.3% above the 500 ms target, not 500 ms exactly. This is expected behavior rather than a defect: `xblockperiodmilliseconds` sets a minimum period, and QBFT adds real proposal, gossip and commit latency on top of it. A configured target and a measured cadence are different quantities. **The obligation this creates.** Aere documents state the target as a configuration value and the interval as a measured value, and never present 500 ms as a measured result. Any derived figure that depends on block cadence, including the EIP-2935 lookback duration quoted in AIP-7 and AIP-18 as "roughly 68 minutes", is computed from the target and is therefore an approximation that runs about 3% optimistic; at the measured 516.4 ms cadence, 8191 blocks is approximately 70.5 minutes. ## Copyright Released to the public domain (CC0). No rights reserved.