feat: client-side completeness verify (canonical hash + verifyServedEvents)#2
Merged
Conversation
…vents) Mirror Willow's on-chain completeness commitment into the SDK so a client can verify an indexer's served matched-log set for a (subgrove, block) is the complete, untampered filter-matched event set the chain attests to — without trusting the indexer. - canonical_event_set_hash(block_number, matched_logs): domain-separated keccak-256 over the exact on-chain preimage (WILLOW_CRYPTO_EVENTS_V1 tag, big-endian length prefixes, per-log address/topics/data), mirroring willow-network::data_sources::types::canonical_event_set_hash. - verify_served_events(commitment, block_number, matched_logs): re-hash the served preimage and compare to the on-chain events_commitment anchor. Reuses the SDK's eth_hash keccak dep. Adds tests asserting both cross-language vectors exactly plus tamper cases (wrong block, drop/add/reorder/mutate log). Skips the optional end-to-end fetch helper: this SDK has no ABCI store-query client for the events_commitment path nor an indexer HTTP client for the matched-logs route (documented in the module). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds CompletenessOperations on top of the existing verify_served_events
helper. verify_block_completeness(subgrove_id, block_number) does the full
client-side check: reads the on-chain events_commitment anchor via the
validator's CometBFT abci_query (/store/events_commitment/{sg}/{block}),
GETs the indexer's matched-log preimage
(/completeness/{sg}/{block}/matched-logs), builds the canonical Log[] from
{address, topics, data}, and re-hashes to compare.
Reachable as client.completeness; the CometBFT RPC URL is derived from
api_url (:3031 -> :26657, matching the light client) and the indexer base
URL comes from the existing indexer_url config. A missing anchor (ABCI
code != 0) or missing preimage (non-200) raises CompletenessError
(not-verifiable), distinct from a False result (tampered set).
Tests: gates the JSON->Log parse against the authoritative vector
(block 7, two logs -> e1544ae9...) and adds a full mocked e2e path over
httpx.MockTransport (anchor + preimage + tampered + missing cases).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Mirrors Willow's on-chain completeness commitment into the Python SDK so a client can verify that an indexer's served completeness data for a
(subgrove, block)is the complete, untampered filter-matched event set the chain attests to — without trusting the indexer.The on-chain
events_commitment(a 32-byte keccak hash) is the trusted anchor; the indexer serves the matched-log preimage; the client re-hashes it here and compares.This is the SDK side of willow PR #676. Canonical Rust source:
willow-network::data_sources::types::canonical_event_set_hash.API (
willow.completeness, re-exported fromwillow)canonical_event_set_hash(block_number: int, matched_logs: list[Log]) -> bytes— domain-separated keccak-256 over the exact preimage (all integers big-endian, no separators):b"WILLOW_CRYPTO_EVENTS_V1"(23 bytes)block_numberas u64 BE (8 bytes)len(matched_logs)as u64 BE (8 bytes)verify_served_events(commitment, block_number, matched_logs) -> bool—canonical_event_set_hash(...) == commitment.Log(address, topics, data)— accepts rawbytesor0x-optional hex strings.Reuses the SDK's existing
eth_hashkeccak dependency (Ethereum keccak, not NIST SHA3-256).Test vectors (cross-language correctness gate)
blockNumber=0,matchedLogs=[]→
0x52089e4c924fbab0475d310d7f74bf8cae542d006a45d3c5d94adacda6937da5blockNumber=7, logs[{addr 0x42*20, topics [0xdd*32, 0x11*32], data 0x01020304}, {addr 0x43*20, topics [0xaa*32], data empty}]→
0xe1544ae919458663e8fce14bdcd06df6a777410c068302c0584dff1587524dfdtests/test_completeness.pyasserts both vectors exactly plus tamper cases (wrong block number, dropped/added/reordered/mutated log). All 13 new tests pass; full suite green (277 passed, 13 pre-existing skips).Skipped: optional end-to-end fetch helper
verify_block_completeness(subgrove_id, block_number)is intentionally not included — this SDK has no generic ABCI store-query client for theevents_commitmentpath nor an indexer HTTP client for the/completeness/{subgrove}/{block}/matched-logsroute. A doc note in the module points to wiring it up once that infra lands.🤖 Generated with Claude Code