mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-09 07:27:55 -05:00
Improve Conflict Detection in Parallelization by Considering Slots to Reduce False Positives (#7923)
Signed-off-by: Karim Taam <karim.t2am@gmail.com>
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
- Add TLS/mTLS options and configure the GraphQL HTTP service[#7910](https://github.com/hyperledger/besu/pull/7910)
|
||||
- Allow plugins to propose transactions during block creation [#8268](https://github.com/hyperledger/besu/pull/8268)
|
||||
- Update `eth_getLogs` to return a `Block not found` error when the requested block is not found. [#8290](https://github.com/hyperledger/besu/pull/8290)
|
||||
- Improve Conflict Detection in Parallelization by Considering Slots to Reduce False Positives. [#7923](https://github.com/hyperledger/besu/pull/7923)
|
||||
### Bug fixes
|
||||
- Upgrade Netty to version 4.1.118 to fix CVE-2025-24970 [#8275](https://github.com/hyperledger/besu/pull/8275)
|
||||
- Add missing RPC method `debug_accountRange` to `RpcMethod.java` and implemented its handler. [#8153](https://github.com/hyperledger/besu/issues/8153)
|
||||
|
||||
@@ -248,7 +248,7 @@ public class MainnetBlockValidator implements BlockValidator {
|
||||
protected BlockProcessingResult processBlock(
|
||||
final ProtocolContext context, final MutableWorldState worldState, final Block block) {
|
||||
|
||||
return blockProcessor.processBlock(context.getBlockchain(), worldState, block);
|
||||
return blockProcessor.processBlock(context, context.getBlockchain(), worldState, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
@@ -95,6 +96,7 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
|
||||
@Override
|
||||
public BlockProcessingResult processBlock(
|
||||
final ProtocolContext protocolContext,
|
||||
final Blockchain blockchain,
|
||||
final MutableWorldState worldState,
|
||||
final BlockHeader blockHeader,
|
||||
@@ -103,6 +105,7 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
final Optional<List<Withdrawal>> maybeWithdrawals,
|
||||
final PrivateMetadataUpdater privateMetadataUpdater) {
|
||||
return processBlock(
|
||||
protocolContext,
|
||||
blockchain,
|
||||
worldState,
|
||||
blockHeader,
|
||||
@@ -114,6 +117,7 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
}
|
||||
|
||||
protected BlockProcessingResult processBlock(
|
||||
final ProtocolContext protocolContext,
|
||||
final Blockchain blockchain,
|
||||
final MutableWorldState worldState,
|
||||
final BlockHeader blockHeader,
|
||||
@@ -151,7 +155,7 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
|
||||
final Optional<PreprocessingContext> preProcessingContext =
|
||||
preprocessingBlockFunction.run(
|
||||
worldState,
|
||||
protocolContext,
|
||||
privateMetadataUpdater,
|
||||
blockHeader,
|
||||
transactions,
|
||||
@@ -342,7 +346,7 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
|
||||
public interface PreprocessingFunction {
|
||||
Optional<PreprocessingContext> run(
|
||||
final MutableWorldState worldState,
|
||||
final ProtocolContext protocolContext,
|
||||
final PrivateMetadataUpdater privateMetadataUpdater,
|
||||
final BlockHeader blockHeader,
|
||||
final List<Transaction> transactions,
|
||||
@@ -354,7 +358,7 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
|
||||
@Override
|
||||
public Optional<PreprocessingContext> run(
|
||||
final MutableWorldState worldState,
|
||||
final ProtocolContext protocolContext,
|
||||
final PrivateMetadataUpdater privateMetadataUpdater,
|
||||
final BlockHeader blockHeader,
|
||||
final List<Transaction> transactions,
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.mainnet;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
@@ -68,14 +69,19 @@ public interface BlockProcessor {
|
||||
/**
|
||||
* Processes the block.
|
||||
*
|
||||
* @param protocolContext the current context of the protocol
|
||||
* @param blockchain the blockchain to append the block to
|
||||
* @param worldState the world state to apply changes to
|
||||
* @param block the block to process
|
||||
* @return the block processing result
|
||||
*/
|
||||
default BlockProcessingResult processBlock(
|
||||
final Blockchain blockchain, final MutableWorldState worldState, final Block block) {
|
||||
final ProtocolContext protocolContext,
|
||||
final Blockchain blockchain,
|
||||
final MutableWorldState worldState,
|
||||
final Block block) {
|
||||
return processBlock(
|
||||
protocolContext,
|
||||
blockchain,
|
||||
worldState,
|
||||
block.getHeader(),
|
||||
@@ -88,6 +94,7 @@ public interface BlockProcessor {
|
||||
/**
|
||||
* Processes the block.
|
||||
*
|
||||
* @param protocolContext the current context of the protocol
|
||||
* @param blockchain the blockchain to append the block to
|
||||
* @param worldState the world state to apply changes to
|
||||
* @param blockHeader the block header for the block
|
||||
@@ -96,18 +103,27 @@ public interface BlockProcessor {
|
||||
* @return the block processing result
|
||||
*/
|
||||
default BlockProcessingResult processBlock(
|
||||
final ProtocolContext protocolContext,
|
||||
final Blockchain blockchain,
|
||||
final MutableWorldState worldState,
|
||||
final BlockHeader blockHeader,
|
||||
final List<Transaction> transactions,
|
||||
final List<BlockHeader> ommers) {
|
||||
return processBlock(
|
||||
blockchain, worldState, blockHeader, transactions, ommers, Optional.empty(), null);
|
||||
protocolContext,
|
||||
blockchain,
|
||||
worldState,
|
||||
blockHeader,
|
||||
transactions,
|
||||
ommers,
|
||||
Optional.empty(),
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the block.
|
||||
*
|
||||
* @param protocolContext the current context of the protocol
|
||||
* @param blockchain the blockchain to append the block to
|
||||
* @param worldState the world state to apply changes to
|
||||
* @param blockHeader the block header for the block
|
||||
@@ -118,6 +134,7 @@ public interface BlockProcessor {
|
||||
* @return the block processing result
|
||||
*/
|
||||
BlockProcessingResult processBlock(
|
||||
ProtocolContext protocolContext,
|
||||
Blockchain blockchain,
|
||||
MutableWorldState worldState,
|
||||
BlockHeader blockHeader,
|
||||
@@ -129,6 +146,7 @@ public interface BlockProcessor {
|
||||
/**
|
||||
* Processes the block when running Besu in GoQuorum-compatible mode
|
||||
*
|
||||
* @param protocolContext the current context of the protocol
|
||||
* @param blockchain the blockchain to append the block to
|
||||
* @param worldState the world state to apply public transactions to
|
||||
* @param privateWorldState the private world state to apply private transaction to
|
||||
@@ -136,6 +154,7 @@ public interface BlockProcessor {
|
||||
* @return the block processing result
|
||||
*/
|
||||
default BlockProcessingResult processBlock(
|
||||
final ProtocolContext protocolContext,
|
||||
final Blockchain blockchain,
|
||||
final MutableWorldState worldState,
|
||||
final MutableWorldState privateWorldState,
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.MainnetBlockValidator;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
|
||||
@@ -1101,6 +1102,7 @@ public abstract class MainnetProtocolSpecs {
|
||||
|
||||
@Override
|
||||
public BlockProcessingResult processBlock(
|
||||
final ProtocolContext protocolContext,
|
||||
final Blockchain blockchain,
|
||||
final MutableWorldState worldState,
|
||||
final BlockHeader blockHeader,
|
||||
@@ -1110,6 +1112,7 @@ public abstract class MainnetProtocolSpecs {
|
||||
final PrivateMetadataUpdater privateMetadataUpdater) {
|
||||
updateWorldStateForDao(worldState);
|
||||
return wrapped.processBlock(
|
||||
protocolContext,
|
||||
blockchain,
|
||||
worldState,
|
||||
blockHeader,
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.hyperledger.besu.enclave.Enclave;
|
||||
import org.hyperledger.besu.enclave.EnclaveClientException;
|
||||
import org.hyperledger.besu.enclave.types.ReceiveResponse;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
@@ -83,6 +84,7 @@ public class PrivacyBlockProcessor implements BlockProcessor {
|
||||
|
||||
@Override
|
||||
public BlockProcessingResult processBlock(
|
||||
final ProtocolContext protocolContext,
|
||||
final Blockchain blockchain,
|
||||
final MutableWorldState worldState,
|
||||
final BlockHeader blockHeader,
|
||||
@@ -102,6 +104,7 @@ public class PrivacyBlockProcessor implements BlockProcessor {
|
||||
|
||||
final BlockProcessingResult result =
|
||||
blockProcessor.processBlock(
|
||||
protocolContext,
|
||||
blockchain,
|
||||
worldState,
|
||||
blockHeader,
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.mainnet.parallelization;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
@@ -31,7 +32,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
|
||||
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldState;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.provider.DiffBasedWorldStateProvider;
|
||||
import org.hyperledger.besu.evm.blockhash.BlockHashLookup;
|
||||
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
|
||||
import org.hyperledger.besu.metrics.BesuMetricCategory;
|
||||
@@ -137,6 +138,7 @@ public class MainnetParallelBlockProcessor extends MainnetBlockProcessor {
|
||||
|
||||
@Override
|
||||
public BlockProcessingResult processBlock(
|
||||
final ProtocolContext protocolContext,
|
||||
final Blockchain blockchain,
|
||||
final MutableWorldState worldState,
|
||||
final BlockHeader blockHeader,
|
||||
@@ -146,6 +148,7 @@ public class MainnetParallelBlockProcessor extends MainnetBlockProcessor {
|
||||
final PrivateMetadataUpdater privateMetadataUpdater) {
|
||||
final BlockProcessingResult blockProcessingResult =
|
||||
super.processBlock(
|
||||
protocolContext,
|
||||
blockchain,
|
||||
worldState,
|
||||
blockHeader,
|
||||
@@ -154,6 +157,7 @@ public class MainnetParallelBlockProcessor extends MainnetBlockProcessor {
|
||||
maybeWithdrawals,
|
||||
privateMetadataUpdater,
|
||||
new ParallelTransactionPreprocessing());
|
||||
|
||||
if (blockProcessingResult.isFailed()) {
|
||||
// Fallback to non-parallel processing if there is a block processing exception .
|
||||
LOG.info(
|
||||
@@ -161,6 +165,7 @@ public class MainnetParallelBlockProcessor extends MainnetBlockProcessor {
|
||||
blockHeader.getNumber(),
|
||||
blockHeader.getBlockHash());
|
||||
return super.processBlock(
|
||||
protocolContext,
|
||||
blockchain,
|
||||
worldState,
|
||||
blockHeader,
|
||||
@@ -209,20 +214,20 @@ public class MainnetParallelBlockProcessor extends MainnetBlockProcessor {
|
||||
|
||||
@Override
|
||||
public Optional<PreprocessingContext> run(
|
||||
final MutableWorldState worldState,
|
||||
final ProtocolContext protocolContext,
|
||||
final PrivateMetadataUpdater privateMetadataUpdater,
|
||||
final BlockHeader blockHeader,
|
||||
final List<Transaction> transactions,
|
||||
final Address miningBeneficiary,
|
||||
final BlockHashLookup blockHashLookup,
|
||||
final Wei blobGasPrice) {
|
||||
if ((worldState instanceof DiffBasedWorldState)) {
|
||||
if ((protocolContext.getWorldStateArchive() instanceof DiffBasedWorldStateProvider)) {
|
||||
ParallelizedConcurrentTransactionProcessor parallelizedConcurrentTransactionProcessor =
|
||||
new ParallelizedConcurrentTransactionProcessor(transactionProcessor);
|
||||
// When enabled, runAsyncBlock performs non-conflicting parallel execution of transactions
|
||||
// in the background using an optimistic approach.
|
||||
// runAsyncBlock, if activated, facilitates the non-blocking parallel execution of
|
||||
// transactions in the background through an optimistic strategy.
|
||||
parallelizedConcurrentTransactionProcessor.runAsyncBlock(
|
||||
worldState,
|
||||
protocolContext,
|
||||
blockHeader,
|
||||
transactions,
|
||||
miningBeneficiary,
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.mainnet.parallelization;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
@@ -23,8 +24,8 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
|
||||
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.cache.NoopBonsaiCachedMerkleTrieLoader;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.provider.WorldStateQueryParams;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldState;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator;
|
||||
import org.hyperledger.besu.evm.blockhash.BlockHashLookup;
|
||||
@@ -61,6 +62,8 @@ public class ParallelizedConcurrentTransactionProcessor {
|
||||
private final Map<Integer, ParallelizedTransactionContext>
|
||||
parallelizedTransactionContextByLocation = new ConcurrentHashMap<>();
|
||||
|
||||
private CompletableFuture<Void>[] completableFuturesForBackgroundTransactions;
|
||||
|
||||
/**
|
||||
* Constructs a PreloadConcurrentTransactionProcessor with a specified transaction processor. This
|
||||
* processor is responsible for the individual processing of transactions.
|
||||
@@ -88,8 +91,7 @@ public class ParallelizedConcurrentTransactionProcessor {
|
||||
* state, ensuring that the original world state passed as a parameter remains unmodified during
|
||||
* this process.
|
||||
*
|
||||
* @param worldState Mutable world state intended for applying transaction results. This world
|
||||
* state is not modified directly; instead, copies are made for transaction execution.
|
||||
* @param protocolContext the current context of the protocol
|
||||
* @param blockHeader Header of the current block containing the transactions.
|
||||
* @param transactions List of transactions to be processed.
|
||||
* @param miningBeneficiary Address of the beneficiary to receive mining rewards.
|
||||
@@ -98,13 +100,15 @@ public class ParallelizedConcurrentTransactionProcessor {
|
||||
* @param privateMetadataUpdater Updater for private transaction metadata.
|
||||
*/
|
||||
public void runAsyncBlock(
|
||||
final MutableWorldState worldState,
|
||||
final ProtocolContext protocolContext,
|
||||
final BlockHeader blockHeader,
|
||||
final List<Transaction> transactions,
|
||||
final Address miningBeneficiary,
|
||||
final BlockHashLookup blockHashLookup,
|
||||
final Wei blobGasPrice,
|
||||
final PrivateMetadataUpdater privateMetadataUpdater) {
|
||||
|
||||
completableFuturesForBackgroundTransactions = new CompletableFuture[transactions.size()];
|
||||
for (int i = 0; i < transactions.size(); i++) {
|
||||
final Transaction transaction = transactions.get(i);
|
||||
final int transactionLocation = i;
|
||||
@@ -114,7 +118,7 @@ public class ParallelizedConcurrentTransactionProcessor {
|
||||
CompletableFuture.runAsync(
|
||||
() ->
|
||||
runTransaction(
|
||||
worldState,
|
||||
protocolContext,
|
||||
blockHeader,
|
||||
transactionLocation,
|
||||
transaction,
|
||||
@@ -128,7 +132,7 @@ public class ParallelizedConcurrentTransactionProcessor {
|
||||
|
||||
@VisibleForTesting
|
||||
public void runTransaction(
|
||||
final MutableWorldState worldState,
|
||||
final ProtocolContext protocolContext,
|
||||
final BlockHeader blockHeader,
|
||||
final int transactionLocation,
|
||||
final Transaction transaction,
|
||||
@@ -136,65 +140,77 @@ public class ParallelizedConcurrentTransactionProcessor {
|
||||
final BlockHashLookup blockHashLookup,
|
||||
final Wei blobGasPrice,
|
||||
final PrivateMetadataUpdater privateMetadataUpdater) {
|
||||
try (final DiffBasedWorldState roundWorldState =
|
||||
new BonsaiWorldState(
|
||||
(BonsaiWorldState) worldState, new NoopBonsaiCachedMerkleTrieLoader())) {
|
||||
roundWorldState.freezeStorage(); // make the clone frozen
|
||||
final ParallelizedTransactionContext.Builder contextBuilder =
|
||||
new ParallelizedTransactionContext.Builder();
|
||||
final DiffBasedWorldStateUpdateAccumulator<?> roundWorldStateUpdater =
|
||||
(DiffBasedWorldStateUpdateAccumulator<?>) roundWorldState.updater();
|
||||
final TransactionProcessingResult result =
|
||||
transactionProcessor.processTransaction(
|
||||
roundWorldStateUpdater,
|
||||
blockHeader,
|
||||
transaction,
|
||||
miningBeneficiary,
|
||||
new OperationTracer() {
|
||||
@Override
|
||||
public void traceBeforeRewardTransaction(
|
||||
final WorldView worldView,
|
||||
final org.hyperledger.besu.datatypes.Transaction tx,
|
||||
final Wei miningReward) {
|
||||
/*
|
||||
* This part checks if the mining beneficiary's account was accessed before increasing its balance for rewards.
|
||||
* Indeed, if the transaction has interacted with the address to read or modify it,
|
||||
* it means that the value is necessary for the proper execution of the transaction and will therefore be considered in collision detection.
|
||||
* If this is not the case, we can ignore this address during conflict detection.
|
||||
*/
|
||||
if (transactionCollisionDetector
|
||||
.getAddressesTouchedByTransaction(
|
||||
transaction, Optional.of(roundWorldStateUpdater))
|
||||
.contains(miningBeneficiary)) {
|
||||
contextBuilder.isMiningBeneficiaryTouchedPreRewardByTransaction(true);
|
||||
}
|
||||
contextBuilder.miningBeneficiaryReward(miningReward);
|
||||
}
|
||||
},
|
||||
blockHashLookup,
|
||||
true,
|
||||
TransactionValidationParams.processingBlock(),
|
||||
privateMetadataUpdater,
|
||||
blobGasPrice);
|
||||
final BlockHeader chainHeadHeader = protocolContext.getBlockchain().getChainHeadHeader();
|
||||
if (chainHeadHeader.getHash().equals(blockHeader.getParentHash())) {
|
||||
try (BonsaiWorldState ws =
|
||||
(BonsaiWorldState)
|
||||
protocolContext
|
||||
.getWorldStateArchive()
|
||||
.getWorldState(
|
||||
WorldStateQueryParams.withBlockHeaderAndNoUpdateNodeHead(chainHeadHeader))
|
||||
.orElse(null)) {
|
||||
if (ws != null) {
|
||||
ws.disableCacheMerkleTrieLoader();
|
||||
final ParallelizedTransactionContext.Builder contextBuilder =
|
||||
new ParallelizedTransactionContext.Builder();
|
||||
final DiffBasedWorldStateUpdateAccumulator<?> roundWorldStateUpdater =
|
||||
(DiffBasedWorldStateUpdateAccumulator<?>) ws.updater();
|
||||
final TransactionProcessingResult result =
|
||||
transactionProcessor.processTransaction(
|
||||
roundWorldStateUpdater,
|
||||
blockHeader,
|
||||
transaction.detachedCopy(),
|
||||
miningBeneficiary,
|
||||
new OperationTracer() {
|
||||
@Override
|
||||
public void traceBeforeRewardTransaction(
|
||||
final WorldView worldView,
|
||||
final org.hyperledger.besu.datatypes.Transaction tx,
|
||||
final Wei miningReward) {
|
||||
/*
|
||||
* This part checks if the mining beneficiary's account was accessed before increasing its balance for rewards.
|
||||
* Indeed, if the transaction has interacted with the address to read or modify it,
|
||||
* it means that the value is necessary for the proper execution of the transaction and will therefore be considered in collision detection.
|
||||
* If this is not the case, we can ignore this address during conflict detection.
|
||||
*/
|
||||
if (transactionCollisionDetector
|
||||
.getAddressesTouchedByTransaction(
|
||||
transaction, Optional.of(roundWorldStateUpdater))
|
||||
.contains(miningBeneficiary)) {
|
||||
contextBuilder.isMiningBeneficiaryTouchedPreRewardByTransaction(true);
|
||||
}
|
||||
contextBuilder.miningBeneficiaryReward(miningReward);
|
||||
}
|
||||
},
|
||||
blockHashLookup,
|
||||
true,
|
||||
TransactionValidationParams.processingBlock(),
|
||||
privateMetadataUpdater,
|
||||
blobGasPrice);
|
||||
|
||||
// commit the accumulator in order to apply all the modifications
|
||||
roundWorldState.getAccumulator().commit();
|
||||
// commit the accumulator in order to apply all the modifications
|
||||
ws.getAccumulator().commit();
|
||||
|
||||
contextBuilder
|
||||
.transactionAccumulator(roundWorldState.getAccumulator())
|
||||
.transactionProcessingResult(result);
|
||||
contextBuilder
|
||||
.transactionAccumulator(ws.getAccumulator())
|
||||
.transactionProcessingResult(result);
|
||||
|
||||
final ParallelizedTransactionContext parallelizedTransactionContext = contextBuilder.build();
|
||||
if (!parallelizedTransactionContext.isMiningBeneficiaryTouchedPreRewardByTransaction()) {
|
||||
/*
|
||||
* If the address of the mining beneficiary has been touched only for adding rewards,
|
||||
* we remove it from the accumulator to avoid a false positive collision.
|
||||
* The balance will be increased during the sequential processing.
|
||||
*/
|
||||
roundWorldStateUpdater.getAccountsToUpdate().remove(miningBeneficiary);
|
||||
final ParallelizedTransactionContext parallelizedTransactionContext =
|
||||
contextBuilder.build();
|
||||
if (!parallelizedTransactionContext.isMiningBeneficiaryTouchedPreRewardByTransaction()) {
|
||||
/*
|
||||
* If the address of the mining beneficiary has been touched only for adding rewards,
|
||||
* we remove it from the accumulator to avoid a false positive collision.
|
||||
* The balance will be increased during the sequential processing.
|
||||
*/
|
||||
roundWorldStateUpdater.getAccountsToUpdate().remove(miningBeneficiary);
|
||||
}
|
||||
parallelizedTransactionContextByLocation.put(
|
||||
transactionLocation, parallelizedTransactionContext);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// no op as failing to get worldstate
|
||||
}
|
||||
parallelizedTransactionContextByLocation.put(
|
||||
transactionLocation, parallelizedTransactionContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,6 +270,7 @@ public class ParallelizedConcurrentTransactionProcessor {
|
||||
if (confirmedParallelizedTransactionCounter.isPresent()) {
|
||||
confirmedParallelizedTransactionCounter.get().inc();
|
||||
transactionProcessingResult.setIsProcessedInParallel(Optional.of(Boolean.TRUE));
|
||||
transactionProcessingResult.accumulator = transactionAccumulator;
|
||||
}
|
||||
return Optional.of(transactionProcessingResult);
|
||||
} else {
|
||||
@@ -264,6 +281,13 @@ public class ParallelizedConcurrentTransactionProcessor {
|
||||
// re-execute the transaction.
|
||||
return Optional.empty();
|
||||
}
|
||||
} else {
|
||||
// stop background processing for this transaction as useless
|
||||
final CompletableFuture<Void> completableFuturesForBackgroundTransaction =
|
||||
completableFuturesForBackgroundTransactions[transactionLocation];
|
||||
if (completableFuturesForBackgroundTransaction != null) {
|
||||
completableFuturesForBackgroundTransaction.cancel(true);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -15,49 +15,75 @@
|
||||
package org.hyperledger.besu.ethereum.mainnet.parallelization;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.StorageSlotKey;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedAccount;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedValue;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.preload.StorageConsumingMap;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
|
||||
public class TransactionCollisionDetector {
|
||||
|
||||
/**
|
||||
* Determines if a transaction has a collision based on the addresses it touches. A collision
|
||||
* occurs if the transaction touches the mining beneficiary address or if there are common
|
||||
* addresses touched by both the transaction and other transactions within the same block.
|
||||
* Checks if there is a conflict between the current block's state and the given transaction.
|
||||
*
|
||||
* @param transaction The transaction to check for collisions.
|
||||
* @param miningBeneficiary The address of the mining beneficiary.
|
||||
* @param parallelizedTransactionContext The context containing the accumulator for the
|
||||
* <p>This method detects conflicts between the transaction and the block's state by checking if
|
||||
* the transaction modifies the same addresses and storage slots that are already modified by the
|
||||
* block. A conflict occurs in two cases: 1. If the transaction touches an address that is also
|
||||
* modified in the block, and the account details (excluding storage) are identical. In this case,
|
||||
* it checks if there is an overlap in the storage slots affected by both the transaction and the
|
||||
* block. 2. If the account details differ between the transaction and the block (excluding
|
||||
* storage), it immediately detects a conflict.
|
||||
*
|
||||
* <p>The method returns `true` if any such conflict is found, otherwise `false`.
|
||||
*
|
||||
* @param transaction The transaction to check for conflicts with the block's state.
|
||||
* @param parallelizedTransactionContext The context for the parallelized execution of the
|
||||
* transaction.
|
||||
* @param blockAccumulator The accumulator for the block.
|
||||
* @return true if there is a collision; false otherwise.
|
||||
* @param blockAccumulator The accumulator containing the state updates of the current block.
|
||||
* @return true if there is a conflict between the transaction and the block's state, otherwise
|
||||
* false.
|
||||
*/
|
||||
public boolean hasCollision(
|
||||
final Transaction transaction,
|
||||
final Address miningBeneficiary,
|
||||
final ParallelizedTransactionContext parallelizedTransactionContext,
|
||||
final DiffBasedWorldStateUpdateAccumulator<?> blockAccumulator) {
|
||||
final DiffBasedWorldStateUpdateAccumulator<? extends DiffBasedAccount> blockAccumulator) {
|
||||
final Set<Address> addressesTouchedByTransaction =
|
||||
getAddressesTouchedByTransaction(
|
||||
transaction, Optional.of(parallelizedTransactionContext.transactionAccumulator()));
|
||||
if (addressesTouchedByTransaction.contains(miningBeneficiary)) {
|
||||
return true;
|
||||
}
|
||||
final Set<Address> addressesTouchedByBlock =
|
||||
getAddressesTouchedByBlock(Optional.of(blockAccumulator));
|
||||
final Iterator<Address> it = addressesTouchedByTransaction.iterator();
|
||||
boolean commonAddressFound = false;
|
||||
while (it.hasNext() && !commonAddressFound) {
|
||||
if (addressesTouchedByBlock.contains(it.next())) {
|
||||
commonAddressFound = true;
|
||||
for (final Address next : addressesTouchedByTransaction) {
|
||||
final Optional<AccountUpdateContext> maybeAddressTouchedByBlock =
|
||||
getAddressTouchedByBlock(next, Optional.of(blockAccumulator));
|
||||
if (maybeAddressTouchedByBlock.isPresent()) {
|
||||
if (maybeAddressTouchedByBlock.get().areAccountDetailsEqualExcludingStorage()) {
|
||||
final Set<StorageSlotKey> slotsTouchedByBlockAndByAddress =
|
||||
getSlotsTouchedByBlockAndByAddress(Optional.of(blockAccumulator), next);
|
||||
final Set<StorageSlotKey> slotsTouchedByTransactionAndByAddress =
|
||||
getSlotsTouchedByTransactionAndByAddress(
|
||||
Optional.of(parallelizedTransactionContext.transactionAccumulator()), next);
|
||||
for (final StorageSlotKey touchedByTransactionAndByAddress :
|
||||
slotsTouchedByTransactionAndByAddress) {
|
||||
if (slotsTouchedByBlockAndByAddress.contains(touchedByTransactionAndByAddress)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return commonAddressFound;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,34 +107,179 @@ public class TransactionCollisionDetector {
|
||||
diffBasedWorldStateUpdateAccumulator -> {
|
||||
diffBasedWorldStateUpdateAccumulator
|
||||
.getAccountsToUpdate()
|
||||
.forEach((address, diffBasedValue) -> addresses.add(address));
|
||||
.forEach(
|
||||
(address, diffBasedValue) -> {
|
||||
addresses.add(address);
|
||||
});
|
||||
addresses.addAll(diffBasedWorldStateUpdateAccumulator.getDeletedAccountAddresses());
|
||||
});
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the set of addresses that were touched by all transactions within a block. This
|
||||
* method filters out addresses that were only read and not modified.
|
||||
* Retrieves the set of storage slot keys that have been touched by the given transaction for the
|
||||
* specified address, based on the provided world state update accumulator.
|
||||
*
|
||||
* @param accumulator An optional accumulator containing state changes made by the block.
|
||||
* @return A set of addresses that were modified by the block's transactions.
|
||||
* <p>This method checks if the accumulator contains storage updates for the specified address. If
|
||||
* such updates are found, it adds the touched storage slot keys to the returned set. The method
|
||||
* does not distinguish between changes or unchanged slots; it simply collects all the storage
|
||||
* slot keys that have been touched by the transaction for the given address.
|
||||
*
|
||||
* @param accumulator An {@link Optional} containing the world state update accumulator, which
|
||||
* holds the updates for storage slots.
|
||||
* @param address The address for which the touched storage slots are being retrieved.
|
||||
* @return A set of storage slot keys that have been touched by the transaction for the given
|
||||
* address. If no updates are found, or the address has no associated updates, an empty set is
|
||||
* returned.
|
||||
*/
|
||||
private Set<Address> getAddressesTouchedByBlock(
|
||||
final Optional<DiffBasedWorldStateUpdateAccumulator<?>> accumulator) {
|
||||
HashSet<Address> addresses = new HashSet<>();
|
||||
private Set<StorageSlotKey> getSlotsTouchedByTransactionAndByAddress(
|
||||
final Optional<DiffBasedWorldStateUpdateAccumulator<?>> accumulator, final Address address) {
|
||||
HashSet<StorageSlotKey> slots = new HashSet<>();
|
||||
accumulator.ifPresent(
|
||||
diffBasedWorldStateUpdateAccumulator -> {
|
||||
diffBasedWorldStateUpdateAccumulator
|
||||
.getAccountsToUpdate()
|
||||
.forEach(
|
||||
(address, diffBasedValue) -> {
|
||||
if (!diffBasedValue.isUnchanged()) {
|
||||
addresses.add(address);
|
||||
}
|
||||
});
|
||||
addresses.addAll(diffBasedWorldStateUpdateAccumulator.getDeletedAccountAddresses());
|
||||
final StorageConsumingMap<StorageSlotKey, DiffBasedValue<UInt256>> map =
|
||||
diffBasedWorldStateUpdateAccumulator.getStorageToUpdate().get(address);
|
||||
if (map != null) {
|
||||
map.forEach(
|
||||
(storageSlotKey, slot) -> {
|
||||
slots.add(storageSlotKey);
|
||||
});
|
||||
}
|
||||
});
|
||||
return addresses;
|
||||
return slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the update context for the given address from the block's world state update
|
||||
* accumulator.
|
||||
*
|
||||
* <p>This method checks if the provided accumulator contains updates for the given address. If an
|
||||
* update is found, it compares the prior and updated states of the account to determine if the
|
||||
* key account details (excluding storage) are considered equal. It then returns an {@link
|
||||
* AccountUpdateContext} containing the address and the result of that comparison.
|
||||
*
|
||||
* <p>If no update is found for the address or the accumulator is absent, the method returns an
|
||||
* empty {@link Optional}.
|
||||
*
|
||||
* @param addressToFind The address for which the update context is being queried.
|
||||
* @param maybeBlockAccumulator An {@link Optional} containing the block's world state update
|
||||
* accumulator, which holds the updates for the accounts in the block.
|
||||
* @return An {@link Optional} containing the {@link AccountUpdateContext} if the address is found
|
||||
* in the block's updates, otherwise an empty {@link Optional}.
|
||||
*/
|
||||
private Optional<AccountUpdateContext> getAddressTouchedByBlock(
|
||||
final Address addressToFind,
|
||||
final Optional<DiffBasedWorldStateUpdateAccumulator<? extends DiffBasedAccount>>
|
||||
maybeBlockAccumulator) {
|
||||
if (maybeBlockAccumulator.isPresent()) {
|
||||
final DiffBasedWorldStateUpdateAccumulator<? extends DiffBasedAccount> blockAccumulator =
|
||||
maybeBlockAccumulator.get();
|
||||
final DiffBasedValue<? extends DiffBasedAccount> diffBasedValue =
|
||||
blockAccumulator.getAccountsToUpdate().get(addressToFind);
|
||||
if (diffBasedValue != null) {
|
||||
return Optional.of(
|
||||
new AccountUpdateContext(
|
||||
addressToFind,
|
||||
areAccountDetailsEqualExcludingStorage(
|
||||
diffBasedValue.getPrior(), diffBasedValue.getUpdated())));
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the set of storage slot keys that have been updated in the block accumulator for the
|
||||
* specified address.
|
||||
*
|
||||
* <p>This method checks if the accumulator contains a storage map for the provided address. If
|
||||
* the address has associated storage updates, it iterates over the storage slots and add it to
|
||||
* the list only if the corresponding storage value has been modified (i.e., is not unchanged).
|
||||
*
|
||||
* @param accumulator An Optional containing the world state block update accumulator, which holds
|
||||
* the storage updates.
|
||||
* @param address The address for which the storage slots are being queried.
|
||||
* @return A set of storage slot keys that have been updated for the given address. If no updates
|
||||
* are found, or the address has no associated updates, an empty set is returned.
|
||||
*/
|
||||
private Set<StorageSlotKey> getSlotsTouchedByBlockAndByAddress(
|
||||
final Optional<DiffBasedWorldStateUpdateAccumulator<?>> accumulator, final Address address) {
|
||||
HashSet<StorageSlotKey> slots = new HashSet<>();
|
||||
accumulator.ifPresent(
|
||||
diffBasedWorldStateUpdateAccumulator -> {
|
||||
final StorageConsumingMap<StorageSlotKey, DiffBasedValue<UInt256>> map =
|
||||
diffBasedWorldStateUpdateAccumulator.getStorageToUpdate().get(address);
|
||||
if (map != null) {
|
||||
map.forEach(
|
||||
(storageSlotKey, slot) -> {
|
||||
if (!slot.isUnchanged()) {
|
||||
slots.add(storageSlotKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the state of two accounts to check if their key properties are identical, excluding
|
||||
* any differences in their storage.
|
||||
*
|
||||
* <p>This method compares the following account properties: - Nonce - Balance - Code Hash
|
||||
*
|
||||
* <p>It returns true if these properties are equal for both accounts, and false otherwise. Note
|
||||
* that this comparison does not include the account's storage.
|
||||
*
|
||||
* @param prior The first account to compare (could be null).
|
||||
* @param next The second account to compare (could be null).
|
||||
* @return true if the account state properties are equal excluding storage, false otherwise.
|
||||
*/
|
||||
private boolean areAccountDetailsEqualExcludingStorage(
|
||||
final DiffBasedAccount prior, final DiffBasedAccount next) {
|
||||
return (prior == null && next == null)
|
||||
|| (prior != null
|
||||
&& next != null
|
||||
&& prior.getNonce() == next.getNonce()
|
||||
&& prior.getBalance().equals(next.getBalance())
|
||||
&& prior.getCodeHash().equals(next.getCodeHash()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the context of an account update, including the account's address and whether the
|
||||
* key details of the account (excluding storage) are considered equal.
|
||||
*
|
||||
* <p>This record holds two main pieces of information: - `address`: The address of the account
|
||||
* being updated. - `areAccountDetailsEqualExcludingStorage`: A boolean value indicating whether
|
||||
* the account details, excluding the storage (nonce, balance, and code hash), are considered
|
||||
* equal when compared to a previous state.
|
||||
*
|
||||
* <p>This record is used to track changes to account states and determine if key properties are
|
||||
* unchanged, which helps in detecting whether further action is needed for the account update.
|
||||
*/
|
||||
private record AccountUpdateContext(
|
||||
Address address, boolean areAccountDetailsEqualExcludingStorage) {
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
AccountUpdateContext that = (AccountUpdateContext) o;
|
||||
return address.equals(that.address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AccountUpdateContext{"
|
||||
+ "address="
|
||||
+ address
|
||||
+ ", areAccountDetailsEqualExcludingStorage="
|
||||
+ areAccountDetailsEqualExcludingStorage
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.processing;
|
||||
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator;
|
||||
import org.hyperledger.besu.evm.log.Log;
|
||||
|
||||
import java.util.List;
|
||||
@@ -54,6 +55,8 @@ public class TransactionProcessingResult
|
||||
private final ValidationResult<TransactionInvalidReason> validationResult;
|
||||
private final Optional<Bytes> revertReason;
|
||||
|
||||
public DiffBasedWorldStateUpdateAccumulator<?> accumulator;
|
||||
|
||||
public static TransactionProcessingResult invalid(
|
||||
final ValidationResult<TransactionInvalidReason> validationResult) {
|
||||
return new TransactionProcessingResult(
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.trie.NodeLoader;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.BonsaiAccount;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.BonsaiWorldStateProvider;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.cache.BonsaiCachedMerkleTrieLoader;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.cache.NoopBonsaiCachedMerkleTrieLoader;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldStateKeyValueStorage;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldStateLayerStorage;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedValue;
|
||||
@@ -60,7 +61,7 @@ import org.apache.tuweni.units.bigints.UInt256;
|
||||
|
||||
public class BonsaiWorldState extends DiffBasedWorldState {
|
||||
|
||||
protected final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader;
|
||||
protected BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader;
|
||||
|
||||
public BonsaiWorldState(
|
||||
final BonsaiWorldStateProvider archive,
|
||||
@@ -76,18 +77,6 @@ public class BonsaiWorldState extends DiffBasedWorldState {
|
||||
worldStateConfig);
|
||||
}
|
||||
|
||||
public BonsaiWorldState(
|
||||
final BonsaiWorldState worldState,
|
||||
final BonsaiCachedMerkleTrieLoader cachedMerkleTrieLoader) {
|
||||
this(
|
||||
new BonsaiWorldStateLayerStorage(worldState.getWorldStateStorage()),
|
||||
cachedMerkleTrieLoader,
|
||||
worldState.cachedWorldStorageManager,
|
||||
worldState.trieLogManager,
|
||||
worldState.accumulator.getEvmConfiguration(),
|
||||
WorldStateConfig.newBuilder(worldState.worldStateConfig).build());
|
||||
}
|
||||
|
||||
public BonsaiWorldState(
|
||||
final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage,
|
||||
final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader,
|
||||
@@ -450,6 +439,10 @@ public class BonsaiWorldState extends DiffBasedWorldState {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void disableCacheMerkleTrieLoader() {
|
||||
this.bonsaiCachedMerkleTrieLoader = new NoopBonsaiCachedMerkleTrieLoader();
|
||||
}
|
||||
|
||||
private MerkleTrie<Bytes, Bytes> createTrie(final NodeLoader nodeLoader, final Bytes32 rootHash) {
|
||||
if (worldStateConfig.isTrieDisabled()) {
|
||||
return new NoOpMerkleTrie<>();
|
||||
|
||||
@@ -124,7 +124,9 @@ public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffB
|
||||
diffBasedValue.getUpdated() != null
|
||||
? copyAccount(diffBasedValue.getUpdated(), this, true)
|
||||
: null;
|
||||
accountsToUpdate.put(address, new DiffBasedValue<>(copyPrior, copyUpdated));
|
||||
accountsToUpdate.put(
|
||||
address,
|
||||
new DiffBasedValue<>(copyPrior, copyUpdated, diffBasedValue.isLastStepCleared()));
|
||||
});
|
||||
source
|
||||
.getCodeToUpdate()
|
||||
@@ -132,7 +134,10 @@ public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffB
|
||||
(address, diffBasedValue) -> {
|
||||
codeToUpdate.put(
|
||||
address,
|
||||
new DiffBasedValue<>(diffBasedValue.getPrior(), diffBasedValue.getUpdated()));
|
||||
new DiffBasedValue<>(
|
||||
diffBasedValue.getPrior(),
|
||||
diffBasedValue.getUpdated(),
|
||||
diffBasedValue.isLastStepCleared()));
|
||||
});
|
||||
source
|
||||
.getStorageToUpdate()
|
||||
@@ -149,10 +154,13 @@ public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffB
|
||||
storageConsumingMap.put(
|
||||
storageSlotKey,
|
||||
new DiffBasedValue<>(
|
||||
uInt256DiffBasedValue.getPrior(), uInt256DiffBasedValue.getUpdated()));
|
||||
uInt256DiffBasedValue.getPrior(),
|
||||
uInt256DiffBasedValue.getUpdated(),
|
||||
uInt256DiffBasedValue.isLastStepCleared()));
|
||||
});
|
||||
});
|
||||
storageToClear.addAll(source.storageToClear);
|
||||
storageKeyHashLookup.putAll(source.storageKeyHashLookup);
|
||||
|
||||
this.isAccumulatorStateChanged = true;
|
||||
}
|
||||
@@ -211,6 +219,7 @@ public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffB
|
||||
uInt256DiffBasedValue.getPrior(), uInt256DiffBasedValue.getPrior()));
|
||||
});
|
||||
});
|
||||
storageKeyHashLookup.putAll(source.storageKeyHashLookup);
|
||||
this.isAccumulatorStateChanged = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,12 +101,13 @@ public class MainnetBlockValidatorTest {
|
||||
when(blockBodyValidator.validateBody(any(), any(), any(), any(), any(), any()))
|
||||
.thenReturn(true);
|
||||
when(blockBodyValidator.validateBodyLight(any(), any(), any(), any())).thenReturn(true);
|
||||
when(blockProcessor.processBlock(any(), any(), any())).thenReturn(successfulProcessingResult);
|
||||
when(blockProcessor.processBlock(eq(protocolContext), any(), any(), any()))
|
||||
.thenReturn(successfulProcessingResult);
|
||||
when(blockProcessor.processBlock(any(), any(), any(), any()))
|
||||
.thenReturn(successfulProcessingResult);
|
||||
when(blockProcessor.processBlock(any(), any(), any(), any(), any()))
|
||||
.thenReturn(successfulProcessingResult);
|
||||
when(blockProcessor.processBlock(any(), any(), any(), any(), any(), any(), any()))
|
||||
when(blockProcessor.processBlock(any(), any(), any(), any(), any(), any(), any(), any()))
|
||||
.thenReturn(successfulProcessingResult);
|
||||
|
||||
assertNoBadBlocks();
|
||||
@@ -211,7 +212,8 @@ public class MainnetBlockValidatorTest {
|
||||
|
||||
@Test
|
||||
public void validateAndProcessBlock_whenProcessBlockFails() {
|
||||
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(block)))
|
||||
when(blockProcessor.processBlock(
|
||||
eq(protocolContext), eq(blockchain), any(MutableWorldState.class), eq(block)))
|
||||
.thenReturn(BlockProcessingResult.FAILED);
|
||||
|
||||
BlockProcessingResult result =
|
||||
@@ -249,7 +251,7 @@ public class MainnetBlockValidatorTest {
|
||||
final String caseName, final Exception storageException) {
|
||||
doThrow(storageException)
|
||||
.when(blockProcessor)
|
||||
.processBlock(eq(blockchain), any(MutableWorldState.class), eq(block));
|
||||
.processBlock(eq(protocolContext), eq(blockchain), any(MutableWorldState.class), eq(block));
|
||||
|
||||
BlockProcessingResult result =
|
||||
mainnetBlockValidator.validateAndProcessBlock(
|
||||
@@ -288,7 +290,8 @@ public class MainnetBlockValidatorTest {
|
||||
final String caseName, final Exception cause) {
|
||||
final BlockProcessingResult exceptionalResult =
|
||||
new BlockProcessingResult(Optional.empty(), cause);
|
||||
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(block)))
|
||||
when(blockProcessor.processBlock(
|
||||
eq(protocolContext), eq(blockchain), any(MutableWorldState.class), eq(block)))
|
||||
.thenReturn(exceptionalResult);
|
||||
|
||||
BlockProcessingResult result =
|
||||
@@ -304,7 +307,8 @@ public class MainnetBlockValidatorTest {
|
||||
|
||||
@Test
|
||||
public void validateAndProcessBlock_withShouldRecordBadBlockFalse() {
|
||||
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(block)))
|
||||
when(blockProcessor.processBlock(
|
||||
eq(protocolContext), eq(blockchain), any(MutableWorldState.class), eq(block)))
|
||||
.thenReturn(BlockProcessingResult.FAILED);
|
||||
|
||||
BlockProcessingResult result =
|
||||
@@ -322,7 +326,8 @@ public class MainnetBlockValidatorTest {
|
||||
|
||||
@Test
|
||||
public void validateAndProcessBlock_withShouldRecordBadBlockTrue() {
|
||||
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(block)))
|
||||
when(blockProcessor.processBlock(
|
||||
eq(protocolContext), eq(blockchain), any(MutableWorldState.class), eq(block)))
|
||||
.thenReturn(BlockProcessingResult.FAILED);
|
||||
|
||||
BlockProcessingResult result =
|
||||
@@ -340,7 +345,8 @@ public class MainnetBlockValidatorTest {
|
||||
|
||||
@Test
|
||||
public void validateAndProcessBlock_withShouldRecordBadBlockNotSet() {
|
||||
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(block)))
|
||||
when(blockProcessor.processBlock(
|
||||
eq(protocolContext), eq(blockchain), any(MutableWorldState.class), eq(block)))
|
||||
.thenReturn(BlockProcessingResult.FAILED);
|
||||
|
||||
BlockProcessingResult result =
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.BlockBody;
|
||||
@@ -79,6 +80,7 @@ class AbstractBlockProcessorIntegrationTest {
|
||||
private static final KeyPair ACCOUNT_GENESIS_2_KEYPAIR =
|
||||
generateKeyPair("fc5141e75bf622179f8eedada7fab3e2e6b3e3da8eb9df4f46d84df22df7430e");
|
||||
|
||||
private ProtocolContext protocolContext;
|
||||
private WorldStateArchive worldStateArchive;
|
||||
private DefaultBlockchain blockchain;
|
||||
private Address coinbase;
|
||||
@@ -94,6 +96,7 @@ class AbstractBlockProcessorIntegrationTest {
|
||||
final BlockHeader blockHeader = new BlockHeaderTestFixture().number(0L).buildHeader();
|
||||
coinbase = blockHeader.getCoinbase();
|
||||
worldStateArchive = contextTestFixture.getStateArchive();
|
||||
protocolContext = contextTestFixture.getProtocolContext();
|
||||
blockchain = (DefaultBlockchain) contextTestFixture.getBlockchain();
|
||||
}
|
||||
|
||||
@@ -227,7 +230,7 @@ class AbstractBlockProcessorIntegrationTest {
|
||||
transactionTransfer2);
|
||||
|
||||
BlockProcessingResult blockProcessingResult =
|
||||
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
|
||||
blockProcessor.processBlock(protocolContext, blockchain, worldState, blockWithTransactions);
|
||||
|
||||
BonsaiAccount updatedSenderAccount1 =
|
||||
(BonsaiAccount) worldState.get(transactionTransfer1.getSender());
|
||||
@@ -269,7 +272,7 @@ class AbstractBlockProcessorIntegrationTest {
|
||||
transferTransaction3);
|
||||
|
||||
BlockProcessingResult blockProcessingResult =
|
||||
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
|
||||
blockProcessor.processBlock(protocolContext, blockchain, worldState, blockWithTransactions);
|
||||
|
||||
BonsaiAccount updatedSenderAccount =
|
||||
(BonsaiAccount) worldState.get(transferTransaction1.getSender());
|
||||
@@ -322,7 +325,7 @@ class AbstractBlockProcessorIntegrationTest {
|
||||
transferTransaction2);
|
||||
|
||||
BlockProcessingResult blockProcessingResult =
|
||||
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
|
||||
blockProcessor.processBlock(protocolContext, blockchain, worldState, blockWithTransactions);
|
||||
|
||||
BonsaiAccount updatedSenderAccount1 =
|
||||
(BonsaiAccount) worldState.get(transferTransaction1.getSender());
|
||||
@@ -380,7 +383,7 @@ class AbstractBlockProcessorIntegrationTest {
|
||||
transferTransaction2);
|
||||
|
||||
BlockProcessingResult blockProcessingResult =
|
||||
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
|
||||
blockProcessor.processBlock(protocolContext, blockchain, worldState, blockWithTransactions);
|
||||
|
||||
BonsaiAccount updatedSenderAccount1 =
|
||||
(BonsaiAccount) worldState.get(transferTransaction1.getSender());
|
||||
@@ -429,7 +432,7 @@ class AbstractBlockProcessorIntegrationTest {
|
||||
|
||||
MutableWorldState worldState = worldStateArchive.getWorldState();
|
||||
BlockProcessingResult blockProcessingResult =
|
||||
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
|
||||
blockProcessor.processBlock(protocolContext, blockchain, worldState, blockWithTransactions);
|
||||
|
||||
assertTrue(blockProcessingResult.isSuccessful());
|
||||
|
||||
@@ -465,7 +468,7 @@ class AbstractBlockProcessorIntegrationTest {
|
||||
MutableWorldState worldState = worldStateArchive.getWorldState();
|
||||
|
||||
BlockProcessingResult blockProcessingResult =
|
||||
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
|
||||
blockProcessor.processBlock(protocolContext, blockchain, worldState, blockWithTransactions);
|
||||
|
||||
assertTrue(blockProcessingResult.isSuccessful());
|
||||
|
||||
@@ -508,7 +511,7 @@ class AbstractBlockProcessorIntegrationTest {
|
||||
MutableWorldState worldState = worldStateArchive.getWorldState();
|
||||
|
||||
BlockProcessingResult blockProcessingResult =
|
||||
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
|
||||
blockProcessor.processBlock(protocolContext, blockchain, worldState, blockWithTransactions);
|
||||
|
||||
assertTrue(blockProcessingResult.isSuccessful());
|
||||
|
||||
@@ -554,7 +557,7 @@ class AbstractBlockProcessorIntegrationTest {
|
||||
MutableWorldState worldState = worldStateArchive.getWorldState();
|
||||
|
||||
BlockProcessingResult blockProcessingResult =
|
||||
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
|
||||
blockProcessor.processBlock(protocolContext, blockchain, worldState, blockWithTransactions);
|
||||
assertTrue(blockProcessingResult.isSuccessful());
|
||||
|
||||
// Verify the state
|
||||
@@ -599,7 +602,7 @@ class AbstractBlockProcessorIntegrationTest {
|
||||
MutableWorldState worldState = worldStateArchive.getWorldState();
|
||||
|
||||
BlockProcessingResult blockProcessingResult =
|
||||
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
|
||||
blockProcessor.processBlock(protocolContext, blockchain, worldState, blockWithTransactions);
|
||||
|
||||
assertTrue(blockProcessingResult.isSuccessful());
|
||||
|
||||
@@ -645,7 +648,7 @@ class AbstractBlockProcessorIntegrationTest {
|
||||
MutableWorldState worldState = worldStateArchive.getWorldState();
|
||||
|
||||
BlockProcessingResult blockProcessingResult =
|
||||
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
|
||||
blockProcessor.processBlock(protocolContext, blockchain, worldState, blockWithTransactions);
|
||||
|
||||
assertTrue(blockProcessingResult.isSuccessful());
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.GWei;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
|
||||
@@ -49,6 +50,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
abstract class AbstractBlockProcessorTest {
|
||||
|
||||
@Mock private ProtocolContext protocolContext;
|
||||
@Mock private MainnetTransactionProcessor transactionProcessor;
|
||||
@Mock private AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory;
|
||||
@Mock private ProtocolSchedule protocolSchedule;
|
||||
@@ -84,7 +86,14 @@ abstract class AbstractBlockProcessorTest {
|
||||
void withProcessorAndEmptyWithdrawals_WithdrawalsAreNotProcessed() {
|
||||
when(protocolSpec.getWithdrawalsProcessor()).thenReturn(Optional.empty());
|
||||
blockProcessor.processBlock(
|
||||
blockchain, worldState, emptyBlockHeader, emptyList(), emptyList(), Optional.empty(), null);
|
||||
protocolContext,
|
||||
blockchain,
|
||||
worldState,
|
||||
emptyBlockHeader,
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
Optional.empty(),
|
||||
null);
|
||||
verify(withdrawalsProcessor, never()).processWithdrawals(any(), any());
|
||||
}
|
||||
|
||||
@@ -92,7 +101,14 @@ abstract class AbstractBlockProcessorTest {
|
||||
void withNoProcessorAndEmptyWithdrawals_WithdrawalsAreNotProcessed() {
|
||||
when(protocolSpec.getWithdrawalsProcessor()).thenReturn(Optional.empty());
|
||||
blockProcessor.processBlock(
|
||||
blockchain, worldState, emptyBlockHeader, emptyList(), emptyList(), Optional.empty(), null);
|
||||
protocolContext,
|
||||
blockchain,
|
||||
worldState,
|
||||
emptyBlockHeader,
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
Optional.empty(),
|
||||
null);
|
||||
verify(withdrawalsProcessor, never()).processWithdrawals(any(), any());
|
||||
}
|
||||
|
||||
@@ -102,6 +118,7 @@ abstract class AbstractBlockProcessorTest {
|
||||
final List<Withdrawal> withdrawals =
|
||||
List.of(new Withdrawal(UInt64.ONE, UInt64.ONE, Address.fromHexString("0x1"), GWei.ONE));
|
||||
blockProcessor.processBlock(
|
||||
protocolContext,
|
||||
blockchain,
|
||||
worldState,
|
||||
emptyBlockHeader,
|
||||
@@ -119,6 +136,7 @@ abstract class AbstractBlockProcessorTest {
|
||||
final List<Withdrawal> withdrawals =
|
||||
List.of(new Withdrawal(UInt64.ONE, UInt64.ONE, Address.fromHexString("0x1"), GWei.ONE));
|
||||
blockProcessor.processBlock(
|
||||
protocolContext,
|
||||
blockchain,
|
||||
worldState,
|
||||
emptyBlockHeader,
|
||||
|
||||
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
|
||||
@@ -45,6 +46,7 @@ public class MainnetBlockProcessorTest extends AbstractBlockProcessorTest {
|
||||
mock(AbstractBlockProcessor.TransactionReceiptFactory.class);
|
||||
private final ProtocolSchedule protocolSchedule = mock(ProtocolSchedule.class);
|
||||
private final ProtocolSpec protocolSpec = mock(ProtocolSpec.class);
|
||||
private final ProtocolContext protocolContext = mock(ProtocolContext.class);
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
@@ -72,7 +74,8 @@ public class MainnetBlockProcessorTest extends AbstractBlockProcessorTest {
|
||||
.transactionsRoot(Hash.EMPTY_LIST_HASH)
|
||||
.ommersHash(Hash.EMPTY_LIST_HASH)
|
||||
.buildHeader();
|
||||
blockProcessor.processBlock(blockchain, worldState, emptyBlockHeader, emptyList(), emptyList());
|
||||
blockProcessor.processBlock(
|
||||
protocolContext, blockchain, worldState, emptyBlockHeader, emptyList(), emptyList());
|
||||
|
||||
// An empty block with 0 reward should not change the world state
|
||||
assertThat(worldState.rootHash()).isEqualTo(initialHash);
|
||||
@@ -101,7 +104,8 @@ public class MainnetBlockProcessorTest extends AbstractBlockProcessorTest {
|
||||
"0xa6b5d50f7b3c39b969c2fe8fed091939c674fef49b4826309cb6994361e39b71"))
|
||||
.ommersHash(Hash.EMPTY_LIST_HASH)
|
||||
.buildHeader();
|
||||
blockProcessor.processBlock(blockchain, worldState, emptyBlockHeader, emptyList(), emptyList());
|
||||
blockProcessor.processBlock(
|
||||
protocolContext, blockchain, worldState, emptyBlockHeader, emptyList(), emptyList());
|
||||
|
||||
// An empty block with 0 reward should change the world state prior to EIP158
|
||||
assertThat(worldState.rootHash()).isNotEqualTo(initialHash);
|
||||
|
||||
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.when;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.enclave.Enclave;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.chain.TransactionLocation;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
@@ -66,6 +67,7 @@ class PrivacyBlockProcessorTest {
|
||||
private AbstractBlockProcessor blockProcessor;
|
||||
private WorldStateArchive privateWorldStateArchive;
|
||||
private Enclave enclave;
|
||||
private ProtocolContext protocolContext;
|
||||
private ProtocolSchedule protocolSchedule;
|
||||
private WorldStateArchive publicWorldStateArchive;
|
||||
|
||||
@@ -75,6 +77,7 @@ class PrivacyBlockProcessorTest {
|
||||
privateStateStorage = new PrivateStateKeyValueStorage(new InMemoryKeyValueStorage());
|
||||
privateWorldStateArchive = mock(WorldStateArchive.class);
|
||||
enclave = mock(Enclave.class);
|
||||
protocolContext = mock(ProtocolContext.class);
|
||||
protocolSchedule = mock(ProtocolSchedule.class);
|
||||
this.privacyBlockProcessor =
|
||||
new PrivacyBlockProcessor(
|
||||
@@ -101,16 +104,17 @@ class PrivacyBlockProcessorTest {
|
||||
final Block secondBlock =
|
||||
blockDataGenerator.block(
|
||||
BlockDataGenerator.BlockOptions.create().setParentHash(firstBlock.getHash()));
|
||||
privacyBlockProcessor.processBlock(blockchain, mutableWorldState, firstBlock);
|
||||
privacyBlockProcessor.processBlock(protocolContext, blockchain, mutableWorldState, firstBlock);
|
||||
privateStateStorage
|
||||
.updater()
|
||||
.putPrivacyGroupHeadBlockMap(firstBlock.getHash(), expected)
|
||||
.commit();
|
||||
privacyBlockProcessor.processBlock(blockchain, mutableWorldState, secondBlock);
|
||||
privacyBlockProcessor.processBlock(protocolContext, blockchain, mutableWorldState, secondBlock);
|
||||
assertThat(privateStateStorage.getPrivacyGroupHeadBlockMap(secondBlock.getHash()))
|
||||
.contains(expected);
|
||||
verify(blockProcessor)
|
||||
.processBlock(
|
||||
eq(protocolContext),
|
||||
eq(blockchain),
|
||||
eq(mutableWorldState),
|
||||
eq(firstBlock.getHeader()),
|
||||
@@ -120,6 +124,7 @@ class PrivacyBlockProcessorTest {
|
||||
any());
|
||||
verify(blockProcessor)
|
||||
.processBlock(
|
||||
eq(protocolContext),
|
||||
eq(blockchain),
|
||||
eq(mutableWorldState),
|
||||
eq(secondBlock.getHeader()),
|
||||
@@ -172,9 +177,10 @@ class PrivacyBlockProcessorTest {
|
||||
firstBlock.getHash(), VALID_BASE64_ENCLAVE_KEY, PrivateBlockMetadata.empty())
|
||||
.commit();
|
||||
|
||||
privacyBlockProcessor.processBlock(blockchain, mutableWorldState, secondBlock);
|
||||
privacyBlockProcessor.processBlock(protocolContext, blockchain, mutableWorldState, secondBlock);
|
||||
verify(blockProcessor)
|
||||
.processBlock(
|
||||
eq(protocolContext),
|
||||
eq(blockchain),
|
||||
eq(mutableWorldState),
|
||||
eq(secondBlock.getHeader()),
|
||||
|
||||
@@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -26,6 +27,8 @@ import static org.mockito.Mockito.when;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
@@ -42,6 +45,7 @@ import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorld
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.trielog.NoOpTrieLogManager;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator;
|
||||
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
import org.hyperledger.besu.evm.blockhash.BlockHashLookup;
|
||||
import org.hyperledger.besu.evm.internal.EvmConfiguration;
|
||||
import org.hyperledger.besu.evm.tracing.OperationTracer;
|
||||
@@ -62,7 +66,9 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
class ParallelizedConcurrentTransactionProcessorTest {
|
||||
|
||||
@Mock private MainnetTransactionProcessor transactionProcessor;
|
||||
@Mock private BlockHeader chainHeadBlockHeader;
|
||||
@Mock private BlockHeader blockHeader;
|
||||
@Mock ProtocolContext protocolContext;
|
||||
@Mock private Transaction transaction;
|
||||
@Mock private PrivateMetadataUpdater privateMetadataUpdater;
|
||||
@Mock private TransactionCollisionDetector transactionCollisionDetector;
|
||||
@@ -89,6 +95,19 @@ class ParallelizedConcurrentTransactionProcessorTest {
|
||||
new NoOpTrieLogManager(),
|
||||
EvmConfiguration.DEFAULT,
|
||||
createStatefulConfigWithTrie());
|
||||
|
||||
when(chainHeadBlockHeader.getHash()).thenReturn(Hash.ZERO);
|
||||
when(chainHeadBlockHeader.getStateRoot()).thenReturn(Hash.EMPTY_TRIE_HASH);
|
||||
when(blockHeader.getParentHash()).thenReturn(Hash.ZERO);
|
||||
|
||||
when(transaction.detachedCopy()).thenReturn(transaction);
|
||||
|
||||
final MutableBlockchain blockchain = mock(MutableBlockchain.class);
|
||||
when(protocolContext.getBlockchain()).thenReturn(blockchain);
|
||||
when(blockchain.getChainHeadHeader()).thenReturn(chainHeadBlockHeader);
|
||||
final WorldStateArchive worldStateArchive = mock(WorldStateArchive.class);
|
||||
when(protocolContext.getWorldStateArchive()).thenReturn(worldStateArchive);
|
||||
when(worldStateArchive.getWorldState(any())).thenReturn(Optional.of(worldState));
|
||||
when(transactionCollisionDetector.hasCollision(any(), any(), any(), any())).thenReturn(false);
|
||||
}
|
||||
|
||||
@@ -105,7 +124,7 @@ class ParallelizedConcurrentTransactionProcessorTest {
|
||||
Collections.emptyList(), 0, 0, Bytes.EMPTY, ValidationResult.valid()));
|
||||
|
||||
processor.runTransaction(
|
||||
worldState,
|
||||
protocolContext,
|
||||
blockHeader,
|
||||
0,
|
||||
transaction,
|
||||
@@ -151,7 +170,7 @@ class ParallelizedConcurrentTransactionProcessorTest {
|
||||
Optional.of(Bytes.EMPTY)));
|
||||
|
||||
processor.runTransaction(
|
||||
worldState,
|
||||
protocolContext,
|
||||
blockHeader,
|
||||
0,
|
||||
transaction,
|
||||
@@ -180,7 +199,7 @@ class ParallelizedConcurrentTransactionProcessorTest {
|
||||
Collections.emptyList(), 0, 0, Bytes.EMPTY, ValidationResult.valid()));
|
||||
|
||||
processor.runTransaction(
|
||||
worldState,
|
||||
protocolContext,
|
||||
blockHeader,
|
||||
0,
|
||||
transaction,
|
||||
|
||||
@@ -19,17 +19,21 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.StorageSlotKey;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.BonsaiAccount;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldStateUpdateAccumulator;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedValue;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.preload.StorageConsumingMap;
|
||||
import org.hyperledger.besu.evm.internal.EvmConfiguration;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -168,16 +172,22 @@ class TransactionCollisionDetectorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCollisionWithModifiedStorageRoot() {
|
||||
void testCollisionWithModifiedStorageRootAndSameSlot() {
|
||||
final Address address = Address.fromHexString("0x1");
|
||||
final BonsaiAccount priorAccountValue = createAccount(address);
|
||||
final BonsaiAccount nextAccountValue = new BonsaiAccount(priorAccountValue, worldState, true);
|
||||
nextAccountValue.setStorageRoot(Hash.EMPTY);
|
||||
|
||||
// Simulate that the address was already modified in the block
|
||||
final StorageSlotKey updateStorageSlotKey = new StorageSlotKey(UInt256.ONE);
|
||||
// Simulate that the address slot was already modified in the block
|
||||
bonsaiUpdater
|
||||
.getAccountsToUpdate()
|
||||
.put(address, new DiffBasedValue<>(priorAccountValue, nextAccountValue));
|
||||
bonsaiUpdater
|
||||
.getStorageToUpdate()
|
||||
.computeIfAbsent(
|
||||
address,
|
||||
__ -> new StorageConsumingMap<>(address, new ConcurrentHashMap<>(), (___, ____) -> {}))
|
||||
.put(updateStorageSlotKey, new DiffBasedValue<>(UInt256.ONE, UInt256.ZERO));
|
||||
|
||||
final Transaction transaction = createTransaction(address, address);
|
||||
|
||||
@@ -185,6 +195,12 @@ class TransactionCollisionDetectorTest {
|
||||
trxUpdater
|
||||
.getAccountsToUpdate()
|
||||
.put(address, new DiffBasedValue<>(priorAccountValue, priorAccountValue));
|
||||
trxUpdater
|
||||
.getStorageToUpdate()
|
||||
.computeIfAbsent(
|
||||
address,
|
||||
__ -> new StorageConsumingMap<>(address, new ConcurrentHashMap<>(), (___, ____) -> {}))
|
||||
.put(updateStorageSlotKey, new DiffBasedValue<>(UInt256.ONE, UInt256.ONE));
|
||||
|
||||
boolean hasCollision =
|
||||
collisionDetector.hasCollision(
|
||||
@@ -196,6 +212,48 @@ class TransactionCollisionDetectorTest {
|
||||
assertTrue(hasCollision, "Expected a collision with the modified address");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCollisionWithModifiedStorageRootNotSameSlot() {
|
||||
final Address address = Address.fromHexString("0x1");
|
||||
final BonsaiAccount priorAccountValue = createAccount(address);
|
||||
final BonsaiAccount nextAccountValue = new BonsaiAccount(priorAccountValue, worldState, true);
|
||||
nextAccountValue.setStorageRoot(Hash.EMPTY);
|
||||
// Simulate that the address slot was already modified in the block
|
||||
bonsaiUpdater
|
||||
.getAccountsToUpdate()
|
||||
.put(address, new DiffBasedValue<>(priorAccountValue, nextAccountValue));
|
||||
bonsaiUpdater
|
||||
.getStorageToUpdate()
|
||||
.computeIfAbsent(
|
||||
address,
|
||||
__ -> new StorageConsumingMap<>(address, new ConcurrentHashMap<>(), (___, ____) -> {}))
|
||||
.put(new StorageSlotKey(UInt256.ZERO), new DiffBasedValue<>(UInt256.ONE, UInt256.ZERO));
|
||||
|
||||
final Transaction transaction = createTransaction(address, address);
|
||||
|
||||
// Simulate that the address is read in the next transaction
|
||||
trxUpdater
|
||||
.getAccountsToUpdate()
|
||||
.put(address, new DiffBasedValue<>(priorAccountValue, priorAccountValue));
|
||||
trxUpdater
|
||||
.getStorageToUpdate()
|
||||
.computeIfAbsent(
|
||||
address,
|
||||
__ -> new StorageConsumingMap<>(address, new ConcurrentHashMap<>(), (___, ____) -> {}))
|
||||
.put(new StorageSlotKey(UInt256.ONE), new DiffBasedValue<>(UInt256.ONE, UInt256.ONE));
|
||||
|
||||
boolean hasCollision =
|
||||
collisionDetector.hasCollision(
|
||||
transaction,
|
||||
Address.ZERO,
|
||||
new ParallelizedTransactionContext(trxUpdater, null, false, Wei.ZERO),
|
||||
bonsaiUpdater);
|
||||
|
||||
assertFalse(
|
||||
hasCollision,
|
||||
"Expected no collision when storage roots are modified but different slots are updated.");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCollisionWithMiningBeneficiaryAddress() {
|
||||
final Address miningBeneficiary = Address.ZERO;
|
||||
@@ -244,7 +302,7 @@ class TransactionCollisionDetectorTest {
|
||||
final BonsaiAccount accountValue = createAccount(address);
|
||||
|
||||
// Simulate that the address was deleted in the block
|
||||
bonsaiUpdater.getDeletedAccountAddresses().add(address);
|
||||
bonsaiUpdater.getAccountsToUpdate().put(address, new DiffBasedValue<>(accountValue, null));
|
||||
|
||||
final Transaction transaction = createTransaction(address, address);
|
||||
|
||||
|
||||
@@ -343,7 +343,7 @@ public abstract class AbstractIsolationTests {
|
||||
protocolSchedule
|
||||
.getByBlockHeader(blockHeader(0))
|
||||
.getBlockProcessor()
|
||||
.processBlock(blockchain, ws, block);
|
||||
.processBlock(protocolContext, blockchain, ws, block);
|
||||
blockchain.appendBlock(block, res.getReceipts());
|
||||
return res;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user