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, sync::Arc, 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 const fn new(result: R, state: S) -> Self {
41 Self { result, state }
42 }
43}
44
45#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73pub struct ResultGas {
74 #[cfg_attr(feature = "serde", serde(rename = "gas_spent"))]
77 total_gas_spent: u64,
78 #[cfg_attr(feature = "serde", serde(default))]
83 state_gas_spent: u64,
84 #[cfg_attr(feature = "serde", serde(rename = "gas_refunded"))]
89 refunded: u64,
90 floor_gas: u64,
92}
93
94impl ResultGas {
95 #[inline]
99 #[deprecated(
100 since = "32.0.0",
101 note = "It can be a footgun as gas limit is removed, use ResultGas::with_* functions instead"
102 )]
103 pub const fn new(total_gas_spent: u64, refunded: u64, floor_gas: u64) -> Self {
104 Self {
105 total_gas_spent,
106 refunded,
107 floor_gas,
108 state_gas_spent: 0,
109 }
110 }
111
112 #[inline]
114 pub const fn new_with_state_gas(
115 total_gas_spent: u64,
116 refunded: u64,
117 floor_gas: u64,
118 state_gas_spent: u64,
119 ) -> Self {
120 Self {
121 total_gas_spent,
122 refunded,
123 floor_gas,
124 state_gas_spent,
125 }
126 }
127
128 #[inline]
134 pub const fn total_gas_spent(&self) -> u64 {
135 self.total_gas_spent
136 }
137
138 #[inline]
145 pub const fn state_gas_spent_final(&self) -> u64 {
146 self.state_gas_spent
147 }
148
149 #[inline]
151 pub const fn floor_gas(&self) -> u64 {
152 self.floor_gas
153 }
154
155 #[inline]
160 pub const fn inner_refunded(&self) -> u64 {
161 self.refunded
162 }
163
164 #[inline]
166 #[deprecated(
167 since = "32.0.0",
168 note = "After EIP-8037 gas is split on
169 regular and state gas, this method is no longer valid.
170 Use [`ResultGas::total_gas_spent`] instead"
171 )]
172 pub const fn spent(&self) -> u64 {
173 self.total_gas_spent()
174 }
175
176 #[inline]
180 pub const fn set_total_gas_spent(&mut self, total_gas_spent: u64) {
181 self.total_gas_spent = total_gas_spent;
182 }
183
184 #[inline]
186 pub const fn set_refunded(&mut self, refunded: u64) {
187 self.refunded = refunded;
188 }
189
190 #[inline]
192 pub const fn set_floor_gas(&mut self, floor_gas: u64) {
193 self.floor_gas = floor_gas;
194 }
195
196 #[inline]
198 pub const fn set_state_gas_spent(&mut self, state_gas_spent: u64) {
199 self.state_gas_spent = state_gas_spent;
200 }
201
202 #[inline]
204 #[deprecated(
205 since = "32.0.0",
206 note = "After EIP-8037 gas is split on
207 regular and state gas, this method is no longer valid.
208 Use [`ResultGas::set_total_gas_spent`] instead"
209 )]
210 pub const fn set_spent(&mut self, spent: u64) {
211 self.total_gas_spent = spent;
212 }
213
214 #[inline]
218 pub const fn with_total_gas_spent(mut self, total_gas_spent: u64) -> Self {
219 self.total_gas_spent = total_gas_spent;
220 self
221 }
222
223 #[inline]
225 pub const fn with_refunded(mut self, refunded: u64) -> Self {
226 self.refunded = refunded;
227 self
228 }
229
230 #[inline]
232 pub const fn with_floor_gas(mut self, floor_gas: u64) -> Self {
233 self.floor_gas = floor_gas;
234 self
235 }
236
237 #[inline]
239 pub const fn with_state_gas_spent(mut self, state_gas_spent: u64) -> Self {
240 self.state_gas_spent = state_gas_spent;
241 self
242 }
243
244 #[inline]
246 #[deprecated(
247 since = "32.0.0",
248 note = "After EIP-8037 gas is split on
249 regular and state gas, this method is no longer valid.
250 Use [`ResultGas::with_total_gas_spent`] instead"
251 )]
252 pub const fn with_spent(mut self, spent: u64) -> Self {
253 self.total_gas_spent = spent;
254 self
255 }
256
257 #[inline]
263 pub const fn tx_gas_used(&self) -> u64 {
264 let total_gas_spent = self.total_gas_spent();
266 let tx_gas_refunded = total_gas_spent.saturating_sub(self.inner_refunded());
268 max(tx_gas_refunded, self.floor_gas())
269 }
270
271 #[inline]
279 pub const fn block_regular_gas_used(&self) -> u64 {
280 max(
281 self.total_gas_spent()
282 .saturating_sub(self.state_gas_spent_final()),
283 self.floor_gas(),
284 )
285 }
286
287 #[inline]
291 pub const fn block_state_gas_used(&self) -> u64 {
292 self.state_gas_spent_final()
293 }
294
295 #[inline]
300 #[deprecated(
301 since = "32.0.0",
302 note = "Used is not descriptive enough, use [`ResultGas::tx_gas_used`] instead"
303 )]
304 pub const fn used(&self) -> u64 {
305 let spent_sub_refunded = self.spent_sub_refunded();
308 if spent_sub_refunded < self.floor_gas {
309 return self.floor_gas;
310 }
311 spent_sub_refunded
312 }
313
314 #[inline]
319 pub const fn spent_sub_refunded(&self) -> u64 {
320 self.total_gas_spent().saturating_sub(self.refunded)
321 }
322
323 #[inline]
328 pub const fn final_refunded(&self) -> u64 {
329 if self.spent_sub_refunded() < self.floor_gas {
330 0
331 } else {
332 self.refunded
333 }
334 }
335}
336
337#[inline(always)]
339pub const fn max(a: u64, b: u64) -> u64 {
340 if a > b {
341 a
342 } else {
343 b
344 }
345}
346
347#[inline(always)]
349pub const fn min(a: u64, b: u64) -> u64 {
350 if a < b {
351 a
352 } else {
353 b
354 }
355}
356
357impl fmt::Display for ResultGas {
358 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359 write!(
360 f,
361 "Gas used: {}, total spent: {}",
362 self.tx_gas_used(),
363 self.total_gas_spent()
364 )?;
365 if self.refunded > 0 {
366 write!(f, ", refunded: {}", self.refunded)?;
367 }
368 if self.floor_gas > 0 {
369 write!(f, ", floor: {}", self.floor_gas)?;
370 }
371 if self.state_gas_spent > 0 {
372 write!(f, ", state_gas: {}", self.state_gas_spent)?;
373 }
374 Ok(())
375 }
376}
377
378#[derive(Clone, Debug, PartialEq, Eq, Hash)]
380#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
381pub enum ExecutionResult<HaltReasonTy = HaltReason> {
382 Success {
384 reason: SuccessReason,
386 gas: ResultGas,
388 logs: Vec<Log>,
390 output: Output,
392 },
393 Revert {
395 gas: ResultGas,
397 logs: Vec<Log>,
399 output: Bytes,
401 },
402 Halt {
404 reason: HaltReasonTy,
406 gas: ResultGas,
411 logs: Vec<Log>,
413 },
414}
415
416impl<HaltReasonTy> ExecutionResult<HaltReasonTy> {
417 pub const fn is_success(&self) -> bool {
423 matches!(self, Self::Success { .. })
424 }
425
426 pub fn map_haltreason<F, OHR>(self, op: F) -> ExecutionResult<OHR>
428 where
429 F: FnOnce(HaltReasonTy) -> OHR,
430 {
431 match self {
432 Self::Success {
433 reason,
434 gas,
435 logs,
436 output,
437 } => ExecutionResult::Success {
438 reason,
439 gas,
440 logs,
441 output,
442 },
443 Self::Revert { gas, logs, output } => ExecutionResult::Revert { gas, logs, output },
444 Self::Halt { reason, gas, logs } => ExecutionResult::Halt {
445 reason: op(reason),
446 gas,
447 logs,
448 },
449 }
450 }
451
452 pub fn created_address(&self) -> Option<Address> {
455 match self {
456 Self::Success { output, .. } => output.address().cloned(),
457 _ => None,
458 }
459 }
460
461 pub const fn is_halt(&self) -> bool {
463 matches!(self, Self::Halt { .. })
464 }
465
466 pub const fn output(&self) -> Option<&Bytes> {
470 match self {
471 Self::Success { output, .. } => Some(output.data()),
472 Self::Revert { output, .. } => Some(output),
473 _ => None,
474 }
475 }
476
477 pub fn into_output(self) -> Option<Bytes> {
481 match self {
482 Self::Success { output, .. } => Some(output.into_data()),
483 Self::Revert { output, .. } => Some(output),
484 _ => None,
485 }
486 }
487
488 pub const fn logs(&self) -> &[Log] {
490 match self {
491 Self::Success { logs, .. } | Self::Revert { logs, .. } | Self::Halt { logs, .. } => {
492 logs.as_slice()
493 }
494 }
495 }
496
497 pub fn into_logs(self) -> Vec<Log> {
499 match self {
500 Self::Success { logs, .. } | Self::Revert { logs, .. } | Self::Halt { logs, .. } => {
501 logs
502 }
503 }
504 }
505
506 pub const fn gas(&self) -> &ResultGas {
508 match self {
509 Self::Success { gas, .. } | Self::Revert { gas, .. } | Self::Halt { gas, .. } => gas,
510 }
511 }
512
513 pub const fn tx_gas_used(&self) -> u64 {
515 self.gas().tx_gas_used()
516 }
517
518 #[inline]
520 #[deprecated(
521 since = "32.0.0",
522 note = "Use `tx_gas_used()` instead, `gas_used` is ambiguous after EIP-8037 state gas split"
523 )]
524 pub const fn gas_used(&self) -> u64 {
525 self.tx_gas_used()
526 }
527}
528
529impl<HaltReasonTy: fmt::Display> fmt::Display for ExecutionResult<HaltReasonTy> {
530 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
531 match self {
532 Self::Success {
533 reason,
534 gas,
535 logs,
536 output,
537 } => {
538 write!(f, "Success ({reason}): {gas}")?;
539 if !logs.is_empty() {
540 write!(
541 f,
542 ", {} log{}",
543 logs.len(),
544 if logs.len() == 1 { "" } else { "s" }
545 )?;
546 }
547 write!(f, ", {output}")
548 }
549 Self::Revert { gas, logs, output } => {
550 write!(f, "Revert: {gas}")?;
551 if !logs.is_empty() {
552 write!(
553 f,
554 ", {} log{}",
555 logs.len(),
556 if logs.len() == 1 { "" } else { "s" }
557 )?;
558 }
559 if !output.is_empty() {
560 write!(f, ", {} bytes output", output.len())?;
561 }
562 Ok(())
563 }
564 Self::Halt { reason, gas, logs } => {
565 write!(f, "Halted: {reason} ({gas})")?;
566 if !logs.is_empty() {
567 write!(
568 f,
569 ", {} log{}",
570 logs.len(),
571 if logs.len() == 1 { "" } else { "s" }
572 )?;
573 }
574 Ok(())
575 }
576 }
577 }
578}
579
580#[derive(Debug, Clone, PartialEq, Eq, Hash)]
582#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
583pub enum Output {
584 Call(Bytes),
586 Create(Bytes, Option<Address>),
588}
589
590impl Output {
591 pub fn into_data(self) -> Bytes {
593 match self {
594 Output::Call(data) => data,
595 Output::Create(data, _) => data,
596 }
597 }
598
599 pub const fn data(&self) -> &Bytes {
601 match self {
602 Output::Call(data) => data,
603 Output::Create(data, _) => data,
604 }
605 }
606
607 pub const fn address(&self) -> Option<&Address> {
609 match self {
610 Output::Call(_) => None,
611 Output::Create(_, address) => address.as_ref(),
612 }
613 }
614}
615
616impl fmt::Display for Output {
617 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
618 match self {
619 Output::Call(data) => {
620 if data.is_empty() {
621 write!(f, "no output")
622 } else {
623 write!(f, "{} bytes output", data.len())
624 }
625 }
626 Output::Create(data, Some(addr)) => {
627 if data.is_empty() {
628 write!(f, "contract created at {}", addr)
629 } else {
630 write!(f, "contract created at {} ({} bytes)", addr, data.len())
631 }
632 }
633 Output::Create(data, None) => {
634 if data.is_empty() {
635 write!(f, "contract creation (no address)")
636 } else {
637 write!(f, "contract creation (no address, {} bytes)", data.len())
638 }
639 }
640 }
641 }
642}
643
644#[derive(Debug, Clone)]
646pub struct AnyError(Arc<dyn core::error::Error + Send + Sync>);
647impl AnyError {
648 pub fn new(err: impl core::error::Error + Send + Sync + 'static) -> Self {
650 Self(Arc::new(err))
651 }
652}
653
654impl PartialEq for AnyError {
655 fn eq(&self, other: &Self) -> bool {
656 Arc::ptr_eq(&self.0, &other.0)
657 }
658}
659impl Eq for AnyError {}
660impl core::hash::Hash for AnyError {
661 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
662 (Arc::as_ptr(&self.0) as *const ()).hash(state);
663 }
664}
665impl fmt::Display for AnyError {
666 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
667 fmt::Display::fmt(&self.0, f)
668 }
669}
670impl core::error::Error for AnyError {
671 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
672 self.0.source()
673 }
674}
675
676#[cfg(feature = "serde")]
677impl serde::Serialize for AnyError {
678 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
679 serializer.collect_str(self)
680 }
681}
682
683#[derive(Debug)]
684struct StringError(String);
685impl fmt::Display for StringError {
686 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
687 f.write_str(&self.0)
688 }
689}
690impl core::error::Error for StringError {}
691
692impl From<String> for AnyError {
693 fn from(value: String) -> Self {
694 Self::new(StringError(value))
695 }
696}
697impl From<&'static str> for AnyError {
698 fn from(s: &'static str) -> Self {
699 Self::new(StringError(s.into()))
700 }
701}
702
703#[cfg(feature = "serde")]
704impl<'de> serde::Deserialize<'de> for AnyError {
705 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
706 let s = String::deserialize(deserializer)?;
707 Ok(s.into())
708 }
709}
710
711#[derive(Debug, Clone, PartialEq, Eq)]
713#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
714pub enum EVMError<DBError, TransactionError = InvalidTransaction> {
715 Transaction(TransactionError),
717 Header(InvalidHeader),
719 Database(DBError),
721 Custom(String),
726 CustomAny(AnyError),
731}
732
733impl<DBError, TransactionValidationErrorT> From<ContextError<DBError>>
734 for EVMError<DBError, TransactionValidationErrorT>
735{
736 fn from(value: ContextError<DBError>) -> Self {
737 match value {
738 ContextError::Db(e) => Self::Database(e),
739 ContextError::Custom(e) => Self::Custom(e),
740 }
741 }
742}
743
744impl<DBError: DBErrorMarker, TX> From<DBError> for EVMError<DBError, TX> {
745 fn from(value: DBError) -> Self {
746 Self::Database(value)
747 }
748}
749
750pub trait FromStringError {
752 fn from_string(value: String) -> Self;
754}
755
756impl<DB, TX> FromStringError for EVMError<DB, TX> {
757 fn from_string(value: String) -> Self {
758 Self::Custom(value)
759 }
760}
761
762impl<DB, TXE: From<InvalidTransaction>> From<InvalidTransaction> for EVMError<DB, TXE> {
763 fn from(value: InvalidTransaction) -> Self {
764 Self::Transaction(TXE::from(value))
765 }
766}
767
768impl<DBError, TransactionValidationErrorT> EVMError<DBError, TransactionValidationErrorT> {
769 pub fn map_db_err<F, E>(self, op: F) -> EVMError<E, TransactionValidationErrorT>
771 where
772 F: FnOnce(DBError) -> E,
773 {
774 match self {
775 Self::Transaction(e) => EVMError::Transaction(e),
776 Self::Header(e) => EVMError::Header(e),
777 Self::Database(e) => EVMError::Database(op(e)),
778 Self::Custom(e) => EVMError::Custom(e),
779 Self::CustomAny(e) => EVMError::CustomAny(e),
780 }
781 }
782}
783
784impl<DBError, TransactionValidationErrorT> core::error::Error
785 for EVMError<DBError, TransactionValidationErrorT>
786where
787 DBError: core::error::Error + 'static,
788 TransactionValidationErrorT: core::error::Error + 'static,
789{
790 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
791 match self {
792 Self::Transaction(e) => Some(e),
793 Self::Header(e) => Some(e),
794 Self::Database(e) => Some(e),
795 Self::Custom(_) => None,
796 Self::CustomAny(e) => Some(e.0.as_ref()),
797 }
798 }
799}
800
801impl<DBError, TransactionValidationErrorT> fmt::Display
802 for EVMError<DBError, TransactionValidationErrorT>
803where
804 DBError: fmt::Display,
805 TransactionValidationErrorT: fmt::Display,
806{
807 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
808 match self {
809 Self::Transaction(e) => write!(f, "transaction validation error: {e}"),
810 Self::Header(e) => write!(f, "header validation error: {e}"),
811 Self::Database(e) => write!(f, "database error: {e}"),
812 Self::Custom(e) => f.write_str(e),
813 Self::CustomAny(e) => write!(f, "{e}"),
814 }
815 }
816}
817
818impl<DBError, TransactionValidationErrorT> From<InvalidHeader>
819 for EVMError<DBError, TransactionValidationErrorT>
820{
821 fn from(value: InvalidHeader) -> Self {
822 Self::Header(value)
823 }
824}
825
826#[derive(Debug, Clone, PartialEq, Eq, Hash)]
828#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
829pub enum InvalidTransaction {
830 PriorityFeeGreaterThanMaxFee,
836 GasPriceLessThanBasefee,
838 CallerGasLimitMoreThanBlock,
840 CallGasCostMoreThanGasLimit {
846 initial_gas: u64,
848 gas_limit: u64,
850 },
851 GasFloorMoreThanGasLimit {
856 gas_floor: u64,
858 gas_limit: u64,
860 },
861 RejectCallerWithCode,
863 LackOfFundForMaxFee {
865 fee: Box<U256>,
867 balance: Box<U256>,
869 },
870 OverflowPaymentInTransaction,
872 NonceOverflowInTransaction,
874 NonceTooHigh {
876 tx: u64,
878 state: u64,
880 },
881 NonceTooLow {
883 tx: u64,
885 state: u64,
887 },
888 CreateInitCodeSizeLimit,
890 InvalidChainId,
892 MissingChainId,
894 TxGasLimitGreaterThanCap {
896 gas_limit: u64,
898 cap: u64,
900 },
901 AccessListNotSupported,
903 MaxFeePerBlobGasNotSupported,
905 BlobVersionedHashesNotSupported,
907 BlobGasPriceGreaterThanMax {
909 block_blob_gas_price: u128,
911 tx_max_fee_per_blob_gas: u128,
913 },
914 EmptyBlobs,
916 BlobCreateTransaction,
920 TooManyBlobs {
922 max: usize,
924 have: usize,
926 },
927 BlobVersionNotSupported,
929 AuthorizationListNotSupported,
931 AuthorizationListInvalidFields,
933 EmptyAuthorizationList,
935 Eip2930NotSupported,
937 Eip1559NotSupported,
939 Eip4844NotSupported,
941 Eip7702NotSupported,
943 Eip7873NotSupported,
945 Eip7873MissingTarget,
947 Str(Cow<'static, str>),
949}
950
951impl TransactionError for InvalidTransaction {}
952
953impl core::error::Error for InvalidTransaction {}
954
955impl fmt::Display for InvalidTransaction {
956 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
957 match self {
958 Self::PriorityFeeGreaterThanMaxFee => {
959 write!(f, "priority fee is greater than max fee")
960 }
961 Self::GasPriceLessThanBasefee => {
962 write!(f, "gas price is less than basefee")
963 }
964 Self::CallerGasLimitMoreThanBlock => {
965 write!(f, "caller gas limit exceeds the block gas limit")
966 }
967 Self::TxGasLimitGreaterThanCap { gas_limit, cap } => {
968 write!(
969 f,
970 "transaction gas limit ({gas_limit}) is greater than the cap ({cap})"
971 )
972 }
973 Self::CallGasCostMoreThanGasLimit {
974 initial_gas,
975 gas_limit,
976 } => {
977 write!(
978 f,
979 "call gas cost ({initial_gas}) exceeds the gas limit ({gas_limit})"
980 )
981 }
982 Self::GasFloorMoreThanGasLimit {
983 gas_floor,
984 gas_limit,
985 } => {
986 write!(
987 f,
988 "gas floor ({gas_floor}) exceeds the gas limit ({gas_limit})"
989 )
990 }
991 Self::RejectCallerWithCode => {
992 write!(f, "reject transactions from senders with deployed code")
993 }
994 Self::LackOfFundForMaxFee { fee, balance } => {
995 write!(f, "lack of funds ({balance}) for max fee ({fee})")
996 }
997 Self::OverflowPaymentInTransaction => {
998 write!(f, "overflow payment in transaction")
999 }
1000 Self::NonceOverflowInTransaction => {
1001 write!(f, "nonce overflow in transaction")
1002 }
1003 Self::NonceTooHigh { tx, state } => {
1004 write!(f, "nonce {tx} too high, expected {state}")
1005 }
1006 Self::NonceTooLow { tx, state } => {
1007 write!(f, "nonce {tx} too low, expected {state}")
1008 }
1009 Self::CreateInitCodeSizeLimit => {
1010 write!(f, "create initcode size limit")
1011 }
1012 Self::InvalidChainId => write!(f, "invalid chain ID"),
1013 Self::MissingChainId => write!(f, "missing chain ID"),
1014 Self::AccessListNotSupported => write!(f, "access list not supported"),
1015 Self::MaxFeePerBlobGasNotSupported => {
1016 write!(f, "max fee per blob gas not supported")
1017 }
1018 Self::BlobVersionedHashesNotSupported => {
1019 write!(f, "blob versioned hashes not supported")
1020 }
1021 Self::BlobGasPriceGreaterThanMax {
1022 block_blob_gas_price,
1023 tx_max_fee_per_blob_gas,
1024 } => {
1025 write!(
1026 f,
1027 "blob gas price ({block_blob_gas_price}) is greater than max fee per blob gas ({tx_max_fee_per_blob_gas})"
1028 )
1029 }
1030 Self::EmptyBlobs => write!(f, "empty blobs"),
1031 Self::BlobCreateTransaction => write!(f, "blob create transaction"),
1032 Self::TooManyBlobs { max, have } => {
1033 write!(f, "too many blobs, have {have}, max {max}")
1034 }
1035 Self::BlobVersionNotSupported => write!(f, "blob version not supported"),
1036 Self::AuthorizationListNotSupported => write!(f, "authorization list not supported"),
1037 Self::AuthorizationListInvalidFields => {
1038 write!(f, "authorization list tx has invalid fields")
1039 }
1040 Self::EmptyAuthorizationList => write!(f, "empty authorization list"),
1041 Self::Eip2930NotSupported => write!(f, "Eip2930 is not supported"),
1042 Self::Eip1559NotSupported => write!(f, "Eip1559 is not supported"),
1043 Self::Eip4844NotSupported => write!(f, "Eip4844 is not supported"),
1044 Self::Eip7702NotSupported => write!(f, "Eip7702 is not supported"),
1045 Self::Eip7873NotSupported => write!(f, "Eip7873 is not supported"),
1046 Self::Eip7873MissingTarget => {
1047 write!(f, "Eip7873 initcode transaction should have `to` address")
1048 }
1049 Self::Str(msg) => f.write_str(msg),
1050 }
1051 }
1052}
1053
1054#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1056#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1057pub enum InvalidHeader {
1058 PrevrandaoNotSet,
1060 ExcessBlobGasNotSet,
1062}
1063
1064impl core::error::Error for InvalidHeader {}
1065
1066impl fmt::Display for InvalidHeader {
1067 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1068 match self {
1069 Self::PrevrandaoNotSet => write!(f, "`prevrandao` not set"),
1070 Self::ExcessBlobGasNotSet => write!(f, "`excess_blob_gas` not set"),
1071 }
1072 }
1073}
1074
1075#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1077#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1078pub enum SuccessReason {
1079 Stop,
1081 Return,
1083 SelfDestruct,
1085}
1086
1087impl fmt::Display for SuccessReason {
1088 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1089 match self {
1090 Self::Stop => write!(f, "Stop"),
1091 Self::Return => write!(f, "Return"),
1092 Self::SelfDestruct => write!(f, "SelfDestruct"),
1093 }
1094 }
1095}
1096
1097#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1101#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1102pub enum HaltReason {
1103 OutOfGas(OutOfGasError),
1105 OpcodeNotFound,
1107 InvalidFEOpcode,
1109 InvalidJump,
1111 NotActivated,
1113 StackUnderflow,
1115 StackOverflow,
1117 OutOfOffset,
1119 CreateCollision,
1121 PrecompileError,
1123 PrecompileErrorWithContext(String),
1125 NonceOverflow,
1127 CreateContractSizeLimit,
1129 CreateContractStartingWithEF,
1131 CreateInitCodeSizeLimit,
1133
1134 OverflowPayment,
1137 StateChangeDuringStaticCall,
1139 CallNotAllowedInsideStatic,
1141 OutOfFunds,
1143 CallTooDeep,
1145}
1146
1147impl core::error::Error for HaltReason {}
1148
1149impl fmt::Display for HaltReason {
1150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1151 match self {
1152 Self::OutOfGas(err) => write!(f, "{err}"),
1153 Self::OpcodeNotFound => write!(f, "opcode not found"),
1154 Self::InvalidFEOpcode => write!(f, "invalid 0xFE opcode"),
1155 Self::InvalidJump => write!(f, "invalid jump destination"),
1156 Self::NotActivated => write!(f, "feature or opcode not activated"),
1157 Self::StackUnderflow => write!(f, "stack underflow"),
1158 Self::StackOverflow => write!(f, "stack overflow"),
1159 Self::OutOfOffset => write!(f, "out of offset"),
1160 Self::CreateCollision => write!(f, "create collision"),
1161 Self::PrecompileError => write!(f, "precompile error"),
1162 Self::PrecompileErrorWithContext(msg) => write!(f, "precompile error: {msg}"),
1163 Self::NonceOverflow => write!(f, "nonce overflow"),
1164 Self::CreateContractSizeLimit => write!(f, "create contract size limit"),
1165 Self::CreateContractStartingWithEF => {
1166 write!(f, "create contract starting with 0xEF")
1167 }
1168 Self::CreateInitCodeSizeLimit => write!(f, "create initcode size limit"),
1169 Self::OverflowPayment => write!(f, "overflow payment"),
1170 Self::StateChangeDuringStaticCall => write!(f, "state change during static call"),
1171 Self::CallNotAllowedInsideStatic => write!(f, "call not allowed inside static call"),
1172 Self::OutOfFunds => write!(f, "out of funds"),
1173 Self::CallTooDeep => write!(f, "call too deep"),
1174 }
1175 }
1176}
1177
1178#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1180#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1181pub enum OutOfGasError {
1182 Basic,
1184 MemoryLimit,
1186 Memory,
1188 Precompile,
1190 InvalidOperand,
1193 ReentrancySentry,
1195}
1196
1197impl core::error::Error for OutOfGasError {}
1198
1199impl fmt::Display for OutOfGasError {
1200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1201 match self {
1202 Self::Basic => write!(f, "out of gas"),
1203 Self::MemoryLimit => write!(f, "out of gas: memory limit exceeded"),
1204 Self::Memory => write!(f, "out of gas: memory expansion"),
1205 Self::Precompile => write!(f, "out of gas: precompile"),
1206 Self::InvalidOperand => write!(f, "out of gas: invalid operand"),
1207 Self::ReentrancySentry => write!(f, "out of gas: reentrancy sentry"),
1208 }
1209 }
1210}
1211
1212#[derive(Debug, Clone, PartialEq, Eq)]
1214#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1215pub struct TransactionIndexedError<Error> {
1216 pub error: Error,
1218 pub transaction_index: usize,
1220}
1221
1222impl<Error> TransactionIndexedError<Error> {
1223 #[must_use]
1225 pub const fn new(error: Error, transaction_index: usize) -> Self {
1226 Self {
1227 error,
1228 transaction_index,
1229 }
1230 }
1231
1232 pub const fn error(&self) -> &Error {
1234 &self.error
1235 }
1236
1237 #[must_use]
1239 pub fn into_error(self) -> Error {
1240 self.error
1241 }
1242}
1243
1244impl<Error: fmt::Display> fmt::Display for TransactionIndexedError<Error> {
1245 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1246 write!(
1247 f,
1248 "transaction {} failed: {}",
1249 self.transaction_index, self.error
1250 )
1251 }
1252}
1253
1254impl<Error: core::error::Error + 'static> core::error::Error for TransactionIndexedError<Error> {
1255 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
1256 Some(&self.error)
1257 }
1258}
1259
1260impl From<&'static str> for InvalidTransaction {
1261 fn from(s: &'static str) -> Self {
1262 Self::Str(Cow::Borrowed(s))
1263 }
1264}
1265
1266impl From<String> for InvalidTransaction {
1267 fn from(s: String) -> Self {
1268 Self::Str(Cow::Owned(s))
1269 }
1270}
1271
1272#[cfg(test)]
1273mod tests {
1274 use super::*;
1275
1276 #[test]
1277 fn test_execution_result_display() {
1278 let result: ExecutionResult<HaltReason> = ExecutionResult::Success {
1279 reason: SuccessReason::Return,
1280 gas: ResultGas::default()
1281 .with_total_gas_spent(100000)
1282 .with_refunded(26000)
1283 .with_floor_gas(5000),
1284 logs: vec![Log::default(), Log::default()],
1285 output: Output::Call(Bytes::from(vec![1, 2, 3])),
1286 };
1287 assert_eq!(
1288 result.to_string(),
1289 "Success (Return): Gas used: 74000, total spent: 100000, refunded: 26000, floor: 5000, 2 logs, 3 bytes output"
1290 );
1291
1292 let result: ExecutionResult<HaltReason> = ExecutionResult::Revert {
1293 gas: ResultGas::default()
1294 .with_total_gas_spent(100000)
1295 .with_refunded(100000),
1296 logs: vec![],
1297 output: Bytes::from(vec![1, 2, 3, 4]),
1298 };
1299 assert_eq!(
1300 result.to_string(),
1301 "Revert: Gas used: 0, total spent: 100000, refunded: 100000, 4 bytes output"
1302 );
1303
1304 let result: ExecutionResult<HaltReason> = ExecutionResult::Halt {
1305 reason: HaltReason::OutOfGas(OutOfGasError::Basic),
1306 gas: ResultGas::default()
1307 .with_total_gas_spent(1000000)
1308 .with_refunded(1000000),
1309 logs: vec![],
1310 };
1311 assert_eq!(
1312 result.to_string(),
1313 "Halted: out of gas (Gas used: 0, total spent: 1000000, refunded: 1000000)"
1314 );
1315 }
1316
1317 #[test]
1318 fn test_result_gas_display() {
1319 assert_eq!(
1321 ResultGas::default().with_total_gas_spent(21000).to_string(),
1322 "Gas used: 21000, total spent: 21000"
1323 );
1324 assert_eq!(
1326 ResultGas::default()
1327 .with_total_gas_spent(50000)
1328 .with_refunded(10000)
1329 .to_string(),
1330 "Gas used: 40000, total spent: 50000, refunded: 10000"
1331 );
1332 assert_eq!(
1334 ResultGas::default()
1335 .with_total_gas_spent(50000)
1336 .with_refunded(10000)
1337 .with_floor_gas(30000)
1338 .to_string(),
1339 "Gas used: 40000, total spent: 50000, refunded: 10000, floor: 30000"
1340 );
1341 }
1342
1343 #[test]
1344 fn test_result_gas_used_and_remaining() {
1345 let gas = ResultGas::default()
1346 .with_total_gas_spent(100)
1347 .with_refunded(30);
1348 assert_eq!(gas.total_gas_spent(), 100);
1349 assert_eq!(gas.inner_refunded(), 30);
1350 assert_eq!(gas.spent_sub_refunded(), 70);
1351
1352 let gas = ResultGas::default()
1354 .with_total_gas_spent(10)
1355 .with_refunded(50);
1356 assert_eq!(gas.spent_sub_refunded(), 0);
1357 }
1358
1359 #[test]
1360 fn test_final_refunded_with_floor_gas() {
1361 let gas = ResultGas::default()
1363 .with_total_gas_spent(50000)
1364 .with_refunded(10000);
1365 assert_eq!(gas.tx_gas_used(), 40000);
1366 assert_eq!(gas.final_refunded(), 10000);
1367
1368 let gas = ResultGas::default()
1371 .with_total_gas_spent(50000)
1372 .with_refunded(10000)
1373 .with_floor_gas(45000);
1374 assert_eq!(gas.tx_gas_used(), 45000);
1375 assert_eq!(gas.final_refunded(), 0);
1376
1377 let gas = ResultGas::default()
1380 .with_total_gas_spent(50000)
1381 .with_refunded(10000)
1382 .with_floor_gas(30000);
1383 assert_eq!(gas.tx_gas_used(), 40000);
1384 assert_eq!(gas.final_refunded(), 10000);
1385
1386 let gas = ResultGas::default()
1389 .with_total_gas_spent(50000)
1390 .with_refunded(10000)
1391 .with_floor_gas(40000);
1392 assert_eq!(gas.tx_gas_used(), 40000);
1393 assert_eq!(gas.final_refunded(), 10000);
1394 }
1395
1396 #[test]
1397 fn test_block_regular_gas_used_no_floor_no_refund() {
1398 let gas = ResultGas::default().with_total_gas_spent(100_000);
1403 assert_eq!(gas.block_regular_gas_used(), 100_000);
1404
1405 let gas = ResultGas::default()
1407 .with_total_gas_spent(100_000)
1408 .with_state_gas_spent(30_000);
1409 assert_eq!(gas.block_regular_gas_used(), 70_000);
1410 assert_eq!(gas.block_state_gas_used(), 30_000);
1411
1412 let gas = ResultGas::default()
1414 .with_total_gas_spent(100_000)
1415 .with_refunded(10_000)
1416 .with_state_gas_spent(30_000);
1417 assert_eq!(gas.tx_gas_used(), 90_000);
1418 assert_eq!(gas.block_regular_gas_used(), 70_000);
1419
1420 let gas = ResultGas::default()
1422 .with_total_gas_spent(100_000)
1423 .with_refunded(90_000)
1424 .with_floor_gas(50_000)
1425 .with_state_gas_spent(30_000);
1426 assert_eq!(gas.tx_gas_used(), 50_000);
1427 assert_eq!(gas.block_regular_gas_used(), 70_000);
1428
1429 let gas = ResultGas::default()
1431 .with_total_gas_spent(20_000)
1432 .with_state_gas_spent(30_000);
1433 assert_eq!(gas.block_regular_gas_used(), 0);
1434 }
1435}