feat: enforce quote valid-until-ms at submit via a quote ledger - #35
Open
mfw78 wants to merge 6 commits into
Open
feat: enforce quote valid-until-ms at submit via a quote ledger#35mfw78 wants to merge 6 commits into
mfw78 wants to merge 6 commits into
Conversation
Contributor
Author
|
Deferred red-team finding filed: #38 (platform e2e tests silently skip when module wasms are absent; a zero-wasm run reports green). Surfaced while gating this PR. |
mfw78
force-pushed
the
feat/quote-staleness-ledger
branch
from
August 4, 2026 23:26
2cf2177 to
df47837
Compare
… ledger The registry records every successful quote under (caller, venue, keccak256(body)) with its valid-until-ms, and submit refuses the same bytes as denied with the stable stale-quote: prefix once that instant elapses. Unquoted bytes submit as before: quoting is not mandatory on this wire. Expired entries sweep on every ledger touch, so growth is bounded by live quotes. The wall clock sits behind an auto_impl Clock seam with a SystemClock default, injected through VenueRegistryBuilder so tests pin time. Closes #14. Retired by the wire-level quote binding tracked in #32. AI Assistance: Claude Code used for implementation and tests.
record_quote admitted whatever valid-until-ms the adapter returned, so the sweep only drained entries the adapter had already dated. Both in-tree adapters quote u64::MAX, which never expires: every distinct body a keeper priced pinned an entry for the process lifetime, and the "bounded by live quotes" invariant held only vacuously. A quotation valid past the one hour horizon is now left unrecorded. It cannot go stale inside the horizon, so the entry could never have produced a refusal, and skipping it costs no enforcement. A cap backs that up: at MAX_QUOTE_ENTRIES a new digest is refused rather than a live entry evicted, so a flooding caller leaves its own submits unchecked instead of disarming another caller's. Both degrade to the unquoted path, which already submits unchecked. Also moves the scripted platform adapter off a 2023 valid-until-ms, so a quote-then-submit added to those system-clock tests exercises the path under test instead of a stale-quote refusal. AI Assistance: Claude Code used for the review and this fix.
mfw78
force-pushed
the
feat/quote-staleness-ledger
branch
from
August 6, 2026 14:52
df47837 to
08b9fd8
Compare
The client rustdoc promised a refusal outright, but a quotation valid past the recording horizon is never recorded, which is what both in-tree adapters emit, and neither is one quoted with the ledger full. Correct the cap's own note too: a full ledger leaves later quotes unrecorded for every caller, not just the flooder.
The check digests a guest-supplied body and sweeps the shared ledger, so running it ahead of resolve and the per-caller quota let an over-quota caller spend host CPU on every submit, against the file's own rule that a gated caller reaches nothing. Fold the duplicated sweep predicate into sweep_quotes alongside the other pruners, and skip the digest entirely when the ledger is empty.
The read-before-sweep ordering the freshness check rests on had no test: inverting the two lines left the whole suite green while every entry past the grace became a silent pass.
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
Clock-injected quote ledger toVenueRegistryincrates/videre-host/src/registry.rs:record_quotestores a keccak256 digest of the quoted body keyed by(caller, venue, digest)mapped tovalid_until_ms, andsubmitcallscheck_quote_freshnessbefore deriving the header, refusing bytes this registry quoted once theirvalid_until_mshas elapsed (VenueError::Deniedwith astale-quote:prefix).QUOTE_HORIZON_MS(1 hour) skips recording quotations valid further out than that, andMAX_QUOTE_ENTRIES(1024) refuses new digests once the ledger is at cap, logging a warning rather than evicting a live entry.QUOTE_GRACE_MS(30s) so an expired entry survives a sweep triggered by any ledger touch, including a third party's;check_quote_freshnessreads its own key before sweeping (so a just-expired entry is still checked ahead of eviction) and removes the entry explicitly on a stale refusal.ClockandSystemClockfromvidere-host(src/lib.rs), and adds awith_clockbuilder method for injecting a test clock.alloy-primitivesandauto_implasvidere-hostdependencies (Cargo.toml) for the digest and theClockforwarding impls.videre-sdk/src/client.rsdoc comments onVenueTransport::quoteandQuoted::submitto describe the host-side, best-effort staleness enforcement.crates/videre-host/tests/platform.rsto quotevalid_until_ms: u64::MAXinstead of a fixed past instant, since the test registry now runs on the system clock.registry.rs:stale_quote_submit_is_denied,fresh_quote_submits,unquoted_body_submits_without_a_ledger_check,a_stale_quote_binds_its_caller_only,expired_entries_prune,a_long_expired_quote_still_refuses_its_owner,an_over_quota_submit_never_reaches_the_quote_ledger,a_stale_submit_is_refused_after_a_third_party_sweep,a_quote_past_the_horizon_is_not_recorded,the_quote_ledger_is_capped.Why
Prices are only valid until
valid_until_ms; without a host-side check, a stale quotation's bytes could still be submitted and accepted after the price had moved. The ledger closes that gap without adapter cooperation, since the check lives inVenueRegistry::submitrather than in each venue adapter. The grace-and-explicit-removal design closes a sweep-eviction hole: without a grace read, a third party's unrelated ledger touch could sweep a just-expired entry ahead of its owner's submit, letting a stale submit through unchecked.Closes #14
Testing
All commands run in
nix develop(rustc 1.94.0, cargo-nextest 0.9.127) on a fresh clone detached atorigin/feat/quote-staleness-ledger(36df715). Touched crates overorigin/chore/nexum-runtime-pin-bump..HEAD:videre-host,videre-sdk(plusCargo.lock); no guest crate orvidere-macroschange.cargo clippy -p videre-host -p videre-sdk --all-targets --all-features -- -D warnings- clean, zero warnings.cargo test --doc -p videre-host -p videre-sdk --all-features- ok, 0 passed / 0 failed for both crates (neither carries a doctest; thevidere-sdk/src/client.rschange is rustdoc prose).RUSTDOCFLAGS='-D warnings' cargo doc --no-deps -p videre-host -p videre-sdk --all-features- clean, docs generated with no warnings.just build-modules(echo-venue, echo-client, echo-keeper, flaky-venue for wasm32-wasip2, release) thencargo nextest run -p videre-host -p videre-sdk- all green.videre-sdk'sQuoted::submitforwards exactly the bytesquotepriced, and the keeper's journal/reconcile paths never quote, so no in-tree submit path regresses); the cap/horizon/prune tests are non-vacuous under mutation. Found and fixed three real defects on the branch, pushed as357d666,e7ee751,36df715.AI Assistance
Implemented with claude-fable-5, red-teamed with claude-opus-5, PR authored with claude-sonnet-5.