use std::fmt::{self, Error, Formatter};
use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use near_rpc_error_macro::RpcError;
#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize)]
pub enum VMError {
FunctionCallError(FunctionCallError),
ExternalError(Vec<u8>),
InconsistentStateError(InconsistentStateError),
CacheError(CacheError),
}
#[derive(
Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize, RpcError,
)]
pub enum FunctionCallError {
CompilationError(CompilationError),
LinkError {
msg: String,
},
MethodResolveError(MethodResolveError),
WasmTrap(WasmTrap),
WasmUnknownError,
HostError(HostError),
EvmError(EvmError),
WasmerRuntimeError(String),
Wasmer1Trap(String),
}
#[derive(
Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize, RpcError,
)]
pub enum CacheError {
ReadError,
WriteError,
DeserializationError,
SerializationError { hash: [u8; 32] },
}
#[derive(
Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize, RpcError,
)]
pub enum WasmTrap {
Unreachable,
IncorrectCallIndirectSignature,
MemoryOutOfBounds,
CallIndirectOOB,
IllegalArithmetic,
MisalignedAtomicAccess,
BreakpointTrap,
StackOverflow,
GenericTrap,
}
#[derive(
Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize, RpcError,
)]
pub enum MethodResolveError {
MethodEmptyName,
MethodNotFound,
MethodInvalidSignature,
}
#[derive(
Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize, RpcError,
)]
pub enum CompilationError {
CodeDoesNotExist { account_id: String },
PrepareError(PrepareError),
WasmerCompileError { msg: String },
UnsupportedCompiler { msg: String },
}
#[derive(
Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize, RpcError,
)]
pub enum PrepareError {
Serialization,
Deserialization,
InternalMemoryDeclared,
GasInstrumentation,
StackHeightInstrumentation,
Instantiate,
Memory,
}
#[derive(
Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize, RpcError,
)]
pub enum HostError {
BadUTF16,
BadUTF8,
GasExceeded,
GasLimitExceeded,
BalanceExceeded,
EmptyMethodName,
GuestPanic { panic_msg: String },
IntegerOverflow,
InvalidPromiseIndex { promise_idx: u64 },
CannotAppendActionToJointPromise,
CannotReturnJointPromise,
InvalidPromiseResultIndex { result_idx: u64 },
InvalidRegisterId { register_id: u64 },
IteratorWasInvalidated { iterator_index: u64 },
MemoryAccessViolation,
InvalidReceiptIndex { receipt_index: u64 },
InvalidIteratorIndex { iterator_index: u64 },
InvalidAccountId,
InvalidMethodName,
InvalidPublicKey,
ProhibitedInView { method_name: String },
NumberOfLogsExceeded { limit: u64 },
KeyLengthExceeded { length: u64, limit: u64 },
ValueLengthExceeded { length: u64, limit: u64 },
TotalLogLengthExceeded { length: u64, limit: u64 },
NumberPromisesExceeded { number_of_promises: u64, limit: u64 },
NumberInputDataDependenciesExceeded { number_of_input_data_dependencies: u64, limit: u64 },
ReturnedValueLengthExceeded { length: u64, limit: u64 },
ContractSizeExceeded { size: u64, limit: u64 },
Deprecated { method_name: String },
#[cfg(feature = "protocol_feature_alt_bn128")]
AltBn128DeserializationError { msg: String },
#[cfg(feature = "protocol_feature_alt_bn128")]
AltBn128SerializationError { msg: String },
}
#[derive(Debug, Clone, Eq, PartialEq, BorshDeserialize, BorshSerialize, Deserialize, Serialize)]
pub enum EvmError {
ContractNotFound,
DuplicateContract(#[serde(with = "hex_format")] Vec<u8>),
DeployFail(#[serde(with = "hex_format")] Vec<u8>),
Revert(#[serde(with = "hex_format")] Vec<u8>),
ArgumentParseError,
MissingDeposit,
InsufficientFunds,
IntegerOverflow,
MethodNotFound,
InvalidEcRecoverSignature,
InvalidNonce,
InvalidSubAccount,
FailSelfWithdraw,
InsufficientDeposit,
OutOfGas,
BadJumpDestination {
destination: u64,
},
BadInstruction {
instruction: u8,
},
StackUnderflow {
instruction: String,
wanted: u64,
on_stack: u64,
},
OutOfStack {
instruction: String,
wanted: u64,
limit: u64,
},
BuiltIn(String),
MutableCallInStaticContext,
OutOfBounds,
Reverted,
InvalidMetaTransactionMethodName,
InvalidMetaTransactionFunctionArg,
InvalidChainId,
}
#[derive(Debug, Clone, PartialEq, BorshDeserialize, BorshSerialize, Deserialize, Serialize)]
pub enum VMLogicError {
HostError(HostError),
ExternalError(Vec<u8>),
InconsistentStateError(InconsistentStateError),
EvmError(EvmError),
}
impl std::error::Error for VMLogicError {}
#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize)]
pub enum InconsistentStateError {
StorageError(String),
IntegerOverflow,
}
impl From<HostError> for VMLogicError {
fn from(err: HostError) -> Self {
VMLogicError::HostError(err)
}
}
impl From<InconsistentStateError> for VMLogicError {
fn from(err: InconsistentStateError) -> Self {
VMLogicError::InconsistentStateError(err)
}
}
impl From<PrepareError> for VMError {
fn from(err: PrepareError) -> Self {
VMError::FunctionCallError(FunctionCallError::CompilationError(
CompilationError::PrepareError(err),
))
}
}
impl From<&VMLogicError> for VMError {
fn from(err: &VMLogicError) -> Self {
match err {
VMLogicError::HostError(h) => {
VMError::FunctionCallError(FunctionCallError::HostError(h.clone()))
}
VMLogicError::ExternalError(s) => VMError::ExternalError(s.clone()),
VMLogicError::InconsistentStateError(e) => VMError::InconsistentStateError(e.clone()),
VMLogicError::EvmError(_) => unreachable!("Wasm can't return EVM error"),
}
}
}
impl fmt::Display for VMLogicError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
write!(f, "{:?}", self)
}
}
impl fmt::Display for PrepareError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
use PrepareError::*;
match self {
Serialization => write!(f, "Error happened while serializing the module."),
Deserialization => write!(f, "Error happened while deserializing the module."),
InternalMemoryDeclared => {
write!(f, "Internal memory declaration has been found in the module.")
}
GasInstrumentation => write!(f, "Gas instrumentation failed."),
StackHeightInstrumentation => write!(f, "Stack instrumentation failed."),
Instantiate => write!(f, "Error happened during instantiation."),
Memory => write!(f, "Error creating memory"),
}
}
}
impl fmt::Display for FunctionCallError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
FunctionCallError::CompilationError(e) => e.fmt(f),
FunctionCallError::MethodResolveError(e) => e.fmt(f),
FunctionCallError::HostError(e) => e.fmt(f),
FunctionCallError::LinkError { msg } => write!(f, "{}", msg),
FunctionCallError::WasmTrap(trap) => write!(f, "WebAssembly trap: {}", trap),
FunctionCallError::WasmUnknownError => {
write!(f, "Unknown error during Wasm contract execution")
}
FunctionCallError::EvmError(e) => write!(f, "EVM: {:?}", e),
FunctionCallError::WasmerRuntimeError(e) => write!(f, "Wasmer Runtime: {}", e),
FunctionCallError::Wasmer1Trap(e) => write!(f, "Wasmer 1.0 trap: {}", e),
}
}
}
impl fmt::Display for WasmTrap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
WasmTrap::Unreachable => write!(f, "An `unreachable` opcode was executed."),
WasmTrap::IncorrectCallIndirectSignature => {
write!(f, "Call indirect incorrect signature trap.")
}
WasmTrap::MemoryOutOfBounds => write!(f, "Memory out of bounds trap."),
WasmTrap::CallIndirectOOB => write!(f, "Call indirect out of bounds trap."),
WasmTrap::IllegalArithmetic => {
write!(f, "An arithmetic exception, e.g. divided by zero.")
}
WasmTrap::MisalignedAtomicAccess => write!(f, "Misaligned atomic access trap."),
WasmTrap::GenericTrap => write!(f, "Generic trap."),
WasmTrap::BreakpointTrap => write!(f, "Breakpoint trap."),
WasmTrap::StackOverflow => write!(f, "Stack overflow."),
}
}
}
impl fmt::Display for CompilationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
CompilationError::CodeDoesNotExist { account_id } => {
write!(f, "cannot find contract code for account {}", account_id)
}
CompilationError::PrepareError(p) => write!(f, "PrepareError: {}", p),
CompilationError::WasmerCompileError { msg } => {
write!(f, "Wasmer compilation error: {}", msg)
}
CompilationError::UnsupportedCompiler { msg } => {
write!(f, "Unsupported compiler: {}", msg)
}
}
}
}
impl fmt::Display for MethodResolveError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Debug::fmt(self, f)
}
}
impl fmt::Display for VMError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
VMError::FunctionCallError(err) => fmt::Display::fmt(err, f),
VMError::ExternalError(_err) => write!(f, "Serialized ExternalError"),
VMError::InconsistentStateError(err) => fmt::Display::fmt(err, f),
VMError::CacheError(err) => write!(f, "Cache error: {:?}", err),
}
}
}
impl std::fmt::Display for InconsistentStateError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
InconsistentStateError::StorageError(err) => write!(f, "Storage error: {:?}", err),
InconsistentStateError::IntegerOverflow => write!(
f,
"Math operation with a value from the state resulted in a integer overflow.",
),
}
}
}
impl std::fmt::Display for HostError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
use HostError::*;
match self {
BadUTF8 => write!(f, "String encoding is bad UTF-8 sequence."),
BadUTF16 => write!(f, "String encoding is bad UTF-16 sequence."),
GasExceeded => write!(f, "Exceeded the prepaid gas."),
GasLimitExceeded => write!(f, "Exceeded the maximum amount of gas allowed to burn per contract."),
BalanceExceeded => write!(f, "Exceeded the account balance."),
EmptyMethodName => write!(f, "Tried to call an empty method name."),
GuestPanic { panic_msg } => write!(f, "Smart contract panicked: {}", panic_msg),
IntegerOverflow => write!(f, "Integer overflow."),
InvalidIteratorIndex { iterator_index } => write!(f, "Iterator index {:?} does not exist", iterator_index),
InvalidPromiseIndex { promise_idx } => write!(f, "{:?} does not correspond to existing promises", promise_idx),
CannotAppendActionToJointPromise => write!(f, "Actions can only be appended to non-joint promise."),
CannotReturnJointPromise => write!(f, "Returning joint promise is currently prohibited."),
InvalidPromiseResultIndex { result_idx } => write!(f, "Accessed invalid promise result index: {:?}", result_idx),
InvalidRegisterId { register_id } => write!(f, "Accessed invalid register id: {:?}", register_id),
IteratorWasInvalidated { iterator_index } => write!(f, "Iterator {:?} was invalidated after its creation by performing a mutable operation on trie", iterator_index),
MemoryAccessViolation => write!(f, "Accessed memory outside the bounds."),
InvalidReceiptIndex { receipt_index } => write!(f, "VM Logic returned an invalid receipt index: {:?}", receipt_index),
InvalidAccountId => write!(f, "VM Logic returned an invalid account id"),
InvalidMethodName => write!(f, "VM Logic returned an invalid method name"),
InvalidPublicKey => write!(f, "VM Logic provided an invalid public key"),
ProhibitedInView { method_name } => write!(f, "{} is not allowed in view calls", method_name),
NumberOfLogsExceeded { limit } => write!(f, "The number of logs will exceed the limit {}", limit),
KeyLengthExceeded { length, limit } => write!(f, "The length of a storage key {} exceeds the limit {}", length, limit),
ValueLengthExceeded { length, limit } => write!(f, "The length of a storage value {} exceeds the limit {}", length, limit),
TotalLogLengthExceeded{ length, limit } => write!(f, "The length of a log message {} exceeds the limit {}", length, limit),
NumberPromisesExceeded { number_of_promises, limit } => write!(f, "The number of promises within a FunctionCall {} exceeds the limit {}", number_of_promises, limit),
NumberInputDataDependenciesExceeded { number_of_input_data_dependencies, limit } => write!(f, "The number of input data dependencies {} exceeds the limit {}", number_of_input_data_dependencies, limit),
ReturnedValueLengthExceeded { length, limit } => write!(f, "The length of a returned value {} exceeds the limit {}", length, limit),
ContractSizeExceeded { size, limit } => write!(f, "The size of a contract code in DeployContract action {} exceeds the limit {}", size, limit),
Deprecated {method_name}=> write!(f, "Attempted to call deprecated host function {}", method_name),
#[cfg(feature = "protocol_feature_alt_bn128")]
AltBn128DeserializationError { msg } => write!(f, "AltBn128 deserialization error: {}", msg),
#[cfg(feature = "protocol_feature_alt_bn128")]
AltBn128SerializationError { msg } => write!(f, "AltBn128 serialization error: {}", msg),
}
}
}
pub mod hex_format {
use hex::{decode, encode};
use serde::de;
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: AsRef<[u8]>,
{
serializer.serialize_str(&encode(data))
}
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: From<Vec<u8>>,
{
let s = String::deserialize(deserializer)?;
decode(&s).map_err(|err| de::Error::custom(err.to_string())).map(Into::into)
}
}
#[cfg(test)]
mod tests {
use crate::{CompilationError, FunctionCallError, MethodResolveError, PrepareError, VMError};
#[test]
fn test_display() {
assert_eq!(
VMError::FunctionCallError(FunctionCallError::MethodResolveError(
MethodResolveError::MethodInvalidSignature
))
.to_string(),
"MethodInvalidSignature"
);
assert_eq!(
VMError::FunctionCallError(FunctionCallError::CompilationError(
CompilationError::PrepareError(PrepareError::StackHeightInstrumentation)
))
.to_string(),
"PrepareError: Stack instrumentation failed."
);
}
}