Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 6 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 17 additions & 8 deletions crates/rpc/rpc-eth-types/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 }
}
Expand Down Expand Up @@ -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 })
}
}
}
Expand Down
21 changes: 12 additions & 9 deletions crates/rpc/rpc-eth-types/src/revm_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 9 additions & 4 deletions crates/transaction-pool/src/error.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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 {
Expand Down
5 changes: 4 additions & 1 deletion crates/transaction-pool/src/pool/txpool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
Loading