cfx_vm_types/
error.rs

1// Copyright 2015-2018 Parity Technologies (UK) Ltd.
2// This file is part of Parity.
3
4// Parity is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity.  If not, see <http://www.gnu.org/licenses/>.
16
17// Copyright 2019 Conflux Foundation. All rights reserved.
18// Conflux is free software and distributed under GNU General Public License.
19// See http://www.gnu.org/licenses/
20
21//! VM errors module
22
23use super::{action_params::ActionParams, ResumeCall, ResumeCreate};
24use cfx_db_errors::statedb::{Error as DbError, Result as DbResult};
25use cfx_types::{Address, U256};
26use solidity_abi::ABIDecodeError;
27use std::fmt;
28
29#[derive(Debug, Clone)]
30pub enum TrapKind {
31    Call(ActionParams),
32    Create(ActionParams),
33}
34
35pub enum TrapError<Call, Create> {
36    Call(ActionParams, Call),
37    Create(ActionParams, Create),
38}
39
40/// VM errors.
41#[derive(Debug, PartialEq)]
42pub enum Error {
43    /// `OutOfGas` is returned when transaction execution runs out of gas.
44    /// The state should be reverted to the state from before the
45    /// transaction execution. But it does not mean that transaction
46    /// was invalid. Balance still should be transferred and nonce
47    /// should be increased.
48    OutOfGas,
49    /// `BadJumpDestination` is returned when execution tried to move
50    /// to position that wasn't marked with JUMPDEST instruction
51    BadJumpDestination {
52        /// Position the code tried to jump to.
53        destination: usize,
54    },
55    /// `BadInstructions` is returned when given instruction is not supported
56    BadInstruction {
57        /// Unrecognized opcode
58        instruction: u8,
59    },
60    /// `StackUnderflow` when there is not enough stack elements to execute
61    /// instruction
62    StackUnderflow {
63        /// Invoked instruction
64        instruction: &'static str,
65        /// How many stack elements was requested by instruction
66        wanted: usize,
67        /// How many elements were on stack
68        on_stack: usize,
69    },
70    /// When execution would exceed defined Stack Limit
71    OutOfStack {
72        /// Invoked instruction
73        instruction: &'static str,
74        /// How many stack elements instruction wanted to push
75        wanted: usize,
76        /// What was the stack limit
77        limit: usize,
78    },
79    /// `SubStackUnderflow` when there is not enough stack elements to execute
80    /// a subroutine return
81    SubStackUnderflow {
82        /// How many stack elements was requested by instruction
83        wanted: usize,
84        /// How many elements were on stack
85        on_stack: usize,
86    },
87    /// When execution would exceed defined subroutine Stack Limit
88    OutOfSubStack {
89        /// How many stack elements instruction wanted to pop
90        wanted: usize,
91        /// What was the stack limit
92        limit: usize,
93    },
94    InvalidSubEntry,
95    /// When balance is not enough for `collateral_for_storage`.
96    /// The state should be reverted to the state from before the
97    /// transaction execution.
98    NotEnoughBalanceForStorage {
99        required: U256,
100        got: U256,
101    },
102    /// `ExceedStorageLimit` is returned when the `collateral_for_storage`
103    /// exceed the `storage_limit`.
104    ExceedStorageLimit,
105    /// Built-in contract failed on given input
106    BuiltIn(String),
107    /// Internal contract failed
108    InternalContract(String),
109    /// When execution tries to modify the state in static context
110    MutableCallInStaticContext,
111    /// Too large init code size (CIP-645i: EIP-3860)
112    CreateInitCodeSizeLimit,
113    /// Error from storage.
114    StateDbError(PartialEqWrapper<DbError>),
115    /// Wasm runtime error
116    Wasm(String),
117    /// Out of bounds access in RETURNDATACOPY.
118    OutOfBounds,
119    /// Execution has been reverted with REVERT.
120    Reverted,
121    /// Invalid address
122    InvalidAddress(Address),
123    /// Create a contract on an address with existing contract
124    ConflictAddress(Address),
125    /// Create a contract on an address with existing contract
126    NonceOverflow(Address),
127    /// CIP-150: Reject new contract code starting with the 0xEF byte
128    CreateContractStartingWithEF,
129}
130
131#[derive(Debug)]
132pub struct PartialEqWrapper<T: std::fmt::Debug>(pub T);
133
134impl<T: std::fmt::Debug> PartialEq for PartialEqWrapper<T> {
135    fn eq(&self, other: &Self) -> bool {
136        format!("{:?}", self.0) == format!("{:?}", other.0)
137    }
138}
139
140impl From<DbError> for Error {
141    fn from(err: DbError) -> Self { Error::StateDbError(PartialEqWrapper(err)) }
142}
143
144impl From<ABIDecodeError> for Error {
145    fn from(err: ABIDecodeError) -> Self {
146        Error::InternalContract(format!("ABI decode error: {}", err.0))
147    }
148}
149
150impl fmt::Display for Error {
151    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
152        use self::Error::*;
153        match *self {
154            OutOfGas => write!(f, "Out of gas"),
155            BadJumpDestination { destination } => {
156                write!(f, "Bad jump destination {:x}", destination)
157            }
158            BadInstruction { instruction } => {
159                write!(f, "Bad instruction {:x}", instruction)
160            }
161            StackUnderflow {
162                instruction,
163                wanted,
164                on_stack,
165            } => write!(
166                f,
167                "Stack underflow {} {}/{}",
168                instruction, wanted, on_stack
169            ),
170            OutOfStack {
171                instruction,
172                wanted,
173                limit,
174            } => write!(f, "Out of stack {} {}/{}", instruction, wanted, limit),
175            SubStackUnderflow { wanted, on_stack } => {
176                write!(f, "Subroutine stack underflow {}/{}", wanted, on_stack)
177            }
178            InvalidSubEntry => {
179                write!(f, "Invalid Subroutine Entry via BEGINSUB")
180            }
181            OutOfSubStack { wanted, limit } => {
182                write!(f, "Out of subroutine stack {}/{}", wanted, limit)
183            }
184            NotEnoughBalanceForStorage { required, got } => {
185                write!(f, "Not enough balance for storage {}/{}", required, got,)
186            }
187            ExceedStorageLimit => write!(f, "Exceed storage limit"),
188            BuiltIn(ref name) => write!(f, "Built-in failed: {}", name),
189            InternalContract(ref name) => {
190                write!(f, "InternalContract failed: {}", name)
191            }
192            StateDbError(ref msg) => {
193                write!(f, "Irrecoverable state db error: {}", msg.0)
194            }
195            MutableCallInStaticContext => {
196                write!(f, "Mutable call in static context")
197            }
198            CreateInitCodeSizeLimit => {
199                write!(f, "Exceed create initcode size limit")
200            }
201            Wasm(ref msg) => write!(f, "Internal error: {}", msg),
202            OutOfBounds => write!(f, "Out of bounds"),
203            Reverted => write!(f, "Reverted by bytecode"),
204            InvalidAddress(ref addr) => write!(f, "InvalidAddress: {}", addr),
205            ConflictAddress(ref addr) => {
206                write!(f, "Contract creation on an existing address: {}", addr)
207            }
208            NonceOverflow(ref addr) => {
209                write!(f, "Address nonce overflow: {}", addr)
210            }
211            CreateContractStartingWithEF => {
212                write!(f, "Create contract starting with EF")
213            }
214        }
215    }
216}
217
218pub type Result<T> = ::std::result::Result<T, Error>;
219
220/// Separate out database-related errors from other EVM errors.
221/// The EVM itself does not distinguish between errors that originate from
222/// within (e.g., index out-of-bounds) and those that come from external
223/// sources like database operations.
224pub fn separate_out_db_error<T>(result: Result<T>) -> DbResult<Result<T>> {
225    match result {
226        Err(Error::StateDbError(err)) => Err(err.0),
227        x => Ok(x),
228    }
229}
230
231pub enum TrapResult<T, Call, Create> {
232    Return(Result<T>),
233    SubCallCreate(TrapError<Call, Create>),
234}
235
236impl<T, Call, Create> TrapResult<T, Call, Create> {
237    #[cfg(any(test, feature = "testonly_code"))]
238    pub fn ok(self) -> Option<Result<T>> {
239        if let TrapResult::Return(result) = self {
240            Some(result)
241        } else {
242            None
243        }
244    }
245}
246
247pub type ExecTrapResult<T> =
248    TrapResult<T, Box<dyn ResumeCall>, Box<dyn ResumeCreate>>;
249
250pub type ExecTrapError = TrapError<Box<dyn ResumeCall>, Box<dyn ResumeCreate>>;