mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-09 15:28:09 -05:00
Support rpc pending block tag when estimating gas (#7951)
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ public class CliqueMinerExecutorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extraDataForNonEpochBlocksDoesNotContainValidaors() {
|
||||
public void extraDataForNonEpochBlocksDoesNotContainValidators() {
|
||||
final Bytes vanityData = generateRandomVanityData();
|
||||
|
||||
final MiningConfiguration miningConfiguration = createMiningConfiguration(vanityData);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -175,6 +175,7 @@ public abstract class AbstractJsonRpcHttpServiceTest {
|
||||
blockchainSetupUtil.getBlockchain(),
|
||||
blockchainSetupUtil.getWorldArchive(),
|
||||
blockchainSetupUtil.getProtocolSchedule(),
|
||||
miningConfiguration,
|
||||
0L);
|
||||
|
||||
return new JsonRpcMethodsFactory()
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user