fix!: enforce merkle payment amount verification#57
Closed
mickvandijke wants to merge 6 commits intomainfrom
Closed
fix!: enforce merkle payment amount verification#57mickvandijke wants to merge 6 commits intomainfrom
mickvandijke wants to merge 6 commits intomainfrom
Conversation
The merkle payment verifier only checked that paid amounts were non-zero, not that they met the candidate's quoted price. A malicious client could submit fake low prices in PoolCommitment candidates while keeping the real poolHash, causing the contract to charge almost nothing while nodes still accepted the proof. Replace `paid_amount.is_zero()` with `paid_amount < node.price` so each paid candidate must receive at least their ML-DSA-65 signed quoted price. Also fix existing unit tests that were missing the Amount field in paid_node_addresses tuples, and add test_merkle_underpayment_rejected. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Migrate from the old QuotingMetrics-based pricing and split DataPayments/MerklePayments contracts to the unified PaymentVault API in evmlib. Key changes: - Replace QuotingMetrics with a single `price: Amount` field on quotes - Replace logarithmic pricing with simple quadratic formula (n/6000)² - Unify data_payments_address + merkle_payments_address into payment_vault_address - Verify payments via completedPayments mapping instead of verify_data_payment batch call Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-payment-vault-v2 branch
The verifier checked `paid_amount >= node.price` (individual quote) but the contract pays each winner `median16(quotes) * 2^depth / depth`. A winner quoting above the median could be paid less than their quote, causing the node to incorrectly reject a valid payment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Integrate `SingleNodePayment::from_quotes` to derive correct on-chain payment amounts. This ensures exact-match checks in the contract's `verifyPayment` function pass by reconstructing amounts as used by the 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.
Summary
paid_amount.is_zero()instead ofpaid_amount >= candidate.pricePoolCommitmentcandidates while keeping the realpoolHash, causing the contract to charge almost nothing while nodes still accepted the proofpaid_amount < node.priceso each paid candidate must receive at least their ML-DSA-65 signed quoted pricetest_merkle_underpayment_rejectedunit testAmountfield inpaid_node_addressestuplesThe attack vector (now closed)
poolHash(commits to full candidate data including signatures)PoolCommitmentwith realpoolHashbut fake 1-wei pricesmedian(1 wei) × 2^depth— essentially nothing> 0, not>= quoted priceNote
MerklePaymentCandidatePool::verify_prices()in evmlib already identified this gap but was never called. The node-side fix is simpler and sufficient since the proof already contains both the on-chain paid amounts and the ML-DSA-65 signed candidate prices.Test plan
cargo test --lib -- payment::verifier::tests)🤖 Generated with Claude Code