diff --git a/Cargo.lock b/Cargo.lock index 5b4ccbd7e37a..d6a4aaaf71db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -820,9 +820,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "aquamarine" @@ -1606,9 +1606,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.18" +version = "1.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" dependencies = [ "jobserver", "libc", @@ -3683,9 +3683,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -4005,9 +4005,9 @@ dependencies = [ [[package]] name = "intrusive-collections" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b694dc9f70c3bda874626d2aed13b780f137aab435f4e9814121955cf706122e" +checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86" dependencies = [ "memoffset", ] @@ -5592,9 +5592,9 @@ dependencies = [ [[package]] name = "pretty_assertions" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", @@ -8966,9 +8966,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b2198350ddee1744cc99812fb39175daf6960af26c1dcfb26ec853c1937d9e" +checksum = "125a280fca309d863831a833e53e0366f2cef215321ba345353cbef421e5a374" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -8978,7 +8978,6 @@ dependencies = [ "boa_engine", "boa_gc", "colorchoice", - "intrusive-collections", "revm", "serde_json", "thiserror", @@ -11471,9 +11470,9 @@ dependencies = [ [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" diff --git a/crates/rpc/rpc-eth-types/src/error.rs b/crates/rpc/rpc-eth-types/src/error.rs index f38cb6ee412c..1ead29d2c327 100644 --- a/crates/rpc/rpc-eth-types/src/error.rs +++ b/crates/rpc/rpc-eth-types/src/error.rs @@ -2,7 +2,7 @@ use std::time::Duration; -use alloy_primitives::{Address, Bytes}; +use alloy_primitives::{Address, Bytes, U256}; use alloy_sol_types::decode_revert_reason; use reth_errors::RethError; use reth_primitives::{revm_primitives::InvalidHeader, BlockId}; @@ -304,9 +304,14 @@ pub enum RpcInvalidTransactionError { /// thrown if creation transaction provides the init code bigger than init code size limit. #[error("max initcode size exceeded")] MaxInitCodeSizeExceeded, - /// Represents the inability to cover max cost + value (account balance too low). - #[error("insufficient funds for gas * price + value")] - InsufficientFunds, + /// Represents the inability to cover max fee + value (account balance too low). + #[error("insufficient funds for gas * price + value: have {balance} want {cost}")] + InsufficientFunds { + /// Transaction cost. + cost: U256, + /// Current balance of transaction sender. + balance: U256, + }, /// Thrown when calculating gas usage #[error("gas uint64 overflow")] GasUintOverflow, @@ -476,7 +481,9 @@ impl From for RpcInvalidTransactionError { InvalidTransaction::CallerGasLimitMoreThanBlock | InvalidTransaction::CallGasCostMoreThanGasLimit => Self::GasTooHigh, InvalidTransaction::RejectCallerWithCode => Self::SenderNoEOA, - InvalidTransaction::LackOfFundForMaxFee { .. } => Self::InsufficientFunds, + InvalidTransaction::LackOfFundForMaxFee { fee, balance } => { + Self::InsufficientFunds { cost: *fee, balance: *balance } + } InvalidTransaction::OverflowPaymentInTransaction => Self::GasUintOverflow, InvalidTransaction::NonceOverflowInTransaction => Self::NonceMaxValue, InvalidTransaction::CreateInitCodeSizeLimit => Self::MaxInitCodeSizeExceeded, @@ -518,7 +525,9 @@ impl From for RpcInvalidTransactionErr // This conversion is used to convert any transaction errors that could occur inside the // txpool (e.g. `eth_sendRawTransaction`) to their corresponding RPC match err { - InvalidTransactionError::InsufficientFunds { .. } => Self::InsufficientFunds, + InvalidTransactionError::InsufficientFunds(res) => { + Self::InsufficientFunds { cost: res.expected, balance: res.got } + } InvalidTransactionError::NonceNotConsistent { tx, state } => { Self::NonceTooLow { tx, state } } @@ -678,8 +687,8 @@ impl From for RpcPoolError { InvalidPoolTransactionError::Other(err) => Self::PoolTransactionError(err), InvalidPoolTransactionError::Eip4844(err) => Self::Eip4844(err), InvalidPoolTransactionError::Eip7702(err) => Self::Eip7702(err), - InvalidPoolTransactionError::Overdraft => { - Self::Invalid(RpcInvalidTransactionError::InsufficientFunds) + InvalidPoolTransactionError::Overdraft { cost, balance } => { + Self::Invalid(RpcInvalidTransactionError::InsufficientFunds { cost, balance }) } } } diff --git a/crates/rpc/rpc-eth-types/src/revm_utils.rs b/crates/rpc/rpc-eth-types/src/revm_utils.rs index bfcc2bf42062..30ea8987cb1b 100644 --- a/crates/rpc/rpc-eth-types/src/revm_utils.rs +++ b/crates/rpc/rpc-eth-types/src/revm_utils.rs @@ -47,16 +47,19 @@ where DB: Database, EthApiError: From<::Error>, { - Ok(db - // Get the caller account. - .basic(env.caller)? - // Get the caller balance. - .map(|acc| acc.balance) - .unwrap_or_default() - // Subtract transferred value from the caller balance. + // Get the caller account. + let caller = db.basic(env.caller)?; + // Get the caller balance. + let balance = caller.map(|acc| acc.balance).unwrap_or_default(); + // Get transaction value. + let value = env.value; + // Subtract transferred value from the caller balance. Return error if the caller has + // insufficient funds. + let balance = balance .checked_sub(env.value) - // Return error if the caller has insufficient funds. - .ok_or_else(|| RpcInvalidTransactionError::InsufficientFunds)? + .ok_or_else(|| RpcInvalidTransactionError::InsufficientFunds { cost: value, balance })?; + + Ok(balance // Calculate the amount of gas the caller can afford with the specified gas price. .checked_div(env.gas_price) // This will be 0 if gas price is 0. It is fine, because we check it before. diff --git a/crates/transaction-pool/src/error.rs b/crates/transaction-pool/src/error.rs index 51989e8ffeea..e5142e18a0d3 100644 --- a/crates/transaction-pool/src/error.rs +++ b/crates/transaction-pool/src/error.rs @@ -1,6 +1,6 @@ //! Transaction pool errors -use alloy_primitives::{Address, TxHash}; +use alloy_primitives::{Address, TxHash, U256}; use reth_primitives::{BlobTransactionValidationError, InvalidTransactionError}; /// Transaction pool result type. @@ -203,8 +203,13 @@ pub enum InvalidPoolTransactionError { #[error("transaction underpriced")] Underpriced, /// Thrown if the transaction's would require an account to be overdrawn - #[error("transaction overdraws from account")] - Overdraft, + #[error("transaction overdraws from account, balance: {balance}, cost: {cost}")] + Overdraft { + /// Cost transaction is allowed to consume. See `reth_transaction_pool::PoolTransaction`. + cost: U256, + /// Balance of account. + balance: U256, + }, /// EIP-4844 related errors #[error(transparent)] Eip4844(#[from] Eip4844PoolTransactionError), @@ -274,7 +279,7 @@ impl InvalidPoolTransactionError { false } Self::IntrinsicGasTooLow => true, - Self::Overdraft => false, + Self::Overdraft { .. } => false, Self::Other(err) => err.is_bad_transaction(), Self::Eip4844(eip4844_err) => { match eip4844_err { diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index a0333b1e4568..62a420b7f8f2 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -553,7 +553,10 @@ impl TxPool { )), InsertErr::Overdraft { transaction } => Err(PoolError::new( *transaction.hash(), - PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft), + PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft { + cost: transaction.cost(), + balance: on_chain_balance, + }), )), InsertErr::TxTypeConflict { transaction } => Err(PoolError::new( *transaction.hash(),