Patch 0005: the import proof was attempted, and it found what no green test had

A from-genesis sync of a patches-only build stopped at block 9,189,161, the
futureEipsTime activation, with a stateroot mismatch. Two causes, one patch:
upstream bases futureEips on the unreleased amsterdam definition while chain
2800 isolates it on Osaka, and the fleet activates EIP-2935 history storage in
the same fork, one system state write per block that a build without this
patch never performs. With 0005 applied the same node, same data directory,
crossed the activation and keeps importing. The fleet's optional Block-STM
wiring is deliberately omitted and the omission is stated in the patch header.
This commit is contained in:
Aere Network 2026-08-15 16:07:02 +03:00
parent 54f6ffed05
commit 600a716070
2 changed files with 99 additions and 5 deletions

View File

@ -67,11 +67,23 @@ network refuses to finalize blocks without that post-quantum quorum.
The anchor ships in this repository both as source, under `anchor/`, and as
`patches/0003-aere-pq-anchor.patch`. A node built without it has not been shown to import
headers past block 13,014,000: the second client needed an explicit change to accept the sixth
element, and no floor-only build has been observed following the chain past that height. What
this repository does NOT yet contain is an import proof for a node built from these patches
alone, of the kind the rest of this file insists on; until that proof is added here, apply
`0001`, `0003` and `0004` together and treat a build without `0003` as unable to follow the
current chain.
element, and no floor-only build has been observed following the chain past that height. Apply
`0001`, `0003`, `0004` and `0005` together and treat a build missing any of them as unable to
follow the current chain.
## Patch 0005 exists because the import proof was actually attempted
Measured 2026-08-15, on a real from-genesis sync of a build from patches
`0001+0003+0004` alone: the node followed chain 2800 for 9.18 million blocks and stopped at
block 9,189,161, the `futureEipsTime` activation, with `failed persisting block due to
stateroot mismatch`. Not one cause but two, both in `MainnetProtocolSpecs.java`, both now
carried by `patches/0005-aere-eip2935-futureeips.patch`: upstream bases the futureEips
milestone on the unreleased amsterdam definition (Block Access Lists, a different state-root
committer), while chain 2800 isolates it on Osaka; and the fleet activates EIP-2935 historical
block hashes in that same fork, one system state write per block from 9,189,161 onward, which
a build without the patch never performs. No amount of green unit tests found this; importing
the real chain found it in one afternoon. The sync continues past the activation with `0005`
applied, and this section will carry the measured height as it climbs.
Measured 2026-08-15: on a pristine `d2032017` checkout, `git apply` (no flags) accepted
`0001`, `0003` and `0004` in that order, and `./gradlew --no-daemon installDist

View File

@ -0,0 +1,82 @@
AERE patch 0005: futureEips activation isolated on Osaka, plus protocol-native EIP-2935
Why this patch exists, measured 2026-08-15 on a from-genesis sync: a node built
from patches 0001+0003+0004 alone followed chain 2800 to block 9,189,160 and
stopped at 9,189,161, the futureEipsTime activation block, with
"failed persisting block due to stateroot mismatch". Two independent causes,
both fixed here, both in MainnetProtocolSpecs.java:
1. Upstream bases futureEips on amsterdamDefinition, which drags in unreleased
amsterdam-milestone changes (EIP-7928 Block Access Lists with a new state
root committer, EIP-7708 transfer logs, 2D gas accounting). Chain 2800 runs
Osaka; the fleet isolates the AERE milestone on osakaDefinition so the
post-activation block format stays identical to Osaka.
2. The fleet activates EIP-2935 (historical block hashes, ring buffer in the
system contract at 0x0000F90827F1C53a10cb7A02335B175320002935) in the SAME
futureEips fork, so every block from 9,189,161 onward performs one system
state write. A build without it computes a different state root on the very
first activation block, which is exactly what was measured.
Modified upstream file: MainnetProtocolSpecs.java (plus one build.gradle line).
The upstream copyright header is unchanged, per Apache License 2.0 section 4(c);
the in-diff comments state exactly what changed, per section 4(b). The base is
upstream commit d2032017bb3b8cb215a97303980a1e4a643f7180.
One deliberate omission: the fleet's own copy of this change also wires an optional
Block-STM parallel block processor behind an environment gate. That wiring references
source files not published here, and with the gate unset it reproduces the default
builder exactly, so this patch carries the consensus-relevant part only: the Osaka
isolation and EIP-2935. A build from this patch follows the chain; it does not enable
parallel execution.
diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
index 87f1a6b..545aeb2 100644
--- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
+++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
@@ -1321,7 +1321,17 @@ public abstract class MainnetProtocolSpecs {
final boolean isParallelTxProcessingEnabled,
final BalConfiguration balConfiguration,
final MetricsSystem metricsSystem) {
- return amsterdamDefinition(
+ // AERE MILESTONE ISOLATION: base the AerePQC (futureEips) activation on the
+ // OSAKA spec, NOT amsterdam. Mainnet runs Osaka/Fusaka, and inheriting
+ // amsterdamDefinition would have silently activated every unreleased
+ // amsterdam-milestone change on futureEipsTime, notably EIP-7928 Block
+ // Access Lists (new block field + BAL state-root committer), EIP-7708
+ // transfer logs, and EIP-7778/EIP-8037 2D pre-refund gas accounting +
+ // Amsterdam gas calculator. Those are block-format / state-transition
+ // changes that would fork the chain away from Osaka. Basing on osaka makes
+ // the post-activation block format IDENTICAL to Osaka and adds ONLY the two
+ // AERE features below: (a) the 5 PQC precompiles and (b) EIP-2935.
+ return osakaDefinition(
chainId,
enableRevertReason,
genesisConfigOptions,
@@ -1330,7 +1340,15 @@ public abstract class MainnetProtocolSpecs {
isParallelTxProcessingEnabled,
balConfiguration,
metricsSystem)
+ // AERE EIP-2935: historical block hashes (ring buffer in the system
+ // contract at 0x0000f9...2935), wired into the SAME futureEips (AerePQC)
+ // hard fork as the PQC precompiles so a single futureEipsTime activation
+ // delivers both. Forced on unconditionally here: the inherited Prague
+ // definition falls back to FrontierPreExecutionProcessor for QBFT/PoA
+ // chains without system-contract addresses, which would silently skip
+ // EIP-2935 history storage. This override restores it, protocol-native.
+ .preExecutionProcessor(new PraguePreExecutionProcessor())
.precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::futureEips)
.hardforkId(FUTURE_EIPS);
}
diff --git a/evm/build.gradle b/evm/build.gradle
index b416c99..9b4e0a5 100644
--- a/evm/build.gradle
+++ b/evm/build.gradle
@@ -37,6 +37,7 @@ dependencies {
annotationProcessor 'com.google.dagger:dagger-compiler'
implementation project(':crypto:algorithms')
+ implementation 'org.bouncycastle:bcprov-jdk18on'
implementation project(':datatypes')
implementation project(':ethereum:rlp')
implementation project(':util')