Skip to main content
Components do not call each other directly; they cooperate only through Kafka, S3, etcd, and a small amount of RPC. These are the parts that must stay compatible when a change spans repositories.
The {version} segment below is optional. With version mode enabled, Kafka topics, S3 keys, and etcd keys all carry a version namespace, which lets multiple data versions run in parallel on the same infrastructure.

Kafka

Internal topic

Published by the write node, consumed by leafage-evm and consistency-checker.
The internal topic carries a totally ordered block stream and must use a single partition. The partition is specified by the partition field in --kafka-s3-config.leafage-evm pins the partition with an explicit assign and persists the offset itself (auto-commit disabled), so every replica receives all messages instead of sharing them within a consumer group.

External topic

Published by consistency-checker, subscribed to by external consumers. leafage-evm does not consume this topic.
New blocks and dropped blocks use the same structure, distinguished by is_fork. In version mode, the checker writes to both the version topic and the singleton topic; only the Leader holding the etcd lock writes to the latter.

S3

The two buckets are split by consumer. All keys start with chainID, so multiple chains can share the same bucket.

Internal bucket (NodeX bucket)

StateDiff is keyed by state root rather than block hash: adjacent blocks with the same state root share one object, so empty blocks produce no new objects. When the parent and current block have the same state root, leafage-evm skips the fetch and treats it as an empty diff.

External bucket (ChainTable bucket)

BlockFile contains the block, transactions, call traces, and event logs, and is the main data source for external consumers. BlockValidation is a validation digest of the same data and also serves as the index by height:
The objects listed under the prefix {chainID}[/{version}]/{height}/ are all the candidate blocks at that height. Two components rely on this index:
  • consistency-checker rewrites non-canonical objects as is_fork: true
  • leafage-evm uses it during S3 catch-up to resolve a height to the canonical block hash
See docs/protocol.md in the pipeline repository for the full field definitions.

etcd key space

All keys start with chainID (with an optional version segment), so different chains are naturally isolated. Structure of the node info:
The node lifecycle is maintained by three components, each with its own role:
  • leafage-evm writes itself as stateType: 2 (lagging) on startup.
  • consistency-checker rewrites it to 1 or 2 after each polling round; when a node is offline and has no lease, it deletes the key outright.
  • nodex-proxy only watches these keys and does not write health state itself.

Version switching

{chainID}/version is the control point for version mode; two components read it:
  • consistency-checker compares its own version config with the value in etcd; when they match, it contends for the {chainID}/outer_block_notice lock to become the Leader that publishes to the singleton topic.
  • nodex-proxy rewrites the base chainId in the request to {chainId}-{version} and routes to the versioned node pool.

RPC contracts

trace_debankBlock

Provided by the write node, used to obtain the complete execution output of a single block in setups without Kafka. leafage-evm’s HTTP fallback mode depends on it.
Returns DebankOutPut, which contains BlockFile, Header, StateDiff, and ValidationHash — corresponding one-to-one with the contents of the four object types on S3.

Health checks

nodex-proxy’s health check retries every 5 seconds, up to node_health_check_max_wait (default 300 seconds). Only nodes that pass the check enter the load balancing pool. When leafage-evm returns these error codes, nodex-proxy automatically switches node pools and retries once.

Compatibility rules

When changing cross-component interfaces, keep in mind:
  • Kafka message structures only add fields and never change semantics. Consumers decode JSON; extra fields are ignored, but missing fields lead to zero values being misinterpreted.
  • S3 key formats are hard-coded conventions shared by leafage-evm, consistency-checker, and external consumers; changing them requires updating all three and accounting for existing objects.
  • etcd key formats are likewise hard-coded in three components; the way the {chainID} and {version} segments are joined must be consistent.
  • Error codes are part of routing behavior; adding one requires matching handling logic on the nodex-proxy side.