@@ -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