Skip to content
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- Fast Sync

### Additions and Improvements
- Add ability to pass a custom tracer to block simulation [#9708](https://github.com/hyperledger/besu/pull/9708)

### Bug fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.trie.pathbased.common.provider.WorldStateQueryParams;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.plugin.Unstable;
import org.hyperledger.besu.plugin.data.BlockOverrides;
import org.hyperledger.besu.plugin.data.PluginBlockSimulationResult;
Expand Down Expand Up @@ -102,7 +103,35 @@ public PluginBlockSimulationResult simulate(
final List<? extends Transaction> transactions,
final BlockOverrides blockOverrides,
final StateOverrideMap stateOverrides) {
return processSimulation(blockNumber, transactions, blockOverrides, stateOverrides, false);
return processSimulation(
blockNumber,
transactions,
blockOverrides,
stateOverrides,
false,
OperationTracer.NO_TRACING);
}

/**
* Simulate the processing of a block given a header, a list of transactions, blockOverrides, and
* a tracer.
*
* @param blockNumber the block number
* @param transactions the transactions to include in the block
* @param blockOverrides the blockSimulationOverride of the block
* @param stateOverrides state overrides of the block
* @param tracer the operation tracer to use during simulation
* @return the block context
*/
@Override
public PluginBlockSimulationResult simulate(
final long blockNumber,
final List<? extends Transaction> transactions,
final BlockOverrides blockOverrides,
final StateOverrideMap stateOverrides,
final OperationTracer tracer) {
return processSimulation(
blockNumber, transactions, blockOverrides, stateOverrides, false, tracer);
}

/**
Expand All @@ -122,15 +151,22 @@ public PluginBlockSimulationResult simulateAndPersistWorldState(
final List<? extends Transaction> transactions,
final BlockOverrides blockOverrides,
final StateOverrideMap stateOverrides) {
return processSimulation(blockNumber, transactions, blockOverrides, stateOverrides, true);
return processSimulation(
blockNumber,
transactions,
blockOverrides,
stateOverrides,
true,
OperationTracer.NO_TRACING);
}

private PluginBlockSimulationResult processSimulation(
final long blockNumber,
final List<? extends Transaction> transactions,
final BlockOverrides blockOverrides,
final StateOverrideMap stateOverrides,
final boolean persistWorldState) {
final boolean persistWorldState,
final OperationTracer tracer) {
BlockHeader header = getBlockHeader(blockNumber);
List<CallParameter> callParameters =
transactions.stream().map(CallParameter::fromTransaction).toList();
Expand All @@ -145,7 +181,7 @@ private PluginBlockSimulationResult processSimulation(
.build();

List<BlockSimulationResult> results =
blockSimulator.process(header, blockSimulationParameter, ws);
blockSimulator.process(header, blockSimulationParameter, ws, tracer);
BlockSimulationResult result = results.getFirst();
if (persistWorldState) {
ws.persist(result.getBlock().getHeader());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,23 @@ public BlockSimulator(
*/
public List<BlockSimulationResult> process(
final BlockHeader header, final BlockSimulationParameter blockSimulationParameter) {
return process(header, blockSimulationParameter, OperationTracer.NO_TRACING);
}

/**
* Processes a list of BlockStateCalls sequentially, collecting the results.
*
* @param header The block header for all simulations.
* @param blockSimulationParameter The BlockSimulationParameter containing the block state calls.
* @param operationTracer The operation tracer to use during simulation.
* @return A list of BlockSimulationResult objects from processing each BlockStateCall.
*/
public List<BlockSimulationResult> process(
final BlockHeader header,
final BlockSimulationParameter blockSimulationParameter,
final OperationTracer operationTracer) {
try (final MutableWorldState ws = getWorldState(header)) {
return process(header, blockSimulationParameter, ws);
return process(header, blockSimulationParameter, ws, operationTracer);
} catch (IllegalArgumentException | BlockStateCallException e) {
throw e;
} catch (final Exception e) {
Expand All @@ -147,6 +162,23 @@ public List<BlockSimulationResult> process(
final BlockHeader blockHeader,
final BlockSimulationParameter simulationParameter,
final MutableWorldState worldState) {
return process(blockHeader, simulationParameter, worldState, OperationTracer.NO_TRACING);
}

/**
* Processes a list of BlockStateCalls sequentially, collecting the results.
*
* @param blockHeader The block header for all simulations.
* @param simulationParameter The BlockSimulationParameter containing the block state calls.
* @param worldState The initial MutableWorldState to start with.
* @param operationTracer The operation tracer to use during simulation.
* @return A list of BlockSimulationResult objects from processing each BlockStateCall.
*/
public List<BlockSimulationResult> process(
final BlockHeader blockHeader,
final BlockSimulationParameter simulationParameter,
final MutableWorldState worldState,
final OperationTracer operationTracer) {
int countStateCalls = simulationParameter.getBlockStateCalls().size();
List<BlockSimulationResult> results = new ArrayList<>(countStateCalls);

Expand All @@ -171,7 +203,8 @@ public List<BlockSimulationResult> process(
simulationParameter.isReturnTrieLog(),
simulationParameter::getFakeSignature,
blockHashCache,
simulationCumulativeGasUsed);
simulationCumulativeGasUsed,
operationTracer);
results.add(result);
BlockHeader resultBlockHeader = result.getBlock().getHeader();
blockHashCache.put(resultBlockHeader.getNumber(), resultBlockHeader.getHash());
Expand All @@ -187,6 +220,7 @@ public List<BlockSimulationResult> process(
* @param baseBlockHeader The block header for the simulation.
* @param blockStateCall The BlockStateCall to process.
* @param ws The MutableWorldState to use for the simulation.
* @param operationTracer The operation tracer to use
* @return A BlockSimulationResult from processing the BlockStateCall.
*/
private BlockSimulationResult processBlockStateCall(
Expand All @@ -198,7 +232,8 @@ private BlockSimulationResult processBlockStateCall(
final boolean returnTrieLog,
final Supplier<SECPSignature> signatureSupplier,
final Map<Long, Hash> blockHashCache,
final long simulationCumulativeGasUsed) {
final long simulationCumulativeGasUsed,
final OperationTracer operationTracer) {

BlockOverrides blockOverrides = blockStateCall.getBlockOverrides();
ProtocolSpec protocolSpec =
Expand Down Expand Up @@ -257,7 +292,8 @@ private BlockSimulationResult processBlockStateCall(
blockHashLookup,
signatureSupplier,
simulationCumulativeGasUsed,
blockAccessListBuilder);
blockAccessListBuilder,
operationTracer);

Optional<AccessLocationTracker> postExecutionAccessLocationTracker =
blockAccessListBuilder.map(
Expand Down Expand Up @@ -305,7 +341,8 @@ protected BlockStateCallSimulationResult processTransactions(
final BlockHashLookup blockHashLookup,
final Supplier<SECPSignature> signatureSupplier,
final long simulationCumulativeGasUsed,
final Optional<BlockAccessListBuilder> blockAccessListBuilder) {
final Optional<BlockAccessListBuilder> blockAccessListBuilder,
final OperationTracer operationTracer) {

TransactionValidationParams transactionValidationParams =
shouldValidate ? STRICT_VALIDATION_PARAMS : SIMULATION_PARAMS;
Expand All @@ -327,8 +364,18 @@ protected BlockStateCallSimulationResult processTransactions(
transactionLocation++) {
final WorldUpdater transactionUpdater = blockUpdater.updater();
final CallParameter callParameter = blockStateCall.getCalls().get(transactionLocation);
OperationTracer operationTracer =
isTraceTransfers ? new EthTransferLogOperationTracer() : OperationTracer.NO_TRACING;

// Custom tracer and EthTraceTransfers are mutually exclusive
OperationTracer finalOperationTracer = operationTracer;
if (isTraceTransfers) {
if (finalOperationTracer == OperationTracer.NO_TRACING) {
finalOperationTracer = new EthTransferLogOperationTracer();
} else {
// this shouldn't happen, and isTraceTransfers will go away with Glamsterdam
throw new IllegalArgumentException(
"Custom tracer and EthTraceTransfers are mutually exclusive");
}
}

long gasLimit =
transactionSimulator.calculateSimulationGasCap(
Expand All @@ -347,7 +394,7 @@ protected BlockStateCallSimulationResult processTransactions(
callParameter,
Optional.empty(), // We have already applied state overrides on block level
transactionValidationParams,
operationTracer,
finalOperationTracer,
blockHeader,
transactionUpdater,
miningBeneficiaryCalculator.calculateBeneficiary(blockHeader),
Expand Down Expand Up @@ -379,7 +426,7 @@ protected BlockStateCallSimulationResult processTransactions(
transactionUpdater.commit();
blockUpdater.commit();

blockStateCallSimulationResult.add(transactionSimulationResult, ws, operationTracer);
blockStateCallSimulationResult.add(transactionSimulationResult, ws, finalOperationTracer);
}

blockAccessListBuilder.ifPresent(b -> blockStateCallSimulationResult.set(b.build()));
Expand Down
2 changes: 1 addition & 1 deletion plugin-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 = 'jZO39Zata8CAfqjFrodIVAVXHUb+u0QzOK9eGIpvY84='
knownHash = 'VUXqv8VKnFsYb7fqL9svGN7+tsxIUFdmt9G7JAIDpeI='
}
check.dependsOn('checkAPIChanges')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import org.hyperledger.besu.datatypes.StateOverrideMap;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.plugin.Unstable;
import org.hyperledger.besu.plugin.data.BlockOverrides;
import org.hyperledger.besu.plugin.data.PluginBlockSimulationResult;
Expand All @@ -40,6 +41,24 @@ PluginBlockSimulationResult simulate(
BlockOverrides blockOverrides,
StateOverrideMap stateOverrides);

/**
* Simulate the processing of a block given a header, a list of transactions, blockOverrides, and
* a tracer.
*
* @param blockNumber the block number
* @param transactions the transactions to include in the block
* @param blockOverrides the blockSimulationOverride of the block
* @param stateOverrides state overrides of the block
* @param tracer the operation tracer to use during simulation
* @return the block context
*/
PluginBlockSimulationResult simulate(
long blockNumber,
List<? extends Transaction> transactions,
BlockOverrides blockOverrides,
StateOverrideMap stateOverrides,
OperationTracer tracer);

/**
* This method is experimental and should be used with caution. Simulate the processing of a block
* given a header, a list of transactions, and blockOverrides and persist the WorldState
Expand Down