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}