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.
8.0 KiB
aere-block-stm
A real Block-STM optimistic-concurrency parallel EVM-transaction executor for AERE, implemented from scratch in Rust with zero external dependencies.
It executes a batch of transactions across many CPU cores and commits a single state root that is provably identical to sequential execution (deterministic serializability), which is exactly what makes the resulting root a valid rollup commitment.
Reference algorithm: Gelashvili, Spiegelman, Xiang, Danezis, Li, Malkhi, Xia, Zhou, "Block-STM: Scaling Blockchain Execution by Turning Ordering Curse to a Performance Blessing" (arXiv:2203.06871, 2022) - the same design that powers Aptos, and the family Sui and Monad build on.
Honest scope (read this first)
What this is NOT: this does not make AERE's Layer-1 base execution parallel. AERE's L1 runs Hyperledger Besu, which executes transactions sequentially. Parallelizing Besu itself would be a fork of a Java execution client, a multi-month effort, and is explicitly out of scope here. Nothing in this module changes how the AERE L1 mainnet executes blocks. The L1 stays Besu-sequential.
What this IS: a standalone, correct, benchmarked parallel executor that
AERE's rollup layer can use. AERE already ships a Rollup-as-a-Service stack
(contracts/contracts/raas/AereRaaSFactory.sol +
AereRollupSettlement.sol). A rollup sequencer executes a batch of L2
transactions off-chain, computes the resulting state root, and commits it via
AereRollupSettlement.proposeStateRoot(epoch, root, l2BlockHash). That batch
execution is a pure, well-defined, embarrassingly-parallelizable workload, and
it is the concrete, real path to parallel execution on AERE. This module is that
executor, wired into the sequencer (see ../rollup-sequencer/).
So the honest one-line framing is:
Real Block-STM parallel executor, wired into the AERE rollup sequencer; the L1 base layer stays Besu-sequential.
What it implements (the real algorithm)
- Multi-version memory (MVMemory) -
src/mvmemory.rs. For every state key, an orderedtxn_index -> cellmap. A read by txnireturns the write of the highest txnj < i, giving each speculative execution a serial-equivalent view. Sharded across 256 mutexes so non-conflicting txns never contend. - Read-set / write-set tracking -
src/parallel.rs. Each execution records exactly what it read (and the version it read) and what it wrote. - Dynamic dependency detection - a read that hits an
ESTIMATEplaceholder (left by a lower txn that is currently being re-executed) aborts the reader and parks it on the blocking txn; it is re-armed when the blocker finishes. No static dependency analysis, no condvars. - Abort + re-execution -
src/scheduler.rs. Collaborative validation: validating txnire-checks its read set against current MVMemory; on mismatch it is aborted, its incarnation bumped, its writes turned into ESTIMATEs, and it is rescheduled. Cursors are decreased so the affected suffix is revalidated. - Serial-equivalent commit - the block is done only when every txn has been validated against the final memory. The committed state provably equals sequential execution.
- State model - account balances + a key/value contract store
(
src/types.rs), with genuine EVM-style transactions whose control flow and written values depend on reads:Transfer,Sweep(value-dependent),Increment(the canonical shared-counter serializability probe), andAmmSwap(two-slot DeFi pool conflict). - keccak256 state root -
src/keccak.rs, a from-scratch keccak256 (verified against known-answer vectors) folds the final state into a real, EVM-compatible 32-byte root for on-chain commitment.
Zero external crates: the scheduler, MVMemory, PRNG and keccak are all built on
std. Build is fully offline and reproducible ($0).
Correctness guarantee (the hard part, proven)
The whole point of Block-STM is that the parallel result must equal
sequential execution. Both paths call the exact same transaction-semantics
function (src/vm.rs::execute_txn), so any divergence can only come from a
concurrency-control bug. The harness proves there are none:
cargo run --release -- harness
runs 6 workload profiles x 200 randomized batches x {1,2,4,8,16} threads =
6000 comparisons (1200 distinct randomized batches), and asserts the
Block-STM committed state equals the sequential oracle for every one. Profiles
include adversarial 100%-conflict cases (all-hot, brutal-single-counter).
Result:
total (batch x threads) comparisons: 6000
mismatches : 0
RESULT: PASS -- parallel Block-STM == sequential on ALL cases
cargo test --release additionally checks:
shared_counter_is_serialized: 500 txns all incrementing one slot yield exactly 500 at 1/2/4/8/16 threads (naive parallelism would lose updates).parallel_equals_sequential_random: 40 seeds x 4 thread counts.keccak_known_answer: keccak256("") and keccak256("abc") match the standard vectors.
Measured speedup (honest numbers)
Measured on the AERE infra box: 16 physical cores, Linux 6.8, Rust 1.96.
20,000 transactions per batch, each carrying a realistic simulated EVM execution
cost (gas = 80 keccak-rounds, a stand-in for the microseconds a real EVM tx
spends in the interpreter). Speedup is versus the single-threaded sequential
oracle. Median of 7 runs.
| Workload | 2 cores | 4 cores | 8 cores | 16 cores | abort % @16 |
|---|---|---|---|---|---|
| Low-conflict (~2% hot-touch) | 1.78x | 3.22x | 5.71x | 8.59x | 0.3% |
| Medium-conflict (~15%) | 1.87x | 3.54x | 6.24x | 9.31x | 1.9% |
| High-conflict (single hot counter, 15% of txns) | 1.70x | 3.04x | 4.87x | 4.62x | 24% |
| Pathological (EVERY tx hits ONE slot) | 0.85x | 0.85x | 0.82x | 0.73x | 19% |
Honest reading of these numbers:
- Low/medium-conflict batches scale nearly linearly: ~8.6-9.3x on 16 cores.
- The high-conflict batch still gets ~4.6x because only ~15% of its transactions touch the single hot slot; the rest are independent transfers.
- The pathological batch (every transaction increments the same slot) has zero available parallelism. Block-STM correctly serializes it and is actually slower than sequential (0.73x) because it pays for aborted speculation. This is the expected, honest worst case: parallelism collapses under total conflict, and no correct parallel executor can beat sequential when there is nothing to parallelize.
There is also an honest floor case worth stating: if transactions do
near-zero compute (gas = 0), the concurrency-control overhead dominates and
the parallel executor is slower than sequential. That is why the benchmark
charges each transaction a realistic execution cost - real EVM transactions are
compute-heavy (opcode interpretation, keccak, ecrecover), which is precisely the
regime Block-STM is designed for. Run bench 0 to see the floor, bench 200
for heavier txs.
Reproduce:
cargo run --release -- bench # gas=80, 20k txns (default)
cargo run --release -- bench 200 # heavier txs
cargo run --release -- bench 0 # trivial txs: scheduler-overhead floor
Sequencer integration
The rollup sequencer calls the execute subcommand (a JSON-out CLI) with a batch
and gets back a self-checked keccak256 state root to commit on-chain. See
../rollup-sequencer/README.md and ../rollup-sequencer/src/blockstm/ for the
TypeScript wrapper and the exact proposeStateRoot flow. The library API
(execute_block_parallel, execute_block_sequential, state_root) is also
usable directly from Rust.
Every execute run computes the batch both sequentially and in parallel and
refuses to emit a root unless they match (parallelEqualsSequential: true), so a
concurrency bug can never silently produce a bad rollup commitment.
Build
source ~/.cargo/env # on the AERE infra box
cargo build --release
cargo test --release
cargo run --release -- harness
cargo run --release -- bench