tasm_lib/hashing/
absorb_multiple_static_size.rs1use triton_vm::prelude::*;
2use twenty_first::tip5::RATE;
3
4use crate::memory::load_words_from_memory_pop_pointer;
5use crate::prelude::*;
6
7#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
9pub struct AbsorbMultipleStaticSize {
10 pub size: usize,
11}
12
13impl AbsorbMultipleStaticSize {
14 fn padded_length(&self) -> usize {
15 (self.size + 1).next_multiple_of(RATE)
16 }
17
18 fn num_absorbs_before_pad(&self) -> usize {
19 self.padded_length() / RATE - 1
20 }
21
22 fn num_remaining_words(&self) -> usize {
23 self.size - self.num_absorbs_before_pad() * RATE
24 }
25
26 fn read_remainder_and_pad(&self) -> Vec<LabelledInstruction> {
27 let num_zeros = self.padded_length() - self.size - 1;
28 let adjust_read_pointer = match self.num_remaining_words() {
29 0 | 1 => triton_asm!(),
30 n => triton_asm!(
31 push {n-1}
33 add
34 ),
36 };
37 [
38 triton_asm![push 0; num_zeros],
39 triton_asm!(push 1),
40 triton_asm!(dup {num_zeros + 1}),
41 adjust_read_pointer,
43 load_words_from_memory_pop_pointer(self.num_remaining_words()),
45 ]
47 .concat()
48 }
49}
50
51impl BasicSnippet for AbsorbMultipleStaticSize {
52 fn inputs(&self) -> Vec<(DataType, String)> {
53 vec![(DataType::VoidPointer, "*sequence".to_string())]
54 }
55
56 fn outputs(&self) -> Vec<(DataType, String)> {
57 vec![(DataType::VoidPointer, "(sequence + size)".to_string())]
58 }
59
60 fn entrypoint(&self) -> String {
61 format!("tasmlib_hashing_absorb_multiple_static_size_{}", self.size)
62 }
63
64 fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
65 let entrypoint = self.entrypoint();
66
67 let absorb_all_non_padded = triton_asm![sponge_absorb_mem; self.num_absorbs_before_pad()];
68 let read_remainder_and_pad = self.read_remainder_and_pad();
69
70 triton_asm! {
71 {entrypoint}:
74 push 0
77 push 0
78 push 0
79 push 0
80 swap 4
81 {&absorb_all_non_padded}
84 swap 4
87 pop 4
88 {&read_remainder_and_pad}
91
92 sponge_absorb
93 push {self.size % RATE}
94 add
95
96 return
99 }
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use twenty_first::prelude::Sponge;
106
107 use super::*;
108 use crate::test_prelude::*;
109
110 impl Procedure for AbsorbMultipleStaticSize {
111 fn rust_shadow(
112 &self,
113 stack: &mut Vec<BFieldElement>,
114 memory: &mut HashMap<BFieldElement, BFieldElement>,
115 _: &NonDeterminism,
116 _: &[BFieldElement],
117 sponge: &mut Option<Tip5>,
118 ) -> Vec<BFieldElement> {
119 let address = stack.pop().unwrap();
121
122 let mut sequence = vec![];
124 for i in 0..self.size {
125 sequence.push(
126 memory
127 .get(&(address + BFieldElement::new(i as u64)))
128 .copied()
129 .unwrap(),
130 )
131 }
132
133 let sponge = sponge.as_mut().expect("sponge must be initialized");
134 sponge.pad_and_absorb_all(&sequence);
135
136 stack.push(address + BFieldElement::new(self.size.try_into().unwrap()));
137
138 vec![]
140 }
141
142 fn pseudorandom_initial_state(
143 &self,
144 seed: [u8; 32],
145 _bench_case: Option<BenchmarkCase>,
146 ) -> ProcedureInitialState {
147 let mut rng = StdRng::from_seed(seed);
148
149 let address = BFieldElement::new(rng.next_u64() % (1 << 20));
151
152 let sequence = (0..self.size)
153 .map(|_| rng.random::<BFieldElement>())
154 .collect_vec();
155
156 let mut memory = HashMap::new();
158 for (i, s) in sequence.into_iter().enumerate() {
159 memory.insert(address + BFieldElement::new(i as u64), s);
160 }
161 let nondeterminism = NonDeterminism::default().with_ram(memory);
162
163 let stack = [self.init_stack_for_isolated_run(), vec![address]].concat();
164
165 let vm_hasher_state = Tip5 {
166 state: rng.random(),
167 };
168
169 ProcedureInitialState {
170 stack,
171 nondeterminism,
172 public_input: vec![],
173 sponge: Some(vm_hasher_state),
174 }
175 }
176 }
177
178 #[test]
179 fn absorb_multiple_static_size_0() {
180 ShadowedProcedure::new(AbsorbMultipleStaticSize { size: 0 }).test();
181 }
182
183 #[test]
184 fn absorb_multiple_static_size_1() {
185 ShadowedProcedure::new(AbsorbMultipleStaticSize { size: 1 }).test();
186 }
187
188 #[test]
189 fn absorb_multiple_static_size_9() {
190 ShadowedProcedure::new(AbsorbMultipleStaticSize { size: 9 }).test();
191 }
192
193 #[test]
194 fn absorb_multiple_static_size_small_pbt() {
195 for size in 0..30 {
196 println!("Testing size {size}");
197 ShadowedProcedure::new(AbsorbMultipleStaticSize { size }).test();
198 }
199 }
200
201 #[proptest(cases = 40)]
202 fn absorb_multiple_static_size_pbt_pbt(#[strategy(arb())] size: u8) {
203 ShadowedProcedure::new(AbsorbMultipleStaticSize {
204 size: size as usize,
205 })
206 .test();
207 }
208}
209
210#[cfg(test)]
211mod benches {
212 use super::*;
213 use crate::test_prelude::*;
214
215 #[test]
216 fn absorb_multiple_static_size_benchmark_102() {
217 ShadowedProcedure::new(AbsorbMultipleStaticSize { size: 102 }).bench();
218 }
219
220 #[test]
221 fn absorb_multiple_static_size_benchmark_400() {
222 ShadowedProcedure::new(AbsorbMultipleStaticSize { size: 400 }).bench();
223 }
224
225 #[test]
226 fn absorb_multiple_static_size_benchmark_2002() {
227 ShadowedProcedure::new(AbsorbMultipleStaticSize { size: 2002 }).bench();
228 }
229}