Journaled world state (#6023)

Introduce a new Journaled World State Updater. Within a transaction it
keeps one copy of account and storage state, restoring previous 
revisions on reverts and exceptional halts. This updater only supports 
post-merge semantics with regard to empty accounts, namely that they do
not exist in world state.

Adds an EvmConfiguration option for stacked vs journaled updater, and
wire it in where needed. The staked updater is default mode, which is
the current behavior prior to this patch.

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
pull/6110/head
Danno Ferrin 1 year ago committed by GitHub
parent 84dee295d9
commit 094c8416df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  2. 15
      besu/src/main/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilder.java
  3. 19
      besu/src/main/java/org/hyperledger/besu/cli/options/unstable/EvmOptions.java
  4. 71
      besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java
  5. 23
      besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java
  6. 7
      build.gradle
  7. 5
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceBlock.java
  8. 5
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java
  9. 3
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java
  10. 8
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/diff/StateDiffGenerator.java
  11. 7
      ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/worldstate/PrunerIntegrationTest.java
  12. 20
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiAccount.java
  13. 11
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateProvider.java
  14. 21
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/cache/CachedWorldStorageManager.java
  15. 13
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldState.java
  16. 13
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java
  17. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java
  18. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java
  19. 59
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldState.java
  20. 11
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultWorldStateArchive.java
  21. 15
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryKeyValueStorageProvider.java
  22. 12
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryPrivacyStorageProvider.java
  23. 16
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java
  24. 4
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/AbstractIsolationTests.java
  25. 63
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateArchiveTest.java
  26. 22
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/LogRollingTests.java
  27. 5
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/RollingImport.java
  28. 9
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/trielog/TrieLogManagerTests.java
  29. 6
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldStateTest.java
  30. 17
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/MarkSweepPrunerTest.java
  31. 103
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/FastWorldStateDownloaderTest.java
  32. 2
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BenchmarkSubCommand.java
  33. 10
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainModule.java
  34. 3
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmTool.java
  35. 16
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java
  36. 31
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommandOptionsModule.java
  37. 4
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/GenesisFileModule.java
  38. 5
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/MainnetGenesisFileModule.java
  39. 92
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nServerSubCommand.java
  40. 7
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java
  41. 6
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestUpdateAccumulator.java
  42. 45
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldState.java
  43. 6
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/DefaultReferenceTestWorldState.java
  44. 9
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java
  45. 26
      ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java
  46. 35
      ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java
  47. 3
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java
  48. 9
      evm/src/main/java/org/hyperledger/besu/collections/undo/UndoList.java
  49. 18
      evm/src/main/java/org/hyperledger/besu/collections/undo/UndoMap.java
  50. 181
      evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java
  51. 126
      evm/src/main/java/org/hyperledger/besu/collections/undo/UndoScalar.java
  52. 11
      evm/src/main/java/org/hyperledger/besu/collections/undo/UndoSet.java
  53. 9
      evm/src/main/java/org/hyperledger/besu/collections/undo/UndoTable.java
  54. 10
      evm/src/main/java/org/hyperledger/besu/collections/undo/Undoable.java
  55. 12
      evm/src/main/java/org/hyperledger/besu/evm/EVM.java
  56. 30
      evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java
  57. 34
      evm/src/main/java/org/hyperledger/besu/evm/internal/EvmConfiguration.java
  58. 19
      evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java
  59. 14
      evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java
  60. 397
      evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java
  61. 184
      evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java
  62. 8
      evm/src/main/java/org/hyperledger/besu/evm/worldstate/StackedUpdater.java
  63. 14
      evm/src/main/java/org/hyperledger/besu/evm/worldstate/UpdateTrackingAccount.java
  64. 7
      evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java
  65. 14
      evm/src/test/java/org/hyperledger/besu/evm/toy/ToyAccount.java

@ -3433,6 +3433,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
}
builder.setTxPoolImplementation(buildTransactionPoolConfiguration().getTxPoolImplementation());
builder.setWorldStateUpdateMode(unstableEvmOptions.toDomainObject().worldUpdaterMode());
builder.setPluginContext(besuComponent.getBesuPluginContext());

@ -16,6 +16,7 @@ package org.hyperledger.besu.cli;
import org.hyperledger.besu.BesuInfo;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.services.BesuPluginContextImpl;
import org.hyperledger.besu.util.log.FramedLogMessage;
import org.hyperledger.besu.util.platform.PlatformDetector;
@ -50,6 +51,7 @@ public class ConfigurationOverviewBuilder {
private String engineJwtFilePath;
private boolean isHighSpec = false;
private TransactionPoolConfiguration.Implementation txPoolImplementation;
private EvmConfiguration.WorldUpdaterMode worldStateUpdateMode;
private Map<String, String> environment;
private BesuPluginContextImpl besuPluginContext;
@ -181,6 +183,18 @@ public class ConfigurationOverviewBuilder {
return this;
}
/**
* Sets the world state updater mode
*
* @param worldStateUpdateMode the world state updater mode
* @return the builder
*/
public ConfigurationOverviewBuilder setWorldStateUpdateMode(
final EvmConfiguration.WorldUpdaterMode worldStateUpdateMode) {
this.worldStateUpdateMode = worldStateUpdateMode;
return this;
}
/**
* Sets the engine jwt file path.
*
@ -257,6 +271,7 @@ public class ConfigurationOverviewBuilder {
}
lines.add("Using " + txPoolImplementation + " transaction pool implementation");
lines.add("Using " + worldStateUpdateMode + " worldstate update mode");
lines.add("");
lines.add("Host:");

@ -18,7 +18,6 @@ package org.hyperledger.besu.cli.options.unstable;
import org.hyperledger.besu.cli.options.CLIOptions;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import java.util.Arrays;
import java.util.List;
import picocli.CommandLine;
@ -29,6 +28,9 @@ public class EvmOptions implements CLIOptions<EvmConfiguration> {
/** The constant JUMPDEST_CACHE_WEIGHT. */
public static final String JUMPDEST_CACHE_WEIGHT = "--Xevm-jumpdest-cache-weight-kb";
/** The constant WORLDSTATE_UPDATE_MODE. */
public static final String WORLDSTATE_UPDATE_MODE = "--Xevm-worldstate-update-mode";
/**
* Create evm options.
*
@ -51,13 +53,24 @@ public class EvmOptions implements CLIOptions<EvmConfiguration> {
private Long jumpDestCacheWeightKilobytes =
32_000L; // 10k contracts, (25k max contract size / 8 bit) + 32byte hash
@CommandLine.Option(
names = {WORLDSTATE_UPDATE_MODE},
description = "How to handle worldstate updates within a transaction",
fallbackValue = "STACKED",
defaultValue = "STACKED",
hidden = true,
arity = "1")
private EvmConfiguration.WorldUpdaterMode worldstateUpdateMode =
EvmConfiguration.WorldUpdaterMode
.STACKED; // Stacked Updater. Years of battle tested correctness.
@Override
public EvmConfiguration toDomainObject() {
return new EvmConfiguration(jumpDestCacheWeightKilobytes);
return new EvmConfiguration(jumpDestCacheWeightKilobytes, worldstateUpdateMode);
}
@Override
public List<String> getCLIOptions() {
return Arrays.asList(JUMPDEST_CACHE_WEIGHT);
return List.of(JUMPDEST_CACHE_WEIGHT, WORLDSTATE_UPDATE_MODE);
}
}

@ -632,10 +632,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
if (chainPrunerConfiguration.getChainPruningEnabled()) {
protocolContext
.safeConsensusContext(MergeContext.class)
.ifPresent(
mergeContext -> {
mergeContext.setIsChainPruningEnabled(true);
});
.ifPresent(mergeContext -> mergeContext.setIsChainPruningEnabled(true));
final ChainDataPruner chainDataPruner = createChainPruner(blockchainStorage);
blockchain.observeBlockAdded(chainDataPruner);
LOG.info(
@ -776,7 +773,6 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
final SubProtocolConfiguration subProtocolConfiguration =
createSubProtocolConfiguration(ethProtocolManager, maybeSnapProtocolManager);
;
final JsonRpcMethods additionalJsonRpcMethodFactory =
createAdditionalJsonRpcMethodFactory(protocolContext);
@ -831,24 +827,21 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
final EthProtocolManager ethProtocolManager,
final PivotBlockSelector pivotBlockSelector) {
final DefaultSynchronizer toUse =
new DefaultSynchronizer(
syncConfig,
protocolSchedule,
protocolContext,
worldStateStorage,
ethProtocolManager.getBlockBroadcaster(),
maybePruner,
ethContext,
syncState,
dataDirectory,
storageProvider,
clock,
metricsSystem,
getFullSyncTerminationCondition(protocolContext.getBlockchain()),
pivotBlockSelector);
return toUse;
return new DefaultSynchronizer(
syncConfig,
protocolSchedule,
protocolContext,
worldStateStorage,
ethProtocolManager.getBlockBroadcaster(),
maybePruner,
ethContext,
syncState,
dataDirectory,
storageProvider,
clock,
metricsSystem,
getFullSyncTerminationCondition(protocolContext.getBlockchain()),
pivotBlockSelector);
}
private PivotBlockSelector createPivotSelector(
@ -930,9 +923,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
final SubProtocolConfiguration subProtocolConfiguration =
new SubProtocolConfiguration().withSubProtocol(EthProtocol.get(), ethProtocolManager);
maybeSnapProtocolManager.ifPresent(
snapProtocolManager -> {
subProtocolConfiguration.withSubProtocol(SnapProtocol.get(), snapProtocolManager);
});
snapProtocolManager ->
subProtocolConfiguration.withSubProtocol(SnapProtocol.get(), snapProtocolManager));
return subProtocolConfiguration;
}
@ -1071,22 +1063,21 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
final WorldStateStorage worldStateStorage,
final Blockchain blockchain,
final CachedMerkleTrieLoader cachedMerkleTrieLoader) {
switch (dataStorageConfiguration.getDataStorageFormat()) {
case BONSAI:
return new BonsaiWorldStateProvider(
(BonsaiWorldStateKeyValueStorage) worldStateStorage,
blockchain,
Optional.of(dataStorageConfiguration.getBonsaiMaxLayersToLoad()),
cachedMerkleTrieLoader,
metricsSystem,
besuComponent.map(BesuComponent::getBesuPluginContext).orElse(null));
case FOREST:
default:
return switch (dataStorageConfiguration.getDataStorageFormat()) {
case BONSAI -> new BonsaiWorldStateProvider(
(BonsaiWorldStateKeyValueStorage) worldStateStorage,
blockchain,
Optional.of(dataStorageConfiguration.getBonsaiMaxLayersToLoad()),
cachedMerkleTrieLoader,
metricsSystem,
besuComponent.map(BesuComponent::getBesuPluginContext).orElse(null),
evmConfiguration);
case FOREST -> {
final WorldStatePreimageStorage preimageStorage =
storageProvider.createWorldStatePreimageStorage();
return new DefaultWorldStateArchive(worldStateStorage, preimageStorage);
}
yield new DefaultWorldStateArchive(worldStateStorage, preimageStorage, evmConfiguration);
}
};
}
private ChainDataPruner createChainPruner(final BlockchainStorage blockchainStorage) {

@ -19,6 +19,8 @@ import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConf
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LEGACY;
import static org.mockito.Mockito.mock;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
@ -159,4 +161,25 @@ class ConfigurationOverviewBuilderTest {
final String legacyTxPoolSelected = builder.build();
assertThat(legacyTxPoolSelected).contains("Using LEGACY transaction pool implementation");
}
@Test
void setWorldStateUpdateModeDefault() {
builder.setWorldStateUpdateMode(EvmConfiguration.DEFAULT.worldUpdaterMode());
final String layeredTxPoolSelected = builder.build();
assertThat(layeredTxPoolSelected).contains("Using STACKED worldstate update mode");
}
@Test
void setWorldStateUpdateModeStacked() {
builder.setWorldStateUpdateMode(EvmConfiguration.WorldUpdaterMode.STACKED);
final String layeredTxPoolSelected = builder.build();
assertThat(layeredTxPoolSelected).contains("Using STACKED worldstate update mode");
}
@Test
void setWorldStateUpdateModeJournaled() {
builder.setWorldStateUpdateMode(EvmConfiguration.WorldUpdaterMode.JOURNALED);
final String layeredTxPoolSelected = builder.build();
assertThat(layeredTxPoolSelected).contains("Using JOURNALED worldstate update mode");
}
}

@ -227,6 +227,13 @@ allprojects {
options.encoding = 'UTF-8'
}
// IntelliJ workaround to allow repeated debugging of unchanged code
tasks.withType(JavaExec) {
if (it.name.contains(".")) {
outputs.upToDateWhen { false }
}
}
/*
* Pass some system properties provided on the gradle command line to test executions for
* convenience.

@ -35,7 +35,6 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.evm.worldstate.StackedUpdater;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.metrics.BesuMetricCategory;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
@ -208,8 +207,8 @@ public class TraceBlock extends AbstractBlockParameterMethod {
// if we have no prior updater, it must be the first TX, so use the block's initial state
if (updater == null) {
updater = worldState.updater();
} else if (updater instanceof StackedUpdater) {
((StackedUpdater) updater).markTransactionBoundary();
} else {
updater.markTransactionBoundary();
}
updater = updater.updater();
return updater;

@ -21,7 +21,6 @@ import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.evm.worldstate.StackedUpdater;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.List;
@ -58,8 +57,8 @@ public class BlockTracer {
// if we have no prior updater, it must be the first TX, so use the block's initial state
if (chainedUpdater == null) {
chainedUpdater = mutableWorldState.updater();
} else if (chainedUpdater instanceof StackedUpdater<?, ?> stackedUpdater) {
stackedUpdater.markTransactionBoundary();
} else {
chainedUpdater.markTransactionBoundary();
}
// create an updater for just this tx
chainedUpdater = chainedUpdater.updater();

@ -33,7 +33,6 @@ import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.tracing.StandardJsonTracer;
import org.hyperledger.besu.evm.worldstate.StackedUpdater;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.io.File;
@ -123,7 +122,7 @@ public class TransactionTracer {
.map(parent -> calculateExcessBlobGasForParent(protocolSpec, parent))
.orElse(BlobGas.ZERO));
for (int i = 0; i < body.getTransactions().size(); i++) {
((StackedUpdater<?, ?>) stackedUpdater).markTransactionBoundary();
stackedUpdater.markTransactionBoundary();
final Transaction transaction = body.getTransactions().get(i);
if (selectedHash.isEmpty()
|| selectedHash.filter(isEqual(transaction.getHash())).isPresent()) {

@ -22,7 +22,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.Trace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.TracingUtils;
import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.worldstate.UpdateTrackingAccount;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.Collections;
@ -39,7 +39,7 @@ public class StateDiffGenerator {
public Stream<Trace> generateStateDiff(final TransactionTrace transactionTrace) {
final List<TraceFrame> traceFrames = transactionTrace.getTraceFrames();
if (traceFrames.size() < 1) {
if (traceFrames.isEmpty()) {
return Stream.empty();
}
@ -60,9 +60,7 @@ public class StateDiffGenerator {
// calculate storage diff
final Map<String, DiffNode> storageDiff = new TreeMap<>();
for (final Map.Entry<UInt256, UInt256> entry :
((UpdateTrackingAccount<?>) updatedAccount)
.getUpdatedStorage()
.entrySet()) { // FIXME cast
((MutableAccount) updatedAccount).getUpdatedStorage().entrySet()) {
final UInt256 newValue = entry.getValue();
if (rootAccount == null) {
if (!UInt256.ZERO.equals(newValue)) {

@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValue
import org.hyperledger.besu.ethereum.trie.MerkleTrie;
import org.hyperledger.besu.ethereum.trie.patricia.StoredMerklePatriciaTrie;
import org.hyperledger.besu.ethereum.worldstate.Pruner.PruningPhase;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.WorldState;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
@ -59,7 +60,9 @@ public class PrunerIntegrationTest {
private final WorldStateStorage worldStateStorage = new WorldStateKeyValueStorage(stateStorage);
private final WorldStateArchive worldStateArchive =
new DefaultWorldStateArchive(
worldStateStorage, new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()));
worldStateStorage,
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()),
EvmConfiguration.DEFAULT);
private final InMemoryKeyValueStorage markStorage = new InMemoryKeyValueStorage();
private final Block genesisBlock = gen.genesisBlock();
private final MutableBlockchain blockchain = createInMemoryBlockchain(genesisBlock);
@ -226,7 +229,7 @@ public class PrunerIntegrationTest {
private void collectTrieNodes(final MerkleTrie<Bytes32, Bytes> trie, final Set<Bytes> collector) {
final Bytes32 rootHash = trie.getRootHash();
trie.visitAll(
(node) -> {
node -> {
if (node.isReferencedByHash() || node.getHash().equals(rootHash)) {
collector.add(node.getEncodedBytes());
}

@ -42,7 +42,7 @@ import org.apache.tuweni.units.bigints.UInt256;
public class BonsaiAccount implements MutableAccount, AccountValue {
private final BonsaiWorldView context;
private boolean mutable;
private boolean immutable;
private final Address address;
private final Hash addressHash;
@ -71,7 +71,7 @@ public class BonsaiAccount implements MutableAccount, AccountValue {
this.storageRoot = storageRoot;
this.codeHash = codeHash;
this.mutable = mutable;
this.immutable = !mutable;
}
public BonsaiAccount(
@ -106,7 +106,7 @@ public class BonsaiAccount implements MutableAccount, AccountValue {
this.code = toCopy.code;
updatedStorage.putAll(toCopy.updatedStorage);
this.mutable = mutable;
this.immutable = !mutable;
}
public BonsaiAccount(
@ -121,7 +121,7 @@ public class BonsaiAccount implements MutableAccount, AccountValue {
this.code = tracked.getCode();
updatedStorage.putAll(tracked.getUpdatedStorage());
this.mutable = true;
this.immutable = false;
}
public static BonsaiAccount fromRLP(
@ -161,7 +161,7 @@ public class BonsaiAccount implements MutableAccount, AccountValue {
@Override
public void setNonce(final long value) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
nonce = value;
@ -174,7 +174,7 @@ public class BonsaiAccount implements MutableAccount, AccountValue {
@Override
public void setBalance(final Wei value) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
balance = value;
@ -190,7 +190,7 @@ public class BonsaiAccount implements MutableAccount, AccountValue {
@Override
public void setCode(final Bytes code) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
this.code = code;
@ -242,7 +242,7 @@ public class BonsaiAccount implements MutableAccount, AccountValue {
@Override
public void setStorageValue(final UInt256 key, final UInt256 value) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
updatedStorage.put(key, value);
@ -264,7 +264,7 @@ public class BonsaiAccount implements MutableAccount, AccountValue {
}
public void setStorageRoot(final Hash storageRoot) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
this.storageRoot = storageRoot;
@ -272,7 +272,7 @@ public class BonsaiAccount implements MutableAccount, AccountValue {
@Override
public void becomeImmutable() {
mutable = false;
immutable = true;
}
@Override

@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.ethereum.trie.patricia.StoredMerklePatriciaTrie;
import org.hyperledger.besu.ethereum.worldstate.StateTrieAccountValue;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.WorldState;
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
import org.hyperledger.besu.plugin.BesuContext;
@ -72,7 +73,8 @@ public class BonsaiWorldStateProvider implements WorldStateArchive {
final Optional<Long> maxLayersToLoad,
final CachedMerkleTrieLoader cachedMerkleTrieLoader,
final ObservableMetricsSystem metricsSystem,
final BesuContext pluginContext) {
final BesuContext pluginContext,
final EvmConfiguration evmConfiguration) {
this.cachedWorldStorageManager =
new CachedWorldStorageManager(this, worldStateStorage, metricsSystem);
@ -83,7 +85,7 @@ public class BonsaiWorldStateProvider implements WorldStateArchive {
this.blockchain = blockchain;
this.worldStateStorage = worldStateStorage;
this.cachedMerkleTrieLoader = cachedMerkleTrieLoader;
this.persistedState = new BonsaiWorldState(this, worldStateStorage);
this.persistedState = new BonsaiWorldState(this, worldStateStorage, evmConfiguration);
blockchain
.getBlockHeader(persistedState.getWorldStateBlockHash())
.ifPresent(
@ -98,12 +100,13 @@ public class BonsaiWorldStateProvider implements WorldStateArchive {
final TrieLogManager trieLogManager,
final BonsaiWorldStateKeyValueStorage worldStateStorage,
final Blockchain blockchain,
final CachedMerkleTrieLoader cachedMerkleTrieLoader) {
final CachedMerkleTrieLoader cachedMerkleTrieLoader,
final EvmConfiguration evmConfiguration) {
this.cachedWorldStorageManager = cachedWorldStorageManager;
this.trieLogManager = trieLogManager;
this.blockchain = blockchain;
this.worldStateStorage = worldStateStorage;
this.persistedState = new BonsaiWorldState(this, worldStateStorage);
this.persistedState = new BonsaiWorldState(this, worldStateStorage, evmConfiguration);
this.cachedMerkleTrieLoader = cachedMerkleTrieLoader;
blockchain
.getBlockHeader(persistedState.getWorldStateBlockHash())

@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiWorldStateKeyValueStor
import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiWorldStateLayerStorage;
import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldState;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
import java.util.ArrayList;
@ -41,6 +42,7 @@ public class CachedWorldStorageManager implements BonsaiStorageSubscriber {
private static final Logger LOG = LoggerFactory.getLogger(CachedWorldStorageManager.class);
private final BonsaiWorldStateProvider archive;
private final ObservableMetricsSystem metricsSystem;
private final EvmConfiguration evmConfiguration;
private final BonsaiWorldStateKeyValueStorage rootWorldStateStorage;
private final Map<Bytes32, CachedBonsaiWorldView> cachedWorldStatesByHash;
@ -49,19 +51,26 @@ public class CachedWorldStorageManager implements BonsaiStorageSubscriber {
final BonsaiWorldStateProvider archive,
final BonsaiWorldStateKeyValueStorage worldStateStorage,
final Map<Bytes32, CachedBonsaiWorldView> cachedWorldStatesByHash,
final ObservableMetricsSystem metricsSystem) {
final ObservableMetricsSystem metricsSystem,
final EvmConfiguration evmConfiguration) {
worldStateStorage.subscribe(this);
this.rootWorldStateStorage = worldStateStorage;
this.cachedWorldStatesByHash = cachedWorldStatesByHash;
this.archive = archive;
this.metricsSystem = metricsSystem;
this.evmConfiguration = evmConfiguration;
}
public CachedWorldStorageManager(
final BonsaiWorldStateProvider archive,
final BonsaiWorldStateKeyValueStorage worldStateStorage,
final ObservableMetricsSystem metricsSystem) {
this(archive, worldStateStorage, new ConcurrentHashMap<>(), metricsSystem);
this(
archive,
worldStateStorage,
new ConcurrentHashMap<>(),
metricsSystem,
EvmConfiguration.DEFAULT);
}
public synchronized void addCachedLayer(
@ -132,7 +141,9 @@ public class CachedWorldStorageManager implements BonsaiStorageSubscriber {
.map(
cached ->
new BonsaiWorldState(
archive, new BonsaiWorldStateLayerStorage(cached.getWorldStateStorage())));
archive,
new BonsaiWorldStateLayerStorage(cached.getWorldStateStorage()),
evmConfiguration));
}
LOG.atDebug()
.setMessage("did not find worldstate in cache for {}")
@ -171,7 +182,7 @@ public class CachedWorldStorageManager implements BonsaiStorageSubscriber {
.map(
storage ->
new BonsaiWorldState( // wrap the state in a layered worldstate
archive, new BonsaiWorldStateLayerStorage(storage)));
archive, new BonsaiWorldStateLayerStorage(storage), evmConfiguration));
}
public Optional<BonsaiWorldState> getHeadWorldState(
@ -188,7 +199,7 @@ public class CachedWorldStorageManager implements BonsaiStorageSubscriber {
addCachedLayer(
blockHeader,
blockHeader.getStateRoot(),
new BonsaiWorldState(archive, rootWorldStateStorage));
new BonsaiWorldState(archive, rootWorldStateStorage, evmConfiguration));
return getWorldState(blockHeader.getHash());
});
}

@ -43,6 +43,7 @@ import org.hyperledger.besu.ethereum.trie.NodeLoader;
import org.hyperledger.besu.ethereum.trie.patricia.StoredMerklePatriciaTrie;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.services.exception.StorageException;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction;
@ -80,19 +81,22 @@ public class BonsaiWorldState
public BonsaiWorldState(
final BonsaiWorldStateProvider archive,
final BonsaiWorldStateKeyValueStorage worldStateStorage) {
final BonsaiWorldStateKeyValueStorage worldStateStorage,
final EvmConfiguration evmConfiguration) {
this(
worldStateStorage,
archive.getCachedMerkleTrieLoader(),
archive.getCachedWorldStorageManager(),
archive.getTrieLogManager());
archive.getTrieLogManager(),
evmConfiguration);
}
protected BonsaiWorldState(
final BonsaiWorldStateKeyValueStorage worldStateStorage,
final CachedMerkleTrieLoader cachedMerkleTrieLoader,
final CachedWorldStorageManager cachedWorldStorageManager,
final TrieLogManager trieLogManager) {
final TrieLogManager trieLogManager,
final EvmConfiguration evmConfiguration) {
this.worldStateStorage = worldStateStorage;
this.worldStateRootHash =
Hash.wrap(
@ -106,7 +110,8 @@ public class BonsaiWorldState
cachedMerkleTrieLoader.preLoadAccount(
getWorldStateStorage(), worldStateRootHash, addr),
(addr, value) ->
cachedMerkleTrieLoader.preLoadStorageSlot(getWorldStateStorage(), addr, value));
cachedMerkleTrieLoader.preLoadStorageSlot(getWorldStateStorage(), addr, value),
evmConfiguration);
this.cachedMerkleTrieLoader = cachedMerkleTrieLoader;
this.cachedWorldStorageManager = cachedWorldStorageManager;
this.trieLogManager = trieLogManager;

@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.AbstractWorldUpdater;
import org.hyperledger.besu.evm.worldstate.UpdateTrackingAccount;
import org.hyperledger.besu.plugin.services.trielogs.TrieLog;
@ -65,6 +66,7 @@ public class BonsaiWorldStateUpdateAccumulator
private final AccountConsumingMap<BonsaiValue<BonsaiAccount>> accountsToUpdate;
private final Map<Address, BonsaiValue<Bytes>> codeToUpdate = new ConcurrentHashMap<>();
private final Set<Address> storageToClear = Collections.synchronizedSet(new HashSet<>());
private final EvmConfiguration evmConfiguration;
// storage sub mapped by _hashed_ key. This is because in self_destruct calls we need to
// enumerate the old storage and delete it. Those are trie stored by hashed key by spec and the
@ -77,18 +79,20 @@ public class BonsaiWorldStateUpdateAccumulator
public BonsaiWorldStateUpdateAccumulator(
final BonsaiWorldView world,
final Consumer<BonsaiValue<BonsaiAccount>> accountPreloader,
final Consumer<StorageSlotKey> storagePreloader) {
super(world);
final Consumer<StorageSlotKey> storagePreloader,
final EvmConfiguration evmConfiguration) {
super(world, evmConfiguration);
this.accountsToUpdate = new AccountConsumingMap<>(new ConcurrentHashMap<>(), accountPreloader);
this.accountPreloader = accountPreloader;
this.storagePreloader = storagePreloader;
this.isAccumulatorStateChanged = false;
this.evmConfiguration = evmConfiguration;
}
public BonsaiWorldStateUpdateAccumulator copy() {
final BonsaiWorldStateUpdateAccumulator copy =
new BonsaiWorldStateUpdateAccumulator(
wrappedWorldView(), accountPreloader, storagePreloader);
wrappedWorldView(), accountPreloader, storagePreloader, evmConfiguration);
copy.cloneFromUpdater(this);
return copy;
}
@ -770,7 +774,8 @@ public class BonsaiWorldStateUpdateAccumulator
codeToUpdate.clear();
accountsToUpdate.clear();
resetAccumulatorStateChanged();
super.reset();
updatedAccounts.clear();
deletedAccounts.clear();
}
public static class AccountConsumingMap<T> extends ForwardingMap<Address, T> {

@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValueStorage;
import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.log.LogsBloomFilter;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
@ -138,7 +139,7 @@ public final class GenesisState {
final WorldStatePreimageKeyValueStorage preimageStorage =
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage());
final MutableWorldState worldState =
new DefaultMutableWorldState(stateStorage, preimageStorage);
new DefaultMutableWorldState(stateStorage, preimageStorage, EvmConfiguration.DEFAULT);
writeAccountsTo(worldState, genesisAccounts, null);
return worldState.rootHash();
}

@ -30,6 +30,7 @@ import org.hyperledger.besu.ethereum.worldstate.DefaultWorldStateArchive;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.plugin.services.PrivacyPluginService;
import org.hyperledger.besu.plugin.services.privacy.PrivacyGroupGenesisProvider;
@ -339,7 +340,8 @@ public class PrivacyParameters {
final WorldStatePreimageStorage privatePreimageStorage =
storageProvider.createWorldStatePreimageStorage();
final WorldStateArchive privateWorldStateArchive =
new DefaultWorldStateArchive(privateWorldStateStorage, privatePreimageStorage);
new DefaultWorldStateArchive(
privateWorldStateStorage, privatePreimageStorage, EvmConfiguration.DEFAULT);
final PrivateStateStorage privateStateStorage = storageProvider.createPrivateStateStorage();
final PrivateStateRootResolver privateStateRootResolver =

@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.trie.MerkleTrie;
import org.hyperledger.besu.ethereum.trie.patricia.StoredMerklePatriciaTrie;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.AccountStorageEntry;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.AbstractWorldUpdater;
import org.hyperledger.besu.evm.worldstate.UpdateTrackingAccount;
import org.hyperledger.besu.evm.worldstate.WorldState;
@ -33,7 +34,6 @@ import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.NavigableMap;
@ -41,7 +41,6 @@ import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Stream;
import org.apache.tuweni.bytes.Bytes;
@ -50,6 +49,7 @@ import org.apache.tuweni.units.bigints.UInt256;
public class DefaultMutableWorldState implements MutableWorldState {
private final EvmConfiguration evmConfiguration;
private final WorldStateStorage worldStateStorage;
private final WorldStatePreimageStorage preimageStorage;
@ -60,31 +60,36 @@ public class DefaultMutableWorldState implements MutableWorldState {
private final Map<Bytes32, Address> newAccountKeyPreimages = new HashMap<>();
public DefaultMutableWorldState(
final WorldStateStorage storage, final WorldStatePreimageStorage preimageStorage) {
this(MerkleTrie.EMPTY_TRIE_NODE_HASH, storage, preimageStorage);
final WorldStateStorage storage,
final WorldStatePreimageStorage preimageStorage,
final EvmConfiguration evmConfiguration) {
this(MerkleTrie.EMPTY_TRIE_NODE_HASH, storage, preimageStorage, evmConfiguration);
}
public DefaultMutableWorldState(
final Bytes32 rootHash,
final WorldStateStorage worldStateStorage,
final WorldStatePreimageStorage preimageStorage) {
final WorldStatePreimageStorage preimageStorage,
final EvmConfiguration evmConfiguration) {
this.worldStateStorage = worldStateStorage;
this.accountStateTrie = newAccountStateTrie(rootHash);
this.preimageStorage = preimageStorage;
this.evmConfiguration = evmConfiguration;
}
public DefaultMutableWorldState(final WorldState worldState) {
public DefaultMutableWorldState(
final WorldState worldState, final EvmConfiguration evmConfiguration) {
// TODO: this is an abstraction leak (and kind of incorrect in that we reuse the underlying
// storage), but the reason for this is that the accounts() method is unimplemented below and
// can't be until NC-754.
if (!(worldState instanceof DefaultMutableWorldState)) {
if (!(worldState instanceof DefaultMutableWorldState other)) {
throw new UnsupportedOperationException();
}
final DefaultMutableWorldState other = (DefaultMutableWorldState) worldState;
this.worldStateStorage = other.worldStateStorage;
this.preimageStorage = other.preimageStorage;
this.accountStateTrie = newAccountStateTrie(other.accountStateTrie.getRootHash());
this.evmConfiguration = evmConfiguration;
}
private MerkleTrie<Bytes32, Bytes> newAccountStateTrie(final Bytes32 rootHash) {
@ -126,16 +131,9 @@ public class DefaultMutableWorldState implements MutableWorldState {
return new WorldStateAccount(address, addressHash, accountValue);
}
private static Bytes serializeAccount(
final long nonce, final Wei balance, final Hash storageRoot, final Hash codeHash) {
final StateTrieAccountValue accountValue =
new StateTrieAccountValue(nonce, balance, storageRoot, codeHash);
return RLP.encode(accountValue::writeTo);
}
@Override
public WorldUpdater updater() {
return new Updater(this);
return new Updater(this, evmConfiguration);
}
@Override
@ -158,11 +156,10 @@ public class DefaultMutableWorldState implements MutableWorldState {
@Override
public final boolean equals(final Object other) {
if (!(other instanceof DefaultMutableWorldState)) {
if (!(other instanceof DefaultMutableWorldState that)) {
return false;
}
final DefaultMutableWorldState that = (DefaultMutableWorldState) other;
return this.rootHash().equals(that.rootHash());
}
@ -197,11 +194,6 @@ public class DefaultMutableWorldState implements MutableWorldState {
stateUpdater.commit();
}
private Optional<UInt256> getStorageTrieKeyPreimage(final Bytes32 trieKey) {
return Optional.ofNullable(newStorageKeyPreimages.get(trieKey))
.or(() -> preimageStorage.getStorageTrieKeyPreimage(trieKey));
}
private static UInt256 convertToUInt256(final Bytes value) {
// TODO: we could probably have an optimized method to decode a single scalar since it's used
// pretty often.
@ -332,13 +324,19 @@ public class DefaultMutableWorldState implements MutableWorldState {
builder.append("codeHash=").append(getCodeHash()).append(", ");
return builder.append("}").toString();
}
private Optional<UInt256> getStorageTrieKeyPreimage(final Bytes32 trieKey) {
return Optional.ofNullable(newStorageKeyPreimages.get(trieKey))
.or(() -> preimageStorage.getStorageTrieKeyPreimage(trieKey));
}
}
protected static class Updater
extends AbstractWorldUpdater<DefaultMutableWorldState, WorldStateAccount> {
protected Updater(final DefaultMutableWorldState world) {
super(world);
protected Updater(
final DefaultMutableWorldState world, final EvmConfiguration evmConfiguration) {
super(world, evmConfiguration);
}
@Override
@ -403,9 +401,7 @@ public class DefaultMutableWorldState implements MutableWorldState {
: origin.storageTrie();
wrapped.updatedStorageTries.put(updated.getAddress(), storageTrie);
final TreeSet<Map.Entry<UInt256, UInt256>> entries =
new TreeSet<>(
Comparator.comparing(
(Function<Map.Entry<UInt256, UInt256>, UInt256>) Map.Entry::getKey));
new TreeSet<>(Map.Entry.comparingByKey());
entries.addAll(updatedStorage.entrySet());
for (final Map.Entry<UInt256, UInt256> entry : entries) {
@ -431,5 +427,12 @@ public class DefaultMutableWorldState implements MutableWorldState {
wrapped.accountStateTrie.put(updated.getAddressHash(), account);
}
}
private static Bytes serializeAccount(
final long nonce, final Wei balance, final Hash storageRoot, final Hash codeHash) {
final StateTrieAccountValue accountValue =
new StateTrieAccountValue(nonce, balance, storageRoot, codeHash);
return RLP.encode(accountValue::writeTo);
}
}
}

@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.proof.WorldStateProof;
import org.hyperledger.besu.ethereum.proof.WorldStateProofProvider;
import org.hyperledger.besu.ethereum.trie.MerkleTrie;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.WorldState;
import java.util.List;
@ -35,14 +36,18 @@ public class DefaultWorldStateArchive implements WorldStateArchive {
private final WorldStateStorage worldStateStorage;
private final WorldStatePreimageStorage preimageStorage;
private final WorldStateProofProvider worldStateProof;
private final EvmConfiguration evmConfiguration;
private static final Hash EMPTY_ROOT_HASH = Hash.wrap(MerkleTrie.EMPTY_TRIE_NODE_HASH);
public DefaultWorldStateArchive(
final WorldStateStorage worldStateStorage, final WorldStatePreimageStorage preimageStorage) {
final WorldStateStorage worldStateStorage,
final WorldStatePreimageStorage preimageStorage,
final EvmConfiguration evmConfiguration) {
this.worldStateStorage = worldStateStorage;
this.preimageStorage = preimageStorage;
this.worldStateProof = new WorldStateProofProvider(worldStateStorage);
this.evmConfiguration = evmConfiguration;
}
@Override
@ -66,7 +71,9 @@ public class DefaultWorldStateArchive implements WorldStateArchive {
if (!worldStateStorage.isWorldStateAvailable(rootHash, blockHash)) {
return Optional.empty();
}
return Optional.of(new DefaultMutableWorldState(rootHash, worldStateStorage, preimageStorage));
return Optional.of(
new DefaultMutableWorldState(
rootHash, worldStateStorage, preimageStorage, evmConfiguration));
}
@Override

@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValue
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState;
import org.hyperledger.besu.ethereum.worldstate.DefaultWorldStateArchive;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import org.hyperledger.besu.services.kvstore.SegmentedInMemoryKeyValueStorage;
@ -79,11 +80,17 @@ public class InMemoryKeyValueStorageProvider extends KeyValueStorageProvider {
public static DefaultWorldStateArchive createInMemoryWorldStateArchive() {
return new DefaultWorldStateArchive(
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()),
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()));
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()),
EvmConfiguration.DEFAULT);
}
public static BonsaiWorldStateProvider createBonsaiInMemoryWorldStateArchive(
final Blockchain blockchain) {
return createBonsaiInMemoryWorldStateArchive(blockchain, EvmConfiguration.DEFAULT);
}
public static BonsaiWorldStateProvider createBonsaiInMemoryWorldStateArchive(
final Blockchain blockchain, final EvmConfiguration evmConfiguration) {
final InMemoryKeyValueStorageProvider inMemoryKeyValueStorageProvider =
new InMemoryKeyValueStorageProvider();
final CachedMerkleTrieLoader cachedMerkleTrieLoader =
@ -95,14 +102,16 @@ public class InMemoryKeyValueStorageProvider extends KeyValueStorageProvider {
Optional.empty(),
cachedMerkleTrieLoader,
new NoOpMetricsSystem(),
null);
null,
evmConfiguration);
}
public static MutableWorldState createInMemoryWorldState() {
final InMemoryKeyValueStorageProvider provider = new InMemoryKeyValueStorageProvider();
return new DefaultMutableWorldState(
provider.createWorldStateStorage(DataStorageFormat.FOREST),
provider.createWorldStatePreimageStorage());
provider.createWorldStatePreimageStorage(),
EvmConfiguration.DEFAULT);
}
public static PrivateStateStorage createInMemoryPrivateStateStorage() {

@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.worldstate.DefaultWorldStateArchive;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
public class InMemoryPrivacyStorageProvider implements PrivacyStorageProvider {
@ -33,13 +34,16 @@ public class InMemoryPrivacyStorageProvider implements PrivacyStorageProvider {
public static WorldStateArchive createInMemoryWorldStateArchive() {
return new DefaultWorldStateArchive(
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()),
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()));
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()),
EvmConfiguration.DEFAULT);
}
public static MutableWorldState createInMemoryWorldState() {
final InMemoryPrivacyStorageProvider provider = new InMemoryPrivacyStorageProvider();
return new DefaultMutableWorldState(
provider.createWorldStateStorage(), provider.createWorldStatePreimageStorage());
provider.createWorldStateStorage(),
provider.createWorldStatePreimageStorage(),
EvmConfiguration.DEFAULT);
}
@Override
@ -68,5 +72,7 @@ public class InMemoryPrivacyStorageProvider implements PrivacyStorageProvider {
}
@Override
public void close() {}
public void close() {
// no cleanup for in-memory data storage
}
}

@ -46,6 +46,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.exception.StorageException;
@ -55,7 +56,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class BlockImportExceptionHandlingTest {
class BlockImportExceptionHandlingTest {
private final MainnetTransactionProcessor transactionProcessor =
mock(MainnetTransactionProcessor.class);
@ -90,7 +91,8 @@ public class BlockImportExceptionHandlingTest {
spy(
new BonsaiWorldState(
(BonsaiWorldStateProvider) worldStateArchive,
(BonsaiWorldStateKeyValueStorage) worldStateStorage));
(BonsaiWorldStateKeyValueStorage) worldStateStorage,
EvmConfiguration.DEFAULT));
private final BadBlockManager badBlockManager = new BadBlockManager();
@ -107,7 +109,7 @@ public class BlockImportExceptionHandlingTest {
}
@Test
public void shouldNotBadBlockWhenInternalErrorDuringPersisting() {
void shouldNotBadBlockWhenInternalErrorDuringPersisting() {
Mockito.doThrow(new StorageException("database problem")).when(persisted).persist(any());
Mockito.doReturn(persisted).when(worldStateArchive).getMutable();
@ -147,7 +149,7 @@ public class BlockImportExceptionHandlingTest {
}
@Test
public void shouldNotBadBlockWhenInternalErrorOnBlockLookup() {
void shouldNotBadBlockWhenInternalErrorOnBlockLookup() {
Block goodBlock =
new BlockDataGenerator()
@ -173,7 +175,7 @@ public class BlockImportExceptionHandlingTest {
any(),
eq(HeaderValidationMode.DETACHED_ONLY)))
.thenReturn(true);
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(0);
assertThat(badBlockManager.getBadBlocks()).isEmpty();
mainnetBlockValidator.validateAndProcessBlock(
protocolContext,
goodBlock,
@ -183,7 +185,7 @@ public class BlockImportExceptionHandlingTest {
}
@Test
public void shouldNotBadBlockWhenInternalErrorDuringValidateHeader() {
void shouldNotBadBlockWhenInternalErrorDuringValidateHeader() {
Block goodBlock =
new BlockDataGenerator()
@ -212,7 +214,7 @@ public class BlockImportExceptionHandlingTest {
}
@Test
public void shouldNotBadBlockWhenInternalErrorDuringValidateBody() {
void shouldNotBadBlockWhenInternalErrorDuringValidateBody() {
Mockito.doNothing().when(persisted).persist(any());
Mockito.doReturn(persisted).when(worldStateArchive).getMutable();
Mockito.doReturn(Optional.of(persisted)).when(worldStateArchive).getMutable(any(), any());

@ -66,6 +66,7 @@ import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProviderBuilder;
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.BesuConfiguration;
import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBKeyValueStorageFactory;
@ -148,7 +149,8 @@ public abstract class AbstractIsolationTests {
Optional.of(16L),
new CachedMerkleTrieLoader(new NoOpMetricsSystem()),
new NoOpMetricsSystem(),
null);
null,
EvmConfiguration.DEFAULT);
var ws = archive.getMutable();
genesisState.writeStateTo(ws);
protocolContext = new ProtocolContext(blockchain, archive, null, Optional.empty());

@ -43,6 +43,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction;
@ -63,7 +64,7 @@ import org.mockito.quality.Strictness;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class BonsaiWorldStateArchiveTest {
class BonsaiWorldStateArchiveTest {
final BlockHeaderTestFixture blockBuilder = new BlockHeaderTestFixture();
@Mock Blockchain blockchain;
@ -89,7 +90,7 @@ public class BonsaiWorldStateArchiveTest {
}
@Test
public void testGetMutableReturnPersistedStateWhenNeeded() {
void testGetMutableReturnPersistedStateWhenNeeded() {
final BlockHeader chainHead = blockBuilder.number(0).buildHeader();
when(segmentedKeyValueStorage.get(TRIE_BRANCH_STORAGE, WORLD_ROOT_HASH_KEY))
@ -106,14 +107,15 @@ public class BonsaiWorldStateArchiveTest {
trieLogManager,
new BonsaiWorldStateKeyValueStorage(storageProvider, new NoOpMetricsSystem()),
blockchain,
new CachedMerkleTrieLoader(new NoOpMetricsSystem()));
new CachedMerkleTrieLoader(new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT);
assertThat(bonsaiWorldStateArchive.getMutable(chainHead, true))
.containsInstanceOf(BonsaiWorldState.class);
}
@Test
public void testGetMutableReturnEmptyWhenLoadMoreThanLimitLayersBack() {
void testGetMutableReturnEmptyWhenLoadMoreThanLimitLayersBack() {
bonsaiWorldStateArchive =
new BonsaiWorldStateProvider(
new BonsaiWorldStateKeyValueStorage(storageProvider, new NoOpMetricsSystem()),
@ -121,7 +123,8 @@ public class BonsaiWorldStateArchiveTest {
Optional.of(512L),
new CachedMerkleTrieLoader(new NoOpMetricsSystem()),
new NoOpMetricsSystem(),
null);
null,
EvmConfiguration.DEFAULT);
final BlockHeader blockHeader = blockBuilder.number(0).buildHeader();
final BlockHeader chainHead = blockBuilder.number(512).buildHeader();
when(blockchain.getChainHeadHeader()).thenReturn(chainHead);
@ -130,7 +133,7 @@ public class BonsaiWorldStateArchiveTest {
}
@Test
public void testGetMutableWhenLoadLessThanLimitLayersBack() {
void testGetMutableWhenLoadLessThanLimitLayersBack() {
bonsaiWorldStateArchive =
new BonsaiWorldStateProvider(
@ -138,7 +141,8 @@ public class BonsaiWorldStateArchiveTest {
trieLogManager,
new BonsaiWorldStateKeyValueStorage(storageProvider, new NoOpMetricsSystem()),
blockchain,
new CachedMerkleTrieLoader(new NoOpMetricsSystem()));
new CachedMerkleTrieLoader(new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT);
final BlockHeader blockHeader = blockBuilder.number(0).buildHeader();
final BlockHeader chainHead = blockBuilder.number(511).buildHeader();
final BonsaiWorldState mockWorldState = mock(BonsaiWorldState.class);
@ -153,9 +157,8 @@ public class BonsaiWorldStateArchiveTest {
.containsInstanceOf(BonsaiWorldState.class);
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Test
public void testGetMutableWithStorageInconsistencyRollbackTheState() {
void testGetMutableWithStorageInconsistencyRollbackTheState() {
doAnswer(__ -> Optional.of(mock(TrieLogLayer.class)))
.when(trieLogManager)
@ -170,10 +173,11 @@ public class BonsaiWorldStateArchiveTest {
trieLogManager,
worldStateStorage,
blockchain,
new CachedMerkleTrieLoader(new NoOpMetricsSystem())));
new CachedMerkleTrieLoader(new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT));
final BlockHeader blockHeader = blockBuilder.number(0).buildHeader();
when(blockchain.getBlockHeader(eq(blockHeader.getHash()))).thenReturn(Optional.of(blockHeader));
when(blockchain.getBlockHeader(blockHeader.getHash())).thenReturn(Optional.of(blockHeader));
assertThat(bonsaiWorldStateArchive.getMutable(null, blockHeader.getHash()))
.containsInstanceOf(BonsaiWorldState.class);
@ -184,7 +188,7 @@ public class BonsaiWorldStateArchiveTest {
// @SuppressWarnings({"unchecked", "rawtypes"})
@Test
public void testGetMutableWithStorageConsistencyNotRollbackTheState() {
void testGetMutableWithStorageConsistencyNotRollbackTheState() {
var worldStateStorage =
new BonsaiWorldStateKeyValueStorage(storageProvider, new NoOpMetricsSystem());
@ -195,12 +199,13 @@ public class BonsaiWorldStateArchiveTest {
trieLogManager,
worldStateStorage,
blockchain,
new CachedMerkleTrieLoader(new NoOpMetricsSystem())));
new CachedMerkleTrieLoader(new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT));
final BlockHeader blockHeader = blockBuilder.number(0).buildHeader();
when(blockchain.getBlockHeader(eq(blockHeader.getHash()))).thenReturn(Optional.of(blockHeader));
when(blockchain.getBlockHeader(eq(Hash.ZERO))).thenReturn(Optional.of(blockHeader));
when(blockchain.getBlockHeader(blockHeader.getHash())).thenReturn(Optional.of(blockHeader));
when(blockchain.getBlockHeader(Hash.ZERO)).thenReturn(Optional.of(blockHeader));
assertThat(bonsaiWorldStateArchive.getMutable(null, blockHeader.getHash()))
.containsInstanceOf(BonsaiWorldState.class);
@ -209,9 +214,8 @@ public class BonsaiWorldStateArchiveTest {
verify(trieLogManager, Mockito.never()).getTrieLogLayer(any());
}
@SuppressWarnings({"unchecked"})
@Test
public void testGetMutableWithStorageConsistencyToRollbackAndRollForwardTheState() {
void testGetMutableWithStorageConsistencyToRollbackAndRollForwardTheState() {
final BlockHeader genesis = blockBuilder.number(0).buildHeader();
final BlockHeader blockHeaderChainA =
blockBuilder.number(1).timestamp(1).parentHash(genesis.getHash()).buildHeader();
@ -232,27 +236,27 @@ public class BonsaiWorldStateArchiveTest {
trieLogManager,
worldStateStorage,
blockchain,
new CachedMerkleTrieLoader(new NoOpMetricsSystem())));
new CachedMerkleTrieLoader(new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT));
// initial persisted state hash key
when(blockchain.getBlockHeader(eq(Hash.ZERO))).thenReturn(Optional.of(blockHeaderChainA));
when(blockchain.getBlockHeader(eq(blockHeaderChainB.getHash())))
when(blockchain.getBlockHeader(Hash.ZERO)).thenReturn(Optional.of(blockHeaderChainA));
when(blockchain.getBlockHeader(blockHeaderChainB.getHash()))
.thenReturn(Optional.of(blockHeaderChainB));
when(blockchain.getBlockHeader(eq(genesis.getHash()))).thenReturn(Optional.of(genesis));
when(blockchain.getBlockHeader(genesis.getHash())).thenReturn(Optional.of(genesis));
assertThat(bonsaiWorldStateArchive.getMutable(null, blockHeaderChainB.getHash()))
.containsInstanceOf(BonsaiWorldState.class);
// verify is trying to get the trie log layers to rollback and roll forward
verify(trieLogManager).getTrieLogLayer(eq(blockHeaderChainA.getHash()));
verify(trieLogManager).getTrieLogLayer(eq(blockHeaderChainB.getHash()));
verify(trieLogManager).getTrieLogLayer(blockHeaderChainA.getHash());
verify(trieLogManager).getTrieLogLayer(blockHeaderChainB.getHash());
}
@SuppressWarnings({"unchecked"})
@Test
// TODO: refactor to test original intent
@Disabled("needs refactor, getMutable(hash, hash) cannot trigger saveTrieLog")
public void testGetMutableWithRollbackNotOverrideTrieLogLayer() {
void testGetMutableWithRollbackNotOverrideTrieLogLayer() {
when(segmentedKeyValueStorage.startTransaction())
.thenReturn(segmentedKeyValueStorageTransaction);
final BlockHeader genesis = blockBuilder.number(0).buildHeader();
@ -272,10 +276,11 @@ public class BonsaiWorldStateArchiveTest {
trieLogManager,
new BonsaiWorldStateKeyValueStorage(storageProvider, new NoOpMetricsSystem()),
blockchain,
new CachedMerkleTrieLoader(new NoOpMetricsSystem())));
new CachedMerkleTrieLoader(new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT));
// initial persisted state hash key
when(blockchain.getBlockHeader(eq(Hash.ZERO))).thenReturn(Optional.of(blockHeaderChainA));
when(blockchain.getBlockHeader(Hash.ZERO)).thenReturn(Optional.of(blockHeaderChainA));
// fake trie log layer
final BytesValueRLPOutput rlpLogBlockB = new BytesValueRLPOutput();
final TrieLogLayer trieLogLayerBlockB = new TrieLogLayer();
@ -284,9 +289,9 @@ public class BonsaiWorldStateArchiveTest {
when(segmentedKeyValueStorage.get(BLOCKCHAIN, blockHeaderChainB.getHash().toArrayUnsafe()))
.thenReturn(Optional.of(rlpLogBlockB.encoded().toArrayUnsafe()));
when(blockchain.getBlockHeader(eq(blockHeaderChainB.getHash())))
when(blockchain.getBlockHeader(blockHeaderChainB.getHash()))
.thenReturn(Optional.of(blockHeaderChainB));
when(blockchain.getBlockHeader(eq(genesis.getHash()))).thenReturn(Optional.of(genesis));
when(blockchain.getBlockHeader(genesis.getHash())).thenReturn(Optional.of(genesis));
assertThat(bonsaiWorldStateArchive.getMutable(null, blockHeaderChainB.getHash()))
.containsInstanceOf(BonsaiWorldState.class);

@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.log.LogsBloomFilter;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
@ -159,7 +160,9 @@ class LogRollingTests {
final BonsaiWorldState worldState =
new BonsaiWorldState(
archive, new BonsaiWorldStateKeyValueStorage(provider, new NoOpMetricsSystem()));
archive,
new BonsaiWorldStateKeyValueStorage(provider, new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT);
final WorldUpdater updater = worldState.updater();
final MutableAccount mutableAccount = updater.createAccount(addressOne, 1, Wei.of(1L));
@ -171,7 +174,8 @@ class LogRollingTests {
final BonsaiWorldState secondWorldState =
new BonsaiWorldState(
secondArchive,
new BonsaiWorldStateKeyValueStorage(secondProvider, new NoOpMetricsSystem()));
new BonsaiWorldStateKeyValueStorage(secondProvider, new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT);
final BonsaiWorldStateUpdateAccumulator secondUpdater =
(BonsaiWorldStateUpdateAccumulator) secondWorldState.updater();
@ -200,7 +204,9 @@ class LogRollingTests {
void rollForwardTwice() {
final BonsaiWorldState worldState =
new BonsaiWorldState(
archive, new BonsaiWorldStateKeyValueStorage(provider, new NoOpMetricsSystem()));
archive,
new BonsaiWorldStateKeyValueStorage(provider, new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT);
final WorldUpdater updater = worldState.updater();
final MutableAccount mutableAccount = updater.createAccount(addressOne, 1, Wei.of(1L));
@ -220,7 +226,8 @@ class LogRollingTests {
final BonsaiWorldState secondWorldState =
new BonsaiWorldState(
secondArchive,
new BonsaiWorldStateKeyValueStorage(secondProvider, new NoOpMetricsSystem()));
new BonsaiWorldStateKeyValueStorage(secondProvider, new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT);
final BonsaiWorldStateUpdateAccumulator secondUpdater =
(BonsaiWorldStateUpdateAccumulator) secondWorldState.updater();
@ -250,7 +257,9 @@ class LogRollingTests {
void rollBackOnce() {
final BonsaiWorldState worldState =
new BonsaiWorldState(
archive, new BonsaiWorldStateKeyValueStorage(provider, new NoOpMetricsSystem()));
archive,
new BonsaiWorldStateKeyValueStorage(provider, new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT);
final WorldUpdater updater = worldState.updater();
final MutableAccount mutableAccount = updater.createAccount(addressOne, 1, Wei.of(1L));
@ -277,7 +286,8 @@ class LogRollingTests {
final BonsaiWorldState secondWorldState =
new BonsaiWorldState(
secondArchive,
new BonsaiWorldStateKeyValueStorage(secondProvider, new NoOpMetricsSystem()));
new BonsaiWorldStateKeyValueStorage(secondProvider, new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT);
final WorldUpdater secondUpdater = secondWorldState.updater();
final MutableAccount secondMutableAccount =

@ -30,6 +30,7 @@ import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldStateUpdateAccu
import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import org.hyperledger.besu.services.kvstore.SegmentedInMemoryKeyValueStorage;
@ -54,7 +55,9 @@ public class RollingImport {
InMemoryKeyValueStorageProvider.createBonsaiInMemoryWorldStateArchive(null);
final BonsaiWorldState bonsaiState =
new BonsaiWorldState(
archive, new BonsaiWorldStateKeyValueStorage(provider, new NoOpMetricsSystem()));
archive,
new BonsaiWorldStateKeyValueStorage(provider, new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT);
final SegmentedInMemoryKeyValueStorage worldStateStorage =
(SegmentedInMemoryKeyValueStorage)
provider.getStorageBySegmentIdentifiers(

@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldStateUpdateAccu
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import java.util.concurrent.atomic.AtomicBoolean;
@ -36,7 +37,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class TrieLogManagerTests {
class TrieLogManagerTests {
BlockHeader blockHeader = new BlockHeaderTestFixture().buildHeader();
@ -47,7 +48,9 @@ public class TrieLogManagerTests {
@Mock BonsaiWorldState worldState;
@Mock Blockchain blockchain;
BonsaiWorldStateUpdateAccumulator bonsaiUpdater =
spy(new BonsaiWorldStateUpdateAccumulator(worldState, (__, ___) -> {}, (__, ___) -> {}));
spy(
new BonsaiWorldStateUpdateAccumulator(
worldState, (__, ___) -> {}, (__, ___) -> {}, EvmConfiguration.DEFAULT));
TrieLogManager trieLogManager;
@ -57,7 +60,7 @@ public class TrieLogManagerTests {
}
@Test
public void testSaveTrieLogEvent() {
void testSaveTrieLogEvent() {
AtomicBoolean eventFired = new AtomicBoolean(false);
trieLogManager.subscribe(
layer -> {

@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValue
import org.hyperledger.besu.ethereum.trie.MerkleTrie;
import org.hyperledger.besu.evm.account.AccountStorageEntry;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.WorldState;
import org.hyperledger.besu.evm.worldstate.WorldState.StreamableAccount;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
@ -56,7 +57,7 @@ class DefaultMutableWorldStateTest {
private static MutableWorldState createEmpty(final WorldStateKeyValueStorage storage) {
final WorldStatePreimageKeyValueStorage preimageStorage =
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage());
return new DefaultMutableWorldState(storage, preimageStorage);
return new DefaultMutableWorldState(storage, preimageStorage, EvmConfiguration.DEFAULT);
}
private static MutableWorldState createEmpty() {
@ -276,7 +277,8 @@ class DefaultMutableWorldStateTest {
new DefaultMutableWorldState(
expectedRootHash,
new WorldStateKeyValueStorage(storage),
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()));
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()),
EvmConfiguration.DEFAULT);
assertThat(newWorldState.rootHash()).isEqualTo(expectedRootHash);
assertThat(newWorldState.get(ADDRESS)).isNotNull();
assertThat(newWorldState.get(ADDRESS).getBalance()).isEqualTo(newBalance);

@ -33,6 +33,7 @@ import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValueStorage;
import org.hyperledger.besu.ethereum.trie.MerkleTrie;
import org.hyperledger.besu.ethereum.trie.patricia.StoredMerklePatriciaTrie;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.WorldState;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
@ -55,7 +56,7 @@ import org.mockito.InOrder;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class MarkSweepPrunerTest {
class MarkSweepPrunerTest {
private final BlockDataGenerator gen = new BlockDataGenerator();
private final NoOpMetricsSystem metricsSystem = new NoOpMetricsSystem();
@ -65,13 +66,15 @@ public class MarkSweepPrunerTest {
spy(new WorldStateKeyValueStorage(stateStorage));
private final WorldStateArchive worldStateArchive =
new DefaultWorldStateArchive(
worldStateStorage, new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()));
worldStateStorage,
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()),
EvmConfiguration.DEFAULT);
private final InMemoryKeyValueStorage markStorage = new InMemoryKeyValueStorage();
private final Block genesisBlock = gen.genesisBlock();
private final MutableBlockchain blockchain = createInMemoryBlockchain(genesisBlock);
@Test
public void mark_marksAllExpectedNodes() {
void mark_marksAllExpectedNodes() {
final MarkSweepPruner pruner =
new MarkSweepPruner(worldStateStorage, blockchain, markStorage, metricsSystem);
@ -84,7 +87,7 @@ public class MarkSweepPrunerTest {
final BlockHeader markBlock = blockchain.getBlockHeader(markBlockNumber).get();
// Collect the nodes we expect to keep
final Set<Bytes> expectedNodes = collectWorldStateNodes(markBlock.getStateRoot());
assertThat(hashValueStore.size()).isGreaterThan(expectedNodes.size()); // Sanity check
assertThat(hashValueStore).hasSizeGreaterThan(expectedNodes.size()); // Sanity check
// Mark and sweep
pruner.mark(markBlock.getStateRoot());
@ -113,14 +116,14 @@ public class MarkSweepPrunerTest {
}
// Check that storage contains only the values we expect
assertThat(hashValueStore.size()).isEqualTo(expectedNodes.size());
assertThat(hashValueStore).hasSameSizeAs(expectedNodes);
assertThat(hashValueStore.values().stream().map(Optional::get))
.containsExactlyInAnyOrderElementsOf(
expectedNodes.stream().map(Bytes::toArrayUnsafe).collect(Collectors.toSet()));
}
@Test
public void sweepBefore_shouldSweepStateRootFirst() {
void sweepBefore_shouldSweepStateRootFirst() {
final MarkSweepPruner pruner =
new MarkSweepPruner(worldStateStorage, blockchain, markStorage, metricsSystem);
@ -155,7 +158,7 @@ public class MarkSweepPrunerTest {
}
@Test
public void sweepBefore_shouldNotRemoveMarkedStateRoots() {
void sweepBefore_shouldNotRemoveMarkedStateRoots() {
final MarkSweepPruner pruner =
new MarkSweepPruner(worldStateStorage, blockchain, markStorage, metricsSystem);

@ -63,6 +63,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage.Updater;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.AccountStorageEntry;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.WorldState;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
@ -99,7 +100,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
@Disabled("PIE-1434 - Ignored while working to make test more reliable")
public class FastWorldStateDownloaderTest {
class FastWorldStateDownloaderTest {
private static final Hash EMPTY_TRIE_ROOT = Hash.wrap(MerkleTrie.EMPTY_TRIE_NODE_HASH);
@ -123,43 +124,43 @@ public class FastWorldStateDownloaderTest {
@Test
@Timeout(value = 60)
public void downloadWorldStateFromPeers_onePeerOneWithManyRequestsOneAtATime() {
void downloadWorldStateFromPeers_onePeerOneWithManyRequestsOneAtATime() {
downloadAvailableWorldStateFromPeers(1, 50, 1, 1);
}
@Test
@Timeout(value = 60)
public void downloadWorldStateFromPeers_onePeerOneWithManyRequests() {
void downloadWorldStateFromPeers_onePeerOneWithManyRequests() {
downloadAvailableWorldStateFromPeers(1, 50, 1, 10);
}
@Test
@Timeout(value = 60)
public void downloadWorldStateFromPeers_onePeerWithSingleRequest() {
void downloadWorldStateFromPeers_onePeerWithSingleRequest() {
downloadAvailableWorldStateFromPeers(1, 1, 100, 10);
}
@Test
@Timeout(value = 60)
public void downloadWorldStateFromPeers_largeStateFromMultiplePeers() {
void downloadWorldStateFromPeers_largeStateFromMultiplePeers() {
downloadAvailableWorldStateFromPeers(5, 100, 10, 10);
}
@Test
@Timeout(value = 60)
public void downloadWorldStateFromPeers_smallStateFromMultiplePeers() {
void downloadWorldStateFromPeers_smallStateFromMultiplePeers() {
downloadAvailableWorldStateFromPeers(5, 5, 1, 10);
}
@Test
@Timeout(value = 60)
public void downloadWorldStateFromPeers_singleRequestWithMultiplePeers() {
void downloadWorldStateFromPeers_singleRequestWithMultiplePeers() {
downloadAvailableWorldStateFromPeers(5, 1, 50, 50);
}
@Test
@Timeout(value = 60)
public void downloadEmptyWorldState() {
void downloadEmptyWorldState() {
final BlockHeader header =
dataGen
@ -173,7 +174,7 @@ public class FastWorldStateDownloaderTest {
Stream.generate(
() -> EthProtocolManagerTestUtil.createPeer(ethProtocolManager, header.getNumber()))
.limit(5)
.collect(Collectors.toList());
.toList();
final InMemoryTasksPriorityQueues<NodeDataRequest> taskCollection =
new InMemoryTasksPriorityQueues<>();
@ -193,7 +194,7 @@ public class FastWorldStateDownloaderTest {
@Test
@Timeout(value = 60)
public void downloadAlreadyAvailableWorldState() {
void downloadAlreadyAvailableWorldState() {
// Setup existing state
final DefaultWorldStateArchive worldStateArchive = createInMemoryWorldStateArchive();
final MutableWorldState worldState = worldStateArchive.getMutable();
@ -210,7 +211,7 @@ public class FastWorldStateDownloaderTest {
Stream.generate(
() -> EthProtocolManagerTestUtil.createPeer(ethProtocolManager, header.getNumber()))
.limit(5)
.collect(Collectors.toList());
.toList();
final InMemoryTasksPriorityQueues<NodeDataRequest> taskCollection =
new InMemoryTasksPriorityQueues<>();
@ -233,7 +234,7 @@ public class FastWorldStateDownloaderTest {
@Test
@Timeout(value = 60)
public void canRecoverFromTimeouts() {
void canRecoverFromTimeouts() {
final DeterministicEthScheduler.TimeoutPolicy timeoutPolicy =
DeterministicEthScheduler.TimeoutPolicy.timeoutXTimes(2);
final EthProtocolManager ethProtocolManager = EthProtocolManagerTestUtil.create(timeoutPolicy);
@ -281,7 +282,8 @@ public class FastWorldStateDownloaderTest {
// Check that all expected account data was downloaded
final WorldStateArchive localWorldStateArchive =
new DefaultWorldStateArchive(localStorage, createPreimageStorage());
new DefaultWorldStateArchive(
localStorage, createPreimageStorage(), EvmConfiguration.DEFAULT);
final WorldState localWorldState = localWorldStateArchive.get(stateRoot, null).get();
assertThat(result).isDone();
assertAccountsMatch(localWorldState, accounts);
@ -289,13 +291,13 @@ public class FastWorldStateDownloaderTest {
@Test
@Timeout(value = 60)
public void handlesPartialResponsesFromNetwork() {
void handlesPartialResponsesFromNetwork() {
downloadAvailableWorldStateFromPeers(5, 100, 10, 10, this::respondPartially);
}
@Test
@Timeout(value = 60)
public void doesNotRequestKnownCodeFromNetwork() {
void doesNotRequestKnownCodeFromNetwork() {
// Setup "remote" state
final WorldStateArchive remoteWorldStateArchive = createInMemoryWorldStateArchive();
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
@ -349,12 +351,13 @@ public class FastWorldStateDownloaderTest {
.map(GetNodeDataMessage::readFrom)
.flatMap(m -> StreamSupport.stream(m.hashes().spliterator(), true))
.collect(Collectors.toList());
assertThat(requestedHashes.size()).isGreaterThan(0);
assertThat(requestedHashes).isNotEmpty();
assertThat(Collections.disjoint(requestedHashes, knownCode.keySet())).isTrue();
// Check that all expected account data was downloaded
final WorldStateArchive localWorldStateArchive =
new DefaultWorldStateArchive(localStorage, createPreimageStorage());
new DefaultWorldStateArchive(
localStorage, createPreimageStorage(), EvmConfiguration.DEFAULT);
final WorldState localWorldState = localWorldStateArchive.get(stateRoot, null).get();
assertThat(result).isDone();
assertAccountsMatch(localWorldState, accounts);
@ -362,13 +365,13 @@ public class FastWorldStateDownloaderTest {
@Test
@Timeout(value = 60)
public void cancelDownloader() {
void cancelDownloader() {
testCancellation(false);
}
@Test
@Timeout(value = 60)
public void cancelDownloaderFuture() {
void cancelDownloaderFuture() {
testCancellation(true);
}
@ -396,7 +399,7 @@ public class FastWorldStateDownloaderTest {
Stream.generate(
() -> EthProtocolManagerTestUtil.createPeer(ethProtocolManager, header.getNumber()))
.limit(5)
.collect(Collectors.toList());
.toList();
final InMemoryTasksPriorityQueues<NodeDataRequest> taskCollection =
new InMemoryTasksPriorityQueues<>();
@ -452,12 +455,13 @@ public class FastWorldStateDownloaderTest {
@Test
@Timeout(value = 60)
public void doesNotRequestKnownAccountTrieNodesFromNetwork() {
void doesNotRequestKnownAccountTrieNodesFromNetwork() {
// Setup "remote" state
final WorldStateStorage remoteStorage =
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive remoteWorldStateArchive =
new DefaultWorldStateArchive(remoteStorage, createPreimageStorage());
new DefaultWorldStateArchive(
remoteStorage, createPreimageStorage(), EvmConfiguration.DEFAULT);
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
// Generate accounts and save corresponding state root
@ -484,7 +488,7 @@ public class FastWorldStateDownloaderTest {
collectTrieNodesToBeRequestedAfterRoot(remoteStorage, remoteWorldState.rootHash(), 5);
final Set<Bytes32> knownNodes = new HashSet<>();
final Set<Bytes32> unknownNodes = new HashSet<>();
assertThat(allNodes.size()).isGreaterThan(0); // Sanity check
assertThat(allNodes).isNotEmpty(); // Sanity check
final Updater localStorageUpdater = localStorage.updater();
final AtomicBoolean storeNode = new AtomicBoolean(true);
allNodes.forEach(
@ -522,13 +526,15 @@ public class FastWorldStateDownloaderTest {
.map(GetNodeDataMessage::readFrom)
.flatMap(m -> StreamSupport.stream(m.hashes().spliterator(), true))
.collect(Collectors.toList());
assertThat(requestedHashes.size()).isGreaterThan(0);
assertThat(requestedHashes).containsAll(unknownNodes);
assertThat(requestedHashes).doesNotContainAnyElementsOf(knownNodes);
assertThat(requestedHashes)
.isNotEmpty()
.containsAll(unknownNodes)
.doesNotContainAnyElementsOf(knownNodes);
// Check that all expected account data was downloaded
final WorldStateArchive localWorldStateArchive =
new DefaultWorldStateArchive(localStorage, createPreimageStorage());
new DefaultWorldStateArchive(
localStorage, createPreimageStorage(), EvmConfiguration.DEFAULT);
final WorldState localWorldState = localWorldStateArchive.get(stateRoot, null).get();
assertThat(result).isDone();
assertAccountsMatch(localWorldState, accounts);
@ -536,12 +542,13 @@ public class FastWorldStateDownloaderTest {
@Test
@Timeout(value = 60)
public void doesNotRequestKnownStorageTrieNodesFromNetwork() {
void doesNotRequestKnownStorageTrieNodesFromNetwork() {
// Setup "remote" state
final WorldStateStorage remoteStorage =
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive remoteWorldStateArchive =
new DefaultWorldStateArchive(remoteStorage, createPreimageStorage());
new DefaultWorldStateArchive(
remoteStorage, createPreimageStorage(), EvmConfiguration.DEFAULT);
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
// Generate accounts and save corresponding state root
@ -582,7 +589,7 @@ public class FastWorldStateDownloaderTest {
allTrieNodes.putAll(
collectTrieNodesToBeRequestedAfterRoot(remoteStorage, storageRootHash, 5));
}
assertThat(allTrieNodes.size()).isGreaterThan(0); // Sanity check
assertThat(allTrieNodes).isNotEmpty(); // Sanity check
final Updater localStorageUpdater = localStorage.updater();
boolean storeNode = true;
for (final Map.Entry<Bytes32, Bytes> entry : allTrieNodes.entrySet()) {
@ -624,13 +631,15 @@ public class FastWorldStateDownloaderTest {
.map(GetNodeDataMessage::readFrom)
.flatMap(m -> StreamSupport.stream(m.hashes().spliterator(), true))
.collect(Collectors.toList());
assertThat(requestedHashes.size()).isGreaterThan(0);
assertThat(requestedHashes).containsAll(unknownNodes);
assertThat(requestedHashes).doesNotContainAnyElementsOf(knownNodes);
assertThat(requestedHashes)
.isNotEmpty()
.containsAll(unknownNodes)
.doesNotContainAnyElementsOf(knownNodes);
// Check that all expected account data was downloaded
final WorldStateArchive localWorldStateArchive =
new DefaultWorldStateArchive(localStorage, createPreimageStorage());
new DefaultWorldStateArchive(
localStorage, createPreimageStorage(), EvmConfiguration.DEFAULT);
final WorldState localWorldState = localWorldStateArchive.get(stateRoot, null).get();
assertThat(result).isDone();
assertAccountsMatch(localWorldState, accounts);
@ -638,7 +647,7 @@ public class FastWorldStateDownloaderTest {
@Test
@Timeout(value = 60)
public void stalledDownloader() {
void stalledDownloader() {
final EthProtocolManager ethProtocolManager =
EthProtocolManagerTestUtil.create(new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()));
@ -646,7 +655,8 @@ public class FastWorldStateDownloaderTest {
final WorldStateStorage remoteStorage =
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive remoteWorldStateArchive =
new DefaultWorldStateArchive(remoteStorage, createPreimageStorage());
new DefaultWorldStateArchive(
remoteStorage, createPreimageStorage(), EvmConfiguration.DEFAULT);
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
// Generate accounts and save corresponding state root
@ -702,12 +712,13 @@ public class FastWorldStateDownloaderTest {
@Test
@Timeout(value = 60)
public void resumesFromNonEmptyQueue() {
void resumesFromNonEmptyQueue() {
// Setup "remote" state
final WorldStateStorage remoteStorage =
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive remoteWorldStateArchive =
new DefaultWorldStateArchive(remoteStorage, createPreimageStorage());
new DefaultWorldStateArchive(
remoteStorage, createPreimageStorage(), EvmConfiguration.DEFAULT);
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
// Generate accounts and save corresponding state root
@ -721,7 +732,7 @@ public class FastWorldStateDownloaderTest {
final InMemoryTasksPriorityQueues<NodeDataRequest> taskCollection =
spy(new InMemoryTasksPriorityQueues<>());
List<Bytes32> queuedHashes = getFirstSetOfChildNodeRequests(remoteStorage, stateRoot);
assertThat(queuedHashes.size()).isGreaterThan(0); // Sanity check
assertThat(queuedHashes).isNotEmpty(); // Sanity check
for (Bytes32 bytes32 : queuedHashes) {
taskCollection.add(new AccountTrieNodeDataRequest(Hash.wrap(bytes32), Optional.empty()));
}
@ -760,8 +771,7 @@ public class FastWorldStateDownloaderTest {
.map(GetNodeDataMessage::readFrom)
.flatMap(m -> StreamSupport.stream(m.hashes().spliterator(), true))
.collect(Collectors.toList());
assertThat(requestedHashes.size()).isGreaterThan(0);
assertThat(requestedHashes).containsAll(queuedHashes);
assertThat(requestedHashes).isNotEmpty().containsAll(queuedHashes);
// Check that already enqueued requests were not enqueued more than once
for (Bytes32 bytes32 : queuedHashes) {
@ -772,7 +782,8 @@ public class FastWorldStateDownloaderTest {
// Check that all expected account data was downloaded
assertThat(result).isDone();
final WorldStateArchive localWorldStateArchive =
new DefaultWorldStateArchive(localStorage, createPreimageStorage());
new DefaultWorldStateArchive(
localStorage, createPreimageStorage(), EvmConfiguration.DEFAULT);
final WorldState localWorldState = localWorldStateArchive.get(stateRoot, null).get();
assertAccountsMatch(localWorldState, accounts);
}
@ -845,7 +856,8 @@ public class FastWorldStateDownloaderTest {
final WorldStateStorage remoteStorage =
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive remoteWorldStateArchive =
new DefaultWorldStateArchive(remoteStorage, createPreimageStorage());
new DefaultWorldStateArchive(
remoteStorage, createPreimageStorage(), EvmConfiguration.DEFAULT);
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
// Generate accounts and save corresponding state root
@ -869,7 +881,8 @@ public class FastWorldStateDownloaderTest {
final WorldStateStorage localStorage =
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive localWorldStateArchive =
new DefaultWorldStateArchive(localStorage, createPreimageStorage());
new DefaultWorldStateArchive(
localStorage, createPreimageStorage(), EvmConfiguration.DEFAULT);
final SynchronizerConfiguration syncConfig =
SynchronizerConfiguration.builder()
.worldStateHashCountPerRequest(hashesPerRequest)
@ -891,7 +904,7 @@ public class FastWorldStateDownloaderTest {
EthProtocolManagerTestUtil.createPeer(
ethProtocolManager, header.getNumber() - 1L))
.limit(trailingPeerCount)
.collect(Collectors.toList());
.toList();
// Start downloader
final CompletableFuture<?> result = downloader.run(null, new FastSyncState(header));

@ -24,6 +24,7 @@ import org.hyperledger.besu.evmtool.benchmarks.BenchmarkExecutor;
import org.hyperledger.besu.evmtool.benchmarks.ECRecoverBenchmark;
import org.hyperledger.besu.evmtool.benchmarks.ModExpBenchmark;
import org.hyperledger.besu.evmtool.benchmarks.Secp256k1Benchmark;
import org.hyperledger.besu.util.LogConfigurator;
import java.io.PrintStream;
import java.util.EnumSet;
@ -85,6 +86,7 @@ public class BenchmarkSubCommand implements Runnable {
@Override
public void run() {
LogConfigurator.setLevel("", "DEBUG");
System.out.println(BesuInfo.version());
var benchmarksToRun = benchmarks.isEmpty() ? EnumSet.allOf(Benchmark.class) : benchmarks;
for (var benchmark : benchmarksToRun) {

@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValue
import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState;
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
@ -59,14 +60,17 @@ public class BlockchainModule {
final WorldStateStorage worldStateStorage,
final WorldStatePreimageStorage worldStatePreimageStorage,
final GenesisState genesisState,
@Named("KeyValueStorageName") final String keyValueStorageName) {
@Named("KeyValueStorageName") final String keyValueStorageName,
final EvmConfiguration evmConfiguration) {
if ("memory".equals(keyValueStorageName)) {
final MutableWorldState mutableWorldState =
new DefaultMutableWorldState(worldStateStorage, worldStatePreimageStorage);
new DefaultMutableWorldState(
worldStateStorage, worldStatePreimageStorage, evmConfiguration);
genesisState.writeStateTo(mutableWorldState);
return mutableWorldState;
} else {
return new DefaultMutableWorldState(stateRoot, worldStateStorage, worldStatePreimageStorage);
return new DefaultMutableWorldState(
stateRoot, worldStateStorage, worldStatePreimageStorage, evmConfiguration);
}
}

@ -15,9 +15,12 @@
*/
package org.hyperledger.besu.evmtool;
import org.hyperledger.besu.util.LogConfigurator;
public final class EvmTool {
public static void main(final String... args) {
LogConfigurator.setLevel("", "DEBUG");
final EvmToolCommand evmToolCommand = new EvmToolCommand();
evmToolCommand.execute(args);

@ -126,19 +126,25 @@ public class EvmToolCommand implements Runnable {
names = {"--sender"},
paramLabel = "<address>",
description = "Calling address for this invocation.")
private final Address sender = Address.fromHexString("0x00");
private final Address sender = Address.ZERO;
@Option(
names = {"--receiver"},
paramLabel = "<address>",
description = "Receiving address for this invocation.")
private final Address receiver = Address.fromHexString("0x00");
private final Address receiver = Address.ZERO;
@Option(
names = {"--contract"},
paramLabel = "<address>",
description = "The address holding the contract code.")
private final Address contract = Address.ZERO;
@Option(
names = {"--coinbase"},
paramLabel = "<address>",
description = "Coinbase for this invocation.")
private final Address coinbase = Address.fromHexString("0x00");
private final Address coinbase = Address.ZERO;
@Option(
names = {"--input"},
@ -383,11 +389,13 @@ public class EvmToolCommand implements Runnable {
WorldUpdater updater = component.getWorldUpdater();
updater.getOrCreate(sender);
updater.getOrCreate(receiver);
var contractAccount = updater.getOrCreate(contract);
contractAccount.setCode(codeBytes);
MessageFrame initialMessageFrame =
MessageFrame.builder()
.type(MessageFrame.Type.MESSAGE_CALL)
.worldUpdater(updater)
.worldUpdater(updater.updater())
.initialGas(txGas)
.contract(Address.ZERO)
.address(receiver)

@ -20,6 +20,7 @@ import static org.hyperledger.besu.cli.DefaultCommandValues.getDefaultBesuDataPa
import org.hyperledger.besu.controller.BesuController;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameter;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.plugin.services.BesuConfiguration;
import org.hyperledger.besu.services.BesuConfigurationImpl;
@ -102,4 +103,34 @@ public class EvmToolCommandOptionsModule {
BlockParameter provideBlockParameter() {
return blockParameter;
}
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"})
@CommandLine.Option(
names = {"--Xevm-jumpdest-cache-weight-kb"},
description =
"size in kilobytes to allow the cache "
+ "of valid jump destinations to grow to before evicting the least recently used entry",
fallbackValue = "32000",
defaultValue = "32000",
hidden = true,
arity = "1")
private Long jumpDestCacheWeightKilobytes =
32_000L; // 10k contracts, (25k max contract size / 8 bit) + 32byte hash
@CommandLine.Option(
names = {"--Xevm-worldstate-update-mode"},
description = "How to handle worldstate updates within a transaction",
fallbackValue = "STACKED",
defaultValue = "STACKED",
hidden = true,
arity = "1")
private EvmConfiguration.WorldUpdaterMode worldstateUpdateMode =
EvmConfiguration.WorldUpdaterMode
.STACKED; // Stacked Updater. Years of battle tested correctness.
@Provides
@Singleton
EvmConfiguration provideEvmConfiguration() {
return new EvmConfiguration(jumpDestCacheWeightKilobytes, worldstateUpdateMode);
}
}

@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.chain.GenesisState;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import java.io.File;
import java.io.IOException;
@ -63,7 +64,8 @@ public class GenesisFileModule {
ProtocolSchedule provideProtocolSchedule(
final GenesisConfigOptions configOptions,
@Named("Fork") final Optional<String> fork,
@Named("RevertReasonEnabled") final boolean revertReasonEnabled) {
@Named("RevertReasonEnabled") final boolean revertReasonEnabled,
final EvmConfiguration evmConfiguration) {
throw new RuntimeException("Abstract");
}

@ -52,7 +52,8 @@ class MainnetGenesisFileModule extends GenesisFileModule {
ProtocolSchedule provideProtocolSchedule(
final GenesisConfigOptions configOptions,
@Named("Fork") final Optional<String> fork,
@Named("RevertReasonEnabled") final boolean revertReasonEnabled) {
@Named("RevertReasonEnabled") final boolean revertReasonEnabled,
final EvmConfiguration evmConfiguration) {
final Optional<String> ecCurve = configOptions.getEcCurve();
if (ecCurve.isEmpty()) {
@ -73,7 +74,7 @@ class MainnetGenesisFileModule extends GenesisFileModule {
return schedule.get();
}
}
return MainnetProtocolSchedule.fromConfig(configOptions, EvmConfiguration.DEFAULT);
return MainnetProtocolSchedule.fromConfig(configOptions, evmConfiguration);
}
public static Map<String, Supplier<ProtocolSchedule>> createSchedules() {

@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestEnv;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evmtool.T8nExecutor.RejectedTransaction;
import org.hyperledger.besu.util.LogConfigurator;
@ -33,8 +34,10 @@ import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
@ -114,50 +117,55 @@ public class T8nServerSubCommand implements Runnable {
ReferenceTestEnv referenceTestEnv =
objectMapper.convertValue(input.get("env"), ReferenceTestEnv.class);
ReferenceTestWorldState initialWorldState =
objectMapper.convertValue(input.get("alloc"), ReferenceTestWorldState.class);
initialWorldState.persist(null);
List<Transaction> transactions = new ArrayList<>();
List<RejectedTransaction> rejections = new ArrayList<>();
JsonNode txs = input.get("txs");
if (txs != null) {
if (txs instanceof ArrayNode txsArray) {
extractTransactions(
new PrintWriter(System.err, true, StandardCharsets.UTF_8),
txsArray.elements(),
transactions,
rejections);
} else if (txs instanceof TextNode txt) {
transactions =
extractTransactions(
new PrintWriter(System.err, true, StandardCharsets.UTF_8),
List.<JsonNode>of(txt).iterator(),
transactions,
rejections);
Map<String, ReferenceTestWorldState.AccountMock> accounts =
objectMapper.convertValue(input.get("alloc"), new TypeReference<>() {});
final T8nExecutor.T8nResult result;
try (ReferenceTestWorldState initialWorldState =
ReferenceTestWorldState.create(accounts, EvmConfiguration.DEFAULT)) {
initialWorldState.persist(null);
List<Transaction> transactions = new ArrayList<>();
List<RejectedTransaction> rejections = new ArrayList<>();
JsonNode txs = input.get("txs");
if (txs != null) {
if (txs instanceof ArrayNode txsArray) {
extractTransactions(
new PrintWriter(System.err, true, StandardCharsets.UTF_8),
txsArray.elements(),
transactions,
rejections);
} else if (txs instanceof TextNode txt) {
transactions =
extractTransactions(
new PrintWriter(System.err, true, StandardCharsets.UTF_8),
List.<JsonNode>of(txt).iterator(),
transactions,
rejections);
}
}
}
final T8nExecutor.T8nResult result =
T8nExecutor.runTest(
chainId,
fork,
reward,
objectMapper,
referenceTestEnv,
initialWorldState,
transactions,
rejections,
new T8nExecutor.TracerManager() {
@Override
public OperationTracer getManagedTracer(final int txIndex, final Hash txHash) {
return OperationTracer.NO_TRACING;
}
@Override
public void disposeTracer(final OperationTracer tracer) {
// No output streams to dispose of
}
});
result =
T8nExecutor.runTest(
chainId,
fork,
reward,
objectMapper,
referenceTestEnv,
initialWorldState,
transactions,
rejections,
new T8nExecutor.TracerManager() {
@Override
public OperationTracer getManagedTracer(final int txIndex, final Hash txHash) {
return OperationTracer.NO_TRACING;
}
@Override
public void disposeTracer(final OperationTracer tracer) {
// No output streams to dispose of
}
});
}
ObjectNode outputObject = objectMapper.createObjectNode();
outputObject.set("alloc", result.allocObject());

@ -24,6 +24,7 @@ import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestEnv;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.tracing.StandardJsonTracer;
import org.hyperledger.besu.evmtool.T8nExecutor.RejectedTransaction;
@ -45,6 +46,7 @@ import java.util.Map;
import java.util.Stack;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
@ -206,8 +208,9 @@ public class T8nSubCommand implements Runnable {
}
referenceTestEnv = objectMapper.convertValue(config.get("env"), ReferenceTestEnv.class);
initialWorldState =
objectMapper.convertValue(config.get("alloc"), ReferenceTestWorldState.class);
Map<String, ReferenceTestWorldState.AccountMock> accounts =
objectMapper.convertValue(config.get("alloc"), new TypeReference<>() {});
initialWorldState = ReferenceTestWorldState.create(accounts, EvmConfiguration.DEFAULT);
initialWorldState.persist(null);
var node = config.get("txs");
Iterator<JsonNode> it;

@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.bonsai.BonsaiValue;
import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiPreImageProxy;
import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldStateUpdateAccumulator;
import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldView;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.apache.tuweni.bytes.Bytes;
@ -31,8 +32,9 @@ public class BonsaiReferenceTestUpdateAccumulator extends BonsaiWorldStateUpdate
final BonsaiWorldView world,
final Consumer<BonsaiValue<BonsaiAccount>> accountPreloader,
final Consumer<StorageSlotKey> storagePreloader,
final BonsaiPreImageProxy preImageProxy) {
super(world, accountPreloader, storagePreloader);
final BonsaiPreImageProxy preImageProxy,
final EvmConfiguration evmConfiguration) {
super(world, accountPreloader, storagePreloader, evmConfiguration);
this.preImageProxy = preImageProxy;
}

@ -26,11 +26,11 @@ import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldState;
import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldStateUpdateAccumulator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.trielogs.TrieLog;
import org.hyperledger.besu.plugin.services.trielogs.TrieLogEvent;
import java.util.Map;
import java.util.Optional;
@ -46,16 +46,24 @@ public class BonsaiReferenceTestWorldState extends BonsaiWorldState
private final BonsaiReferenceTestWorldStateStorage refTestStorage;
private final BonsaiPreImageProxy preImageProxy;
private final EvmConfiguration evmConfiguration;
protected BonsaiReferenceTestWorldState(
final BonsaiReferenceTestWorldStateStorage worldStateStorage,
final CachedMerkleTrieLoader cachedMerkleTrieLoader,
final CachedWorldStorageManager cachedWorldStorageManager,
final TrieLogManager trieLogManager,
final BonsaiPreImageProxy preImageProxy) {
super(worldStateStorage, cachedMerkleTrieLoader, cachedWorldStorageManager, trieLogManager);
final BonsaiPreImageProxy preImageProxy,
final EvmConfiguration evmConfiguration) {
super(
worldStateStorage,
cachedMerkleTrieLoader,
cachedWorldStorageManager,
trieLogManager,
evmConfiguration);
this.refTestStorage = worldStateStorage;
this.preImageProxy = preImageProxy;
this.evmConfiguration = evmConfiguration;
setAccumulator(
new BonsaiReferenceTestUpdateAccumulator(
this,
@ -64,7 +72,8 @@ public class BonsaiReferenceTestWorldState extends BonsaiWorldState
getWorldStateStorage(), worldStateRootHash, addr),
(addr, value) ->
cachedMerkleTrieLoader.preLoadStorageSlot(getWorldStateStorage(), addr, value),
preImageProxy));
preImageProxy,
evmConfiguration));
}
@Override
@ -75,7 +84,8 @@ public class BonsaiReferenceTestWorldState extends BonsaiWorldState
cachedMerkleTrieLoader,
cachedWorldStorageManager,
trieLogManager,
preImageProxy);
preImageProxy,
evmConfiguration);
}
/**
@ -93,6 +103,13 @@ public class BonsaiReferenceTestWorldState extends BonsaiWorldState
@JsonCreator
public static BonsaiReferenceTestWorldState create(
final Map<String, ReferenceTestWorldState.AccountMock> accounts) {
return create(accounts, EvmConfiguration.DEFAULT);
}
@JsonCreator
public static BonsaiReferenceTestWorldState create(
final Map<String, ReferenceTestWorldState.AccountMock> accounts,
final EvmConfiguration evmConfiguration) {
final ObservableMetricsSystem metricsSystem = new NoOpMetricsSystem();
final CachedMerkleTrieLoader cachedMerkleTrieLoader = new CachedMerkleTrieLoader(metricsSystem);
final TrieLogManager trieLogManager = new NoOpTrieLogManager();
@ -114,7 +131,8 @@ public class BonsaiReferenceTestWorldState extends BonsaiWorldState
cachedMerkleTrieLoader,
noOpCachedWorldStorageManager,
trieLogManager,
preImageProxy);
preImageProxy,
evmConfiguration);
final WorldUpdater updater = worldState.updater();
for (final Map.Entry<String, ReferenceTestWorldState.AccountMock> entry : accounts.entrySet()) {
@ -140,12 +158,13 @@ public class BonsaiReferenceTestWorldState extends BonsaiWorldState
new NoOpMetricsSystem());
}
@SuppressWarnings({"UnsynchronizedOverridesSynchronized", "squid:S3551"})
@Override
public void addCachedLayer(
final BlockHeader blockHeader,
final Hash worldStateRootHash,
final BonsaiWorldState forWorldState) {
// reference tests do not cache layers
// reference test world states are not cached
}
@Override
@ -181,7 +200,7 @@ public class BonsaiReferenceTestWorldState extends BonsaiWorldState
super(null, null, 0, null);
}
@SuppressWarnings("UnsynchronizedOverridesSynchronized")
@SuppressWarnings({"UnsynchronizedOverridesSynchronized", "squid:S3551"})
@Override
public void saveTrieLog(
final BonsaiWorldStateUpdateAccumulator localUpdater,
@ -202,16 +221,6 @@ public class BonsaiReferenceTestWorldState extends BonsaiWorldState
public Optional<TrieLog> getTrieLogLayer(final Hash blockHash) {
return Optional.empty();
}
@Override
public synchronized long subscribe(final TrieLogEvent.TrieLogObserver sub) {
return trieLogObservers.subscribe(sub);
}
@Override
public synchronized void unsubscribe(final long id) {
trieLogObservers.unsubscribe(id);
}
}
@Override

@ -18,6 +18,7 @@ import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValueStorage;
import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.WorldState;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
@ -32,11 +33,12 @@ public class DefaultReferenceTestWorldState extends DefaultMutableWorldState
DefaultReferenceTestWorldState() {
super(
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()),
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()));
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()),
EvmConfiguration.DEFAULT);
}
public DefaultReferenceTestWorldState(final WorldState worldState) {
super(worldState);
super(worldState, EvmConfiguration.DEFAULT);
}
@Override

@ -19,6 +19,7 @@ import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.HashMap;
@ -92,6 +93,12 @@ public interface ReferenceTestWorldState extends MutableWorldState {
@JsonCreator
static ReferenceTestWorldState create(final Map<String, AccountMock> accounts) {
// delegate to a Bonsai reference test world state:
return BonsaiReferenceTestWorldState.create(accounts);
return create(accounts, EvmConfiguration.DEFAULT);
}
static ReferenceTestWorldState create(
final Map<String, AccountMock> accounts, final EvmConfiguration evmConfiguration) {
// delegate to a Bonsai reference test world state:
return BonsaiReferenceTestWorldState.create(accounts, evmConfiguration);
}
}

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.vm;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assumptions.assumeThat;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
@ -29,12 +30,17 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.referencetests.BlockchainReferenceTestCaseSpec;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules;
import org.hyperledger.besu.ethereum.rlp.RLPException;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.account.AccountState;
import org.hyperledger.besu.evm.internal.EvmConfiguration.WorldUpdaterMode;
import org.hyperledger.besu.testutil.JsonTestParameters;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.tuweni.bytes.Bytes32;
import org.assertj.core.api.Assertions;
public class BlockchainReferenceTestTools {
@ -58,7 +64,8 @@ public class BlockchainReferenceTestTools {
.generator(
(testName, fullPath, spec, collector) -> {
final String eip = spec.getNetwork();
collector.add(testName + "[" + eip + "]", fullPath, spec, NETWORKS_TO_RUN.contains(eip));
collector.add(
testName + "[" + eip + "]", fullPath, spec, NETWORKS_TO_RUN.contains(eip));
});
static {
@ -73,7 +80,7 @@ public class BlockchainReferenceTestTools {
// Absurd amount of gas, doesn't run in parallel
params.ignore("randomStatetest94_\\w+");
// Don't do time consuming tests
// Don't do time-consuming tests
params.ignore("CALLBlake2f_MaxRounds.*");
params.ignore("loopMul_*");
@ -100,7 +107,6 @@ public class BlockchainReferenceTestTools {
spec.getWorldStateArchive()
.getMutable(genesisBlockHeader.getStateRoot(), genesisBlockHeader.getHash())
.get();
assertThat(worldState.rootHash()).isEqualTo(genesisBlockHeader.getStateRoot());
final ProtocolSchedule schedule =
REFERENCE_TEST_PROTOCOL_SCHEDULES.getByName(spec.getNetwork());
@ -119,6 +125,20 @@ public class BlockchainReferenceTestTools {
final ProtocolSpec protocolSpec = schedule.getByBlockHeader(block.getHeader());
final BlockImporter blockImporter = protocolSpec.getBlockImporter();
EVM evm = protocolSpec.getEvm();
if (evm.getEvmConfiguration().worldUpdaterMode() == WorldUpdaterMode.JOURNALED) {
assumeThat(
worldState
.streamAccounts(Bytes32.ZERO, Integer.MAX_VALUE)
.anyMatch(AccountState::isEmpty))
.withFailMessage("Journaled account configured and empty account detected")
.isFalse();
assumeThat(EvmSpecVersion.SPURIOUS_DRAGON.compareTo(evm.getEvmVersion()) > 0)
.withFailMessage("Journaled account configured and fork prior to the merge specified")
.isFalse();
}
final HeaderValidationMode validationMode =
"NoProof".equalsIgnoreCase(spec.getSealEngine())
? HeaderValidationMode.LIGHT

@ -15,7 +15,14 @@
package org.hyperledger.besu.ethereum.vm;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assumptions.assumeThat;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32;
import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
@ -33,17 +40,15 @@ import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.AccountState;
import org.hyperledger.besu.evm.internal.EvmConfiguration.WorldUpdaterMode;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.testutil.JsonTestParameters;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class GeneralStateReferenceTestTools {
private static final ReferenceTestProtocolSchedules REFERENCE_TEST_PROTOCOL_SCHEDULES =
ReferenceTestProtocolSchedules.create();
@ -101,7 +106,7 @@ public class GeneralStateReferenceTestTools {
params.ignore("static_Call1MB1024Calldepth-\\w");
params.ignore("ShanghaiLove_.*");
// Don't do time consuming tests
// Don't do time-consuming tests
params.ignore("CALLBlake2f_MaxRounds.*");
params.ignore("loopMul-.*");
@ -121,6 +126,20 @@ public class GeneralStateReferenceTestTools {
final BlockHeader blockHeader = spec.getBlockHeader();
final ReferenceTestWorldState initialWorldState = spec.getInitialWorldState();
final Transaction transaction = spec.getTransaction();
ProtocolSpec protocolSpec = protocolSpec(spec.getFork());
EVM evm = protocolSpec.getEvm();
if (evm.getEvmConfiguration().worldUpdaterMode() == WorldUpdaterMode.JOURNALED) {
assumeThat(
initialWorldState
.streamAccounts(Bytes32.ZERO, Integer.MAX_VALUE)
.anyMatch(AccountState::isEmpty))
.withFailMessage("Journaled account configured and empty account detected")
.isFalse();
assumeThat(EvmSpecVersion.SPURIOUS_DRAGON.compareTo(evm.getEvmVersion()) > 0)
.withFailMessage("Journaled account configured and fork prior to the merge specified")
.isFalse();
}
// Sometimes the tests ask us assemble an invalid transaction. If we have
// no valid transaction then there is no test. GeneralBlockChain tests
@ -145,7 +164,7 @@ public class GeneralStateReferenceTestTools {
final WorldUpdater worldStateUpdater = worldState.updater();
final ReferenceTestBlockchain blockchain = new ReferenceTestBlockchain(blockHeader.getNumber());
final Wei blobGasPrice =
protocolSpec(spec.getFork())
protocolSpec
.getFeeMarket()
.blobGasPricePerGas(blockHeader.getExcessBlobGas().orElse(BlobGas.ZERO));
final TransactionProcessingResult result =

@ -167,7 +167,8 @@ public class RetestethContext {
final WorldStateArchive worldStateArchive =
new DefaultWorldStateArchive(
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()),
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()));
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()),
EvmConfiguration.DEFAULT);
final MutableWorldState worldState = worldStateArchive.getMutable();
genesisState.writeStateTo(worldState);

@ -33,11 +33,11 @@ import javax.annotation.Nonnull;
*
* @param <V> The type of the collection.
*/
public class UndoList<V> implements List<V>, UndoableCollection {
public class UndoList<V> implements List<V>, Undoable {
record UndoEntry<V>(int index, boolean set, V value, long level) {
UndoEntry(final int index, final boolean set, final V value) {
this(index, set, value, UndoableCollection.incrementMarkStatic());
this(index, set, value, Undoable.incrementMarkStatic());
}
@Override
@ -140,6 +140,11 @@ public class UndoList<V> implements List<V>, UndoableCollection {
}
}
@Override
public long lastUpdate() {
return undoLog.isEmpty() ? 0L : undoLog.get(undoLog.size() - 1).level;
}
@Override
public boolean add(final V v) {
undoLog.add(new UndoEntry<>(delegate.size(), false, null));

@ -34,11 +34,11 @@ import javax.annotation.Nonnull;
*
* @param <V> The type of the collection.
*/
public class UndoMap<K, V> implements Map<K, V>, UndoableCollection {
public class UndoMap<K, V> implements Map<K, V>, Undoable {
record UndoEntry<K, V>(K key, V value, long level) {
UndoEntry(final K key, final V value) {
this(key, value, UndoableCollection.incrementMarkStatic());
this(key, value, Undoable.incrementMarkStatic());
}
}
@ -70,6 +70,20 @@ public class UndoMap<K, V> implements Map<K, V>, UndoableCollection {
}
}
@Override
public long lastUpdate() {
return undoLog.isEmpty() ? 0L : undoLog.get(undoLog.size() - 1).level;
}
/**
* Has the map been changed
*
* @return true if there are any undo entries in the log
*/
public boolean updated() {
return !undoLog.isEmpty();
}
@Override
public int size() {
return delegate.size();

@ -0,0 +1,181 @@
/*
* Copyright contributors to Hyperledger Besu
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
package org.hyperledger.besu.collections.undo;
import java.util.Comparator;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.SortedMap;
/**
* A map that supports rolling back the map to a prior state.
*
* <p>To register a prior state you want to roll back to call `mark()`. Then use that value in a
* subsequent call to `undo(mark)`. Every mutation operation across all undoable collections
* increases the global mark, so a mark set in once collection is usable across all
* UndoableCollection instances.
*
* @param <V> The type of the collection.
*/
public class UndoNavigableMap<K, V> extends UndoMap<K, V> implements NavigableMap<K, V> {
/**
* Create an UndoMap backed by another Map instance.
*
* @param delegate The Map instance to use for backing storage
*/
public UndoNavigableMap(final NavigableMap<K, V> delegate) {
super(delegate);
}
/**
* Create an undo navigable map backed by a specific map.
*
* @param map The map storing the current state
* @return an undoable map
* @param <K> the key type
* @param <V> the value type
*/
public static <K, V> UndoNavigableMap<K, V> of(final NavigableMap<K, V> map) {
return new UndoNavigableMap<>(map);
}
@Override
public Comparator<? super K> comparator() {
return ((NavigableMap<K, V>) delegate).comparator();
}
@Override
public SortedMap<K, V> subMap(final K fromKey, final K toKey) {
return ((NavigableMap<K, V>) delegate).subMap(fromKey, toKey);
}
@Override
public SortedMap<K, V> headMap(final K toKey) {
return ((NavigableMap<K, V>) delegate).headMap(toKey);
}
@Override
public SortedMap<K, V> tailMap(final K fromKey) {
return ((NavigableMap<K, V>) delegate).tailMap(fromKey);
}
@Override
public K firstKey() {
return ((NavigableMap<K, V>) delegate).firstKey();
}
@Override
public K lastKey() {
return ((NavigableMap<K, V>) delegate).lastKey();
}
@Override
public Entry<K, V> lowerEntry(final K key) {
return ((NavigableMap<K, V>) delegate).lowerEntry(key);
}
@Override
public K lowerKey(final K key) {
return ((NavigableMap<K, V>) delegate).lowerKey(key);
}
@Override
public Entry<K, V> floorEntry(final K key) {
return ((NavigableMap<K, V>) delegate).floorEntry(key);
}
@Override
public K floorKey(final K key) {
return ((NavigableMap<K, V>) delegate).floorKey(key);
}
@Override
public Entry<K, V> ceilingEntry(final K key) {
return ((NavigableMap<K, V>) delegate).ceilingEntry(key);
}
@Override
public K ceilingKey(final K key) {
return ((NavigableMap<K, V>) delegate).ceilingKey(key);
}
@Override
public Entry<K, V> higherEntry(final K key) {
return ((NavigableMap<K, V>) delegate).higherEntry(key);
}
@Override
public K higherKey(final K key) {
return ((NavigableMap<K, V>) delegate).higherKey(key);
}
@Override
public Entry<K, V> firstEntry() {
return ((NavigableMap<K, V>) delegate).firstEntry();
}
@Override
public Entry<K, V> lastEntry() {
return ((NavigableMap<K, V>) delegate).lastEntry();
}
@Override
public Entry<K, V> pollFirstEntry() {
return ((NavigableMap<K, V>) delegate).pollFirstEntry();
}
@Override
public Entry<K, V> pollLastEntry() {
return ((NavigableMap<K, V>) delegate).pollLastEntry();
}
@Override
public NavigableMap<K, V> descendingMap() {
return ((NavigableMap<K, V>) delegate).descendingMap();
}
@Override
public NavigableSet<K> navigableKeySet() {
return ((NavigableMap<K, V>) delegate).navigableKeySet();
}
@Override
public NavigableSet<K> descendingKeySet() {
return ((NavigableMap<K, V>) delegate).descendingKeySet();
}
@Override
public NavigableMap<K, V> subMap(
final K fromKey, final boolean fromInclusive, final K toKey, final boolean toInclusive) {
return ((NavigableMap<K, V>) delegate).subMap(fromKey, fromInclusive, toKey, toInclusive);
}
@Override
public NavigableMap<K, V> headMap(final K toKey, final boolean inclusive) {
return ((NavigableMap<K, V>) delegate).headMap(toKey, inclusive);
}
@Override
public NavigableMap<K, V> tailMap(final K fromKey, final boolean inclusive) {
return ((NavigableMap<K, V>) delegate).tailMap(fromKey, inclusive);
}
@Override
public String toString() {
return "UndoSortedSet{" + "delegate=" + delegate + ", undoLog=" + undoLog + '}';
}
}

@ -0,0 +1,126 @@
/*
* Copyright contributors to Hyperledger Besu
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
package org.hyperledger.besu.collections.undo;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* An undoable value that tracks the value across time.
*
* @param <T> The type of the scaler.
*/
public class UndoScalar<T> implements Undoable {
record UndoEntry<T>(T value, long level) {
UndoEntry(final T value) {
this(value, Undoable.incrementMarkStatic());
}
}
T value;
final List<UndoEntry<T>> undoLog;
/**
* Create an undoable scalar with an initial value
*
* @param value the initial value
* @return the undoable scalar
* @param <T> the type of the scalar
*/
public static <T> UndoScalar<T> of(final T value) {
return new UndoScalar<>(value);
}
/**
* Create an undo scalar with an initial value
*
* @param value the initial value
*/
public UndoScalar(final T value) {
undoLog = new ArrayList<>();
this.value = value;
}
@Override
public long lastUpdate() {
return undoLog.isEmpty() ? 0L : undoLog.get(undoLog.size() - 1).level;
}
/**
* Has this scalar had any change since the inital value
*
* @return true if there are any changes to undo
*/
public boolean updated() {
return !undoLog.isEmpty();
}
/**
* Get the current value of the scalar.
*
* @return the current value
*/
public T get() {
return value;
}
/**
* Set a new value in the scalar.
*
* @param value new value
*/
public void set(final T value) {
if (!Objects.equals(this.value, value)) {
undoLog.add(new UndoEntry<>(this.value));
this.value = value;
}
}
@Override
public void undo(final long mark) {
if (undoLog.isEmpty()) {
return;
}
int pos = undoLog.size() - 1;
while (pos >= 0) {
UndoEntry<T> entry = undoLog.get(pos);
if (entry.level <= mark) {
return;
}
value = entry.value;
undoLog.remove(pos);
pos--;
}
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof UndoScalar<?> that)) return false;
return Objects.equals(value, that.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
@Override
public String toString() {
return "UndoScalar{" + "value=" + value + ", undoLog=" + undoLog + '}';
}
}

@ -33,15 +33,15 @@ import javax.annotation.Nonnull;
*
* @param <V> The type of the collection.
*/
public class UndoSet<V> implements Set<V>, UndoableCollection {
public class UndoSet<V> implements Set<V>, Undoable {
record UndoEntry<V>(V value, boolean add, long level) {
static <V> UndoSet.UndoEntry<V> add(final V value) {
return new UndoEntry<>(value, true, UndoableCollection.incrementMarkStatic());
return new UndoEntry<>(value, true, Undoable.incrementMarkStatic());
}
static <V> UndoSet.UndoEntry<V> remove(final V value) {
return new UndoEntry<>(value, false, UndoableCollection.incrementMarkStatic());
return new UndoEntry<>(value, false, Undoable.incrementMarkStatic());
}
}
@ -79,6 +79,11 @@ public class UndoSet<V> implements Set<V>, UndoableCollection {
}
}
@Override
public long lastUpdate() {
return undoLog.isEmpty() ? 0L : undoLog.get(undoLog.size() - 1).level;
}
@Override
public int size() {
return delegate.size();

@ -37,11 +37,11 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
*
* @param <V> The type of the collection.
*/
public class UndoTable<R, C, V> implements Table<R, C, V>, UndoableCollection {
public class UndoTable<R, C, V> implements Table<R, C, V>, Undoable {
record UndoEntry<R, C, V>(R row, C column, V value, long level) {
UndoEntry(final R row, final C column, final V value) {
this(row, column, value, UndoableCollection.incrementMarkStatic());
this(row, column, value, Undoable.incrementMarkStatic());
}
}
@ -86,6 +86,11 @@ public class UndoTable<R, C, V> implements Table<R, C, V>, UndoableCollection {
}
}
@Override
public long lastUpdate() {
return undoLog.isEmpty() ? 0L : undoLog.get(undoLog.size() - 1).level;
}
@Override
public int size() {
return delegate.size();

@ -23,7 +23,7 @@ import java.util.concurrent.atomic.AtomicLong;
* This allows for tracking of only one undo marker across multiple collections and rolling back
* multiple collections to a consistent point with only one number.
*/
public interface UndoableCollection {
public interface Undoable {
/** The global mark clock for registering marks in undoable collections. */
AtomicLong markState = new AtomicLong();
@ -48,6 +48,14 @@ public interface UndoableCollection {
return markState.incrementAndGet();
}
/**
* The last time this object was updated. Any undo requrests greater than this mark will result in
* no changes.
*
* @return The most recent mark.
*/
long lastUpdate();
/**
* Returns the state of the collection to the state it was in when the mark was retrieved.
* Additions and removals are undone un reverse order until the collection state is restored.

@ -78,6 +78,7 @@ public class EVM {
/** The constant OVERFLOW_RESPONSE. */
protected static final OperationResult OVERFLOW_RESPONSE =
new OperationResult(0L, ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
/** The constant UNDERFLOW_RESPONSE. */
protected static final OperationResult UNDERFLOW_RESPONSE =
new OperationResult(0L, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
@ -86,6 +87,7 @@ public class EVM {
private final GasCalculator gasCalculator;
private final Operation endOfScriptStop;
private final CodeCache codeCache;
private final EvmConfiguration evmConfiguration;
private final EvmSpecVersion evmSpecVersion;
// Optimized operation flags
@ -107,6 +109,7 @@ public class EVM {
this.operations = operations;
this.gasCalculator = gasCalculator;
this.endOfScriptStop = new VirtualOperation(new StopOperation(gasCalculator));
this.evmConfiguration = evmConfiguration;
this.codeCache = new CodeCache(evmConfiguration);
this.evmSpecVersion = evmSpecVersion;
@ -131,6 +134,15 @@ public class EVM {
return evmSpecVersion.maxEofVersion;
}
/**
* Returns the non-fork related configuration parameters of the EVM.
*
* @return the EVM coniguration.
*/
public EvmConfiguration getEvmConfiguration() {
return evmConfiguration;
}
/**
* Returns the configured EVM spec version for this EVM
*

@ -38,7 +38,7 @@ public class SimpleAccount implements MutableAccount {
private final Account parent;
private boolean mutable = true;
private boolean immutable = false;
private Address address;
private final Supplier<Hash> addressHash =
@ -140,7 +140,7 @@ public class SimpleAccount implements MutableAccount {
@Override
public void setNonce(final long value) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
nonce = value;
@ -148,7 +148,7 @@ public class SimpleAccount implements MutableAccount {
@Override
public void setBalance(final Wei value) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
balance = value;
@ -156,7 +156,7 @@ public class SimpleAccount implements MutableAccount {
@Override
public void setCode(final Bytes code) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
this.code = code;
@ -165,7 +165,7 @@ public class SimpleAccount implements MutableAccount {
@Override
public void setStorageValue(final UInt256 key, final UInt256 value) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
storage.put(key, value);
@ -173,7 +173,7 @@ public class SimpleAccount implements MutableAccount {
@Override
public void clearStorage() {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
storage.clear();
@ -186,7 +186,23 @@ public class SimpleAccount implements MutableAccount {
@Override
public void becomeImmutable() {
mutable = false;
immutable = true;
}
/**
* Commit this simple account entry to the parent.
*
* @return true if there was a parent account that was committed to
*/
public boolean commit() {
if (parent instanceof SimpleAccount simpleAccount) {
simpleAccount.balance = balance;
simpleAccount.nonce = nonce;
simpleAccount.storage.putAll(storage);
return true;
} else {
return false;
}
}
/**

@ -16,21 +16,22 @@
package org.hyperledger.besu.evm.internal;
/** The Evm configuration. */
public class EvmConfiguration {
/** The constant DEFAULT. */
public static final EvmConfiguration DEFAULT = new EvmConfiguration(32_000L);
private final long jumpDestCacheWeightKB;
public record EvmConfiguration(long jumpDestCacheWeightKB, WorldUpdaterMode worldUpdaterMode) {
/**
* Instantiates a new Evm configuration.
*
* @param jumpDestCacheWeightKB the jump dest cache weight kb
*/
public EvmConfiguration(final long jumpDestCacheWeightKB) {
this.jumpDestCacheWeightKB = jumpDestCacheWeightKB;
/** How should the world state update be handled within transactions? */
public enum WorldUpdaterMode {
/**
* Stack updates, requiring original account and storage values to read through the whole stack
*/
STACKED,
/** Share a single state for accounts and storage values, undoing changes on reverts. */
JOURNALED
}
/** The constant DEFAULT. */
public static final EvmConfiguration DEFAULT =
new EvmConfiguration(32_000L, WorldUpdaterMode.STACKED);
/**
* Gets jump dest cache weight bytes.
*
@ -39,13 +40,4 @@ public class EvmConfiguration {
public long getJumpDestCacheWeightBytes() {
return jumpDestCacheWeightKB * 1024L;
}
/**
* Gets jump dest cache weight kb.
*
* @return the jump dest cache weight kb
*/
public long getJumpDestCacheWeightKB() {
return jumpDestCacheWeightKB;
}
}

@ -146,25 +146,6 @@ public interface Words {
}
}
/**
* Multiplies a and b, but if an underflow/overflow occurs return the Integer max/min value
*
* @param a first value
* @param b second value
* @return value of a times b if no over/underflows or Integer.MAX_VALUE/Integer.MIN_VALUE
* otherwise
*/
static int clampedMultiply(final int a, final int b) {
long r = (long) a * (long) b;
int ri = (int) r;
if (ri == r) {
return ri;
} else {
// out of bounds, clamp it!
return ((a ^ b) < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE;
}
}
/**
* Returns the lesser of the two values, when compared as an unsigned value
*

@ -19,6 +19,7 @@ import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import java.util.Collection;
import java.util.Collections;
@ -40,9 +41,11 @@ public abstract class AbstractWorldUpdater<W extends WorldView, A extends Accoun
implements WorldUpdater {
private final W world;
private final EvmConfiguration evmConfiguration;
/** The Updated accounts. */
protected Map<Address, UpdateTrackingAccount<A>> updatedAccounts = new ConcurrentHashMap<>();
/** The Deleted accounts. */
protected Set<Address> deletedAccounts =
Collections.synchronizedSet(new BytesTrieSet<>(Address.SIZE));
@ -51,9 +54,11 @@ public abstract class AbstractWorldUpdater<W extends WorldView, A extends Accoun
* Instantiates a new Abstract world updater.
*
* @param world the world
* @param evmConfiguration the EVM Configuration parameters
*/
protected AbstractWorldUpdater(final W world) {
protected AbstractWorldUpdater(final W world, final EvmConfiguration evmConfiguration) {
this.world = world;
this.evmConfiguration = evmConfiguration;
}
/**
@ -131,12 +136,15 @@ public abstract class AbstractWorldUpdater<W extends WorldView, A extends Accoun
* visible on this updater when the returned updater is committed. Note however that updates
* to this updater <b>may or may not</b> be reflected to the created updater, so it is
* <b>strongly</b> advised to not update this updater until the returned one is discarded
* (either after having been committed, or because the updates it represent are meant to be
* (either after having been committed, or because the updates it represents are meant to be
* discarded).
*/
@Override
public WorldUpdater updater() {
return new StackedUpdater<>(this);
return switch (evmConfiguration.worldUpdaterMode()) {
case STACKED -> new StackedUpdater<>(this, evmConfiguration);
case JOURNALED -> new JournaledUpdater<>(this, evmConfiguration);
};
}
/**

@ -0,0 +1,397 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
package org.hyperledger.besu.evm.worldstate;
import static com.google.common.base.Preconditions.checkNotNull;
import org.hyperledger.besu.collections.undo.UndoNavigableMap;
import org.hyperledger.besu.collections.undo.UndoScalar;
import org.hyperledger.besu.collections.undo.Undoable;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.ModificationNotAllowedException;
import org.hyperledger.besu.evm.account.AccountStorageEntry;
import org.hyperledger.besu.evm.account.MutableAccount;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import javax.annotation.Nullable;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
/**
* An implementation of {@link MutableAccount} that tracks updates made to the account since the
* creation of the updater this is linked to.
*
* <p>Note that in practice this only track the modified value of the nonce and balance, but doesn't
* remind if those were modified or not (the reason being that any modification of an account imply
* the underlying trie node will have to be updated, and so knowing if the nonce and balance where
* updated or not doesn't matter, we just need their new value).
*/
public class JournaledAccount implements MutableAccount, Undoable {
private final Address address;
private final Hash addressHash;
@Nullable private MutableAccount account;
private long transactionBoundaryMark;
private final UndoScalar<Long> nonce;
private final UndoScalar<Wei> balance;
private final UndoScalar<Bytes> code;
private final UndoScalar<Hash> codeHash;
private final UndoScalar<Boolean> deleted;
// Only contains updated storage entries, but may contain entry with a value of 0 to signify
// deletion.
private final UndoNavigableMap<UInt256, UInt256> updatedStorage;
private boolean storageWasCleared = false;
boolean immutable;
/**
* Instantiates a new Update tracking account.
*
* @param address the address
*/
JournaledAccount(final Address address) {
checkNotNull(address);
this.address = address;
this.addressHash = this.address.addressHash();
this.account = null;
this.nonce = UndoScalar.of(0L);
this.balance = UndoScalar.of(Wei.ZERO);
this.code = UndoScalar.of(Bytes.EMPTY);
this.codeHash = UndoScalar.of(Hash.EMPTY);
this.deleted = UndoScalar.of(Boolean.FALSE);
this.updatedStorage = UndoNavigableMap.of(new TreeMap<>());
this.transactionBoundaryMark = mark();
}
/**
* Instantiates a new Update tracking account.
*
* @param account the account
*/
public JournaledAccount(final MutableAccount account) {
checkNotNull(account);
this.address = account.getAddress();
this.addressHash =
(account instanceof JournaledAccount journaledAccount)
? journaledAccount.addressHash
: this.address.addressHash();
this.account = account;
if (account instanceof JournaledAccount that) {
this.nonce = that.nonce;
this.balance = that.balance;
this.code = that.code;
this.codeHash = that.codeHash;
this.deleted = that.deleted;
this.updatedStorage = that.updatedStorage;
} else {
this.nonce = UndoScalar.of(account.getNonce());
this.balance = UndoScalar.of(account.getBalance());
this.code = UndoScalar.of(account.getCode());
this.codeHash = UndoScalar.of(account.getCodeHash());
this.deleted = UndoScalar.of(Boolean.FALSE);
this.updatedStorage = UndoNavigableMap.of(new TreeMap<>());
}
transactionBoundaryMark = mark();
}
/**
* The original account over which this tracks updates.
*
* @return The original account over which this tracks updates, or {@code null} if this is a newly
* created account.
*/
public MutableAccount getWrappedAccount() {
return account;
}
/**
* Sets wrapped account.
*
* @param account the account
*/
public void setWrappedAccount(final MutableAccount account) {
if (this.account == null) {
this.account = account;
storageWasCleared = false;
} else {
throw new IllegalStateException("Already tracking a wrapped account");
}
}
/**
* Whether the code of the account was modified.
*
* @return {@code true} if the code was updated.
*/
public boolean codeWasUpdated() {
return code.lastUpdate() >= transactionBoundaryMark;
}
/**
* A map of the storage entries that were modified.
*
* @return a map containing all entries that have been modified. This <b>may</b> contain entries
* with a value of 0 to signify deletion.
*/
@Override
public Map<UInt256, UInt256> getUpdatedStorage() {
return updatedStorage;
}
@Override
public void becomeImmutable() {
immutable = true;
}
@Override
public Address getAddress() {
return address;
}
@Override
public Hash getAddressHash() {
return addressHash;
}
@Override
public long getNonce() {
return nonce.get();
}
@Override
public void setNonce(final long value) {
if (immutable) {
throw new ModificationNotAllowedException();
}
nonce.set(value);
}
@Override
public Wei getBalance() {
return balance.get();
}
@Override
public void setBalance(final Wei value) {
if (immutable) {
throw new ModificationNotAllowedException();
}
balance.set(value);
}
@Override
public Bytes getCode() {
return code.get();
}
@Override
public Hash getCodeHash() {
return codeHash.get();
}
@Override
public boolean hasCode() {
return !code.get().isEmpty();
}
/**
* Mark the account as deleted/not deleted
*
* @param accountDeleted delete or don't delete this account.
*/
public void setDeleted(final boolean accountDeleted) {
if (immutable) {
throw new ModificationNotAllowedException();
}
deleted.set(accountDeleted);
}
/**
* Is the account marked as deleted?
*
* @return is the account deleted?
*/
public Boolean getDeleted() {
return deleted.get();
}
@Override
public void setCode(final Bytes code) {
if (immutable) {
throw new ModificationNotAllowedException();
}
this.code.set(code == null ? Bytes.EMPTY : code);
this.codeHash.set(code == null ? Hash.EMPTY : Hash.hash(code));
}
/** Mark transaction boundary. */
void markTransactionBoundary() {
transactionBoundaryMark = mark();
}
@Override
public UInt256 getStorageValue(final UInt256 key) {
final UInt256 value = updatedStorage.get(key);
if (value != null) {
return value;
}
if (storageWasCleared) {
return UInt256.ZERO;
}
// We haven't updated the key-value yet, so either it's a new account, and it doesn't have the
// key, or we should query the underlying storage for its existing value (which might be 0).
return account == null ? UInt256.ZERO : account.getStorageValue(key);
}
@Override
public UInt256 getOriginalStorageValue(final UInt256 key) {
// if storage was cleared then it is because it was an empty account, hence zero storage
// if we have no backing account, it's a new account, hence zero storage
// otherwise ask outside of what we are journaling, journaled change may not be original value
return (storageWasCleared || account == null) ? UInt256.ZERO : account.getStorageValue(key);
}
@Override
public NavigableMap<Bytes32, AccountStorageEntry> storageEntriesFrom(
final Bytes32 startKeyHash, final int limit) {
final NavigableMap<Bytes32, AccountStorageEntry> entries;
if (account != null) {
entries = account.storageEntriesFrom(startKeyHash, limit);
} else {
entries = new TreeMap<>();
}
updatedStorage.entrySet().stream()
.map(entry -> AccountStorageEntry.forKeyAndValue(entry.getKey(), entry.getValue()))
.filter(entry -> entry.getKeyHash().compareTo(startKeyHash) >= 0)
.forEach(entry -> entries.put(entry.getKeyHash(), entry));
while (entries.size() > limit) {
entries.remove(entries.lastKey());
}
return entries;
}
@Override
public void setStorageValue(final UInt256 key, final UInt256 value) {
if (immutable) {
throw new ModificationNotAllowedException();
}
updatedStorage.put(key, value);
}
@Override
public void clearStorage() {
if (immutable) {
throw new ModificationNotAllowedException();
}
storageWasCleared = true;
updatedStorage.clear();
}
/**
* Gets storage was cleared.
*
* @return boolean if storage was cleared
*/
public boolean getStorageWasCleared() {
return storageWasCleared;
}
/**
* Sets storage was cleared.
*
* @param storageWasCleared the storage was cleared
*/
public void setStorageWasCleared(final boolean storageWasCleared) {
if (immutable) {
throw new ModificationNotAllowedException();
}
this.storageWasCleared = storageWasCleared;
}
@Override
public String toString() {
String storage = updatedStorage.isEmpty() ? "[not updated]" : updatedStorage.toString();
if (updatedStorage.isEmpty() && storageWasCleared) {
storage = "[cleared]";
}
return String.format(
"%s -> {nonce: %s, balance:%s, code:%s, storage:%s }",
address,
nonce,
balance,
code.mark() >= transactionBoundaryMark ? "[not updated]" : code.get(),
storage);
}
@Override
public long lastUpdate() {
return Math.max(
nonce.lastUpdate(),
Math.max(
balance.lastUpdate(),
Math.max(
code.lastUpdate(), Math.max(codeHash.lastUpdate(), updatedStorage.lastUpdate()))));
}
@Override
public void undo(final long mark) {
nonce.undo(mark);
balance.undo(mark);
code.undo(mark);
codeHash.undo(mark);
deleted.undo(mark);
updatedStorage.undo(mark);
}
/** Commit this journaled account entry to the parent, if it is not a journaled account. */
public void commit() {
if (!(account instanceof JournaledAccount)) {
if (nonce.updated()) {
account.setNonce(nonce.get());
}
if (balance.updated()) {
account.setBalance(balance.get());
}
if (code.updated()) {
account.setCode(code.get() == null ? Bytes.EMPTY : code.get());
}
if (updatedStorage.updated()) {
updatedStorage.forEach(account::setStorageValue);
}
}
}
}

@ -0,0 +1,184 @@
/*
* Copyright contributors to Hyperledger Besu
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.worldstate;
import org.hyperledger.besu.collections.undo.UndoMap;
import org.hyperledger.besu.collections.undo.UndoSet;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Optional;
/**
* The Journaled updater.
*
* @param <W> the WorldView type parameter
*/
public class JournaledUpdater<W extends WorldView> implements WorldUpdater {
final EvmConfiguration evmConfiguration;
final WorldUpdater parentWorld;
final AbstractWorldUpdater<W, ? extends MutableAccount> rootWorld;
final UndoMap<Address, JournaledAccount> accounts;
final UndoSet<Address> deleted;
final long undoMark;
/**
* Instantiates a new Stacked updater.
*
* @param world the world
* @param evmConfiguration the EVM Configuration parameters
*/
@SuppressWarnings("unchecked")
public JournaledUpdater(final WorldUpdater world, final EvmConfiguration evmConfiguration) {
parentWorld = world;
this.evmConfiguration = evmConfiguration;
if (world instanceof JournaledUpdater<?>) {
JournaledUpdater<W> journaledUpdater = (JournaledUpdater<W>) world;
accounts = journaledUpdater.accounts;
deleted = journaledUpdater.deleted;
rootWorld = journaledUpdater.rootWorld;
} else if (world instanceof AbstractWorldUpdater<?, ?>) {
accounts = new UndoMap<>(new HashMap<>());
deleted = UndoSet.of(new HashSet<>());
rootWorld = (AbstractWorldUpdater<W, ? extends MutableAccount>) world;
} else {
throw new IllegalArgumentException(
"WorldUpdater must be a JournaledWorldUpdater or an AbstractWorldUpdater");
}
undoMark = accounts.mark();
}
/**
* Get an account suitable for mutation. Defer to parent if not tracked locally.
*
* @param address the account at the address, for mutaton.
* @return the mutable account
*/
protected MutableAccount getForMutation(final Address address) {
final JournaledAccount wrappedTracker = accounts.get(address);
if (wrappedTracker != null) {
return wrappedTracker;
}
final MutableAccount account = rootWorld.getForMutation(address);
return account == null ? null : new UpdateTrackingAccount<>(account);
}
@Override
public Collection<? extends Account> getTouchedAccounts() {
return new ArrayList<>(accounts.values());
}
@Override
public Collection<Address> getDeletedAccountAddresses() {
return new ArrayList<>(deleted);
}
/**
* Remove all changes done by this layer. Rollback to the state prior to the updater's changes.
*/
protected void reset() {
accounts.values().forEach(a -> a.undo(undoMark));
accounts.undo(undoMark);
deleted.undo(undoMark);
}
@Override
public void revert() {
reset();
}
@Override
public void commit() {
if (!(parentWorld instanceof JournaledUpdater<?>)) {
accounts.values().forEach(JournaledAccount::commit);
deleted.forEach(parentWorld::deleteAccount);
}
}
@Override
public Optional<WorldUpdater> parentUpdater() {
return Optional.of(parentWorld);
}
/** Mark transaction boundary. */
@Override
public void markTransactionBoundary() {
accounts.values().forEach(JournaledAccount::markTransactionBoundary);
}
@Override
public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) {
JournaledAccount journaledAccount =
new JournaledAccount(rootWorld.createAccount(address, nonce, balance));
accounts.put(address, journaledAccount);
return new JournaledAccount(journaledAccount);
}
@Override
public MutableAccount getAccount(final Address address) {
// We may have updated it already, so check that first.
final JournaledAccount existing = accounts.get(address);
if (existing != null) {
return existing;
}
if (deleted.contains(address)) {
return null;
}
// Otherwise, get it from our wrapped view and create a new update tracker.
final MutableAccount origin = rootWorld.getAccount(address);
if (origin == null) {
return null;
} else {
var newAccount = new JournaledAccount(origin);
accounts.put(address, newAccount);
return newAccount;
}
}
@Override
public void deleteAccount(final Address address) {
deleted.add(address);
var account = accounts.get(address);
if (account != null) {
account.setDeleted(true);
}
}
@Override
public Account get(final Address address) {
final MutableAccount existing = accounts.get(address);
if (existing != null) {
return existing;
}
if (deleted.contains(address)) {
return null;
}
return rootWorld.get(address);
}
@Override
public WorldUpdater updater() {
return new JournaledUpdater<W>(this, evmConfiguration);
}
}

@ -16,6 +16,7 @@ package org.hyperledger.besu.evm.worldstate;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import java.util.ArrayList;
import java.util.Collection;
@ -33,9 +34,11 @@ public class StackedUpdater<W extends WorldView, A extends Account>
* Instantiates a new Stacked updater.
*
* @param world the world
* @param evmConfiguration the EVM Configuration parameters
*/
public StackedUpdater(final AbstractWorldUpdater<W, A> world) {
super(world);
public StackedUpdater(
final AbstractWorldUpdater<W, A> world, final EvmConfiguration evmConfiguration) {
super(world, evmConfiguration);
}
@Override
@ -109,6 +112,7 @@ public class StackedUpdater<W extends WorldView, A extends Account>
}
/** Mark transaction boundary. */
@Override
public void markTransactionBoundary() {
getUpdatedAccounts().forEach(UpdateTrackingAccount::markTransactionBoundary);
}

@ -52,7 +52,7 @@ public class UpdateTrackingAccount<A extends Account> implements MutableAccount
@Nullable private A account; // null if this is a new account.
private boolean mutable = true;
private boolean immutable;
private long nonce;
private Wei balance;
@ -173,7 +173,7 @@ public class UpdateTrackingAccount<A extends Account> implements MutableAccount
@Override
public void setNonce(final long value) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
this.nonce = value;
@ -186,7 +186,7 @@ public class UpdateTrackingAccount<A extends Account> implements MutableAccount
@Override
public void setBalance(final Wei value) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
this.balance = value;
@ -221,7 +221,7 @@ public class UpdateTrackingAccount<A extends Account> implements MutableAccount
@Override
public void setCode(final Bytes code) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
this.updatedCode = code;
@ -281,7 +281,7 @@ public class UpdateTrackingAccount<A extends Account> implements MutableAccount
@Override
public void setStorageValue(final UInt256 key, final UInt256 value) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
updatedStorage.put(key, value);
@ -289,7 +289,7 @@ public class UpdateTrackingAccount<A extends Account> implements MutableAccount
@Override
public void clearStorage() {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
storageWasCleared = true;
@ -298,7 +298,7 @@ public class UpdateTrackingAccount<A extends Account> implements MutableAccount
@Override
public void becomeImmutable() {
mutable = false;
immutable = true;
}
/**

@ -38,7 +38,7 @@ public interface WorldUpdater extends MutableWorldView {
* Creates a new account, or reset it (that is, act as if it was deleted and created anew) if it
* already exists.
*
* <p>After this call, the account will exists and will have the provided nonce and balance. His
* <p>After this call, the account will exists and will have the provided nonce and balance. The
* code and storage will be empty.
*
* @param address the address of the account to create (or reset).
@ -151,4 +151,9 @@ public interface WorldUpdater extends MutableWorldView {
new ArrayList<>(getTouchedAccounts())
.stream().filter(Account::isEmpty).forEach(a -> deleteAccount(a.getAddress()));
}
/** Mark transaction boundary. */
default void markTransactionBoundary() {
// default is to ignore
}
}

@ -37,7 +37,7 @@ public class ToyAccount implements MutableAccount {
private final Account parent;
private boolean mutable = true;
private boolean immutable = false;
private Address address;
private final Supplier<Hash> addressHash =
@ -120,7 +120,7 @@ public class ToyAccount implements MutableAccount {
@Override
public void setNonce(final long value) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
nonce = value;
@ -128,7 +128,7 @@ public class ToyAccount implements MutableAccount {
@Override
public void setBalance(final Wei value) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
balance = value;
@ -136,7 +136,7 @@ public class ToyAccount implements MutableAccount {
@Override
public void setCode(final Bytes code) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
this.code = code;
@ -145,7 +145,7 @@ public class ToyAccount implements MutableAccount {
@Override
public void setStorageValue(final UInt256 key, final UInt256 value) {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
storage.put(key, value);
@ -153,7 +153,7 @@ public class ToyAccount implements MutableAccount {
@Override
public void clearStorage() {
if (!mutable) {
if (immutable) {
throw new ModificationNotAllowedException();
}
storage.clear();
@ -166,6 +166,6 @@ public class ToyAccount implements MutableAccount {
@Override
public void becomeImmutable() {
mutable = false;
immutable = true;
}
}

Loading…
Cancel
Save