Skip to content

feat(payment): accept flexible single-node proof bundles#136

Merged
jacderida merged 3 commits into
WithAutonomi:rc-2026.6.2from
mickvandijke:feat/flexible-single-quote-payment-proofs
Jun 12, 2026
Merged

feat(payment): accept flexible single-node proof bundles#136
jacderida merged 3 commits into
WithAutonomi:rc-2026.6.2from
mickvandijke:feat/flexible-single-quote-payment-proofs

Conversation

@mickvandijke

@mickvandijke mickvandijke commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR changes node-side payment verification for the existing 0x01 single-node proof format. It keeps the proof tag and wire serialization compatible, allows upgraded nodes to verify smaller quote bundles, and separates proof verification from receiver/admission placement checks.

The payment verifier now validates proof contents plus paid-quote issuer locality. The call sites decide whether this node is allowed to accept the write before payment verification runs:

  • direct client PUT and fresh chunk replication require this node to be in the configured close group for the chunk address according to the local routing table
  • fresh paid-list replication requires this node to be in the paid-list close group / K closest peers for the chunk address according to the local routing table
  • the paid quote issuer must be in the configured close group for the quoted chunk address according to the verifier's local routing table

Added

  • Flexible 0x01 single-node proof verification for any 1..=CLOSE_GROUP_SIZE quote bundle:
    • 0 quotes are rejected.
    • 1 quote is treated as the median-paid quote.
    • 2..=7 quotes are sorted by price and the median-priced quote is treated as the paid quote.
    • more than CLOSE_GROUP_SIZE quotes are rejected.
    • duplicate peer IDs are rejected.
  • Paid median candidate verification without using SingleNodePayment::from_quotes, so partial bundles can be checked directly against completedPayments(quote_hash).
  • Live checks for each paid median candidate:
    • quote content matches the chunk address
    • quote public key derives the claimed peer ID
    • quote ML-DSA signature verifies
    • paid quote issuer is in this node's local configured close group for the chunk address
    • quote price satisfies the local 20% price-floor tolerance
    • on-chain completedPayments(quote_hash) is at least 3 * median_price
  • Direct client PUT storage responsibility in AntProtocol before payment verification/cache lookup.
  • Context-aware verified-payment cache entries:
    • ClientPut entries satisfy later client PUT/fresh replication and paid-list lookups.
    • PaidListAdmission entries satisfy paid-list lookups only.
  • PaymentVerifierConfig::close_group_size, wired from ReplicationConfig in production, devnet, and e2e test setup.
  • AntProtocol::attach_p2p_node, so startup paths attach P2P through the protocol and keep direct PUT responsibility, paid-issuer, and merkle closeness checks wired consistently.
  • Fresh-replication content-address validation before payment verification, matching direct PUT behavior.
  • E2E/unit coverage for:
    • flexible single-node proof bundles
    • single-quote 3x payment requirement
    • paid-list cache entries not authorizing client PUTs
    • direct PUT storage responsibility running before payment cache hits
    • fresh chunk replication and paid-list notification propagation through a local testnet
    • merkle pay-yourself fabricated-pool rejection through the live DHT path

Changed

  • PaymentVerifier::verify_payment no longer checks whether this node is a receiver/member for the request. VerificationContext now controls cache strength only.
  • Paid quote issuer locality is checked against the configured close group, not K closest peers.
  • Direct client PUTs reject locally when this node is not in the configured close group for the chunk address, even if payment is cached.
  • Fresh chunk replication checks close-group responsibility before payment verification, then uses VerificationContext::ClientPut because the receiver immediately stores the chunk as part of fresh write fan-out.
  • Fresh paid-list replication checks paid-list close-group / K membership before payment verification, then uses VerificationContext::PaidListAdmission so it writes a paid-list-strength cache entry.
  • Quote pricing/verifier comments were updated from "freshness" terminology to local paid-quote price-floor terminology.
  • Devnet, production node startup, and e2e testnet startup now attach P2P via AntProtocol rather than attaching only the verifier directly.

Removed

  • Removed receiver/self membership checks from payment verifier functions.
  • Removed the verifier's test-only receiver-membership override; direct PUT storage responsibility now has its own handler-level test override.
  • Removed K-wide paid-quote issuer acceptance for single-node proofs.
  • Removed the old VerificationContext::Replication proof-verification mode.
  • Removed the old replication-verified cache level and replaced it with explicit PaidList vs ClientPut cache strength.
  • Removed the exact-CLOSE_GROUP_SIZE requirement for single-node proof bundles.
  • Removed full validation of non-median quotes in single-node proofs. Non-median quotes are now used only to derive the median price; the paid median candidate is the one whose content, peer binding, signature, issuer locality, local price floor, and on-chain payment are authoritative.
  • Removed the old local-recipient/rewards-address check from single-node verification. Fresh receiver authorization now comes from call-site local placement checks before cache/proof verification, plus paid-issuer close-group locality for the paid quote.

Unchanged

  • No new proof tag is introduced.
  • Existing 7-quote 0x01 proofs remain valid.
  • Merkle proof verification still runs candidate signature checks, candidate closeness, on-chain merkle payment lookup/cache, merkle proof validation, and per-node payment checks.
  • Later neighbour-sync repair paths still do not carry proof bytes and do not call the payment verifier. They continue to use existing closest-set quorum and paid-list evidence.

Verification

Ran locally:

  • cargo fmt --all -- --check
  • git diff --check
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test --lib --features test-utils -- --nocapture — 526 passed
  • cargo test --test e2e --features test-utils data_types::chunk::tests::test_chunk_store_retrieve_small -- --nocapture — passed, local minimal testnet
  • cargo test --test e2e --features test-utils replication::test_fresh_replication_propagates_to_close_group -- --nocapture — passed, local replication testnet
  • cargo test --test e2e --features test-utils replication::scenario_24_fresh_replication_propagates_paid_notify -- --nocapture — passed, local replication testnet
  • cargo test --test e2e --features test-utils payment_flow::helper_tests::test_init_testnet_and_evm -- --nocapture — passed, local 10-node payment-enabled testnet + Anvil setup
  • cargo test --test e2e --features test-utils merkle_payment::test_attack_merkle_pay_yourself_fabricated_pool -- --nocapture — passed, local merkle/DHT attack testnet

Note: scripts/test_e2e.sh was not run from this checkout because it requires target/release/ant-cli, which is not built/present in this repository checkout.

@mickvandijke mickvandijke force-pushed the feat/flexible-single-quote-payment-proofs branch 6 times, most recently from 747277b to 861f197 Compare June 11, 2026 22:49
Verify paid single-node quotes from 1..=CLOSE_GROUP_SIZE supplied quotes while preserving the existing 0x01 proof format and 3x payment requirement.

Run direct client PUTs and fresh chunk replication through the full ClientPut check set, including local close-group receiver membership.

Run fresh paid-list admission through the same live payment checks with K-wide receiver membership, and keep verified-payment cache entries scoped by verification strength.
@mickvandijke mickvandijke force-pushed the feat/flexible-single-quote-payment-proofs branch from 861f197 to 692a4a0 Compare June 12, 2026 07:45
@mickvandijke mickvandijke marked this pull request as ready for review June 12, 2026 11:16
Move receiver placement checks out of PaymentVerifier so proof verification only validates payment contents and paid-quote issuer locality. Direct client PUTs now check local close-group storage responsibility before payment verification, while fresh replication and paid-list notification paths keep their pre-verification admission gates. Paid quote issuers are now checked against the configured close group instead of K closest peers.
@dirvine

dirvine commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Re-review after 5e54ef6598512ccfc8d6084f737c1ce6206a712f:

The paid-issuer-width concern from my earlier review looks addressed.

What I verified:

  • The paid quote issuer is now checked with the configured close-group width via find_closest_nodes_local_with_self(xorname, close_group_size), rather than K_BUCKET_SIZE / top-20.
  • Direct PUT receiver responsibility is checked in AntProtocol before payment verification/cache use.
  • Fresh replication still checks config.close_group_size responsibility before proof verification.
  • PaidListAdmission keeps weaker cache semantics, so a paid-list cache entry does not authorise a later client PUT.

Local checks run on this head:

  • cargo test payment::verifier --lib --features test-utils -- --nocapture — passed, 74 tests.
  • cargo test storage::handler::tests::test_put_rejects_out_of_range_receiver_before_payment_cache --lib --features test-utils -- --nocapture — passed.

CI at time of review: builds, clippy, format, docs, security audit, and no-logging test are green; OS matrix tests are still pending. No remaining blocker from my focused review, pending those matrix tests completing green.

@jacderida jacderida merged commit cafc27b into WithAutonomi:rc-2026.6.2 Jun 12, 2026
12 checks passed
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.

3 participants