Nethereum.MainnetChain.Server
NuGet:
Nethereum.MainnetChain.Server| Source:src/Nethereum.MainnetChain.Server/
Nethereum.MainnetChain.Server
PREVIEW — This package is in preview. APIs may change between releases.
A read-only Ethereum mainnet follower with a JSON-RPC server, built in .NET. It syncs and validates blocks from the devp2p network, keeps a local state database, and serves that state over JSON-RPC — and, optionally, to other devp2p peers. It follows and serves the chain; it does not propose or mine.
Install
dotnet tool install -g Nethereum.MainnetChain.Server
Run it
nethereum-mainnetchain --beacon http://127.0.0.1:5052
This runs the follower with the default options: it snap-syncs a recent state snapshot, validates every block as it follows the head, and serves JSON-RPC on http://127.0.0.1:8545. State is kept in ./mainnet-data. Run it again to resume where it left off.
The --beacon endpoint is a beacon-node REST API: the follower cross-checks the chain it's following against the beacon-attested chain, rejecting any block that disagrees, and eth_getBlockByNumber with the finalized / safe tags becomes meaningful.
A beacon endpoint is how the follower verifies consensus, so the server refuses to start without one — a forgotten --beacon fails loudly instead of silently trusting whatever peers serve. The alternative is --engine, which lets a consensus client drive the follower through the Engine API (see below); --engine and --beacon together are refused. To run without a beacon anyway (not recommended for mainnet), opt out explicitly:
nethereum-mainnetchain --allow-unverified-consensus
Options
SERVER:
-p, --port <PORT> JSON-RPC port (default: 8545)
--host <HOST> Host to bind to (default: 127.0.0.1)
-v, --verbose Verbose logging
--metrics-port <PORT> Expose Prometheus /metrics on this port (default: off)
SYNC:
-d, --data-dir <DIR> Chain data directory (default: ./mainnet-data)
--no-snap Skip snap sync (snap is the default; a full sync is impractical for mainnet)
--trusted-peer <ENODE> Pinned enode:// peer to always dial
--listen-port <PORT> Also serve snap/eth to other peers (default: off)
CONSENSUS:
--beacon <URL> Beacon REST endpoint (enables the light-client gate)
--allow-unverified-consensus Start WITHOUT a beacon (no consensus verification; NOT recommended for mainnet)
--engine Serve the Engine API so a consensus client (Lighthouse) drives the follower; excludes --beacon
--genesis <PATH> Custom chain: a standard genesis.json (chain id, fork schedule, alloc). Needs --engine; runs with no peers, no snap and no light client, and serves debug_setHead when --MainnetChain:RpcModules includes debug
--engine-port <PORT> Engine API port (default: 8551)
--engine-jwt-secret <PATH> JWT secret file shared with the consensus client (default: <data-dir>/jwt.hex, created if missing)
MAINTENANCE:
--wipe-state One-shot: clear state, keeping the block archive, then exit; the next start re-runs the state sync
--rebuild-state-from-flat One-shot: rebuild stale storage-trie nodes from flat state, then verify
--verify-flat One-shot: write-free flat/trie verify at the committed head, logs the full breakdown, then stops
--verify-flat-sample <N> With --verify-flat: verify only the first N accounts per shard (0 = full walk, default)
--node-key-file <PATH> Persisted node identity key (default: <data-dir>/nodekey, created on first run)
TUNING (common advanced):
--flush-cadence <N> Persist state every N blocks (default 1 = every block)
--prefetch-workers <N> Dedicated threads that prefetch each block's transactions into the RocksDB caches (default 0 = off)
--state-root-workers <N> Dedicated threads that update storage tries in parallel while computing each block's state root (default 4; 1 = sequential)
--checkpoint-every <N> Checkpoint cadence in blocks (default 50000)
--cache-size <SIZE> RocksDB block cache size, e.g. 4G / 512M (default 1G)
--rocksdb-statistics Collect RocksDB statistics and log block-cache counters every 100 blocks
--replay-blocks <N> One-shot performance gate: re-execute the N blocks after the executed head from the stored bodies, verify every root, exit 0 (all roots matched) or 1
--replay-report <PATH> JSON report written by --replay-blocks
Flags also accept the --name=value form.
Settings can also come from an appsettings.json MainnetChain section or MainnetChain__-prefixed environment variables. Run --help for the everyday flags, and --help-advanced for the full set of expert --MainnetChain:* knobs, --state-root-workers and the replay flags (peer/batch sizing, journal and trie-node history windows, freezer and split-history storage, the RPC-serving caps, and more).
Advanced
Operational knobs you reach for occasionally:
- Engine API.
--engineserves the Engine API on--engine-port(default 8551, bound to 127.0.0.1), authenticated with the JWT secret at--engine-jwt-secret(default<data-dir>/jwt.hex, created on first run as a 32-byte hex file readable by its owner only on Unix). The follower executes nothing until the consensus client's firstforkchoiceUpdated. With--genesis <path>as well it runs a custom chain engine-only: no peers, no snap sync, no light client, anddebug_setHead(when--MainnetChain:RpcModulesincludesdebug) rewinds the chain to an earlier block. - What the JSON-RPC port serves. Only the
eth,netandweb3modules by default; addtxpoolordebug(adminis accepted but this server registers noadmin_*method) with--MainnetChain:RpcModules eth,net,web3,debug. A request whoseHostis notlocalhost, the bind host, an IP address or listed in--MainnetChain:RpcAllowedHostsgets403, a content type other than JSON gets415, and no CORS header is sent unless--MainnetChain:RpcCorsOriginslists the origin (or*). - Logging. The server logs
Nethereumat Information (Debug with-v) and everything else at Warning; the standard--Logging:LogLevel:<Category>=<Level>overrides win over those defaults. It does not watch its configuration files for changes. - Failure and shutdown. An unhandled follower failure, or a RocksDB write failure that is not transient, is logged at Critical and the process exits
1. A stop waits for the block being written to finish. - Clear state, keep the block archive. Run once with
--wipe-state(orMainnetChain:WipeStateinappsettings.json/MainnetChain__WipeStatein the environment). This wipes the state / trie / state-history column families and the snap-sync metadata and exits, but keeps headers, bodies, receipts and the Phase-1 cursors — so the next start re-runs the state sync without re-downloading the block archive. - Stable node identity. By default the node persists its identity key at
<data-dir>/nodekey(generated on first run) and reuses it for both the inbound listener and every outbound dial, so its enode never changes across restarts or reconnects. This is what lets a peer durably trust this node by enode — a rotating identity would make that trust evaporate on the next reconnect. The file is a raw 32-byte private key, not a hex text file, so a node key file written by another client is not interchangeable; pointing this at one fails to load rather than overwriting it. Override the path with--node-key-file <path>(orMainnetChain:NodeKeyFile). - Prometheus metrics.
--metrics-port 9090exposes the node's metrics — snap-sync phase/progress, peer counts, fetch failures, durations — athttp://<host>:9090/metricsin Prometheus text format, on a separate port from the JSON-RPC. - Serve state to other peers.
--listen-port 30303turns on the inbound RLPx listener so the node answers snap/1 + eth requests, not just JSON-RPC. - Pin a peer.
--trusted-peer enode://…always dials a known-good peer. - Full sync instead of snap.
--no-snap(rarely useful — a from-genesis mainnet replay takes days). - Storage mode. State is path-keyed (bounded, prunable) by default; the legacy hash-keyed store is selectable in config but is incompatible on-disk, so switching needs a fresh sync. See the
Nethereum.MainnetChainreference for the full configuration surface.
Embed it
To host the follower inside your own .NET app instead of running the tool, use the library:
var builder = WebApplication.CreateBuilder(args);
var config = new MainnetChainServerConfig { DataDir = "./mainnet-data", SnapBootstrap = true };
builder.AddMainnetChainServer(config);
using var loggerFactory = LoggerFactory.Create(logging => logging.AddConsole());
await builder.Services.AddMainnetNodeAsync(config, loggerFactory);
var app = builder.Build();
app.MapMainnetChainEndpoints();
app.Run("http://127.0.0.1:8545");
AddMainnetChainServer registers the follower graph (validation, the optional beacon light-client gate, and the JSON-RPC dispatcher); AddMainnetNodeAsync builds and registers the RocksDB and DevP2P production node; MapMainnetChainEndpoints maps POST / (JSON-RPC) and GET / (health). See the Nethereum.MainnetChain package for the hosting API.