tasm_lib/hashing/
hash_from_stack.rs

1use triton_vm::prelude::*;
2use twenty_first::prelude::*;
3
4use crate::prelude::*;
5
6#[derive(Debug, Clone, Eq, PartialEq, Hash)]
7pub struct HashFromStack {
8    ty: DataType,
9    ty_len: usize,
10}
11
12impl HashFromStack {
13    /// # Panics
14    ///
15    /// Panics if the argument does not have statically-known length, or if that
16    /// length is larger than or equal to [`Tip5::RATE`].
17    pub fn new(ty: DataType) -> Self {
18        let ty_len = ty
19            .static_length()
20            .expect("data type to hash should have static length");
21        assert!(ty_len < Tip5::RATE, "type length should be small");
22
23        Self { ty, ty_len }
24    }
25}
26
27impl BasicSnippet for HashFromStack {
28    fn inputs(&self) -> Vec<(DataType, String)> {
29        vec![(self.ty.clone(), "preimage".to_string())]
30    }
31    fn outputs(&self) -> Vec<(DataType, String)> {
32        vec![(DataType::Digest, "digest".to_string())]
33    }
34
35    fn entrypoint(&self) -> String {
36        format!(
37            "tasmlib_hashing_hash_from_stack___{}",
38            self.ty.label_friendly_name()
39        )
40    }
41
42    fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
43        let pad_single_zero = triton_asm!(
44            push 0
45            place {self.ty_len}
46        );
47
48        let num_zeros_in_pad = Tip5::RATE - self.ty_len - 1;
49        let pad_zeros = vec![pad_single_zero; num_zeros_in_pad].concat();
50
51        let entrypoint = self.entrypoint();
52        triton_asm!(
53            {entrypoint}:
54
55                {&pad_zeros}
56                // _ [0, …, 0] [preimage]
57
58                push 1
59                place {self.ty_len}
60                // _ [0, …, 0] 1 [preimage]
61                // _ [padded-preimage]
62
63                sponge_init
64                sponge_absorb
65                sponge_squeeze
66
67                pick 9
68                pick 9
69                pick 9
70                pick 9
71                pick 9
72                pop 5
73
74                return
75        )
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use crate::test_prelude::*;
83
84    impl Closure for HashFromStack {
85        type Args = Vec<BFieldElement>;
86
87        fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) {
88            let mut preimage = vec![];
89            for _ in 0..self.ty_len {
90                preimage.push(stack.pop().unwrap());
91            }
92
93            push_encodable(stack, &Tip5::hash_varlen(&preimage));
94        }
95
96        fn pseudorandom_args(&self, seed: [u8; 32], _: Option<BenchmarkCase>) -> Self::Args {
97            self.ty.seeded_random_element(&mut StdRng::from_seed(seed))
98        }
99
100        fn set_up_test_stack(&self, args: Self::Args) -> Vec<BFieldElement> {
101            let mut stack = self.init_stack_for_isolated_run();
102            stack.extend(args.into_iter().rev());
103
104            stack
105        }
106    }
107
108    #[test]
109    fn unit() {
110        let types = [
111            DataType::Bool,
112            DataType::U32,
113            DataType::U64,
114            DataType::U128,
115            DataType::Bfe,
116            DataType::Xfe,
117            DataType::Digest,
118        ];
119        for data_type in types {
120            ShadowedClosure::new(HashFromStack::new(data_type)).test();
121        }
122    }
123}
124
125#[cfg(test)]
126mod benches {
127    use super::*;
128    use crate::test_prelude::*;
129
130    #[test]
131    fn benchmark() {
132        let types = [DataType::Bfe, DataType::Digest];
133        for data_type in types {
134            ShadowedClosure::new(HashFromStack::new(data_type)).bench();
135        }
136    }
137}