fix(rpc): add fee/value and balance to insufficient funds RPC error (#10872)

This commit is contained in:
Emilia Hane
2024-09-17 11:47:18 +02:00
committed by GitHub
parent e1b10ca699
commit 21e92b83bd
5 changed files with 56 additions and 37 deletions

29
Cargo.lock generated
View File

@@ -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"

View File

@@ -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<revm::primitives::InvalidTransaction> 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<reth_primitives::InvalidTransactionError> 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<InvalidPoolTransactionError> 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 })
}
}
}

View File

@@ -47,16 +47,19 @@ where
DB: Database,
EthApiError: From<<DB as Database>::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.

View File

@@ -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 {

View File

@@ -553,7 +553,10 @@ impl<T: TransactionOrdering> TxPool<T> {
)),
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(),