From 094c8416df9cf003e0a7d1fb0651d34468137896 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 31 Oct 2023 10:10:48 -0600 Subject: [PATCH] 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 --- .../org/hyperledger/besu/cli/BesuCommand.java | 1 + .../cli/ConfigurationOverviewBuilder.java | 15 + .../besu/cli/options/unstable/EvmOptions.java | 19 +- .../controller/BesuControllerBuilder.java | 71 ++-- .../cli/ConfigurationOverviewBuilderTest.java | 23 + build.gradle | 7 + .../jsonrpc/internal/methods/TraceBlock.java | 5 +- .../internal/processor/BlockTracer.java | 5 +- .../internal/processor/TransactionTracer.java | 3 +- .../tracing/diff/StateDiffGenerator.java | 8 +- .../worldstate/PrunerIntegrationTest.java | 7 +- .../besu/ethereum/bonsai/BonsaiAccount.java | 20 +- .../bonsai/BonsaiWorldStateProvider.java | 11 +- .../cache/CachedWorldStorageManager.java | 21 +- .../bonsai/worldview/BonsaiWorldState.java | 13 +- .../BonsaiWorldStateUpdateAccumulator.java | 13 +- .../besu/ethereum/chain/GenesisState.java | 3 +- .../besu/ethereum/core/PrivacyParameters.java | 4 +- .../worldstate/DefaultMutableWorldState.java | 59 +-- .../worldstate/DefaultWorldStateArchive.java | 11 +- .../core/InMemoryKeyValueStorageProvider.java | 15 +- .../core/InMemoryPrivacyStorageProvider.java | 12 +- .../BlockImportExceptionHandlingTest.java | 16 +- .../bonsai/AbstractIsolationTests.java | 4 +- .../bonsai/BonsaiWorldStateArchiveTest.java | 63 +-- .../besu/ethereum/bonsai/LogRollingTests.java | 22 +- .../besu/ethereum/bonsai/RollingImport.java | 5 +- .../bonsai/trielog/TrieLogManagerTests.java | 9 +- .../DefaultMutableWorldStateTest.java | 6 +- .../worldstate/MarkSweepPrunerTest.java | 17 +- .../FastWorldStateDownloaderTest.java | 103 +++-- .../besu/evmtool/BenchmarkSubCommand.java | 2 + .../besu/evmtool/BlockchainModule.java | 10 +- .../org/hyperledger/besu/evmtool/EvmTool.java | 3 + .../besu/evmtool/EvmToolCommand.java | 16 +- .../evmtool/EvmToolCommandOptionsModule.java | 31 ++ .../besu/evmtool/GenesisFileModule.java | 4 +- .../evmtool/MainnetGenesisFileModule.java | 5 +- .../besu/evmtool/T8nServerSubCommand.java | 92 ++-- .../besu/evmtool/T8nSubCommand.java | 7 +- .../BonsaiReferenceTestUpdateAccumulator.java | 6 +- .../BonsaiReferenceTestWorldState.java | 45 +- .../DefaultReferenceTestWorldState.java | 6 +- .../ReferenceTestWorldState.java | 9 +- .../vm/BlockchainReferenceTestTools.java | 26 +- .../vm/GeneralStateReferenceTestTools.java | 35 +- .../ethereum/retesteth/RetestethContext.java | 3 +- .../besu/collections/undo/UndoList.java | 9 +- .../besu/collections/undo/UndoMap.java | 18 +- .../collections/undo/UndoNavigableMap.java | 181 ++++++++ .../besu/collections/undo/UndoScalar.java | 126 ++++++ .../besu/collections/undo/UndoSet.java | 11 +- .../besu/collections/undo/UndoTable.java | 9 +- ...{UndoableCollection.java => Undoable.java} | 10 +- .../java/org/hyperledger/besu/evm/EVM.java | 12 + .../besu/evm/fluent/SimpleAccount.java | 30 +- .../besu/evm/internal/EvmConfiguration.java | 34 +- .../hyperledger/besu/evm/internal/Words.java | 19 - .../evm/worldstate/AbstractWorldUpdater.java | 14 +- .../besu/evm/worldstate/JournaledAccount.java | 397 ++++++++++++++++++ .../besu/evm/worldstate/JournaledUpdater.java | 184 ++++++++ .../besu/evm/worldstate/StackedUpdater.java | 8 +- .../evm/worldstate/UpdateTrackingAccount.java | 14 +- .../besu/evm/worldstate/WorldUpdater.java | 7 +- .../hyperledger/besu/evm/toy/ToyAccount.java | 14 +- 65 files changed, 1598 insertions(+), 390 deletions(-) create mode 100644 evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java create mode 100644 evm/src/main/java/org/hyperledger/besu/collections/undo/UndoScalar.java rename evm/src/main/java/org/hyperledger/besu/collections/undo/{UndoableCollection.java => Undoable.java} (92%) create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 6d6bed2bfc..5a6fc38671 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -3433,6 +3433,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { } builder.setTxPoolImplementation(buildTransactionPoolConfiguration().getTxPoolImplementation()); + builder.setWorldStateUpdateMode(unstableEvmOptions.toDomainObject().worldUpdaterMode()); builder.setPluginContext(besuComponent.getBesuPluginContext()); diff --git a/besu/src/main/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilder.java b/besu/src/main/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilder.java index 508b195a8e..8b25f6377e 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilder.java @@ -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 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:"); diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/EvmOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/EvmOptions.java index 7e787cf83b..f91fcc880f 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/EvmOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/EvmOptions.java @@ -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 { /** 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 { 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 getCLIOptions() { - return Arrays.asList(JUMPDEST_CACHE_WEIGHT); + return List.of(JUMPDEST_CACHE_WEIGHT, WORLDSTATE_UPDATE_MODE); } } diff --git a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java index 802be1215b..1d3c96aea6 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -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) { diff --git a/besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java b/besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java index 4fff2b36f5..7642e14c94 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java @@ -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"); + } } diff --git a/build.gradle b/build.gradle index a1950eb65d..63397b210e 100644 --- a/build.gradle +++ b/build.gradle @@ -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. diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceBlock.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceBlock.java index 6ac03eb91a..f4e1255473 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceBlock.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceBlock.java @@ -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; diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java index d7386453ff..b665109370 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java @@ -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(); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java index 2811a01135..5b9cdfc2fc 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java @@ -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()) { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/diff/StateDiffGenerator.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/diff/StateDiffGenerator.java index e8b975e11b..ffc816db73 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/diff/StateDiffGenerator.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/diff/StateDiffGenerator.java @@ -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 generateStateDiff(final TransactionTrace transactionTrace) { final List 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 storageDiff = new TreeMap<>(); for (final Map.Entry entry : - ((UpdateTrackingAccount) updatedAccount) - .getUpdatedStorage() - .entrySet()) { // FIXME cast + ((MutableAccount) updatedAccount).getUpdatedStorage().entrySet()) { final UInt256 newValue = entry.getValue(); if (rootAccount == null) { if (!UInt256.ZERO.equals(newValue)) { diff --git a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/worldstate/PrunerIntegrationTest.java b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/worldstate/PrunerIntegrationTest.java index 86f2694c3c..77f871f100 100644 --- a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/worldstate/PrunerIntegrationTest.java +++ b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/worldstate/PrunerIntegrationTest.java @@ -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 trie, final Set collector) { final Bytes32 rootHash = trie.getRootHash(); trie.visitAll( - (node) -> { + node -> { if (node.isReferencedByHash() || node.getHash().equals(rootHash)) { collector.add(node.getEncodedBytes()); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiAccount.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiAccount.java index 7362d345ce..476a0ab22e 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiAccount.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiAccount.java @@ -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 diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateProvider.java index b64f3855a3..f0869333ba 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateProvider.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateProvider.java @@ -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 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()) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/cache/CachedWorldStorageManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/cache/CachedWorldStorageManager.java index f2434bbf91..bf205f05b7 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/cache/CachedWorldStorageManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/cache/CachedWorldStorageManager.java @@ -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 cachedWorldStatesByHash; @@ -49,19 +51,26 @@ public class CachedWorldStorageManager implements BonsaiStorageSubscriber { final BonsaiWorldStateProvider archive, final BonsaiWorldStateKeyValueStorage worldStateStorage, final Map 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 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()); }); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldState.java index d4c4c223a2..7c7f7fd551 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldState.java @@ -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; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java index 23fd7625fa..f66fcd0f56 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java @@ -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> accountsToUpdate; private final Map> codeToUpdate = new ConcurrentHashMap<>(); private final Set
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> accountPreloader, - final Consumer storagePreloader) { - super(world); + final Consumer 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 extends ForwardingMap { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java index f6c969d58b..a69e920dd2 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java @@ -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(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java index 43bdff8aaf..1d4cfdf6c1 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java @@ -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 = diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldState.java index b9283d50da..d50f702af3 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldState.java @@ -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 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 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 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 getStorageTrieKeyPreimage(final Bytes32 trieKey) { + return Optional.ofNullable(newStorageKeyPreimages.get(trieKey)) + .or(() -> preimageStorage.getStorageTrieKeyPreimage(trieKey)); + } } protected static class Updater extends AbstractWorldUpdater { - 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> entries = - new TreeSet<>( - Comparator.comparing( - (Function, UInt256>) Map.Entry::getKey)); + new TreeSet<>(Map.Entry.comparingByKey()); entries.addAll(updatedStorage.entrySet()); for (final Map.Entry 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); + } } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultWorldStateArchive.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultWorldStateArchive.java index b1955561cc..a29bd9b2ff 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultWorldStateArchive.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultWorldStateArchive.java @@ -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 diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryKeyValueStorageProvider.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryKeyValueStorageProvider.java index f5d5fb9726..e54cd96a30 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryKeyValueStorageProvider.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryKeyValueStorageProvider.java @@ -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() { diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryPrivacyStorageProvider.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryPrivacyStorageProvider.java index 7a76d88d64..91e115cffa 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryPrivacyStorageProvider.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryPrivacyStorageProvider.java @@ -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 + } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java index 9f1e30ff3b..a33f6a2b04 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java @@ -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()); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/AbstractIsolationTests.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/AbstractIsolationTests.java index a6441f970f..9da6cbc7fc 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/AbstractIsolationTests.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/AbstractIsolationTests.java @@ -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()); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateArchiveTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateArchiveTest.java index 0721522ffb..0c664f77e0 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateArchiveTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateArchiveTest.java @@ -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); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/LogRollingTests.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/LogRollingTests.java index 054e1b88cc..225930fdb7 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/LogRollingTests.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/LogRollingTests.java @@ -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 = diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/RollingImport.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/RollingImport.java index 397ebf1f9a..5aa0f68daa 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/RollingImport.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/RollingImport.java @@ -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( diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/trielog/TrieLogManagerTests.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/trielog/TrieLogManagerTests.java index 0c911d3355..9dfe5fac2d 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/trielog/TrieLogManagerTests.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/trielog/TrieLogManagerTests.java @@ -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 -> { diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldStateTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldStateTest.java index bc632e0f62..4e42356341 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldStateTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldStateTest.java @@ -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); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/MarkSweepPrunerTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/MarkSweepPrunerTest.java index c91ea5dcb0..28997591f7 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/MarkSweepPrunerTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/MarkSweepPrunerTest.java @@ -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 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); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/FastWorldStateDownloaderTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/FastWorldStateDownloaderTest.java index 74c6364383..e69a56e498 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/FastWorldStateDownloaderTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/FastWorldStateDownloaderTest.java @@ -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 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 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 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 knownNodes = new HashSet<>(); final Set 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 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 taskCollection = spy(new InMemoryTasksPriorityQueues<>()); List 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)); diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BenchmarkSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BenchmarkSubCommand.java index 078a6e1c4c..8078bd6ba1 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BenchmarkSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BenchmarkSubCommand.java @@ -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) { diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainModule.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainModule.java index 91cdad76a1..0109d6c819 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainModule.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainModule.java @@ -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); } } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmTool.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmTool.java index c6bef5726c..00582c9503 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmTool.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmTool.java @@ -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); diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java index 4f2c2a5a5f..743c215ecb 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java @@ -126,19 +126,25 @@ public class EvmToolCommand implements Runnable { names = {"--sender"}, paramLabel = "
", description = "Calling address for this invocation.") - private final Address sender = Address.fromHexString("0x00"); + private final Address sender = Address.ZERO; @Option( names = {"--receiver"}, paramLabel = "
", description = "Receiving address for this invocation.") - private final Address receiver = Address.fromHexString("0x00"); + private final Address receiver = Address.ZERO; + + @Option( + names = {"--contract"}, + paramLabel = "
", + description = "The address holding the contract code.") + private final Address contract = Address.ZERO; @Option( names = {"--coinbase"}, paramLabel = "
", 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) diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommandOptionsModule.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommandOptionsModule.java index 9c97c8d36f..b8df5fa4a9 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommandOptionsModule.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommandOptionsModule.java @@ -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); + } } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/GenesisFileModule.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/GenesisFileModule.java index 6bd2ee52ac..596c9eddc7 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/GenesisFileModule.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/GenesisFileModule.java @@ -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 fork, - @Named("RevertReasonEnabled") final boolean revertReasonEnabled) { + @Named("RevertReasonEnabled") final boolean revertReasonEnabled, + final EvmConfiguration evmConfiguration) { throw new RuntimeException("Abstract"); } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/MainnetGenesisFileModule.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/MainnetGenesisFileModule.java index 5445fe2b43..e7c576c585 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/MainnetGenesisFileModule.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/MainnetGenesisFileModule.java @@ -52,7 +52,8 @@ class MainnetGenesisFileModule extends GenesisFileModule { ProtocolSchedule provideProtocolSchedule( final GenesisConfigOptions configOptions, @Named("Fork") final Optional fork, - @Named("RevertReasonEnabled") final boolean revertReasonEnabled) { + @Named("RevertReasonEnabled") final boolean revertReasonEnabled, + final EvmConfiguration evmConfiguration) { final Optional 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> createSchedules() { diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nServerSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nServerSubCommand.java index 8b636b9a8c..a0ef6384b0 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nServerSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nServerSubCommand.java @@ -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 transactions = new ArrayList<>(); - List 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.of(txt).iterator(), - transactions, - rejections); + Map accounts = + objectMapper.convertValue(input.get("alloc"), new TypeReference<>() {}); + + final T8nExecutor.T8nResult result; + try (ReferenceTestWorldState initialWorldState = + ReferenceTestWorldState.create(accounts, EvmConfiguration.DEFAULT)) { + initialWorldState.persist(null); + List transactions = new ArrayList<>(); + List 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.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()); diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java index 006a806e21..40f7bb0798 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java @@ -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 accounts = + objectMapper.convertValue(config.get("alloc"), new TypeReference<>() {}); + initialWorldState = ReferenceTestWorldState.create(accounts, EvmConfiguration.DEFAULT); initialWorldState.persist(null); var node = config.get("txs"); Iterator it; diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestUpdateAccumulator.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestUpdateAccumulator.java index c8349b7f79..e346b5abf6 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestUpdateAccumulator.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestUpdateAccumulator.java @@ -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> accountPreloader, final Consumer storagePreloader, - final BonsaiPreImageProxy preImageProxy) { - super(world, accountPreloader, storagePreloader); + final BonsaiPreImageProxy preImageProxy, + final EvmConfiguration evmConfiguration) { + super(world, accountPreloader, storagePreloader, evmConfiguration); this.preImageProxy = preImageProxy; } diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldState.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldState.java index 59eb9329f3..c46536592c 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldState.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldState.java @@ -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 accounts) { + return create(accounts, EvmConfiguration.DEFAULT); + } + + @JsonCreator + public static BonsaiReferenceTestWorldState create( + final Map 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 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 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 diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/DefaultReferenceTestWorldState.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/DefaultReferenceTestWorldState.java index ec0b058ba9..45771578e1 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/DefaultReferenceTestWorldState.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/DefaultReferenceTestWorldState.java @@ -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 diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java index 825d7a2cd4..3d4db1edaa 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java @@ -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 accounts) { // delegate to a Bonsai reference test world state: - return BonsaiReferenceTestWorldState.create(accounts); + return create(accounts, EvmConfiguration.DEFAULT); + } + + static ReferenceTestWorldState create( + final Map accounts, final EvmConfiguration evmConfiguration) { + // delegate to a Bonsai reference test world state: + return BonsaiReferenceTestWorldState.create(accounts, evmConfiguration); } } diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java index af6405fb0f..fea3d8671d 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java @@ -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 diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java index af6181dab0..e0b0c7332e 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java @@ -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 = diff --git a/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java b/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java index bed7652c6d..a467248940 100644 --- a/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java +++ b/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java @@ -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); diff --git a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoList.java b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoList.java index cda40649af..72cdc56fee 100644 --- a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoList.java +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoList.java @@ -33,11 +33,11 @@ import javax.annotation.Nonnull; * * @param The type of the collection. */ -public class UndoList implements List, UndoableCollection { +public class UndoList implements List, Undoable { record UndoEntry(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 implements List, 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)); diff --git a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoMap.java b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoMap.java index 1a76985b5f..08a20b447c 100644 --- a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoMap.java +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoMap.java @@ -34,11 +34,11 @@ import javax.annotation.Nonnull; * * @param The type of the collection. */ -public class UndoMap implements Map, UndoableCollection { +public class UndoMap implements Map, Undoable { record UndoEntry(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 implements Map, 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(); diff --git a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java new file mode 100644 index 0000000000..3bbdc242e1 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java @@ -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. + * + *

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 The type of the collection. + */ +public class UndoNavigableMap extends UndoMap implements NavigableMap { + + /** + * Create an UndoMap backed by another Map instance. + * + * @param delegate The Map instance to use for backing storage + */ + public UndoNavigableMap(final NavigableMap 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 the key type + * @param the value type + */ + public static UndoNavigableMap of(final NavigableMap map) { + return new UndoNavigableMap<>(map); + } + + @Override + public Comparator comparator() { + return ((NavigableMap) delegate).comparator(); + } + + @Override + public SortedMap subMap(final K fromKey, final K toKey) { + return ((NavigableMap) delegate).subMap(fromKey, toKey); + } + + @Override + public SortedMap headMap(final K toKey) { + return ((NavigableMap) delegate).headMap(toKey); + } + + @Override + public SortedMap tailMap(final K fromKey) { + return ((NavigableMap) delegate).tailMap(fromKey); + } + + @Override + public K firstKey() { + return ((NavigableMap) delegate).firstKey(); + } + + @Override + public K lastKey() { + return ((NavigableMap) delegate).lastKey(); + } + + @Override + public Entry lowerEntry(final K key) { + return ((NavigableMap) delegate).lowerEntry(key); + } + + @Override + public K lowerKey(final K key) { + return ((NavigableMap) delegate).lowerKey(key); + } + + @Override + public Entry floorEntry(final K key) { + return ((NavigableMap) delegate).floorEntry(key); + } + + @Override + public K floorKey(final K key) { + return ((NavigableMap) delegate).floorKey(key); + } + + @Override + public Entry ceilingEntry(final K key) { + return ((NavigableMap) delegate).ceilingEntry(key); + } + + @Override + public K ceilingKey(final K key) { + return ((NavigableMap) delegate).ceilingKey(key); + } + + @Override + public Entry higherEntry(final K key) { + return ((NavigableMap) delegate).higherEntry(key); + } + + @Override + public K higherKey(final K key) { + return ((NavigableMap) delegate).higherKey(key); + } + + @Override + public Entry firstEntry() { + return ((NavigableMap) delegate).firstEntry(); + } + + @Override + public Entry lastEntry() { + return ((NavigableMap) delegate).lastEntry(); + } + + @Override + public Entry pollFirstEntry() { + return ((NavigableMap) delegate).pollFirstEntry(); + } + + @Override + public Entry pollLastEntry() { + return ((NavigableMap) delegate).pollLastEntry(); + } + + @Override + public NavigableMap descendingMap() { + return ((NavigableMap) delegate).descendingMap(); + } + + @Override + public NavigableSet navigableKeySet() { + return ((NavigableMap) delegate).navigableKeySet(); + } + + @Override + public NavigableSet descendingKeySet() { + return ((NavigableMap) delegate).descendingKeySet(); + } + + @Override + public NavigableMap subMap( + final K fromKey, final boolean fromInclusive, final K toKey, final boolean toInclusive) { + return ((NavigableMap) delegate).subMap(fromKey, fromInclusive, toKey, toInclusive); + } + + @Override + public NavigableMap headMap(final K toKey, final boolean inclusive) { + return ((NavigableMap) delegate).headMap(toKey, inclusive); + } + + @Override + public NavigableMap tailMap(final K fromKey, final boolean inclusive) { + return ((NavigableMap) delegate).tailMap(fromKey, inclusive); + } + + @Override + public String toString() { + return "UndoSortedSet{" + "delegate=" + delegate + ", undoLog=" + undoLog + '}'; + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoScalar.java b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoScalar.java new file mode 100644 index 0000000000..0e3356c379 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoScalar.java @@ -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 The type of the scaler. + */ +public class UndoScalar implements Undoable { + record UndoEntry(T value, long level) { + UndoEntry(final T value) { + this(value, Undoable.incrementMarkStatic()); + } + } + + T value; + final List> undoLog; + + /** + * Create an undoable scalar with an initial value + * + * @param value the initial value + * @return the undoable scalar + * @param the type of the scalar + */ + public static UndoScalar 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 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 + '}'; + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoSet.java b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoSet.java index 1ba509d309..d1f018ac14 100644 --- a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoSet.java +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoSet.java @@ -33,15 +33,15 @@ import javax.annotation.Nonnull; * * @param The type of the collection. */ -public class UndoSet implements Set, UndoableCollection { +public class UndoSet implements Set, Undoable { record UndoEntry(V value, boolean add, long level) { static UndoSet.UndoEntry add(final V value) { - return new UndoEntry<>(value, true, UndoableCollection.incrementMarkStatic()); + return new UndoEntry<>(value, true, Undoable.incrementMarkStatic()); } static UndoSet.UndoEntry 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 implements Set, UndoableCollection { } } + @Override + public long lastUpdate() { + return undoLog.isEmpty() ? 0L : undoLog.get(undoLog.size() - 1).level; + } + @Override public int size() { return delegate.size(); diff --git a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoTable.java b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoTable.java index 26af3225f4..0109ccdc8f 100644 --- a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoTable.java +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoTable.java @@ -37,11 +37,11 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; * * @param The type of the collection. */ -public class UndoTable implements Table, UndoableCollection { +public class UndoTable implements Table, Undoable { record UndoEntry(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 implements Table, UndoableCollection { } } + @Override + public long lastUpdate() { + return undoLog.isEmpty() ? 0L : undoLog.get(undoLog.size() - 1).level; + } + @Override public int size() { return delegate.size(); diff --git a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoableCollection.java b/evm/src/main/java/org/hyperledger/besu/collections/undo/Undoable.java similarity index 92% rename from evm/src/main/java/org/hyperledger/besu/collections/undo/UndoableCollection.java rename to evm/src/main/java/org/hyperledger/besu/collections/undo/Undoable.java index 25bfaa8ee5..274bbc2297 100644 --- a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoableCollection.java +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/Undoable.java @@ -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. diff --git a/evm/src/main/java/org/hyperledger/besu/evm/EVM.java b/evm/src/main/java/org/hyperledger/besu/evm/EVM.java index a77b4ac93a..4ce73ef9cc 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/EVM.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/EVM.java @@ -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 * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java index 05ad66574f..edc3edd1a4 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java @@ -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 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; + } } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/internal/EvmConfiguration.java b/evm/src/main/java/org/hyperledger/besu/evm/internal/EvmConfiguration.java index 19533a538a..c3ad7ba17a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/internal/EvmConfiguration.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/internal/EvmConfiguration.java @@ -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; - } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java b/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java index 71d599d0a2..821406c8aa 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java @@ -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 * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java index 2eb48dd1ad..947f6f2427 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java @@ -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> updatedAccounts = new ConcurrentHashMap<>(); + /** The Deleted accounts. */ protected Set

deletedAccounts = Collections.synchronizedSet(new BytesTrieSet<>(Address.SIZE)); @@ -51,9 +54,11 @@ public abstract class AbstractWorldUpdatermay or may not be reflected to the created updater, so it is * strongly 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); + }; } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java new file mode 100644 index 0000000000..e527e5301d --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java @@ -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. + * + *

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 nonce; + private final UndoScalar balance; + private final UndoScalar code; + private final UndoScalar codeHash; + private final UndoScalar deleted; + + // Only contains updated storage entries, but may contain entry with a value of 0 to signify + // deletion. + private final UndoNavigableMap 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 may contain entries + * with a value of 0 to signify deletion. + */ + @Override + public Map 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 storageEntriesFrom( + final Bytes32 startKeyHash, final int limit) { + final NavigableMap 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); + } + } + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java new file mode 100644 index 0000000000..bdec559e07 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java @@ -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 the WorldView type parameter + */ +public class JournaledUpdater implements WorldUpdater { + + final EvmConfiguration evmConfiguration; + final WorldUpdater parentWorld; + final AbstractWorldUpdater rootWorld; + final UndoMap accounts; + final UndoSet

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 journaledUpdater = (JournaledUpdater) 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) 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 getTouchedAccounts() { + return new ArrayList<>(accounts.values()); + } + + @Override + public Collection
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 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(this, evmConfiguration); + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/StackedUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/StackedUpdater.java index 348f76dfa1..471a807e70 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/StackedUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/StackedUpdater.java @@ -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 * Instantiates a new Stacked updater. * * @param world the world + * @param evmConfiguration the EVM Configuration parameters */ - public StackedUpdater(final AbstractWorldUpdater world) { - super(world); + public StackedUpdater( + final AbstractWorldUpdater world, final EvmConfiguration evmConfiguration) { + super(world, evmConfiguration); } @Override @@ -109,6 +112,7 @@ public class StackedUpdater } /** Mark transaction boundary. */ + @Override public void markTransactionBoundary() { getUpdatedAccounts().forEach(UpdateTrackingAccount::markTransactionBoundary); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/UpdateTrackingAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/UpdateTrackingAccount.java index bc8a0fdfcd..a8c697a21f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/UpdateTrackingAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/UpdateTrackingAccount.java @@ -52,7 +52,7 @@ public class UpdateTrackingAccount 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 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 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 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 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 implements MutableAccount @Override public void clearStorage() { - if (!mutable) { + if (immutable) { throw new ModificationNotAllowedException(); } storageWasCleared = true; @@ -298,7 +298,7 @@ public class UpdateTrackingAccount implements MutableAccount @Override public void becomeImmutable() { - mutable = false; + immutable = true; } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java index fe88c86a7d..9b57b6ab9e 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java @@ -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. * - *

After this call, the account will exists and will have the provided nonce and balance. His + *

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 + } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/toy/ToyAccount.java b/evm/src/test/java/org/hyperledger/besu/evm/toy/ToyAccount.java index 5819ade39d..64e2b7ae07 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/toy/ToyAccount.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/toy/ToyAccount.java @@ -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 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; } }