Saltar a contenido

Quickstart

Get UMNode joining Ethereum mainnet in a couple of minutes. Pick Go if you have a toolchain, or Docker if you'd rather not.

Requirements

  • Go 1.25+ (the module requires go 1.25.0) — only for source builds.
  • Docker 24+ — alternative to a local Go toolchain.
  • Outbound network access on TCP/UDP 30303 (the default Ethereum p2p port) for dialing peers and UDP discovery. No inbound port is needed — UMNode only ever dials out and never accepts incoming connections.

Run with Go

git clone [email protected]:ofzhur/umnode.git
cd umnode

make build               # produces bin/umnode
./bin/umnode             # joins Ethereum mainnet with the bundled config

Without the Makefile:

go run . --network mainnet --log-level debug

By default UMNode reads cfg/ethereum.yaml from the current working directory and joins mainnet. It dials out only and binds UDP discovery on 0.0.0.0:30303. The bundled mainnet config keeps up to 20 peers with discovery v4 + v5 enabled.

Override any setting with a flag:

./bin/umnode --network mainnet --max-peers 50 --log-level debug

See CLI flags for the full list and Configuration for the per-network YAML.

Already running a synced node?

Point UMNode at its JSON-RPC and it fetches block data from there first, falling back to p2p peers for whatever that node lacks — a partially synced node contributes what it has:

./bin/umnode --rpc-peers http://127.0.0.1:8545

See RPC peers.

Run with Docker

The image is a distroless static build (~15 MB) with cfg/ethereum.yaml baked in for an out-of-the-box mainnet run.

make docker              # builds ofzhur/umnode:<git-describe> and :latest
make docker-run          # runs it with cfg/ and data/ mounted

make docker-run is equivalent to:

docker run --rm -it \
  -v "$PWD/cfg:/app/cfg:ro" \
  -v "$PWD/data:/app/data" \
  ofzhur/umnode:latest

No published ports are needed: UMNode dials out and runs discovery outbound, but accepts nothing inbound.

  • Mount cfg/ only if you want to override the bundled config.
  • The data/ mount preserves the discovery node database (and, once it fills, the block store) between runs, so UMNode doesn't have to rediscover peers on every start.

To pass flags, append them after the image name:

docker run --rm -it ofzhur/umnode:latest --log-level debug --max-peers 50

Testnets

The bundled config also describes Sepolia, Hoodi and Ephemery. Pick one with --network and give it its own data directories — every store is bound to the chain it was created for:

./bin/umnode --network sepolia \
  --block-db data/sepolia/chain --state-db data/sepolia/state \
  --node-db data/sepolia/p2p/enode --inventory-db data/sepolia/p2p/inventory

Ephemery resets every 28 days; after a reset run make ephemery-genesis to fetch the new iteration's genesis and start from empty directories. See Bundled networks.

What you should see

On a healthy start UMNode logs that it has loaded the network config, opened its stores, and begun discovery. As peers complete the eth handshake you'll see Peer ready lines (alongside geth's periodic Looking for peers count), and as the head advances the realtime worker logs each block it imports.

Inventory persistence enabled   path=data/p2p/inventory compactionEvery=1h0m0s
Block store ready               path=data/chain segmentSize=50000 codec=s2 pendingBackend=pebble sealConfirmations=256 ...
Looking for peers               peercount=3 tried=8 static=0
Peer ready                      peer=a1b2c3d4e5f6a7b8 version=70
Imported block                  number=25,232,371 hash=0xbde1…51bfe txs=290

Two workers fill the store

The realtime worker writes upcoming blocks (header, body, receipts) into the store as the head advances, while the backfill worker fills history downward toward genesis. So the store grows forward from the head and backward to block 0 at the same time.

Where the state comes from

With execution on (the default), an empty node first asks its segment archive — the public one unless --archive-source names another — for a state snapshot: --syncmode snap, the default, restores the newest one and executes on from its head, downloading only the blocks the node keeps (--block-history). When the archive publishes none — the public archive carries no snapshots — the node says so and builds the state from genesis instead (--syncmode full), which needs every block. See Sync modes.

Next steps

  • Configuration — tune peers, discovery and the block store per network.
  • Architecture — understand how UMNode is meant to fill and serve its store.
  • Block store — the segment format, and why a couple of settings are locked at database creation.