Add ADR-0005 node-side audit tally for earned reward eligibility#169
Open
grumbach wants to merge 2 commits into
Open
Add ADR-0005 node-side audit tally for earned reward eligibility#169grumbach wants to merge 2 commits into
grumbach wants to merge 2 commits into
Conversation
Every node already audits its neighbours (ADR-0002/0004); this records the outcomes so a client can decide, at quote collection, whether a quoter has earned a clean audited week at the size it wants to charge for. - audit_tally: per-peer, per-day tally of subtree-audit passes and the size proven, with sticky convictions and a fence for unanswered monetized-pin challenges. Rows and the fence age out with the window; the on-disk snapshot carries a magic+version tag so a format change is rejected cleanly rather than mis-decoded, and the observer starts fresh. - quote: attach the signed audit report (bound to the client's report_nonce) to quote responses. - replication/storage: wire pass/conviction/fence recording into the audit paths and persist the tally with write-on-change atomic rename. - Cargo: temporarily pin ant-protocol to the PR branch carrying the wire types (revert to a published bump once released).
There was a problem hiding this comment.
Pull request overview
Implements the node-side portion of ADR-0005 “earned reward eligibility” by recording subtree-audit outcomes in a per-peer/per-day tally, persisting it across restarts, and attaching a signed audit report (bound to the client-provided nonce) to quote responses so clients can evaluate eligibility at quote-collection time.
Changes:
- Add a new replication audit tally (per peer/day, with conviction + fence semantics) plus snapshot load/persist support.
- Extend quote generation/handling to optionally include a signed ADR-0005 audit report on the wire.
- Wire tally recording into existing subtree-audit paths and connect the replication engine tally into the node’s quote generator during node assembly.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/storage/handler.rs | Adds quote-response audit_report attachment and handler-level wiring/tests for the new report source. |
| src/replication/storage_commitment_audit.rs | Threads audited commitment key-count into subtree pass results so the tally can record “passed at this size”. |
| src/replication/mod.rs | Adds audit-tally state, wiring from audit outcomes (pass/conviction/fence), and snapshot load/persist alongside retention. |
| src/replication/config.rs | Introduces ADR-0005 testnet env knobs to compress audit/neighbor-sync cadence for time-compressed networks. |
| src/replication/audit.rs | Extends AuditTickResult::Passed with optional commitment key-count (only present for subtree lane). |
| src/replication/audit_tally.rs | New module implementing the per-peer/per-day tally, snapshot tagging/serialization, and quote report adapter. |
| src/payment/quote.rs | Adds AuditReportSource and report building/signing logic bound to report_nonce, plus tests. |
| src/node.rs | Wires the replication engine’s tally into the quote generator as the node’s report source. |
| Cargo.toml | Temporarily patches ant-protocol to a git branch containing the new ADR-0005 wire types. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+261
to
+263
| let mut rows: Vec<(&PeerId, &TallyRow)> = self.rows.iter().collect(); | ||
| rows.sort_by_key(|(_, row)| std::cmp::Reverse(row.last_touched_day)); | ||
| rows.truncate(MAX_REPORT_ROWS); |
Comment on lines
+200
to
+206
| # ADR-0005 depends on the audit-report wire types added to ant-protocol in | ||
| # https://github.com/WithAutonomi/ant-protocol/pull/19, not yet on crates.io. | ||
| # Point the pin at that PR branch so this builds in CI. Revert to a published | ||
| # ant-protocol version bump once the wire types are released (evmlib is | ||
| # re-exported through ant-protocol, so no separate evmlib pin is needed). | ||
| [patch.crates-io] | ||
| ant-protocol = { git = "https://github.com/grumbach/ant-protocol.git", branch = "adr-0005-earned-reward-eligibility" } |
Delete every test/testnet-only ADR-0005 seam from the node so no release binary carries them: - the destructive fat-pin cheater and idle-honest seams (ADR5_TEST_CHEAT_DELETE_AFTER_SECS / ADR5_TEST_IDLE_AFTER_SECS), which could delete all stored chunks or refuse storage if an env var leaked; - the compressed-time knobs (ADR5_DAY_SECS and the ADR5_TEST_* cadence overrides), so an untrusted reporter can never redefine a "day" and the tally day is a fixed 86400s in production. The seam functions collapse to their production form and the storage admission paths always proceed, so production behaviour is unchanged; the CI tests direct-drive the tally, so nothing depended on the seams. Also harden persistence and the report producer: - flush the tally eagerly on a conviction or fence, not only on the 30s periodic loop, so a crash cannot lose negative evidence; - give the audit-tally persist/load path its own log labels instead of reusing the commitment-retention ones; - enforce the report row/day/byte caps in the producer before signing, so an over-cap report is never put on the wire. Add the ADR-0005 source document and pin ant-protocol to the immutable PR rev for a reproducible build.
Comment on lines
+175
to
+190
| /// `now_secs` MUST be the observer's real-time clock at the moment the audit | ||
| /// outcome is processed — never a timestamp carried on the wire. All three | ||
| /// record paths pass [`unix_now_secs`], so events are stamped in processing | ||
| /// order and `now_secs` is monotonic non-decreasing. This is what makes | ||
| /// "a pass clears the fence" safe: a pass can only clear a fence that was set | ||
| /// at or before the same wall-clock instant, so an out-of-order/replayed | ||
| /// pass cannot retroactively lift a newer fence. Do not feed this a | ||
| /// caller-supplied or attacker-influenced timestamp. | ||
| pub fn record_pass(&mut self, peer: PeerId, commitment_key_count: u32, now_secs: u64) { | ||
| let today = now_secs / tally_day_secs(); | ||
| self.evict_if_full(&peer, today); | ||
| let row = self.rows.entry(peer).or_default(); | ||
| row.prune(today); | ||
| row.fenced_day = None; | ||
| row.last_touched_day = today; | ||
| match row.days.iter_mut().find(|(day, _)| *day == today) { |
Comment on lines
+220
to
+228
| pub fn record_conviction(&mut self, peer: PeerId, now_secs: u64) { | ||
| let today = now_secs / tally_day_secs(); | ||
| self.evict_if_full(&peer, today); | ||
| let row = self.rows.entry(peer).or_default(); | ||
| row.days.clear(); | ||
| row.fenced_day = None; | ||
| row.convicted_day = Some(today); | ||
| row.last_touched_day = today; | ||
| } |
Comment on lines
+170
to
+172
| let reporter_peer_id = source.reporter_peer_id(); | ||
| let payload = | ||
| ant_protocol::payment::audit_report_signed_payload(&reporter_peer_id, &nonce, &rows); |
Comment on lines
+232
to
+238
| pub fn record_unanswered_monetized_challenge(&mut self, peer: PeerId, now_secs: u64) { | ||
| let today = now_secs / tally_day_secs(); | ||
| self.evict_if_full(&peer, today); | ||
| let row = self.rows.entry(peer).or_default(); | ||
| row.fenced_day = Some(today); | ||
| row.last_touched_day = today; | ||
| } |
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 node side of ADR-0005: earned reward eligibility. Every node already audits its neighbours (ADR-0002/0004); this PR just writes down the results, per peer per day, so a client can decide at quote collection whether a quoter has earned a clean audited week at the size it wants to charge for. Nothing here gates payment on its own; the client-side gate lands in the ant-client PR, and the shared wire types are in ant-protocol#19.
Changes
replication/audit_tally.rs(new) — per-peer, per-day tally of subtree-audit passes and the size proven. Sticky convictions (a caught cheater re-earns its whole week), and a fence that freezes an observer's vouch when a monetized-pin challenge goes unanswered until the peer next passes. Rows and an evidence-less fence age out with the window. The on-disk snapshot carries a magic + version tag so a layout change is rejected cleanly rather than mis-decoded positionally, and the observer safely starts fresh (a lost snapshot only costs one testimony window, re-earned through ordinary audits).payment/quote.rs— attach the node's signed audit report, bound to the client'sreport_nonce, to quote responses.replication/*+storage/handler.rs— wire pass / conviction / fence recording into the existing audit paths and persist the tally with the same write-on-change atomic-rename discipline as the ADR-0004 retention snapshot.Dependency
Builds against the ADR-0005 wire types in WithAutonomi/ant-protocol#19.
Cargo.tomltemporarily pinsant-protocolat that PR branch so CI can build; this reverts to a published version bump once the wire types are released (same release dance as ADR-0004).Testing
cargo test --lib(695 pass, including the tally, fence age-out, and snapshot-tag tests),cargo clippy --all-targets --all-featureswith the panic/unwrap/expect denies clean,cargo fmt --checkclean.