Support rpc pending block tag when estimating gas (#7951)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
This commit is contained in:
Fabio Di Fabio
2024-12-06 13:50:38 +01:00
committed by GitHub
parent a3592a71b1
commit f8e93bf544
30 changed files with 621 additions and 361 deletions

View File

@@ -16,6 +16,7 @@
- Fast Sync
### Additions and Improvements
- Proper support for `pending` block tag when calling `eth_estimateGas` and `eth_createAccessList` [#7951](https://github.com/hyperledger/besu/pull/7951)
### Bug fixes
- Correct default parameters for frontier transactions in `eth_call` and `eth_estimateGas` [#7965](https://github.com/hyperledger/besu/pull/7965)
@@ -68,7 +69,6 @@
- Add histogram to Prometheus metrics system [#7944](https://github.com/hyperledger/besu/pull/7944)
- Improve newPayload and FCU logs [#7961](https://github.com/hyperledger/besu/pull/7961)
### Bug fixes
- Fix registering new metric categories from plugins [#7825](https://github.com/hyperledger/besu/pull/7825)
- Fix CVE-2024-47535 [7878](https://github.com/hyperledger/besu/pull/7878)

View File

@@ -396,9 +396,14 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final ProtocolSchedule protocolSchedule,
final MiningConfiguration miningConfiguration,
final ApiConfiguration apiConfiguration) {
return new TransactionSimulator(
blockchain, worldStateArchive, protocolSchedule, apiConfiguration.getGasCap());
blockchain,
worldStateArchive,
protocolSchedule,
miningConfiguration,
apiConfiguration.getGasCap());
}
@Provides

View File

@@ -626,7 +626,11 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
transactionSimulator =
new TransactionSimulator(
blockchain, worldStateArchive, protocolSchedule, apiConfiguration.getGasCap());
blockchain,
worldStateArchive,
protocolSchedule,
miningConfiguration,
apiConfiguration.getGasCap());
final var consensusContext =
createConsensusContext(blockchain, worldStateArchive, protocolSchedule);

View File

@@ -18,7 +18,6 @@ import org.hyperledger.besu.datatypes.AccountOverrideMap;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
@@ -35,11 +34,6 @@ import java.util.Optional;
/** TransactionSimulationServiceImpl */
@Unstable
public class TransactionSimulationServiceImpl implements TransactionSimulationService {
private static final TransactionValidationParams SIMULATOR_ALLOWING_EXCEEDING_BALANCE =
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowExceedingBalance(true)
.build();
private Blockchain blockchain;
private TransactionSimulator transactionSimulator;
@@ -57,46 +51,50 @@ public class TransactionSimulationServiceImpl implements TransactionSimulationSe
this.transactionSimulator = transactionSimulator;
}
@Override
public Optional<TransactionSimulationResult> simulate(
final Transaction transaction,
final Hash blockHash,
final OperationTracer operationTracer,
final boolean isAllowExceedingBalance) {
return simulate(
transaction, Optional.empty(), blockHash, operationTracer, isAllowExceedingBalance);
}
@Override
public Optional<TransactionSimulationResult> simulate(
final Transaction transaction,
final Optional<AccountOverrideMap> maybeAccountOverrides,
final Hash blockHash,
final Optional<Hash> maybeBlockHash,
final OperationTracer operationTracer,
final boolean isAllowExceedingBalance) {
final CallParameter callParameter = CallParameter.fromTransaction(transaction);
final var maybeBlockHeader =
blockchain.getBlockHeader(blockHash).or(() -> blockchain.getBlockHeaderSafe(blockHash));
if (maybeBlockHash.isPresent()) {
final Hash blockHash = maybeBlockHash.get();
if (maybeBlockHeader.isEmpty()) {
return Optional.of(
new TransactionSimulationResult(
transaction,
TransactionProcessingResult.invalid(
ValidationResult.invalid(TransactionInvalidReason.BLOCK_NOT_FOUND))));
final var maybeBlockHeader =
blockchain.getBlockHeader(blockHash).or(() -> blockchain.getBlockHeaderSafe(blockHash));
if (maybeBlockHeader.isEmpty()) {
return Optional.of(
new TransactionSimulationResult(
transaction,
TransactionProcessingResult.invalid(
ValidationResult.invalid(TransactionInvalidReason.BLOCK_NOT_FOUND))));
}
return transactionSimulator
.process(
callParameter,
isAllowExceedingBalance
? TransactionValidationParams.transactionSimulatorAllowExceedingBalance()
: TransactionValidationParams.transactionSimulator(),
operationTracer,
maybeBlockHeader.get())
.map(res -> new TransactionSimulationResult(transaction, res.result()));
}
return transactionSimulator
.process(
.processOnPending(
callParameter,
maybeAccountOverrides,
isAllowExceedingBalance
? SIMULATOR_ALLOWING_EXCEEDING_BALANCE
? TransactionValidationParams.transactionSimulatorAllowExceedingBalance()
: TransactionValidationParams.transactionSimulator(),
operationTracer,
maybeBlockHeader.get())
transactionSimulator.simulatePendingBlockHeader())
.map(res -> new TransactionSimulationResult(transaction, res.result()));
}
}

View File

@@ -154,7 +154,7 @@ public class CliqueMinerExecutorTest {
}
@Test
public void extraDataForNonEpochBlocksDoesNotContainValidaors() {
public void extraDataForNonEpochBlocksDoesNotContainValidators() {
final Bytes vanityData = generateRandomVanityData();
final MiningConfiguration miningConfiguration = createMiningConfiguration(vanityData);

View File

@@ -387,7 +387,7 @@ public class TestContextBuilder {
final boolean useFixedBaseFee,
final List<QbftFork> qbftForks) {
final MiningConfiguration miningParams =
final MiningConfiguration miningConfiguration =
ImmutableMiningConfiguration.builder()
.mutableInitValues(
MutableInitValues.builder()
@@ -445,7 +445,8 @@ public class TestContextBuilder {
final BftValidatorOverrides validatorOverrides = convertBftForks(qbftForks);
final TransactionSimulator transactionSimulator =
new TransactionSimulator(blockChain, worldStateArchive, protocolSchedule, 0L);
new TransactionSimulator(
blockChain, worldStateArchive, protocolSchedule, miningConfiguration, 0L);
final BlockValidatorProvider blockValidatorProvider =
BlockValidatorProvider.forkingValidatorProvider(
@@ -496,7 +497,7 @@ public class TestContextBuilder {
protocolContext,
protocolSchedule,
forksSchedule,
miningParams,
miningConfiguration,
localAddress,
BFT_EXTRA_DATA_ENCODER,
ethScheduler);

View File

@@ -15,7 +15,6 @@
package org.hyperledger.besu.consensus.qbft.validator;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.transaction.CallParameter;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
@@ -94,10 +93,7 @@ public class ValidatorContractController {
final CallParameter callParams =
new CallParameter(null, contractAddress, -1, null, null, payload);
final TransactionValidationParams transactionValidationParams =
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowExceedingBalance(true)
.build();
TransactionValidationParams.transactionSimulatorAllowExceedingBalance();
return transactionSimulator.process(
callParams, transactionValidationParams, OperationTracer.NO_TRACING, blockNumber);
}

View File

@@ -97,12 +97,13 @@ public class JsonRpcTestMethodsFactory {
final BlockImporter blockImporter = protocolSpec.getBlockImporter();
blockImporter.importBlock(context, block, HeaderValidationMode.FULL);
}
final var miningConfiguration = MiningConfiguration.newDefault();
this.blockchainQueries =
new BlockchainQueries(
protocolSchedule, blockchain, stateArchive, MiningConfiguration.newDefault());
new BlockchainQueries(protocolSchedule, blockchain, stateArchive, miningConfiguration);
this.transactionSimulator =
new TransactionSimulator(blockchain, stateArchive, protocolSchedule, 0L);
new TransactionSimulator(
blockchain, stateArchive, protocolSchedule, miningConfiguration, 0L);
}
public JsonRpcTestMethodsFactory(
@@ -115,15 +116,14 @@ public class JsonRpcTestMethodsFactory {
this.stateArchive = stateArchive;
this.context = context;
this.protocolSchedule = importer.getProtocolSchedule();
final var miningConfiguration = MiningConfiguration.newDefault();
this.blockchainQueries =
new BlockchainQueries(
importer.getProtocolSchedule(),
blockchain,
stateArchive,
MiningConfiguration.newDefault());
importer.getProtocolSchedule(), blockchain, stateArchive, miningConfiguration);
this.synchronizer = mock(Synchronizer.class);
this.transactionSimulator =
new TransactionSimulator(blockchain, stateArchive, protocolSchedule, 0L);
new TransactionSimulator(
blockchain, stateArchive, protocolSchedule, miningConfiguration, 0L);
}
public JsonRpcTestMethodsFactory(
@@ -138,14 +138,13 @@ public class JsonRpcTestMethodsFactory {
this.context = context;
this.synchronizer = synchronizer;
this.protocolSchedule = importer.getProtocolSchedule();
final var miningConfiguration = MiningConfiguration.newDefault();
this.blockchainQueries =
new BlockchainQueries(
importer.getProtocolSchedule(),
blockchain,
stateArchive,
MiningConfiguration.newDefault());
importer.getProtocolSchedule(), blockchain, stateArchive, miningConfiguration);
this.transactionSimulator =
new TransactionSimulator(blockchain, stateArchive, protocolSchedule, 0L);
new TransactionSimulator(
blockchain, stateArchive, protocolSchedule, miningConfiguration, 0L);
}
public BlockchainQueries getBlockchainQueries() {

View File

@@ -26,7 +26,6 @@ import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.LogWithMetadata;
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
@@ -359,14 +358,9 @@ public class BlockAdapterBase extends AdapterBase {
data,
Optional.empty());
ImmutableTransactionValidationParams.Builder transactionValidationParams =
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator());
transactionValidationParams.isAllowExceedingBalance(true);
return transactionSimulator.process(
param,
transactionValidationParams.build(),
TransactionValidationParams.transactionSimulatorAllowExceedingBalance(),
OperationTracer.NO_TRACING,
(mutableWorldState, transactionSimulatorResult) ->
transactionSimulatorResult.map(

View File

@@ -16,10 +16,12 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonCallParameterUtil.validateAndGetCallParams;
import org.hyperledger.besu.datatypes.AccountOverrideMap;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter;
@@ -28,6 +30,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorR
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.transaction.CallParameter;
@@ -35,9 +38,12 @@ import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import java.util.Optional;
import com.google.common.annotations.VisibleForTesting;
public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod {
private static final double SUB_CALL_REMAINING_GAS_RATIO = 65D / 64D;
@@ -60,27 +66,24 @@ public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod {
}
}
protected Optional<BlockHeader> blockHeader(final long blockNumber) {
if (getBlockchainQueries().headBlockNumber() == blockNumber) {
// chain head header if cached, and we can return it form memory
return Optional.of(getBlockchainQueries().getBlockchain().getChainHeadHeader());
}
return getBlockchainQueries().getBlockHeaderByNumber(blockNumber);
}
protected abstract Object simulate(
final JsonRpcRequestContext requestContext,
final CallParameter callParams,
final long gasLimit,
final TransactionSimulationFunction simulationFunction);
protected Optional<RpcErrorType> validateBlockHeader(
final Optional<BlockHeader> maybeBlockHeader) {
if (maybeBlockHeader.isEmpty()) {
return Optional.of(RpcErrorType.BLOCK_NOT_FOUND);
}
final var blockHeader = maybeBlockHeader.get();
if (!getBlockchainQueries()
.getWorldStateArchive()
.isWorldStateAvailable(blockHeader.getStateRoot(), blockHeader.getHash())) {
return Optional.of(RpcErrorType.WORLD_STATE_UNAVAILABLE);
}
return Optional.empty();
@Override
protected Object pendingResult(final JsonRpcRequestContext requestContext) {
final JsonCallParameter jsonCallParameter = validateAndGetCallParams(requestContext);
final var validationParams = getTransactionValidationParams(jsonCallParameter);
final var maybeStateOverrides = getAddressAccountOverrideMap(requestContext);
final var pendingBlockHeader = transactionSimulator.simulatePendingBlockHeader();
final TransactionSimulationFunction simulationFunction =
(cp, op) ->
transactionSimulator.processOnPending(
cp, maybeStateOverrides, validationParams, op, pendingBlockHeader);
return simulate(
requestContext, jsonCallParameter, pendingBlockHeader.getGasLimit(), simulationFunction);
}
@Override
@@ -95,13 +98,43 @@ public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod {
return resultByBlockHeader(requestContext, jsonCallParameter, maybeBlockHeader.get());
}
protected abstract Object resultByBlockHeader(
private Object resultByBlockHeader(
final JsonRpcRequestContext requestContext,
final JsonCallParameter jsonCallParameter,
final BlockHeader blockHeader);
final BlockHeader blockHeader) {
final var validationParams = getTransactionValidationParams(jsonCallParameter);
final var maybeStateOverrides = getAddressAccountOverrideMap(requestContext);
final TransactionSimulationFunction simulationFunction =
(cp, op) ->
transactionSimulator.process(
cp, maybeStateOverrides, validationParams, op, blockHeader);
return simulate(
requestContext, jsonCallParameter, blockHeader.getGasLimit(), simulationFunction);
}
protected CallParameter overrideGasLimitAndPrice(
final JsonCallParameter callParams, final long gasLimit) {
private Optional<BlockHeader> blockHeader(final long blockNumber) {
if (getBlockchainQueries().headBlockNumber() == blockNumber) {
// chain head header if cached, and we can return it form memory
return Optional.of(getBlockchainQueries().getBlockchain().getChainHeadHeader());
}
return getBlockchainQueries().getBlockHeaderByNumber(blockNumber);
}
private Optional<RpcErrorType> validateBlockHeader(final Optional<BlockHeader> maybeBlockHeader) {
if (maybeBlockHeader.isEmpty()) {
return Optional.of(RpcErrorType.BLOCK_NOT_FOUND);
}
final var blockHeader = maybeBlockHeader.get();
if (!getBlockchainQueries()
.getWorldStateArchive()
.isWorldStateAvailable(blockHeader.getStateRoot(), blockHeader.getHash())) {
return Optional.of(RpcErrorType.WORLD_STATE_UNAVAILABLE);
}
return Optional.empty();
}
protected CallParameter overrideGasLimit(final CallParameter callParams, final long gasLimit) {
return new CallParameter(
callParams.getChainId(),
callParams.getFrom(),
@@ -142,7 +175,7 @@ public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod {
final ValidationResult<TransactionInvalidReason> validationResult =
result.getValidationResult();
if (validationResult != null && !validationResult.isValid()) {
if (validationResult.getErrorMessage().length() > 0) {
if (!validationResult.getErrorMessage().isEmpty()) {
return errorResponse(request, JsonRpcError.from(validationResult));
}
return errorResponse(
@@ -170,4 +203,29 @@ public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod {
final JsonRpcRequestContext request, final JsonRpcError jsonRpcError) {
return new JsonRpcErrorResponse(request.getRequest().getId(), jsonRpcError);
}
protected static TransactionValidationParams getTransactionValidationParams(
final JsonCallParameter callParams) {
final boolean isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE);
return isAllowExceedingBalance
? TransactionValidationParams.transactionSimulatorAllowExceedingBalance()
: TransactionValidationParams.transactionSimulator();
}
@VisibleForTesting
protected Optional<AccountOverrideMap> getAddressAccountOverrideMap(
final JsonRpcRequestContext request) {
try {
return request.getOptionalParameter(2, AccountOverrideMap.class);
} catch (JsonRpcParameter.JsonRpcParameterException e) {
throw new InvalidJsonRpcRequestException(
"Invalid account overrides parameter (index 2)", RpcErrorType.INVALID_CALL_PARAMS, e);
}
}
protected interface TransactionSimulationFunction {
Optional<TransactionSimulatorResult> simulate(
CallParameter callParams, OperationTracer operationTracer);
}
}

View File

@@ -33,7 +33,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.vm.VmT
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.debug.TraceOptions;
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
@@ -117,9 +116,7 @@ public abstract class AbstractTraceByBlock extends AbstractBlockParameterMethod
}
protected TransactionValidationParams buildTransactionValidationParams() {
return ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.build();
return TransactionValidationParams.transactionSimulator();
}
protected TraceOptions buildTraceOptions(final Set<TraceTypeParameter.TraceType> traceTypes) {

View File

@@ -35,7 +35,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSucces
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
@@ -169,20 +168,18 @@ public class EthCall extends AbstractBlockParameterOrBlockHashMethod {
private TransactionValidationParams buildTransactionValidationParams(
final BlockHeader header, final JsonCallParameter callParams) {
ImmutableTransactionValidationParams.Builder transactionValidationParams =
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator());
final boolean isAllowExceedingBalance;
// if it is not set explicitly whether we want a strict check of the balance or not. this will
// be decided according to the provided parameters
if (callParams.isMaybeStrict().isEmpty()) {
transactionValidationParams.isAllowExceedingBalance(
isAllowExceedingBalanceAutoSelection(header, callParams));
isAllowExceedingBalance = isAllowExceedingBalanceAutoSelection(header, callParams);
} else {
transactionValidationParams.isAllowExceedingBalance(
!callParams.isMaybeStrict().orElse(Boolean.FALSE));
isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE);
}
return transactionValidationParams.build();
return isAllowExceedingBalance
? TransactionValidationParams.transactionSimulatorAllowExceedingBalance()
: TransactionValidationParams.transactionSimulator();
}
private boolean isAllowExceedingBalanceAutoSelection(

View File

@@ -18,13 +18,9 @@ import org.hyperledger.besu.datatypes.AccessListEntry;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.CreateAccessListResult;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.transaction.CallParameter;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
@@ -48,20 +44,24 @@ public class EthCreateAccessList extends AbstractEstimateGas {
}
@Override
protected Object resultByBlockHeader(
protected Object simulate(
final JsonRpcRequestContext requestContext,
final JsonCallParameter jsonCallParameter,
final BlockHeader blockHeader) {
final AccessListSimulatorResult maybeResult =
processTransaction(jsonCallParameter, blockHeader);
final CallParameter callParams,
final long gasLimit,
final TransactionSimulationFunction simulationFunction) {
final AccessListOperationTracer tracer = AccessListOperationTracer.create();
final Optional<TransactionSimulatorResult> firstResult =
simulationFunction.simulate(overrideGasLimit(callParams, gasLimit), tracer);
// if the call accessList is different from the simulation result, calculate gas and return
if (shouldProcessWithAccessListOverride(jsonCallParameter, maybeResult.tracer())) {
if (shouldProcessWithAccessListOverride(callParams, tracer)) {
final AccessListSimulatorResult result =
processTransactionWithAccessListOverride(
jsonCallParameter, blockHeader, maybeResult.tracer().getAccessList());
callParams, gasLimit, tracer.getAccessList(), simulationFunction);
return createResponse(requestContext, result);
} else {
return createResponse(requestContext, maybeResult);
return createResponse(requestContext, new AccessListSimulatorResult(firstResult, tracer));
}
}
@@ -73,16 +73,8 @@ public class EthCreateAccessList extends AbstractEstimateGas {
.orElseGet(() -> errorResponse(requestContext, RpcErrorType.INTERNAL_ERROR));
}
private TransactionValidationParams transactionValidationParams(
final boolean isAllowExceedingBalance) {
return ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowExceedingBalance(isAllowExceedingBalance)
.build();
}
private boolean shouldProcessWithAccessListOverride(
final JsonCallParameter parameters, final AccessListOperationTracer tracer) {
final CallParameter parameters, final AccessListOperationTracer tracer) {
// if empty, transaction did not access any storage, does not need to reprocess
if (tracer.getAccessList().isEmpty()) {
@@ -107,39 +99,23 @@ public class EthCreateAccessList extends AbstractEstimateGas {
: errorResponse(request, result);
}
private AccessListSimulatorResult processTransaction(
final JsonCallParameter jsonCallParameter, final BlockHeader blockHeader) {
final TransactionValidationParams transactionValidationParams =
transactionValidationParams(!jsonCallParameter.isMaybeStrict().orElse(Boolean.FALSE));
final CallParameter callParams =
overrideGasLimitAndPrice(jsonCallParameter, blockHeader.getGasLimit());
final AccessListOperationTracer tracer = AccessListOperationTracer.create();
final Optional<TransactionSimulatorResult> result =
transactionSimulator.process(callParams, transactionValidationParams, tracer, blockHeader);
return new AccessListSimulatorResult(result, tracer);
}
private AccessListSimulatorResult processTransactionWithAccessListOverride(
final JsonCallParameter jsonCallParameter,
final BlockHeader blockHeader,
final List<AccessListEntry> accessList) {
final TransactionValidationParams transactionValidationParams =
transactionValidationParams(!jsonCallParameter.isMaybeStrict().orElse(Boolean.FALSE));
final CallParameter callParameter,
final long gasLimit,
final List<AccessListEntry> accessList,
final TransactionSimulationFunction simulationFunction) {
final AccessListOperationTracer tracer = AccessListOperationTracer.create();
final CallParameter callParameter =
overrideAccessList(jsonCallParameter, blockHeader.getGasLimit(), accessList);
final CallParameter modifiedCallParameter =
overrideAccessList(callParameter, gasLimit, accessList);
final Optional<TransactionSimulatorResult> result =
transactionSimulator.process(
callParameter, transactionValidationParams, tracer, blockHeader);
simulationFunction.simulate(modifiedCallParameter, tracer);
return new AccessListSimulatorResult(result, tracer);
}
protected CallParameter overrideAccessList(
final JsonCallParameter callParams,
private CallParameter overrideAccessList(
final CallParameter callParams,
final long gasLimit,
final List<AccessListEntry> accessListEntries) {
return new CallParameter(
@@ -151,7 +127,7 @@ public class EthCreateAccessList extends AbstractEstimateGas {
callParams.getMaxFeePerGas(),
callParams.getValue(),
callParams.getPayload(),
Optional.ofNullable(accessListEntries));
Optional.of(accessListEntries));
}
private record AccessListSimulatorResult(

View File

@@ -14,19 +14,12 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import org.hyperledger.besu.datatypes.AccountOverrideMap;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.transaction.CallParameter;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
@@ -34,7 +27,6 @@ import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer;
import java.util.Optional;
import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,33 +44,17 @@ public class EthEstimateGas extends AbstractEstimateGas {
}
@Override
protected Object resultByBlockHeader(
protected Object simulate(
final JsonRpcRequestContext requestContext,
final JsonCallParameter callParams,
final BlockHeader blockHeader) {
final CallParameter modifiedCallParams =
overrideGasLimitAndPrice(callParams, blockHeader.getGasLimit());
Optional<AccountOverrideMap> maybeStateOverrides = getAddressAccountOverrideMap(requestContext);
// TODO implement for block overrides
final boolean isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE);
final CallParameter callParams,
final long gasLimit,
final TransactionSimulationFunction simulationFunction) {
final EstimateGasOperationTracer operationTracer = new EstimateGasOperationTracer();
final var transactionValidationParams =
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowExceedingBalance(isAllowExceedingBalance)
.build();
LOG.debug("Processing transaction with params: {}", modifiedCallParams);
LOG.debug("Processing transaction with params: {}", callParams);
final var maybeResult =
transactionSimulator.process(
modifiedCallParams,
maybeStateOverrides,
transactionValidationParams,
operationTracer,
blockHeader);
simulationFunction.simulate(overrideGasLimit(callParams, gasLimit), operationTracer);
final Optional<JsonRpcErrorResponse> maybeErrorResponse =
validateSimulationResult(requestContext, maybeResult);
@@ -89,12 +65,7 @@ public class EthEstimateGas extends AbstractEstimateGas {
final var result = maybeResult.get();
long low = result.result().getEstimateGasUsedByTransaction();
final var lowResult =
transactionSimulator.process(
overrideGasLimitAndPrice(callParams, low),
maybeStateOverrides,
transactionValidationParams,
operationTracer,
blockHeader);
simulationFunction.simulate(overrideGasLimit(callParams, low), operationTracer);
if (lowResult.isPresent() && lowResult.get().isSuccessful()) {
return Quantity.create(low);
@@ -106,12 +77,7 @@ public class EthEstimateGas extends AbstractEstimateGas {
while (low + 1 < high) {
mid = (low + high) / 2;
var binarySearchResult =
transactionSimulator.process(
overrideGasLimitAndPrice(callParams, mid),
maybeStateOverrides,
transactionValidationParams,
operationTracer,
blockHeader);
simulationFunction.simulate(overrideGasLimit(callParams, mid), operationTracer);
if (binarySearchResult.isEmpty() || !binarySearchResult.get().isSuccessful()) {
low = mid;
@@ -139,15 +105,4 @@ public class EthEstimateGas extends AbstractEstimateGas {
}
return Optional.empty();
}
@VisibleForTesting
protected Optional<AccountOverrideMap> getAddressAccountOverrideMap(
final JsonRpcRequestContext request) {
try {
return request.getOptionalParameter(2, AccountOverrideMap.class);
} catch (JsonRpcParameter.JsonRpcParameterException e) {
throw new InvalidJsonRpcRequestException(
"Invalid account overrides parameter (index 2)", RpcErrorType.INVALID_CALL_PARAMS, e);
}
}
}

View File

@@ -116,8 +116,7 @@ public class TraceCallMany extends TraceCall implements JsonRpcMethod {
.getAndMapWorldState(
blockHeader.getBlockHash(),
ws -> {
final WorldUpdater updater =
transactionSimulator.getEffectiveWorldStateUpdater(blockHeader, ws);
final WorldUpdater updater = transactionSimulator.getEffectiveWorldStateUpdater(ws);
try {
Arrays.stream(transactionsAndTraceTypeParameters)
.forEachOrdered(
@@ -158,6 +157,11 @@ public class TraceCallMany extends TraceCall implements JsonRpcMethod {
final Set<TraceTypeParameter.TraceType> traceTypes = traceTypeParameter.getTraceTypes();
final DebugOperationTracer tracer =
new DebugOperationTracer(buildTraceOptions(traceTypes), false);
final var miningBeneficiary =
protocolSchedule
.getByBlockHeader(header)
.getMiningBeneficiaryCalculator()
.calculateBeneficiary(header);
final Optional<TransactionSimulatorResult> maybeSimulatorResult =
transactionSimulator.processWithWorldUpdater(
callParameter,
@@ -165,7 +169,8 @@ public class TraceCallMany extends TraceCall implements JsonRpcMethod {
buildTransactionValidationParams(),
tracer,
header,
worldUpdater);
worldUpdater,
miningBeneficiary);
LOG.trace("Executing {} call for transaction {}", traceTypeParameter, callParameter);
if (maybeSimulatorResult.isEmpty()) {

View File

@@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.DefaultSyncStatus;
import org.hyperledger.besu.ethereum.core.ImmutableMiningConfiguration;
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
import org.hyperledger.besu.ethereum.core.Synchronizer;
import org.hyperledger.besu.ethereum.core.Transaction;
@@ -141,6 +142,7 @@ public abstract class AbstractEthGraphQLHttpServiceTest {
blockchain,
blockchainSetupUtil.getWorldArchive(),
blockchainSetupUtil.getProtocolSchedule(),
ImmutableMiningConfiguration.newDefault(),
0L);
service =

View File

@@ -175,6 +175,7 @@ public abstract class AbstractJsonRpcHttpServiceTest {
blockchainSetupUtil.getBlockchain(),
blockchainSetupUtil.getWorldArchive(),
blockchainSetupUtil.getProtocolSchedule(),
miningConfiguration,
0L);
return new JsonRpcMethodsFactory()

View File

@@ -72,6 +72,7 @@ public class EthCreateAccessListTest {
@Mock private BlockHeader latestBlockHeader;
@Mock private BlockHeader finalizedBlockHeader;
@Mock private BlockHeader genesisBlockHeader;
@Mock private BlockHeader pendingBlockHeader;
@Mock private Blockchain blockchain;
@Mock private BlockchainQueries blockchainQueries;
@Mock private TransactionSimulator transactionSimulator;
@@ -93,6 +94,9 @@ public class EthCreateAccessListTest {
when(blockchain.getChainHeadHeader()).thenReturn(latestBlockHeader);
when(latestBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
when(latestBlockHeader.getNumber()).thenReturn(2L);
when(pendingBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
when(pendingBlockHeader.getNumber()).thenReturn(3L);
when(transactionSimulator.simulatePendingBlockHeader()).thenReturn(pendingBlockHeader);
when(worldStateArchive.isWorldStateAvailable(any(), any())).thenReturn(true);
method = new EthCreateAccessList(blockchainQueries, transactionSimulator);
@@ -139,6 +143,18 @@ public class EthCreateAccessListTest {
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void pendingBlockTagEstimateOnPendingBlock() {
final JsonRpcRequestContext request =
ethCreateAccessListRequest(legacyTransactionCallParameter(Wei.ZERO), "pending");
mockTransactionSimulatorResult(true, false, 1L, pendingBlockHeader);
final JsonRpcResponse expectedResponse =
new JsonRpcSuccessResponse(null, new CreateAccessListResult(new ArrayList<>(), 1L));
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void shouldReturnGasEstimateErrorWhenGasPricePresentForEip1559Transaction() {
final JsonRpcRequestContext request =
@@ -186,7 +202,8 @@ public class EthCreateAccessListTest {
mockTransactionSimulatorResult(true, false, 1L, latestBlockHeader);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(transactionSimulator, times(1)).process(any(), any(), any(), eq(latestBlockHeader));
verify(transactionSimulator, times(1))
.process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader));
}
@Test
@@ -207,7 +224,8 @@ public class EthCreateAccessListTest {
assertThat(responseWithMockTracer(request, tracer))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(latestBlockHeader));
verify(transactionSimulator, times(2))
.process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader));
}
@Test
@@ -224,7 +242,8 @@ public class EthCreateAccessListTest {
// Set TransactionSimulator.process response
mockTransactionSimulatorResult(true, false, 1L, latestBlockHeader);
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(transactionSimulator, times(1)).process(any(), any(), any(), eq(latestBlockHeader));
verify(transactionSimulator, times(1))
.process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader));
}
@Test
@@ -245,7 +264,8 @@ public class EthCreateAccessListTest {
assertThat(responseWithMockTracer(request, tracer))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
verify(transactionSimulator, times(1)).process(any(), any(), any(), eq(latestBlockHeader));
verify(transactionSimulator, times(1))
.process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader));
}
@Test
@@ -269,7 +289,8 @@ public class EthCreateAccessListTest {
assertThat(responseWithMockTracer(request, tracer))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(latestBlockHeader));
verify(transactionSimulator, times(2))
.process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader));
}
@Test
@@ -289,7 +310,8 @@ public class EthCreateAccessListTest {
assertThat(responseWithMockTracer(request, tracer))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(finalizedBlockHeader));
verify(transactionSimulator, times(2))
.process(any(), eq(Optional.empty()), any(), any(), eq(finalizedBlockHeader));
}
@Test
@@ -309,7 +331,8 @@ public class EthCreateAccessListTest {
assertThat(responseWithMockTracer(request, tracer))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(genesisBlockHeader));
verify(transactionSimulator, times(2))
.process(any(), eq(Optional.empty()), any(), any(), eq(genesisBlockHeader));
}
private JsonRpcResponse responseWithMockTracer(
@@ -328,14 +351,21 @@ public class EthCreateAccessListTest {
return tracer;
}
@SuppressWarnings("ReferenceEquality")
private void mockTransactionSimulatorResult(
final boolean isSuccessful,
final boolean isReverted,
final long estimateGas,
final BlockHeader blockHeader) {
final TransactionSimulatorResult mockTxSimResult = mock(TransactionSimulatorResult.class);
when(transactionSimulator.process(any(), any(), any(), eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult));
if (blockHeader == pendingBlockHeader) {
when(transactionSimulator.processOnPending(
any(), eq(Optional.empty()), any(), any(), eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult));
} else {
when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult));
}
final TransactionProcessingResult mockResult = mock(TransactionProcessingResult.class);
when(mockResult.getEstimateGasUsedByTransaction()).thenReturn(estimateGas);
when(mockResult.getRevertReason())

View File

@@ -38,7 +38,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
@@ -70,6 +69,7 @@ public class EthEstimateGasTest {
@Mock private BlockHeader latestBlockHeader;
@Mock private BlockHeader finalizedBlockHeader;
@Mock private BlockHeader genesisBlockHeader;
@Mock private BlockHeader pendingBlockHeader;
@Mock private Blockchain blockchain;
@Mock private BlockchainQueries blockchainQueries;
@Mock private TransactionSimulator transactionSimulator;
@@ -91,6 +91,9 @@ public class EthEstimateGasTest {
when(blockchain.getChainHeadHeader()).thenReturn(latestBlockHeader);
when(latestBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
when(latestBlockHeader.getNumber()).thenReturn(2L);
when(pendingBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
when(pendingBlockHeader.getNumber()).thenReturn(3L);
when(transactionSimulator.simulatePendingBlockHeader()).thenReturn(pendingBlockHeader);
when(worldStateArchive.isWorldStateAvailable(any(), any())).thenReturn(true);
method = new EthEstimateGas(blockchainQueries, transactionSimulator);
@@ -376,11 +379,7 @@ public class EthEstimateGasTest {
.process(
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
eq(Optional.empty()), // no account overrides
eq(
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowExceedingBalance(true)
.build()),
eq(TransactionValidationParams.transactionSimulatorAllowExceedingBalance()),
any(OperationTracer.class),
eq(latestBlockHeader));
}
@@ -397,11 +396,7 @@ public class EthEstimateGasTest {
.process(
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
eq(Optional.empty()), // no account overrides
eq(
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowExceedingBalance(false)
.build()),
eq(TransactionValidationParams.transactionSimulator()),
any(OperationTracer.class),
eq(latestBlockHeader));
}
@@ -432,6 +427,17 @@ public class EthEstimateGasTest {
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void pendingBlockTagEstimateOnPendingBlock() {
final JsonRpcRequestContext request =
ethEstimateGasRequest(eip1559TransactionCallParameter(), "pending");
mockTransientProcessorResultGasEstimate(1L, true, false, pendingBlockHeader);
final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(null, Quantity.create(1L));
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void shouldUseBlockNumberParamWhenPresent() {
final JsonRpcRequestContext request =
@@ -488,6 +494,7 @@ public class EthEstimateGasTest {
isSuccessful, estimateGas, gasPrice, revertReason, blockHeader);
}
@SuppressWarnings("ReferenceEquality")
private TransactionSimulatorResult getMockTransactionSimulatorResult(
final boolean isSuccessful,
final long estimateGas,
@@ -495,21 +502,37 @@ public class EthEstimateGasTest {
final Optional<Bytes> revertReason,
final BlockHeader blockHeader) {
final TransactionSimulatorResult mockTxSimResult = mock(TransactionSimulatorResult.class);
when(transactionSimulator.process(
eq(modifiedLegacyTransactionCallParameter(gasPrice)),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class),
any(OperationTracer.class),
eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult));
when(transactionSimulator.process(
eq(modifiedEip1559TransactionCallParameter()),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class),
any(OperationTracer.class),
eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult));
if (blockHeader == pendingBlockHeader) {
when(transactionSimulator.processOnPending(
eq(modifiedLegacyTransactionCallParameter(gasPrice)),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class),
any(OperationTracer.class),
eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult));
when(transactionSimulator.processOnPending(
eq(modifiedEip1559TransactionCallParameter()),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class),
any(OperationTracer.class),
eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult));
} else {
when(transactionSimulator.process(
eq(modifiedLegacyTransactionCallParameter(gasPrice)),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class),
any(OperationTracer.class),
eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult));
when(transactionSimulator.process(
eq(modifiedEip1559TransactionCallParameter()),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class),
any(OperationTracer.class),
eq(blockHeader)))
.thenReturn(Optional.of(mockTxSimResult));
}
final TransactionProcessingResult mockResult = mock(TransactionProcessingResult.class);
when(mockResult.getEstimateGasUsedByTransaction()).thenReturn(estimateGas);
when(mockResult.getRevertReason()).thenReturn(revertReason);

View File

@@ -14,6 +14,7 @@
*/
package org.hyperledger.besu.ethereum.blockcreation;
import static org.hyperledger.besu.ethereum.core.BlockHeaderBuilder.createPending;
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator.calculateExcessBlobGasForParent;
import org.hyperledger.besu.datatypes.Address;
@@ -29,7 +30,6 @@ import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
@@ -41,15 +41,12 @@ import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
import org.hyperledger.besu.ethereum.mainnet.DifficultyCalculator;
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.ScheduleBasedBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsProcessor;
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
import org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.mainnet.requests.ProcessRequestContext;
import org.hyperledger.besu.ethereum.mainnet.requests.RequestProcessorCoordinator;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
@@ -60,7 +57,6 @@ import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleExcepti
import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector;
import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CancellationException;
@@ -198,12 +194,15 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
protocolSchedule.getForNextBlockHeader(parentHeader, timestamp);
final ProcessableBlockHeader processableBlockHeader =
createPendingBlockHeader(
timestamp,
maybePrevRandao,
maybeParentBeaconBlockRoot,
newProtocolSpec,
parentHeader);
createPending(
newProtocolSpec,
parentHeader,
miningConfiguration,
timestamp,
maybePrevRandao,
maybeParentBeaconBlockRoot)
.buildProcessableBlockHeader();
final Address miningBeneficiary =
miningBeneficiaryCalculator.getMiningBeneficiary(processableBlockHeader.getNumber());
@@ -421,52 +420,6 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
return Lists.newArrayList();
}
private ProcessableBlockHeader createPendingBlockHeader(
final long timestamp,
final Optional<Bytes32> maybePrevRandao,
final Optional<Bytes32> maybeParentBeaconBlockRoot,
final ProtocolSpec protocolSpec,
final BlockHeader parentHeader) {
final long newBlockNumber = parentHeader.getNumber() + 1;
long gasLimit =
protocolSpec
.getGasLimitCalculator()
.nextGasLimit(
parentHeader.getGasLimit(),
miningConfiguration.getTargetGasLimit().orElse(parentHeader.getGasLimit()),
newBlockNumber);
final DifficultyCalculator difficultyCalculator = protocolSpec.getDifficultyCalculator();
final BigInteger difficulty = difficultyCalculator.nextDifficulty(timestamp, parentHeader);
final Wei baseFee =
Optional.of(protocolSpec.getFeeMarket())
.filter(FeeMarket::implementsBaseFee)
.map(BaseFeeMarket.class::cast)
.map(
feeMarket ->
feeMarket.computeBaseFee(
newBlockNumber,
parentHeader.getBaseFee().orElse(Wei.ZERO),
parentHeader.getGasUsed(),
feeMarket.targetGasUsed(parentHeader)))
.orElse(null);
final Bytes32 prevRandao = maybePrevRandao.orElse(null);
final Bytes32 parentBeaconBlockRoot = maybeParentBeaconBlockRoot.orElse(null);
return BlockHeaderBuilder.create()
.parentHash(parentHeader.getHash())
.coinbase(miningConfiguration.getCoinbase().orElseThrow())
.difficulty(Difficulty.of(difficulty))
.number(newBlockNumber)
.gasLimit(gasLimit)
.timestamp(timestamp)
.baseFee(baseFee)
.prevRandao(prevRandao)
.parentBeaconBlockRoot(parentBeaconBlockRoot)
.buildProcessableBlockHeader();
}
@Override
public void cancel() {
isCancelled.set(true);

View File

@@ -22,10 +22,14 @@ import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.mainnet.DifficultyCalculator;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
import org.hyperledger.besu.evm.log.LogsBloomFilter;
import java.time.Instant;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
@@ -158,6 +162,55 @@ public class BlockHeaderBuilder {
return toBuilder;
}
public static BlockHeaderBuilder createPending(
final ProtocolSpec protocolSpec,
final BlockHeader parentHeader,
final MiningConfiguration miningConfiguration,
final long timestamp,
final Optional<Bytes32> maybePrevRandao,
final Optional<Bytes32> maybeParentBeaconBlockRoot) {
final long newBlockNumber = parentHeader.getNumber() + 1;
final long gasLimit =
protocolSpec
.getGasLimitCalculator()
.nextGasLimit(
parentHeader.getGasLimit(),
miningConfiguration.getTargetGasLimit().orElse(parentHeader.getGasLimit()),
newBlockNumber);
final DifficultyCalculator difficultyCalculator = protocolSpec.getDifficultyCalculator();
final var difficulty =
Difficulty.of(difficultyCalculator.nextDifficulty(timestamp, parentHeader));
final Wei baseFee;
if (protocolSpec.getFeeMarket().implementsBaseFee()) {
final var baseFeeMarket = (BaseFeeMarket) protocolSpec.getFeeMarket();
baseFee =
baseFeeMarket.computeBaseFee(
newBlockNumber,
parentHeader.getBaseFee().orElse(Wei.ZERO),
parentHeader.getGasUsed(),
baseFeeMarket.targetGasUsed(parentHeader));
} else {
baseFee = null;
}
final Bytes32 prevRandao = maybePrevRandao.orElse(null);
final Bytes32 parentBeaconBlockRoot = maybeParentBeaconBlockRoot.orElse(null);
return BlockHeaderBuilder.create()
.parentHash(parentHeader.getHash())
.coinbase(miningConfiguration.getCoinbase().orElseThrow())
.difficulty(difficulty)
.number(newBlockNumber)
.gasLimit(gasLimit)
.timestamp(timestamp)
.baseFee(baseFee)
.prevRandao(prevRandao)
.parentBeaconBlockRoot(parentBeaconBlockRoot);
}
public BlockHeader buildBlockHeader() {
validateBlockHeader();

View File

@@ -196,4 +196,25 @@ public class ProcessableBlockHeader
public String toLogString() {
return getNumber() + " (time: " + getTimestamp() + ")";
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("ProcessableBlockHeader{");
sb.append("number=").append(number).append(", ");
sb.append("parentHash=").append(parentHash).append(", ");
sb.append("coinbase=").append(coinbase).append(", ");
sb.append("difficulty=").append(difficulty).append(", ");
sb.append("gasLimit=").append(gasLimit).append(", ");
sb.append("timestamp=").append(timestamp).append(", ");
sb.append("baseFee=").append(baseFee).append(", ");
sb.append("mixHashOrPrevRandao=").append(mixHashOrPrevRandao).append(", ");
if (parentBeaconBlockRoot != null) {
sb.append("parentBeaconBlockRoot=").append(parentBeaconBlockRoot).append(", ");
}
if (targetBlobsPerBlock != null) {
sb.append("targetBlobsPerBlock=").append(targetBlobsPerBlock);
}
return sb.append("}").toString();
}
}

View File

@@ -35,6 +35,9 @@ public interface TransactionValidationParams {
TransactionValidationParams transactionSimulatorParams =
ImmutableTransactionValidationParams.of(false, false, false, false, false, true);
TransactionValidationParams transactionSimulatorAllowExceedingBalanceParams =
ImmutableTransactionValidationParams.of(false, true, false, false, false, true);
@Value.Default
default boolean isAllowFutureNonce() {
return false;
@@ -69,6 +72,10 @@ public interface TransactionValidationParams {
return transactionSimulatorParams;
}
static TransactionValidationParams transactionSimulatorAllowExceedingBalance() {
return transactionSimulatorAllowExceedingBalanceParams;
}
static TransactionValidationParams processingBlock() {
return processingBlockParams;
}

View File

@@ -28,9 +28,10 @@ import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
@@ -84,16 +85,19 @@ public class TransactionSimulator {
private final Blockchain blockchain;
private final WorldStateArchive worldStateArchive;
private final ProtocolSchedule protocolSchedule;
private final MiningConfiguration miningConfiguration;
private final long rpcGasCap;
public TransactionSimulator(
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final ProtocolSchedule protocolSchedule,
final MiningConfiguration miningConfiguration,
final long rpcGasCap) {
this.blockchain = blockchain;
this.worldStateArchive = worldStateArchive;
this.protocolSchedule = protocolSchedule;
this.miningConfiguration = miningConfiguration;
this.rpcGasCap = rpcGasCap;
}
@@ -141,14 +145,82 @@ public class TransactionSimulator {
blockHeader);
}
public Optional<TransactionSimulatorResult> processOnPending(
final CallParameter callParams,
final Optional<AccountOverrideMap> maybeStateOverrides,
final TransactionValidationParams transactionValidationParams,
final OperationTracer operationTracer,
final ProcessableBlockHeader pendingBlockHeader) {
try (final MutableWorldState disposableWorldState =
duplicateWorldStateAtParent(pendingBlockHeader.getParentHash())) {
WorldUpdater updater = getEffectiveWorldStateUpdater(disposableWorldState);
// in order to trace the state diff we need to make sure that
// the world updater always has a parent
if (operationTracer instanceof DebugOperationTracer) {
updater = updater.parentUpdater().isPresent() ? updater : updater.updater();
}
return processWithWorldUpdater(
callParams,
maybeStateOverrides,
transactionValidationParams,
operationTracer,
pendingBlockHeader,
updater,
Address.ZERO);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public ProcessableBlockHeader simulatePendingBlockHeader() {
final long timestamp = System.currentTimeMillis();
final var chainHeadHeader = blockchain.getChainHeadHeader();
final ProtocolSpec protocolSpec =
protocolSchedule.getForNextBlockHeader(chainHeadHeader, timestamp);
final var simulatedBlockHeader =
BlockHeaderBuilder.createPending(
protocolSpec,
chainHeadHeader,
miningConfiguration,
timestamp,
Optional.empty(),
Optional.empty())
.buildProcessableBlockHeader();
LOG.trace("Simulated block header: {}", simulatedBlockHeader);
return simulatedBlockHeader;
}
private MutableWorldState duplicateWorldStateAtParent(final Hash parentHash) {
final var parentHeader =
blockchain
.getBlockHeader(parentHash)
.orElseThrow(
() ->
new IllegalStateException("Block with hash " + parentHash + " not available"));
final Hash parentStateRoot = parentHeader.getStateRoot();
return worldStateArchive
.getMutable(parentHeader, false)
.orElseThrow(
() ->
new IllegalArgumentException(
"World state not available for block "
+ parentHeader.getNumber()
+ " with state root "
+ parentStateRoot));
}
public Optional<TransactionSimulatorResult> processAtHead(final CallParameter callParams) {
final var chainHeadHash = blockchain.getChainHeadHash();
return process(
callParams,
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowExceedingBalance(true)
.build(),
TransactionValidationParams.transactionSimulatorAllowExceedingBalance(),
OperationTracer.NO_TRACING,
(mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult,
blockchain
@@ -209,7 +281,7 @@ public class TransactionSimulator {
try (final MutableWorldState ws = getWorldState(header)) {
WorldUpdater updater = getEffectiveWorldStateUpdater(header, ws);
WorldUpdater updater = getEffectiveWorldStateUpdater(ws);
// in order to trace the state diff we need to make sure that
// the world updater always has a parent
@@ -217,6 +289,12 @@ public class TransactionSimulator {
updater = updater.parentUpdater().isPresent() ? updater : updater.updater();
}
final var miningBeneficiary =
protocolSchedule
.getByBlockHeader(header)
.getMiningBeneficiaryCalculator()
.calculateBeneficiary(header);
return preWorldStateCloseGuard.apply(
ws,
processWithWorldUpdater(
@@ -225,7 +303,8 @@ public class TransactionSimulator {
transactionValidationParams,
operationTracer,
header,
updater));
updater,
miningBeneficiary));
} catch (final Exception e) {
return Optional.empty();
@@ -267,21 +346,25 @@ public class TransactionSimulator {
final Optional<AccountOverrideMap> maybeStateOverrides,
final TransactionValidationParams transactionValidationParams,
final OperationTracer operationTracer,
final BlockHeader header,
final WorldUpdater updater) {
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(header);
final ProcessableBlockHeader processableHeader,
final WorldUpdater updater,
final Address miningBeneficiary) {
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(processableHeader);
final Address senderAddress =
callParams.getFrom() != null ? callParams.getFrom() : DEFAULT_FROM;
BlockHeader blockHeaderToProcess = header;
if (transactionValidationParams.isAllowExceedingBalance() && header.getBaseFee().isPresent()) {
final ProcessableBlockHeader blockHeaderToProcess;
if (transactionValidationParams.isAllowExceedingBalance()
&& processableHeader.getBaseFee().isPresent()) {
blockHeaderToProcess =
BlockHeaderBuilder.fromHeader(header)
new BlockHeaderBuilder()
.populateFrom(processableHeader)
.baseFee(Wei.ZERO)
.blockHeaderFunctions(protocolSpec.getBlockHeaderFunctions())
.buildBlockHeader();
.buildProcessableBlockHeader();
} else {
blockHeaderToProcess = processableHeader;
}
if (maybeStateOverrides.isPresent()) {
for (Address accountToOverride : maybeStateOverrides.get().keySet()) {
@@ -315,7 +398,7 @@ public class TransactionSimulator {
buildTransaction(
callParams,
transactionValidationParams,
header,
processableHeader,
senderAddress,
nonce,
simulationGasCap,
@@ -330,9 +413,7 @@ public class TransactionSimulator {
updater,
blockHeaderToProcess,
transaction,
protocolSpec
.getMiningBeneficiaryCalculator()
.calculateBeneficiary(blockHeaderToProcess),
miningBeneficiary,
new CachingBlockHashLookup(blockHeaderToProcess, blockchain),
false,
transactionValidationParams,
@@ -395,7 +476,7 @@ public class TransactionSimulator {
private Optional<Transaction> buildTransaction(
final CallParameter callParams,
final TransactionValidationParams transactionValidationParams,
final BlockHeader header,
final ProcessableBlockHeader processableHeader,
final Address senderAddress,
final long nonce,
final long gasLimit,
@@ -435,11 +516,11 @@ public class TransactionSimulator {
maxFeePerBlobGas = callParams.getMaxFeePerBlobGas().orElse(blobGasPrice);
}
if (shouldSetGasPrice(callParams, header)) {
if (shouldSetGasPrice(callParams, processableHeader)) {
transactionBuilder.gasPrice(gasPrice);
}
if (shouldSetMaxFeePerGas(callParams, header)) {
if (shouldSetMaxFeePerGas(callParams, processableHeader)) {
transactionBuilder.maxFeePerGas(maxFeePerGas).maxPriorityFeePerGas(maxPriorityFeePerGas);
}
@@ -463,8 +544,7 @@ public class TransactionSimulator {
return Optional.ofNullable(transaction);
}
public WorldUpdater getEffectiveWorldStateUpdater(
final BlockHeader header, final MutableWorldState publicWorldState) {
public WorldUpdater getEffectiveWorldStateUpdater(final MutableWorldState publicWorldState) {
return publicWorldState.updater();
}
@@ -490,7 +570,8 @@ public class TransactionSimulator {
return Optional.of(worldState.get(address) != null);
}
private boolean shouldSetGasPrice(final CallParameter callParams, final BlockHeader header) {
private boolean shouldSetGasPrice(
final CallParameter callParams, final ProcessableBlockHeader header) {
if (header.getBaseFee().isEmpty()) {
return true;
}
@@ -499,7 +580,8 @@ public class TransactionSimulator {
return callParams.getMaxPriorityFeePerGas().isEmpty() && callParams.getMaxFeePerGas().isEmpty();
}
private boolean shouldSetMaxFeePerGas(final CallParameter callParams, final BlockHeader header) {
private boolean shouldSetMaxFeePerGas(
final CallParameter callParams, final ProcessableBlockHeader header) {
if (protocolSchedule.getChainId().isEmpty()) {
return false;
}

View File

@@ -15,8 +15,10 @@
package org.hyperledger.besu.ethereum.transaction;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.tracing.OperationTracer.NO_TRACING;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -32,12 +34,14 @@ import org.hyperledger.besu.datatypes.BlobsWithCommitments;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.TransactionType;
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.BlobTestFixture;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
@@ -52,7 +56,7 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult.Stat
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.math.BigInteger;
@@ -99,10 +103,13 @@ public class TransactionSimulatorTest {
@BeforeEach
public void setUp() {
final var miningConfiguration = MiningConfiguration.newDefault().setCoinbase(Address.ZERO);
this.transactionSimulator =
new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule, 0);
new TransactionSimulator(
blockchain, worldStateArchive, protocolSchedule, miningConfiguration, 0);
this.cappedTransactionSimulator =
new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule, GAS_CAP);
new TransactionSimulator(
blockchain, worldStateArchive, protocolSchedule, miningConfiguration, GAS_CAP);
}
@Test
@@ -182,6 +189,43 @@ public class TransactionSimulatorTest {
verifyTransactionWasProcessed(expectedTransaction);
}
@Test
public void simulateOnPendingBlockWorks() {
final CallParameter callParameter = eip1559TransactionCallParameter();
final BlockHeader blockHeader = mockBlockHeader(Hash.ZERO, 1L, Wei.ONE);
mockBlockchainForBlockHeader(blockHeader);
mockWorldStateForAccount(blockHeader, callParameter.getFrom(), 1L);
final Transaction expectedTransaction =
Transaction.builder()
.type(TransactionType.EIP1559)
.chainId(BigInteger.ONE)
.nonce(1L)
.gasLimit(blockHeader.getGasLimit())
.maxFeePerGas(callParameter.getMaxFeePerGas().orElseThrow())
.maxPriorityFeePerGas(callParameter.getMaxPriorityFeePerGas().orElseThrow())
.to(callParameter.getTo())
.sender(callParameter.getFrom())
.value(callParameter.getValue())
.payload(callParameter.getPayload())
.signature(FAKE_SIGNATURE)
.build();
mockProcessorStatusForTransaction(expectedTransaction, Status.SUCCESSFUL);
final Optional<TransactionSimulatorResult> result =
transactionSimulator.processOnPending(
callParameter,
Optional.empty(),
TransactionValidationParams.transactionSimulator(),
NO_TRACING,
transactionSimulator.simulatePendingBlockHeader());
assertThat(result.get().isSuccessful()).isTrue();
verifyTransactionWasProcessed(expectedTransaction);
}
@Test
public void shouldSetGasPriceToZeroWhenExceedingBalanceAllowed() {
final CallParameter callParameter = legacyTransactionCallParameter(Wei.ONE);
@@ -209,7 +253,7 @@ public class TransactionSimulatorTest {
transactionSimulator.process(
callParameter,
ImmutableTransactionValidationParams.builder().isAllowExceedingBalance(true).build(),
OperationTracer.NO_TRACING,
NO_TRACING,
1L);
verifyTransactionWasProcessed(expectedTransaction);
@@ -245,7 +289,7 @@ public class TransactionSimulatorTest {
transactionSimulator.process(
callParameter,
ImmutableTransactionValidationParams.builder().isAllowExceedingBalance(true).build(),
OperationTracer.NO_TRACING,
NO_TRACING,
1L);
verifyTransactionWasProcessed(expectedTransaction);
@@ -279,7 +323,7 @@ public class TransactionSimulatorTest {
transactionSimulator.process(
callParameter,
ImmutableTransactionValidationParams.builder().isAllowExceedingBalance(false).build(),
OperationTracer.NO_TRACING,
NO_TRACING,
1L);
verifyTransactionWasProcessed(expectedTransaction);
@@ -314,7 +358,7 @@ public class TransactionSimulatorTest {
transactionSimulator.process(
callParameter,
ImmutableTransactionValidationParams.builder().isAllowExceedingBalance(false).build(),
OperationTracer.NO_TRACING,
NO_TRACING,
1L);
verifyTransactionWasProcessed(expectedTransaction);
@@ -600,10 +644,7 @@ public class TransactionSimulatorTest {
// call process with original transaction
cappedTransactionSimulator.process(
callParameter,
TransactionValidationParams.transactionSimulator(),
OperationTracer.NO_TRACING,
1L);
callParameter, TransactionValidationParams.transactionSimulator(), NO_TRACING, 1L);
// expect overwritten transaction to be processed
verifyTransactionWasProcessed(expectedTransaction);
@@ -638,10 +679,7 @@ public class TransactionSimulatorTest {
// call process with original transaction
cappedTransactionSimulator.process(
callParameter,
TransactionValidationParams.transactionSimulator(),
OperationTracer.NO_TRACING,
1L);
callParameter, TransactionValidationParams.transactionSimulator(), NO_TRACING, 1L);
// expect overwritten transaction to be processed
verifyTransactionWasProcessed(expectedTransaction);
@@ -677,10 +715,7 @@ public class TransactionSimulatorTest {
// call process with original transaction
cappedTransactionSimulator.process(
callParameter,
TransactionValidationParams.transactionSimulator(),
OperationTracer.NO_TRACING,
1L);
callParameter, TransactionValidationParams.transactionSimulator(), NO_TRACING, 1L);
// expect transaction with the original gas limit to be processed
verifyTransactionWasProcessed(expectedTransaction);
@@ -799,6 +834,8 @@ public class TransactionSimulatorTest {
when(blockchain.getBlockHeader(blockHeader.getNumber())).thenReturn(Optional.of(blockHeader));
when(blockchain.getBlockHeader(blockHeader.getBlockHash()))
.thenReturn(Optional.of(blockHeader));
when(blockchain.getChainHeadHash()).thenReturn(blockHeader.getHash());
when(blockchain.getChainHeadHeader()).thenReturn(blockHeader);
}
private void mockProtocolSpecForProcessWithWorldUpdater() {
@@ -806,11 +843,15 @@ public class TransactionSimulatorTest {
final BlockHashProcessor blockHashProcessor = mock(BlockHashProcessor.class);
when(protocolSchedule.getChainId()).thenReturn(Optional.of(BigInteger.ONE));
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
when(protocolSchedule.getForNextBlockHeader(any(), anyLong())).thenReturn(protocolSpec);
when(protocolSpec.getTransactionProcessor()).thenReturn(transactionProcessor);
when(protocolSpec.getMiningBeneficiaryCalculator()).thenReturn(BlockHeader::getCoinbase);
when(protocolSpec.getBlockHeaderFunctions()).thenReturn(blockHeaderFunctions);
when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0));
when(protocolSpec.getBlockHashProcessor()).thenReturn(blockHashProcessor);
when(protocolSpec.getGasCalculator()).thenReturn(new FrontierGasCalculator());
when(protocolSpec.getGasLimitCalculator()).thenReturn(GasLimitCalculator.constant());
when(protocolSpec.getDifficultyCalculator()).thenReturn((time, parent) -> BigInteger.TEN);
}
private void mockProcessorStatusForTransaction(

View File

@@ -28,6 +28,7 @@ import org.hyperledger.besu.config.JsonUtil;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.chain.GenesisState;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
@@ -70,7 +71,8 @@ public class NodeSmartContractPermissioningControllerTest {
genesisState.writeStateTo(worldArchive.getMutable());
final TransactionSimulator ts =
new TransactionSimulator(blockchain, worldArchive, protocolSchedule, 0L);
new TransactionSimulator(
blockchain, worldArchive, protocolSchedule, MiningConfiguration.newDefault(), 0L);
final Address contractAddress = Address.fromHexString(contractAddressString);
when(metricsSystem.createCounter(

View File

@@ -29,6 +29,7 @@ import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.chain.GenesisState;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@@ -69,7 +70,8 @@ public class TransactionSmartContractPermissioningControllerTest {
genesisState.writeStateTo(worldArchive.getMutable());
final TransactionSimulator ts =
new TransactionSimulator(blockchain, worldArchive, protocolSchedule, 0L);
new TransactionSimulator(
blockchain, worldArchive, protocolSchedule, MiningConfiguration.newDefault(), 0L);
final Address contractAddress = Address.fromHexString(contractAddressString);
when(metricsSystem.createCounter(

View File

@@ -47,15 +47,16 @@ public class AccessListOperationTracer extends EstimateGasOperationTracer {
* @return the access list
*/
public List<AccessListEntry> getAccessList() {
final List<AccessListEntry> list = new ArrayList<>();
if (warmedUpStorage != null && !warmedUpStorage.isEmpty()) {
final List<AccessListEntry> list = new ArrayList<>(warmedUpStorage.size());
warmedUpStorage
.rowMap()
.forEach(
(address, storageKeys) ->
list.add(new AccessListEntry(address, new ArrayList<>(storageKeys.keySet()))));
return list;
}
return list;
return List.of();
}
/**

View File

@@ -71,7 +71,7 @@ Calculated : ${currentHash}
tasks.register('checkAPIChanges', FileStateChecker) {
description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
files = sourceSets.main.allJava.files
knownHash = 'dsbVupAvtmZlEeEeVDtk+VrzGFvyKxHgQntaMtOq5TY='
knownHash = 'TPCo4SZ61OrJxRAa2SIcAIOAOjVTdRw+UOeHMuiJP84='
}
check.dependsOn('checkAPIChanges')

View File

@@ -26,27 +26,14 @@ import java.util.Optional;
/** Transaction simulation service interface */
@Unstable
public interface TransactionSimulationService extends BesuService {
/**
* Simulate transaction execution at the block identified by the hash
*
* @param transaction tx
* @param blockHash the hash of the block
* @param operationTracer the tracer
* @param isAllowExceedingBalance should ignore the sender balance during the simulation?
* @return the result of the simulation
*/
Optional<TransactionSimulationResult> simulate(
Transaction transaction,
Hash blockHash,
OperationTracer operationTracer,
boolean isAllowExceedingBalance);
/**
* Simulate transaction execution at the block identified by the hash
* Simulate transaction execution at the block identified by the hash if present, otherwise on the
* pending block, with optional state overrides that can be applied before the simulation.
*
* @param transaction tx
* @param accountOverrides state overrides to apply to this simulation
* @param blockHash the hash of the block
* @param maybeBlockHash optional hash of the block, empty to simulate on pending block
* @param operationTracer the tracer
* @param isAllowExceedingBalance should ignore the sender balance during the simulation?
* @return the result of the simulation
@@ -54,7 +41,77 @@ public interface TransactionSimulationService extends BesuService {
Optional<TransactionSimulationResult> simulate(
Transaction transaction,
Optional<AccountOverrideMap> accountOverrides,
Hash blockHash,
Optional<Hash> maybeBlockHash,
OperationTracer operationTracer,
boolean isAllowExceedingBalance);
/**
* Simulate transaction execution at the block identified by the hash if present, otherwise on the
* pending block
*
* @param transaction tx
* @param maybeBlockHash optional hash of the block, empty to simulate on pending block
* @param operationTracer the tracer
* @param isAllowExceedingBalance should ignore the sender balance during the simulation?
* @return the result of the simulation
*/
default Optional<TransactionSimulationResult> simulate(
final Transaction transaction,
final Optional<Hash> maybeBlockHash,
final OperationTracer operationTracer,
final boolean isAllowExceedingBalance) {
return simulate(
transaction, Optional.empty(), maybeBlockHash, operationTracer, isAllowExceedingBalance);
}
/**
* Simulate transaction execution at the block identified by the hash
*
* @param transaction tx
* @param blockHash then hash of the block
* @param operationTracer the tracer
* @param isAllowExceedingBalance should ignore the sender balance during the simulation?
* @return the result of the simulation
* @deprecated use {@link #simulate(Transaction, Optional, OperationTracer, boolean)}
*/
@Deprecated(since = "24.12", forRemoval = true)
default Optional<TransactionSimulationResult> simulate(
final Transaction transaction,
final Hash blockHash,
final OperationTracer operationTracer,
final boolean isAllowExceedingBalance) {
return simulate(
transaction,
Optional.empty(),
Optional.of(blockHash),
operationTracer,
isAllowExceedingBalance);
}
/**
* Simulate transaction execution at the block identified by the hash, with optional state
* overrides that can be applied before the simulation.
*
* @param transaction tx
* @param accountOverrides state overrides to apply to this simulation
* @param blockHash the hash of the block
* @param operationTracer the tracer
* @param isAllowExceedingBalance should ignore the sender balance during the simulation?
* @return the result of the simulation
* @deprecated use {@link #simulate(Transaction, Optional, Optional, OperationTracer, boolean)}
*/
@Deprecated(since = "24.12", forRemoval = true)
default Optional<TransactionSimulationResult> simulate(
final Transaction transaction,
final Optional<AccountOverrideMap> accountOverrides,
final Hash blockHash,
final OperationTracer operationTracer,
final boolean isAllowExceedingBalance) {
return simulate(
transaction,
accountOverrides,
Optional.of(blockHash),
operationTracer,
isAllowExceedingBalance);
}
}