Gas accounting for EIP-4844 (#4992)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/5135/head
Fabio Di Fabio 2 years ago committed by GitHub
parent 28d5702c57
commit 28f2bd4a1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 4
      consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java
  3. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java
  4. 7
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAt.java
  5. 58
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java
  6. 10
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java
  7. 30
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java
  8. 1
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java
  9. 4
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAtTest.java
  10. 9
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java
  11. 38
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java
  12. 107
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockTransactionSelector.java
  13. 43
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java
  14. 9
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java
  15. 9
      ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java
  16. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/GasLimitCalculator.java
  17. 1
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeaderBuilder.java
  18. 49
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java
  19. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockProcessor.java
  20. 17
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java
  21. 31
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/CancunTargetingGasLimitCalculator.java
  22. 11
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java
  23. 75
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
  24. 70
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java
  25. 74
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java
  26. 8
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java
  27. 72
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/CancunFeeMarket.java
  28. 14
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/FeeMarket.java
  29. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/LondonFeeMarket.java
  30. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/ZeroBaseFeeMarket.java
  31. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateGroupRehydrationBlockProcessor.java
  32. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java
  33. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java
  34. 12
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java
  35. 79
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionEIP1559Test.java
  36. 9
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java
  37. 105
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java
  38. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java
  39. 1
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/feemarket/ZeroBaseFeeMarketTest.java
  40. 24
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java
  41. 21
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionReplacementByFeeMarketRule.java
  42. 9
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java
  43. 8
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java
  44. 18
      ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java
  45. 20
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculator.java
  46. 24
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java

@ -30,6 +30,7 @@ tests are updated to use EC private keys instead of RSA keys.
- Support for EIP-4895 - Withdrawals for Shanghai fork
- If a PoS block creation repetition takes less than a configurable duration, then waits before next repetition [#5048](https://github.com/hyperledger/besu/pull/5048)
- Added the option --kzg-trusted-setup to pass a custom setup file for custom networks or to override the default one for named networks [#5084](https://github.com/hyperledger/besu/pull/5084)
- Gas accounting for EIP-4844 [#4992](https://github.com/hyperledger/besu/pull/4992)
### Bug Fixes
- Mitigation fix for stale bonsai code storage leading to log rolling issues on contract recreates [#4906](https://github.com/hyperledger/besu/pull/4906)

@ -26,7 +26,6 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@ -437,7 +436,8 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
.getTransactions()
.isEmpty()) {
// this is called by the first empty block
doThrow(new MerkleTrieException("lock")) // first fail
doCallRealMethod() // first work
.doThrow(new MerkleTrieException("lock")) // second fail
.doCallRealMethod() // then work
.when(blockchain)
.getBlockHeader(any());

@ -67,6 +67,8 @@ public class JsonRpcErrorConverter {
return JsonRpcError.NONCE_TOO_FAR_IN_FUTURE_FOR_SENDER;
case LOWER_NONCE_INVALID_TRANSACTION_EXISTS:
return JsonRpcError.LOWER_NONCE_INVALID_TRANSACTION_EXISTS;
case TOTAL_DATA_GAS_TOO_HIGH:
return JsonRpcError.TOTAL_DATA_GAS_TOO_HIGH;
default:
return JsonRpcError.INVALID_PARAMS;
}

@ -96,7 +96,12 @@ public class DebugStorageRangeAt implements JsonRpcMethod {
.afterTransactionInBlock(
blockHash,
transactionWithMetadata.getTransaction().getHash(),
(transaction, blockHeader, blockchain, worldState, transactionProcessor) ->
(transaction,
blockHeader,
blockchain,
worldState,
transactionProcessor,
protocolSpec) ->
extractStorageAt(
requestContext, accountAddress, startKey, limit, worldState))
.orElseGet(() -> emptyResponse(requestContext))))

@ -14,7 +14,9 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor;
import org.hyperledger.besu.datatypes.DataGas;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
@ -52,8 +54,17 @@ public class BlockReplay {
return performActionWithBlock(
block.getHeader(),
block.getBody(),
(body, header, blockchain, mutableWorldState, transactionProcessor) -> {
List<TransactionTrace> transactionTraces =
(body, header, blockchain, mutableWorldState, transactionProcessor, protocolSpec) -> {
final Wei dataGasPrice =
protocolSpec
.getFeeMarket()
.dataPrice(
blockchain
.getBlockHeader(header.getParentHash())
.flatMap(BlockHeader::getExcessDataGas)
.orElse(DataGas.ZERO));
final List<TransactionTrace> transactionTraces =
body.getTransactions().stream()
.map(
transaction ->
@ -62,7 +73,8 @@ public class BlockReplay {
header,
blockchain,
mutableWorldState,
transactionProcessor))
transactionProcessor,
dataGasPrice))
.collect(Collectors.toList());
return Optional.of(new BlockTrace(transactionTraces));
});
@ -77,24 +89,38 @@ public class BlockReplay {
final Hash blockHash, final Hash transactionHash, final TransactionAction<T> action) {
return performActionWithBlock(
blockHash,
(body, header, blockchain, mutableWorldState, transactionProcessor) -> {
(body, header, blockchain, mutableWorldState, transactionProcessor, protocolSpec) -> {
final BlockHashLookup blockHashLookup = new BlockHashLookup(header, blockchain);
final Wei dataGasPrice =
protocolSpec
.getFeeMarket()
.dataPrice(
blockchain
.getBlockHeader(header.getParentHash())
.flatMap(BlockHeader::getExcessDataGas)
.orElse(DataGas.ZERO));
for (final Transaction transaction : body.getTransactions()) {
if (transaction.getHash().equals(transactionHash)) {
return Optional.of(
action.performAction(
transaction, header, blockchain, mutableWorldState, transactionProcessor));
transaction,
header,
blockchain,
mutableWorldState,
transactionProcessor,
dataGasPrice));
} else {
final ProtocolSpec spec = protocolSchedule.getByBlockHeader(header);
transactionProcessor.processTransaction(
blockchain,
mutableWorldState.updater(),
header,
transaction,
spec.getMiningBeneficiaryCalculator().calculateBeneficiary(header),
protocolSpec.getMiningBeneficiaryCalculator().calculateBeneficiary(header),
blockHashLookup,
false,
TransactionValidationParams.blockReplay());
TransactionValidationParams.blockReplay(),
dataGasPrice);
}
}
return Optional.empty();
@ -106,7 +132,7 @@ public class BlockReplay {
return beforeTransactionInBlock(
blockHash,
transactionHash,
(transaction, blockHeader, blockchain, worldState, transactionProcessor) -> {
(transaction, blockHeader, blockchain, worldState, transactionProcessor, dataGasPrice) -> {
final ProtocolSpec spec = protocolSchedule.getByBlockHeader(blockHeader);
transactionProcessor.processTransaction(
blockchain,
@ -116,9 +142,10 @@ public class BlockReplay {
spec.getMiningBeneficiaryCalculator().calculateBeneficiary(blockHeader),
new BlockHashLookup(blockHeader, blockchain),
false,
TransactionValidationParams.blockReplay());
TransactionValidationParams.blockReplay(),
dataGasPrice);
return action.performAction(
transaction, blockHeader, blockchain, worldState, transactionProcessor);
transaction, blockHeader, blockchain, worldState, transactionProcessor, dataGasPrice);
});
}
@ -160,7 +187,8 @@ public class BlockReplay {
new IllegalArgumentException(
"Missing worldstate for stateroot "
+ previous.getStateRoot().toShortHexString()))) {
return action.perform(body, header, blockchain, worldState, transactionProcessor);
return action.perform(
body, header, blockchain, worldState, transactionProcessor, protocolSpec);
} catch (Exception ex) {
return Optional.empty();
}
@ -190,7 +218,8 @@ public class BlockReplay {
BlockHeader blockHeader,
Blockchain blockchain,
MutableWorldState worldState,
MainnetTransactionProcessor transactionProcessor);
MainnetTransactionProcessor transactionProcessor,
ProtocolSpec protocolSpec);
}
@FunctionalInterface
@ -200,6 +229,7 @@ public class BlockReplay {
BlockHeader blockHeader,
Blockchain blockchain,
MutableWorldState worldState,
MainnetTransactionProcessor transactionProcessor);
MainnetTransactionProcessor transactionProcessor,
Wei dataGasPrice);
}
}

@ -47,7 +47,12 @@ public class BlockTracer {
private BlockReplay.TransactionAction<TransactionTrace> prepareReplayAction(
final DebugOperationTracer tracer) {
return (transaction, header, blockchain, mutableWorldState, transactionProcessor) -> {
return (transaction,
header,
blockchain,
mutableWorldState,
transactionProcessor,
dataGasPrice) -> {
// if we have no prior updater, it must be the first TX, so use the block's initial state
if (chainedUpdater == null) {
chainedUpdater = mutableWorldState.updater();
@ -65,7 +70,8 @@ public class BlockTracer {
header.getCoinbase(),
tracer,
new BlockHashLookup(header, blockchain),
false);
false,
dataGasPrice);
final List<TraceFrame> traceFrames = tracer.copyTraceFrames();
tracer.reset();
return new TransactionTrace(transaction, result, traceFrames);

@ -16,7 +16,9 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor;
import static java.util.function.Predicate.isEqual;
import org.hyperledger.besu.datatypes.DataGas;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TransactionTraceParams;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -65,7 +67,7 @@ public class TransactionTracer {
return blockReplay.beforeTransactionInBlock(
blockHash,
transactionHash,
(transaction, header, blockchain, worldState, transactionProcessor) -> {
(transaction, header, blockchain, worldState, transactionProcessor, dataGasPrice) -> {
final TransactionProcessingResult result =
processTransaction(
header,
@ -73,7 +75,8 @@ public class TransactionTracer {
worldState.updater(),
transaction,
transactionProcessor,
tracer);
tracer,
dataGasPrice);
return new TransactionTrace(transaction, result, tracer.getTraceFrames());
});
}
@ -101,9 +104,17 @@ public class TransactionTracer {
return blockReplay
.performActionWithBlock(
blockHash,
(body, header, blockchain, worldState, transactionProcessor) -> {
(body, header, blockchain, worldState, transactionProcessor, protocolSpec) -> {
WorldUpdater stackedUpdater = worldState.updater().updater();
final List<String> traces = new ArrayList<>();
final Wei dataGasPrice =
protocolSpec
.getFeeMarket()
.dataPrice(
blockchain
.getBlockHeader(header.getParentHash())
.flatMap(BlockHeader::getExcessDataGas)
.orElse(DataGas.ZERO));
for (int i = 0; i < body.getTransactions().size(); i++) {
((StackedUpdater<?, ?>) stackedUpdater).markTransactionBoundary();
final Transaction transaction = body.getTransactions().get(i);
@ -119,7 +130,8 @@ public class TransactionTracer {
stackedUpdater,
transaction,
transactionProcessor,
new StandardJsonTracer(out, showMemory, true, true));
new StandardJsonTracer(out, showMemory, true, true),
dataGasPrice);
out.println(
summaryTrace(
transaction, timer.stop().elapsed(TimeUnit.NANOSECONDS), result));
@ -135,7 +147,8 @@ public class TransactionTracer {
stackedUpdater,
transaction,
transactionProcessor,
OperationTracer.NO_TRACING);
OperationTracer.NO_TRACING,
dataGasPrice);
}
}
return Optional.of(traces);
@ -165,7 +178,9 @@ public class TransactionTracer {
final WorldUpdater worldUpdater,
final Transaction transaction,
final MainnetTransactionProcessor transactionProcessor,
final OperationTracer tracer) {
final OperationTracer tracer,
final Wei dataGasPrice) {
return transactionProcessor.processTransaction(
blockchain,
worldUpdater,
@ -175,7 +190,8 @@ public class TransactionTracer {
tracer,
new BlockHashLookup(header, blockchain),
false,
ImmutableTransactionValidationParams.builder().isAllowFutureNonce(true).build());
ImmutableTransactionValidationParams.builder().isAllowFutureNonce(true).build(),
dataGasPrice);
}
public static String summaryTrace(

@ -77,6 +77,7 @@ public enum JsonRpcError {
-32000, "Transaction nonce is too distant from current sender nonce"),
LOWER_NONCE_INVALID_TRANSACTION_EXISTS(
-32000, "An invalid transaction with a lower nonce exists"),
TOTAL_DATA_GAS_TOO_HIGH(-32000, "Total data gas too high"),
// Execution engine failures
UNKNOWN_PAYLOAD(-32001, "Payload does not exist / is not available"),

@ -23,6 +23,7 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockReplay;
@ -155,6 +156,7 @@ public class DebugStorageRangeAtTest {
//noinspection rawtypes
return Optional.of(
((BlockReplay.TransactionAction) invocation.getArgument(2))
.performAction(transaction, blockHeader, blockchain, worldState, transactionProcessor));
.performAction(
transaction, blockHeader, blockchain, worldState, transactionProcessor, Wei.ZERO));
}
}

@ -21,6 +21,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.ImmutableTransactionTraceParams;
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.chain.Blockchain;
@ -32,6 +33,7 @@ import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
@ -115,6 +117,7 @@ public class TransactionTracerTest {
when(protocolSchedule.getByBlockHeader(blockHeader)).thenReturn(protocolSpec);
when(protocolSpec.getTransactionProcessor()).thenReturn(transactionProcessor);
when(protocolSpec.getMiningBeneficiaryCalculator()).thenReturn(BlockHeader::getCoinbase);
when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0L));
when(blockchain.getChainHeadHeader()).thenReturn(blockHeader);
when(protocolSpec.getBadBlocksManager()).thenReturn(new BadBlockManager());
when(mutableWorldState.copy()).thenReturn(mutableWorldState);
@ -180,7 +183,8 @@ public class TransactionTracerTest {
eq(tracer),
any(),
any(),
any()))
any(),
eq(Wei.ZERO)))
.thenReturn(result);
final Optional<TransactionTrace> transactionTrace =
@ -267,7 +271,8 @@ public class TransactionTracerTest {
any(StandardJsonTracer.class),
any(),
any(),
any()))
any(),
eq(Wei.ZERO)))
.thenReturn(result);
final List<String> transactionTraces =

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.blockcreation;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.DataGas;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.ProtocolContext;
@ -43,6 +44,7 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.account.EvmAccount;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.data.TransactionType;
import org.hyperledger.besu.plugin.services.exception.StorageException;
import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException;
@ -166,6 +168,10 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
createPendingBlockHeader(timestamp, maybePrevRandao, newProtocolSpec);
final Address miningBeneficiary =
miningBeneficiaryCalculator.getMiningBeneficiary(processableBlockHeader.getNumber());
final Wei dataGasPrice =
newProtocolSpec
.getFeeMarket()
.dataPrice(parentHeader.getExcessDataGas().orElse(DataGas.ZERO));
throwIfStopped();
@ -178,6 +184,7 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
disposableWorldState,
maybeTransactions,
miningBeneficiary,
dataGasPrice,
newProtocolSpec);
throwIfStopped();
@ -209,6 +216,10 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
throwIfStopped();
final DataGas newExcessDataGas = computeExcessDataGas(transactionResults, newProtocolSpec);
throwIfStopped();
final SealableBlockHeader sealableBlockHeader =
BlockHeaderBuilder.create()
.populateFrom(processableBlockHeader)
@ -224,6 +235,7 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
withdrawalsCanBeProcessed
? BodyValidation.withdrawalsRoot(maybeWithdrawals.get())
: null)
.excessDataGas(newExcessDataGas)
.buildSealableBlockHeader();
final BlockHeader blockHeader = createFinalBlockHeader(sealableBlockHeader);
@ -245,11 +257,32 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
}
}
private DataGas computeExcessDataGas(
BlockTransactionSelector.TransactionSelectionResults transactionResults,
ProtocolSpec newProtocolSpec) {
if (newProtocolSpec.getFeeMarket().implementsDataFee()) {
final var gasCalculator = newProtocolSpec.getGasCalculator();
final int newBlobsCount =
transactionResults.getTransactionsByType(TransactionType.BLOB).stream()
.map(tx -> tx.getVersionedHashes().orElseThrow())
.mapToInt(List::size)
.sum();
// casting parent excess data gas to long since for the moment it should be well below that
// limit
return DataGas.of(
gasCalculator.computeExcessDataGas(
parentHeader.getExcessDataGas().map(DataGas::toLong).orElse(0L), newBlobsCount));
}
return null;
}
private BlockTransactionSelector.TransactionSelectionResults selectTransactions(
final ProcessableBlockHeader processableBlockHeader,
final MutableWorldState disposableWorldState,
final Optional<List<Transaction>> transactions,
final Address miningBeneficiary,
final Wei dataGasPrice,
final ProtocolSpec protocolSpec)
throws RuntimeException {
final MainnetTransactionProcessor transactionProcessor = protocolSpec.getTransactionProcessor();
@ -269,7 +302,10 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
minBlockOccupancyRatio,
isCancelled::get,
miningBeneficiary,
protocolSpec.getFeeMarket());
dataGasPrice,
protocolSpec.getFeeMarket(),
protocolSpec.getGasCalculator(),
protocolSpec.getGasLimitCalculator());
if (transactions.isPresent()) {
return selector.evaluateTransactions(transactions.get());

@ -18,6 +18,7 @@ import static org.hyperledger.besu.util.Slf4jLambdaHelper.traceLambda;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
@ -35,14 +36,20 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.evm.account.EvmAccount;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CancellationException;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.common.collect.Lists;
import org.apache.tuweni.bytes.Bytes;
@ -70,11 +77,6 @@ import org.slf4j.LoggerFactory;
* not cleared between executions of buildTransactionListForBlock().
*/
public class BlockTransactionSelector {
private static final Logger LOG = LoggerFactory.getLogger(BlockTransactionSelector.class);
private final Wei minTransactionGasPrice;
private final Double minBlockOccupancyRatio;
public static class TransactionValidationResult {
private final Transaction transaction;
private final ValidationResult<TransactionInvalidReason> validationResult;
@ -115,22 +117,30 @@ public class BlockTransactionSelector {
public static class TransactionSelectionResults {
private final List<Transaction> transactions = Lists.newArrayList();
private final Map<TransactionType, List<Transaction>> transactionsByType = new HashMap<>();
private final List<TransactionReceipt> receipts = Lists.newArrayList();
private final List<TransactionValidationResult> invalidTransactions = Lists.newArrayList();
private long cumulativeGasUsed = 0;
private long cumulativeDataGasUsed = 0;
private void update(
final Transaction transaction, final TransactionReceipt receipt, final long gasUsed) {
transactions.add(transaction);
final Transaction transaction,
final TransactionReceipt receipt,
final long gasUsed,
final long dataGasUsed) {
transactionsByType
.computeIfAbsent(transaction.getType(), type -> new ArrayList<>())
.add(transaction);
receipts.add(receipt);
cumulativeGasUsed += gasUsed;
cumulativeDataGasUsed += dataGasUsed;
traceLambda(
LOG,
"New selected transaction {}, total transactions {}, cumulative gas used {}",
"New selected transaction {}, total transactions {}, cumulative gas used {}, cumulative data gas used {}",
transaction::toTraceLog,
transactions::size,
() -> cumulativeGasUsed);
() -> transactionsByType.values().stream().mapToInt(List::size).sum(),
() -> cumulativeGasUsed,
() -> cumulativeDataGasUsed);
}
private void updateWithInvalidTransaction(
@ -140,7 +150,11 @@ public class BlockTransactionSelector {
}
public List<Transaction> getTransactions() {
return transactions;
return streamAllTransactions().collect(Collectors.toList());
}
public List<Transaction> getTransactionsByType(final TransactionType type) {
return transactionsByType.getOrDefault(type, List.of());
}
public List<TransactionReceipt> getReceipts() {
@ -151,10 +165,18 @@ public class BlockTransactionSelector {
return cumulativeGasUsed;
}
public long getCumulativeDataGasUsed() {
return cumulativeDataGasUsed;
}
public List<TransactionValidationResult> getInvalidTransactions() {
return invalidTransactions;
}
private Stream<Transaction> streamAllTransactions() {
return transactionsByType.values().stream().flatMap(List::stream);
}
@Override
public boolean equals(final Object o) {
if (this == o) {
@ -165,24 +187,36 @@ public class BlockTransactionSelector {
}
TransactionSelectionResults that = (TransactionSelectionResults) o;
return cumulativeGasUsed == that.cumulativeGasUsed
&& transactions.equals(that.transactions)
&& cumulativeDataGasUsed == that.cumulativeDataGasUsed
&& transactionsByType.equals(that.transactionsByType)
&& receipts.equals(that.receipts)
&& invalidTransactions.equals(that.invalidTransactions);
}
@Override
public int hashCode() {
return Objects.hash(transactions, receipts, invalidTransactions, cumulativeGasUsed);
return Objects.hash(
transactionsByType,
receipts,
invalidTransactions,
cumulativeGasUsed,
cumulativeDataGasUsed);
}
public String toTraceLog() {
return "cumulativeGasUsed="
+ cumulativeGasUsed
+ ", cumulativeDataGasUsed="
+ cumulativeDataGasUsed
+ ", transactions="
+ transactions.stream().map(Transaction::toTraceLog).collect(Collectors.joining("; "));
+ streamAllTransactions().map(Transaction::toTraceLog).collect(Collectors.joining("; "));
}
}
private static final Logger LOG = LoggerFactory.getLogger(BlockTransactionSelector.class);
private final Wei minTransactionGasPrice;
private final Double minBlockOccupancyRatio;
private final Supplier<Boolean> isCancelled;
private final MainnetTransactionProcessor transactionProcessor;
private final ProcessableBlockHeader processableBlockHeader;
@ -191,7 +225,10 @@ public class BlockTransactionSelector {
private final PendingTransactions pendingTransactions;
private final AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory;
private final Address miningBeneficiary;
private final Wei dataGasPrice;
private final FeeMarket feeMarket;
private final GasCalculator gasCalculator;
private final GasLimitCalculator gasLimitCalculator;
private final TransactionSelectionResults transactionSelectionResult =
new TransactionSelectionResults();
@ -207,7 +244,10 @@ public class BlockTransactionSelector {
final Double minBlockOccupancyRatio,
final Supplier<Boolean> isCancelled,
final Address miningBeneficiary,
final FeeMarket feeMarket) {
final Wei dataGasPrice,
final FeeMarket feeMarket,
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator) {
this.transactionProcessor = transactionProcessor;
this.blockchain = blockchain;
this.worldState = worldState;
@ -218,7 +258,10 @@ public class BlockTransactionSelector {
this.minTransactionGasPrice = minTransactionGasPrice;
this.minBlockOccupancyRatio = minBlockOccupancyRatio;
this.miningBeneficiary = miningBeneficiary;
this.dataGasPrice = dataGasPrice;
this.feeMarket = feeMarket;
this.gasCalculator = gasCalculator;
this.gasLimitCalculator = gasLimitCalculator;
}
/*
@ -278,6 +321,9 @@ public class BlockTransactionSelector {
if (transactionCurrentPriceBelowMin(transaction)) {
return TransactionSelectionResult.CONTINUE;
}
if (transactionDataPriceBelowMin(transaction)) {
return TransactionSelectionResult.CONTINUE;
}
final WorldUpdater worldStateUpdater = worldState.updater();
final BlockHashLookup blockHashLookup = new BlockHashLookup(processableBlockHeader, blockchain);
@ -298,7 +344,7 @@ public class BlockTransactionSelector {
transaction.getHash().toHexString());
return transactionSelectionResultForInvalidResult(transaction, validationResult);
} else {
// valid GoQuorum private tx, we need to hand craft the receipt and increment the nonce
// valid GoQuorum private tx, we need to handcraft the receipt and increment the nonce
effectiveResult = publicResultForWhenWeHaveAPrivateTransaction(transaction);
worldStateUpdater.getOrCreate(transaction.getSender()).getMutable().incrementNonce();
}
@ -312,7 +358,8 @@ public class BlockTransactionSelector {
miningBeneficiary,
blockHashLookup,
false,
TransactionValidationParams.mining());
TransactionValidationParams.mining(),
dataGasPrice);
}
if (!effectiveResult.isInvalid()) {
@ -331,6 +378,15 @@ public class BlockTransactionSelector {
return TransactionSelectionResult.CONTINUE;
}
private boolean transactionDataPriceBelowMin(final Transaction transaction) {
if (transaction.getType().supportsBlob()) {
if (transaction.getMaxFeePerDataGas().orElseThrow().lessThan(dataGasPrice)) {
return true;
}
}
return false;
}
private boolean transactionCurrentPriceBelowMin(final Transaction transaction) {
// Here we only care about EIP1159 since for Frontier and local transactions the checks
// that we do when accepting them in the pool are enough
@ -426,11 +482,14 @@ public class BlockTransactionSelector {
final long cumulativeGasUsed =
transactionSelectionResult.getCumulativeGasUsed() + gasUsedByTransaction;
final long dataGasUsed = gasCalculator.dataGasCost(transaction.getBlobCount());
transactionSelectionResult.update(
transaction,
transactionReceiptFactory.create(
transaction.getType(), result, worldState, cumulativeGasUsed),
gasUsedByTransaction);
gasUsedByTransaction,
dataGasUsed);
}
private boolean isIncorrectNonce(final ValidationResult<TransactionInvalidReason> result) {
@ -448,7 +507,15 @@ public class BlockTransactionSelector {
}
private boolean transactionTooLargeForBlock(final Transaction transaction) {
return transaction.getGasLimit()
final long dataGasUsed = gasCalculator.dataGasCost(transaction.getBlobCount());
if (dataGasUsed
> gasLimitCalculator.currentDataGasLimit()
- transactionSelectionResult.getCumulativeDataGasUsed()) {
return true;
}
return transaction.getGasLimit() + dataGasUsed
> processableBlockHeader.getGasLimit() - transactionSelectionResult.getCumulativeGasUsed();
}

@ -27,6 +27,7 @@ import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.AddressHelpers;
import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -50,6 +51,7 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.WorldState;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
@ -130,7 +132,8 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(mainnetTransactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary);
createBlockSelector(
mainnetTransactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary, Wei.ZERO);
final BlockTransactionSelector.TransactionSelectionResults results =
selector.buildTransactionListForBlock();
@ -153,7 +156,8 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary);
createBlockSelector(
transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary, Wei.ZERO);
final BlockTransactionSelector.TransactionSelectionResults results =
selector.buildTransactionListForBlock();
@ -184,7 +188,8 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary);
createBlockSelector(
transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary, Wei.ZERO);
final BlockTransactionSelector.TransactionSelectionResults results =
selector.buildTransactionListForBlock();
@ -211,7 +216,8 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary);
createBlockSelector(
transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary, Wei.ZERO);
final BlockTransactionSelector.TransactionSelectionResults results =
selector.buildTransactionListForBlock();
@ -255,7 +261,10 @@ public abstract class AbstractBlockTransactionSelectorTest {
0.8,
this::isCancelled,
miningBeneficiary,
FeeMarket.london(0L));
Wei.ZERO,
FeeMarket.london(0L),
new LondonGasCalculator(),
GasLimitCalculator.constant());
// this should fill up all the block space
final Transaction fillingLegacyTx =
@ -303,7 +312,8 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary);
createBlockSelector(
transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary, Wei.ZERO);
final TransactionTestFixture txTestFixture = new TransactionTestFixture();
// Add 3 transactions to the Pending Transactions, 79% of block, 100% of block and 10% of block
@ -342,7 +352,8 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary);
createBlockSelector(
transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary, Wei.ZERO);
final TransactionTestFixture txTestFixture = new TransactionTestFixture();
// Add 4 transactions to the Pending Transactions 15% (ok), 79% (ok), 25% (too large), 10%
@ -390,7 +401,8 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary);
createBlockSelector(
transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary, Wei.ZERO);
final TransactionTestFixture txTestFixture = new TransactionTestFixture();
final Transaction validTransaction =
@ -425,7 +437,8 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary);
createBlockSelector(
transactionProcessor, blockHeader, Wei.ZERO, miningBeneficiary, Wei.ZERO);
final BlockTransactionSelector.TransactionSelectionResults results =
selector.buildTransactionListForBlock();
@ -439,7 +452,8 @@ public abstract class AbstractBlockTransactionSelectorTest {
final MainnetTransactionProcessor transactionProcessor,
final ProcessableBlockHeader blockHeader,
final Wei minGasPrice,
final Address miningBeneficiary) {
final Address miningBeneficiary,
final Wei dataGasPrice) {
final BlockTransactionSelector selector =
new BlockTransactionSelector(
transactionProcessor,
@ -452,7 +466,10 @@ public abstract class AbstractBlockTransactionSelectorTest {
0.8,
this::isCancelled,
miningBeneficiary,
getFeeMarket());
dataGasPrice,
getFeeMarket(),
new LondonGasCalculator(),
GasLimitCalculator.constant());
return selector;
}
@ -489,7 +506,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
protected void ensureTransactionIsValid(
final Transaction tx, final long gasUsedByTransaction, final long gasRemaining) {
when(transactionProcessor.processTransaction(
any(), any(), any(), eq(tx), any(), any(), anyBoolean(), any()))
any(), any(), any(), eq(tx), any(), any(), anyBoolean(), any(), any()))
.thenReturn(
TransactionProcessingResult.successful(
new ArrayList<>(),
@ -502,7 +519,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
protected void ensureTransactionIsInvalid(
final Transaction tx, final TransactionInvalidReason invalidReason) {
when(transactionProcessor.processTransaction(
any(), any(), any(), eq(tx), any(), any(), anyBoolean(), any()))
any(), any(), any(), eq(tx), any(), any(), anyBoolean(), any(), any()))
.thenReturn(TransactionProcessingResult.invalid(ValidationResult.invalid(invalidReason)));
}
}

@ -70,7 +70,8 @@ public class LondonFeeMarketBlockTransactionSelectorTest
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(transactionProcessor, blockHeader, Wei.of(6), miningBeneficiary);
createBlockSelector(
transactionProcessor, blockHeader, Wei.of(6), miningBeneficiary, Wei.ZERO);
// tx is willing to pay max 6 wei for gas, but current network condition (baseFee == 1)
// result in it paying 2 wei, that is below the minimum accepted by the node, so it is skipped
@ -90,7 +91,8 @@ public class LondonFeeMarketBlockTransactionSelectorTest
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(transactionProcessor, blockHeader, Wei.of(6), miningBeneficiary);
createBlockSelector(
transactionProcessor, blockHeader, Wei.of(6), miningBeneficiary, Wei.ZERO);
// tx is willing to pay max 6 wei for gas, and current network condition (baseFee == 5)
// result in it paying the max, that is >= the minimum accepted by the node, so it is selected
@ -112,7 +114,8 @@ public class LondonFeeMarketBlockTransactionSelectorTest
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(transactionProcessor, blockHeader, Wei.of(6), miningBeneficiary);
createBlockSelector(
transactionProcessor, blockHeader, Wei.of(6), miningBeneficiary, Wei.ZERO);
// tx is willing to pay max 6 wei for gas, but current network condition (baseFee == 1)
// result in it paying 2 wei, that is below the minimum accepted by the node, but since it is

@ -103,7 +103,8 @@ public class TraceTransactionIntegrationTest {
genesisBlockHeader.getCoinbase(),
blockHashLookup,
false,
TransactionValidationParams.blockReplay());
TransactionValidationParams.blockReplay(),
Wei.ZERO);
assertThat(result.isSuccessful()).isTrue();
final Account createdContract =
createTransactionUpdater.getTouchedAccounts().stream()
@ -135,7 +136,8 @@ public class TraceTransactionIntegrationTest {
genesisBlockHeader.getCoinbase(),
tracer,
blockHashLookup,
false);
false,
Wei.ZERO);
assertThat(result.isSuccessful()).isTrue();
@ -176,7 +178,8 @@ public class TraceTransactionIntegrationTest {
genesisBlockHeader.getCoinbase(),
tracer,
new BlockHashLookup(genesisBlockHeader, blockchain),
false);
false,
Wei.ZERO);
final int expectedDepth = 0; // Reference impl returned 1. Why the difference?

@ -21,4 +21,8 @@ public interface GasLimitCalculator {
static GasLimitCalculator constant() {
return (currentGasLimit, targetGasLimit, newBlockNumber) -> currentGasLimit;
}
default long currentDataGasLimit() {
return 0L;
}
}

@ -385,7 +385,6 @@ public class BlockHeaderBuilder {
}
public BlockHeaderBuilder excessDataGas(final DataGas excessDataGas) {
checkArgument(gasLimit >= 0L);
this.excessDataGas = excessDataGas;
return this;
}

@ -44,7 +44,6 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.common.primitives.Longs;
import org.apache.tuweni.bytes.Bytes;
@ -478,6 +477,15 @@ public class Transaction
return gasLimit;
}
/**
* Returns the number of blobs this transaction has, or 0 if not a blob transaction type
*
* @return return the count
*/
public int getBlobCount() {
return versionedHashes.map(List::size).orElse(0);
}
/**
* Returns the transaction recipient.
*
@ -692,18 +700,13 @@ public class Transaction
}
/**
* Calculates the up-front cost for the gas the transaction can use.
* Calculates the max up-front cost for the gas the transaction can use.
*
* @return the up-front cost for the gas the transaction can use.
* @return the max up-front cost for the gas the transaction can use.
*/
public Wei getUpfrontGasCost() {
private Wei getMaxUpfrontGasCost(final long dataGasPerBlock) {
return getUpfrontGasCost(
Stream.concat(maxFeePerGas.stream(), gasPrice.stream())
.findFirst()
.orElseThrow(
() ->
new IllegalStateException(
String.format("Transaction requires either gasPrice or maxFeePerGas"))));
getMaxGasPrice(), getMaxFeePerDataGas().orElse(Wei.ZERO), dataGasPerBlock);
}
/**
@ -712,21 +715,23 @@ public class Transaction
* @return true is upfront data cost overflow uint256 max value
*/
private boolean isUpfrontGasCostTooHigh() {
return calculateUpfrontGasCost(getMaxGasPrice()).bitLength() > 256;
return calculateUpfrontGasCost(getMaxGasPrice(), Wei.ZERO, 0L).bitLength() > 256;
}
/**
* Calculates the up-front cost for the gas the transaction can use.
* Calculates the up-front cost for the gas and data gas the transaction can use.
*
* @param gasPrice the gas price to use
* @param dataGasPrice the data gas price to use
* @return the up-front cost for the gas the transaction can use.
*/
public Wei getUpfrontGasCost(final Wei gasPrice) {
public Wei getUpfrontGasCost(
final Wei gasPrice, final Wei dataGasPrice, final long totalDataGas) {
if (gasPrice == null || gasPrice.isZero()) {
return Wei.ZERO;
}
final var cost = calculateUpfrontGasCost(gasPrice);
final var cost = calculateUpfrontGasCost(gasPrice, dataGasPrice, totalDataGas);
if (cost.bitLength() > 256) {
return Wei.MAX_WEI;
@ -735,8 +740,16 @@ public class Transaction
}
}
private BigInteger calculateUpfrontGasCost(final Wei gasPrice) {
return new BigInteger(1, Longs.toByteArray(getGasLimit())).multiply(gasPrice.getAsBigInteger());
private BigInteger calculateUpfrontGasCost(
final Wei gasPrice, final Wei dataGasPrice, final long totalDataGas) {
var cost =
new BigInteger(1, Longs.toByteArray(getGasLimit())).multiply(gasPrice.getAsBigInteger());
if (transactionType.supportsBlob()) {
cost = cost.add(dataGasPrice.getAsBigInteger().multiply(BigInteger.valueOf(totalDataGas)));
}
return cost;
}
/**
@ -748,8 +761,8 @@ public class Transaction
*
* @return the up-front gas cost for the transaction
*/
public Wei getUpfrontCost() {
return getUpfrontGasCost().addExact(getValue());
public Wei getUpfrontCost(final long totalDataGas) {
return getMaxUpfrontGasCost(totalDataGas).addExact(getValue());
}
/**

@ -150,7 +150,8 @@ public class GoQuorumBlockProcessor extends MainnetBlockProcessor {
blockHashLookup,
true,
TransactionValidationParams.processingBlock(),
null);
null,
Wei.ZERO);
if (result.isInvalid()) {
String errorMessage =

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.DataGas;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
@ -96,6 +97,9 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
final PrivateMetadataUpdater privateMetadataUpdater) {
final List<TransactionReceipt> receipts = new ArrayList<>();
long currentGasUsed = 0;
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(blockHeader);
for (final Transaction transaction : transactions) {
if (!hasAvailableBlockBudget(blockHeader, transaction, currentGasUsed)) {
return new BlockProcessingResult(Optional.empty(), "provided gas insufficient");
@ -105,6 +109,14 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
final BlockHashLookup blockHashLookup = new BlockHashLookup(blockHeader, blockchain);
final Address miningBeneficiary =
miningBeneficiaryCalculator.calculateBeneficiary(blockHeader);
final Wei dataGasPrice =
protocolSpec
.getFeeMarket()
.dataPrice(
blockchain
.getBlockHeader(blockHeader.getParentHash())
.flatMap(BlockHeader::getExcessDataGas)
.orElse(DataGas.ZERO));
final TransactionProcessingResult result =
transactionProcessor.processTransaction(
@ -117,7 +129,8 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
blockHashLookup,
true,
TransactionValidationParams.processingBlock(),
privateMetadataUpdater);
privateMetadataUpdater,
dataGasPrice);
if (result.isInvalid()) {
String errorMessage =
MessageFormat.format(
@ -141,7 +154,7 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
}
final Optional<WithdrawalsProcessor> maybeWithdrawalsProcessor =
protocolSchedule.getByBlockHeader(blockHeader).getWithdrawalsProcessor();
protocolSpec.getWithdrawalsProcessor();
if (maybeWithdrawalsProcessor.isPresent() && maybeWithdrawals.isPresent()) {
try {
maybeWithdrawalsProcessor

@ -0,0 +1,31 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
public class CancunTargetingGasLimitCalculator extends LondonTargetingGasLimitCalculator {
private static final long MAX_DATA_GAS_PER_BLOCK = 1 << 19;
public CancunTargetingGasLimitCalculator(
final long londonForkBlock, final BaseFeeMarket feeMarket) {
super(londonForkBlock, feeMarket);
}
@Override
public long currentDataGasLimit() {
return MAX_DATA_GAS_PER_BLOCK;
}
}

@ -76,9 +76,9 @@ public class ClassicProtocolSpecs {
contractSizeLimit, configStackSizeLimit, quorumCompatibilityMode, evmConfiguration)
.gasCalculator(TangerineWhistleGasCalculator::new)
.transactionValidatorBuilder(
gasCalculator ->
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
gasCalculator, true, chainId, quorumCompatibilityMode))
gasCalculator, gasLimitCalculator, true, chainId, quorumCompatibilityMode))
.name("ClassicTangerineWhistle");
}
@ -149,9 +149,9 @@ public class ClassicProtocolSpecs {
evmConfiguration)
.difficultyCalculator(ClassicDifficultyCalculators.DIFFICULTY_BOMB_REMOVED)
.transactionValidatorBuilder(
gasCalculator ->
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
gasCalculator, true, chainId, quorumCompatibilityMode))
gasCalculator, gasLimitCalculator, true, chainId, quorumCompatibilityMode))
.name("DefuseDifficultyBomb");
}
@ -353,9 +353,10 @@ public class ClassicProtocolSpecs {
evmConfiguration)
.gasCalculator(BerlinGasCalculator::new)
.transactionValidatorBuilder(
gasCalculator ->
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
gasCalculator,
gasLimitCalculator,
true,
chainId,
Set.of(TransactionType.FRONTIER, TransactionType.ACCESS_LIST),

@ -19,6 +19,7 @@ import org.hyperledger.besu.config.PowAlgorithm;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.MainnetBlockValidator;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -45,6 +46,7 @@ import org.hyperledger.besu.evm.contractvalidation.PrefixCodeRule;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.BerlinGasCalculator;
import org.hyperledger.besu.evm.gascalculator.ByzantiumGasCalculator;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator;
import org.hyperledger.besu.evm.gascalculator.HomesteadGasCalculator;
@ -122,9 +124,9 @@ public abstract class MainnetProtocolSpecs {
Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)),
0))
.transactionValidatorBuilder(
gasCalculator ->
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
gasCalculator, false, Optional.empty(), goQuorumMode))
gasCalculator, gasLimitCalculator, false, Optional.empty(), goQuorumMode))
.transactionProcessorBuilder(
(gasCalculator,
transactionValidator,
@ -228,9 +230,13 @@ public abstract class MainnetProtocolSpecs {
Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)),
0))
.transactionValidatorBuilder(
gasCalculator ->
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
gasCalculator, true, Optional.empty(), quorumCompatibilityMode))
gasCalculator,
gasLimitCalculator,
true,
Optional.empty(),
quorumCompatibilityMode))
.difficultyCalculator(MainnetDifficultyCalculators.HOMESTEAD)
.name("Homestead");
}
@ -315,9 +321,9 @@ public abstract class MainnetProtocolSpecs {
1,
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES))
.transactionValidatorBuilder(
gasCalculator ->
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
gasCalculator, true, chainId, quorumCompatibilityMode))
gasCalculator, gasLimitCalculator, true, chainId, quorumCompatibilityMode))
.transactionProcessorBuilder(
(gasCalculator,
transactionValidator,
@ -482,9 +488,10 @@ public abstract class MainnetProtocolSpecs {
evmConfiguration)
.gasCalculator(BerlinGasCalculator::new)
.transactionValidatorBuilder(
gasCalculator ->
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
gasCalculator,
gasLimitCalculator,
true,
chainId,
Set.of(TransactionType.FRONTIER, TransactionType.ACCESS_LIST),
@ -524,9 +531,10 @@ public abstract class MainnetProtocolSpecs {
.gasLimitCalculator(
new LondonTargetingGasLimitCalculator(londonForkBlockNumber, londonFeeMarket))
.transactionValidatorBuilder(
gasCalculator ->
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
gasCalculator,
gasLimitCalculator,
londonFeeMarket,
true,
chainId,
@ -651,7 +659,7 @@ public abstract class MainnetProtocolSpecs {
final boolean quorumCompatibilityMode,
final EvmConfiguration evmConfiguration) {
// extra vaiables need to support flipping the warm coinbase flag.
// extra variables need to support flipping the warm coinbase flag.
final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE);
final long londonForkBlockNumber = genesisConfigOptions.getLondonBlockNumber().orElse(0L);
final BaseFeeMarket londonFeeMarket =
@ -692,9 +700,10 @@ public abstract class MainnetProtocolSpecs {
CoinbaseFeePriceCalculator.eip1559()))
// Contract creation rules for EIP-3860 Limit and meter intitcode
.transactionValidatorBuilder(
gasCalculator ->
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
gasCalculator,
gasLimitCalculator,
londonFeeMarket,
true,
chainId,
@ -718,8 +727,17 @@ public abstract class MainnetProtocolSpecs {
final boolean quorumCompatibilityMode,
final EvmConfiguration evmConfiguration) {
final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE);
final int contractSizeLimit =
configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT);
final long londonForkBlockNumber = genesisConfigOptions.getLondonBlockNumber().orElse(0L);
final BaseFeeMarket cancunFeeMarket =
genesisConfigOptions.isZeroBaseFee()
? FeeMarket.zeroBaseFee(londonForkBlockNumber)
: FeeMarket.cancun(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas());
final GasLimitCalculator cancunGasLimitCalculator =
new CancunTargetingGasLimitCalculator(londonForkBlockNumber, cancunFeeMarket);
return shanghaiDefinition(
chainId,
@ -729,6 +747,11 @@ public abstract class MainnetProtocolSpecs {
genesisConfigOptions,
quorumCompatibilityMode,
evmConfiguration)
.feeMarket(cancunFeeMarket)
// gas calculator for EIP-4844 data gas
.gasCalculator(CancunGasCalculator::new)
// gas limit with EIP-4844 max data gas per block
.gasLimitCalculator(cancunGasLimitCalculator)
// EVM changes to support EOF EIPs (3670, 4200, 4750, 5450)
.evmBuilder(
(gasCalculator, jdCacheConfig) ->
@ -745,6 +768,38 @@ public abstract class MainnetProtocolSpecs {
MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1, false)),
1,
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES))
// use Cancun fee market
.transactionProcessorBuilder(
(gasCalculator,
transactionValidator,
contractCreationProcessor,
messageCallProcessor) ->
new MainnetTransactionProcessor(
gasCalculator,
transactionValidator,
contractCreationProcessor,
messageCallProcessor,
true,
true,
stackSizeLimit,
cancunFeeMarket,
CoinbaseFeePriceCalculator.eip1559()))
// change to check for max data gas per block for EIP-4844
.transactionValidatorBuilder(
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
gasCalculator,
gasLimitCalculator,
cancunFeeMarket,
true,
chainId,
Set.of(
TransactionType.FRONTIER,
TransactionType.ACCESS_LIST,
TransactionType.EIP1559,
TransactionType.BLOB),
quorumCompatibilityMode,
SHANGHAI_INIT_CODE_SIZE_LIMIT))
.precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::cancun)
.name("Cancun");
}

@ -125,7 +125,8 @@ public class MainnetTransactionProcessor {
final Address miningBeneficiary,
final BlockHashLookup blockHashLookup,
final Boolean isPersistingPrivateState,
final TransactionValidationParams transactionValidationParams) {
final TransactionValidationParams transactionValidationParams,
final Wei dataGasPrice) {
return processTransaction(
blockchain,
worldState,
@ -136,7 +137,8 @@ public class MainnetTransactionProcessor {
blockHashLookup,
isPersistingPrivateState,
transactionValidationParams,
null);
null,
dataGasPrice);
}
/**
@ -165,7 +167,8 @@ public class MainnetTransactionProcessor {
final BlockHashLookup blockHashLookup,
final Boolean isPersistingPrivateState,
final TransactionValidationParams transactionValidationParams,
final OperationTracer operationTracer) {
final OperationTracer operationTracer,
final Wei dataGasPrice) {
return processTransaction(
blockchain,
worldState,
@ -176,7 +179,8 @@ public class MainnetTransactionProcessor {
blockHashLookup,
isPersistingPrivateState,
transactionValidationParams,
null);
null,
dataGasPrice);
}
/**
@ -200,7 +204,8 @@ public class MainnetTransactionProcessor {
final Address miningBeneficiary,
final OperationTracer operationTracer,
final BlockHashLookup blockHashLookup,
final Boolean isPersistingPrivateState) {
final Boolean isPersistingPrivateState,
final Wei dataGasPrice) {
return processTransaction(
blockchain,
worldState,
@ -211,7 +216,8 @@ public class MainnetTransactionProcessor {
blockHashLookup,
isPersistingPrivateState,
ImmutableTransactionValidationParams.builder().build(),
null);
null,
dataGasPrice);
}
/**
@ -237,7 +243,8 @@ public class MainnetTransactionProcessor {
final OperationTracer operationTracer,
final BlockHashLookup blockHashLookup,
final Boolean isPersistingPrivateState,
final TransactionValidationParams transactionValidationParams) {
final TransactionValidationParams transactionValidationParams,
final Wei dataGasPrice) {
return processTransaction(
blockchain,
worldState,
@ -248,7 +255,8 @@ public class MainnetTransactionProcessor {
blockHashLookup,
isPersistingPrivateState,
transactionValidationParams,
null);
null,
dataGasPrice);
}
public TransactionProcessingResult processTransaction(
@ -261,7 +269,8 @@ public class MainnetTransactionProcessor {
final BlockHashLookup blockHashLookup,
final Boolean isPersistingPrivateState,
final TransactionValidationParams transactionValidationParams,
final PrivateMetadataUpdater privateMetadataUpdater) {
final PrivateMetadataUpdater privateMetadataUpdater,
final Wei dataGasPrice) {
try {
LOG.trace("Starting execution of {}", transaction);
ValidationResult<TransactionInvalidReason> validationResult =
@ -288,15 +297,19 @@ public class MainnetTransactionProcessor {
final MutableAccount senderMutableAccount = sender.getMutable();
final long previousNonce = senderMutableAccount.incrementNonce();
final Wei transactionGasPrice =
feeMarket.getTransactionPriceCalculator().price(transaction, blockHeader.getBaseFee());
LOG.trace(
"Incremented sender {} nonce ({} -> {})",
senderAddress,
previousNonce,
sender.getNonce());
final Wei upfrontGasCost = transaction.getUpfrontGasCost(transactionGasPrice);
final Wei transactionGasPrice =
feeMarket.getTransactionPriceCalculator().price(transaction, blockHeader.getBaseFee());
final long dataGas = gasCalculator.dataGasCost(transaction.getBlobCount());
final Wei upfrontGasCost =
transaction.getUpfrontGasCost(transactionGasPrice, dataGasPrice, dataGas);
final Wei previousBalance = senderMutableAccount.decrementBalance(upfrontGasCost);
LOG.trace(
"Deducted sender {} upfront gas cost {} ({} -> {})",
@ -327,12 +340,14 @@ public class MainnetTransactionProcessor {
transaction.getPayload(), transaction.isContractCreation());
final long accessListGas =
gasCalculator.accessListGasCost(accessListEntries.size(), accessListStorageCount);
final long gasAvailable = transaction.getGasLimit() - intrinsicGas - accessListGas;
LOG.trace(
"Gas available for execution {} = {} - {} (limit - intrinsic)",
"Gas available for execution {} = {} - {} - {} (limit - intrinsic - accessList)",
gasAvailable,
transaction.getGasLimit(),
intrinsicGas);
intrinsicGas,
accessListGas);
final WorldUpdater worldUpdater = worldState.updater();
final Deque<MessageFrame> messageFrameStack = new ArrayDeque<>();
@ -424,9 +439,9 @@ public class MainnetTransactionProcessor {
// after the other so that if it is the same account somehow, we end up with the right result)
final long selfDestructRefund =
gasCalculator.getSelfDestructRefundAmount() * initialFrame.getSelfDestructs().size();
final long refundGas = initialFrame.getGasRefund() + selfDestructRefund;
final long refunded = refunded(transaction, initialFrame.getRemainingGas(), refundGas);
final Wei refundedWei = transactionGasPrice.multiply(refunded);
final long baseRefundGas = initialFrame.getGasRefund() + selfDestructRefund;
final long refundedGas = refunded(transaction, initialFrame.getRemainingGas(), baseRefundGas);
final Wei refundedWei = transactionGasPrice.multiply(refundedGas);
senderMutableAccount.incrementBalance(refundedWei);
final long gasUsedByTransaction = transaction.getGasLimit() - initialFrame.getRemainingGas();
@ -434,25 +449,26 @@ public class MainnetTransactionProcessor {
if (!worldState.getClass().equals(GoQuorumMutablePrivateWorldStateUpdater.class)) {
// if this is not a private GoQuorum transaction we have to update the coinbase
final var coinbase = worldState.getOrCreate(miningBeneficiary).getMutable();
final long coinbaseFee = transaction.getGasLimit() - refunded;
final long usedGas = transaction.getGasLimit() - refundedGas;
final CoinbaseFeePriceCalculator coinbaseCalculator;
if (blockHeader.getBaseFee().isPresent()) {
final Wei baseFee = blockHeader.getBaseFee().get();
if (transactionGasPrice.compareTo(baseFee) < 0) {
return TransactionProcessingResult.failed(
gasUsedByTransaction,
refunded,
refundedGas,
ValidationResult.invalid(
TransactionInvalidReason.TRANSACTION_PRICE_TOO_LOW,
"transaction price must be greater than base fee"),
Optional.empty());
}
coinbaseCalculator = coinbaseFeePriceCalculator;
} else {
coinbaseCalculator = CoinbaseFeePriceCalculator.frontier();
}
final CoinbaseFeePriceCalculator coinbaseCalculator =
blockHeader.getBaseFee().isPresent()
? coinbaseFeePriceCalculator
: CoinbaseFeePriceCalculator.frontier();
final Wei coinbaseWeiDelta =
coinbaseCalculator.price(coinbaseFee, transactionGasPrice, blockHeader.getBaseFee());
coinbaseCalculator.price(usedGas, transactionGasPrice, blockHeader.getBaseFee());
coinbase.incrementBalance(coinbaseWeiDelta);
}
@ -467,12 +483,12 @@ public class MainnetTransactionProcessor {
return TransactionProcessingResult.successful(
initialFrame.getLogs(),
gasUsedByTransaction,
refunded,
refundedGas,
initialFrame.getOutputData(),
validationResult);
} else {
return TransactionProcessingResult.failed(
gasUsedByTransaction, refunded, validationResult, initialFrame.getRevertReason());
gasUsedByTransaction, refundedGas, validationResult, initialFrame.getRevertReason());
}
} catch (final MerkleTrieException re) {
// need to throw to trigger the heal
@ -508,7 +524,7 @@ public class MainnetTransactionProcessor {
protected long refunded(
final Transaction transaction, final long gasRemaining, final long gasRefund) {
// Integer truncation takes care of the the floor calculation needed after the divide.
// Integer truncation takes care of the floor calculation needed after the divide.
final long maxRefundAllowance =
(transaction.getGasLimit() - gasRemaining) / gasCalculator.getMaxRefundQuotient();
final long refundAllowance = Math.min(maxRefundAllowance, gasRefund);

@ -20,6 +20,7 @@ import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionFilter;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
@ -41,6 +42,7 @@ import java.util.Set;
public class MainnetTransactionValidator {
private final GasCalculator gasCalculator;
private final GasLimitCalculator gasLimitCalculator;
private final FeeMarket feeMarket;
private final boolean disallowSignatureMalleability;
@ -55,11 +57,13 @@ public class MainnetTransactionValidator {
public MainnetTransactionValidator(
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
final boolean checkSignatureMalleability,
final Optional<BigInteger> chainId,
final boolean goQuorumCompatibilityMode) {
this(
gasCalculator,
gasLimitCalculator,
checkSignatureMalleability,
chainId,
Set.of(TransactionType.FRONTIER),
@ -68,12 +72,14 @@ public class MainnetTransactionValidator {
public MainnetTransactionValidator(
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
final boolean checkSignatureMalleability,
final Optional<BigInteger> chainId,
final Set<TransactionType> acceptedTransactionTypes,
final boolean quorumCompatibilityMode) {
this(
gasCalculator,
gasLimitCalculator,
FeeMarket.legacy(),
checkSignatureMalleability,
chainId,
@ -84,6 +90,7 @@ public class MainnetTransactionValidator {
public MainnetTransactionValidator(
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
final FeeMarket feeMarket,
final boolean checkSignatureMalleability,
final Optional<BigInteger> chainId,
@ -91,6 +98,7 @@ public class MainnetTransactionValidator {
final boolean goQuorumCompatibilityMode,
final int maxInitcodeSize) {
this.gasCalculator = gasCalculator;
this.gasLimitCalculator = gasLimitCalculator;
this.feeMarket = feeMarket;
this.disallowSignatureMalleability = checkSignatureMalleability;
this.chainId = chainId;
@ -119,12 +127,6 @@ public class MainnetTransactionValidator {
return signatureResult;
}
if (goQuorumCompatibilityMode && transaction.hasCostParams()) {
return ValidationResult.invalid(
TransactionInvalidReason.GAS_PRICE_MUST_BE_ZERO,
"gasPrice must be set to zero on a GoQuorum compatible network");
}
final TransactionType transactionType = transaction.getType();
if (!acceptedTransactionTypes.contains(transactionType)) {
return ValidationResult.invalid(
@ -134,10 +136,37 @@ public class MainnetTransactionValidator {
transactionType, acceptedTransactionTypes));
}
if (baseFee.isPresent()) {
final Wei price = feeMarket.getTransactionPriceCalculator().price(transaction, baseFee);
if (transaction.getNonce() == MAX_NONCE) {
return ValidationResult.invalid(
TransactionInvalidReason.NONCE_OVERFLOW, "Nonce must be less than 2^64-1");
}
if (transaction.isContractCreation() && transaction.getPayload().size() > maxInitcodeSize) {
return ValidationResult.invalid(
TransactionInvalidReason.INITCODE_TOO_LARGE,
String.format(
"Initcode size of %d exceeds maximum size of %s",
transaction.getPayload().size(), maxInitcodeSize));
}
return validateCostAndFee(transaction, baseFee, transactionValidationParams);
}
private ValidationResult<TransactionInvalidReason> validateCostAndFee(
final Transaction transaction,
final Optional<Wei> maybeBaseFee,
final TransactionValidationParams transactionValidationParams) {
if (goQuorumCompatibilityMode && transaction.hasCostParams()) {
return ValidationResult.invalid(
TransactionInvalidReason.GAS_PRICE_MUST_BE_ZERO,
"gasPrice must be set to zero on a GoQuorum compatible network");
}
if (maybeBaseFee.isPresent()) {
final Wei price = feeMarket.getTransactionPriceCalculator().price(transaction, maybeBaseFee);
if (!transactionValidationParams.isAllowMaxFeeGasBelowBaseFee()
&& price.compareTo(baseFee.orElseThrow()) < 0) {
&& price.compareTo(maybeBaseFee.orElseThrow()) < 0) {
return ValidationResult.invalid(
TransactionInvalidReason.GAS_PRICE_BELOW_CURRENT_BASE_FEE,
"gasPrice is less than the current BaseFee");
@ -157,9 +186,15 @@ public class MainnetTransactionValidator {
}
}
if (transaction.getNonce() == MAX_NONCE) {
return ValidationResult.invalid(
TransactionInvalidReason.NONCE_OVERFLOW, "Nonce must be less than 2^64-1");
if (transaction.getType().supportsBlob()) {
final long txTotalDataGas = gasCalculator.dataGasCost(transaction.getBlobCount());
if (txTotalDataGas > gasLimitCalculator.currentDataGasLimit()) {
return ValidationResult.invalid(
TransactionInvalidReason.TOTAL_DATA_GAS_TOO_HIGH,
String.format(
"total data gas %d exceeds max data gas per block %d",
txTotalDataGas, gasLimitCalculator.currentDataGasLimit()));
}
}
final long intrinsicGasCost =
@ -174,14 +209,6 @@ public class MainnetTransactionValidator {
intrinsicGasCost, transaction.getGasLimit()));
}
if (transaction.isContractCreation() && transaction.getPayload().size() > maxInitcodeSize) {
return ValidationResult.invalid(
TransactionInvalidReason.INITCODE_TOO_LARGE,
String.format(
"Initcode size of %d exceeds maximum size of %s",
transaction.getPayload().size(), maxInitcodeSize));
}
return ValidationResult.valid();
}
@ -199,13 +226,14 @@ public class MainnetTransactionValidator {
if (sender.getCodeHash() != null) codeHash = sender.getCodeHash();
}
if (transaction.getUpfrontCost().compareTo(senderBalance) > 0) {
final Wei upfrontCost =
transaction.getUpfrontCost(gasCalculator.dataGasCost(transaction.getBlobCount()));
if (upfrontCost.compareTo(senderBalance) > 0) {
return ValidationResult.invalid(
TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE,
String.format(
"transaction up-front cost %s exceeds transaction sender account balance %s",
transaction.getUpfrontCost().toQuantityHexString(),
senderBalance.toQuantityHexString()));
upfrontCost.toQuantityHexString(), senderBalance.toQuantityHexString()));
}
if (transaction.getNonce() < senderNonce) {

@ -54,7 +54,8 @@ public class ProtocolSpecBuilder {
private DifficultyCalculator difficultyCalculator;
private EvmConfiguration evmConfiguration;
private BiFunction<GasCalculator, EvmConfiguration, EVM> evmBuilder;
private Function<GasCalculator, MainnetTransactionValidator> transactionValidatorBuilder;
private BiFunction<GasCalculator, GasLimitCalculator, MainnetTransactionValidator>
transactionValidatorBuilder;
private Function<FeeMarket, BlockHeaderValidator.Builder> blockHeaderValidatorBuilder;
private Function<FeeMarket, BlockHeaderValidator.Builder> ommerHeaderValidatorBuilder;
private Function<HeaderBasedProtocolSchedule, BlockBodyValidator> blockBodyValidatorBuilder;
@ -124,7 +125,8 @@ public class ProtocolSpecBuilder {
}
public ProtocolSpecBuilder transactionValidatorBuilder(
final Function<GasCalculator, MainnetTransactionValidator> transactionValidatorBuilder) {
final BiFunction<GasCalculator, GasLimitCalculator, MainnetTransactionValidator>
transactionValidatorBuilder) {
this.transactionValidatorBuilder = transactionValidatorBuilder;
return this;
}
@ -296,7 +298,7 @@ public class ProtocolSpecBuilder {
final PrecompiledContractConfiguration precompiledContractConfiguration =
new PrecompiledContractConfiguration(gasCalculator, privacyParameters);
final MainnetTransactionValidator transactionValidator =
transactionValidatorBuilder.apply(gasCalculator);
transactionValidatorBuilder.apply(gasCalculator, gasLimitCalculator);
final AbstractMessageProcessor contractCreationProcessor =
contractCreationProcessorBuilder.apply(gasCalculator, evm);
final PrecompileContractRegistry precompileContractRegistry =

@ -0,0 +1,72 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.mainnet.feemarket;
import static org.hyperledger.besu.util.Slf4jLambdaHelper.traceLambda;
import org.hyperledger.besu.datatypes.DataGas;
import org.hyperledger.besu.datatypes.Wei;
import java.math.BigInteger;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CancunFeeMarket extends LondonFeeMarket {
private static final Logger LOG = LoggerFactory.getLogger(CancunFeeMarket.class);
private static final BigInteger MIN_DATA_GAS_PRICE = BigInteger.ONE;
private static final BigInteger DATA_GAS_PRICE_UPDATE_FRACTION = BigInteger.valueOf(2225652);
public CancunFeeMarket(
final long londonForkBlockNumber, final Optional<Wei> baseFeePerGasOverride) {
super(londonForkBlockNumber, baseFeePerGasOverride);
}
@Override
public boolean implementsDataFee() {
return true;
}
@Override
public Wei dataPrice(final DataGas excessDataGas) {
final var dataGasPrice =
Wei.of(
fakeExponential(
MIN_DATA_GAS_PRICE, excessDataGas.toBigInteger(), DATA_GAS_PRICE_UPDATE_FRACTION));
traceLambda(
LOG,
"parentExcessDataGas: {} dataGasPrice: {}",
excessDataGas::toShortHexString,
dataGasPrice::toHexString);
return dataGasPrice;
}
private BigInteger fakeExponential(
final BigInteger factor, final BigInteger numerator, final BigInteger denominator) {
int i = 1;
BigInteger output = BigInteger.ZERO;
BigInteger numeratorAccumulator = factor.multiply(denominator);
while (numeratorAccumulator.signum() > 0) {
output = output.add(numeratorAccumulator);
numeratorAccumulator =
(numeratorAccumulator.multiply(numerator))
.divide(denominator.multiply(BigInteger.valueOf(i)));
++i;
}
return output.divide(denominator);
}
}

@ -14,6 +14,7 @@
*/
package org.hyperledger.besu.ethereum.mainnet.feemarket;
import org.hyperledger.besu.datatypes.DataGas;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.feemarket.TransactionPriceCalculator;
@ -26,6 +27,10 @@ public interface FeeMarket {
return false;
}
default boolean implementsDataFee() {
return false;
}
TransactionPriceCalculator getTransactionPriceCalculator();
boolean satisfiesFloorTxFee(Transaction txn);
@ -39,6 +44,11 @@ public interface FeeMarket {
return new LondonFeeMarket(londonForkBlockNumber, baseFeePerGasOverride);
}
static BaseFeeMarket cancun(
final long londonForkBlockNumber, final Optional<Wei> baseFeePerGasOverride) {
return new CancunFeeMarket(londonForkBlockNumber, baseFeePerGasOverride);
}
static BaseFeeMarket zeroBaseFee(final long londonForkBlockNumber) {
return new ZeroBaseFeeMarket(londonForkBlockNumber);
}
@ -46,4 +56,8 @@ public interface FeeMarket {
static FeeMarket legacy() {
return new LegacyFeeMarket();
}
default Wei dataPrice(final DataGas excessDataGas) {
return Wei.ZERO;
}
}

@ -26,12 +26,14 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LondonFeeMarket implements BaseFeeMarket {
private static final Logger LOG = LoggerFactory.getLogger(LondonFeeMarket.class);
static final Wei DEFAULT_BASEFEE_INITIAL_VALUE =
GenesisConfigFile.BASEFEE_AT_GENESIS_DEFAULT_VALUE;
static final long DEFAULT_BASEFEE_MAX_CHANGE_DENOMINATOR = 8L;
static final long DEFAULT_SLACK_COEFFICIENT = 2L;
private static final Wei DEFAULT_BASEFEE_FLOOR = Wei.of(7L);
private static final Logger LOG = LoggerFactory.getLogger(LondonFeeMarket.class);
private final Wei baseFeeInitialValue;
private final long londonForkBlockNumber;

@ -38,4 +38,9 @@ public class ZeroBaseFeeMarket extends LondonFeeMarket {
public ValidationMode baseFeeValidationMode(final long blockNumber) {
return ValidationMode.NONE;
}
@Override
public boolean implementsDataFee() {
return true;
}
}

@ -177,7 +177,8 @@ public class PrivateGroupRehydrationBlockProcessor {
miningBeneficiary,
blockHashLookup,
false,
TransactionValidationParams.processingBlock());
TransactionValidationParams.processingBlock(),
Wei.ZERO);
if (result.isInvalid()) {
return BlockProcessingResult.FAILED;
}

@ -109,7 +109,8 @@ public class PrivateMigrationBlockProcessor {
miningBeneficiary,
blockHashLookup,
true,
TransactionValidationParams.processingBlock());
TransactionValidationParams.processingBlock(),
Wei.ZERO);
if (result.isInvalid()) {
return BlockProcessingResult.FAILED;
}

@ -54,5 +54,6 @@ public enum TransactionInvalidReason {
ETHER_VALUE_NOT_SUPPORTED,
UPFRONT_FEE_TOO_HIGH,
NONCE_TOO_FAR_IN_FUTURE_FOR_SENDER,
LOWER_NONCE_INVALID_TRANSACTION_EXISTS
LOWER_NONCE_INVALID_TRANSACTION_EXISTS,
TOTAL_DATA_GAS_TOO_HIGH
}

@ -20,6 +20,7 @@ import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.DataGas;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.chain.Blockchain;
@ -222,6 +223,14 @@ public class TransactionSimulator {
return Optional.empty();
}
final Optional<BlockHeader> maybeParentHeader =
blockchain.getBlockHeader(blockHeaderToProcess.getParentHash());
final Wei dataGasPrice =
protocolSpec
.getFeeMarket()
.dataPrice(
maybeParentHeader.flatMap(BlockHeader::getExcessDataGas).orElse(DataGas.ZERO));
final Transaction transaction = maybeTransaction.get();
final TransactionProcessingResult result =
transactionProcessor.processTransaction(
@ -235,7 +244,8 @@ public class TransactionSimulator {
new BlockHashLookup(blockHeaderToProcess, blockchain),
false,
transactionValidationParams,
operationTracer);
operationTracer,
dataGasPrice);
// If GoQuorum privacy enabled, and value = zero, get max gas possible for a PMT hash.
// It is possible to have a data field that has a lower intrinsic value than the PMT hash.

@ -1,79 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.core;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.evm.AccessListEntry;
import java.math.BigInteger;
import java.util.List;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.Test;
public class TransactionEIP1559Test {
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
@Test
public void buildEip1559Transaction() {
final List<AccessListEntry> accessListEntries =
List.of(
new AccessListEntry(
Address.fromHexString("0x000000000000000000000000000000000000aaaa"),
List.of(Bytes32.ZERO)));
final Transaction tx =
Transaction.builder()
.chainId(new BigInteger("1559", 10))
.nonce(0)
.value(Wei.ZERO)
.gasLimit(30000)
.maxPriorityFeePerGas(Wei.of(2))
.payload(Bytes.EMPTY.trimLeadingZeros())
.maxFeePerGas(Wei.of(new BigInteger("5000000000", 10)))
.gasPrice(null)
.to(Address.fromHexString("0x000000000000000000000000000000000000aaaa"))
.accessList(accessListEntries)
.guessType()
.signAndBuild(
keyPair("0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63"));
final BytesValueRLPOutput out = new BytesValueRLPOutput();
tx.writeTo(out);
System.out.println(out.encoded().toHexString());
System.out.println(tx.getUpfrontCost());
// final String raw =
// "b8a902f8a686796f6c6f7632800285012a05f20082753094000000000000000000000000000000000000aaaa8080f838f794000000000000000000000000000000000000aaaae1a0000000000000000000000000000000000000000000000000000000000000000001a00c1d69648e348fe26155b45de45004f0e4195f6352d8f0935bc93e98a3e2a862a060064e5b9765c0ac74223b0cf49635c59ae0faf82044fd17bcc68a549ade6f95";
final String raw = out.encoded().toHexString();
final Transaction decoded = Transaction.readFrom(RLP.input(Bytes.fromHexString(raw)));
System.out.println(decoded);
System.out.println(decoded.getAccessList().orElseThrow().get(0).getAddressString());
System.out.println(decoded.getAccessList().orElseThrow().get(0).getStorageKeysString());
}
private static KeyPair keyPair(final String privateKey) {
final SignatureAlgorithm signatureAlgorithm = SIGNATURE_ALGORITHM.get();
return signatureAlgorithm.createKeyPair(
signatureAlgorithm.createPrivateKey(Bytes32.fromHexString(privateKey)));
}
}

@ -119,7 +119,8 @@ public class MainnetTransactionProcessorTest {
coinbaseAddress,
blockHashLookup,
false,
ImmutableTransactionValidationParams.builder().build());
ImmutableTransactionValidationParams.builder().build(),
Wei.ZERO);
assertThat(coinbaseWarmed).isTrue();
@ -132,7 +133,8 @@ public class MainnetTransactionProcessorTest {
coinbaseAddress,
blockHashLookup,
false,
ImmutableTransactionValidationParams.builder().build());
ImmutableTransactionValidationParams.builder().build(),
Wei.ZERO);
assertThat(coinbaseWarmed).isFalse();
}
@ -155,7 +157,8 @@ public class MainnetTransactionProcessorTest {
Address.fromHexString("1"),
blockHashLookup,
false,
ImmutableTransactionValidationParams.builder().build());
ImmutableTransactionValidationParams.builder().build(),
Wei.ZERO);
assertThat(txValidationParamCaptor.getValue())
.usingRecursiveComparison()

@ -33,6 +33,7 @@ import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionFilter;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
@ -78,7 +79,11 @@ public class MainnetTransactionValidatorTest {
public void shouldRejectTransactionIfIntrinsicGasExceedsGasLimit() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.empty(),
defaultGoQuorumCompatibilityMode);
final Transaction transaction =
new TransactionTestFixture()
.gasLimit(10)
@ -95,7 +100,11 @@ public class MainnetTransactionValidatorTest {
public void shouldRejectTransactionWhenTransactionHasChainIdAndValidatorDoesNot() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.empty(),
defaultGoQuorumCompatibilityMode);
assertThat(validator.validate(basicTransaction, Optional.empty(), transactionValidationParams))
.isEqualTo(
ValidationResult.invalid(
@ -107,6 +116,7 @@ public class MainnetTransactionValidatorTest {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.of(BigInteger.valueOf(2)),
defaultGoQuorumCompatibilityMode);
@ -118,7 +128,11 @@ public class MainnetTransactionValidatorTest {
public void shouldRejectTransactionWhenSenderAccountDoesNotExist() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.of(BigInteger.ONE), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.of(BigInteger.ONE),
defaultGoQuorumCompatibilityMode);
assertThat(validator.validateForSender(basicTransaction, null, false))
.isEqualTo(ValidationResult.invalid(TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE));
}
@ -127,7 +141,11 @@ public class MainnetTransactionValidatorTest {
public void shouldRejectTransactionWhenTransactionNonceBelowAccountNonce() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.of(BigInteger.ONE), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.of(BigInteger.ONE),
defaultGoQuorumCompatibilityMode);
final Account account = accountWithNonce(basicTransaction.getNonce() + 1);
assertThat(validator.validateForSender(basicTransaction, account, false))
@ -139,7 +157,11 @@ public class MainnetTransactionValidatorTest {
shouldRejectTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsNotAllowed() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.of(BigInteger.ONE), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.of(BigInteger.ONE),
defaultGoQuorumCompatibilityMode);
final Account account = accountWithNonce(basicTransaction.getNonce() - 1);
assertThat(validator.validateForSender(basicTransaction, account, false))
@ -151,7 +173,11 @@ public class MainnetTransactionValidatorTest {
shouldAcceptTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsAllowed() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.of(BigInteger.ONE), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.of(BigInteger.ONE),
defaultGoQuorumCompatibilityMode);
final Account account = accountWithNonce(basicTransaction.getNonce() - 1);
assertThat(validator.validateForSender(basicTransaction, account, true))
@ -162,7 +188,11 @@ public class MainnetTransactionValidatorTest {
public void shouldRejectTransactionWhenNonceExceedsMaximumAllowedNonce() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.of(BigInteger.ONE), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.of(BigInteger.ONE),
defaultGoQuorumCompatibilityMode);
final Transaction transaction =
new TransactionTestFixture().nonce(11).createTransaction(senderKeys);
@ -176,7 +206,11 @@ public class MainnetTransactionValidatorTest {
public void transactionWithNullSenderCanBeValidIfGasPriceAndValueIsZero() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.of(BigInteger.ONE), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.of(BigInteger.ONE),
defaultGoQuorumCompatibilityMode);
final TransactionTestFixture builder = new TransactionTestFixture();
final KeyPair senderKeyPair = SIGNATURE_ALGORITHM.get().generateKeyPair();
@ -191,11 +225,16 @@ public class MainnetTransactionValidatorTest {
public void shouldRejectTransactionIfAccountIsNotEOA() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.empty(),
defaultGoQuorumCompatibilityMode);
validator.setTransactionFilter(transactionFilter(false));
Account invalidEOA =
when(account(basicTransaction.getUpfrontCost(), basicTransaction.getNonce()).getCodeHash())
when(account(basicTransaction.getUpfrontCost(0L), basicTransaction.getNonce())
.getCodeHash())
.thenReturn(Hash.fromHexStringLenient("0xdeadbeef"))
.getMock();
@ -207,7 +246,11 @@ public class MainnetTransactionValidatorTest {
public void shouldRejectTransactionIfAccountIsNotPermitted() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.empty(),
defaultGoQuorumCompatibilityMode);
validator.setTransactionFilter(transactionFilter(false));
assertThat(validator.validateForSender(basicTransaction, accountWithNonce(0), true))
@ -218,7 +261,11 @@ public class MainnetTransactionValidatorTest {
public void shouldAcceptValidTransactionIfAccountIsPermitted() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.empty(),
defaultGoQuorumCompatibilityMode);
validator.setTransactionFilter(transactionFilter(true));
assertThat(validator.validateForSender(basicTransaction, accountWithNonce(0), true))
@ -229,7 +276,11 @@ public class MainnetTransactionValidatorTest {
public void shouldRejectTransactionWithMaxFeeTimesGasLimitGreaterThanBalance() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.empty(),
defaultGoQuorumCompatibilityMode);
validator.setTransactionFilter(transactionFilter(true));
assertThat(
@ -255,6 +306,7 @@ public class MainnetTransactionValidatorTest {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L),
false,
Optional.of(BigInteger.ONE),
@ -302,7 +354,11 @@ public class MainnetTransactionValidatorTest {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.empty(),
defaultGoQuorumCompatibilityMode);
validator.setTransactionFilter(transactionFilter);
final TransactionValidationParams validationParams =
@ -320,7 +376,11 @@ public class MainnetTransactionValidatorTest {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode);
gasCalculator,
GasLimitCalculator.constant(),
false,
Optional.empty(),
defaultGoQuorumCompatibilityMode);
validator.setTransactionFilter(transactionFilter);
final TransactionValidationParams validationParams =
@ -342,6 +402,7 @@ public class MainnetTransactionValidatorTest {
final MainnetTransactionValidator frontierValidator =
new MainnetTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.legacy(),
false,
Optional.of(BigInteger.ONE),
@ -352,6 +413,7 @@ public class MainnetTransactionValidatorTest {
final MainnetTransactionValidator eip1559Validator =
new MainnetTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L),
false,
Optional.of(BigInteger.ONE),
@ -385,6 +447,7 @@ public class MainnetTransactionValidatorTest {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L),
false,
Optional.of(BigInteger.ONE),
@ -409,6 +472,7 @@ public class MainnetTransactionValidatorTest {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L, zeroBaseFee),
false,
Optional.of(BigInteger.ONE),
@ -432,6 +496,7 @@ public class MainnetTransactionValidatorTest {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L),
false,
Optional.of(BigInteger.ONE),
@ -457,6 +522,7 @@ public class MainnetTransactionValidatorTest {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L),
false,
Optional.of(BigInteger.ONE),
@ -483,6 +549,7 @@ public class MainnetTransactionValidatorTest {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L),
false,
Optional.of(BigInteger.ONE),
@ -508,7 +575,8 @@ public class MainnetTransactionValidatorTest {
@Test
public void goQuorumCompatibilityModeRejectNonZeroGasPrice() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(gasCalculator, false, Optional.empty(), true);
new MainnetTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty(), true);
final Transaction transaction =
new TransactionTestFixture()
.gasPrice(Wei.ONE)
@ -530,7 +598,8 @@ public class MainnetTransactionValidatorTest {
@Test
public void goQuorumCompatibilityModeSuccessZeroGasPrice() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(gasCalculator, false, Optional.empty(), true);
new MainnetTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty(), true);
final Transaction transaction =
new TransactionTestFixture()
.gasPrice(Wei.ZERO)
@ -547,7 +616,7 @@ public class MainnetTransactionValidatorTest {
}
private Account accountWithNonce(final long nonce) {
return account(basicTransaction.getUpfrontCost(), nonce);
return account(basicTransaction.getUpfrontCost(0L), nonce);
}
private Account account(final Wei balance, final long nonce) {

@ -202,7 +202,7 @@ public class PrivacyBlockProcessorTest {
final MainnetTransactionProcessor mockPublicTransactionProcessor =
mock(MainnetTransactionProcessor.class);
when(mockPublicTransactionProcessor.processTransaction(
any(), any(), any(), any(), any(), any(), anyBoolean(), any()))
any(), any(), any(), any(), any(), any(), anyBoolean(), any(), any()))
.thenReturn(
TransactionProcessingResult.successful(
Collections.emptyList(), 0, 0, Bytes.EMPTY, ValidationResult.valid()));

@ -67,6 +67,7 @@ public class ZeroBaseFeeMarketTest {
.maxPriorityFeePerGas(Optional.of(Wei.of(8)))
.gasPrice(null)
.createTransaction(KEY_PAIR1);
assertThat(
zeroBaseFeeMarket
.getTransactionPriceCalculator()

@ -39,6 +39,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionValidator;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult.Status;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
@ -539,6 +540,7 @@ public class TransactionSimulatorTest {
when(protocolSpec.getTransactionProcessor()).thenReturn(transactionProcessor);
when(protocolSpec.getMiningBeneficiaryCalculator()).thenReturn(BlockHeader::getCoinbase);
when(protocolSpec.getBlockHeaderFunctions()).thenReturn(blockHeaderFunctions);
when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0));
final TransactionProcessingResult result = mock(TransactionProcessingResult.class);
switch (status) {
@ -551,14 +553,32 @@ public class TransactionSimulatorTest {
break;
}
when(transactionProcessor.processTransaction(
any(), any(), any(), eq(transaction), any(), any(), anyBoolean(), any(), any()))
any(),
any(),
any(),
eq(transaction),
any(),
any(),
anyBoolean(),
any(),
any(),
any(Wei.class)))
.thenReturn(result);
}
private void verifyTransactionWasProcessed(final Transaction expectedTransaction) {
verify(transactionProcessor)
.processTransaction(
any(), any(), any(), eq(expectedTransaction), any(), any(), anyBoolean(), any(), any());
any(),
any(),
any(),
eq(expectedTransaction),
any(),
any(),
anyBoolean(),
any(),
any(),
any(Wei.class));
}
private CallParameter legacyTransactionCallParameter() {

@ -17,7 +17,6 @@ package org.hyperledger.besu.ethereum.eth.transactions;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.feemarket.TransactionPriceCalculator;
import org.hyperledger.besu.plugin.data.TransactionType;
import org.hyperledger.besu.util.number.Percentage;
import java.util.Optional;
@ -38,26 +37,26 @@ public class TransactionReplacementByFeeMarketRule implements TransactionPoolRep
public boolean shouldReplace(
final PendingTransaction existingPendingTransaction,
final PendingTransaction newPendingTransaction,
final Optional<Wei> baseFee) {
final Optional<Wei> maybeBaseFee) {
// bail early if basefee is absent or neither transaction supports 1559 fee market
if (baseFee.isEmpty()
if (maybeBaseFee.isEmpty()
|| !(isNotGasPriced(existingPendingTransaction) || isNotGasPriced(newPendingTransaction))) {
return false;
}
Wei newEffPrice = priceOf(newPendingTransaction.getTransaction(), baseFee);
Wei newEffPrice = priceOf(newPendingTransaction.getTransaction(), maybeBaseFee);
Wei newEffPriority =
newPendingTransaction.getTransaction().getEffectivePriorityFeePerGas(baseFee);
newPendingTransaction.getTransaction().getEffectivePriorityFeePerGas(maybeBaseFee);
// bail early if price is not strictly positive
if (newEffPrice.equals(Wei.ZERO)) {
return false;
}
Wei curEffPrice = priceOf(existingPendingTransaction.getTransaction(), baseFee);
Wei curEffPrice = priceOf(existingPendingTransaction.getTransaction(), maybeBaseFee);
Wei curEffPriority =
existingPendingTransaction.getTransaction().getEffectivePriorityFeePerGas(baseFee);
existingPendingTransaction.getTransaction().getEffectivePriorityFeePerGas(maybeBaseFee);
if (isBumpedBy(curEffPrice, newEffPrice, priceBump)) {
// if effective price is bumped by percent:
@ -71,12 +70,10 @@ public class TransactionReplacementByFeeMarketRule implements TransactionPoolRep
return false;
}
private Wei priceOf(final Transaction transaction, final Optional<Wei> baseFee) {
private Wei priceOf(final Transaction transaction, final Optional<Wei> maybeBaseFee) {
final TransactionPriceCalculator transactionPriceCalculator =
transaction.getType().equals(TransactionType.EIP1559)
? EIP1559_CALCULATOR
: FRONTIER_CALCULATOR;
return transactionPriceCalculator.price(transaction, baseFee);
transaction.getType().supports1559FeeMarket() ? EIP1559_CALCULATOR : FRONTIER_CALCULATOR;
return transactionPriceCalculator.price(transaction, maybeBaseFee);
}
private boolean isBumpedBy(final Wei val, final Wei bumpVal, final Percentage percent) {

@ -20,7 +20,9 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import static org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules.shouldClearEmptyAccounts;
import static org.hyperledger.besu.evmtool.StateTestSubCommand.COMMAND_NAME;
import org.hyperledger.besu.datatypes.DataGas;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Transaction;
@ -210,12 +212,14 @@ public class StateTestSubCommand implements Runnable {
throw new UnsupportedForkException(forkName);
}
ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(blockHeader);
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(blockHeader);
final MainnetTransactionProcessor processor = protocolSpec.getTransactionProcessor();
final WorldUpdater worldStateUpdater = worldState.updater();
final ReferenceTestBlockchain blockchain =
new ReferenceTestBlockchain(blockHeader.getNumber());
final Stopwatch timer = Stopwatch.createStarted();
// Todo: EIP-4844 use the excessDataGas of the parent instead of DataGas.ZERO
final Wei dataGasPrice = protocolSpec.getFeeMarket().dataPrice(DataGas.ZERO);
final TransactionProcessingResult result =
processor.processTransaction(
blockchain,
@ -226,7 +230,8 @@ public class StateTestSubCommand implements Runnable {
new BlockHashLookup(blockHeader, blockchain),
false,
TransactionValidationParams.processingBlock(),
tracer);
tracer,
dataGasPrice);
timer.stop();
if (shouldClearEmptyAccounts(spec.getFork())) {
final Account coinbase =

@ -25,6 +25,7 @@ import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.DataGas;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -274,6 +275,9 @@ public class T8nSubCommand implements Runnable {
List<Transaction> validTransactions = new ArrayList<>();
ArrayNode receiptsArray = objectMapper.createArrayNode();
long gasUsed = 0;
// Todo: EIP-4844 use the excessDataGas of the parent instead of DataGas.ZERO
final Wei dataGasPrice = protocolSpec.getFeeMarket().dataPrice(DataGas.ZERO);
for (int i = 0; i < transactions.size(); i++) {
Transaction transaction = transactions.get(i);
@ -300,6 +304,7 @@ public class T8nSubCommand implements Runnable {
} else {
tracer = OperationTracer.NO_TRACING;
}
result =
processor.processTransaction(
blockchain,
@ -310,7 +315,8 @@ public class T8nSubCommand implements Runnable {
new BlockHashLookup(referenceTestEnv, blockchain),
false,
TransactionValidationParams.processingBlock(),
tracer);
tracer,
dataGasPrice);
} catch (IOException e) {
throw new RuntimeException(e);
}

@ -16,12 +16,15 @@ package org.hyperledger.besu.ethereum.vm;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.DataGas;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.referencetests.GeneralStateTestCaseEipSpec;
@ -49,12 +52,16 @@ public class GeneralStateReferenceTestTools {
Arrays.asList("Frontier", "Homestead", "EIP150");
private static MainnetTransactionProcessor transactionProcessor(final String name) {
return REFERENCE_TEST_PROTOCOL_SCHEDULES
.getByName(name)
.getByBlockHeader(BlockHeaderBuilder.createDefault().buildBlockHeader())
return protocolSpec(name)
.getTransactionProcessor();
}
private static ProtocolSpec protocolSpec(final String name) {
return REFERENCE_TEST_PROTOCOL_SCHEDULES
.getByName(name)
.getByBlockHeader(BlockHeaderBuilder.createDefault().buildBlockHeader());
}
private static final List<String> EIPS_TO_RUN;
static {
@ -138,6 +145,8 @@ public class GeneralStateReferenceTestTools {
final MainnetTransactionProcessor processor = transactionProcessor(spec.getFork());
final WorldUpdater worldStateUpdater = worldState.updater();
final ReferenceTestBlockchain blockchain = new ReferenceTestBlockchain(blockHeader.getNumber());
// Todo: EIP-4844 use the excessDataGas of the parent instead of DataGas.ZERO
final Wei dataGasPrice = protocolSpec(spec.getFork()).getFeeMarket().dataPrice(DataGas.ZERO);
final TransactionProcessingResult result =
processor.processTransaction(
blockchain,
@ -147,7 +156,8 @@ public class GeneralStateReferenceTestTools {
blockHeader.getCoinbase(),
new BlockHashLookup(blockHeader, blockchain),
false,
TransactionValidationParams.processingBlock());
TransactionValidationParams.processingBlock(),
dataGasPrice);
if (result.isInvalid()) {
assertThat(spec.getExpectException()).isNotNull();
return;

@ -19,6 +19,7 @@ package org.hyperledger.besu.evm.gascalculator;
*
* <UL>
* <LI>Gas costs for TSTORE/TLOAD
* <LI>Data gas for EIP-4844
* </UL>
*/
public class CancunGasCalculator extends LondonGasCalculator {
@ -26,6 +27,9 @@ public class CancunGasCalculator extends LondonGasCalculator {
private static final long TLOAD_GAS = WARM_STORAGE_READ_COST;
private static final long TSTORE_GAS = WARM_STORAGE_READ_COST;
private static final long DATA_GAS_PER_BLOB = 1 << 17;
private static final long TARGET_DATA_GAS_PER_BLOCK = 1 << 18;
// EIP-1153
@Override
public long getTransientLoadOperationGasCost() {
@ -36,4 +40,20 @@ public class CancunGasCalculator extends LondonGasCalculator {
public long getTransientStoreOperationGasCost() {
return TSTORE_GAS;
}
@Override
public long dataGasCost(final int blobCount) {
return DATA_GAS_PER_BLOB * blobCount;
}
@Override
public long computeExcessDataGas(final long parentExcessDataGas, final int newBlobs) {
final long consumedDataGas = dataGasCost(newBlobs);
final long currentExcessDataGas = parentExcessDataGas + consumedDataGas;
if (currentExcessDataGas < TARGET_DATA_GAS_PER_BLOCK) {
return 0L;
}
return currentExcessDataGas - TARGET_DATA_GAS_PER_BLOCK;
}
}

@ -438,7 +438,7 @@ public interface GasCalculator {
long codeDepositGasCost(int codeSize);
/**
* Returns the intrinsic gas cost of a transaction pauload, i.e. the cost deriving from its
* Returns the intrinsic gas cost of a transaction payload, i.e. the cost deriving from its
* encoded binary representation when stored on-chain.
*
* @param transactionPayload The encoded transaction, as bytes
@ -505,4 +505,26 @@ public interface GasCalculator {
default long getTransientStoreOperationGasCost() {
return 0L;
}
/**
* Return the gas cost given the number of blobs
*
* @param blobCount the number of blobs
* @return the total gas cost
*/
default long dataGasCost(final int blobCount) {
return 0L;
}
/**
* Compute the new value for the excess data gas, given the parent value and the count of new
* blobs
*
* @param parentExcessDataGas excess data gas from the parent
* @param newBlobs count of new blobs
* @return the new excess data gas value
*/
default long computeExcessDataGas(final long parentExcessDataGas, final int newBlobs) {
return 0L;
}
}

Loading…
Cancel
Save