Skip to content

Commit 0f198c5

Browse files
macfarlaclaude
andauthored
eth_simulateV1 protocol spec for pre-merge blocks (#10146)
Fixes ethSimulate-empty-with-block-num-set-firstblock hive rpc-compat test. Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6aa32a4 commit 0f198c5

File tree

1 file changed

+27
-5
lines changed

1 file changed

+27
-5
lines changed

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

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,18 @@ private BlockSimulationResult processBlockStateCall(
235235
final OperationTracer operationTracer) {
236236

237237
BlockOverrides blockOverrides = blockStateCall.getBlockOverrides();
238-
ProtocolSpec protocolSpec =
239-
protocolSchedule.getForNextBlockHeader(
240-
baseBlockHeader, blockOverrides.getTimestamp().orElseThrow());
238+
// Use the parent's actual difficulty (not Difficulty.ZERO) so that
239+
// TransitionProtocolSchedule correctly identifies pre-merge blocks: it dispatches
240+
// to the post-merge schedule when isPostMerge()=true AND difficulty==0, so a PoW
241+
// parent must preserve its non-zero difficulty to select the pre-merge spec.
242+
final BlockHeader syntheticNextBlockHeader =
243+
BlockHeaderBuilder.fromHeader(baseBlockHeader)
244+
.number(baseBlockHeader.getNumber() + 1)
245+
.timestamp(blockOverrides.getTimestamp().orElseThrow())
246+
.parentHash(baseBlockHeader.getBlockHash())
247+
.blockHeaderFunctions(new MainnetBlockHeaderFunctions())
248+
.buildBlockHeader();
249+
ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(syntheticNextBlockHeader);
241250

242251
BlockHeader overridenBaseBlockHeader =
243252
overrideBlockHeader(baseBlockHeader, protocolSpec, blockOverrides, shouldValidate);
@@ -319,6 +328,18 @@ private BlockSimulationResult processBlockStateCall(
319328
blockAccessListBuilder.ifPresent(
320329
builder -> builder.apply(tracker, ws.updater().updater())));
321330

331+
// Apply block reward for PoW blocks, matching geth's FinalizeAndAssemble behaviour.
332+
// Post-merge specs have blockReward=ZERO and skipZeroBlockRewards=true, so no reward is
333+
// applied for them.
334+
Wei blockReward = protocolSpec.getBlockReward();
335+
if (!(protocolSpec.isSkipZeroBlockRewards() && blockReward.isZero())) {
336+
Address miner = overridenBaseBlockHeader.getCoinbase();
337+
WorldUpdater rewardUpdater = ws.updater();
338+
MutableAccount minerAccount = rewardUpdater.getOrCreate(miner);
339+
minerAccount.incrementBalance(blockReward);
340+
rewardUpdater.commit();
341+
}
342+
322343
return createFinalBlock(
323344
overridenBaseBlockHeader,
324345
blockStateCallSimulationResult,
@@ -626,8 +647,9 @@ protected BlockHeader overrideBlockHeader(
626647
: Wei.ZERO));
627648
}
628649

629-
// Merge+: parentBeaconBlockRoot
630-
if (newProtocolSpec.isPoS()) {
650+
// Cancun+: parentBeaconBlockRoot (set for all Cancun-timestamp blocks, not just PoS,
651+
// matching geth's eth_simulateV1 behaviour where EIP-4788 runs based on timestamp)
652+
if (newProtocolSpec.getFeeMarket().implementsBlobFee()) {
631653
builder.parentBeaconBlockRoot(blockOverrides.getParentBeaconBlockRoot().orElse(Bytes32.ZERO));
632654
} else {
633655
builder.parentBeaconBlockRoot(null);

0 commit comments

Comments
 (0)