Simulation - Expose BlockHashLookup to BlockSimulatorService (#8267)

Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>
This commit is contained in:
Gabriel-Trintinalia
2025-02-08 09:43:13 +08:00
committed by GitHub
parent cbabf87766
commit 72a4e4a0a3
6 changed files with 126 additions and 14 deletions

View File

@@ -60,7 +60,11 @@ public class BlockSimulatorServiceImpl implements BlockSimulationService {
this.blockchain = blockchain;
blockSimulator =
new BlockSimulator(
worldStateArchive, protocolSchedule, transactionSimulator, miningConfiguration);
worldStateArchive,
protocolSchedule,
transactionSimulator,
miningConfiguration,
blockchain);
this.worldStateArchive = worldStateArchive;
}

View File

@@ -21,6 +21,7 @@ import org.hyperledger.besu.datatypes.Hash;
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.mainnet.feemarket.FeeMarket;
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 @@ public class BlockSimulator {
MiningBeneficiaryCalculator miningBeneficiaryCalculator =
getMiningBeneficiaryCalculator(blockOverrides, newProtocolSpec);
BlockHashLookup blockHashLookup =
createBlockHashLookup(blockOverrides, newProtocolSpec, blockHeader);
List<TransactionSimulatorResult> transactionSimulatorResults =
processTransactions(blockHeader, blockStateCall, ws, miningBeneficiaryCalculator);
processTransactions(
blockHeader, blockStateCall, ws, miningBeneficiaryCalculator, blockHashLookup);
return finalizeBlock(
blockHeader, blockStateCall, ws, newProtocolSpec, transactionSimulatorResults);
@@ -161,7 +170,8 @@ public class BlockSimulator {
final BlockHeader blockHeader,
final BlockStateCall blockStateCall,
final MutableWorldState ws,
final MiningBeneficiaryCalculator miningBeneficiaryCalculator) {
final MiningBeneficiaryCalculator miningBeneficiaryCalculator,
final BlockHashLookup blockHashLookup) {
List<TransactionSimulatorResult> transactionSimulations = new ArrayList<>();
@@ -176,7 +186,8 @@ public class BlockSimulator {
OperationTracer.NO_TRACING,
blockHeader,
transactionUpdater,
miningBeneficiaryCalculator);
miningBeneficiaryCalculator,
blockHashLookup);
if (transactionSimulatorResult.isEmpty()) {
throw new BlockSimulationException("Transaction simulator result is empty");
@@ -409,4 +420,28 @@ public class BlockSimulator {
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()
.<BlockHashLookup>map(
blockHashLookup -> (frame, blockNumber) -> blockHashLookup.apply(blockNumber))
.orElseGet(
() ->
newProtocolSpec
.getBlockHashProcessor()
.createBlockHashLookup(blockchain, blockHeader));
}
}

View File

@@ -44,6 +44,7 @@ import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
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 class TransactionSimulator {
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 class TransactionSimulator {
operationTracer,
header,
updater,
miningBeneficiary);
miningBeneficiary,
blockHashLookup);
}
@Nonnull
@@ -377,7 +380,31 @@ public class TransactionSimulator {
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<TransactionSimulatorResult> processWithWorldUpdater(
final CallParameter callParams,
final Optional<StateOverrideMap> 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 class TransactionSimulator {
blockHeaderToProcess,
transaction,
miningBeneficiary,
protocolSpec
.getBlockHashProcessor()
.createBlockHashLookup(blockchain, blockHeaderToProcess),
blockHashLookup,
false,
transactionValidationParams,
operationTracer,

View File

@@ -32,6 +32,7 @@ import org.hyperledger.besu.datatypes.StateOverrideMap;
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.MiningBeneficiaryCalculator;
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 class BlockSimulatorTest {
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 class BlockSimulatorTest {
.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 class BlockSimulatorTest {
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 =

View File

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

View File

@@ -21,6 +21,7 @@ import org.hyperledger.besu.datatypes.parameters.UnsignedLongParameter;
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<BigInteger> difficulty;
private final Optional<Bytes> extraData;
private final Optional<Hash> mixHashOrPrevRandao;
private final Optional<Function<Long, Hash>> blockHashLookup;
/**
* Constructs a new BlockOverrides instance.
@@ -81,6 +83,7 @@ public class BlockOverrides {
this.difficulty = difficulty;
this.extraData = extraData;
this.mixHashOrPrevRandao = mixHashOrPrevRandao;
this.blockHashLookup = Optional.empty();
}
/**
@@ -101,6 +104,7 @@ public class BlockOverrides {
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 class BlockOverrides {
return mixHashOrPrevRandao;
}
/**
* Gets the block hash lookup.
*
* @return the optional block hash lookup
*/
public Optional<Function<Long, Hash>> getBlockHashLookup() {
return blockHashLookup;
}
/**
* Creates a new Builder instance.
*
@@ -234,6 +247,7 @@ public class BlockOverrides {
private BigInteger difficulty;
private Bytes extraData;
private Hash mixHashOrPrevRandao;
private Function<Long, Hash> blockHashLookup;
/** Constructs a new Builder instance. */
public Builder() {}
@@ -370,6 +384,17 @@ public class BlockOverrides {
return this;
}
/**
* Sets the block hash lookup.
*
* @param blockHashLookup the block hash lookup to set
* @return the builder instance
*/
public Builder blockHashLookup(final Function<Long, Hash> blockHashLookup) {
this.blockHashLookup = blockHashLookup;
return this;
}
/**
* Builds a new BlockOverrides instance.
*