diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/fees/CoinbaseFeePriceCalculator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/fees/CoinbaseFeePriceCalculator.java index 88b20fde7e..b8ddace20c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/fees/CoinbaseFeePriceCalculator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/fees/CoinbaseFeePriceCalculator.java @@ -22,16 +22,17 @@ import java.util.Optional; @FunctionalInterface public interface CoinbaseFeePriceCalculator { - Wei price(Gas coinbaseFee, Wei transactionGasPrice, Optional baseFee); + long price(Gas coinbaseFee, Wei transactionGasPrice, Optional baseFee); static CoinbaseFeePriceCalculator frontier() { - return (coinbaseFee, transactionGasPrice, baseFee) -> coinbaseFee.priceFor(transactionGasPrice); + return (coinbaseFee, transactionGasPrice, baseFee) -> + coinbaseFee.priceFor(transactionGasPrice).toLong(); } static CoinbaseFeePriceCalculator eip1559() { return (coinbaseFee, transactionGasPrice, baseFee) -> { ExperimentalEIPs.eip1559MustBeEnabled(); - return coinbaseFee.priceFor(Wei.of(transactionGasPrice.toLong() - baseFee.orElseThrow())); + return coinbaseFee.toLong() * (transactionGasPrice.toLong() - baseFee.orElseThrow()); }; } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index 11153c372e..0f5849c10f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -327,10 +327,22 @@ public class MainnetTransactionProcessor implements TransactionProcessor { final MutableAccount coinbase = worldState.getOrCreate(miningBeneficiary).getMutable(); final Gas coinbaseFee = Gas.of(transaction.getGasLimit()).minus(refunded); - final Wei coinbaseWei = + final long coinbaseWeiDelta = coinbaseFeePriceCalculator.price( coinbaseFee, transactionGasPrice, blockHeader.getBaseFee()); - coinbase.incrementBalance(coinbaseWei); + if (coinbaseWeiDelta > 0) { + coinbase.incrementBalance(Wei.of(coinbaseWeiDelta)); + } else if (coinbaseWeiDelta < 0) { + if (coinbaseWeiDelta > coinbase.getBalance().toLong()) { + return Result.failed( + refunded.toLong(), + ValidationResult.invalid( + TransactionValidator.TransactionInvalidReason.INSUFFICIENT_COINBASE_BALANCE, + "insufficient coinbase balance"), + Optional.empty()); + } + coinbase.decrementBalance(Wei.of(Math.abs(coinbaseWeiDelta))); + } initialFrame.getSelfDestructs().forEach(worldState::deleteAccount); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java index 5ad2b5b9fc..30476d9eee 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java @@ -72,6 +72,7 @@ public interface TransactionValidator { CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE, EXCEEDS_PER_TRANSACTION_GAS_LIMIT, INVALID_TRANSACTION_FORMAT, + INSUFFICIENT_COINBASE_BALANCE, // Private Transaction Invalid Reasons PRIVATE_TRANSACTION_FAILED, PRIVATE_NONCE_TOO_LOW, diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/fees/CoinbaseFeePriceCalculatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/fees/CoinbaseFeePriceCalculatorTest.java index 3627e3140d..0c00eed2df 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/fees/CoinbaseFeePriceCalculatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/fees/CoinbaseFeePriceCalculatorTest.java @@ -42,14 +42,14 @@ public class CoinbaseFeePriceCalculatorTest { private final Gas coinbaseFee; private final Wei transactionGasPrice; private final Optional baseFee; - private final Wei expectedPrice; + private final long expectedPrice; public CoinbaseFeePriceCalculatorTest( final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator, final Gas coinbaseFee, final Wei transactionGasPrice, final Optional baseFee, - final Wei expectedPrice) { + final long expectedPrice) { this.coinbaseFeePriceCalculator = coinbaseFeePriceCalculator; this.coinbaseFee = coinbaseFee; this.transactionGasPrice = transactionGasPrice; @@ -62,9 +62,11 @@ public class CoinbaseFeePriceCalculatorTest { return Arrays.asList( new Object[][] { // legacy transaction must return gas price * gas - {FRONTIER_CALCULATOR, Gas.of(100), Wei.of(10L), Optional.empty(), Wei.of(1000L)}, + {FRONTIER_CALCULATOR, Gas.of(100), Wei.of(10L), Optional.empty(), 1000L}, // EIP-1559 must return gas * (gas price - base fee) - {EIP_1559_CALCULATOR, Gas.of(100), Wei.of(10L), Optional.of(4L), Wei.of(600L)} + {EIP_1559_CALCULATOR, Gas.of(100), Wei.of(10L), Optional.of(4L), 600L}, + // Negative transaction gas price case + {EIP_1559_CALCULATOR, Gas.of(100), Wei.of(95L), Optional.of(100L), -500L} }); }