revm_handler/
instructions.rs

1use auto_impl::auto_impl;
2use interpreter::{
3    gas::params::GasParams,
4    instructions::{instruction_table_gas_changes_spec, InstructionTable},
5    Host, Instruction, InterpreterTypes,
6};
7use primitives::hardfork::SpecId;
8use std::boxed::Box;
9
10/// Stores instructions for EVM.
11#[auto_impl(&mut, Box)]
12pub trait InstructionProvider {
13    /// Context type.
14    type Context;
15    /// Interpreter types.
16    type InterpreterTypes: InterpreterTypes;
17
18    /// Returns the instruction table that is used by EvmTr to execute instructions.
19    fn instruction_table(&self) -> &InstructionTable<Self::InterpreterTypes, Self::Context>;
20
21    /// Returns the gas params that is used by EvmTr to execute instructions.
22    fn gas_params(&self) -> GasParams;
23
24    /// Sets the spec. Return true if the spec was changed.
25    fn set_spec(&mut self, spec: SpecId) -> bool;
26}
27
28/// Ethereum instruction contains list of mainnet instructions that is used for Interpreter execution.
29#[derive(Debug)]
30pub struct EthInstructions<WIRE: InterpreterTypes, HOST: ?Sized> {
31    /// Table containing instruction implementations indexed by opcode.
32    pub instruction_table: Box<InstructionTable<WIRE, HOST>>,
33    /// Gas params that sets gas costs for instructions.
34    pub gas_params: GasParams,
35    /// Spec that is used to set gas costs for instructions.
36    pub spec: SpecId,
37}
38
39impl<WIRE, HOST: Host + ?Sized> Clone for EthInstructions<WIRE, HOST>
40where
41    WIRE: InterpreterTypes,
42{
43    fn clone(&self) -> Self {
44        Self {
45            instruction_table: self.instruction_table.clone(),
46            gas_params: self.gas_params.clone(),
47            spec: self.spec,
48        }
49    }
50}
51
52impl<WIRE, HOST> EthInstructions<WIRE, HOST>
53where
54    WIRE: InterpreterTypes,
55    HOST: Host,
56{
57    /// Returns `EthInstructions` with mainnet spec.
58    pub fn new_mainnet() -> Self {
59        let spec = SpecId::default();
60        Self::new(
61            instruction_table_gas_changes_spec(spec),
62            GasParams::new_spec(spec),
63            spec,
64        )
65    }
66
67    /// Returns a new instance of `EthInstructions` with custom instruction table.
68    #[inline]
69    pub fn new(
70        base_table: InstructionTable<WIRE, HOST>,
71        gas_params: GasParams,
72        spec: SpecId,
73    ) -> Self {
74        Self {
75            instruction_table: Box::new(base_table),
76            gas_params,
77            spec,
78        }
79    }
80
81    /// Inserts a new instruction into the instruction table.
82    #[inline]
83    pub fn insert_instruction(&mut self, opcode: u8, instruction: Instruction<WIRE, HOST>) {
84        self.instruction_table[opcode as usize] = instruction;
85    }
86}
87
88impl<IT, CTX> InstructionProvider for EthInstructions<IT, CTX>
89where
90    IT: InterpreterTypes,
91    CTX: Host,
92{
93    type InterpreterTypes = IT;
94    type Context = CTX;
95
96    fn instruction_table(&self) -> &InstructionTable<Self::InterpreterTypes, Self::Context> {
97        &self.instruction_table
98    }
99
100    fn gas_params(&self) -> GasParams {
101        self.gas_params.clone()
102    }
103
104    fn set_spec(&mut self, spec: SpecId) -> bool {
105        if spec == self.spec {
106            return false;
107        }
108        *self.instruction_table = instruction_table_gas_changes_spec(spec);
109        self.gas_params = GasParams::new_spec(spec);
110
111        true
112    }
113}
114
115impl<WIRE, HOST> Default for EthInstructions<WIRE, HOST>
116where
117    WIRE: InterpreterTypes,
118    HOST: Host,
119{
120    fn default() -> Self {
121        Self::new_mainnet()
122    }
123}