Add ADR-0005 client-side earned reward eligibility gate - #2
Closed
grumbach wants to merge 12 commits into
Closed
Conversation
Implement the client half of ADR-0003: the client pays nothing it cannot resolve. Before paying, it runs the full binding check on every quote and candidate, so a node that overstates its storage to inflate its price is dropped before any on-chain payment instead of after. For each single-node quote and each merkle candidate, the client now: - verifies the quote's own ML-DSA-65 signature and that it is for the requested content; - checks the (committed_key_count, commitment_pin) shape, the count cap, and that price == calculate_price(count) by exact recomputation; and - for a bound quote, fully resolves the shipped commitment: parse it, check it is bound to the quoting peer, verify its signature, that it hashes to the quote's pin, and that its key_count matches the claimed count. Anything unresolvable, withheld, off-curve, or contradictory is dropped before payment, exactly as the storer would reject it. The verified commitments are then forwarded as sidecars in the PUT bundle (single-node and merkle) so storers cross-check synchronously. Sidecar blobs are size-capped before parsing. Pricing and the commitment verification come from ant-protocol, so client and node never disagree. Adds an e2e test (real QUIC nodes + Anvil) proving bound quotes are shipped, priced, fully resolve, and round-trip; and the off-curve / forged-commitment rejection is covered in unit tests. Depends on the evmlib, ant-protocol, and ant-node ADR-0003 releases; will not build standalone until those publish.
[patch.crates-io] points evmlib/ant-protocol at their PR branches; ant-core points the ant-node dep (runtime + dev) at the matching ant-node ADR-0004 branch (published versions version-collide with the patch). At release: revert ant-node to a published version, drop the patch block, bump ant-protocol pin.
…ote flow; renumber ADR-0003 -> ADR-0004 StoreQuote carries the commitment sidecar end-to-end (resolve-before-pay); get_store_quotes family + median helpers + downstream consumers updated.
The renamed ADR-0004 e2e target was auto-discovered but never listed in the CI E2E command, so ant-client could go green without exercising the main ADR-0004 end-to-end coverage. Add --test e2e_adr0004.
Point the ant-node / ant-protocol / evmlib git-branch deps at the freshly clean-rebased ADR-0004 revisions (ant-node ff7a547 — the audit-code merge against current main resolved correctly; ant-protocol 3a4eb638 = 2.3.0; evmlib a7ac7e26 = 0.9.0). Recovers the workspace build now that ant-node's branch compiles again.
Add negative unit tests for the three resolve-before-pay commitment sub-checks that previously had no coverage (deleting any of them still passed all tests): invalid commitment signature, commitment that does not hash to the quote's pin, and quote count disagreeing with the committed key_count. Each constructs a validly-signed, peer-bound commitment and breaks exactly one field to isolate its check. Rename binding_rejects_garbage_and_wrong_pin_commitment to binding_rejects_unparseable_and_peer_unbound_commitment to reflect what it actually exercises (the 'wrong pin' case failed peer-binding first), and fix the stale 'adr-0003' e2e test payload strings.
Once nodes rotate their first storage commitment, every merkle candidate is bound and finalize_merkle_batch copied all 16 winner-pool commitment sidecars into EVERY per-chunk proof, growing it from ~128 KB to ~342 KB, past the storer's 256 KB payment-proof cap. Every merkle chunk PUT was rejected AFTER the on-chain payment, so forced-merkle uploads burned their payment and then failed every store attempt (DEV-01, 2026-07-06 19:38 UTC: both forced-merkle clients flipped to 100% failure the moment 855 nodes rotated their first commitment; single-node payments were unaffected). The sidecars are not needed in the bundle: the client fully resolves every candidate's shipped commitment before paying, and the storer's cross-check is best-effort with a gossip-cache / GetCommitmentByPin fallback. Drop the forwarding and the dead plumbing that threaded the sidecar map from pool collection into finalization. Receipts cached by pre-fix clients still carry the fat proofs, so a resume would replay the rejection: strip sidecars from cached merkle receipts on load, with an atomic (create_new tmp + fsync + rename) write-back that can never truncate the only copy of a paid receipt. New coverage: - e2e: forced-merkle upload against 35 committed nodes; failed with the exact production signature before this fix, passes after, against the unchanged 256 KB storer cap - unit: finalize_merkle_batch ships no sidecars (bound candidates) - unit: cached receipt strip + idempotency + non-merkle passthrough - unit: atomic overwrite lands and leaves no tmp sibling
The client decides who is payable. This adds the eligibility gate at quote collection (payee selection and payment verification untouched): a quoter is payable when more than half the other responders who know it vouch for a clean audited week at the quoted size. - eligibility: aggregate the quorum of signed audit reports into a two-tier decision. Tier 1 (size) needs a vouched clean week AT the quoted size; tier 2 (dues) relaxes size to keep the network paying under fast growth, and is safe because ADR-0004 independently forces a current-size proof at payment. Both tiers still exclude fresh identities (no dues) and caught cheaters (fenced/convicted). The eligibility map is keyed by (peer, quoted size) so one peer's small quote can never leak size-tier admission onto its larger quote. If too few are eligible, it prefers the ones with the most dues, then falls back to today's rules; the network never stalls. - merkle / quote: carry the client report_nonce on quote requests, collect the returned audit reports, and apply the gate at pool / close-group composition. - tests: unit coverage for the bar, both tiers, and the exclusions, plus an end-to-end suite (mixed pool under enforcement, fenced/unproven substitution, and fast-growth dues fallback). - Cargo: temporarily pin ant-protocol and ant-node to the ADR-0005 PR branches carrying the wire types and node tally; revert to published bumps once released. Based on the ADR-0004 client work in WithAutonomi#126.
jacderida
force-pushed
the
adr-0003-quote-commitment-resolve
branch
from
July 9, 2026 16:58
872b29b to
059cc0a
Compare
Owner
Author
|
Superseded by WithAutonomi#151 — restaged onto WithAutonomi/ant-client |
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
The client side of ADR-0005: earned reward eligibility, and the piece that actually decides who gets paid. The gate sits at quote collection, so payee selection and payment verification are untouched: a quoter is payable when more than half of the other responders who know it vouch for a clean audited week at the quoted size.
Depends on the ADR-0005 wire types (ant-protocol#19) and node tally (ant-node#169).
Changes
eligibility.rs(new) — aggregate the quorum of signed audit reports into a two-tier decision:merkle.rs/quote.rs— carry the clientreport_nonceon quote requests, collect the returned audit reports, and apply the gate at pool / close-group composition.Dependency pins (revert before merge)
Cargo.tomltemporarily pinsant-protocol(ant-protocol#19) andant-node(ant-node#169) to their ADR-0005 PR branches so CI can build the coordinated change. These revert to published version bumps once the wire types and node tally are released, same as the ADR-0004 cutover.Follow-up
Wiring
--test e2e_adr0005into.github/workflows/ci.ymlis left out of this PR (token lacks theworkflowscope); it should be added in a small follow-up so the new e2e suite runs in CI. It passes locally.Testing
cargo test -p ant-core --features test-utils --lib(426 pass, incl. 14 eligibility unit tests) and the ADR-0005 e2e suite (6 tests) pass locally against the ant-protocol#19 / ant-node#169 branches;cargo clippy --all-targets --features test-utils -D warningsclean;cargo fmt --checkclean.