Skip to content

Commit 3e74fee

Browse files
garyschultejflo
andauthored
Feature add block import trace to eth simulate (#10211)
* initial addition of optional block import tracing to eth_simulateV1 * move block traceEnd prior to trielog write --------- Signed-off-by: garyschulte <garyschulte@gmail.com> Co-authored-by: Justin Florentine <justin+github@florentine.us>
1 parent d35a222 commit 3e74fee

8 files changed

Lines changed: 89 additions & 23 deletions

File tree

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSimulateV1.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
4141
import org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError;
4242
import org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallException;
43+
import org.hyperledger.besu.evm.tracing.OperationTracer;
44+
import org.hyperledger.besu.plugin.ServiceManager;
45+
import org.hyperledger.besu.plugin.services.BlockImportTracerProvider;
46+
import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer;
4347

4448
import java.util.List;
4549
import java.util.Optional;
@@ -52,8 +56,10 @@ public class EthSimulateV1 extends AbstractBlockParameterOrBlockHashMethod {
5256

5357
private final BlockSimulator blockSimulator;
5458
private final ProtocolSchedule protocolSchedule;
59+
private final BlockImportTracerProvider blockImportTracerProvider;
5560

5661
public EthSimulateV1(
62+
final ServiceManager serviceManager,
5763
final BlockchainQueries blockchainQueries,
5864
final ProtocolSchedule protocolSchedule,
5965
final TransactionSimulator transactionSimulator,
@@ -69,6 +75,12 @@ public EthSimulateV1(
6975
miningConfiguration,
7076
blockchainQueries.getBlockchain(),
7177
apiConfiguration.getGasCap());
78+
79+
this.blockImportTracerProvider =
80+
Optional.ofNullable(serviceManager)
81+
.flatMap(mgr -> mgr.getService(BlockImportTracerProvider.class))
82+
// if block import tracer provider is not specified by plugin, default to no tracing
83+
.orElse(__ -> BlockAwareOperationTracer.NO_TRACING);
7284
}
7385

7486
@VisibleForTesting
@@ -79,6 +91,7 @@ public EthSimulateV1(
7991
super(blockchainQueries);
8092
this.protocolSchedule = protocolSchedule;
8193
this.blockSimulator = blockSimulator;
94+
this.blockImportTracerProvider = __ -> BlockAwareOperationTracer.NO_TRACING;
8295
}
8396

8497
@Override
@@ -143,8 +156,14 @@ protected Object resultByBlockHeader(
143156
}
144157

145158
private Object process(final BlockHeader header, final SimulateV1Parameter simulateV1Parameter) {
159+
var blockImportTracer =
160+
simulateV1Parameter.isTraceBlockImport()
161+
? blockImportTracerProvider.getBlockImportTracer(header)
162+
: OperationTracer.NO_TRACING;
163+
146164
final List<BlockSimulationResult> simulationResults =
147-
blockSimulator.process(header, simulateV1Parameter);
165+
blockSimulator.process(header, simulateV1Parameter, blockImportTracer);
166+
148167
return simulationResults.stream()
149168
.map(
150169
result ->

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/SimulateV1Parameter.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,21 @@
2323

2424
public class SimulateV1Parameter extends BlockSimulationParameter {
2525

26+
private final boolean traceBlockImport;
27+
2628
@JsonCreator
2729
public SimulateV1Parameter(
2830
@JsonProperty("blockStateCalls") final List<JsonBlockStateCallParameter> blockStateCalls,
2931
@JsonProperty("validation") final boolean validation,
3032
@JsonProperty("traceTransfers") final boolean traceTransfers,
3133
@JsonProperty("returnFullTransactions") final boolean returnFullTransactions,
32-
@JsonProperty("returnTrieLog") final boolean returnTrieLog) {
34+
@JsonProperty("returnTrieLog") final boolean returnTrieLog,
35+
@JsonProperty("traceBlockImport") final boolean traceBlockImport) {
3336
super(blockStateCalls, validation, traceTransfers, returnFullTransactions, returnTrieLog);
37+
this.traceBlockImport = traceBlockImport;
38+
}
39+
40+
public boolean isTraceBlockImport() {
41+
return traceBlockImport;
3442
}
3543
}

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
7272
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
7373
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
74+
import org.hyperledger.besu.plugin.ServiceManager;
7475
import org.hyperledger.besu.plugin.services.MetricsSystem;
7576

7677
import java.util.Map;
@@ -92,6 +93,7 @@ public class EthJsonRpcMethods extends ApiGroupJsonRpcMethods {
9293
private final ApiConfiguration apiConfiguration;
9394
private final GenesisConfigOptions genesisConfigOptions;
9495
private final TransactionSimulator transactionSimulator;
96+
private final ServiceManager serviceManager;
9597
private final MetricsSystem metricsSystem;
9698

9799
public EthJsonRpcMethods(
@@ -106,6 +108,7 @@ public EthJsonRpcMethods(
106108
final ApiConfiguration apiConfiguration,
107109
final GenesisConfigOptions genesisConfigOptions,
108110
final TransactionSimulator transactionSimulator,
111+
final ServiceManager serviceManager,
109112
final MetricsSystem metricsSystem) {
110113
this.blockchainQueries = blockchainQueries;
111114
this.synchronizer = synchronizer;
@@ -118,6 +121,7 @@ public EthJsonRpcMethods(
118121
this.apiConfiguration = apiConfiguration;
119122
this.genesisConfigOptions = genesisConfigOptions;
120123
this.transactionSimulator = transactionSimulator;
124+
this.serviceManager = serviceManager;
121125
this.metricsSystem = metricsSystem;
122126
}
123127

@@ -173,6 +177,7 @@ protected Map<String, JsonRpcMethod> create() {
173177
new EthBlobBaseFee(blockchainQueries.getBlockchain(), protocolSchedule),
174178
new EthMaxPriorityFeePerGas(blockchainQueries),
175179
new EthSimulateV1(
180+
serviceManager,
176181
blockchainQueries,
177182
protocolSchedule,
178183
transactionSimulator,

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ public Map<String, JsonRpcMethod> methods(
136136
apiConfiguration,
137137
genesisConfigOptions,
138138
transactionSimulator,
139+
protocolContext.getPluginServiceManager(),
139140
metricsSystem),
140141
new NetJsonRpcMethods(
141142
p2pNetwork,

ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSimulateV1Test.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError;
4040
import org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallException;
4141
import org.hyperledger.besu.evm.precompile.PrecompileContractRegistry;
42+
import org.hyperledger.besu.evm.tracing.OperationTracer;
4243

4344
import java.util.List;
4445
import java.util.Map;
@@ -76,6 +77,7 @@ public class EthSimulateV1Test {
7677
public void setUp() {
7778
method =
7879
new EthSimulateV1(
80+
null,
7981
blockchainQueries,
8082
protocolSchedule,
8183
transactionSimulator,
@@ -107,7 +109,7 @@ public void shouldReturnBlockNotFoundErrorWhenFutureBlockNumberSpecified() {
107109
public void shouldReturnInvalidParamsWhenUpfrontCostExceedsBalanceWithValidation() {
108110
setupMethodWithMockSimulator();
109111
setupBlockchainForLatest();
110-
when(blockSimulator.process(any(BlockHeader.class), any()))
112+
when(blockSimulator.process(any(BlockHeader.class), any(), any(OperationTracer.class)))
111113
.thenThrow(
112114
new BlockStateCallException(
113115
"Upfront cost exceeds balance", BlockStateCallError.UPFRONT_COST_EXCEEDS_BALANCE));
@@ -125,7 +127,7 @@ public void shouldReturnInvalidParamsWhenUpfrontCostExceedsBalanceWithValidation
125127
public void shouldReturnOriginalErrorCodeWhenUpfrontCostExceedsBalanceWithoutValidation() {
126128
setupMethodWithMockSimulator();
127129
setupBlockchainForLatest();
128-
when(blockSimulator.process(any(BlockHeader.class), any()))
130+
when(blockSimulator.process(any(BlockHeader.class), any(), any(OperationTracer.class)))
129131
.thenThrow(
130132
new BlockStateCallException(
131133
"Upfront cost exceeds balance", BlockStateCallError.UPFRONT_COST_EXCEEDS_BALANCE));
@@ -143,7 +145,8 @@ public void shouldReturnOriginalErrorCodeWhenUpfrontCostExceedsBalanceWithoutVal
143145
public void shouldNotReturnInvalidParamsWhenInputAndDataHaveDifferentValues() {
144146
setupMethodWithMockSimulator();
145147
setupBlockchainForLatest();
146-
when(blockSimulator.process(any(BlockHeader.class), any())).thenReturn(List.of());
148+
when(blockSimulator.process(any(BlockHeader.class), any(), any(OperationTracer.class)))
149+
.thenReturn(List.of());
147150

148151
// Reproduces issue #9960: both input and data provided with different values.
149152
// Other EL clients (Geth, Nethermind, Reth, Erigon) accept this and use input.
@@ -166,7 +169,8 @@ public void shouldNotReturnInvalidParamsWhenInputAndDataHaveDifferentValues() {
166169

167170
final ArgumentCaptor<BlockSimulationParameter> captor =
168171
ArgumentCaptor.forClass(BlockSimulationParameter.class);
169-
verify(blockSimulator).process(any(BlockHeader.class), captor.capture());
172+
verify(blockSimulator)
173+
.process(any(BlockHeader.class), captor.capture(), any(OperationTracer.class));
170174
final Bytes payload =
171175
captor.getValue().getBlockStateCalls().get(0).getCalls().get(0).getPayload().orElseThrow();
172176
assertThat(payload).isEqualTo(Bytes.fromHexString("0xDEADBEEF"));
@@ -202,7 +206,7 @@ private void setupBlockchainForLatest() {
202206
}
203207

204208
private SimulateV1Parameter simulateParameter(final boolean validation) {
205-
return new SimulateV1Parameter(List.of(), validation, false, false, false);
209+
return new SimulateV1Parameter(List.of(), validation, false, false, false, false);
206210
}
207211

208212
private JsonRpcRequestContext ethSimulateV1Request(

ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSimulateV1TrielogTest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ public void createStorage() {
9090

9191
method =
9292
new EthSimulateV1(
93+
null,
9394
blockchainQueries,
9495
protocolSchedule,
9596
new TransactionSimulator(
@@ -115,7 +116,7 @@ public void shouldReturnTrielogWhenReturnTrieLogTrue() {
115116

116117
// Create simulation parameter with returnTrieLog = true
117118
SimulateV1Parameter simulateV1Parameter =
118-
new SimulateV1Parameter(List.of(blockStateCall), false, false, false, true);
119+
new SimulateV1Parameter(List.of(blockStateCall), false, false, false, true, false);
119120

120121
JsonRpcRequestContext request =
121122
new JsonRpcRequestContext(
@@ -166,7 +167,7 @@ public void shouldNotReturnTrielogWhenReturnTrieLogFalse() {
166167

167168
// Create simulation parameter with returnTrieLog = false
168169
SimulateV1Parameter simulateV1Parameter =
169-
new SimulateV1Parameter(List.of(blockStateCall), false, false, false, false);
170+
new SimulateV1Parameter(List.of(blockStateCall), false, false, false, false, false);
170171

171172
JsonRpcRequestContext request =
172173
new JsonRpcRequestContext(
@@ -199,7 +200,7 @@ public void shouldReturnTrielogWithMultipleTransactions() {
199200
new JsonBlockStateCallParameter(List.of(callParameter1, callParameter2), null, null);
200201

201202
SimulateV1Parameter simulateV1Parameter =
202-
new SimulateV1Parameter(List.of(blockStateCall), false, false, false, true);
203+
new SimulateV1Parameter(List.of(blockStateCall), false, false, false, true, false);
203204

204205
JsonRpcRequestContext request =
205206
new JsonRpcRequestContext(

ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/SimulateV1ParameterTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ private void validateSimulateV1Parameter(
3838
final List<JsonBlockStateCallParameter> blockStateCalls,
3939
final BlockStateCallError expectedError) {
4040
SimulateV1Parameter simulateV1Parameter =
41-
new SimulateV1Parameter(blockStateCalls, false, false, false, false);
41+
new SimulateV1Parameter(blockStateCalls, false, false, false, false, false);
4242
Optional<BlockStateCallError> maybeValidationError =
4343
simulateV1Parameter.validate(VALID_PRECOMPILE_ADDRESSES);
4444
assertThat(maybeValidationError).isPresent();

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@
5151
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.BlockAccessListBuilder;
5252
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessListFactory;
5353
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
54-
import org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator;
5554
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
5655
import org.hyperledger.besu.ethereum.mainnet.requests.RequestProcessingContext;
5756
import org.hyperledger.besu.ethereum.mainnet.requests.RequestProcessorCoordinator;
@@ -67,6 +66,7 @@
6766
import org.hyperledger.besu.evm.tracing.OperationTracer;
6867
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
6968
import org.hyperledger.besu.plugin.data.BlockOverrides;
69+
import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer;
7070

7171
import java.util.ArrayList;
7272
import java.util.HashMap;
@@ -79,6 +79,8 @@
7979
import com.google.common.annotations.VisibleForTesting;
8080
import org.apache.tuweni.bytes.Bytes;
8181
import org.apache.tuweni.bytes.Bytes32;
82+
import org.slf4j.Logger;
83+
import org.slf4j.LoggerFactory;
8284

8385
/**
8486
* Simulates the execution of a block, processing transactions and applying state overrides. This
@@ -91,6 +93,8 @@
9193
*/
9294
public class BlockSimulator {
9395

96+
private static final Logger LOG = LoggerFactory.getLogger(BlockSimulator.class);
97+
9498
private static final TransactionValidationParams STRICT_VALIDATION_PARAMS =
9599
TransactionValidationParams.blockSimulatorStrict();
96100

@@ -280,9 +284,22 @@ private BlockSimulationResult processBlockStateCall(
280284
ws,
281285
protocolSpec,
282286
blockHashLookup,
283-
OperationTracer.NO_TRACING,
287+
operationTracer,
284288
Optional.empty());
285289

290+
// if operationTracer is block-aware, traceStart and hold onto the option ref for traceEnd
291+
var maybeBlockAwareOperationTracer =
292+
Optional.of(operationTracer)
293+
.filter(z -> z instanceof BlockAwareOperationTracer)
294+
.map(BlockAwareOperationTracer.class::cast);
295+
296+
maybeBlockAwareOperationTracer.ifPresent(
297+
tracer -> {
298+
LOG.trace("traceStartBlock sim for {}", overridenBaseBlockHeader.toLogString());
299+
tracer.traceStartBlock(
300+
ws, overridenBaseBlockHeader, overridenBaseBlockHeader.getCoinbase());
301+
});
302+
286303
protocolSpec
287304
.getPreExecutionProcessor()
288305
.process(blockProcessingContext, preExecutionAccessLocationTracker);
@@ -340,14 +357,18 @@ private BlockSimulationResult processBlockStateCall(
340357
rewardUpdater.commit();
341358
}
342359

343-
return createFinalBlock(
344-
overridenBaseBlockHeader,
345-
blockStateCallSimulationResult,
346-
blockOverrides,
347-
protocolSpec,
348-
ws,
349-
maybeRequests,
350-
returnTrieLog);
360+
var finalBlock =
361+
createFinalBlock(
362+
overridenBaseBlockHeader,
363+
blockStateCallSimulationResult,
364+
blockOverrides,
365+
protocolSpec,
366+
ws,
367+
maybeRequests,
368+
maybeBlockAwareOperationTracer,
369+
returnTrieLog);
370+
371+
return finalBlock;
351372
}
352373

353374
protected BlockStateCallSimulationResult processTransactions(
@@ -516,6 +537,7 @@ private BlockSimulationResult createFinalBlock(
516537
final ProtocolSpec protocolSpec,
517538
final MutableWorldState ws,
518539
final Optional<List<Request>> maybeRequests,
540+
final Optional<BlockAwareOperationTracer> maybeBlockAwareOperationTracer,
519541
final boolean returnTrieLog) {
520542

521543
List<Transaction> transactions = simResult.getTransactions();
@@ -557,6 +579,13 @@ private BlockSimulationResult createFinalBlock(
557579

558580
Block block = new Block(finalBlockHeader, new BlockBody(transactions, List.of(), withdrawals));
559581

582+
// if we have a block-aware operation tracer, trace end block here
583+
maybeBlockAwareOperationTracer.ifPresent(
584+
tracer -> {
585+
LOG.trace("traceEndBlock sim for {}", blockHeader.toLogString());
586+
tracer.traceEndBlock(blockHeader, block.getBody());
587+
});
588+
560589
if (returnTrieLog && ws instanceof PathBasedWorldState) {
561590
// if requested and path-based worldstate, return result with trielog and serializer:
562591
var pathBasedArchive = (PathBasedWorldStateProvider) worldStateArchive;
@@ -657,8 +686,7 @@ protected BlockHeader overrideBlockHeader(
657686

658687
// Cancun+: excessBlobGas
659688
if (newProtocolSpec.getFeeMarket().implementsBlobFee()) {
660-
builder.excessBlobGas(
661-
ExcessBlobGasCalculator.calculateExcessBlobGasForParent(newProtocolSpec, header));
689+
builder.excessBlobGas(calculateExcessBlobGasForParent(newProtocolSpec, header));
662690
} else {
663691
builder.excessBlobGas(null);
664692
builder.blobGasUsed(null);

0 commit comments

Comments
 (0)