diff --git a/besu/src/main/java/org/hyperledger/besu/services/BlockSimulatorServiceImpl.java b/besu/src/main/java/org/hyperledger/besu/services/BlockSimulatorServiceImpl.java index ed290cc9d2e..ca463cdaad1 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/BlockSimulatorServiceImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/BlockSimulatorServiceImpl.java @@ -60,7 +60,11 @@ public BlockSimulatorServiceImpl( this.blockchain = blockchain; blockSimulator = new BlockSimulator( - worldStateArchive, protocolSchedule, transactionSimulator, miningConfiguration); + worldStateArchive, + protocolSchedule, + transactionSimulator, + miningConfiguration, + blockchain); this.worldStateArchive = worldStateArchive; } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java index bc94177afef..b6e59df4ae6 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.datatypes.StateOverride; import org.hyperledger.besu.datatypes.StateOverrideMap; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; @@ -44,6 +45,7 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.plugin.data.BlockOverrides; @@ -69,16 +71,19 @@ public class BlockSimulator { private final WorldStateArchive worldStateArchive; private final ProtocolSchedule protocolSchedule; private final MiningConfiguration miningConfiguration; + private final Blockchain blockchain; public BlockSimulator( final WorldStateArchive worldStateArchive, final ProtocolSchedule protocolSchedule, final TransactionSimulator transactionSimulator, - final MiningConfiguration miningConfiguration) { + final MiningConfiguration miningConfiguration, + final Blockchain blockchain) { this.worldStateArchive = worldStateArchive; this.protocolSchedule = protocolSchedule; this.miningConfiguration = miningConfiguration; this.transactionSimulator = transactionSimulator; + this.blockchain = blockchain; } /** @@ -149,8 +154,12 @@ private BlockSimulationResult processSingleBlockStateCall( MiningBeneficiaryCalculator miningBeneficiaryCalculator = getMiningBeneficiaryCalculator(blockOverrides, newProtocolSpec); + BlockHashLookup blockHashLookup = + createBlockHashLookup(blockOverrides, newProtocolSpec, blockHeader); + List transactionSimulatorResults = - processTransactions(blockHeader, blockStateCall, ws, miningBeneficiaryCalculator); + processTransactions( + blockHeader, blockStateCall, ws, miningBeneficiaryCalculator, blockHashLookup); return finalizeBlock( blockHeader, blockStateCall, ws, newProtocolSpec, transactionSimulatorResults); @@ -161,7 +170,8 @@ protected List processTransactions( final BlockHeader blockHeader, final BlockStateCall blockStateCall, final MutableWorldState ws, - final MiningBeneficiaryCalculator miningBeneficiaryCalculator) { + final MiningBeneficiaryCalculator miningBeneficiaryCalculator, + final BlockHashLookup blockHashLookup) { List transactionSimulations = new ArrayList<>(); @@ -176,7 +186,8 @@ protected List processTransactions( OperationTracer.NO_TRACING, blockHeader, transactionUpdater, - miningBeneficiaryCalculator); + miningBeneficiaryCalculator, + blockHashLookup); if (transactionSimulatorResult.isEmpty()) { throw new BlockSimulationException("Transaction simulator result is empty"); @@ -409,4 +420,28 @@ public ParsedExtraData parseExtraData(final BlockHeader header) { return blockHeaderFunctions.parseExtraData(header); } } + + /** + * Creates a BlockHashLookup for the block simulation. If a BlockHashLookup is provided in the + * BlockOverrides, it is used. Otherwise, the default BlockHashLookup is created. + * + * @param blockOverrides The BlockOverrides to use. + * @param newProtocolSpec The ProtocolSpec for the block. + * @param blockHeader The block header for the simulation. + * @return The BlockHashLookup for the block simulation. + */ + private BlockHashLookup createBlockHashLookup( + final BlockOverrides blockOverrides, + final ProtocolSpec newProtocolSpec, + final BlockHeader blockHeader) { + return blockOverrides + .getBlockHashLookup() + .map( + blockHashLookup -> (frame, blockNumber) -> blockHashLookup.apply(blockNumber)) + .orElseGet( + () -> + newProtocolSpec + .getBlockHashProcessor() + .createBlockHashLookup(blockchain, blockHeader)); + } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java index 09ceab09a40..ed9f8356ff0 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java @@ -44,6 +44,7 @@ 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.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldUpdater; @@ -353,7 +354,8 @@ public Optional processWithWorldUpdater( final OperationTracer operationTracer, final BlockHeader header, final WorldUpdater updater, - final MiningBeneficiaryCalculator miningBeneficiaryCalculator) { + final MiningBeneficiaryCalculator miningBeneficiaryCalculator, + final BlockHashLookup blockHashLookup) { final Address miningBeneficiary = miningBeneficiaryCalculator.calculateBeneficiary(header); @@ -364,7 +366,8 @@ public Optional processWithWorldUpdater( operationTracer, header, updater, - miningBeneficiary); + miningBeneficiary, + blockHashLookup); } @Nonnull @@ -377,7 +380,31 @@ public Optional processWithWorldUpdater( final WorldUpdater updater, final Address miningBeneficiary) { final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(processableHeader); + final BlockHashLookup blockHashLookup = + protocolSpec.getBlockHashProcessor().createBlockHashLookup(blockchain, processableHeader); + return processWithWorldUpdater( + callParams, + maybeStateOverrides, + transactionValidationParams, + operationTracer, + processableHeader, + updater, + miningBeneficiary, + blockHashLookup); + } + @Nonnull + public Optional processWithWorldUpdater( + final CallParameter callParams, + final Optional maybeStateOverrides, + final TransactionValidationParams transactionValidationParams, + final OperationTracer operationTracer, + final ProcessableBlockHeader processableHeader, + final WorldUpdater updater, + final Address miningBeneficiary, + final BlockHashLookup blockHashLookup) { + + final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(processableHeader); final Address senderAddress = callParams.getFrom() != null ? callParams.getFrom() : DEFAULT_FROM; @@ -448,9 +475,7 @@ public Optional processWithWorldUpdater( blockHeaderToProcess, transaction, miningBeneficiary, - protocolSpec - .getBlockHashProcessor() - .createBlockHashLookup(blockchain, blockHeaderToProcess), + blockHashLookup, false, transactionValidationParams, operationTracer, diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/BlockSimulatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/BlockSimulatorTest.java index 567fb56cfa0..48899a5db19 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/BlockSimulatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/BlockSimulatorTest.java @@ -32,6 +32,7 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.parameters.UnsignedLongParameter; import org.hyperledger.besu.ethereum.GasLimitCalculator; +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.Difficulty; @@ -42,6 +43,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; +import org.hyperledger.besu.ethereum.mainnet.blockhash.BlockHashProcessor; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.trie.diffbased.common.provider.WorldStateQueryParams; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; @@ -74,6 +76,8 @@ public class BlockSimulatorTest { @Mock private TransactionSimulator transactionSimulator; @Mock private MiningConfiguration miningConfiguration; @Mock private MutableWorldState mutableWorldState; + @Mock private Blockchain blockchain; + private BlockHeader blockHeader; private BlockSimulator blockSimulator; @@ -82,7 +86,11 @@ public class BlockSimulatorTest { public void setUp() { blockSimulator = new BlockSimulator( - worldStateArchive, protocolSchedule, transactionSimulator, miningConfiguration); + worldStateArchive, + protocolSchedule, + transactionSimulator, + miningConfiguration, + blockchain); blockHeader = BlockHeaderBuilder.createDefault().buildBlockHeader(); ProtocolSpec protocolSpec = mock(ProtocolSpec.class); when(miningConfiguration.getCoinbase()) @@ -94,6 +102,7 @@ public void setUp() { when(protocolSpec.getGasLimitCalculator()).thenReturn(gasLimitCalculator); when(gasLimitCalculator.nextGasLimit(anyLong(), anyLong(), anyLong())).thenReturn(1L); when(protocolSpec.getFeeMarket()).thenReturn(mock(FeeMarket.class)); + when(protocolSpec.getBlockHashProcessor()).thenReturn(mock(BlockHashProcessor.class)); } @Test @@ -135,7 +144,14 @@ public void shouldStopWhenTransactionSimulationIsInvalid() { .thenReturn(Optional.of("Invalid Transaction")); when(transactionSimulator.processWithWorldUpdater( - any(), any(), any(), any(), any(), any(), any(MiningBeneficiaryCalculator.class))) + any(), + any(), + any(), + any(), + any(), + any(), + any(MiningBeneficiaryCalculator.class), + any())) .thenReturn(Optional.of(transactionSimulatorResult)); BlockSimulationException exception = @@ -154,7 +170,14 @@ public void shouldStopWhenTransactionSimulationIsEmpty() { BlockStateCall blockStateCall = new BlockStateCall(List.of(callParameter), null, null, true); when(transactionSimulator.processWithWorldUpdater( - any(), any(), any(), any(), any(), any(), any(MiningBeneficiaryCalculator.class))) + any(), + any(), + any(), + any(), + any(), + any(), + any(MiningBeneficiaryCalculator.class), + any())) .thenReturn(Optional.empty()); BlockSimulationException exception = diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index ba8c074f208..b95ba661af6 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -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 = 'U/zVfjqq/stLY920xHh1N26KU+KoAdgEiV2nPWIFRIs=' + knownHash = 'I2IrN2aLU610031Vw8xNr3hcT8/wb25pDrclwZUggE4=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/BlockOverrides.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/BlockOverrides.java index 703bb64b373..8d9f08a50a3 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/BlockOverrides.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/BlockOverrides.java @@ -21,6 +21,7 @@ import java.math.BigInteger; import java.util.Optional; +import java.util.function.Function; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; @@ -39,6 +40,7 @@ public class BlockOverrides { private final Optional difficulty; private final Optional extraData; private final Optional mixHashOrPrevRandao; + private final Optional> blockHashLookup; /** * Constructs a new BlockOverrides instance. @@ -81,6 +83,7 @@ public BlockOverrides( this.difficulty = difficulty; this.extraData = extraData; this.mixHashOrPrevRandao = mixHashOrPrevRandao; + this.blockHashLookup = Optional.empty(); } /** @@ -101,6 +104,7 @@ private BlockOverrides(final Builder builder) { this.difficulty = Optional.ofNullable(builder.difficulty); this.extraData = Optional.ofNullable(builder.extraData); this.mixHashOrPrevRandao = Optional.ofNullable(builder.mixHashOrPrevRandao); + this.blockHashLookup = Optional.ofNullable(builder.blockHashLookup); } /** @@ -211,6 +215,15 @@ public Optional getMixHashOrPrevRandao() { return mixHashOrPrevRandao; } + /** + * Gets the block hash lookup. + * + * @return the optional block hash lookup + */ + public Optional> getBlockHashLookup() { + return blockHashLookup; + } + /** * Creates a new Builder instance. * @@ -234,6 +247,7 @@ public static class Builder { private BigInteger difficulty; private Bytes extraData; private Hash mixHashOrPrevRandao; + private Function blockHashLookup; /** Constructs a new Builder instance. */ public Builder() {} @@ -370,6 +384,17 @@ public Builder mixHashOrPrevRandao(final Hash mixHashOrPrevRandao) { return this; } + /** + * Sets the block hash lookup. + * + * @param blockHashLookup the block hash lookup to set + * @return the builder instance + */ + public Builder blockHashLookup(final Function blockHashLookup) { + this.blockHashLookup = blockHashLookup; + return this; + } + /** * Builds a new BlockOverrides instance. *