tasm_lib/hashing/
squeeze_repeatedly.rs1use triton_vm::prelude::*;
2
3use crate::prelude::*;
4
5#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
8pub struct SqueezeRepeatedly;
9
10impl BasicSnippet for SqueezeRepeatedly {
11 fn parameters(&self) -> Vec<(DataType, String)> {
12 vec![
13 (DataType::VoidPointer, "address".to_string()),
14 (DataType::U32, "num_squeezes".to_string()),
15 ]
16 }
17
18 fn return_values(&self) -> Vec<(DataType, String)> {
19 vec![
20 (DataType::VoidPointer, "address".to_string()),
21 (DataType::U32, "num_squeezes".to_string()),
22 ]
23 }
24
25 fn entrypoint(&self) -> String {
26 "tasmlib_hashing_squeeze_repeatedly".to_string()
27 }
28
29 fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
30 triton_asm! {
31 {self.entrypoint()}:
34
35 dup 0
37 push 0 eq skiz return
39
40 addi -1
41
42 sponge_squeeze pick 11 write_mem 5
46 write_mem 5
47 place 1 recurse
52 }
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use twenty_first::prelude::Sponge;
59
60 use super::*;
61 use crate::empty_stack;
62 use crate::test_helpers::rust_final_state;
63 use crate::test_helpers::tasm_final_state;
64 use crate::test_helpers::verify_memory_equivalence;
65 use crate::test_helpers::verify_sponge_equivalence;
66 use crate::test_helpers::verify_stack_equivalence;
67 use crate::test_helpers::verify_stack_growth;
68 use crate::test_prelude::*;
69
70 impl Procedure for SqueezeRepeatedly {
71 fn rust_shadow(
72 &self,
73 stack: &mut Vec<BFieldElement>,
74 memory: &mut HashMap<BFieldElement, BFieldElement>,
75 _: &NonDeterminism,
76 _: &[BFieldElement],
77 sponge: &mut Option<Tip5>,
78 ) -> Result<Vec<BFieldElement>, RustShadowError> {
79 let num_squeezes = stack.pop().ok_or(RustShadowError::StackUnderflow)?.value() as usize;
80 let address = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
81
82 let Some(sponge) = sponge.as_mut() else {
83 return Err(RustShadowError::SpongeUninitialized);
84 };
85 let sequence = (0..num_squeezes)
86 .flat_map(|_| sponge.squeeze().to_vec())
87 .collect_vec();
88
89 for (i, s) in sequence.into_iter().enumerate() {
90 memory.insert(address + BFieldElement::new(i as u64), s);
91 }
92
93 let new_address = address + BFieldElement::new(tip5::RATE as u64 * num_squeezes as u64);
94 stack.push(new_address);
95 stack.push(BFieldElement::new(0));
96
97 Ok(Vec::new())
98 }
99
100 fn pseudorandom_initial_state(
101 &self,
102 seed: [u8; 32],
103 bench_case: Option<BenchmarkCase>,
104 ) -> ProcedureInitialState {
105 let mut rng = StdRng::from_seed(seed);
106 let num_squeezes = match bench_case {
107 Some(BenchmarkCase::CommonCase) => 10,
108 Some(BenchmarkCase::WorstCase) => 200,
109 None => rng.random_range(0..10),
110 };
111
112 let sponge = Tip5 {
113 state: rng.random(),
114 };
115 let mut stack = empty_stack();
116 let address = BFieldElement::new(rng.next_u64() % (1 << 20));
117 stack.push(address);
118 stack.push(BFieldElement::new(num_squeezes as u64));
119
120 ProcedureInitialState {
121 stack,
122 nondeterminism: NonDeterminism::default(),
123 public_input: vec![],
124 sponge: Some(sponge),
125 }
126 }
127 }
128
129 #[macro_rules_attr::apply(test)]
130 fn test() {
131 let shadow = ShadowedProcedure::new(SqueezeRepeatedly);
134 let num_states = 15;
135 let mut rng = rand::rng();
136 let entrypoint = shadow.inner().entrypoint();
137
138 for _ in 0..num_states {
139 let seed: [u8; 32] = rng.random();
140 println!("testing {entrypoint} common case with seed: {seed:x?}");
141 let ProcedureInitialState {
142 stack,
143 nondeterminism,
144 public_input: stdin,
145 sponge,
146 } = SqueezeRepeatedly.pseudorandom_initial_state(seed, None);
147
148 let init_stack = stack.to_vec();
149
150 let rust = rust_final_state(&shadow, &stack, &stdin, &nondeterminism, &sponge);
151
152 let tasm = tasm_final_state(&shadow, &stack, &stdin, nondeterminism, &sponge).unwrap();
154
155 assert_eq!(
156 rust.public_output, tasm.public_output,
157 "Rust shadowing and VM std out must agree"
158 );
159
160 verify_stack_equivalence(
161 "Rust-shadow",
162 &rust.stack,
163 "TVM execution",
164 &tasm.op_stack.stack,
165 );
166 verify_memory_equivalence("Rust-shadow", &rust.ram, "TVM execution", &tasm.ram);
167 verify_stack_growth(&shadow, &init_stack, &tasm.op_stack.stack);
168 verify_sponge_equivalence(&rust.sponge, &tasm.sponge);
169 }
170 }
171}
172
173#[cfg(test)]
174mod benches {
175 use super::*;
176 use crate::test_prelude::*;
177
178 #[macro_rules_attr::apply(test)]
179 fn benchmark() {
180 ShadowedProcedure::new(SqueezeRepeatedly).bench();
181 }
182}