A high-performance blockchain system combining HotStuff-2 BFT consensus with reth EVM execution, featuring parallel mobile device verification for enhanced security.
┌─────────────────────────────────────────────┐
│ N42 Node (IDC) │
│ │
│ ┌───────────┐ ┌────────────────────────┐ │
│ │ reth CLI │ │ ConsensusOrchestrator │ │
│ │ (launch) │ │ (3-way select! loop) │ │
│ └─────┬─────┘ └──┬──────┬──────┬───────┘ │
│ │ │ │ │ │
│ ┌─────▼─────┐ ┌──▼──┐ ┌▼────┐ │ │
│ │ Execution │ │Timer│ │Net │ │ │
│ │ (EVM) │ │ │ │Event│ │ │
│ │ + Witness │ └──┬──┘ └┬────┘ │ │
│ └───────────┘ │ │ │ │
│ ┌───▼─────▼──┐ ┌─▼────────┐ │
│ │ Consensus │ │ P2P Net │ │
│ │ Engine │ │ GossipSub│ │
│ │ (HotStuff2)│ │ (QUIC) │ │
│ └────────────┘ └──────────┘ │
│ │ │
│ ┌──────▼────┐ │
│ │ StarHub │ │
│ │ (QUIC) │ │
│ └─────┬─────┘ │
└────────────────────────────────────┼───────┘
│
┌──────────────────────────────┼──────────────┐
│ │ │ │
┌───▼───┐ ┌────▼───┐ ┌────▼───┐ ┌────▼───┐
│Phone 1│ │Phone 2 │ │Phone 3 │ │Phone N │
│Ed25519│ │Ed25519 │ │Ed25519 │ │Ed25519 │
└───────┘ └────────┘ └────────┘ └────────┘
Design Principles:
- IDC nodes (100-500) handle block production, consensus voting, and state storage
- Mobile devices (~10,000 per node) perform parallel verification — not on the consensus critical path
- 8-second slot target with measured minimum block interval of 0.4s-0.9s
- Event-driven state machine — fully deterministic, testable without async runtime
- HotStuff-2 Consensus: 2-round optimistic commit with 3-round timeout recovery
- BLS12-381 Signatures: Aggregated signatures for compact quorum certificates
- reth v2.4.1 Integration: Uses
n42blockchain/rethcommit23316e3fffromchore/reth-upstream-20260804, plus the checked-in transaction-root cache patch; aligned with Alloy 2.3.0 / REVM 42.0.1. LLVM JIT remains explicit opt-in. - Reserve SBMT Path:
N42_JMT=1explicitly selects the legacy-compatible 16-shard sparse binary backend and RPC surface - Compact Block Propagation: Leader caches execution output; the default follower path skips duplicate EVM execution (cache hit ~3ms)
- QMDB Binary Twig Backend: The QMDB-style 16-shard binary twig tree is the default N42 state-proof backend (
N42_TWIGdefaults on) - Follower Validation Modes: Target production default is cache-hit + QMDB/LtHash commitment verification; six-follower independent full replay is the optional audit mode (see
docs/follower-validation-modes.md) - Optimistic Voting: Followers vote immediately after proposal validation, before block import
- TX Forward to Leader: O(n) message complexity replacing O(n²) gossip for transactions
- Binary TCP Injection: High-throughput transaction injection for stress testing (122K tx/s)
- Execution-Spec Shards CI: Sharded Hive/execution-spec lane for regression testing against upstream reth contracts
- Execution Witness: State witness generation for mobile re-execution
- Mobile Verification Protocol: Ed25519 receipts, commit-reveal anti-copying, LRU code cache
- QUIC Mobile Client:
QuicMobileClientfor phone-side connection to StarHub with deadline-based timeouts - Mobile Reward System: EIP-4895 withdrawal-based rewards distributed per epoch (logarithmic scaling)
- Mobile FFI SDK:
n42-mobile-fficrate exposing C/JNI bindings for Android and iOS integration - Mobile Simulator:
n42-mobile-simbinary for load testing with deterministic BLS key generation - libp2p GossipSub: QUIC transport with content-based message deduplication
- QUIC Star-Hub: High-concurrency mobile connections (up to 10,000 per node)
- Parallel EVM: Optimistic parallel EVM execution (
n42-parallel-evm) for higher throughput - ZK Sidecar Proof System: Asynchronous ZK proof generation with SP1 zkVM backend, phones verify proofs instead of re-executing EVM
n42-26/
├── bin/
│ ├── n42-node/ # CLI entry point (reth NodeBuilder)
│ ├── n42-stress/ # High-throughput stress testing tool
│ ├── n42-mobile-sim/ # Mobile verifier simulator for load testing
│ └── n42-evm-bench/ # EVM benchmarking utility
├── crates/
│ ├── n42-primitives/ # BLS keys, consensus message types
│ ├── n42-chainspec/ # Chain config, ValidatorInfo
│ ├── n42-consensus/ # HotStuff-2 state machine + reth adapter
│ ├── n42-execution/ # EVM config wrapper, witness & state diff
│ ├── n42-parallel-evm/ # Optimistic parallel EVM execution
│ ├── n42-jmt/ # Jellyfish Merkle Tree (Blake3, 16-shard parallel)
│ ├── n42-zkproof/ # ZK sidecar proof system (SP1 + MockProver)
│ ├── n42-zkproof-guest/ # SP1 zkVM guest program (RISC-V ELF)
│ ├── n42-network/ # libp2p GossipSub + QUIC StarHub
│ ├── n42-mobile/ # Mobile verification protocol (no reth deps)
│ ├── n42-mobile-ffi/ # C/JNI FFI bindings for Android & iOS
│ └── n42-node/ # Node type assembly + ConsensusOrchestrator
├── docs/ # Development logs (devlog-01 through devlog-53)
├── scripts/ # Testnet launch scripts
└── DEVLOG.md # Development log index
N42 uses a HotStuff-2 variant — a two-round BFT consensus achieving O(n) message complexity:
Round 1 (Prepare):
Leader ──Proposal──▶ Validators ──Vote──▶ Leader ──▶ PrepareQC
Round 2 (Commit):
Leader ──PrepareQC──▶ Validators ──CommitVote──▶ Leader ──▶ CommitQC ──▶ Block Committed
Timeout Recovery:
Timer expires ──▶ Broadcast Timeout ──▶ Collect 2f+1 ──▶ TC ──▶ NewView ──▶ Next Leader
| Parameter | Value |
|---|---|
| Fault tolerance | f = (n-1)/3 |
| Quorum size | 2f + 1 |
| Leader selection | Round-robin (view % n) |
| Timeout backoff | min(base × 2^consecutive_timeouts, max) |
| Signature scheme | BLS12-381 (min_pk variant) |
| Message Type | Signing Content |
|---|---|
| Vote (Round 1) | view (8B LE) || block_hash (32B) |
| CommitVote (Round 2) | "commit" || view (8B LE) || block_hash (32B) |
| Timeout | "timeout" || view (8B LE) |
Validators maintain a locked_qc (highest QC seen). Before voting on a proposal:
proposal.justify_qc.view >= locked_qc.view // Must hold, otherwise reject
Mobile devices perform parallel block verification as an additional security layer, operating independently from the consensus critical path.
1. IDC executes block → captures ExecutionWitness
2. Witness compacted (remove cached bytecodes) → VerificationPacket
3. Packet pushed to phones via QUIC (QuicMobileClient)
4. Phone re-executes → generates VerificationReceipt (Ed25519)
5. IDC aggregates receipts → threshold (2/3) attestation
6. Per-epoch: MobileRewardManager calculates logarithmic rewards
7. Rewards injected as EIP-4895 withdrawals into next block's PayloadAttributes
Phone: commitment_hash = keccak256(block_hash || result || random_nonce)
──── send commitment ────▶ IDC
[wait for window close]
──── send reveal (result + nonce) ────▶ IDC
IDC: verify hash(block_hash || result || nonce) == commitment_hash
Verification rewards are distributed per epoch (default: 21,600 blocks ≈ 24h at 4s block time):
| Parameter | Value | Description |
|---|---|---|
| Epoch length | 21,600 blocks | Reward settlement interval |
| Max rewards per block | 32 | Throughput cap for large validator sets |
| Reward queue limit | 1,000,000 | Prevents unbounded memory growth |
| Address derivation | keccak256(bls_pubkey_bytes)[12..] |
BLS pubkey → ETH address |
| Scaling | Logarithmic | Diminishing returns per attestation count |
Rewards are injected as Withdrawal entries in PayloadAttributes — no transaction signing, gas, or nonce required.
| Operation | Ed25519 | BLS12-381 |
|---|---|---|
| Sign | ~14 us | ~320 us |
| Verify | ~38 us | ~763 us |
| Signature size | 64 B | 96 B |
| Mobile suitability | Excellent | Poor |
| Mode | TPS | Block Cap | Notes |
|---|---|---|---|
| TCP Inject + Pool Gate + Fast Propose | 45,668 | 48K | Zero nonce gaps, zero stuck tx |
| TCP Inject + Fast Propose | 47,527 | 48K | Sustained injection 122K tx/s |
| Sync Wave + Fast Propose | 25,797 | 48K | Inject→drain→inject cycle |
| Cache Hit Fast Path | 90,949 | 90K | Peak single-block TPS |
| 2s Slot + All Optimizations | 13,858 | 48K | Production-like timing |
Latest cadence follow-up: devlog-82 uses continuous per-node ingest with repeated 90K blocks. It reached 67.7K active-block TPS and 90K max block TPS, but wall sustained TPS over 90.3s was 13.5K because remaining gaps are view-timeout/cadence stalls plus a leader build/broadcast path near the 2s slot budget.
| Optimization | Impact |
|---|---|
| Compact Block (cache hit) | Follower import: 209ms → 3ms |
| Optimistic Voting | R1 vote_delay: 363ms → 0ms |
| OrdMap + Packing | Pool overhead: 430ms → 23ms |
| Channel Split + Dedicated Runtime | R2 p50: 369ms → 221ms |
| TX Forward to Leader | O(n²) gossip → O(n) direct |
| Validators | Quorum | Time |
|---|---|---|
| 4 | 3 | 3.4 ms |
| 10 | 7 | 7.9 ms |
| 67 | 45 | 50.8 ms |
| 100 | 67 | 76.0 ms |
| 333 | 221 | 247.6 ms |
| 500 | 333 | 388.0 ms |
Both configurations are well within the 8-second slot target.
- Rust 1.97+ (development and CI are pinned to Rust 1.97.1)
- N42
reth2.4.1 fork checked out at../reth(23316e3ff8ad), with the patch below applied - Android local builds: JDK 17 recommended for Gradle/Kotlin
- SP1 toolchain v4.2.1 (optional, for ZK proof guest build):
curl -L https://sp1up.succinct.xyz | bash && sp1up --version v4.2.1
git clone https://github.com/n42blockchain/reth.git ../reth
git -C ../reth checkout 23316e3ff8adca8c3bd5085ff0565fcae019202a
bash scripts/apply-reth-patches.sh ../rethThe patch supplies the transaction-root cache APIs used by n42-node and
records the root when reth builds a payload. CI and Docker apply this same patch
before compiling. Re-running the script on an already patched checkout is safe;
an incompatible checkout fails before building.
# Verify the full workspace against the patched N42 reth fork
cargo check --all-targets --locked
# Main binaries
cargo build --release -p n42-node-bin -p n42-stress -p e2e-test
# Optional mobile / SDK artifacts
cargo build --target aarch64-apple-ios-sim -p n42-mobile-ffi
JAVA_HOME=$(/usr/libexec/java_home -v 17) \
./mobile/android/gradlew :app:compileDebugKotlingit -C ../reth fetch origin
git -C ../reth checkout 23316e3ff8adca8c3bd5085ff0565fcae019202a
bash scripts/apply-reth-patches.sh ../rethWhen upgrading the baseline, update the CI refs and validate the patch and
Cargo.lock together before changing this commit.
# Development node
./target/debug/n42-node node --dev
# With custom chain spec
./target/debug/n42-node node --chain /path/to/genesis.json
# Mobile simulator (connect to running node's StarHub)
./target/debug/n42-mobile-sim --starhub-ports 9100,9101,9102 --phone-count 100 --duration 60# Run all integration tests
cargo test -p n42-consensus --test integration_test
# Run specific module
cargo test -p n42-consensus --test integration_test genesis_bootstrap
cargo test -p n42-consensus --test integration_test fault_tolerance
cargo test -p n42-consensus --test integration_test stress_performance
# With output
cargo test -p n42-consensus --test integration_test -- --nocapture| Module | Tests | Coverage |
|---|---|---|
| genesis_bootstrap | 3 | Initial state, single-validator genesis, first block commit |
| multi_node_consensus | 6 | 4/7/10/100-node consensus, consecutive blocks, leader rotation |
| mobile_verification | 6 | Receipt signing, aggregation threshold, dedup, commit-reveal |
| fault_tolerance | 9 | f-crash, byzantine votes, duplicate votes, view change, safety |
| boundary_conditions | 7 | Single-node instant commit, exact quorum, f+1 crash stall |
| stress_performance | 4 | 100 consecutive blocks, 500 validators, 1000 mobile receipts |
| stability | 4 | 1000 mixed views, channel leak check, locked_qc monotonicity |
# Run ignored benchmark-style tests explicitly
cargo test -p n42-consensus --test performance_bench --release -- --ignored --nocapture
cargo test -p n42-node --test comm_stress_bench --release -- --ignored --nocapture# All workspace unit/integration tests
cargo test --workspace
# Specific crate
cargo test -p n42-consensus
cargo test -p n42-primitives
cargo test -p n42-mobile
cargo test -p n42-jmt
cargo test -p n42-zkproof
cargo test -p n42-node# Build the node binary and the E2E harness
cargo build --release -p n42-node-bin -p e2e-test
# Run correctness-oriented E2E scenarios
target/release/e2e-test --binary target/release/n42-node --scenario 5
E2E_SCENARIO_FILTER=1,3,4,5,8,12 \
target/release/e2e-test --binary target/release/n42-node
# LAN pressure / timing work
scripts/testnet.sh
scripts/step_stress.shUse tests/e2e/README.md as the source of truth for the current split between:
- correctness CI
- manual integrated E2E
- LAN pressure / timing optimization
Additional lanes introduced in this branch:
- execution-spec shard workflows in
.github/workflows/execution-spec-shards.yml - integrated 7-node smoke via
scripts/test-7node-integrated-smoke.sh - reth image packaging helpers in
.github/scripts/hive/
bin/n42-node bin/n42-stress bin/n42-mobile-sim
└── n42-node └── reth, alloy └── n42-mobile ─── ed25519-dalek
├── n42-consensus ──┬── n42-primitives ── blst (BLS12-381)
│ └── n42-chainspec ──── n42-primitives
├── n42-execution ──┬── reth-evm, reth-revm
│ └── n42-chainspec
├── n42-jmt ────────── jmt, blake3, rayon (16-shard parallel)
├── n42-zkproof ──── alloy-primitives, serde, tokio, sp1-sdk (optional)
│ └── n42-zkproof-guest (SP1 RISC-V, built separately)
├── n42-network ────┬── n42-primitives
│ ├── n42-mobile ─── ed25519-dalek
│ └── libp2p (gossipsub, quic)
└── reth-node-builder, reth-ethereum-*
n42-mobile-ffi (Android/iOS SDK)
├── n42-mobile
├── n42-primitives
├── n42-chainspec
└── n42-execution
Key design: n42-mobile has zero reth dependencies — only alloy-primitives, ed25519-dalek, lru, serde.
n42-mobile-ffi compiles as staticlib + cdylib, exposing a C ABI and JNI bridge for Android.
n42-jmt provides Jellyfish Merkle Tree with Blake3 hashing and 16-shard parallelism for state proofs.
n42-zkproof provides backend-agnostic ZK proof generation (trait ZkProver) with MockProver for testing and Sp1Prover for real SP1 zkVM proofs.
n42-zkproof-guest is the SP1 RISC-V guest program, built separately with cargo prove build (excluded from workspace).
// Event-driven state machine — no internal event loop
let engine = ConsensusEngine::new(my_index, secret_key, validator_set,
base_timeout_ms, max_timeout_ms, output_tx);
// External driver feeds events
engine.process_event(ConsensusEvent::BlockReady(block_hash))?;
engine.process_event(ConsensusEvent::Message(msg))?;
engine.on_timeout()?;
// Read outputs from channel
match output_rx.recv() {
EngineOutput::BroadcastMessage(msg) => network.broadcast(msg),
EngineOutput::BlockCommitted { view, block_hash, .. } => storage.commit(block_hash),
EngineOutput::ViewChanged { new_view } => log::info!("view changed to {}", new_view),
// ...
}// Sign receipt (mobile side)
let receipt = sign_receipt(block_hash, block_number, true, true, timestamp, &ed25519_key);
// Verify and aggregate (IDC side)
receipt.verify_signature()?;
let mut aggregator = ReceiptAggregator::new(threshold, max_blocks);
aggregator.register_block(block_hash, block_number);
if aggregator.process_receipt(&receipt) == Some(true) {
println!("Block attested by sufficient mobile verifiers");
}
// QUIC client (phone-side connection)
let client = QuicMobileClient::connect("127.0.0.1:9100", &ed25519_pubkey).await?;
let packet = client.receive_packet(Duration::from_secs(30)).await?;
client.send_receipt(&receipt_bytes).await?;
// Reward distribution (IDC side, per epoch)
let mut rewards = MobileRewardManager::new(blocks_per_epoch, base_reward_wei);
rewards.record_attestation(&bls_pubkey_hex);
// On payload build:
let withdrawals = rewards.take_pending_rewards(committed_block_number);
payload_attributes.withdrawals = Some(withdrawals);| Parameter | Default | Description |
|---|---|---|
slot_time_ms |
8000 | Target block interval |
base_timeout_ms |
4000 | Initial view-change timeout |
max_timeout_ms |
8000 | Maximum timeout (cap for exponential backoff) |
chain_id |
4242 | N42 chain identifier |
| Parameter | Env Variable | Default | Description |
|---|---|---|---|
| Enable | N42_ZK_PROOF |
0 (disabled) |
Set to 1 to enable ZK sidecar |
| Interval | N42_ZK_INTERVAL |
300 |
Blocks between proof generations |
| Backend | N42_ZK_BACKEND |
mock |
Prover backend (mock / sp1) |
| Mode | N42_ZK_MODE |
cpu |
SP1 mode (cpu / cuda / mock) |
RPC methods: n42_zkProof(block_number), n42_zkProofByHash(block_hash), n42_zkLatest(), n42_zkVerify(block_number), n42_zkStatus()
| Parameter | Default | Description |
|---|---|---|
| GossipSub mesh degree (D) | 8 | Target peers in mesh |
| GossipSub heartbeat | 1s | Mesh maintenance interval |
| StarHub max connections | 10,000 | Mobile devices per node |
| StarHub idle timeout | 300s | Inactive connection timeout |
| QUIC handshake timeout | 5s | Mobile must send pubkey within |
| Topic | Path | Purpose |
|---|---|---|
| Consensus | /n42/consensus/1 |
All HotStuff-2 messages |
| Block Announce | /n42/blocks/1 |
Header-first block dissemination |
| Verification | /n42/verification/1 |
Mobile verification receipts |
See LICENSE for details.