1use crate::{context::ContextError, transaction::TransactionError};
11use core::fmt::{self, Debug};
12use database_interface::DBErrorMarker;
13use primitives::{Address, Bytes, Log, U256};
14use state::EvmState;
15use std::{borrow::Cow, boxed::Box, string::String, vec::Vec};
16
17pub trait HaltReasonTr: Clone + Debug + PartialEq + Eq + From<HaltReason> {}
19
20impl<T> HaltReasonTr for T where T: Clone + Debug + PartialEq + Eq + From<HaltReason> {}
21
22#[derive(Clone, Debug, PartialEq, Eq, Hash)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct ExecResultAndState<R, S = EvmState> {
26 pub result: R,
28 pub state: S,
30}
31
32pub type ResultAndState<H = HaltReason, S = EvmState> = ExecResultAndState<ExecutionResult<H>, S>;
34
35pub type ResultVecAndState<R, S> = ExecResultAndState<Vec<R>, S>;
37
38impl<R, S> ExecResultAndState<R, S> {
39 pub fn new(result: R, state: S) -> Self {
41 Self { result, state }
42 }
43}
44
45#[derive(Clone, Debug, PartialEq, Eq, Hash)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub enum ExecutionResult<HaltReasonTy = HaltReason> {
49 Success {
51 reason: SuccessReason,
53 gas_used: u64,
55 gas_refunded: u64,
57 logs: Vec<Log>,
59 output: Output,
61 },
62 Revert {
64 gas_used: u64,
66 output: Bytes,
68 },
69 Halt {
71 reason: HaltReasonTy,
73 gas_used: u64,
77 },
78}
79
80impl<HaltReasonTy> ExecutionResult<HaltReasonTy> {
81 pub fn is_success(&self) -> bool {
87 matches!(self, Self::Success { .. })
88 }
89
90 pub fn map_haltreason<F, OHR>(self, op: F) -> ExecutionResult<OHR>
92 where
93 F: FnOnce(HaltReasonTy) -> OHR,
94 {
95 match self {
96 Self::Success {
97 reason,
98 gas_used,
99 gas_refunded,
100 logs,
101 output,
102 } => ExecutionResult::Success {
103 reason,
104 gas_used,
105 gas_refunded,
106 logs,
107 output,
108 },
109 Self::Revert { gas_used, output } => ExecutionResult::Revert { gas_used, output },
110 Self::Halt { reason, gas_used } => ExecutionResult::Halt {
111 reason: op(reason),
112 gas_used,
113 },
114 }
115 }
116
117 pub fn created_address(&self) -> Option<Address> {
120 match self {
121 Self::Success { output, .. } => output.address().cloned(),
122 _ => None,
123 }
124 }
125
126 pub fn is_halt(&self) -> bool {
128 matches!(self, Self::Halt { .. })
129 }
130
131 pub fn output(&self) -> Option<&Bytes> {
135 match self {
136 Self::Success { output, .. } => Some(output.data()),
137 Self::Revert { output, .. } => Some(output),
138 _ => None,
139 }
140 }
141
142 pub fn into_output(self) -> Option<Bytes> {
146 match self {
147 Self::Success { output, .. } => Some(output.into_data()),
148 Self::Revert { output, .. } => Some(output),
149 _ => None,
150 }
151 }
152
153 pub fn logs(&self) -> &[Log] {
155 match self {
156 Self::Success { logs, .. } => logs.as_slice(),
157 _ => &[],
158 }
159 }
160
161 pub fn into_logs(self) -> Vec<Log> {
163 match self {
164 Self::Success { logs, .. } => logs,
165 _ => Vec::new(),
166 }
167 }
168
169 pub fn gas_used(&self) -> u64 {
171 match *self {
172 Self::Success { gas_used, .. }
173 | Self::Revert { gas_used, .. }
174 | Self::Halt { gas_used, .. } => gas_used,
175 }
176 }
177}
178
179impl<HaltReasonTy: fmt::Display> fmt::Display for ExecutionResult<HaltReasonTy> {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 match self {
182 Self::Success {
183 reason,
184 gas_used,
185 gas_refunded,
186 logs,
187 output,
188 } => {
189 write!(
190 f,
191 "Success ({}): {} gas used, {} refunded",
192 reason, gas_used, gas_refunded
193 )?;
194 if !logs.is_empty() {
195 write!(
196 f,
197 ", {} log{}",
198 logs.len(),
199 if logs.len() == 1 { "" } else { "s" }
200 )?;
201 }
202 write!(f, ", {}", output)
203 }
204 Self::Revert { gas_used, output } => {
205 write!(f, "Revert: {} gas used", gas_used)?;
206 if !output.is_empty() {
207 write!(f, ", {} bytes output", output.len())?;
208 }
209 Ok(())
210 }
211 Self::Halt { reason, gas_used } => {
212 write!(f, "Halted: {} ({} gas used)", reason, gas_used)
213 }
214 }
215 }
216}
217
218#[derive(Debug, Clone, PartialEq, Eq, Hash)]
220#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
221pub enum Output {
222 Call(Bytes),
224 Create(Bytes, Option<Address>),
226}
227
228impl Output {
229 pub fn into_data(self) -> Bytes {
231 match self {
232 Output::Call(data) => data,
233 Output::Create(data, _) => data,
234 }
235 }
236
237 pub fn data(&self) -> &Bytes {
239 match self {
240 Output::Call(data) => data,
241 Output::Create(data, _) => data,
242 }
243 }
244
245 pub fn address(&self) -> Option<&Address> {
247 match self {
248 Output::Call(_) => None,
249 Output::Create(_, address) => address.as_ref(),
250 }
251 }
252}
253
254impl fmt::Display for Output {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256 match self {
257 Output::Call(data) => {
258 if data.is_empty() {
259 write!(f, "no output")
260 } else {
261 write!(f, "{} bytes output", data.len())
262 }
263 }
264 Output::Create(data, Some(addr)) => {
265 if data.is_empty() {
266 write!(f, "contract created at {}", addr)
267 } else {
268 write!(f, "contract created at {} ({} bytes)", addr, data.len())
269 }
270 }
271 Output::Create(data, None) => {
272 if data.is_empty() {
273 write!(f, "contract creation (no address)")
274 } else {
275 write!(f, "contract creation (no address, {} bytes)", data.len())
276 }
277 }
278 }
279 }
280}
281
282#[derive(Debug, Clone, PartialEq, Eq)]
284#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
285pub enum EVMError<DBError, TransactionError = InvalidTransaction> {
286 Transaction(TransactionError),
288 Header(InvalidHeader),
290 Database(DBError),
292 Custom(String),
296}
297
298impl<DBError, TransactionValidationErrorT> From<ContextError<DBError>>
299 for EVMError<DBError, TransactionValidationErrorT>
300{
301 fn from(value: ContextError<DBError>) -> Self {
302 match value {
303 ContextError::Db(e) => Self::Database(e),
304 ContextError::Custom(e) => Self::Custom(e),
305 }
306 }
307}
308
309impl<DBError: DBErrorMarker, TX> From<DBError> for EVMError<DBError, TX> {
310 fn from(value: DBError) -> Self {
311 Self::Database(value)
312 }
313}
314
315pub trait FromStringError {
317 fn from_string(value: String) -> Self;
319}
320
321impl<DB, TX> FromStringError for EVMError<DB, TX> {
322 fn from_string(value: String) -> Self {
323 Self::Custom(value)
324 }
325}
326
327impl<DB, TXE: From<InvalidTransaction>> From<InvalidTransaction> for EVMError<DB, TXE> {
328 fn from(value: InvalidTransaction) -> Self {
329 Self::Transaction(TXE::from(value))
330 }
331}
332
333impl<DBError, TransactionValidationErrorT> EVMError<DBError, TransactionValidationErrorT> {
334 pub fn map_db_err<F, E>(self, op: F) -> EVMError<E, TransactionValidationErrorT>
336 where
337 F: FnOnce(DBError) -> E,
338 {
339 match self {
340 Self::Transaction(e) => EVMError::Transaction(e),
341 Self::Header(e) => EVMError::Header(e),
342 Self::Database(e) => EVMError::Database(op(e)),
343 Self::Custom(e) => EVMError::Custom(e),
344 }
345 }
346}
347
348impl<DBError, TransactionValidationErrorT> core::error::Error
349 for EVMError<DBError, TransactionValidationErrorT>
350where
351 DBError: core::error::Error + 'static,
352 TransactionValidationErrorT: core::error::Error + 'static,
353{
354 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
355 match self {
356 Self::Transaction(e) => Some(e),
357 Self::Header(e) => Some(e),
358 Self::Database(e) => Some(e),
359 Self::Custom(_) => None,
360 }
361 }
362}
363
364impl<DBError, TransactionValidationErrorT> fmt::Display
365 for EVMError<DBError, TransactionValidationErrorT>
366where
367 DBError: fmt::Display,
368 TransactionValidationErrorT: fmt::Display,
369{
370 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371 match self {
372 Self::Transaction(e) => write!(f, "transaction validation error: {e}"),
373 Self::Header(e) => write!(f, "header validation error: {e}"),
374 Self::Database(e) => write!(f, "database error: {e}"),
375 Self::Custom(e) => f.write_str(e),
376 }
377 }
378}
379
380impl<DBError, TransactionValidationErrorT> From<InvalidHeader>
381 for EVMError<DBError, TransactionValidationErrorT>
382{
383 fn from(value: InvalidHeader) -> Self {
384 Self::Header(value)
385 }
386}
387
388#[derive(Debug, Clone, PartialEq, Eq, Hash)]
390#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
391pub enum InvalidTransaction {
392 PriorityFeeGreaterThanMaxFee,
398 GasPriceLessThanBasefee,
400 CallerGasLimitMoreThanBlock,
402 CallGasCostMoreThanGasLimit {
408 initial_gas: u64,
410 gas_limit: u64,
412 },
413 GasFloorMoreThanGasLimit {
418 gas_floor: u64,
420 gas_limit: u64,
422 },
423 RejectCallerWithCode,
425 LackOfFundForMaxFee {
427 fee: Box<U256>,
429 balance: Box<U256>,
431 },
432 OverflowPaymentInTransaction,
434 NonceOverflowInTransaction,
436 NonceTooHigh {
438 tx: u64,
440 state: u64,
442 },
443 NonceTooLow {
445 tx: u64,
447 state: u64,
449 },
450 CreateInitCodeSizeLimit,
452 InvalidChainId,
454 MissingChainId,
456 TxGasLimitGreaterThanCap {
458 gas_limit: u64,
460 cap: u64,
462 },
463 AccessListNotSupported,
465 MaxFeePerBlobGasNotSupported,
467 BlobVersionedHashesNotSupported,
469 BlobGasPriceGreaterThanMax {
471 block_blob_gas_price: u128,
473 tx_max_fee_per_blob_gas: u128,
475 },
476 EmptyBlobs,
478 BlobCreateTransaction,
482 TooManyBlobs {
484 max: usize,
486 have: usize,
488 },
489 BlobVersionNotSupported,
491 AuthorizationListNotSupported,
493 AuthorizationListInvalidFields,
495 EmptyAuthorizationList,
497 Eip2930NotSupported,
499 Eip1559NotSupported,
501 Eip4844NotSupported,
503 Eip7702NotSupported,
505 Eip7873NotSupported,
507 Eip7873MissingTarget,
509 Str(Cow<'static, str>),
511}
512
513impl TransactionError for InvalidTransaction {}
514
515impl core::error::Error for InvalidTransaction {}
516
517impl fmt::Display for InvalidTransaction {
518 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519 match self {
520 Self::PriorityFeeGreaterThanMaxFee => {
521 write!(f, "priority fee is greater than max fee")
522 }
523 Self::GasPriceLessThanBasefee => {
524 write!(f, "gas price is less than basefee")
525 }
526 Self::CallerGasLimitMoreThanBlock => {
527 write!(f, "caller gas limit exceeds the block gas limit")
528 }
529 Self::TxGasLimitGreaterThanCap { gas_limit, cap } => {
530 write!(
531 f,
532 "transaction gas limit ({gas_limit}) is greater than the cap ({cap})"
533 )
534 }
535 Self::CallGasCostMoreThanGasLimit {
536 initial_gas,
537 gas_limit,
538 } => {
539 write!(
540 f,
541 "call gas cost ({initial_gas}) exceeds the gas limit ({gas_limit})"
542 )
543 }
544 Self::GasFloorMoreThanGasLimit {
545 gas_floor,
546 gas_limit,
547 } => {
548 write!(
549 f,
550 "gas floor ({gas_floor}) exceeds the gas limit ({gas_limit})"
551 )
552 }
553 Self::RejectCallerWithCode => {
554 write!(f, "reject transactions from senders with deployed code")
555 }
556 Self::LackOfFundForMaxFee { fee, balance } => {
557 write!(f, "lack of funds ({balance}) for max fee ({fee})")
558 }
559 Self::OverflowPaymentInTransaction => {
560 write!(f, "overflow payment in transaction")
561 }
562 Self::NonceOverflowInTransaction => {
563 write!(f, "nonce overflow in transaction")
564 }
565 Self::NonceTooHigh { tx, state } => {
566 write!(f, "nonce {tx} too high, expected {state}")
567 }
568 Self::NonceTooLow { tx, state } => {
569 write!(f, "nonce {tx} too low, expected {state}")
570 }
571 Self::CreateInitCodeSizeLimit => {
572 write!(f, "create initcode size limit")
573 }
574 Self::InvalidChainId => write!(f, "invalid chain ID"),
575 Self::MissingChainId => write!(f, "missing chain ID"),
576 Self::AccessListNotSupported => write!(f, "access list not supported"),
577 Self::MaxFeePerBlobGasNotSupported => {
578 write!(f, "max fee per blob gas not supported")
579 }
580 Self::BlobVersionedHashesNotSupported => {
581 write!(f, "blob versioned hashes not supported")
582 }
583 Self::BlobGasPriceGreaterThanMax {
584 block_blob_gas_price,
585 tx_max_fee_per_blob_gas,
586 } => {
587 write!(
588 f,
589 "blob gas price ({block_blob_gas_price}) is greater than max fee per blob gas ({tx_max_fee_per_blob_gas})"
590 )
591 }
592 Self::EmptyBlobs => write!(f, "empty blobs"),
593 Self::BlobCreateTransaction => write!(f, "blob create transaction"),
594 Self::TooManyBlobs { max, have } => {
595 write!(f, "too many blobs, have {have}, max {max}")
596 }
597 Self::BlobVersionNotSupported => write!(f, "blob version not supported"),
598 Self::AuthorizationListNotSupported => write!(f, "authorization list not supported"),
599 Self::AuthorizationListInvalidFields => {
600 write!(f, "authorization list tx has invalid fields")
601 }
602 Self::EmptyAuthorizationList => write!(f, "empty authorization list"),
603 Self::Eip2930NotSupported => write!(f, "Eip2930 is not supported"),
604 Self::Eip1559NotSupported => write!(f, "Eip1559 is not supported"),
605 Self::Eip4844NotSupported => write!(f, "Eip4844 is not supported"),
606 Self::Eip7702NotSupported => write!(f, "Eip7702 is not supported"),
607 Self::Eip7873NotSupported => write!(f, "Eip7873 is not supported"),
608 Self::Eip7873MissingTarget => {
609 write!(f, "Eip7873 initcode transaction should have `to` address")
610 }
611 Self::Str(msg) => f.write_str(msg),
612 }
613 }
614}
615
616#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
618#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
619pub enum InvalidHeader {
620 PrevrandaoNotSet,
622 ExcessBlobGasNotSet,
624}
625
626impl core::error::Error for InvalidHeader {}
627
628impl fmt::Display for InvalidHeader {
629 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
630 match self {
631 Self::PrevrandaoNotSet => write!(f, "`prevrandao` not set"),
632 Self::ExcessBlobGasNotSet => write!(f, "`excess_blob_gas` not set"),
633 }
634 }
635}
636
637#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
639#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
640pub enum SuccessReason {
641 Stop,
643 Return,
645 SelfDestruct,
647}
648
649impl fmt::Display for SuccessReason {
650 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
651 match self {
652 Self::Stop => write!(f, "Stop"),
653 Self::Return => write!(f, "Return"),
654 Self::SelfDestruct => write!(f, "SelfDestruct"),
655 }
656 }
657}
658
659#[derive(Debug, Clone, PartialEq, Eq, Hash)]
663#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
664pub enum HaltReason {
665 OutOfGas(OutOfGasError),
667 OpcodeNotFound,
669 InvalidFEOpcode,
671 InvalidJump,
673 NotActivated,
675 StackUnderflow,
677 StackOverflow,
679 OutOfOffset,
681 CreateCollision,
683 PrecompileError,
685 PrecompileErrorWithContext(String),
687 NonceOverflow,
689 CreateContractSizeLimit,
691 CreateContractStartingWithEF,
693 CreateInitCodeSizeLimit,
695
696 OverflowPayment,
699 StateChangeDuringStaticCall,
701 CallNotAllowedInsideStatic,
703 OutOfFunds,
705 CallTooDeep,
707}
708
709impl core::error::Error for HaltReason {}
710
711impl fmt::Display for HaltReason {
712 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
713 match self {
714 Self::OutOfGas(err) => write!(f, "{err}"),
715 Self::OpcodeNotFound => write!(f, "opcode not found"),
716 Self::InvalidFEOpcode => write!(f, "invalid 0xFE opcode"),
717 Self::InvalidJump => write!(f, "invalid jump destination"),
718 Self::NotActivated => write!(f, "feature or opcode not activated"),
719 Self::StackUnderflow => write!(f, "stack underflow"),
720 Self::StackOverflow => write!(f, "stack overflow"),
721 Self::OutOfOffset => write!(f, "out of offset"),
722 Self::CreateCollision => write!(f, "create collision"),
723 Self::PrecompileError => write!(f, "precompile error"),
724 Self::PrecompileErrorWithContext(msg) => write!(f, "precompile error: {msg}"),
725 Self::NonceOverflow => write!(f, "nonce overflow"),
726 Self::CreateContractSizeLimit => write!(f, "create contract size limit"),
727 Self::CreateContractStartingWithEF => {
728 write!(f, "create contract starting with 0xEF")
729 }
730 Self::CreateInitCodeSizeLimit => write!(f, "create initcode size limit"),
731 Self::OverflowPayment => write!(f, "overflow payment"),
732 Self::StateChangeDuringStaticCall => write!(f, "state change during static call"),
733 Self::CallNotAllowedInsideStatic => write!(f, "call not allowed inside static call"),
734 Self::OutOfFunds => write!(f, "out of funds"),
735 Self::CallTooDeep => write!(f, "call too deep"),
736 }
737 }
738}
739
740#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
742#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
743pub enum OutOfGasError {
744 Basic,
746 MemoryLimit,
748 Memory,
750 Precompile,
752 InvalidOperand,
755 ReentrancySentry,
757}
758
759impl core::error::Error for OutOfGasError {}
760
761impl fmt::Display for OutOfGasError {
762 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
763 match self {
764 Self::Basic => write!(f, "out of gas"),
765 Self::MemoryLimit => write!(f, "out of gas: memory limit exceeded"),
766 Self::Memory => write!(f, "out of gas: memory expansion"),
767 Self::Precompile => write!(f, "out of gas: precompile"),
768 Self::InvalidOperand => write!(f, "out of gas: invalid operand"),
769 Self::ReentrancySentry => write!(f, "out of gas: reentrancy sentry"),
770 }
771 }
772}
773
774#[derive(Debug, Clone, PartialEq, Eq)]
776#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
777pub struct TransactionIndexedError<Error> {
778 pub error: Error,
780 pub transaction_index: usize,
782}
783
784impl<Error> TransactionIndexedError<Error> {
785 #[must_use]
787 pub fn new(error: Error, transaction_index: usize) -> Self {
788 Self {
789 error,
790 transaction_index,
791 }
792 }
793
794 pub fn error(&self) -> &Error {
796 &self.error
797 }
798
799 #[must_use]
801 pub fn into_error(self) -> Error {
802 self.error
803 }
804}
805
806impl<Error: fmt::Display> fmt::Display for TransactionIndexedError<Error> {
807 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
808 write!(
809 f,
810 "transaction {} failed: {}",
811 self.transaction_index, self.error
812 )
813 }
814}
815
816impl<Error: core::error::Error + 'static> core::error::Error for TransactionIndexedError<Error> {
817 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
818 Some(&self.error)
819 }
820}
821
822impl From<&'static str> for InvalidTransaction {
823 fn from(s: &'static str) -> Self {
824 Self::Str(Cow::Borrowed(s))
825 }
826}
827
828impl From<String> for InvalidTransaction {
829 fn from(s: String) -> Self {
830 Self::Str(Cow::Owned(s))
831 }
832}
833
834#[cfg(test)]
835mod tests {
836 use super::*;
837
838 #[test]
839 fn test_execution_result_display() {
840 let result: ExecutionResult<HaltReason> = ExecutionResult::Success {
841 reason: SuccessReason::Return,
842 gas_used: 21000,
843 gas_refunded: 5000,
844 logs: vec![Log::default(), Log::default()],
845 output: Output::Call(Bytes::from(vec![1, 2, 3])),
846 };
847 assert_eq!(
848 result.to_string(),
849 "Success (Return): 21000 gas used, 5000 refunded, 2 logs, 3 bytes output"
850 );
851
852 let result: ExecutionResult<HaltReason> = ExecutionResult::Revert {
853 gas_used: 100000,
854 output: Bytes::from(vec![1, 2, 3, 4]),
855 };
856 assert_eq!(
857 result.to_string(),
858 "Revert: 100000 gas used, 4 bytes output"
859 );
860
861 let result: ExecutionResult<HaltReason> = ExecutionResult::Halt {
862 reason: HaltReason::OutOfGas(OutOfGasError::Basic),
863 gas_used: 1000000,
864 };
865 assert_eq!(result.to_string(), "Halted: out of gas (1000000 gas used)");
866 }
867}