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; } }