Skip to content

feat: client-side completeness verify (canonical hash + verifyServedEvents)#2

Merged
pauldelucia merged 2 commits into
masterfrom
feat/completeness-verify
Jun 25, 2026
Merged

feat: client-side completeness verify (canonical hash + verifyServedEvents)#2
pauldelucia merged 2 commits into
masterfrom
feat/completeness-verify

Conversation

@pauldelucia

Copy link
Copy Markdown
Contributor

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 from willow)

  • 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_number as u64 BE (8 bytes)
    • len(matched_logs) as u64 BE (8 bytes)
    • per log: address (20) | topics.len u32 BE (4) | each topic (32) | data.len u32 BE (4) | data
  • verify_served_events(commitment, block_number, matched_logs) -> boolcanonical_event_set_hash(...) == commitment.
  • Log(address, topics, data) — accepts raw bytes or 0x-optional hex strings.

Reuses the SDK's existing eth_hash keccak dependency (Ethereum keccak, not NIST SHA3-256).

Test vectors (cross-language correctness gate)

  • A (empty set): blockNumber=0, matchedLogs=[]
    0x52089e4c924fbab0475d310d7f74bf8cae542d006a45d3c5d94adacda6937da5
  • B: blockNumber=7, logs [{addr 0x42*20, topics [0xdd*32, 0x11*32], data 0x01020304}, {addr 0x43*20, topics [0xaa*32], data empty}]
    0xe1544ae919458663e8fce14bdcd06df6a777410c068302c0584dff1587524dfd

tests/test_completeness.py asserts 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 the events_commitment path nor an indexer HTTP client for the /completeness/{subgrove}/{block}/matched-logs route. A doc note in the module points to wiring it up once that infra lands.

🤖 Generated with Claude Code

pauldelucia and others added 2 commits June 25, 2026 12:21
…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>
@pauldelucia pauldelucia merged commit f342c49 into master Jun 25, 2026
1 check passed
@pauldelucia pauldelucia deleted the feat/completeness-verify branch June 25, 2026 06:01
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.

1 participant