use borsh::{BorshDeserialize, BorshSerialize};
use near_rpc_error_macro::RpcError;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize)]
pub enum VMError {
FunctionCallError(FunctionCallError),
ExternalError(Vec<u8>),
InconsistentStateError(InconsistentStateError),
}
#[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),
}
#[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,
MethodUTF8Error,
MethodNotFound,
MethodInvalidSignature,
}
#[derive(
Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize, RpcError,
)]
pub enum CompilationError {
CodeDoesNotExist { account_id: String },
PrepareError(PrepareError),
WasmerCompileError { 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 },
}
#[derive(Debug, Clone, PartialEq, BorshDeserialize, BorshSerialize, Deserialize, Serialize)]
pub enum VMLogicError {
HostError(HostError),
ExternalError(Vec<u8>),
InconsistentStateError(InconsistentStateError),
}
#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize)]
pub enum InconsistentStateError {
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 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")
}
}
}
}
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)
}
}
}
}
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),
}
}
}
impl std::fmt::Display for InconsistentStateError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
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(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."
);
}
}