Skip to content

Commit 4393ba6

Browse files
zhangfengcdtclaude
andauthored
Add generate_proof and verify_proof methods to VersionedKvStore (#101)
* Add generate_proof and verify_proof methods to VersionedKvStore Expose cryptographic proof functionality from ProllyTree to VersionedKvStore in both Rust and Python APIs for consistent proof generation and verification across versioned key-value operations. ## Changes ### Rust Implementation - Add `generate_proof(&self, key: &[u8]) -> Proof<N>` to VersionedKvStore - Add `verify(&self, proof: Proof<N>, key: &[u8], expected_value: Option<&[u8]>) -> bool` - Both methods delegate to underlying ProllyTree implementation - Add comprehensive unit test `test_versioned_store_proof_methods` ### Python Bindings - Add `generate_proof(key: bytes) -> bytes` to PyVersionedKvStore - Add `verify_proof(proof_bytes: bytes, key: bytes, expected_value: Optional[bytes]) -> bool` - Handle proof serialization/deserialization with bincode - Thread-safe implementation with proper GIL handling ## API Usage ```python store = VersionedKvStore.open("/path/to/repo") proof = store.generate_proof(b"key1") is_valid = store.verify_proof(proof, b"key1", b"value1") ``` 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Update Python README to document VersionedKvStore proof methods Add documentation for the newly added generate_proof and verify_proof methods in the VersionedKvStore example section. Also update the cryptographic verification feature description to clarify it's available for both trees and versioned storage. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent 897803d commit 4393ba6

File tree

3 files changed

+145
-1
lines changed

3 files changed

+145
-1
lines changed

python/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ The full documentation includes:
3939
- **🌳 Probabilistic Trees** - High-performance data storage with automatic balancing
4040
- **🤖 AI Agent Memory** - Multi-layered memory systems for intelligent agents
4141
- **📚 Versioned Storage** - Git-like version control for key-value data
42-
- **🔐 Cryptographic Verification** - Merkle proofs for data integrity
42+
- **🔐 Cryptographic Verification** - Merkle proofs for data integrity across trees and versioned storage
4343
- **⚡ SQL Queries** - Query your data using SQL syntax
4444

4545
## 🔥 Key Use Cases
@@ -84,6 +84,10 @@ commit_id = store.commit("Add production config")
8484
store.create_branch("experiment")
8585
store.insert(b"feature", b"experimental_data")
8686
store.commit("Add experimental feature")
87+
88+
# Cryptographic verification on versioned data
89+
proof = store.generate_proof(b"config")
90+
is_valid = store.verify_proof(proof, b"config", b"production_settings")
8791
```
8892

8993
### SQL Queries

src/git/versioned_store.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,110 @@ where
605605

606606
Ok(self.git_repo.path().join(staging_filename))
607607
}
608+
609+
/// Generate a cryptographic proof for a key's existence and value in the tree
610+
/// This proof can be used to verify the integrity of the key-value pair without
611+
/// requiring access to the entire tree structure.
612+
///
613+
/// # Parameters
614+
/// - `key`: The key to generate proof for
615+
///
616+
/// # Returns
617+
/// - A proof object containing the hash path from root to the target node
618+
pub fn generate_proof(&self, key: &[u8]) -> crate::proof::Proof<N> {
619+
self.tree.generate_proof(key)
620+
}
621+
622+
/// Verify a cryptographic proof for a key-value pair
623+
/// This checks that the proof is valid and optionally verifies the expected value
624+
///
625+
/// # Parameters
626+
/// - `proof`: The proof to verify
627+
/// - `key`: The key that the proof claims to prove
628+
/// - `expected_value`: Optional expected value to verify against
629+
///
630+
/// # Returns
631+
/// - `true` if the proof is valid, `false` otherwise
632+
pub fn verify(
633+
&self,
634+
proof: crate::proof::Proof<N>,
635+
key: &[u8],
636+
expected_value: Option<&[u8]>,
637+
) -> bool {
638+
self.tree.verify(proof, key, expected_value)
639+
}
640+
}
641+
642+
#[cfg(test)]
643+
mod proof_tests {
644+
use super::*;
645+
use tempfile::TempDir;
646+
647+
#[test]
648+
fn test_versioned_store_proof_methods() {
649+
// Create a temporary directory for the test
650+
let temp_dir = TempDir::new().expect("Failed to create temp dir");
651+
let repo_path = temp_dir.path().to_str().unwrap();
652+
653+
// Initialize git repo
654+
std::process::Command::new("git")
655+
.args(["init"])
656+
.current_dir(repo_path)
657+
.output()
658+
.expect("Failed to initialize git repo");
659+
660+
// Set git config
661+
std::process::Command::new("git")
662+
.args(["config", "user.name", "Test User"])
663+
.current_dir(repo_path)
664+
.output()
665+
.expect("Failed to set git user name");
666+
667+
std::process::Command::new("git")
668+
.args(["config", "user.email", "[email protected]"])
669+
.current_dir(repo_path)
670+
.output()
671+
.expect("Failed to set git user email");
672+
673+
// Create a subdirectory for the dataset (git-prolly requires this)
674+
let dataset_path = temp_dir.path().join("dataset");
675+
std::fs::create_dir(&dataset_path).expect("Failed to create dataset directory");
676+
677+
// Change to the dataset subdirectory
678+
let original_dir = std::env::current_dir().expect("Failed to get current dir");
679+
std::env::set_current_dir(&dataset_path).expect("Failed to change directory");
680+
681+
// Initialize the versioned store from the dataset subdirectory
682+
let mut store =
683+
GitVersionedKvStore::<32>::init(&dataset_path).expect("Failed to initialize store");
684+
685+
// Insert test data
686+
let key = b"proof_test_key".to_vec();
687+
let value = b"proof_test_value".to_vec();
688+
689+
store
690+
.insert(key.clone(), value.clone())
691+
.expect("Failed to insert");
692+
store
693+
.commit("Add test data for proof")
694+
.expect("Failed to commit");
695+
696+
// Test generate_proof method exists and works
697+
let proof = store.generate_proof(&key);
698+
699+
// Test verify method with correct value
700+
assert!(store.verify(proof.clone(), &key, Some(&value)));
701+
702+
// Test verify method for existence only
703+
assert!(store.verify(proof.clone(), &key, None));
704+
705+
// Test verify with wrong value should fail
706+
let wrong_value = b"wrong_value".to_vec();
707+
assert!(!store.verify(proof.clone(), &key, Some(&wrong_value)));
708+
709+
// Restore original directory
710+
std::env::set_current_dir(original_dir).expect("Failed to restore directory");
711+
}
608712
}
609713

610714
// Generic diff functionality for all storage types

src/python.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,42 @@ impl PyVersionedKvStore {
11231123
let store = self.inner.lock().unwrap();
11241124
Ok(store.storage_backend().clone().into())
11251125
}
1126+
1127+
fn generate_proof(&self, py: Python, key: &Bound<'_, PyBytes>) -> PyResult<Py<PyBytes>> {
1128+
let key_vec = key.as_bytes().to_vec();
1129+
1130+
let proof_bytes = py.allow_threads(|| {
1131+
let store = self.inner.lock().unwrap();
1132+
let proof = store.generate_proof(&key_vec);
1133+
1134+
bincode::serialize(&proof)
1135+
.map_err(|e| PyValueError::new_err(format!("Proof serialization failed: {}", e)))
1136+
})?;
1137+
1138+
Ok(PyBytes::new_bound(py, &proof_bytes).into())
1139+
}
1140+
1141+
#[pyo3(signature = (proof_bytes, key, expected_value=None))]
1142+
fn verify_proof(
1143+
&self,
1144+
py: Python,
1145+
proof_bytes: &Bound<'_, PyBytes>,
1146+
key: &Bound<'_, PyBytes>,
1147+
expected_value: Option<&Bound<'_, PyBytes>>,
1148+
) -> PyResult<bool> {
1149+
let key_vec = key.as_bytes().to_vec();
1150+
let proof_vec = proof_bytes.as_bytes().to_vec();
1151+
let value_option = expected_value.map(|v| v.as_bytes().to_vec());
1152+
1153+
py.allow_threads(|| {
1154+
let proof: crate::proof::Proof<32> = bincode::deserialize(&proof_vec).map_err(|e| {
1155+
PyValueError::new_err(format!("Proof deserialization failed: {}", e))
1156+
})?;
1157+
1158+
let store = self.inner.lock().unwrap();
1159+
Ok(store.verify(proof, &key_vec, value_option.as_deref()))
1160+
})
1161+
}
11261162
}
11271163

11281164
#[cfg(feature = "git")]

0 commit comments

Comments
 (0)