quilibrium_verkle/
schema.rs

1use super::trie::VectorCommitmentTrie;
2use crate::Result;
3use serde::{Deserialize, Serialize};
4
5/// Field mapping for structured data in a verkle trie
6///
7/// This provides a generic way to map field names to their paths and values
8/// in a verkle trie, useful for structured data like records with named fields.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct FieldMapping {
11    /// Field name (e.g., "owner", "name", "parent")
12    pub field_name: String,
13    /// Field path in the trie (derived from field name)
14    pub field_path: Vec<u8>,
15    /// Field value as bytes
16    pub value: Vec<u8>,
17}
18
19/// Generic schema for mapping structured records to verkle trie
20///
21/// This provides a way to organize structured data in a verkle trie,
22/// where each field maps to a specific path derived from the field name.
23pub struct RecordSchema {
24    trie: VectorCommitmentTrie,
25}
26
27impl RecordSchema {
28    /// Create a new schema
29    pub fn new() -> Self {
30        Self {
31            trie: VectorCommitmentTrie::new(),
32        }
33    }
34
35    /// Insert a field into the trie
36    pub fn insert_field(&mut self, field_name: &str, value: &[u8]) -> Result<()> {
37        let field_path = Self::derive_field_path(field_name);
38        self.trie.insert(field_path, value.to_vec())
39    }
40
41    /// Derive a field path from field name
42    /// Uses SHA512 to ensure uniform distribution of fields across the trie
43    pub fn derive_field_path(field_name: &str) -> Vec<u8> {
44        use sha2::{Sha512, Digest};
45        let mut hasher = Sha512::new();
46        hasher.update(b"field:");
47        hasher.update(field_name.as_bytes());
48        hasher.finalize().to_vec()
49    }
50
51    /// Get the trie commitment
52    pub fn commitment(&mut self) -> Option<Vec<u8>> {
53        self.trie.commit()
54    }
55
56    /// Get the underlying trie
57    pub fn trie(&self) -> &VectorCommitmentTrie {
58        &self.trie
59    }
60
61    /// Get mutable reference to the underlying trie
62    pub fn trie_mut(&mut self) -> &mut VectorCommitmentTrie {
63        &mut self.trie
64    }
65}
66
67impl Default for RecordSchema {
68    fn default() -> Self {
69        Self::new()
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_field_path_derivation() {
79        let path1 = RecordSchema::derive_field_path("owner");
80        let path2 = RecordSchema::derive_field_path("name");
81        let path3 = RecordSchema::derive_field_path("owner");
82
83        // Different fields should produce different paths
84        assert_ne!(path1, path2);
85
86        // Same field should produce same path
87        assert_eq!(path1, path3);
88
89        // All paths should be 64 bytes (SHA512 output)
90        assert_eq!(path1.len(), 64);
91        assert_eq!(path2.len(), 64);
92    }
93
94    #[test]
95    fn test_schema_basic_operations() {
96        bls48581::init();
97
98        let mut schema = RecordSchema::new();
99
100        // Insert fields
101        schema.insert_field("owner", b"alice").unwrap();
102        schema.insert_field("name", b"test").unwrap();
103        schema.insert_field("value", b"12345").unwrap();
104
105        // Should be able to get commitment
106        let commitment = schema.commitment();
107        assert!(commitment.is_some());
108        // With multiple fields, we'll have a branch node -> KZG commitment (74 bytes)
109        assert_eq!(commitment.unwrap().len(), 74);
110    }
111
112    #[test]
113    fn test_deterministic_commitment() {
114        bls48581::init();
115
116        let mut schema1 = RecordSchema::new();
117        schema1.insert_field("field1", b"value1").unwrap();
118        schema1.insert_field("field2", b"value2").unwrap();
119
120        let mut schema2 = RecordSchema::new();
121        schema2.insert_field("field1", b"value1").unwrap();
122        schema2.insert_field("field2", b"value2").unwrap();
123
124        let commitment1 = schema1.commitment().unwrap();
125        let commitment2 = schema2.commitment().unwrap();
126
127        // Same fields should produce same commitment
128        assert_eq!(commitment1, commitment2);
129    }
130}