tasm_lib/hashing/absorb_multiple.rs
1use std::collections::HashMap;
2
3use triton_vm::prelude::*;
4
5use crate::prelude::*;
6use crate::traits::basic_snippet::Reviewer;
7use crate::traits::basic_snippet::SignOffFingerprint;
8
9/// Absorb a sequence of field elements stored in memory, into the Sponge.
10///
11/// ### Behavior
12///
13/// ```text
14/// BEFORE: _ *sequence [len: u32]
15/// AFTER: _
16/// ```
17///
18/// ### Preconditions
19///
20/// - all input arguments are properly [`BFieldCodec`] encoded
21///
22/// ### Postconditions
23///
24/// None.
25#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
26pub struct AbsorbMultiple;
27
28impl BasicSnippet for AbsorbMultiple {
29 fn inputs(&self) -> Vec<(DataType, String)> {
30 vec![
31 (DataType::VoidPointer, "*sequence".to_string()),
32 (DataType::U32, "len".to_string()),
33 ]
34 }
35
36 fn outputs(&self) -> Vec<(DataType, String)> {
37 vec![]
38 }
39
40 fn entrypoint(&self) -> String {
41 "tasmlib_hashing_absorb_multiple".to_string()
42 }
43
44 fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
45 let entrypoint = self.entrypoint();
46 let absorb_all_full_chunks = format!("{entrypoint}_absorb_all_full_chunks");
47 let pad_varnum_zeros = format!("{entrypoint}_pad_varnum_zeros");
48 let read_remainder = format!("{entrypoint}_read_remainder");
49
50 triton_asm! {
51 // BEFORE: _ *bfe_sequence length
52 // AFTER: _
53 {entrypoint}:
54 push 10
55 dup 1
56 div_mod // _ *bfe_sequence length (length/10) (length%10)
57 place 2
58 pop 1 // _ *bfe_sequence (length%10) length
59
60 dup 1 // _ *bfe_sequence (length%10) length (length%10)
61 push -1
62 mul // _ *bfe_sequence (length%10) length (-length%10)
63 dup 3
64 add
65 add // _ *bfe_sequence (length%10) (*bfe_sequence + length - length%10)
66 // _ *bfe_sequence (length%10) *remainder
67
68 push 0
69 push 0
70 push 0
71 push 0 // _ *bfe_sequence (length%10) *remainder 0 0 0 0
72 pick 6 // _ (length%10) *remainder 0 0 0 0 *bfe_sequence
73
74 call {absorb_all_full_chunks}
75 // _ (length%10) *remainder e f g h *remainder
76 pop 5 // _ (length%10) *remainder
77
78 /* Calculate stop condition for reading remainder */
79 addi -1 // _ (length%10) (*remainder - 1)
80 dup 1 // _ (length%10) (*remainder - 1) (length%10)
81 push -1 // _ (length%10) (*remainder - 1) (length%10) -1
82 mul // _ (length%10) (*remainder - 1) (-length%10)
83 addi 9 // _ (length%10) (*remainder - 1) (9-length%10)
84 call {pad_varnum_zeros}
85 // _ [0; 9-length%10] (length%10) (*remainder - 1) 0
86
87 pop 1 // _ [0; 9-length%10] (length%10) (*remainder - 1)
88 push 1 // _ [0; 9-length%10] (length%10) (*remainder - 1) 1
89 swap 2 // _ [0; 9-length%10] 1 (*remainder - 1) (length%10)
90 dup 1
91 add // _ [0; 9-length%10] 1 (*remainder - 1) *last_word
92 call {read_remainder}
93 // _ [last_chunk_padded; 10] (*remainder - 1) (*remainder - 1)
94 pop 2
95 sponge_absorb
96 return
97
98 // BEFORE: _ *remainder 0 0 0 0 *bfe_sequence
99 // INVARIANT: _ *remainder a b c d *bfe_sequence'
100 // AFTER: _ *remainder e f g h *remainder
101 {absorb_all_full_chunks}:
102 dup 5 dup 1 eq
103 skiz return
104
105 // _ *remainder a b c d *bfe_sequence
106 sponge_absorb_mem
107
108 // _ *remainder e f g h *bfe_sequence'
109 recurse
110
111 // BEFORE: _ (length%10) (*remainder - 1) num_zeros
112 // INVARIANT: _ [0; i] (length%10) (*remainder - 1) (num_zeros - i)
113 // AFTER: _ [0; num_zeros] (length%10) (*remainder - 1) 0
114 {pad_varnum_zeros}:
115 dup 0
116 push 0 eq
117 skiz return
118 // _ [0; i] (length%10) (*remainder - 1) (num_zeros - i)
119 push 0
120 place 3 // _ [0; i+1] (length%10) (*remainder - 1) (num_zeros - i)
121 addi -1
122 recurse
123
124 // BEFORE: _ (*remainder - 1) *last_word
125 // INVARIANT: _ [elements; num_elements_read] (*remainder - 1) *some_addr
126 // AFTER: _ [elements; remainder_length] (*remainder - 1) (*remainder - 1)
127 {read_remainder}:
128 dup 1 dup 1 eq
129 skiz return
130 // _ [elements; num_elements_read] (*remainder - 1) *some_addr
131 read_mem 1 // _ [elements; num_elements_read] (*remainder - 1) element (*addr-1)
132 pick 1 // _ [elements; num_elements_read] (*remainder - 1) (*addr-1) element
133 place 2 // _ [elements; num_elements_read+1] (*remainder - 1) (*addr-1)
134 recurse
135 }
136 }
137
138 fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
139 let mut sign_offs = HashMap::new();
140 sign_offs.insert(Reviewer("ferdinand"), 0x66d806516fc1be22.into());
141 sign_offs
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use std::collections::VecDeque;
148
149 use twenty_first::prelude::Sponge;
150
151 use super::*;
152 use crate::empty_stack;
153 use crate::rust_shadowing_helper_functions::array::array_from_memory;
154 use crate::test_prelude::*;
155
156 impl MemPreserver for AbsorbMultiple {
157 fn rust_shadow(
158 &self,
159 stack: &mut Vec<BFieldElement>,
160 memory: &HashMap<BFieldElement, BFieldElement>,
161 _: VecDeque<BFieldElement>,
162 _: VecDeque<Digest>,
163 _: VecDeque<BFieldElement>,
164 sponge: &mut Option<Tip5>,
165 ) -> Vec<BFieldElement> {
166 let length = pop_encodable::<u32>(stack).try_into().unwrap();
167 let address = stack.pop().unwrap();
168
169 let sponge = sponge.as_mut().expect("sponge must be initialized");
170 sponge.pad_and_absorb_all(&array_from_memory::<BFieldElement>(address, length, memory));
171
172 vec![]
173 }
174
175 fn pseudorandom_initial_state(
176 &self,
177 seed: [u8; 32],
178 bench_case: Option<BenchmarkCase>,
179 ) -> MemPreserverInitialState {
180 let mut rng = StdRng::from_seed(seed);
181
182 let address = rng.random::<BFieldElement>();
183 let length = match bench_case {
184 Some(BenchmarkCase::CommonCase) => 102,
185 Some(BenchmarkCase::WorstCase) => 2002,
186 None => rng.random_range(0..=29),
187 };
188
189 let memory: HashMap<_, _> = (0..length)
190 .map(|i| (address + bfe!(i), rng.random()))
191 .collect();
192
193 MemPreserverInitialState {
194 stack: [empty_stack(), bfe_vec![address, length]].concat(),
195 nondeterminism: NonDeterminism::default().with_ram(memory),
196 public_input: VecDeque::new(),
197 sponge_state: Some(Tip5 {
198 state: rng.random(),
199 }),
200 }
201 }
202
203 fn corner_case_initial_states(&self) -> Vec<MemPreserverInitialState> {
204 let mut states = vec![];
205
206 // (all remainders) x {0, 1, 2 full absorptions}
207 for num_words in 0..=29 {
208 // populate RAM with 2s because padding consists of 0s and 1s
209 let ram: HashMap<_, _> = (0..num_words).map(|i| (bfe!(i), bfe!(2))).collect();
210
211 states.push(MemPreserverInitialState {
212 stack: [empty_stack(), bfe_vec![0, num_words]].concat(),
213 nondeterminism: NonDeterminism::default().with_ram(ram),
214 public_input: VecDeque::new(),
215 sponge_state: Some(Tip5::default()),
216 });
217 }
218
219 states
220 }
221 }
222
223 #[test]
224 fn test() {
225 ShadowedMemPreserver::new(AbsorbMultiple).test();
226 }
227}
228
229#[cfg(test)]
230mod benches {
231 use super::*;
232 use crate::test_prelude::*;
233
234 #[test]
235 fn benchmark() {
236 ShadowedMemPreserver::new(AbsorbMultiple).bench();
237 }
238}