quilibrium_verkle/
node.rs

1use sha2::{Sha512, Digest};
2use num_bigint::BigUint;
3use bls48581;
4use std::sync::Once;
5
6static INIT_BLS: Once = Once::new();
7
8fn ensure_bls_init() {
9    INIT_BLS.call_once(|| {
10        bls48581::init();
11    });
12}
13
14/// Number of children per branch node (64-ary tree)
15pub const BRANCH_NODES: usize = 64;
16/// Bits per branch level (log2(64) = 6)
17pub const BRANCH_BITS: usize = 6;
18/// Mask for extracting branch index
19pub const BRANCH_MASK: usize = BRANCH_NODES - 1;
20
21/// Node types for serialization
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum NodeType {
24    Nil = 0,
25    Leaf = 1,
26    Branch = 2,
27}
28
29/// Vector commitment node - enum variant approach for safety
30#[derive(Clone)]
31pub enum VectorCommitmentNode {
32    Leaf(VectorCommitmentLeafNode),
33    Branch(Box<VectorCommitmentBranchNode>),
34}
35
36impl VectorCommitmentNode {
37    /// Compute the commitment for this node
38    pub fn commit(&mut self, recalculate: bool) -> Vec<u8> {
39        match self {
40            VectorCommitmentNode::Leaf(leaf) => leaf.commit(recalculate),
41            VectorCommitmentNode::Branch(branch) => branch.commit(recalculate),
42        }
43    }
44
45    /// Get the size of this node
46    pub fn get_size(&self) -> BigUint {
47        match self {
48            VectorCommitmentNode::Leaf(leaf) => leaf.size.clone(),
49            VectorCommitmentNode::Branch(branch) => branch.size.clone(),
50        }
51    }
52
53    /// Check if this is a leaf node
54    pub fn is_leaf(&self) -> bool {
55        matches!(self, VectorCommitmentNode::Leaf(_))
56    }
57
58    /// Check if this is a branch node
59    pub fn is_branch(&self) -> bool {
60        matches!(self, VectorCommitmentNode::Branch(_))
61    }
62
63    /// Get as leaf reference
64    pub fn as_leaf(&self) -> Option<&VectorCommitmentLeafNode> {
65        match self {
66            VectorCommitmentNode::Leaf(leaf) => Some(leaf),
67            _ => None,
68        }
69    }
70
71    /// Get as mutable leaf reference
72    pub fn as_leaf_mut(&mut self) -> Option<&mut VectorCommitmentLeafNode> {
73        match self {
74            VectorCommitmentNode::Leaf(leaf) => Some(leaf),
75            _ => None,
76        }
77    }
78
79    /// Get as branch reference
80    pub fn as_branch(&self) -> Option<&VectorCommitmentBranchNode> {
81        match self {
82            VectorCommitmentNode::Branch(branch) => Some(branch),
83            _ => None,
84        }
85    }
86
87    /// Get as mutable branch reference
88    pub fn as_branch_mut(&mut self) -> Option<&mut VectorCommitmentBranchNode> {
89        match self {
90            VectorCommitmentNode::Branch(branch) => Some(branch),
91            _ => None,
92        }
93    }
94}
95
96/// Leaf node containing a key-value pair
97#[derive(Debug, Clone)]
98pub struct VectorCommitmentLeafNode {
99    /// Key for this leaf
100    pub key: Vec<u8>,
101    /// Value stored at this leaf
102    pub value: Vec<u8>,
103    /// Optional hash target (if provided, used instead of value for commitment)
104    pub hash_target: Option<Vec<u8>>,
105    /// Cached commitment
106    pub commitment: Option<Vec<u8>>,
107    /// Size (always 1 for a leaf)
108    pub size: BigUint,
109}
110
111impl VectorCommitmentLeafNode {
112    /// Create a new leaf node
113    pub fn new(key: Vec<u8>, value: Vec<u8>) -> Self {
114        Self {
115            key,
116            value,
117            hash_target: None,
118            commitment: None,
119            size: BigUint::from(1u32),
120        }
121    }
122
123    /// Create a new leaf node with a hash target
124    pub fn new_with_hash(key: Vec<u8>, value: Vec<u8>, hash_target: Vec<u8>) -> Self {
125        Self {
126            key,
127            value,
128            hash_target: Some(hash_target),
129            commitment: None,
130            size: BigUint::from(1u32),
131        }
132    }
133
134    pub fn commit(&mut self, recalculate: bool) -> Vec<u8> {
135        if self.commitment.is_none() || recalculate {
136            // Use SHA512 for leaf commitments (matches Go implementation)
137            let mut hasher = Sha512::new();
138            // Type byte for leaf
139            hasher.update([0u8]);
140            // Key
141            hasher.update(&self.key);
142            // Value or hash target
143            if let Some(ref hash_target) = self.hash_target {
144                hasher.update(hash_target);
145            } else {
146                hasher.update(&self.value);
147            }
148            self.commitment = Some(hasher.finalize().to_vec());
149        }
150        self.commitment.as_ref().unwrap().clone()
151    }
152}
153
154/// Branch node with 64 children
155#[derive(Clone)]
156pub struct VectorCommitmentBranchNode {
157    /// Prefix bits for this branch (path from root)
158    pub prefix: Vec<usize>,
159    /// Children nodes (64-way branching)
160    pub children: [Option<VectorCommitmentNode>; BRANCH_NODES],
161    /// Cached commitment
162    pub commitment: Option<Vec<u8>>,
163    /// Total size of all children
164    pub size: BigUint,
165    /// Number of leaf nodes under this branch
166    pub leaf_count: usize,
167    /// Length of longest branch path
168    pub longest_branch: usize,
169}
170
171impl VectorCommitmentBranchNode {
172    /// Create a new branch node
173    pub fn new(prefix: Vec<usize>) -> Self {
174        // Create array of None options
175        const INIT: Option<VectorCommitmentNode> = None;
176        let children = [INIT; BRANCH_NODES];
177
178        Self {
179            prefix,
180            children,
181            commitment: None,
182            size: BigUint::from(0u32),
183            leaf_count: 0,
184            longest_branch: 0,
185        }
186    }
187
188    /// Get the polynomial data for this branch (64 × 64 bytes)
189    /// This is the same data used for commitment, but returned for proof generation
190    pub fn get_polynomial(&mut self) -> Vec<u8> {
191        let mut data = Vec::with_capacity(BRANCH_NODES * 64);
192
193        for child_opt in &mut self.children {
194            if let Some(child) = child_opt {
195                let child_commit = child.commit(false);
196
197                // For branch children, hash with prefix
198                let commit = if let Some(branch) = child.as_branch() {
199                    let mut hasher = sha2::Sha512::new();
200                    hasher.update([1u8]); // Type byte for branch
201
202                    // Include prefix
203                    for &p in &branch.prefix {
204                        hasher.update((p as u32).to_be_bytes());
205                    }
206
207                    hasher.update(&child_commit);
208                    hasher.finalize().to_vec()
209                } else {
210                    // Leaf - use commitment as-is (SHA512, 64 bytes)
211                    child_commit
212                };
213
214                data.extend_from_slice(&commit);
215            } else {
216                // Empty slot - push zeros (64 bytes)
217                data.extend_from_slice(&vec![0u8; 64]);
218            }
219        }
220
221        data
222    }
223
224    /// Generate a KZG proof for a specific child index
225    /// Returns the proof bytes that can be used to verify the child commitment
226    pub fn prove(&mut self, index: usize) -> Option<Vec<u8>> {
227        if index >= BRANCH_NODES {
228            return None;
229        }
230
231        // Get polynomial data
232        let data = self.get_polynomial();
233
234        // Initialize BLS48581 if not already done
235        ensure_bls_init();
236
237        // Generate KZG proof for the specific index
238        let proof = bls48581::prove_raw(&data, index as u64, 64);
239        Some(proof)
240    }
241
242    /// Set a child at the given index
243    pub fn set_child(&mut self, index: usize, child: VectorCommitmentNode) {
244        if index < BRANCH_NODES {
245            self.children[index] = Some(child);
246            self.commitment = None; // Invalidate cache
247        }
248    }
249
250    /// Get a child at the given index
251    pub fn get_child(&self, index: usize) -> Option<&VectorCommitmentNode> {
252        if index < BRANCH_NODES {
253            self.children[index].as_ref()
254        } else {
255            None
256        }
257    }
258
259    /// Get a mutable child at the given index
260    pub fn get_child_mut(&mut self, index: usize) -> Option<&mut VectorCommitmentNode> {
261        if index < BRANCH_NODES {
262            self.children[index].as_mut()
263        } else {
264            None
265        }
266    }
267
268    pub fn commit(&mut self, recalculate: bool) -> Vec<u8> {
269        if self.commitment.is_none() || recalculate {
270            // Collect all child commitments (SHA512 hashes, 64 bytes each)
271            let mut vector: Vec<Vec<u8>> = Vec::with_capacity(BRANCH_NODES);
272
273            for child_opt in &mut self.children {
274                if let Some(child) = child_opt {
275                    let child_commit = child.commit(recalculate);
276
277                    // For branch children, hash with prefix
278                    let commit = if let Some(branch) = child.as_branch() {
279                        let mut hasher = Sha512::new();
280                        hasher.update([1u8]); // Type byte for branch
281
282                        // Include prefix
283                        for &p in &branch.prefix {
284                            hasher.update((p as u32).to_be_bytes());
285                        }
286
287                        hasher.update(&child_commit);
288                        hasher.finalize().to_vec()
289                    } else {
290                        // Leaf - use commitment as-is (SHA512, 64 bytes)
291                        child_commit
292                    };
293
294                    vector.push(commit);
295                } else {
296                    // Empty slot - push zeros (64 bytes to match SHA512)
297                    vector.push(vec![0u8; 64]);
298                }
299            }
300
301            // Flatten into polynomial: 64 children × 64 bytes = 4096 bytes
302            let mut data = Vec::with_capacity(BRANCH_NODES * 64);
303            for commit in &vector {
304                data.extend_from_slice(commit);
305            }
306
307            // Initialize BLS48581 if not already done
308            ensure_bls_init();
309
310            // Compute KZG commitment with poly_size = 64
311            // The polynomial has 64 coefficients (one per child)
312            let commitment = bls48581::commit_raw(&data, 64);
313
314            self.commitment = Some(commitment);
315
316            // Update size
317            self.size = BigUint::from(0u32);
318            for child_opt in &self.children {
319                if let Some(child) = child_opt {
320                    self.size += child.get_size();
321                }
322            }
323        }
324
325        self.commitment.as_ref().unwrap().clone()
326    }
327}