Skip to main content

ovr_evm_precompile_blake2/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// This file is part of Frontier.
3//
4// Copyright (c) 2020 Parity Technologies (UK) Ltd.
5//
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18extern crate alloc;
19
20mod eip_152;
21
22use core::mem::size_of;
23use fp_evm::{
24	Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileOutput,
25	PrecompileResult,
26};
27
28pub struct Blake2F;
29
30impl Blake2F {
31	const GAS_COST_PER_ROUND: u64 = 1; // https://eips.ethereum.org/EIPS/eip-152#gas-costs-and-benchmarks
32}
33
34impl Precompile for Blake2F {
35	/// Format of `input`:
36	/// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f]
37	fn execute(
38		input: &[u8],
39		target_gas: Option<u64>,
40		_context: &Context,
41		_is_static: bool,
42	) -> PrecompileResult {
43		const BLAKE2_F_ARG_LEN: usize = 213;
44
45		if input.len() != BLAKE2_F_ARG_LEN {
46			return Err(PrecompileFailure::Error {
47				exit_status: ExitError::Other(
48					"input length for Blake2 F precompile should be exactly 213 bytes".into(),
49				),
50			});
51		}
52
53		let mut rounds_buf: [u8; 4] = [0; 4];
54		rounds_buf.copy_from_slice(&input[0..4]);
55		let rounds: u32 = u32::from_be_bytes(rounds_buf);
56
57		let gas_cost: u64 = (rounds as u64) * Blake2F::GAS_COST_PER_ROUND;
58		if let Some(gas_left) = target_gas {
59			if gas_left < gas_cost {
60				return Err(PrecompileFailure::Error {
61					exit_status: ExitError::OutOfGas,
62				});
63			}
64		}
65
66		// we use from_le_bytes below to effectively swap byte order to LE if architecture is BE
67
68		let mut h_buf: [u8; 64] = [0; 64];
69		h_buf.copy_from_slice(&input[4..68]);
70		let mut h = [0u64; 8];
71		let mut ctr = 0;
72		for state_word in &mut h {
73			let mut temp: [u8; 8] = Default::default();
74			temp.copy_from_slice(&h_buf[(ctr * 8)..(ctr + 1) * 8]);
75			*state_word = u64::from_le_bytes(temp).into();
76			ctr += 1;
77		}
78
79		let mut m_buf: [u8; 128] = [0; 128];
80		m_buf.copy_from_slice(&input[68..196]);
81		let mut m = [0u64; 16];
82		ctr = 0;
83		for msg_word in &mut m {
84			let mut temp: [u8; 8] = Default::default();
85			temp.copy_from_slice(&m_buf[(ctr * 8)..(ctr + 1) * 8]);
86			*msg_word = u64::from_le_bytes(temp).into();
87			ctr += 1;
88		}
89
90		let mut t_0_buf: [u8; 8] = [0; 8];
91		t_0_buf.copy_from_slice(&input[196..204]);
92		let t_0 = u64::from_le_bytes(t_0_buf);
93
94		let mut t_1_buf: [u8; 8] = [0; 8];
95		t_1_buf.copy_from_slice(&input[204..212]);
96		let t_1 = u64::from_le_bytes(t_1_buf);
97
98		let f = if input[212] == 1 {
99			true
100		} else if input[212] == 0 {
101			false
102		} else {
103			return Err(PrecompileFailure::Error {
104				exit_status: ExitError::Other("incorrect final block indicator flag".into()),
105			});
106		};
107
108		crate::eip_152::compress(&mut h, m, [t_0.into(), t_1.into()], f, rounds as usize);
109
110		let mut output_buf = [0u8; 8 * size_of::<u64>()];
111		for (i, state_word) in h.iter().enumerate() {
112			output_buf[i * 8..(i + 1) * 8].copy_from_slice(&state_word.to_le_bytes());
113		}
114
115		Ok(PrecompileOutput {
116			exit_status: ExitSucceed::Returned,
117			cost: gas_cost,
118			output: output_buf.to_vec(),
119			logs: Default::default(),
120		})
121	}
122}
123
124#[cfg(test)]
125mod tests {
126	use super::*;
127	use pallet_evm_test_vector_support::test_precompile_test_vectors;
128
129	#[test]
130	fn process_consensus_tests() -> Result<(), String> {
131		test_precompile_test_vectors::<Blake2F>("../testdata/blake2F.json")?;
132		Ok(())
133	}
134}