Write node
The write node is the chain’s original execution client (a go-ethereum fork for ETH, the respective fork for other chains) with the pipeline library compiled in. Leafage does not replace its sync or execution logic; it only attaches tracing hooks to the execution path.
The write node’s RPC port is not for application clients. It serves two callers: leafage-evm in HTTP polling mode, and engineers comparing a block’s StateDiff while debugging.
Leader and standby nodes
A chain can run several write nodes. All of them sync, execute, and upload to S3; S3 objects are keyed by block hash and state root, so repeated uploads are idempotent. There is exactly one difference: only the Leader publishes notifications to Kafka.
A new Leader waits
grace_period (default 10 seconds) before it starts publishing, giving the previous Leader time to finish.
Query node
The query node is the leafage-evm process. The most precise way to define it is to list what it does not do:
It does exactly two things: apply state diffs in notification order, and run read-only calls with revm on local state (
eth_call, batch calls, gas estimation, call tracing).
Because it stores no transactions,
eth_getBlockByNumber and eth_getBlockByHash return only the block header, and transactions and uncles are always empty arrays. This is a design trade-off, not a defect.Three query node types
leafage-evm splits into State and Archive nodes by startup flags. A Native node is a full node of the original chain, registered in etcd manually by operators as a fallback.
A State node returns
-39006 BlockNotFound for heights outside its window; an Archive node returns -39007 InvalidBlockID for blocks it does not have. The former makes nodex-proxy retry on an Archive node; the latter does not.
See Block context and routing for the routing rules across the three types, and State and state diffs for the storage layout.
Node lifecycle
Whether a query node receives traffic is not its own decision. Three components take turns on the etcd key{chainID}[/{version}]/nodes/{ip}_{port}:
1
leafage-evm registers itself
On startup it writes its address and
nodeType, with stateType set to 2 (lagging). Registration uses a “write only if the key does not exist” transaction, so it never overwrites state written by others; on exit it deletes its own key.2
consistency-checker decides
For every block it processes, it polls
eth_blockNumber on all nodes and writes the result back: 1 caught up with the chain head, 2 lagging, 3 offline. An offline node without a lease has its key deleted outright.3
nodex-proxy admits
When it sees a new node, it runs a health check first (
getLatestBlock) and only then adds the node to the load-balancing pool; a delete event removes the node immediately.
When investigating “why does this node get no traffic”, start from consistency-checker’s polling results, not from nodex-proxy.
Summary
- The write node is the original execution client + pipeline and the only block executor; all write nodes upload to S3, only the Leader publishes to Kafka.
- The query node does not sync, execute, maintain an MPT, or store transactions; it only applies state diffs and runs read-only calls.
- Query nodes come as State / Archive / Native, distinguished by how much state they keep; error codes
-39006and-39008drive the proxy’s fallback between them. - Whether a node receives traffic is decided by the
stateTypethat consistency-checker writes to etcd; leafage-evm only registers, nodex-proxy only consumes.
- State and state diffs: how a query node organizes state internally.
- Block context and routing: how nodex-proxy chooses among the three node types.
- Architecture overview: storage footprint of each node type on ETH mainnet.