Skip to content

Add log_watcher: block-scoped log watching (closes #36)#59

Merged
koko1123 merged 1 commit into
mainfrom
feat/watch-logs
Jun 10, 2026
Merged

Add log_watcher: block-scoped log watching (closes #36)#59
koko1123 merged 1 commit into
mainfrom
feat/watch-logs

Conversation

@koko1123

@koko1123 koko1123 commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

What

Implements watchLogs from #36: a log_watcher module that drives per-block eth_getLogs from a WsClient newHeads subscription.

  • LogWatcher.pollOnce() — pull API: blocks until the next head, returns the logs for the planned block range
  • watchLogs(...) — the callback loop from the issue
  • Back-fills blocks missed across reconnects (gap between last processed head and the new one)
  • Reorg handling: parent-hash mismatch on a sequential head re-fetches the replaced range; a re-announced old height re-fetches that height (the new chain may be shorter than our previous head, so it deliberately does not fetch up to the old cursor)
  • start_block_lag warm-up and handle_reorgs opt-out per the issue's WatchOpts

Design notes

  • Range planning is a pure function (planRange) with unit tests covering first-head lag (incl. genesis saturation), sequential heads, gaps, both reorg shapes, and handle_reorgs = false.
  • On reorgs, logs are re-delivered; consumers key on (block_hash, log_index) — documented in the module, README, and websockets guide.

Bug fix this exposed

subscription.parseBlockFromNotification called std.json.stringifyAlloc, which doesn't exist in Zig 0.15.2 — it compiled until now only because nothing referenced it (lazy analysis). It now parses the notification object directly via a new provider.parseBlockHeaderObject, shared with parseBlockHeader.

Verification

  • make ci on 0.15.2 and zig build test on 0.17.0-dev.813: pass
  • zig build integration-test against Anvil: pass, including a new LogWatcher test that mines blocks and asserts cursor tracking
  • Docs site builds with the new "Watching Logs Per Block" section

Closes #36

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Introduced block-scoped log watching with automatic handling of missed blocks and chain reorgs.
  • Bug Fixes

    • Fixed compatibility issue with Zig 0.15.2 in block notification parsing.
  • Documentation

    • Added documentation and code examples for the log watching feature.

On each newHeads notification, fetch eth_getLogs scoped to the planned
block range. Blocks missed across reconnects are back-filled (the gap
between the last processed head and the new one), and reorgs detected
via parent-hash mismatch re-fetch the replaced range. Re-announced old
heights re-fetch only that height, since the new chain may be shorter
than the previous head.

The range planning lives in a pure planRange() function with unit tests
for first-head lag, sequential heads, gaps, both reorg shapes, and the
handle_reorgs=false paths. LogWatcher.pollOnce() is the pull-based API;
watchLogs() is the callback loop from the issue.

Also fixes a latent 0.15.2 incompatibility this feature exposed:
subscription.parseBlockFromNotification used std.json.stringifyAlloc
(removed in 0.15) to re-wrap the notification for parseBlockHeader. The
header object is now parsed directly via the new
provider.parseBlockHeaderObject, with no stringify round-trip.

Verified: zig build test (0.15.2 and 0.17.0-dev), integration tests
against Anvil including a new LogWatcher cursor-tracking test, docs
build.
@vercel

vercel Bot commented Jun 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
eth-zig Ready Ready Preview, Comment Jun 10, 2026 2:01am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5f1da0c0-50d9-4e8e-af72-27729a468493

📥 Commits

Reviewing files that changed from the base of the PR and between 0a429f5 and 7909189.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • README.md
  • docs/content/docs/websockets.mdx
  • src/log_watcher.zig
  • src/provider.zig
  • src/root.zig
  • src/subscription.zig
  • tests/integration_tests.zig

📝 Walkthrough

Walkthrough

This PR introduces LogWatcher, a new utility for block-scoped log watching that combines WebSocket newHeads subscriptions with per-block eth_getLogs calls. It includes automatic gap backfilling on reconnect, optional reorg re-fetching, and comprehensive tests. Supporting refactors extract block header parsing to fix Zig 0.15.2 compatibility.

Changes

Log Watcher Feature

Layer / File(s) Summary
Provider block header parsing helper
src/provider.zig
New public parseBlockHeaderObject extracts block header decoding logic from raw JSON objects, enabling direct reuse by other modules like the subscription parser.
Subscription block parsing fix
src/subscription.zig
parseBlockFromNotification now directly parses the notification result object via parseBlockHeaderObject, removing an incompatible JSON re-stringify step for Zig 0.15.2 compatibility.
LogWatcher core implementation
src/log_watcher.zig
Implements block-scoped log watching with WatchOpts config (start lag, reorg handling), Cursor state tracking, RangePlan computation, LogWatcher struct with pollOnce() polling loop, watchLogs() callback wrapper, and unit tests covering lag, genesis saturation, gap backfilling, parent-hash reorg detection, and reorg-disable behavior.
Module export and integration test
src/root.zig, tests/integration_tests.zig
Exports log_watcher module in public API and includes in test compilation. Integration test validates LogWatcher initialization, polling across multiple blocks, log freeing, and cursor advancement.
Feature documentation
CHANGELOG.md, README.md, docs/content/docs/websockets.mdx
Changelog documents new log_watcher and parseBlockHeaderObject additions plus subscription fix. README includes example usage. Websockets guide provides initialization, polling, reorg/deduplication notes, and callback wrapper explanation.

Sequence Diagram

sequenceDiagram
    participant Client
    participant LogWatcher
    participant WsClient
    participant Provider
    
    Client->>LogWatcher: pollOnce()
    LogWatcher->>WsClient: wait for next newHeads event
    WsClient-->>LogWatcher: BlockHeader
    LogWatcher->>LogWatcher: planRange(cursor, header)
    LogWatcher->>Provider: getLogs(fromBlock, toBlock, filter)
    Provider-->>LogWatcher: logs[]
    LogWatcher->>LogWatcher: update cursor (block number, hash)
    LogWatcher-->>Client: logs[]
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

  • StrobeLabs/eth.zig#51: Also refactors parseBlockFromNotification in src/subscription.zig and centralizes notification-result extraction and header parsing, overlapping with the subscription fix in this PR.

Poem

🐰 Block by block, a watcher so keen,
Fetching logs from the on-chain scene,
Through reconnects and reorgs it stays,
Guiding keepers through market's haze.
No more gaps in the keeper's sight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add log_watcher: block-scoped log watching (closes #36)' accurately summarizes the main change—introduction of a log_watcher module for block-scoped log watching—and references the closed issue.
Linked Issues check ✅ Passed The pull request fully implements all core requirements from issue #36: LogWatcher with newHeads subscription, eth_getLogs per-block fetching, reconnect backfill, reorg handling, WatchOpts configuration, callback/pull APIs, and documentation.
Out of Scope Changes check ✅ Passed All changes directly support the log_watcher feature or its dependencies: log_watcher module, provider.parseBlockHeaderObject for parsing, subscription fix for Zig 0.15.2 compatibility, documentation, tests, and root exports are all in-scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/watch-logs

Comment @coderabbitai help to get the list of available commands and usage tips.

@koko1123
koko1123 merged commit 40d731b into main Jun 10, 2026
14 checks passed
@koko1123
koko1123 deleted the feat/watch-logs branch June 10, 2026 02:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

watchLogs: block-scoped log subscription helper

1 participant