Skip to content

Commit

Permalink
Extended TransactionStatus erros and added From impls for EVM ExitError
Browse files Browse the repository at this point in the history
  • Loading branch information
mrLSD committed Aug 13, 2024
1 parent 0dd2cb9 commit a993c64
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 5 deletions.
65 changes: 65 additions & 0 deletions engine-types/src/parameters/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,76 @@ pub struct ResultLog {
pub data: Vec<u8>,
}

/// EVM error king of transaction status
#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
#[cfg_attr(feature = "impl-serde", derive(Serialize, Deserialize))]
pub enum TransactionStatusEvmErrorKind {
/// Trying to pop from an empty stack.
StackUnderflow,
/// Trying to push into a stack over stack limit.
StackOverflow,
/// Jump destination is invalid.
InvalidJump,
/// An opcode accesses memory region, but the region is invalid.
InvalidRange,
/// Encountered the designated invalid opcode.
DesignatedInvalid,
/// Call stack is too deep (runtime).
CallTooDeep,
/// Create opcode encountered collision (runtime).
CreateCollision,
/// Create init code exceeds limit (runtime).
CreateContractLimit,
/// Invalid opcode during execution or starting byte is 0xef. See [EIP-3541](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3541.md).
InvalidCode(u8),
/// An opcode accesses external information, but the request is off offset
/// limit (runtime).
OutOfOffset,
/// Execution runs out of gas (runtime).
OutOfGas,
/// Not enough fund to start the execution (runtime).
OutOfFund,
/// PC underflowed (unused).
#[allow(clippy::upper_case_acronyms)]
PCUnderflow,
/// Attempt to create an empty account (runtime, unused).
CreateEmpty,
/// Other normal errors.
Other(crate::Cow<'static, str>),
/// Nonce reached maximum value of 2^64-1
MaxNonce,
}

impl AsRef<[u8]> for TransactionStatusEvmErrorKind {
fn as_ref(&self) -> &[u8] {
match self {
Self::StackUnderflow => b"STACK_UNDERFLOW",
Self::StackOverflow => b"STACK_OVERFLOW",
Self::InvalidJump => b"INVALID_JUMP",
Self::InvalidRange => b"INVALID_RANGE",
Self::DesignatedInvalid => b"DESIGNATED_INVALID",
Self::CallTooDeep => b"CALL_TOO_DEEP",
Self::CreateCollision => b"CREATE_COLLISION",
Self::CreateContractLimit => b"CREATE_CONTRACT_LIMIT",
Self::InvalidCode(_) => b"INVALID_CODE",
Self::OutOfOffset => b"OUT_OF_OFFSET",
Self::OutOfGas => b"OUT_OF_GAS",
Self::OutOfFund => b"OUT_OF_FUND",
Self::PCUnderflow => b"PC_UNDERFLOW",
Self::CreateEmpty => b"CREATE_EMPTY",
Self::MaxNonce => b"MAX_NONCE",
Self::Other(e) => e.as_bytes(),
}
}
}

/// The status of a transaction.
#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
#[cfg_attr(feature = "impl-serde", derive(Serialize, Deserialize))]
pub enum TransactionStatus {
Succeed(Vec<u8>),
Revert(Vec<u8>),
Error(TransactionStatusEvmErrorKind),
OutOfGas,
OutOfFund,
OutOfOffset,
Expand Down Expand Up @@ -214,6 +278,7 @@ impl AsRef<[u8]> for TransactionStatus {
match self {
Self::Succeed(_) => b"SUCCESS",
Self::Revert(_) => errors::ERR_REVERT,
Self::Error(kind) => kind.as_ref(),
Self::OutOfFund => errors::ERR_OUT_OF_FUNDS,
Self::OutOfGas => errors::ERR_OUT_OF_GAS,
Self::OutOfOffset => errors::ERR_OUT_OF_OFFSET,
Expand Down
67 changes: 62 additions & 5 deletions engine/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use aurora_engine_precompiles::PrecompileConstructorContext;
use aurora_engine_types::parameters::connector::{
Erc20Identifier, Erc20Metadata, MirrorErc20TokenArgs,
};
use aurora_engine_types::parameters::engine::FunctionCallArgsV2;
use aurora_engine_types::parameters::engine::{FunctionCallArgsV2, TransactionStatusEvmErrorKind};
use aurora_engine_types::types::EthGas;
use core::cell::RefCell;
use core::iter::once;
Expand Down Expand Up @@ -168,6 +168,33 @@ impl From<ExitFatal> for EngineErrorKind {
}
}

impl From<TransactionStatusEvmErrorKind> for EngineErrorKind {
fn from(kind: TransactionStatusEvmErrorKind) -> Self {
match kind {
TransactionStatusEvmErrorKind::StackUnderflow => ExitError::StackUnderflow.into(),
TransactionStatusEvmErrorKind::StackOverflow => ExitError::StackOverflow.into(),
TransactionStatusEvmErrorKind::InvalidJump => ExitError::InvalidJump.into(),
TransactionStatusEvmErrorKind::InvalidRange => ExitError::InvalidRange.into(),
TransactionStatusEvmErrorKind::DesignatedInvalid => ExitError::DesignatedInvalid.into(),
TransactionStatusEvmErrorKind::CallTooDeep => ExitError::CallTooDeep.into(),
TransactionStatusEvmErrorKind::CreateCollision => ExitError::CreateCollision.into(),
TransactionStatusEvmErrorKind::CreateContractLimit => {
ExitError::CreateContractLimit.into()
}
TransactionStatusEvmErrorKind::InvalidCode(opcode) => {
ExitError::InvalidCode(evm::Opcode(opcode)).into()
}
TransactionStatusEvmErrorKind::OutOfOffset => ExitError::OutOfOffset.into(),
TransactionStatusEvmErrorKind::OutOfGas => ExitError::OutOfGas.into(),
TransactionStatusEvmErrorKind::OutOfFund => ExitError::OutOfFund.into(),
TransactionStatusEvmErrorKind::PCUnderflow => ExitError::PCUnderflow.into(),
TransactionStatusEvmErrorKind::CreateEmpty => ExitError::CreateEmpty.into(),
TransactionStatusEvmErrorKind::MaxNonce => ExitError::MaxNonce.into(),
TransactionStatusEvmErrorKind::Other(msg) => ExitError::Other(msg).into(),
}
}
}

/// An engine result.
pub type EngineResult<T> = Result<T, EngineError>;

Expand All @@ -178,16 +205,42 @@ trait ExitIntoResult {

impl ExitIntoResult for ExitReason {
/// We should be aligned to Ethereum's gas charging:
/// - Success| Revert
/// - ExitError - Execution errors should charge gas from users
/// - ExitFatal - shouldn't charge user gas
/// - `Success` | `Revert`
/// - `ExitError` - Execution errors should charge gas from users
/// - `ExitFatal` - shouldn't charge user gas
/// NOTE: Transactions validation errors should not charge user gas
fn into_result(self, data: Vec<u8>) -> Result<TransactionStatus, EngineErrorKind> {
match self {
Self::Succeed(_) => Ok(TransactionStatus::Succeed(data)),
Self::Revert(_) => Ok(TransactionStatus::Revert(data)),
// To be compatible with Ethereum behaviour we should charge gas for Execution errors
Self::Error(err) => Ok(err.into()),
Self::Error(err) => {
let error_status = match err {
ExitError::StackUnderflow => TransactionStatusEvmErrorKind::StackUnderflow,
ExitError::StackOverflow => TransactionStatusEvmErrorKind::StackOverflow,
ExitError::InvalidJump => TransactionStatusEvmErrorKind::InvalidJump,
ExitError::InvalidRange => TransactionStatusEvmErrorKind::InvalidRange,
ExitError::DesignatedInvalid => {
TransactionStatusEvmErrorKind::DesignatedInvalid
}
ExitError::CallTooDeep => TransactionStatusEvmErrorKind::CallTooDeep,
ExitError::CreateCollision => TransactionStatusEvmErrorKind::CreateCollision,
ExitError::CreateContractLimit => {
TransactionStatusEvmErrorKind::CreateContractLimit
}
ExitError::InvalidCode(opcode) => {
TransactionStatusEvmErrorKind::InvalidCode(opcode.0)
}
ExitError::OutOfOffset => TransactionStatusEvmErrorKind::OutOfOffset,
ExitError::OutOfGas => TransactionStatusEvmErrorKind::OutOfGas,
ExitError::OutOfFund => TransactionStatusEvmErrorKind::OutOfFund,
ExitError::PCUnderflow => TransactionStatusEvmErrorKind::PCUnderflow,
ExitError::CreateEmpty => TransactionStatusEvmErrorKind::CreateEmpty,
ExitError::MaxNonce => TransactionStatusEvmErrorKind::MaxNonce,
ExitError::Other(msg) => TransactionStatusEvmErrorKind::Other(msg),
};
Ok(TransactionStatus::Error(error_status))
}
Self::Fatal(e) => Err(e.into()),
}
}
Expand Down Expand Up @@ -802,6 +855,10 @@ impl<'env, I: IO + Copy, E: Env, M: ModExpAlgorithm> Engine<'env, I, E, M> {
gas_used: submit_result.gas_used,
})
}
TransactionStatus::Error(evm_err) => Err(EngineError {
kind: evm_err.into(),
gas_used: submit_result.gas_used,
}),
TransactionStatus::OutOfFund => Err(EngineError {
kind: EngineErrorKind::EvmError(ExitError::OutOfFund),
gas_used: submit_result.gas_used,
Expand Down

0 comments on commit a993c64

Please sign in to comment.