aurora_engine_precompiles/
blake2.rs

1use aurora_evm::{Context, ExitError};
2
3use crate::prelude::types::EthGas;
4use crate::prelude::{
5    types::{make_address, Address},
6    Borrowed,
7};
8use crate::{EvmPrecompileResult, Precompile, PrecompileOutput};
9use aurora_engine_types::Vec;
10
11/// Blake2 costs.
12mod costs {
13    use crate::prelude::types::EthGas;
14
15    /// Cost per round of Blake2 F.
16    pub(super) const F_ROUND: EthGas = EthGas::new(1);
17}
18
19/// Blake2 constants.
20mod consts {
21    pub(super) const INPUT_LENGTH: usize = 213;
22
23    /// The precomputed SIGMA.
24    ///
25    /// See [RFC 7693](https://datatracker.ietf.org/doc/html/rfc7693#section-2.7) specification for more details.
26    pub(super) const SIGMA: [[usize; 16]; 10] = [
27        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
28        [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
29        [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
30        [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
31        [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
32        [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
33        [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
34        [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
35        [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
36        [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
37    ];
38
39    /// The initialization vector.
40    ///
41    /// See [RFC 7693](https://tools.ietf.org/html/rfc7693#section-2.6) specification for more details.
42    pub(super) const IV: [u64; 8] = [
43        0x6a09e667f3bcc908,
44        0xbb67ae8584caa73b,
45        0x3c6ef372fe94f82b,
46        0xa54ff53a5f1d36f1,
47        0x510e527fade682d1,
48        0x9b05688c2b3e6c1f,
49        0x1f83d9abfb41bd6b,
50        0x5be0cd19137e2179,
51    ];
52
53    // G rotation constants.
54
55    /// G rotation 1.
56    pub(super) const R1: u32 = 32;
57
58    /// G rotation 2.
59    pub(super) const R2: u32 = 24;
60
61    /// G rotation 3.
62    pub(super) const R3: u32 = 16;
63
64    /// G rotation 4.
65    pub(super) const R4: u32 = 63;
66}
67
68/// The G primitive function which mixes two input worlds, "x" and "y", into
69/// four words indexed by "a", "b", "c", and "d" in the working vector v[0..15].
70///
71/// See [RFC 7693](https://datatracker.ietf.org/doc/html/rfc7693#section-3.1) specification for more
72/// details.
73#[allow(clippy::many_single_char_names)]
74fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) {
75    v[a] = v[a].wrapping_add(v[b]).wrapping_add(x);
76    v[d] = (v[d] ^ v[a]).rotate_right(consts::R1);
77    v[c] = v[c].wrapping_add(v[d]);
78    v[b] = (v[b] ^ v[c]).rotate_right(consts::R2);
79    v[a] = v[a].wrapping_add(v[b]).wrapping_add(y);
80    v[d] = (v[d] ^ v[a]).rotate_right(consts::R3);
81    v[c] = v[c].wrapping_add(v[d]);
82    v[b] = (v[b] ^ v[c]).rotate_right(consts::R4);
83}
84
85/// Takes as an argument the state vector `h`, message block vector `m` (the last block is padded
86/// with zeros to full block size, if required), 2w-bit offset counter `t`, and final block
87/// indicator flag `f`. Local vector v[0..15] is used in processing. F returns a new state vector.
88/// The number of rounds, `r`, is 12 for `BLAKE2b` and 10 for BLAKE2s. Rounds are numbered from 0 to
89/// r - 1.
90///
91/// See [RFC 7693](https://datatracker.ietf.org/doc/html/rfc7693#section-3.2) specification for more
92/// details.
93#[allow(clippy::many_single_char_names)]
94fn f(mut h: [u64; 8], m: [u64; 16], t: [u64; 2], f: bool, rounds: u32) -> Vec<u8> {
95    // Initialize the work vector.
96    let mut v = [0u64; 16];
97    v[0..8].copy_from_slice(&h); // First half from state.
98    v[8..16].copy_from_slice(&consts::IV); // Second half from IV.
99
100    v[12] ^= t[0]; // Low word of the offset.
101    v[13] ^= t[1]; // High word.
102
103    if f {
104        // last block flag?
105        v[14] = !v[14]; // Invert all bits.
106    }
107
108    for i in 0..rounds {
109        // Typically twelve rounds for blake2b.
110        // Message word selection permutation for this round.
111        let s = &consts::SIGMA[usize::try_from(i).expect("Round can convert to usize") % 10];
112        g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
113        g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
114        g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
115        g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
116
117        g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
118        g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
119        g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
120        g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
121    }
122
123    for i in 0..8 {
124        // XOR the two halves.
125        h[i] ^= v[i] ^ v[i + 8];
126    }
127
128    let mut result = Vec::with_capacity(64);
129    for value in h {
130        result.extend_from_slice(&value.to_le_bytes());
131    }
132
133    result
134}
135
136pub struct Blake2F;
137
138impl Blake2F {
139    pub const ADDRESS: Address = make_address(0, 9);
140}
141
142impl Precompile for Blake2F {
143    fn required_gas(input: &[u8]) -> Result<EthGas, ExitError> {
144        let (int_bytes, _) = input.split_at(size_of::<u32>());
145        let num_rounds = u32::from_be_bytes(
146            // Unwrap is fine here as it can not fail
147            int_bytes.try_into().unwrap(),
148        );
149        Ok(num_rounds * costs::F_ROUND)
150    }
151
152    /// The compression function of the blake2 algorithm.
153    ///
154    /// Takes as an argument the state vector `h`, message block vector `m` (the last block is padded
155    /// with zeros to full block size, if required), 2w-bit offset counter `t`, and final block
156    /// indicator flag `f`. Local vector v[0..15] is used in processing. F returns a new state vector.
157    /// The number of rounds, `r`, is 12 for `BLAKE2b` and 10 for BLAKE2s. Rounds are numbered from 0 to
158    /// r - 1.
159    ///
160    /// See: `https://eips.ethereum.org/EIPS/eip-152`
161    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000009`
162    fn run(
163        &self,
164        input: &[u8],
165        target_gas: Option<EthGas>,
166        _context: &Context,
167        _is_static: bool,
168    ) -> EvmPrecompileResult {
169        if input.len() != consts::INPUT_LENGTH {
170            return Err(ExitError::Other(Borrowed("ERR_BLAKE2F_INVALID_LEN")));
171        }
172
173        let cost = Self::required_gas(input)?;
174        if let Some(target_gas) = target_gas {
175            if cost > target_gas {
176                return Err(ExitError::OutOfGas);
177            }
178        }
179
180        let mut rounds_bytes = [0u8; 4];
181        rounds_bytes.copy_from_slice(&input[0..4]);
182        let rounds = u32::from_be_bytes(rounds_bytes);
183
184        let mut h = [0u64; 8];
185        for (mut x, value) in h.iter_mut().enumerate() {
186            let mut word: [u8; 8] = [0u8; 8];
187            x = x * 8 + 4;
188            word.copy_from_slice(&input[x..(x + 8)]);
189            *value = u64::from_le_bytes(word);
190        }
191
192        let mut m = [0u64; 16];
193        for (mut x, value) in m.iter_mut().enumerate() {
194            let mut word: [u8; 8] = [0u8; 8];
195            x = x * 8 + 68;
196            word.copy_from_slice(&input[x..(x + 8)]);
197            *value = u64::from_le_bytes(word);
198        }
199
200        let mut t: [u64; 2] = [0u64; 2];
201        for (mut x, value) in t.iter_mut().enumerate() {
202            let mut word: [u8; 8] = [0u8; 8];
203            x = x * 8 + 196;
204            word.copy_from_slice(&input[x..(x + 8)]);
205            *value = u64::from_le_bytes(word);
206        }
207
208        if input[212] != 0 && input[212] != 1 {
209            return Err(ExitError::Other(Borrowed("ERR_BLAKE2F_FINAL_FLAG")));
210        }
211        let finished = input[212] != 0;
212
213        let output = f(h, m, t, finished, rounds);
214        Ok(PrecompileOutput::without_logs(cost, output))
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use super::super::utils::new_context;
221    use crate::prelude::Vec;
222
223    use super::*;
224
225    // [4 bytes for rounds]
226    // [64 bytes for h]
227    // [128 bytes for m]
228    // [8 bytes for t_0]
229    // [8 bytes for t_1]
230    // [1 byte for f]
231    const INPUT: &str = "\
232            0000000c\
233            48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5\
234            d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b\
235            6162630000000000000000000000000000000000000000000000000000000000\
236            0000000000000000000000000000000000000000000000000000000000000000\
237            0000000000000000000000000000000000000000000000000000000000000000\
238            0000000000000000000000000000000000000000000000000000000000000000\
239            0300000000000000\
240            0000000000000000\
241            01";
242
243    fn test_blake2f_out_of_gas() -> EvmPrecompileResult {
244        let input = hex::decode(INPUT).unwrap();
245        Blake2F.run(&input, Some(EthGas::new(11)), &new_context(), false)
246    }
247
248    fn test_blake2f_empty() -> EvmPrecompileResult {
249        let input = [0u8; 0];
250        Blake2F.run(&input, Some(EthGas::new(0)), &new_context(), false)
251    }
252
253    fn test_blake2f_invalid_len_1() -> EvmPrecompileResult {
254        let input = hex::decode(
255            "\
256            00000c\
257            48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5\
258            d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b\
259            6162630000000000000000000000000000000000000000000000000000000000\
260            0000000000000000000000000000000000000000000000000000000000000000\
261            0000000000000000000000000000000000000000000000000000000000000000\
262            0000000000000000000000000000000000000000000000000000000000000000\
263            0300000000000000\
264            0000000000000000\
265            01",
266        )
267        .unwrap();
268        Blake2F.run(&input, Some(EthGas::new(12)), &new_context(), false)
269    }
270
271    fn test_blake2f_invalid_len_2() -> EvmPrecompileResult {
272        let input = hex::decode(
273            "\
274            000000000c\
275            48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5\
276            d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b\
277            6162630000000000000000000000000000000000000000000000000000000000\
278            0000000000000000000000000000000000000000000000000000000000000000\
279            0000000000000000000000000000000000000000000000000000000000000000\
280            0000000000000000000000000000000000000000000000000000000000000000\
281            0300000000000000\
282            0000000000000000\
283            01",
284        )
285        .unwrap();
286        Blake2F.run(&input, Some(EthGas::new(12)), &new_context(), false)
287    }
288
289    fn test_blake2f_invalid_flag() -> EvmPrecompileResult {
290        let input = hex::decode(
291            "\
292            0000000c\
293            48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5\
294            d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b\
295            6162630000000000000000000000000000000000000000000000000000000000\
296            0000000000000000000000000000000000000000000000000000000000000000\
297            0000000000000000000000000000000000000000000000000000000000000000\
298            0000000000000000000000000000000000000000000000000000000000000000\
299            0300000000000000\
300            0000000000000000\
301            02",
302        )
303        .unwrap();
304        Blake2F.run(&input, Some(EthGas::new(12)), &new_context(), false)
305    }
306
307    fn test_blake2f_r_0() -> Vec<u8> {
308        let input = hex::decode(
309            "\
310            00000000\
311            48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5\
312            d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b\
313            6162630000000000000000000000000000000000000000000000000000000000\
314            0000000000000000000000000000000000000000000000000000000000000000\
315            0000000000000000000000000000000000000000000000000000000000000000\
316            0000000000000000000000000000000000000000000000000000000000000000\
317            0300000000000000\
318            0000000000000000\
319            01",
320        )
321        .unwrap();
322        Blake2F
323            .run(&input, Some(EthGas::new(12)), &new_context(), false)
324            .unwrap()
325            .output
326    }
327
328    fn test_blake2f_r_12() -> Vec<u8> {
329        let input = hex::decode(INPUT).unwrap();
330        Blake2F
331            .run(&input, Some(EthGas::new(12)), &new_context(), false)
332            .unwrap()
333            .output
334    }
335
336    fn test_blake2f_final_block_false() -> Vec<u8> {
337        let input = hex::decode(
338            "\
339            0000000c\
340            48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5\
341            d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b\
342            6162630000000000000000000000000000000000000000000000000000000000\
343            0000000000000000000000000000000000000000000000000000000000000000\
344            0000000000000000000000000000000000000000000000000000000000000000\
345            0000000000000000000000000000000000000000000000000000000000000000\
346            0300000000000000\
347            0000000000000000\
348            00",
349        )
350        .unwrap();
351        Blake2F
352            .run(&input, Some(EthGas::new(12)), &new_context(), false)
353            .unwrap()
354            .output
355    }
356
357    #[test]
358    fn test_blake2f() {
359        assert!(matches!(
360            test_blake2f_out_of_gas(),
361            Err(ExitError::OutOfGas)
362        ));
363
364        assert!(matches!(
365            test_blake2f_empty(),
366            Err(ExitError::Other(Borrowed("ERR_BLAKE2F_INVALID_LEN")))
367        ));
368
369        assert!(matches!(
370            test_blake2f_invalid_len_1(),
371            Err(ExitError::Other(Borrowed("ERR_BLAKE2F_INVALID_LEN")))
372        ));
373
374        assert!(matches!(
375            test_blake2f_invalid_len_2(),
376            Err(ExitError::Other(Borrowed("ERR_BLAKE2F_INVALID_LEN")))
377        ));
378
379        assert!(matches!(
380            test_blake2f_invalid_flag(),
381            Err(ExitError::Other(Borrowed("ERR_BLAKE2F_FINAL_FLAG",)))
382        ));
383
384        let expected = hex::decode(
385            "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d\
386            282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b",
387        )
388        .unwrap();
389        assert_eq!(test_blake2f_r_0(), expected);
390
391        let expected = hex::decode(
392            "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1\
393                7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
394        )
395        .unwrap();
396        assert_eq!(test_blake2f_r_12(), expected);
397
398        let expected = hex::decode(
399            "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d28752\
400            98743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735",
401        )
402        .unwrap();
403        assert_eq!(test_blake2f_final_block_false(), expected);
404    }
405}