tasm_lib/verifier/master_table/
verify_table_rows.rs1use strum::Display;
2use strum::EnumIter;
3use triton_vm::prelude::*;
4use triton_vm::table::NUM_QUOTIENT_SEGMENTS;
5use triton_vm::table::master_table::MasterAuxTable;
6use triton_vm::table::master_table::MasterMainTable;
7use twenty_first::math::x_field_element::EXTENSION_DEGREE;
8
9use crate::hashing::algebraic_hasher::hash_static_size::HashStaticSize;
10use crate::prelude::*;
11
12#[derive(Debug, Copy, Clone, Display, EnumIter)]
13pub enum ColumnType {
14 Main,
15 Aux,
16 Quotient,
17}
18
19#[derive(Debug, Copy, Clone)]
22pub struct VerifyTableRows {
23 pub column_type: ColumnType,
24}
25
26impl VerifyTableRows {
27 pub fn new(column_type: ColumnType) -> Self {
28 Self { column_type }
29 }
30}
31
32impl VerifyTableRows {
33 pub fn row_size(&self) -> usize {
34 match self.column_type {
35 ColumnType::Main => MasterMainTable::NUM_COLUMNS,
36 ColumnType::Aux => MasterAuxTable::NUM_COLUMNS * EXTENSION_DEGREE,
37 ColumnType::Quotient => NUM_QUOTIENT_SEGMENTS * EXTENSION_DEGREE,
38 }
39 }
40}
41
42impl BasicSnippet for VerifyTableRows {
43 fn inputs(&self) -> Vec<(DataType, String)> {
44 vec![
45 (DataType::U32, "num_combination_codeword_checks".to_owned()),
46 (DataType::U32, "merkle_tree_height".to_owned()),
47 (DataType::VoidPointer, "*merkle_tree_root".to_owned()),
48 (DataType::VoidPointer, "*fri_revealed".to_owned()),
49 (DataType::VoidPointer, "*table_rows".to_owned()),
54 ]
55 }
56
57 fn outputs(&self) -> Vec<(DataType, String)> {
58 vec![]
59 }
60
61 fn entrypoint(&self) -> String {
62 format!(
63 "tasmlib_verifier_master_table_verify_{}_table_rows",
64 self.column_type
65 )
66 }
67
68 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
69 let entrypoint = self.entrypoint();
70
71 let hash_row = library.import(Box::new(HashStaticSize {
72 size: self.row_size(),
73 }));
74
75 let loop_over_auth_path_digests_label =
76 format!("{entrypoint}_loop_over_auth_path_elements");
77 let loop_over_auth_paths_code = triton_asm!(
78 {loop_over_auth_path_digests_label}:
79 merkle_step recurse_or_return );
82
83 let loop_over_rows_label = format!("{entrypoint}_loop_over_rows");
84 let loop_over_rows_code = triton_asm!(
85 {loop_over_rows_label}:
87 dup 9
89 push 0
90 eq
91 skiz
92 return
93
94 push 1
97 dup 7
101 read_mem 1
102 push {EXTENSION_DEGREE + 2}
103 add
104 swap 9
108 pop 1
109 dup 10
112 add
113 dup 7
117 call {hash_row}
118 swap 13
121 pop 1
122 call {loop_over_auth_path_digests_label}
125 swap 2
128 swap 4
129 swap 6
130 pop 1
131 swap 2
132 swap 4
133 pop 1
134 assert_vector error_id 40
137 swap 9
140 push -1
141 add
142 swap 9
143 recurse
146
147 {&loop_over_auth_paths_code}
148 );
149
150 triton_asm!(
151 {entrypoint}:
153
154 swap 1
155 push {1 + EXTENSION_DEGREE}
156 add
157 swap 1
158 read_mem 1
162 push 2
163 add
164 swap 1
165 dup 5
168 eq
169 assert error_id 41
170 swap 3
173 push 2
174 pow
175 swap 3
179 dup 2
182 push {Digest::LEN - 1}
183 add
184 read_mem {Digest::LEN}
185 pop 1
186
187 call {loop_over_rows_label}
190 pop 5
193 pop 5
194 return
197
198 {&loop_over_rows_code}
199 )
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use twenty_first::math::other::random_elements;
206 use twenty_first::prelude::*;
207 use twenty_first::tip5::RATE;
208
209 use super::*;
210 use crate::memory::encode_to_memory;
211 use crate::rust_shadowing_helper_functions::list::list_insert;
212 use crate::test_prelude::*;
213
214 #[test]
215 fn verify_table_pbt_main() {
216 ShadowedProcedure::new(VerifyTableRows {
217 column_type: ColumnType::Main,
218 })
219 .test()
220 }
221
222 #[test]
223 fn verify_table_pbt_aux() {
224 ShadowedProcedure::new(VerifyTableRows {
225 column_type: ColumnType::Aux,
226 })
227 .test()
228 }
229
230 #[test]
231 fn verify_table_pbt_quot() {
232 ShadowedProcedure::new(VerifyTableRows {
233 column_type: ColumnType::Quotient,
234 })
235 .test()
236 }
237
238 mod negative_tests {
239 use strum::IntoEnumIterator;
240 use tasm_lib::test_helpers::test_assertion_failure;
241 use test_strategy::proptest;
242
243 use super::*;
244
245 #[proptest(cases = 50)]
246 fn verify_bad_auth_path_crashes_vm(seed: [u8; 32]) {
247 let mut rng = StdRng::from_seed(seed);
248 let snippets = ColumnType::iter().map(|column_type| VerifyTableRows { column_type });
249
250 for snippet in snippets {
251 let mut init_state = snippet.pseudorandom_initial_state(seed, None);
252 let num_digests = init_state.nondeterminism.digests.len();
253 init_state.nondeterminism.digests[rng.random_range(0..num_digests)] = rng.random();
254
255 test_assertion_failure(&ShadowedProcedure::new(snippet), init_state.into(), &[40]);
256 }
257 }
258
259 #[proptest(cases = 50)]
260 fn verify_bad_row_list_length(seed: [u8; 32]) {
261 let snippets = ColumnType::iter().map(|column_type| VerifyTableRows { column_type });
262 for snippet in snippets {
263 let mut init_state = snippet.pseudorandom_initial_state(seed, None);
264
265 let init_stack_length = init_state.stack.len();
267 init_state.stack[init_stack_length - 5].increment();
268
269 test_assertion_failure(&ShadowedProcedure::new(snippet), init_state.into(), &[41]);
270 }
271 }
272 }
273
274 impl Procedure for VerifyTableRows {
278 fn rust_shadow(
279 &self,
280 stack: &mut Vec<BFieldElement>,
281 memory: &mut HashMap<BFieldElement, BFieldElement>,
282 nondeterminism: &NonDeterminism,
283 _public_input: &[BFieldElement],
284 sponge: &mut Option<Tip5>,
285 ) -> Vec<BFieldElement> {
286 fn verify_one_row(
287 leaf_index: u32,
288 merkle_root: Digest,
289 merkle_tree_height: u32,
290 authentication_path: Vec<Digest>,
291 row: &[BFieldElement],
292 sponge: &mut Tip5,
293 ) {
294 fn local_hash_varlen(input: &[BFieldElement], sponge: &mut Tip5) -> Digest {
297 *sponge = Tip5::init();
298 sponge.pad_and_absorb_all(input);
299 let produce: [BFieldElement; RATE] = sponge.squeeze();
300
301 Digest::new((&produce[..Digest::LEN]).try_into().unwrap())
302 }
303
304 let leaf_digest = local_hash_varlen(row, sponge);
305 let merkle_tree_inclusion_proof = MerkleTreeInclusionProof {
306 tree_height: merkle_tree_height,
307 indexed_leafs: vec![(leaf_index as usize, leaf_digest)],
308 authentication_structure: authentication_path,
309 };
310
311 assert!(merkle_tree_inclusion_proof.verify(merkle_root));
312 }
313
314 *sponge = Some(Tip5::init());
315 let table_rows_pointer = stack.pop().unwrap();
316 let fri_revealed_pointer = stack.pop().unwrap();
317 let merkle_tree_root_pointer = stack.pop().unwrap();
318 let merkle_tree_height: u32 = stack.pop().unwrap().try_into().unwrap();
319 let num_combination_codeword_checks: u32 = stack.pop().unwrap().try_into().unwrap();
320
321 let merkle_root = Digest::new(
322 (0..Digest::LEN)
323 .map(|i| memory[&(merkle_tree_root_pointer + bfe!(i as u32))])
324 .collect_vec()
325 .try_into()
326 .unwrap(),
327 );
328
329 let mut j = 0;
331 for i in 0..num_combination_codeword_checks {
332 let row = (0..self.row_size())
334 .map(|l| {
335 memory[&(table_rows_pointer
336 + bfe!(l as u64 + 1 + (self.row_size() as u64) * i as u64))]
337 })
338 .collect_vec();
339
340 let leaf_index: u32 = nondeterminism.ram
342 [&(fri_revealed_pointer + bfe!(4) + BFieldElement::new(i as u64 * 4))]
343 .try_into()
344 .unwrap();
345 let mut authentication_path = vec![];
346 for _ in 0..merkle_tree_height {
347 authentication_path.push(nondeterminism.digests[j]);
348 j += 1;
349 }
350
351 verify_one_row(
352 leaf_index,
353 merkle_root,
354 merkle_tree_height,
355 authentication_path,
356 &row,
357 sponge.as_mut().unwrap(),
358 )
359 }
360
361 vec![]
362 }
363
364 fn pseudorandom_initial_state(
365 &self,
366 seed: [u8; 32],
367 bench_case: Option<BenchmarkCase>,
368 ) -> ProcedureInitialState {
369 let mut rng = StdRng::from_seed(seed);
370 let merkle_tree_height = match bench_case {
371 Some(BenchmarkCase::CommonCase) => 17,
372 Some(BenchmarkCase::WorstCase) => 22,
373 None => rng.random_range(2..7),
374 };
375 let num_leafs = 1 << merkle_tree_height;
376 let num_combination_codeword_checks = 3;
377 let mut memory = HashMap::default();
378
379 let rows: Vec<Vec<BFieldElement>> =
380 vec![vec![rng.random(); self.row_size()]; num_combination_codeword_checks];
381 let leaf_indices: Vec<usize> = (0..num_combination_codeword_checks)
382 .map(|_| rng.random_range(0..num_leafs))
383 .collect_vec();
384
385 let mut leafs: Vec<Digest> = random_elements(1 << merkle_tree_height);
388 for (leaf_index, leaf_preimage) in leaf_indices.iter().zip_eq(rows.iter()) {
389 leafs[*leaf_index] = Tip5::hash_varlen(leaf_preimage);
390 }
391
392 let merkle_tree = MerkleTree::par_new(&leafs).unwrap();
393 let merkle_root = merkle_tree.root();
394 let merkle_root_pointer: BFieldElement = rng.random();
395 encode_to_memory(&mut memory, merkle_root_pointer, &merkle_root);
396
397 let row_pointer: BFieldElement = rng.random();
399 memory.insert(row_pointer, bfe!(num_combination_codeword_checks as u64));
400 let mut j: BFieldElement = bfe!(1);
401 for row in rows {
402 for word in row {
403 memory.insert(row_pointer + j, word);
404 j.increment();
405 }
406 }
407
408 let mocked_fri_return_value: Vec<(u32, XFieldElement)> = leaf_indices
409 .iter()
410 .map(|x| *x as u32)
411 .zip((0..num_combination_codeword_checks).map(|_| rng.random()))
412 .collect_vec();
413 let fri_return_value_pointer = rng.random();
414 list_insert(
415 fri_return_value_pointer,
416 mocked_fri_return_value,
417 &mut memory,
418 );
419
420 let mut authentication_paths: Vec<Digest> = vec![];
421 for leaf_index in leaf_indices {
422 authentication_paths
423 .extend(merkle_tree.authentication_structure(&[leaf_index]).unwrap());
424 }
425
426 let stack = [
427 self.init_stack_for_isolated_run(),
428 vec![
429 bfe!(num_combination_codeword_checks as u64),
430 bfe!(merkle_tree_height),
431 merkle_root_pointer,
432 fri_return_value_pointer,
433 row_pointer,
434 ],
435 ]
436 .concat();
437 ProcedureInitialState {
438 stack,
439 nondeterminism: NonDeterminism::default()
440 .with_ram(memory)
441 .with_digests(authentication_paths),
442 public_input: vec![],
443 sponge: None,
444 }
445 }
446 }
447}
448
449#[cfg(test)]
450mod benches {
451 use super::*;
452 use crate::test_prelude::*;
453
454 #[test]
455 fn verify_table_bench_main() {
456 ShadowedProcedure::new(VerifyTableRows {
457 column_type: ColumnType::Main,
458 })
459 .bench()
460 }
461
462 #[test]
463 fn verify_table_bench_aux() {
464 ShadowedProcedure::new(VerifyTableRows {
465 column_type: ColumnType::Aux,
466 })
467 .bench()
468 }
469
470 #[test]
471 fn verify_table_bench_quot() {
472 ShadowedProcedure::new(VerifyTableRows {
473 column_type: ColumnType::Quotient,
474 })
475 .bench()
476 }
477}