1use std::{collections::HashMap, fmt::Debug, marker::PhantomData};
18
19use num_bigint::BigUint;
20
21use crate::{
22 instructions::{hash::HashCPU, map::MapCPU},
23 utils::util::big_to_fe,
24 CircuitField,
25};
26
27pub(crate) const TREE_HEIGHT: u8 = 128;
31
32#[derive(Clone, Debug)]
36pub struct MapMt<F: CircuitField, H: HashCPU<F, F>> {
37 pub(crate) root: F,
38 nodes: HashMap<(u8, u128), F>,
40 map: HashMap<BigUint, F>,
42 default_nodes: [F; TREE_HEIGHT as usize],
44 _marker: PhantomData<H>,
45}
46
47impl<F: CircuitField, H: HashCPU<F, F>> PartialEq for MapMt<F, H> {
48 fn eq(&self, other: &Self) -> bool {
49 self.root == other.root
50 && self.nodes == other.nodes
51 && self.default_nodes == other.default_nodes
52 && self.map == other.map
53 }
54}
55
56impl<F: CircuitField, H: HashCPU<F, F>> Eq for MapMt<F, H> {}
57
58impl<F, H> MapCPU<F, F, F> for MapMt<F, H>
59where
60 F: CircuitField,
61 H: HashCPU<F, F>,
62{
63 fn new(default: &F) -> Self {
64 let nodes = HashMap::new();
66 let map = HashMap::new();
67
68 let mut default_nodes = [*default; TREE_HEIGHT as usize];
69
70 for i in 1..TREE_HEIGHT as usize {
71 default_nodes[i] =
72 <H as HashCPU<F, F>>::hash(&[default_nodes[i - 1], default_nodes[i - 1]]);
73 }
74
75 let root = <H as HashCPU<F, F>>::hash(&[default_nodes[127], default_nodes[127]]);
76
77 Self {
78 root,
79 nodes,
80 map,
81 default_nodes,
82 _marker: PhantomData,
83 }
84 }
85
86 fn succinct_repr(&self) -> F {
87 self.root
88 }
89
90 fn insert(&mut self, key: &F, value: &F) {
91 self.map.insert(key.to_biguint(), *value);
92
93 let mut child = *value;
95 let mut node_index = Self::compute_node_index(key);
96
97 for height in 0..TREE_HEIGHT {
98 self.nodes.insert((height, node_index), child);
99
100 let sibling = self.get_sibling(node_index, height);
101 let (x, y) = conditional_swap(node_index & 1 == 1, &child, &sibling);
102 child = <H as HashCPU<F, F>>::hash(&[x, y]);
103 node_index >>= 1;
104 }
105
106 self.root = child;
107 }
108
109 fn get(&self, key: &F) -> F {
110 self.map.get(&key.to_biguint()).copied().unwrap_or(self.default_nodes[0])
111 }
112}
113
114impl<F, H> IntoIterator for MapMt<F, H>
115where
116 F: CircuitField,
117 H: HashCPU<F, F>,
118{
119 type Item = (F, F);
120 type IntoIter = std::iter::Map<
121 std::collections::hash_map::IntoIter<BigUint, F>,
122 fn((BigUint, F)) -> (F, F),
123 >;
124
125 fn into_iter(self) -> Self::IntoIter {
126 self.map.into_iter().map(|(key, value)| (big_to_fe(key), value))
127 }
128}
129
130impl<F, H> MapMt<F, H>
131where
132 F: CircuitField,
133 H: HashCPU<F, F>,
134{
135 pub(crate) fn get_path(&self, key: &F) -> [F; TREE_HEIGHT as usize] {
137 let mut node_index = Self::compute_node_index(key);
138
139 let mut nodes = [F::ZERO; TREE_HEIGHT as usize];
140
141 for (i, val) in nodes.iter_mut().enumerate() {
142 *val = self.get_sibling(node_index, i as u8);
143 node_index >>= 1;
144 }
145
146 nodes
147 }
148
149 #[cfg(test)]
153 fn verify_mem_proof(root: &F, path: &[F; TREE_HEIGHT as usize], key: &F, value: F) -> bool {
154 let mut node_index = Self::compute_node_index(key);
155 let mut child = value;
156
157 for node in path {
158 let (x, y) = conditional_swap(node_index & 1 == 1, &child, node);
159 child = <H as HashCPU<F, F>>::hash(&[x, y]);
160 node_index >>= 1;
161 }
162
163 *root == child
164 }
165
166 fn get_sibling(&self, node_index: u128, height: u8) -> F {
168 assert!(height == 0 || (node_index < 1 << (TREE_HEIGHT - height)));
169
170 let sibling_index = node_index + 1 - 2 * (node_index & 1);
173
174 *self
176 .nodes
177 .get(&(height, sibling_index))
178 .unwrap_or(&self.default_nodes[height as usize])
179 }
180
181 fn compute_node_index(element: &F) -> u128 {
184 let hashed_value = <H as HashCPU<F, F>>::hash(&[*element, F::ZERO]);
185 let bytes = hashed_value.to_bytes_le();
186
187 u128::from_le_bytes(bytes[..TREE_HEIGHT as usize / 8].try_into().unwrap())
188 }
189}
190
191fn conditional_swap<F: CircuitField>(cond: bool, left_input: &F, right_input: &F) -> (F, F) {
193 if cond {
194 (*right_input, *left_input)
195 } else {
196 (*left_input, *right_input)
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use rand::SeedableRng;
203 use rand_chacha::ChaCha8Rng;
204
205 use super::*;
206 use crate::hash::poseidon::{constants::PoseidonField, PoseidonChip};
207
208 fn test_map<F, H>()
209 where
210 F: CircuitField,
211 H: HashCPU<F, F> + Debug,
212 {
213 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
214 let mut mt = MapMt::<F, H>::new(&F::ZERO);
215
216 for _ in 0..100 {
218 mt.insert(&F::random(&mut rng), &F::ONE);
219 }
220
221 mt.insert(&F::ONE, &F::ONE);
223 assert_eq!(mt.get(&F::ONE), F::ONE);
224
225 let old_mt = mt.clone();
227 mt.insert(&F::ONE, &F::ONE);
228
229 assert_eq!(old_mt, mt);
230
231 let one_path = mt.get_path(&F::ONE);
234 let member = MapMt::<F, H>::verify_mem_proof(&mt.root, &one_path, &F::ONE, F::ONE);
235 assert!(member);
236
237 let non_member = MapMt::<F, H>::verify_mem_proof(&mt.root, &one_path, &F::ONE, F::ZERO);
238 assert!(!non_member);
239
240 let new_value = F::random(&mut rng);
242 let path = mt.get_path(&new_value);
243 let non_member = MapMt::<F, H>::verify_mem_proof(&mt.root, &path, &new_value, F::ZERO);
244 assert!(non_member);
245 }
246
247 fn run_poseidon_test<F: PoseidonField>() {
248 test_map::<F, PoseidonChip<F>>();
249 }
250
251 #[test]
252 fn test_map_poseidon() {
253 run_poseidon_test::<midnight_curves::Fq>();
254 }
255}