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 parameters(&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 return_values(&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 #[macro_rules_attr::apply(test)]
215 fn verify_table_pbt_main() {
216 ShadowedProcedure::new(VerifyTableRows {
217 column_type: ColumnType::Main,
218 })
219 .test()
220 }
221
222 #[macro_rules_attr::apply(test)]
223 fn verify_table_pbt_aux() {
224 ShadowedProcedure::new(VerifyTableRows {
225 column_type: ColumnType::Aux,
226 })
227 .test()
228 }
229
230 #[macro_rules_attr::apply(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
242 use super::*;
243
244 #[macro_rules_attr::apply(proptest(cases = 50))]
245 fn verify_bad_auth_path_crashes_vm(seed: [u8; 32]) {
246 let mut rng = StdRng::from_seed(seed);
247 let snippets = ColumnType::iter().map(|column_type| VerifyTableRows { column_type });
248
249 for snippet in snippets {
250 let mut init_state = snippet.pseudorandom_initial_state(seed, None);
251 let num_digests = init_state.nondeterminism.digests.len();
252 init_state.nondeterminism.digests[rng.random_range(0..num_digests)] = rng.random();
253
254 test_assertion_failure(&ShadowedProcedure::new(snippet), init_state.into(), &[40]);
255 }
256 }
257
258 #[macro_rules_attr::apply(proptest(cases = 50))]
259 fn verify_bad_row_list_length(seed: [u8; 32]) {
260 let snippets = ColumnType::iter().map(|column_type| VerifyTableRows { column_type });
261 for snippet in snippets {
262 let mut init_state = snippet.pseudorandom_initial_state(seed, None);
263
264 let init_stack_length = init_state.stack.len();
266 init_state.stack[init_stack_length - 5].increment();
267
268 test_assertion_failure(&ShadowedProcedure::new(snippet), init_state.into(), &[41]);
269 }
270 }
271 }
272
273 impl Procedure for VerifyTableRows {
277 fn rust_shadow(
278 &self,
279 stack: &mut Vec<BFieldElement>,
280 memory: &mut HashMap<BFieldElement, BFieldElement>,
281 nondeterminism: &NonDeterminism,
282 _public_input: &[BFieldElement],
283 sponge_arg: &mut Option<Tip5>,
284 ) -> Result<Vec<BFieldElement>, RustShadowError> {
285 fn verify_one_row(
286 leaf_index: u32,
287 merkle_root: Digest,
288 merkle_tree_height: u32,
289 authentication_path: Vec<Digest>,
290 row: &[BFieldElement],
291 sponge: &mut Tip5,
292 ) -> Result<(), RustShadowError> {
293 fn local_hash_varlen(input: &[BFieldElement], sponge: &mut Tip5) -> Digest {
296 *sponge = Tip5::init();
297 sponge.pad_and_absorb_all(input);
298 let produce: [BFieldElement; RATE] = sponge.squeeze();
299
300 Digest::new((&produce[..Digest::LEN]).try_into().unwrap())
301 }
302
303 let leaf_digest = local_hash_varlen(row, sponge);
304 let merkle_tree_inclusion_proof = MerkleTreeInclusionProof {
305 tree_height: merkle_tree_height,
306 indexed_leafs: vec![(leaf_index as usize, leaf_digest)],
307 authentication_structure: authentication_path,
308 };
309
310 merkle_tree_inclusion_proof
311 .verify(merkle_root)
312 .then_some(())
313 .ok_or(RustShadowError::InvalidProof)
314 }
315
316 let table_rows_pointer = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
317 let fri_revealed_pointer = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
318 let merkle_tree_root_pointer = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
319 let merkle_tree_height: u32 = stack
320 .pop()
321 .ok_or(RustShadowError::StackUnderflow)?
322 .try_into()
323 .map_err(|_| RustShadowError::U64ToU32Error)?;
324 let num_combination_codeword_checks: u32 = stack
325 .pop()
326 .ok_or(RustShadowError::StackUnderflow)?
327 .try_into()
328 .map_err(|_| RustShadowError::U64ToU32Error)?;
329
330 let merkle_root_innards = (0..Digest::LEN)
331 .map(|i| {
332 memory
333 .get(&(merkle_tree_root_pointer + bfe!(i as u32)))
334 .ok_or(RustShadowError::DecodingError)
335 .copied()
336 })
337 .try_collect::<_, Vec<_>, _>()?;
338 let merkle_root = Digest::new(merkle_root_innards.try_into().unwrap());
339
340 let mut sponge = Tip5::init();
342 let mut j = 0;
343 for i in 0..num_combination_codeword_checks {
344 let row: Vec<_> = (0..self.row_size())
346 .map(|l| {
347 let word_pointer = table_rows_pointer
348 + bfe!(l as u64 + 1 + (self.row_size() as u64) * i as u64);
349 memory
350 .get(&word_pointer)
351 .copied()
352 .ok_or(RustShadowError::DecodingError)
353 })
354 .try_collect()?;
355
356 let leaf_index: u32 = nondeterminism
358 .ram
359 .get(&(fri_revealed_pointer + bfe!(4) + BFieldElement::new(i as u64 * 4)))
360 .copied()
361 .ok_or(RustShadowError::DecodingError)?
362 .try_into()
363 .map_err(|_| RustShadowError::U64ToU32Error)?;
364 let mut authentication_path = vec![];
365 for _ in 0..merkle_tree_height {
366 authentication_path.push(nondeterminism.digests[j]);
367 j += 1;
368 }
369
370 verify_one_row(
371 leaf_index,
372 merkle_root,
373 merkle_tree_height,
374 authentication_path,
375 &row,
376 &mut sponge,
377 )?;
378 }
379 *sponge_arg = Some(sponge);
380
381 Ok(Vec::new())
382 }
383
384 fn pseudorandom_initial_state(
385 &self,
386 seed: [u8; 32],
387 bench_case: Option<BenchmarkCase>,
388 ) -> ProcedureInitialState {
389 let mut rng = StdRng::from_seed(seed);
390 let merkle_tree_height = match bench_case {
391 Some(BenchmarkCase::CommonCase) => 17,
392 Some(BenchmarkCase::WorstCase) => 22,
393 None => rng.random_range(2..7),
394 };
395 let num_leafs = 1 << merkle_tree_height;
396 let num_combination_codeword_checks = 3;
397 let mut memory = HashMap::default();
398
399 let rows: Vec<Vec<BFieldElement>> =
400 vec![vec![rng.random(); self.row_size()]; num_combination_codeword_checks];
401 let leaf_indices: Vec<usize> = (0..num_combination_codeword_checks)
402 .map(|_| rng.random_range(0..num_leafs))
403 .collect_vec();
404
405 let mut leafs: Vec<Digest> = random_elements(1 << merkle_tree_height);
408 for (leaf_index, leaf_preimage) in leaf_indices.iter().zip_eq(rows.iter()) {
409 leafs[*leaf_index] = Tip5::hash_varlen(leaf_preimage);
410 }
411
412 let merkle_tree = MerkleTree::par_new(&leafs).unwrap();
413 let merkle_root = merkle_tree.root();
414 let merkle_root_pointer: BFieldElement = rng.random();
415 encode_to_memory(&mut memory, merkle_root_pointer, &merkle_root);
416
417 let row_pointer: BFieldElement = rng.random();
419 memory.insert(row_pointer, bfe!(num_combination_codeword_checks as u64));
420 let mut j: BFieldElement = bfe!(1);
421 for row in rows {
422 for word in row {
423 memory.insert(row_pointer + j, word);
424 j.increment();
425 }
426 }
427
428 let mocked_fri_return_value: Vec<(u32, XFieldElement)> = leaf_indices
429 .iter()
430 .map(|x| *x as u32)
431 .zip((0..num_combination_codeword_checks).map(|_| rng.random()))
432 .collect_vec();
433 let fri_return_value_pointer = rng.random();
434 list_insert(
435 fri_return_value_pointer,
436 mocked_fri_return_value,
437 &mut memory,
438 );
439
440 let mut authentication_paths: Vec<Digest> = vec![];
441 for leaf_index in leaf_indices {
442 authentication_paths
443 .extend(merkle_tree.authentication_structure(&[leaf_index]).unwrap());
444 }
445
446 let stack = [
447 self.init_stack_for_isolated_run(),
448 vec![
449 bfe!(num_combination_codeword_checks as u64),
450 bfe!(merkle_tree_height),
451 merkle_root_pointer,
452 fri_return_value_pointer,
453 row_pointer,
454 ],
455 ]
456 .concat();
457 ProcedureInitialState {
458 stack,
459 nondeterminism: NonDeterminism::default()
460 .with_ram(memory)
461 .with_digests(authentication_paths),
462 public_input: vec![],
463 sponge: None,
464 }
465 }
466 }
467}
468
469#[cfg(test)]
470mod benches {
471 use super::*;
472 use crate::test_prelude::*;
473
474 #[macro_rules_attr::apply(test)]
475 fn verify_table_bench_main() {
476 ShadowedProcedure::new(VerifyTableRows {
477 column_type: ColumnType::Main,
478 })
479 .bench()
480 }
481
482 #[macro_rules_attr::apply(test)]
483 fn verify_table_bench_aux() {
484 ShadowedProcedure::new(VerifyTableRows {
485 column_type: ColumnType::Aux,
486 })
487 .bench()
488 }
489
490 #[macro_rules_attr::apply(test)]
491 fn verify_table_bench_quot() {
492 ShadowedProcedure::new(VerifyTableRows {
493 column_type: ColumnType::Quotient,
494 })
495 .bench()
496 }
497}