-
Notifications
You must be signed in to change notification settings - Fork 25
Simple range proof verification #1566
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -262,12 +262,64 @@ impl<T: TrieReader> Merkle<T> { | |||||||||
| /// incremental range proof verification | ||||||||||
| pub fn verify_range_proof( | ||||||||||
| &self, | ||||||||||
| _first_key: Option<impl KeyType>, | ||||||||||
| _last_key: Option<impl KeyType>, | ||||||||||
| _root_hash: &TrieHash, | ||||||||||
| _proof: &RangeProof<impl KeyType, impl ValueType, impl ProofCollection>, | ||||||||||
| first_key: Option<impl KeyType>, | ||||||||||
| last_key: Option<impl KeyType>, | ||||||||||
| root_hash: &TrieHash, | ||||||||||
| proof: &RangeProof<impl KeyType, impl ValueType, impl ProofCollection>, | ||||||||||
| ) -> Result<(), api::Error> { | ||||||||||
| todo!() | ||||||||||
| // check that the keys are in ascending order | ||||||||||
| let key_values = proof.key_values(); | ||||||||||
| if !key_values | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Using is_sorted_by is slightly simpler: |
||||||||||
| .iter() | ||||||||||
| .zip(key_values.iter().skip(1)) | ||||||||||
| .all(|(a, b)| a.0.as_ref() < b.0.as_ref()) | ||||||||||
| { | ||||||||||
| return Err(api::Error::ProofError( | ||||||||||
| ProofError::NonMonotonicIncreaseRange, | ||||||||||
| )); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // check that the start and end proofs are valid | ||||||||||
| let left = key_values | ||||||||||
| .first() | ||||||||||
| .ok_or(api::Error::ProofError(ProofError::Empty))?; | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is incorrect. An empty set of key-values is only invalid if both key bounds are empty. An empty set of key-values with a bounded range correctly represents an empty set of key-values between the two keys. |
||||||||||
|
|
||||||||||
| // Verify that first_key (if provided) is <= the first key in the proof | ||||||||||
| if let Some(ref requested_first) = first_key | ||||||||||
| && requested_first.as_ref() > left.0.as_ref() | ||||||||||
rkuris marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
| { | ||||||||||
| return Err(api::Error::InvalidRange { | ||||||||||
| start_key: requested_first.as_ref().to_vec().into(), | ||||||||||
| end_key: left.0.as_ref().to_vec().into(), | ||||||||||
| }); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| proof | ||||||||||
| .start_proof() | ||||||||||
| .verify(&left.0, Some(&left.1), root_hash)?; | ||||||||||
|
Comment on lines
+297
to
+299
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is incorrect. When we generate proofs, we always generate a proof for the lower bound. The proof for the lower bound may or may not prove the first key provided in the range. If the lower bound does not exist, the provided proof may not have all the information required to prove the inclusion of the first key.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like we should perhaps construct the key provided and make sure its <= to the first key. |
||||||||||
|
|
||||||||||
| let right = key_values | ||||||||||
| .last() | ||||||||||
| .ok_or(api::Error::ProofError(ProofError::Empty))?; | ||||||||||
|
|
||||||||||
| // Verify that last_key (if provided) is >= the last key in the proof | ||||||||||
| if let Some(ref requested_last) = last_key | ||||||||||
| && requested_last.as_ref() < right.0.as_ref() | ||||||||||
| { | ||||||||||
| return Err(api::Error::InvalidRange { | ||||||||||
| start_key: right.0.as_ref().to_vec().into(), | ||||||||||
| end_key: requested_last.as_ref().to_vec().into(), | ||||||||||
|
Comment on lines
+310
to
+311
|
||||||||||
| start_key: right.0.as_ref().to_vec().into(), | |
| end_key: requested_last.as_ref().to_vec().into(), | |
| start_key: requested_last.as_ref().to_vec().into(), | |
| end_key: right.0.as_ref().to_vec().into(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly here, when we generate the upper bound proof, we generate a proof for the final key in the yielded range only if we truncated the range. Otherwise, we generate a proof for the requested upper bound. This proof may or may not be sufficient to verify the final yielded key.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -26,7 +26,6 @@ fn test_missing_key_proof() { | |||||
| #[test] | ||||||
| // Tests normal range proof with both edge proofs as the existent proof. | ||||||
| // The test cases are generated randomly. | ||||||
| #[ignore = "https://github.com/ava-labs/firewood/issues/738"] | ||||||
| fn test_range_proof() { | ||||||
| let rng = firewood_storage::SeededRng::from_env_or_random(); | ||||||
|
|
||||||
|
|
@@ -69,7 +68,6 @@ fn test_range_proof() { | |||||
| #[test] | ||||||
| // Tests a few cases which the proof is wrong. | ||||||
| // The prover is expected to detect the error. | ||||||
| #[ignore = "https://github.com/ava-labs/firewood/issues/738"] | ||||||
| fn test_bad_range_proof() { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (q / feel-free-to-ignore): I wonder if it's cleaner to split this test into two - one test that tests with verification and one test that doesn't. Having the |
||||||
| let rng = firewood_storage::SeededRng::from_env_or_random(); | ||||||
|
|
||||||
|
|
@@ -78,7 +76,7 @@ fn test_bad_range_proof() { | |||||
| items.sort_unstable(); | ||||||
| let merkle = init_merkle(items.clone()); | ||||||
|
|
||||||
| for _ in 0..10 { | ||||||
| 'skip_test: for _ in 0..10 { | ||||||
|
||||||
| 'skip_test: for _ in 0..10 { | |
| 'next_iteration: for _ in 0..10 { |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These continue statements skip validation of intentionally corrupted proofs. The test cases for modified keys, modified values, gapped entries, empty keys, and nil values are no longer being verified as invalid, which defeats the purpose of test_bad_range_proof. These should either be properly validated or the test should remain ignored until full verification is implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The whole point of this PR is to enable the parts of the test that do work, and out of order is an integral part of this test. The others will get skipped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trace logging for all tests is very expensive, noticed a big improvement here.
Sorry to sneak this in to this PR, really it belongs as a separate PR. Complain if you agree and I'll move it.