Skip to content

fix(payment): require issuer-aware SNP quote quorum#132

Open
mickvandijke wants to merge 5 commits into
mainfrom
fix/snp-quote-quorum-payment
Open

fix(payment): require issuer-aware SNP quote quorum#132
mickvandijke wants to merge 5 commits into
mainfrom
fix/snp-quote-quorum-payment

Conversation

@mickvandijke

@mickvandijke mickvandijke commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR fixes single-node payment quote selection for chunk uploads after several real upload failures where the client either spent successfully but one close-group PUT was later rejected by the storer, or failed before payment despite having enough reachable quote responders to attempt majority storage.

The client still queries the witnessed close group for quotes, but the proof of payment only needs to include one paid quote. The paid quote is now selected as the cheapest quote that should be accepted by the required storage majority, instead of paying a median/high quote or falling back to less relevant peers.

What changed

  • Query exactly the CLOSE_GROUP_SIZE witnessed SNP quote peers for single-node uploads, without falling back to extra peers or weaker witnessed candidates when some peers do not return usable quotes.
  • Keep only one selected paid quote in the proof of payment.
  • Select the cheapest paid quote that clears the storer-side 20% underpayment tolerance for the storage-majority quote target, avoiding incentives for a few operators to inflate quotes and become more likely to be paid.
  • Require CLOSE_GROUP_MAJORITY successful quote acceptors, not the stricter witnessed-consensus quorum, once the seven-peer candidate set has already been witnessed. This allows uploads with four usable quotes out of seven to proceed when those four should accept the selected paid quote.
  • Count already-stored close-group peers toward the quote acceptance target, so partially available chunks do not fail before payment when enough peers already confirm storage.
  • Reuse cached single-node payment proofs only when they are still safe locally:
    • embedded quote timestamps are within the safe reuse window;
    • embedded quote prices still meet the current fresh quote floor for that chunk.
  • Make SNP PUT targets match the final witnessed quote peer set, not a stale initial peer list.
  • Make paid quote selection issuer-aware: a quote now only counts toward the acceptance target for a responder when that responder should accept both the price floor and the paid quote issuer in its witnessed local K=20 view.

Failure modes addressed

PUT rejected after fresh payment

One observed failure was not stale cached proof reuse. The client paid a fresh quote, then one chunk only stored on 3 of 4 required peers.

The rejected PUTs showed two checks the old client-side selector did not model together:

  • Paid quote price below local floor: the paid quote price was accepted by a price-only quorum, but not by all PUT peers.
  • Paid quote issuer ... is not among this node's local K=20 closest peers: a storer can reject a paid quote issuer even if the amount is otherwise acceptable.

There was also a mismatch between the quote responders and the later PUT targets. A quote could be selected using one witnessed peer set while the chunk was stored to a different initial peer set, which made the client-side acceptance estimate too optimistic.

Pre-payment failure with four usable quotes

upload-36.log failed before payment:

Got 4 quotes, need 5 (7 responses: 0 already_stored, 3 failed ...)

That need 5 target was too strict for paid quote acceptance. The 5-of-7 witnessed quorum is still used to build the trusted close-group candidate set, but after that the upload only needs CLOSE_GROUP_MAJORITY peers to accept the PUT. With four usable quotes and no already-stored confirmations, the client should be able to select a paid quote accepted by those four peers and attempt storage.

Expected behavior

For SNP chunk upload payment:

  1. Build the witnessed close-group candidate set using the witnessed consensus quorum.
  2. Ask the selected seven witnessed peers for quotes.
  3. Require enough successful quote data for storage majority after counting already-stored close-group confirmations.
  4. Pick the lowest quote that should be accepted by that required majority under:
    • 20% underpayment tolerance;
    • paid quote issuer being visible in each accepting responder's witnessed local view.
  5. Pay only that selected quote.
  6. Store to the same witnessed peer set used for quote selection.

Tests

Ran locally:

cargo test -p ant-core data::client::quote::tests --lib
cargo test -p ant-core data::client::batch::tests --lib
cargo test -p ant-core --test e2e_payment test_quote_collection -- --nocapture
cargo fmt --all -- --check
git diff --check
cargo clippy --all-targets --all-features -- -D warnings

Added regression coverage for:

  • choosing the lowest quote that clears the storage-majority price-floor target;
  • allowing exactly four successful witnessed quote responses when those four accept the selected quote;
  • ignoring high quote outliers;
  • reducing the quote target when close-group peers already report the chunk stored;
  • requiring witnessed issuer acceptance, not just price acceptance;
  • making PUT peers follow the final witnessed quote window;
  • rejecting stale or underpriced cached payment proofs before reuse.

Query the witnessed close group as before, but require enough successful quote responses to choose the cheapest paid quote expected to pass quorum price floors. Keep a single paid quote in the proof and avoid selecting inflated high outliers when lower quotes clear the target.
Allow partially stored chunks to proceed when already-stored close-group votes cover part of the witnessed acceptance target. Carry the adjusted target from quote collection into the one-quote payment selector so retries do not fail before payment after valid partial availability.
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