aurora_evm/executor/stack/
precompile.rs

1use crate::prelude::*;
2use crate::{Context, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed, Transfer};
3use primitive_types::{H160, H256};
4
5/// A precompile result.
6pub type PrecompileResult = Result<PrecompileOutput, PrecompileFailure>;
7
8/// Data returned by a precompile on success.
9#[derive(Debug, Eq, PartialEq, Clone)]
10pub struct PrecompileOutput {
11    pub exit_status: ExitSucceed,
12    pub output: Vec<u8>,
13}
14
15/// Data returned by a precompile in case of failure.
16#[derive(Debug, Eq, PartialEq, Clone)]
17pub enum PrecompileFailure {
18    /// Reverts the state changes and consume all the gas.
19    Error { exit_status: ExitError },
20    /// Reverts the state changes.
21    /// Returns the provided error message.
22    Revert {
23        exit_status: ExitRevert,
24        output: Vec<u8>,
25    },
26    /// Mark this failure as fatal, and all EVM execution stacks must be exited.
27    Fatal { exit_status: ExitFatal },
28}
29
30impl From<ExitError> for PrecompileFailure {
31    fn from(error: ExitError) -> Self {
32        Self::Error { exit_status: error }
33    }
34}
35
36/// Handle provided to a precompile to interact with the EVM.
37pub trait PrecompileHandle {
38    /// Perform subcall in provided context.
39    /// Precompile specifies in which context the subcall is executed.
40    fn call(
41        &mut self,
42        to: H160,
43        transfer: Option<Transfer>,
44        input: Vec<u8>,
45        gas_limit: Option<u64>,
46        is_static: bool,
47        context: &Context,
48    ) -> (ExitReason, Vec<u8>);
49
50    /// Record cost to the Runtime gasometer.
51    ///
52    /// # Errors
53    /// Return `ExitError`
54    fn record_cost(&mut self, cost: u64) -> Result<(), ExitError>;
55
56    /// Record Substrate specific cost.
57    ///
58    /// # Errors
59    /// Return `ExitError`
60    fn record_external_cost(
61        &mut self,
62        ref_time: Option<u64>,
63        proof_size: Option<u64>,
64        storage_growth: Option<u64>,
65    ) -> Result<(), ExitError>;
66
67    /// Refund Substrate specific cost.
68    fn refund_external_cost(&mut self, ref_time: Option<u64>, proof_size: Option<u64>);
69
70    /// Retreive the remaining gas.
71    fn remaining_gas(&self) -> u64;
72
73    /// Record a log.
74    ///
75    /// # Errors
76    /// Return `ExitError`
77    fn log(&mut self, address: H160, topics: Vec<H256>, data: Vec<u8>) -> Result<(), ExitError>;
78
79    /// Retreive the code address (what is the address of the precompile being called).
80    fn code_address(&self) -> H160;
81
82    /// Retreive the input data the precompile is called with.
83    fn input(&self) -> &[u8];
84
85    /// Retreive the context in which the precompile is executed.
86    fn context(&self) -> &Context;
87
88    /// Is the precompile call is done statically.
89    fn is_static(&self) -> bool;
90
91    /// Retreive the gas limit of this call.
92    fn gas_limit(&self) -> Option<u64>;
93}
94
95/// A set of precompiles.
96///
97/// Checks if the provided address is in the precompile set. This should be
98/// as cheap as possible since it may be called often.
99pub trait PrecompileSet {
100    /// Tries to execute a precompile in the precompile set.
101    /// If the provided address is not a precompile, returns None.
102    fn execute(&self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;
103
104    /// Check if the given address is a precompile. Should only be called to
105    /// perform the check while not executing the precompile afterward, since
106    /// `execute` already performs a check internally.
107    fn is_precompile(&self, address: H160) -> bool;
108}
109
110impl PrecompileSet for () {
111    fn execute(&self, _: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
112        None
113    }
114
115    fn is_precompile(&self, _: H160) -> bool {
116        false
117    }
118}
119
120/// Precompiles function signature. Expected input arguments are:
121///  * Input
122///  * Gas limit
123///  * Context
124///  * Is static
125///
126/// In case of success returns the output and the cost.
127pub type PrecompileFn =
128    fn(&[u8], Option<u64>, &Context, bool) -> Result<(PrecompileOutput, u64), PrecompileFailure>;
129
130impl PrecompileSet for BTreeMap<H160, PrecompileFn> {
131    fn execute(&self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
132        let address = handle.code_address();
133
134        self.get(&address).map(|precompile| {
135            let input = handle.input();
136            let gas_limit = handle.gas_limit();
137            let context = handle.context();
138            let is_static = handle.is_static();
139
140            match (*precompile)(input, gas_limit, context, is_static) {
141                Ok((output, cost)) => {
142                    handle.record_cost(cost)?;
143                    Ok(output)
144                }
145                Err(err) => Err(err),
146            }
147        })
148    }
149
150    /// Check if the given address is a precompile. Should only be called to
151    /// perform the check while not executing the precompile afterward, since
152    /// `execute` already performs a check internally.
153    fn is_precompile(&self, address: H160) -> bool {
154        self.contains_key(&address)
155    }
156}