- Publishes confirmed block notifications to external consumers.
- Marks fork blocks on S3.
- Writes node health state into etcd.
What problem it solves
At the moment the write node publishes a notification, the replicas have not yet applied that block. If external consumers subscribed to the internal topic directly, they would receive the notification while leafage-evm still cannot serve the block. The checker adds a confirmation step in between: it waits until a sufficient ratio of replicas has actually caught up to that height before publishing to the external topic. External consumers therefore get a guarantee — when the notification arrives, querying the cluster is certain to return that block. Along the way, it also solves two other things:- Fork blocks are not marked on S3, because only an observer knows which chain is canonical.
- nodex-proxy needs to know whether each node is currently healthy.
Processing flow
Process in check/check.go. A few design points:
Prefetch and wait in parallel
Prefetch and wait in parallel
The S3 prefetch and replica polling run at the same time. Polling usually takes tens to hundreds of milliseconds, which covers the S3 round trip; the failure path does not wait for the prefetch to finish, the goroutine wraps up on its own.
Idempotency and retries
Idempotency and retries
- When the whole
Processfails, the Kafka offset is not committed, and the next round retries the same message. - Duplicate messages, and the case of “already processed through the publish step but the whole message not committed”, short-circuit to the alignment step and advance directly, avoiding retry deadlocks.
- Destinations that were delivered successfully are recorded in
deliveredand skipped on retry, so nothing is delivered twice or missed.
Timeout semantics of replica polling
Timeout semantics of replica polling
Polls
eth_blockNumber on all replicas at check_interval_ms intervals until a ready_ratio share of replicas reaches the target height. A timeout counts as failure, and Process retries as a whole.The
check_num config option is deprecated and no longer takes effect.Post-publish recheck
Post-publish recheck
The prefetched key list at the same height is a snapshot taken at the start of
Process; objects uploaded while waiting for replicas are not in it. After publishing the notification, it runs a fresh LIST and checks again. This step is not on the critical path; failures are only logged, and the periodic scan serves as a fallback.Fork marking
TheBlockValidation object in the S3 external bucket carries an is_fork field, which is always false when the write node uploads it — because at the moment of upload the write node does not yet know whether the block will end up on the canonical chain. The checker is responsible for writing it back.
Three trigger paths:
A non-zero
pipeline_fork_scan_rewrites_total means a mark was overwritten or previously failed, and deserves attention.
leafage-evm relies on this mark during S3 catch-up to resolve heights into canonical hashes. If the checker stalls for a long time, catch-up for new nodes slows down.
Node state maintenance
After each polling round, the result is written back to etcd (committed in a single transaction):
When a node is offline and has no lease, the key is deleted outright. The height is written only when it changes, to avoid needless etcd write amplification.
These two keys are the entire basis for nodex-proxy routing: node pool membership comes from the former, and the State / Archive selection threshold comes from the latter.
Two modes
Determined jointly by theversion and outer_version_new_block_topic config options.
- Version mode
- Legacy mode
Writes both the version topic and the singleton topic.
- etcd keys use the
{chainID}/{version}/prefix - S3 paths include a version segment
- Write access to the singleton topic requires election through an etcd distributed lock (
{chainID}/outer_block_notice) - The Leader periodically compares
{chainID}/versionand releases the lock when the version does not match
AlignOuterSingleton and align.Local storage
Pebble DB stores confirmed blocks with a dual index:JSON-RPC API
The listen address is set by thelisten config option (default :8663); the same port serves GET /metrics.
-39005.
Metrics
Running
Development
check/critical_path_test.go covers the idempotency and retry semantics of the critical path; read it first before changing the Process flow.