tasm_lib/hashing/sponge_hasher/
absorb.rs

1use triton_vm::prelude::*;
2use twenty_first::tip5::RATE;
3
4use crate::data_type::ArrayType;
5use crate::prelude::*;
6
7#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
8pub struct Absorb;
9
10impl BasicSnippet for Absorb {
11    fn inputs(&self) -> Vec<(DataType, String)> {
12        vec![(
13            DataType::Array(Box::new(ArrayType {
14                element_type: DataType::Bfe,
15                length: RATE,
16            })),
17            "input".to_owned(),
18        )]
19    }
20
21    fn outputs(&self) -> Vec<(DataType, String)> {
22        vec![]
23    }
24
25    fn entrypoint(&self) -> String {
26        "tasmlib_hashing_sponge_hasher_absorb".to_string()
27    }
28
29    fn code(&self, _library: &mut Library) -> Vec<LabelledInstruction> {
30        assert_eq!(10, RATE, "Code assumes RATE is 10");
31
32        triton_asm!(
33            {self.entrypoint()}:
34                // _ *input
35
36                push 0
37                push 0
38                push 0
39                push 0
40                pick 4
41                // _ 0 0 0 0 *input
42
43                sponge_absorb_mem
44                // _ g0 g1 g2 g3 (*input+10)
45
46                pop 5
47                // _
48
49                return
50        )
51
52        // Can also be implemented without the use of `sponge_absorb_mem`:
53        // triton_asm!(
54        //     {entrypoint}:
55        //         // _ *input
56
57        //         push 9 add
58        //         // _ *last_bfe
59
60        //         read_mem 5
61        //         read_mem 5
62        //         // _ [word_9..word_0] (*first_elem - 1)
63
64        //         pop 1
65        //         // _ [word_9..word_0]
66
67        //         sponge_absorb
68        //         // _
69
70        //         return
71        // )
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use arbitrary::Arbitrary;
78    use arbitrary::Unstructured;
79    use twenty_first::math::other::random_elements;
80    use twenty_first::prelude::Sponge;
81
82    use super::*;
83    use crate::empty_stack;
84    use crate::test_prelude::*;
85
86    fn init_memory_and_stack() -> (HashMap<BFieldElement, BFieldElement>, Vec<BFieldElement>) {
87        let array_pointer: BFieldElement = rand::random();
88        let init_stack = [empty_stack(), vec![array_pointer]].concat();
89        let init_memory: HashMap<BFieldElement, BFieldElement> = (0..RATE)
90            .map(|i| array_pointer + BFieldElement::new(i as u64))
91            .zip(random_elements(RATE))
92            .collect();
93
94        (init_memory, init_stack)
95    }
96
97    impl Procedure for Absorb {
98        fn rust_shadow(
99            &self,
100            stack: &mut Vec<BFieldElement>,
101            memory: &mut HashMap<BFieldElement, BFieldElement>,
102            _nondeterminism: &NonDeterminism,
103            _public_input: &[BFieldElement],
104            sponge: &mut Option<Tip5>,
105        ) -> Vec<BFieldElement> {
106            let sponge = sponge.as_mut().expect("sponge must be initialized");
107            let input_pointer: BFieldElement = stack.pop().unwrap();
108            let input: [BFieldElement; 10] = (0..10)
109                .map(|i| memory[&(BFieldElement::new(i) + input_pointer)])
110                .collect_vec()
111                .try_into()
112                .unwrap();
113            sponge.absorb(input);
114            Vec::default()
115        }
116
117        fn pseudorandom_initial_state(
118            &self,
119            seed: [u8; 32],
120            _bench_case: Option<BenchmarkCase>,
121        ) -> ProcedureInitialState {
122            let mut rng = StdRng::from_seed(seed);
123            let mut bytes = [0u8; 400];
124            rng.fill_bytes(&mut bytes);
125            let mut unstructured = Unstructured::new(&bytes);
126            let (memory, stack) = init_memory_and_stack();
127
128            ProcedureInitialState {
129                stack,
130                nondeterminism: NonDeterminism::default().with_ram(memory),
131                public_input: Vec::default(),
132                sponge: Some(Tip5::arbitrary(&mut unstructured).unwrap()),
133            }
134        }
135
136        fn corner_case_initial_states(&self) -> Vec<ProcedureInitialState> {
137            let empty_sponge = {
138                let (memory, stack) = init_memory_and_stack();
139                ProcedureInitialState {
140                    stack,
141                    nondeterminism: NonDeterminism::default().with_ram(memory),
142                    public_input: Vec::default(),
143                    sponge: Some(Tip5::init()),
144                }
145            };
146
147            vec![empty_sponge]
148        }
149    }
150
151    #[test]
152    fn absorb_test() {
153        ShadowedProcedure::new(Absorb).test();
154    }
155}
156
157#[cfg(test)]
158mod benches {
159    use super::*;
160    use crate::test_prelude::*;
161
162    #[test]
163    fn benchmark() {
164        ShadowedProcedure::new(Absorb).bench();
165    }
166}