tasm_lib/hashing/sponge_hasher/
pad_and_absorb_all.rs

1use triton_vm::prelude::*;
2
3use crate::list::LIST_METADATA_SIZE;
4use crate::prelude::*;
5
6#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
7pub struct PadAndAbsorbAll;
8
9impl BasicSnippet for PadAndAbsorbAll {
10    fn inputs(&self) -> Vec<(DataType, String)> {
11        vec![(DataType::List(Box::new(DataType::Bfe)), "input".to_string())]
12    }
13
14    fn outputs(&self) -> Vec<(DataType, String)> {
15        vec![]
16    }
17
18    fn entrypoint(&self) -> String {
19        "tasmlib_hashing_sponge_hasher_pad_and_absorb_all".into()
20    }
21
22    fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
23        let entrypoint = self.entrypoint();
24        let hash_absorb_snippet_subroutine =
25            library.import(Box::new(crate::hashing::absorb_multiple::AbsorbMultiple));
26        triton_asm!(
27            {entrypoint}:
28                // _ *input_list
29
30                read_mem 1
31                // _ length (*input_list - 1)
32
33                addi {LIST_METADATA_SIZE + 1}
34                // _ length *first_element
35
36                pick 1
37                // _ *first_element length
38
39                call {hash_absorb_snippet_subroutine}
40                // _
41
42                return
43        )
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use arbitrary::Arbitrary;
50    use arbitrary::Unstructured;
51    use triton_vm::prelude::*;
52    use twenty_first::prelude::Sponge;
53
54    use super::*;
55    use crate::empty_stack;
56    use crate::rust_shadowing_helper_functions::list::insert_random_list;
57    use crate::test_prelude::*;
58
59    impl PadAndAbsorbAll {
60        fn init_memory_and_stack(
61            &self,
62            input_length: usize,
63        ) -> (HashMap<BFieldElement, BFieldElement>, Vec<BFieldElement>) {
64            let list_pointer: BFieldElement = rand::random();
65            let init_stack = [empty_stack(), vec![list_pointer]].concat();
66            let mut init_memory = HashMap::default();
67            insert_random_list(&DataType::Bfe, list_pointer, input_length, &mut init_memory);
68
69            (init_memory, init_stack)
70        }
71    }
72
73    impl Procedure for PadAndAbsorbAll {
74        fn rust_shadow(
75            &self,
76            stack: &mut Vec<BFieldElement>,
77            memory: &mut HashMap<BFieldElement, BFieldElement>,
78            _nondeterminism: &NonDeterminism,
79            _public_input: &[BFieldElement],
80            sponge: &mut Option<Tip5>,
81        ) -> Vec<BFieldElement> {
82            let sponge = sponge.as_mut().expect("sponge must be initialized");
83            let input_pointer: BFieldElement = stack.pop().unwrap();
84            let first_word = input_pointer + BFieldElement::new(LIST_METADATA_SIZE as u64);
85            let input_length = memory[&input_pointer].value();
86            let input = (0..input_length)
87                .map(|i| memory[&(BFieldElement::new(i) + first_word)])
88                .collect_vec();
89            sponge.pad_and_absorb_all(&input);
90
91            Vec::default()
92        }
93
94        fn pseudorandom_initial_state(
95            &self,
96            seed: [u8; 32],
97            bench_case: Option<BenchmarkCase>,
98        ) -> ProcedureInitialState {
99            let mut rng = StdRng::from_seed(seed);
100            let mut bytes = [0u8; 400];
101            rng.fill_bytes(&mut bytes);
102            let mut unstructured = Unstructured::new(&bytes);
103            let input_length = match bench_case {
104                Some(BenchmarkCase::CommonCase) => 100,
105                Some(BenchmarkCase::WorstCase) => 10000,
106                None => rng.random_range(0..200),
107            };
108            let (memory, stack) = self.init_memory_and_stack(input_length);
109
110            ProcedureInitialState {
111                stack,
112                nondeterminism: NonDeterminism::default().with_ram(memory),
113                public_input: Vec::default(),
114                sponge: Some(Tip5::arbitrary(&mut unstructured).unwrap()),
115            }
116        }
117
118        fn corner_case_initial_states(&self) -> Vec<ProcedureInitialState> {
119            let empty_input_empty_sponge = {
120                let (memory, stack) = self.init_memory_and_stack(0);
121                ProcedureInitialState {
122                    stack,
123                    nondeterminism: NonDeterminism::default().with_ram(memory),
124                    public_input: Vec::default(),
125                    sponge: Some(Tip5::init()),
126                }
127            };
128            let empty_input_random_sponge = {
129                let (memory, stack) = self.init_memory_and_stack(0);
130                let mut rng = StdRng::from_seed([12u8; 32]);
131                let mut bytes = [0u8; 400];
132                rng.fill_bytes(&mut bytes);
133                let mut unstructured = Unstructured::new(&bytes);
134                ProcedureInitialState {
135                    stack,
136                    nondeterminism: NonDeterminism::default().with_ram(memory),
137                    public_input: Vec::default(),
138                    sponge: Some(Tip5::arbitrary(&mut unstructured).unwrap()),
139                }
140            };
141            let length_one_input_empty_sponge = {
142                let (memory, stack) = self.init_memory_and_stack(1);
143                ProcedureInitialState {
144                    stack,
145                    nondeterminism: NonDeterminism::default().with_ram(memory),
146                    public_input: Vec::default(),
147                    sponge: Some(Tip5::init()),
148                }
149            };
150            vec![
151                empty_input_empty_sponge,
152                empty_input_random_sponge,
153                length_one_input_empty_sponge,
154            ]
155        }
156    }
157
158    #[test]
159    fn pad_and_absorb_all_test() {
160        ShadowedProcedure::new(PadAndAbsorbAll).test();
161    }
162}