xOne has four clients: xOneRS (Rust), xOneTS (TypeScript), xOnePy (Python) and xOneC (C++). The xOneTS, xOnePy and xOneC repositories are private, so their links need access. This post describes what is on main today.
The modes at a glance
| Mode | Flag | Starts from | Keeps afterwards |
|---|---|---|---|
| full | --syncmode full (default) |
genesis | state of a recent window only |
| snap | --syncmode snap |
an empty datadir | state from the pivot, plus a window of headers, bodies and receipts |
| fast | --syncmode fast |
an empty datadir | the same as snap |
| minimal | --minimal |
a snapshot file | the same window as snap |
fast is an alias of snap, as in geth-XDC. All four clients accept it. Archive mode (--gcmode archive) has its own post.
Full
The node asks peers for blocks from genesis. Each block goes through one function, Chain::insert. It checks header and body, executes the block on the parent's own state, and checks receipts, bloom, gas and state root. There is no "trusted" or "skip" path (stages).
The xOneRS net2040 follow notes record one full sync from genesis to block 762,846 in about 95 minutes.
Snap and fast
Snap sync needs an empty datadir. A datadir that holds a chain continues that chain. The steps, from the pivot rule:
- Connect to a peer that shares snap/1. After 120 seconds without one, the start fails. The node never falls back to full sync.
- Download headers up to the peer's head. Each must link to its parent.
- Pick the pivot: the head minus 64 blocks, lowered to the first block with a valid anchor. The anchor is the epoch-switch header's masternode list, the quorum certificates of the next three blocks, and the 3-chain that commits the pivot.
- Pin the switch header. Use
--snap.pins <file>(recommended beyond test networks), or let at least two peers serve the same header. - Download the window of bodies and receipts. Each is checked against its header.
- Read the state of the gap blocks below the pivot. It comes from an xOne peer, or from an archive RPC given with
--snap.gap-rpc. Every proof is checked against the header, so the RPC is not trusted. - Download the state with proven ranges. The root rebuilt from the rows must equal the pivot's state root.
- Install everything in one write, then execute from the pivot plus one.
No block below the pivot is executed. Trust rests on the pin and the peers' header chain. Every block above the pivot is verified and executed.
The window is derived from the rules that read below a block, not fixed (retention). In the net2040 runs it was 1,799 bodies and receipts. Below the window, RPC answers with a typed unsupported history error. It never returns an empty result.
Minimal
Minimal mode keeps the same retention as a snap node. It gets there from a file, not from peers:
xone snapshot export --network net2040 --datadir <src> --out net2040.xsnap
xone snapshot import --network net2040 --datadir <dst> --in net2040.xsnap --pins pins.txt
xone --network net2040 --datadir <dst> --minimal
The pins file holds <number> <0x hash> lines. The import checks the anchor against it.
A minimal node never syncs from genesis. After each imported block it prunes below the window (minimal mode). The other three clients use xonets, xonepy and xonec with the same snapshot export, snapshot import and --minimal.
Differences between the clients
- xOneRS: the snap download is held in memory. With
--state.diskthe head state is read from MDBX, and such a node serves no snap/1 or snapshot export (flags). The spec does not cover resuming an interrupted download. Issue #145 tracks disk-backed state. - xOneTS: slots and codes are staged on disk, so a stop before the install resumes from them (source).
- xOnePy: downloaded ranges are written to SQLite as they arrive. A snapshot export needs a preimage for every row. A node that synced from geth refuses with a typed
Preimageerror (notes). - xOneC: the root of the download stands 32 blocks below the tip. The final pivot stands 16 blocks below it. The node then heals the trie with
GetTrieNodes. Its Apothem notes describe snap sync from geth, nethermind and besu peers, with--snap.gap-rpc(net2040, Apothem).
What runs on net2040 today
The latest tracker comment on xOneRS#140 is from 2026-11-14 18:30 UTC, with the net2040 tip at block 840762.
| Client | full | snap | fast | minimal |
|---|---|---|---|---|
| xOneRS | lag 0 | lag 0 | lag 0 | lag 0 |
| xOneTS | lag 1 | two nodes, both down | two nodes: one at lag 0, one down | two nodes, lag 0 |
| xOnePy | lag 0 | lag 2 | not built | lag 0 |
| xOneC | lag 0 | lag 0 | not built | lag 0 |
Lag is the number of blocks behind the archive node. For xOnePy and xOneC the tracker says "not built" and "alias of snap, not deployed". The flag still works and maps to snap. The tracker also lists archive nodes for all four clients. The xOneTS one was still syncing from genesis.
The xOnePy and xOneC snap nodes hold block-producer seats. The tracker shows 314 seals for xOnePy and 908 for xOneC, 0 misses each.
Measured on net2040
These numbers come from each client's ops notes, from different builds on 2026-11-12 and 2026-11-13. The net2040 state is small (9,532 accounts in the xOneRS run), so they do not predict mainnet or Apothem.
| Client | Snap, start to tip | Data moved | Datadir at tip |
|---|---|---|---|
| xOneRS | 10 s | 2 MB | 72 MB |
| xOneTS | 17.8 s | 1.7 MB | 15,881,704 B |
| xOnePy | 34.1 s | 8.9 MB | 20,687,035 B |
| xOneC | not recorded (download 4.569889 s) | 8.121088 MB | not recorded for that run |
Sources
- xOneRS: net2040-sync-modes.md, spec/snap/pivot.md, spec/bootstrap/snap-install.md, docs/cli/flags.md
- xOneTS (private): net2040-ts-sync-modes.md, config.ts
- xOnePy (private): net2040-py-sync-modes.md, config.py
- xOneC (private): config.cpp, driver.hpp
- Tracker: xOneRS#140
This article describes the source and evidence at its review date. Check the linked repository for current implementation and qualification status.
Explore the engineering evidence