fp_evm/
precompile.rs

1// This file is part of Frontier.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
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
18use alloc::vec::Vec;
19pub use evm::{
20	executor::stack::{
21		IsPrecompileResult, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileSet,
22	},
23	Context, ExitError, ExitRevert, ExitSucceed, Transfer,
24};
25
26pub type PrecompileResult = Result<PrecompileOutput, PrecompileFailure>;
27
28/// One single precompile used by EVM engine.
29pub trait Precompile {
30	/// Try to execute the precompile with given `handle` which provides all call data
31	/// and allow to register costs and logs.
32	fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult;
33}
34
35pub trait LinearCostPrecompile {
36	const BASE: u64;
37	const WORD: u64;
38
39	fn execute(
40		input: &[u8],
41		cost: u64,
42	) -> core::result::Result<(ExitSucceed, Vec<u8>), PrecompileFailure>;
43}
44
45impl<T: LinearCostPrecompile> Precompile for T {
46	fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
47		let target_gas = handle.gas_limit();
48		let cost = ensure_linear_cost(target_gas, handle.input().len() as u64, T::BASE, T::WORD)?;
49
50		handle.record_cost(cost)?;
51		let (exit_status, output) = T::execute(handle.input(), cost)?;
52		Ok(PrecompileOutput {
53			exit_status,
54			output,
55		})
56	}
57}
58
59/// Linear gas cost
60fn ensure_linear_cost(
61	target_gas: Option<u64>,
62	len: u64,
63	base: u64,
64	word: u64,
65) -> Result<u64, PrecompileFailure> {
66	let cost = base
67		.checked_add(word.checked_mul(len.saturating_add(31) / 32).ok_or(
68			PrecompileFailure::Error {
69				exit_status: ExitError::OutOfGas,
70			},
71		)?)
72		.ok_or(PrecompileFailure::Error {
73			exit_status: ExitError::OutOfGas,
74		})?;
75
76	if let Some(target_gas) = target_gas {
77		if cost > target_gas {
78			return Err(PrecompileFailure::Error {
79				exit_status: ExitError::OutOfGas,
80			});
81		}
82	}
83
84	Ok(cost)
85}