Skip to main content

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 parameters(&self) -> Vec<(DataType, String)> {
29        vec![(self.ty.clone(), "preimage".to_string())]
30    }
31    fn return_values(&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>) -> Result<(), RustShadowError> {
88            let mut preimage = vec![];
89            for _ in 0..self.ty_len {
90                preimage.push(stack.pop().ok_or(RustShadowError::StackUnderflow)?);
91            }
92
93            push_encodable(stack, &Tip5::hash_varlen(&preimage));
94            Ok(())
95        }
96
97        fn pseudorandom_args(&self, seed: [u8; 32], _: Option<BenchmarkCase>) -> Self::Args {
98            self.ty.seeded_random_element(&mut StdRng::from_seed(seed))
99        }
100
101        fn set_up_test_stack(&self, args: Self::Args) -> Vec<BFieldElement> {
102            let mut stack = self.init_stack_for_isolated_run();
103            stack.extend(args.into_iter().rev());
104
105            stack
106        }
107    }
108
109    #[macro_rules_attr::apply(test)]
110    fn unit() {
111        let types = [
112            DataType::Bool,
113            DataType::U32,
114            DataType::U64,
115            DataType::U128,
116            DataType::Bfe,
117            DataType::Xfe,
118            DataType::Digest,
119        ];
120        for data_type in types {
121            ShadowedClosure::new(HashFromStack::new(data_type)).test();
122        }
123    }
124}
125
126#[cfg(test)]
127mod benches {
128    use super::*;
129    use crate::test_prelude::*;
130
131    #[macro_rules_attr::apply(test)]
132    fn benchmark() {
133        let types = [DataType::Bfe, DataType::Digest];
134        for data_type in types {
135            ShadowedClosure::new(HashFromStack::new(data_type)).bench();
136        }
137    }
138}