XDC's legacy client is a 2018 Geth fork
XDPoSChain (the canonical XDC client, v2.7.0) is a hard fork of go-ethereum ~1.8.3. It selectively backported the toolchain and EVM rules — but kept 2018-era state storage, sync, and networking. Upstream geth spent the next seven years rebuilding exactly those layers.
This is the engineering "why" behind the multi-client effort
The Architecture page shows the live result — 4 independent clients on mainnet. This page is the deep rationale: precisely what modern geth delivers, sourced and verified, and how it maps to XDC operator pain today.
What the 2018 base costs operators
Seven compounding costs fall directly out of the legacy storage, sync, and networking architecture — higher infrastructure spend, slower and more fragile node operations, and accelerating maintenance risk.
Full nodes grow without bound
~919 GB and rising. The hash-scheme trie accumulates dead nodes and there's no online pruning (no snapshot → no prune-state), so operators must periodically resync to reclaim space.
Initial sync takes days
Fast-sync only — state is fetched node-by-node by hash, millions of latency-bound round-trips. New masternode / RPC bring-up and recovery are painfully slow.
Archive nodes are ~2.5 TB & slow
The hash-scheme archive keeps every trie node ever written; reads walk the trie. Costly and high-latency for explorers, indexers, and analytics.
Fragile, aging peering
eth/62–63 with no ForkID and no request IDs. The node can't cheaply reject wrong-fork peers and needs custom bridging to talk to modern clients.
~7 years of maintenance drift
The base predates every modern subsystem. Security backports are hard, new EIPs can't land cleanly, and developer mindshare is low — risk compounds the longer the gap persists.
No modern data / history features
No EIP-4844 blobs, no era-file / EIP-4444 history expiry. No path to offload history or offer modern data availability.
P7 · Execution internals stuck in 2018
Single-threaded code paths — no trie prefetcher, no parallel state hashing, LevelDB only. Lower throughput headroom under load than any modern client.
What geth shipped, 1.8.3 → 1.17.3
Every version below is the first stable release that shipped the feature, verified against upstream git history and Ethereum Foundation release blogs. None of it exists in the XDC 2.7.0 base.
Freezer / ancients + ForkID (eth/64)
Cold blocks/receipts split into append-only flat files (~90k-block cutoff); EIP-2124 ForkID rejects incompatible peers at handshake.
State snapshot (flat layer)
O(1) account/storage reads and the foundation for snap sync.
Snap sync + offline pruning + eth/66 request IDs
Range-proof state download (snap default v1.10.4); request IDs match responses to requests.
eth/67 — drops GetNodeData
The prerequisite for PBSS: a node serving raw trie nodes by hash can't move to path-based keys.
The Merge (PoS) + Engine API
Internal PoW sealing retired (fully removed v1.14.0). XDC keeps XDPoS — adopts the cleanup, not the CL split.
Pebble DB + eth/68
Actively-maintained DB engine (default v1.12.0) replacing the one-person LevelDB project.
Path-based state storage (PBSS)
Online pruning by default — bounded full-node state. Default for new full nodes in v1.14.0. The headline fix.
Era files (EIP-4444 path)
Exportable, compressible history — lets nodes offload ancient history.
eth/69 → eth/70
History-range advertisement (history-expiry-aware peering) and the latest wire protocol.
Each pain point maps to a shipped capability
The migration isn't speculative — every remedy is a feature already battle-tested on Ethereum mainnet, and now measured live on XDC mainnet. This is the heart of the case.
| Pain | Remedy (upstream capability) | First shipped | Outcome |
|---|---|---|---|
| P1 node bloat + resync | PBSS + online pruning | v1.13.0 / v1.14.0 | Full node stops growing; no resync |
| P2 days-long sync | Flat snapshot + snap sync | v1.9.13 / v1.10.0 | Fresh node to head in hours |
| P3 huge slow archive | PBSS archive + era files | v1.13.0 / v1.13.12 | Smaller, flat O(1) reads |
| P4 fragile peering | ForkID + request IDs | v1.9.0 / v1.10.0 | Robust peering; no bridge hacks |
| P5 maintenance drift | Within 1 minor of upstream | ongoing | Security fixes + EIPs land; mindshare |
| P6 no modern features | blobpool / 4844, era / 4444 | v1.12.1 / v1.13.12 | Optionality for blobs + history offload |
| P7 slow internals | Trie prefetch, parallel hashing, Pebble | v1.10–v1.11 | Throughput headroom |
Why hash-scheme nodes grow — and PBSS bounds them
The single biggest operator win, now measured. A hash-scheme node accumulates dead trie nodes with no online pruning; the modern PBSS node keys nodes by path with a bounded, self-pruning history window — so the live-state portion stops growing.
* Modern node 303 GB (281 GB freezer + ~22 GB live PBSS) is measured on an XDC mainnet production node synced to head (~block 103.3M, syncing = false, 24 peers). Legacy ~919 GB full / ~2.5 TB archive are the corresponding hash-scheme figures; geth's own docs cite a hash-scheme full node growing >650 GB at +14 GB/week (illustrating the mechanism).
Consensus-neutral by construction
This is an engine-room swap, not a constitutional change. For any block, a migrated node must produce an identical post-state root, receipts root, and block hash to canonical XDC — a hard, harness-enforced constraint.
Nothing at the consensus layer changes
The migration adopts modern execution, storage, and sync underneath an unchanged XDPoS. Bit-exact parity is the release gate.
XDPoS consensus
v1 (round-robin) and v2 (HotStuff-style BFT), QC/TC, epochs, masternode selection — unchanged.
Internal block production
Masternodes seal blocks via XDC's own engine path. We do not adopt geth's external-CL / Engine-API model.
Block & header RLP / hashes
Validators / Validator / Penalties fields, byte-exact encoding, genesis hashes — unchanged.
Economics
Fixed 12.5 gwei base fee (no burn), TRC21 fee routing, rewards (90/10), penalties — unchanged.
Chain identity
Chain IDs 50/51, genesis, fork schedule, system contracts 0x88/0x89/0x90 — unchanged.
Parity harness
Every consensus-touching change validated bit-exact vs canonical XDC — across normal blocks and fork boundaries.
Fact-checked against primary sources
The technical claims on this page were verified by a deep-research pass — fan-out search, then adversarial 3-vote verification (a claim needed a majority of refutes to be killed).
| Verified claim | Source |
|---|---|
| Freezer split: 79 GB ancients vs 60 GB LevelDB at block 7.77M | EF blog v1.9.0 · geth docs |
| PBSS officially shipped v1.13.0, default for new full nodes v1.14.0 | EF blog v1.13.0 · v1.11.0 notes |
| Hash-scheme node >650 GB, +14 GB/week; PBSS prunes online | geth dbpruning docs |
| Snap sync: leaves + range proofs + healing; default v1.10.4 | geth sync-modes · devp2p snap |
| eth/67 dropping GetNodeData was the prerequisite for PBSS | EIP-4938 · EF blog v1.13.0 |
| ForkID (eth/64) rejects incompatible peers before block exchange | EIP-2124 / EIP-2364 |
Full citations and the code-ship-vs-feature-ship reconciliation are in the project's EVIDENCE-BASE doc (see PR below).
Complete the migration to production
The work is already underway and de-risked: XDPoS has been re-based onto a pristine go-ethereum v1.17.3 base (branch reset/integration-1.17.3) and verified syncing mainnet to head with zero consensus divergence. The proposal is to finish it to a tagged production release, with the parity harness as the mandatory gate.
Approve completion
Finish the modern-geth line to a tagged production release with an operator migration path.
Parity harness as gate
Bit-exact state-root / receipts-root / block-hash parity on every consensus-touching change.
Resource mainnet + RPC parity
Validate mainnet across fork boundaries; close the documented RPC parity gaps.