From d459de999fc81fe952b9f61c1284322c3c5067f5 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 12 Sep 2023 11:28:27 -0600 Subject: [PATCH 01/10] Mostly There JournaledUpdater to replace StackedUpdater in most cases. All but 279 ref tests pass. So not done yet. Mostly EIP159/161 issues. Signed-off-by: Danno Ferrin --- .../jsonrpc/internal/methods/TraceBlock.java | 5 +- .../internal/processor/BlockTracer.java | 5 +- .../internal/processor/TransactionTracer.java | 6 +- .../BonsaiWorldStateUpdateAccumulator.java | 114 +++--- .../worldstate/DefaultMutableWorldState.java | 38 +- .../besu/evmtool/BenchmarkSubCommand.java | 2 + .../org/hyperledger/besu/evmtool/EvmTool.java | 4 + .../besu/collections/undo/UndoList.java | 9 +- .../besu/collections/undo/UndoMap.java | 13 +- .../collections/undo/UndoNavigableMap.java | 173 +++++++++ .../besu/collections/undo/UndoScalar.java | 94 +++++ .../besu/collections/undo/UndoSet.java | 11 +- .../besu/collections/undo/UndoTable.java | 9 +- ...{UndoableCollection.java => Undoable.java} | 10 +- ...ularExponentiationPrecompiledContract.java | 41 +- .../evm/worldstate/AbstractWorldUpdater.java | 7 +- .../besu/evm/worldstate/JournaledAccount.java | 362 ++++++++++++++++++ .../besu/evm/worldstate/JournaledUpdater.java | 172 +++++++++ .../besu/evm/worldstate/StackedUpdater.java | 1 + .../besu/evm/worldstate/WorldUpdater.java | 7 +- 20 files changed, 961 insertions(+), 122 deletions(-) create mode 100644 evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java create mode 100644 evm/src/main/java/org/hyperledger/besu/collections/undo/UndoScalar.java rename evm/src/main/java/org/hyperledger/besu/collections/undo/{UndoableCollection.java => Undoable.java} (92%) create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java diff --git a/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 6ac03eb91a1..f4e12554738 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.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 WorldUpdater getNextUpdater() { // 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 d7386453ff2..b6651093702 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.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 @@ private BlockReplay.TransactionAction prepareReplayAction( // 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 71628c88969..e0ede8a4b26 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,6 +33,7 @@ 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.AbstractWorldUpdater; import org.hyperledger.besu.evm.worldstate.StackedUpdater; import org.hyperledger.besu.evm.worldstate.WorldUpdater; @@ -114,7 +115,8 @@ public List traceTransactionToFile( .performActionWithBlock( blockHash, (body, header, blockchain, transactionProcessor, protocolSpec) -> { - WorldUpdater stackedUpdater = mutableWorldState.updater().updater(); + WorldUpdater stackedUpdater = + new StackedUpdater<>((AbstractWorldUpdater) mutableWorldState).updater(); final List traces = new ArrayList<>(); final Wei blobGasPrice = protocolSpec @@ -125,7 +127,7 @@ public List traceTransactionToFile( .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/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 2a04f92fb19..b7039957691 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 @@ -44,6 +44,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.function.Function; import com.google.common.collect.ForwardingMap; @@ -176,17 +177,15 @@ protected BonsaiAccount loadAccount( final BonsaiValue bonsaiValue = accountsToUpdate.get(address); if (bonsaiValue == null) { final Account account; - if (wrappedWorldView() instanceof BonsaiWorldStateUpdateAccumulator) { - account = - ((BonsaiWorldStateUpdateAccumulator) wrappedWorldView()) - .loadAccount(address, bonsaiAccountFunction); + if (wrappedWorldView() instanceof BonsaiWorldStateUpdateAccumulator bonsaiAccumulator) { + account = bonsaiAccumulator.loadAccount(address, bonsaiAccountFunction); } else { account = wrappedWorldView().get(address); } - BonsaiAccount mutableAccount = null; - if (account instanceof BonsaiAccount) { - mutableAccount = new BonsaiAccount((BonsaiAccount) account, this, true); - accountsToUpdate.put(address, new BonsaiValue<>((BonsaiAccount) account, mutableAccount)); + + if (account instanceof BonsaiAccount bonsaiAccount) { + BonsaiAccount mutableAccount = new BonsaiAccount(bonsaiAccount, this, true); + accountsToUpdate.put(address, new BonsaiValue<>(bonsaiAccount, mutableAccount)); return mutableAccount; } else { // add the empty read in accountsToUpdate @@ -418,14 +417,13 @@ public Optional getStorageValueByStorageSlotKey( } try { final Optional valueUInt = - (wrappedWorldView() instanceof BonsaiWorldState) - ? ((BonsaiWorldState) wrappedWorldView()) - .getStorageValueByStorageSlotKey( - () -> - Optional.ofNullable(loadAccount(address, BonsaiValue::getPrior)) - .map(BonsaiAccount::getStorageRoot), - address, - storageSlotKey) + (wrappedWorldView() instanceof BonsaiWorldState bonsaiWorldState) + ? bonsaiWorldState.getStorageValueByStorageSlotKey( + () -> + Optional.ofNullable(loadAccount(address, BonsaiValue::getPrior)) + .map(BonsaiAccount::getStorageRoot), + address, + storageSlotKey) : wrappedWorldView().getStorageValueByStorageSlotKey(address, storageSlotKey); storageToUpdate .computeIfAbsent( @@ -495,53 +493,37 @@ public BonsaiWorldStateKeyValueStorage getWorldStateStorage() { } public void rollForward(final TrieLog layer) { - layer.getAccountChanges().entrySet().stream() - .forEach( - entry -> - rollAccountChange( - entry.getKey(), entry.getValue().getPrior(), entry.getValue().getUpdated())); - layer.getCodeChanges().entrySet().stream() - .forEach( - entry -> - rollCodeChange( - entry.getKey(), entry.getValue().getPrior(), entry.getValue().getUpdated())); - layer.getStorageChanges().entrySet().stream() + layer + .getAccountChanges() + .forEach((key, value1) -> rollAccountChange(key, value1.getPrior(), value1.getUpdated())); + layer + .getCodeChanges() + .forEach((key, value1) -> rollCodeChange(key, value1.getPrior(), value1.getUpdated())); + layer + .getStorageChanges() .forEach( - entry -> - entry - .getValue() - .forEach( - (storageSlotKey, value) -> - rollStorageChange( - entry.getKey(), - storageSlotKey, - value.getPrior(), - value.getUpdated()))); + (key, value1) -> + value1.forEach( + (storageSlotKey, value) -> + rollStorageChange( + key, storageSlotKey, value.getPrior(), value.getUpdated()))); } public void rollBack(final TrieLog layer) { - layer.getAccountChanges().entrySet().stream() + layer + .getAccountChanges() + .forEach((key, value1) -> rollAccountChange(key, value1.getUpdated(), value1.getPrior())); + layer + .getCodeChanges() + .forEach((key, value1) -> rollCodeChange(key, value1.getUpdated(), value1.getPrior())); + layer + .getStorageChanges() .forEach( - entry -> - rollAccountChange( - entry.getKey(), entry.getValue().getUpdated(), entry.getValue().getPrior())); - layer.getCodeChanges().entrySet().stream() - .forEach( - entry -> - rollCodeChange( - entry.getKey(), entry.getValue().getUpdated(), entry.getValue().getPrior())); - layer.getStorageChanges().entrySet().stream() - .forEach( - entry -> - entry - .getValue() - .forEach( - (storageSlotKey, value) -> - rollStorageChange( - entry.getKey(), - storageSlotKey, - value.getUpdated(), - value.getPrior()))); + (key, value1) -> + value1.forEach( + (storageSlotKey, value) -> + rollStorageChange( + key, storageSlotKey, value.getUpdated(), value.getPrior()))); } private void rollAccountChange( @@ -598,8 +580,7 @@ private BonsaiValue loadAccountFromParent( final Address address, final BonsaiValue defaultValue) { try { final Account parentAccount = wrappedWorldView().get(address); - if (parentAccount instanceof BonsaiAccount) { - final BonsaiAccount account = (BonsaiAccount) parentAccount; + if (parentAccount instanceof BonsaiAccount account) { final BonsaiValue loadedAccountValue = new BonsaiValue<>(new BonsaiAccount(account), account); accountsToUpdate.put(address, loadedAccountValue); @@ -634,7 +615,7 @@ private void rollCodeChange( } if (codeValue == null) { - if ((expectedCode == null || expectedCode.size() == 0) && replacementCode != null) { + if ((expectedCode == null || expectedCode.isEmpty()) && replacementCode != null) { codeToUpdate.put(address, new BonsaiValue<>(null, replacementCode)); } else { throw new IllegalStateException( @@ -767,16 +748,17 @@ public void reset() { codeToUpdate.clear(); accountsToUpdate.clear(); resetAccumulatorStateChanged(); - super.reset(); + updatedAccounts.clear(); + deletedAccounts.clear(); } public static class AccountConsumingMap extends ForwardingMap { - private final ConcurrentHashMap accounts; + private final ConcurrentMap accounts; private final Consumer consumer; public AccountConsumingMap( - final ConcurrentHashMap accounts, final Consumer consumer) { + final ConcurrentMap accounts, final Consumer consumer) { this.accounts = accounts; this.consumer = consumer; } @@ -801,11 +783,11 @@ public static class StorageConsumingMap extends ForwardingMap { private final Address address; - private final ConcurrentHashMap storages; + private final ConcurrentMap storages; private final Consumer consumer; public StorageConsumingMap( - final Address address, final ConcurrentHashMap storages, final Consumer consumer) { + final Address address, final ConcurrentMap storages, final Consumer consumer) { this.address = address; this.storages = storages; this.consumer = consumer; 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 b9283d50da0..72a8df3efe0 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 @@ -33,7 +33,6 @@ 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 +40,6 @@ 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; @@ -77,11 +75,10 @@ public DefaultMutableWorldState(final WorldState worldState) { // 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()); @@ -158,11 +155,10 @@ public int hashCode() { @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()); } @@ -323,14 +319,24 @@ public NavigableMap storageEntriesFrom( @Override public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("AccountState").append("{"); - builder.append("address=").append(getAddress()).append(", "); - builder.append("nonce=").append(getNonce()).append(", "); - builder.append("balance=").append(getBalance()).append(", "); - builder.append("storageRoot=").append(getStorageRoot()).append(", "); - builder.append("codeHash=").append(getCodeHash()).append(", "); - return builder.append("}").toString(); + return "AccountState" + + "{" + + "address=" + + getAddress() + + ", " + + "nonce=" + + getNonce() + + ", " + + "balance=" + + getBalance() + + ", " + + "storageRoot=" + + getStorageRoot() + + ", " + + "codeHash=" + + getCodeHash() + + ", " + + "}"; } } @@ -403,9 +409,7 @@ public void commit() { : 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) { 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 078a6e1c4c2..8078bd6ba1b 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.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 BenchmarkSubCommand(final PrintStream output) { @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/EvmTool.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmTool.java index c6bef5726cf..1cb3cfa33e4 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,13 @@ */ 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"); + System.out.println(";/"); final EvmToolCommand evmToolCommand = new EvmToolCommand(); evmToolCommand.execute(args); 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 cda40649af6..b1eef4ec547 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 @@ * * @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 void undo(final long mark) { } } + @Override + public long lastUpdate() { + return 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 1a76985b5f3..7833d267e66 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 @@ * * @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,15 @@ public void undo(final long mark) { } } + @Override + public long lastUpdate() { + return undoLog.get(undoLog.size() - 1).level; + } + + 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 00000000000..2edf58cb01d --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java @@ -0,0 +1,173 @@ +/* + * 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); + } + + 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 00000000000..bfa79a9ec89 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoScalar.java @@ -0,0 +1,94 @@ +/* + * 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; + +public class UndoScalar implements Undoable { + record UndoEntry(T value, long level) { + UndoEntry(final T value) { + this(value, Undoable.incrementMarkStatic()); + } + } + + T value; + final List> undoLog; + + public static UndoScalar of(final T value) { + return new UndoScalar<>(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; + } + + public boolean updated() { + return !undoLog.isEmpty(); + } + + public T get() { + return 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 1ba509d3094..a1ff6fd0ca7 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 @@ * * @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 void undo(final long mark) { } } + @Override + public long lastUpdate() { + return 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 26af3225f47..abe71c83695 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 @@ * * @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 void undo(final long mark) { } } + @Override + public long lastUpdate() { + return 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 25bfaa8ee50..12b1e3dbb97 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 @@ * 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 @@ static long incrementMarkStatic() { return markState.incrementAndGet(); } + /** + * The last time this object was updated. Any undo requrests greater than this mark will result in + * no chantes. + * + * @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/precompile/BigIntegerModularExponentiationPrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java index 669edc78103..9f33b45b2ff 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java @@ -100,21 +100,6 @@ public long gasRequirement(final Bytes input) { @Override public PrecompileContractResult computePrecompile( final Bytes input, @Nonnull final MessageFrame messageFrame) { - if (useNative) { - return computeNative(input); - } else { - return computeDefault(input); - } - } - - /** - * Compute default precompile contract. - * - * @param input the input - * @return the precompile contract result - */ - @Nonnull - public PrecompileContractResult computeDefault(final Bytes input) { final int baseLength = clampedToInt(baseLength(input)); final int exponentLength = clampedToInt(exponentLength(input)); final int modulusLength = clampedToInt(modulusLength(input)); @@ -130,6 +115,27 @@ public PrecompileContractResult computeDefault(final Bytes input) { final BigInteger exp = extractParameter(input, exponentOffset, exponentLength); final BigInteger mod = extractParameter(input, modulusOffset, modulusLength); + if (useNative && mod.getLowestSetBit() > 0) { + return computeNative(input, modulusLength); + } else { + return computeDefault(input, base, exp, mod, modulusLength); + } + } + + /** + * Compute default precompile contract. + * + * @param input the input + * @return the precompile contract result + */ + @Nonnull + public PrecompileContractResult computeDefault( + final Bytes input, + final BigInteger base, + final BigInteger exp, + final BigInteger mod, + final int modulusLength) { + final Bytes modExp; // Result must be the length of the modulus. final MutableBytes result = MutableBytes.create(modulusLength); @@ -241,10 +247,11 @@ private static long square(final long n) { * Compute native precompile contract. * * @param input the input + * @param modulusLength length, in bytes, of the modulus * @return the precompile contract result */ - public PrecompileContractResult computeNative(final @Nonnull Bytes input) { - final int modulusLength = clampedToInt(modulusLength(input)); + public PrecompileContractResult computeNative( + final @Nonnull Bytes input, final int modulusLength) { final IntByReference o_len = new IntByReference(modulusLength); final byte[] result = new byte[modulusLength]; 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 fde3aa8c244..4d0c569832f 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 @@ -136,7 +136,8 @@ public void deleteAccount(final Address address) { */ @Override public WorldUpdater updater() { - return new StackedUpdater<>(this); + return new JournaledUpdater<>(this); + // return new StackedUpdater<>(this); } /** @@ -150,8 +151,8 @@ protected W wrappedWorldView() { @Override public Optional parentUpdater() { - if (world instanceof WorldUpdater) { - return Optional.of((WorldUpdater) world); + if (world instanceof WorldUpdater worldUpdater) { + return Optional.of(worldUpdater); } else { return Optional.empty(); } 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 00000000000..3f7b6186801 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java @@ -0,0 +1,362 @@ +/* + * 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.EvmAccount; +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, EvmAccount, Undoable { + private final Address address; + private final Hash addressHash; + + @Nullable private EvmAccount 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; + + /** + * 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 EvmAccount 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 EvmAccount getWrappedAccount() { + return account; + } + + /** + * Sets wrapped account. + * + * @param account the account + */ + public void setWrappedAccount(final EvmAccount 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 Address getAddress() { + return address; + } + + @Override + public Hash getAddressHash() { + return addressHash; + } + + @Override + public long getNonce() { + return nonce.get(); + } + + @Override + public void setNonce(final long value) { + nonce.set(value); + } + + @Override + public Wei getBalance() { + return balance.get(); + } + + @Override + public void setBalance(final Wei value) { + 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(); + } + + public void setDeleted(final boolean accountDeleted) { + deleted.set(accountDeleted); + } + + public Boolean getDeleted() { + return deleted.get(); + } + + @Override + public void setCode(final Bytes code) { + 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) { + return account.getOriginalStorageValue(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) { + updatedStorage.put(key, value); + } + + @Override + public void clearStorage() { + 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) { + 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 MutableAccount getMutable() throws ModificationNotAllowedException { + return this; + } + + @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); + } + + public void commit() { + MutableAccount mutableAccount = account.getMutable(); + if (!(mutableAccount instanceof JournaledAccount)) { + if (nonce.updated()) { + mutableAccount.setNonce(nonce.get()); + } + if (balance.updated()) { + mutableAccount.setBalance(balance.get()); + } + if (code.updated()) { + mutableAccount.setCode(code.get() == null ? Bytes.EMPTY : code.get()); + } + if (updatedStorage.updated()) { + updatedStorage.forEach(mutableAccount::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 00000000000..20f68e12fb5 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java @@ -0,0 +1,172 @@ +/* + * 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.EvmAccount; +import org.hyperledger.besu.evm.account.MutableAccount; + +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 WorldUpdater parentWorld; + final AbstractWorldUpdater rootWorld; + final UndoMap accounts; + final UndoSet

deleted; + final long undoMark; + + /** + * Instantiates a new Stacked updater. + * + * @param world the world + */ + @SuppressWarnings("unchecked") + public JournaledUpdater(final WorldUpdater world) { + parentWorld = world; + 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(); + } + + protected EvmAccount getForMutation(final Address address) { + final JournaledAccount wrappedTracker = accounts.get(address); + if (wrappedTracker != null) { + return wrappedTracker.getWrappedAccount(); + } + final EvmAccount 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); + } + + 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 EvmAccount 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 EvmAccount 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 EvmAccount 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); + } +} 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 348f76dfa10..3d585151c56 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 @@ -109,6 +109,7 @@ public void commit() { } /** Mark transaction boundary. */ + @Override public void markTransactionBoundary() { getUpdatedAccounts().forEach(UpdateTrackingAccount::markTransactionBoundary); } 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 a9b012facbf..61ec77ccfd2 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 @@ -39,7 +39,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). @@ -152,4 +152,9 @@ default void clearAccountsThatAreEmpty() { new ArrayList<>(getTouchedAccounts()) .stream().filter(Account::isEmpty).forEach(a -> deleteAccount(a.getAddress())); } + + /** Mark transaction boundary. */ + default void markTransactionBoundary() { + // default is to ignore + } } From 2aa8bc43d1ce14afb47a96559dbce7da4ab42814 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 21 Sep 2023 09:31:52 -0600 Subject: [PATCH 02/10] spotless Signed-off-by: Danno Ferrin --- build.gradle | 7 +++++++ .../internal/results/tracing/diff/StateDiffGenerator.java | 8 +++----- .../main/java/org/hyperledger/besu/evmtool/EvmTool.java | 1 - .../java/org/hyperledger/besu/evmtool/EvmToolCommand.java | 4 ++-- .../hyperledger/besu/evm/worldstate/JournaledAccount.java | 7 +++++-- .../java/org/hyperledger/besu/evm/toy/ToyAccount.java | 4 ++-- 6 files changed, 19 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 8ce41eb46f1..f594436b890 100644 --- a/build.gradle +++ b/build.gradle @@ -213,6 +213,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/results/tracing/diff/StateDiffGenerator.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/diff/StateDiffGenerator.java index e8b975e11b8..af9b438c8eb 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.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.worldstate.JournaledAccount; 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 Stream generateStateDiff(final TransactionTrace transactionTrace) // calculate storage diff final Map storageDiff = new TreeMap<>(); for (final Map.Entry entry : - ((UpdateTrackingAccount) updatedAccount) - .getUpdatedStorage() - .entrySet()) { // FIXME cast + ((JournaledAccount) updatedAccount).getUpdatedStorage().entrySet()) { // FIXME cast final UInt256 newValue = entry.getValue(); if (rootAccount == null) { if (!UInt256.ZERO.equals(newValue)) { 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 1cb3cfa33e4..00582c9503d 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 @@ -21,7 +21,6 @@ public final class EvmTool { public static void main(final String... args) { LogConfigurator.setLevel("", "DEBUG"); - System.out.println(";/"); 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 d41dd56093b..898bfa178f4 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 @@ -466,10 +466,10 @@ public static void dumpWorldState(final WorldState worldState, final PrintWriter " \"" + accountStorageEntry .getKey() - .map(UInt256::toHexString) + .map(UInt256::toQuantityHexString) .orElse("-") + "\": \"" - + accountStorageEntry.getValue().toHexString() + + accountStorageEntry.getValue().toQuantityHexString() + "\"") .toList())); out.println(" },"); 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 index 8c004db72e8..ff02e0d421e 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java @@ -27,7 +27,6 @@ import org.hyperledger.besu.evm.ModificationNotAllowedException; import org.hyperledger.besu.evm.account.AccountStorageEntry; import org.hyperledger.besu.evm.account.MutableAccount; -import org.hyperledger.besu.evm.account.MutableAccount; import java.util.Map; import java.util.NavigableMap; @@ -269,7 +268,11 @@ public UInt256 getStorageValue(final UInt256 key) { @Override public UInt256 getOriginalStorageValue(final UInt256 key) { - return account.getOriginalStorageValue(key); + if (storageWasCleared) { + return getStorageValue(key); + } else { + return account.getOriginalStorageValue(key); + } } @Override 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 9ddac0e0dd3..64e2b7ae07e 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 = @@ -166,6 +166,6 @@ public Map getUpdatedStorage() { @Override public void becomeImmutable() { - mutable = false; + immutable = true; } } From b430ee9f538f14206cb4e18cd503a419edc3fa25 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 25 Sep 2023 15:12:28 -0500 Subject: [PATCH 03/10] javadoc Signed-off-by: Danno Ferrin --- .../besu/collections/undo/UndoMap.java | 5 +++ .../collections/undo/UndoNavigableMap.java | 7 ++++ .../besu/collections/undo/UndoScalar.java | 32 +++++++++++++++++++ .../besu/evm/fluent/SimpleAccount.java | 16 ++++++++++ .../besu/evm/fluent/SimpleWorld.java | 30 ++++++++++++++--- ...ularExponentiationPrecompiledContract.java | 4 +++ .../besu/evm/worldstate/JournaledAccount.java | 9 ++++++ .../besu/evm/worldstate/JournaledUpdater.java | 9 ++++++ 8 files changed, 107 insertions(+), 5 deletions(-) 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 7833d267e66..e68f1ba31a5 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 @@ -75,6 +75,11 @@ public long lastUpdate() { return 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(); } 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 index 2edf58cb01d..f9c54c42412 100644 --- a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java @@ -41,6 +41,13 @@ 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); } 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 index bfa79a9ec89..0e3356c379b 100644 --- a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoScalar.java +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoScalar.java @@ -19,6 +19,11 @@ 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) { @@ -29,10 +34,22 @@ record UndoEntry(T value, long level) { 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; @@ -43,14 +60,29 @@ 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)); 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 403c72d4b2c..755523e6b49 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 @@ -188,4 +188,20 @@ public Map getUpdatedStorage() { public void becomeImmutable() { 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/fluent/SimpleWorld.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java index 6bc19ca0dae..404edf17025 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java @@ -66,6 +66,9 @@ public Account get(final Address address) { @Override public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) { + if (getAccount(address) != null) { + throw new IllegalStateException("Cannot create an account when one already exists"); + } SimpleAccount account = new SimpleAccount(address, nonce, balance); accounts.put(address, account); return account; @@ -75,11 +78,23 @@ public MutableAccount createAccount(final Address address, final long nonce, fin public MutableAccount getAccount(final Address address) { if (accounts.containsKey(address)) { return accounts.get(address); - } else if (parent != null) { - return parent.getAccount(address); - } else { + } + if (parent == null) { return null; } + Account parentAccount = parent.getAccount(address); + if (parentAccount == null) { + return null; + } + SimpleAccount account = + new SimpleAccount( + parentAccount, + parentAccount.getAddress(), + parentAccount.getNonce(), + parentAccount.getBalance(), + parentAccount.getCode()); + accounts.put(address, account); + return account; } @Override @@ -107,11 +122,16 @@ public void revert() { @Override public void commit() { - parent.accounts.putAll(accounts); + accounts.forEach( + (address, account) -> { + if (!account.commit()) { + accounts.put(address, account); + } + }); } @Override public Optional parentUpdater() { - return Optional.empty(); + return Optional.ofNullable(parent); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java index 9f33b45b2ff..42e3279c727 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java @@ -126,6 +126,10 @@ public PrecompileContractResult computePrecompile( * Compute default precompile contract. * * @param input the input + * @param base base of the exponent + * @param exp the exponent + * @param mod the modulus + * @param modulusLength the length of the modulus, in bytes * @return the precompile contract result */ @Nonnull 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 index ff02e0d421e..9d2bd060d41 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java @@ -226,6 +226,10 @@ 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(); @@ -233,6 +237,10 @@ public void setDeleted(final boolean accountDeleted) { deleted.set(accountDeleted); } + /** + * Is the account marked as deleted? + * @return is the account deleted? + */ public Boolean getDeleted() { return deleted.get(); } @@ -368,6 +376,7 @@ public void undo(final long 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()) { 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 index 59f81faa1d1..85acf5ed204 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java @@ -64,6 +64,12 @@ public JournaledUpdater(final WorldUpdater world) { 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) { @@ -83,6 +89,9 @@ 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); From 01b793f225a35edaeb07c8a4d06a9da489e43250 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 11 Oct 2023 15:12:43 -0600 Subject: [PATCH 04/10] Wire in stacked vs journaled updater Add an EvmConfiguration option for stacked vs journaled updater, and wire it in where needed. This is high touch as it's dependency injection, but fairly simple code. Signed-off-by: Danno Ferrin --- .../besu/cli/options/unstable/EvmOptions.java | 19 +++- .../controller/BesuControllerBuilder.java | 94 +++++++++------- .../internal/processor/TransactionTracer.java | 5 +- .../tracing/diff/StateDiffGenerator.java | 4 +- .../worldstate/PrunerIntegrationTest.java | 7 +- .../bonsai/BonsaiWorldStateProvider.java | 20 ++-- .../cache/CachedWorldStorageManager.java | 20 +++- .../bonsai/worldview/BonsaiWorldState.java | 16 ++- .../BonsaiWorldStateUpdateAccumulator.java | 10 +- .../besu/ethereum/chain/GenesisState.java | 3 +- .../besu/ethereum/core/PrivacyParameters.java | 4 +- .../worldstate/DefaultMutableWorldState.java | 47 ++++---- .../worldstate/DefaultWorldStateArchive.java | 11 +- .../core/InMemoryKeyValueStorageProvider.java | 10 +- .../core/InMemoryPrivacyStorageProvider.java | 12 +- .../BlockImportExceptionHandlingTest.java | 23 ++-- .../bonsai/AbstractIsolationTests.java | 4 +- .../bonsai/BonsaiWorldStateArchiveTest.java | 63 ++++++----- .../besu/ethereum/bonsai/LogRollingTests.java | 32 ++++-- .../besu/ethereum/bonsai/RollingImport.java | 12 +- .../bonsai/trielog/TrieLogManagerTests.java | 12 +- .../DefaultMutableWorldStateTest.java | 6 +- .../worldstate/MarkSweepPrunerTest.java | 17 +-- .../FastWorldStateDownloaderTest.java | 103 ++++++++++-------- .../besu/evmtool/BlockchainModule.java | 10 +- .../evmtool/EvmToolCommandOptionsModule.java | 31 ++++++ .../besu/evmtool/GenesisFileModule.java | 4 +- .../evmtool/MainnetGenesisFileModule.java | 5 +- .../besu/evmtool/T8nServerSubCommand.java | 99 ++++++++++------- .../besu/evmtool/T8nSubCommand.java | 7 +- .../BonsaiReferenceTestUpdateAccumulator.java | 6 +- .../BonsaiReferenceTestWorldState.java | 34 ++++-- .../DefaultReferenceTestWorldState.java | 6 +- .../ReferenceTestWorldState.java | 9 +- .../vm/BlockchainReferenceTestTools.java | 13 ++- .../vm/GeneralStateReferenceTestTools.java | 26 +++-- .../ethereum/retesteth/RetestethContext.java | 3 +- .../besu/collections/undo/UndoMap.java | 2 +- .../collections/undo/UndoNavigableMap.java | 1 + .../besu/evm/internal/EvmConfiguration.java | 34 +++--- .../evm/worldstate/AbstractWorldUpdater.java | 15 ++- .../besu/evm/worldstate/JournaledAccount.java | 2 + .../besu/evm/worldstate/JournaledUpdater.java | 8 +- .../besu/evm/worldstate/StackedUpdater.java | 7 +- 44 files changed, 566 insertions(+), 310 deletions(-) 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 7e787cf83b8..f91fcc880f7 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 @@ 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 static EvmOptions create() { 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 d023cdd76da..bea18e90eaa 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -129,60 +129,83 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides /** The Sync config. */ protected SynchronizerConfiguration syncConfig; + /** The Ethereum wire protocol configuration. */ protected EthProtocolConfiguration ethereumWireProtocolConfiguration; + /** The Transaction pool configuration. */ protected TransactionPoolConfiguration transactionPoolConfiguration; + /** The Network id. */ protected BigInteger networkId; + /** The Mining parameters. */ protected MiningParameters miningParameters; + /** The Metrics system. */ protected ObservableMetricsSystem metricsSystem; + /** The Privacy parameters. */ protected PrivacyParameters privacyParameters; + /** The Pki block creation configuration. */ protected Optional pkiBlockCreationConfiguration = Optional.empty(); + /** The Data directory. */ protected Path dataDirectory; + /** The Clock. */ protected Clock clock; + /** The Node key. */ protected NodeKey nodeKey; + /** The Is revert reason enabled. */ protected boolean isRevertReasonEnabled; + /** The Gas limit calculator. */ GasLimitCalculator gasLimitCalculator; + /** The Storage provider. */ protected StorageProvider storageProvider; + /** The Is pruning enabled. */ protected boolean isPruningEnabled; + /** The Pruner configuration. */ protected PrunerConfiguration prunerConfiguration; + /** The Required blocks. */ protected Map requiredBlocks = Collections.emptyMap(); + /** The Reorg logging threshold. */ protected long reorgLoggingThreshold; + /** The Data storage configuration. */ protected DataStorageConfiguration dataStorageConfiguration = DataStorageConfiguration.DEFAULT_CONFIG; + /** The Message permissioning providers. */ protected List messagePermissioningProviders = Collections.emptyList(); + /** The Evm configuration. */ protected EvmConfiguration evmConfiguration; + /** The Max peers. */ protected int maxPeers; private int peerLowerBound; private int maxRemotelyInitiatedPeers; + /** The Chain pruner configuration. */ protected ChainPrunerConfiguration chainPrunerConfiguration = ChainPrunerConfiguration.DEFAULT; private NetworkingConfiguration networkingConfiguration; private Boolean randomPeerPriority; private Optional transactionSelectorFactory = Optional.empty(); + /** the Dagger configured context that can provide dependencies */ protected Optional besuComponent = Optional.empty(); @@ -618,10 +641,7 @@ public BesuController build() { 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( @@ -762,7 +782,6 @@ public BesuController build() { final SubProtocolConfiguration subProtocolConfiguration = createSubProtocolConfiguration(ethProtocolManager, maybeSnapProtocolManager); - ; final JsonRpcMethods additionalJsonRpcMethodFactory = createAdditionalJsonRpcMethodFactory(protocolContext); @@ -817,24 +836,21 @@ protected Synchronizer createSynchronizer( 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( @@ -916,9 +932,8 @@ protected SubProtocolConfiguration createSubProtocolConfiguration( 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; } @@ -1057,22 +1072,21 @@ WorldStateArchive createWorldStateArchive( 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); + default -> { 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/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 a9a786382c0..5b9cdfc2fcc 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,8 +33,6 @@ 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.AbstractWorldUpdater; -import org.hyperledger.besu.evm.worldstate.StackedUpdater; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.io.File; @@ -113,8 +111,7 @@ public List traceTransactionToFile( .performActionWithBlock( blockHash, (body, header, blockchain, transactionProcessor, protocolSpec) -> { - WorldUpdater stackedUpdater = - new StackedUpdater<>((AbstractWorldUpdater) mutableWorldState).updater(); + WorldUpdater stackedUpdater = mutableWorldState.updater().updater(); final List traces = new ArrayList<>(); final Wei blobGasPrice = protocolSpec 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 af9b438c8eb..3212d92403c 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.TracingUtils; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.worldstate.JournaledAccount; +import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.Collections; @@ -60,7 +60,7 @@ public Stream generateStateDiff(final TransactionTrace transactionTrace) // calculate storage diff final Map storageDiff = new TreeMap<>(); for (final Map.Entry entry : - ((JournaledAccount) updatedAccount).getUpdatedStorage().entrySet()) { // FIXME cast + ((MutableAccount) updatedAccount).getUpdatedStorage().entrySet()) { // FIXME cast 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 86f2694c3c0..77f871f1002 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.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 @@ private Set collectWorldStateNodes(final Hash stateRootHash, final Set 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/BonsaiWorldStateProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateProvider.java index 299b826369c..7b651a11fa1 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 @@ -38,6 +38,7 @@ import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat; 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 BonsaiWorldStateProvider( final Blockchain blockchain, final CachedMerkleTrieLoader cachedMerkleTrieLoader, final ObservableMetricsSystem metricsSystem, - final BesuContext pluginContext) { + final BesuContext pluginContext, + final EvmConfiguration evmConfiguration) { this( (BonsaiWorldStateKeyValueStorage) provider.createWorldStateStorage(DataStorageFormat.BONSAI), @@ -80,7 +82,8 @@ public BonsaiWorldStateProvider( Optional.empty(), cachedMerkleTrieLoader, metricsSystem, - pluginContext); + pluginContext, + evmConfiguration); } public BonsaiWorldStateProvider( @@ -89,7 +92,8 @@ public BonsaiWorldStateProvider( final Optional maxLayersToLoad, final CachedMerkleTrieLoader cachedMerkleTrieLoader, final ObservableMetricsSystem metricsSystem, - final BesuContext pluginContext) { + final BesuContext pluginContext, + final EvmConfiguration evmConfiguration) { // TODO: de-dup constructors this.trieLogManager = @@ -99,11 +103,12 @@ public BonsaiWorldStateProvider( worldStateStorage, metricsSystem, maxLayersToLoad.orElse(RETAINED_LAYERS), - pluginContext); + pluginContext, + evmConfiguration); 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( @@ -117,11 +122,12 @@ public BonsaiWorldStateProvider( final TrieLogManager trieLogManager, final BonsaiWorldStateKeyValueStorage worldStateStorage, final Blockchain blockchain, - final CachedMerkleTrieLoader cachedMerkleTrieLoader) { + final CachedMerkleTrieLoader cachedMerkleTrieLoader, + final EvmConfiguration evmConfiguration) { 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 d0f1be97d89..580b83604ac 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 @@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldState; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.metrics.ObservableMetricsSystem; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.services.TrieLogService; @@ -52,6 +53,7 @@ public class CachedWorldStorageManager extends AbstractTrieLogManager private static final Logger LOG = LoggerFactory.getLogger(CachedWorldStorageManager.class); private final BonsaiWorldStateProvider archive; private final ObservableMetricsSystem metricsSystem; + private final EvmConfiguration evmConfiguration; CachedWorldStorageManager( final BonsaiWorldStateProvider archive, @@ -60,11 +62,13 @@ public class CachedWorldStorageManager extends AbstractTrieLogManager final long maxLayersToLoad, final Map cachedWorldStatesByHash, final BesuContext pluginContext, - final ObservableMetricsSystem metricsSystem) { + final ObservableMetricsSystem metricsSystem, + final EvmConfiguration evmConfiguration) { super(blockchain, worldStateStorage, maxLayersToLoad, cachedWorldStatesByHash, pluginContext); worldStateStorage.subscribe(this); this.archive = archive; this.metricsSystem = metricsSystem; + this.evmConfiguration = evmConfiguration; } public CachedWorldStorageManager( @@ -73,7 +77,8 @@ public CachedWorldStorageManager( final BonsaiWorldStateKeyValueStorage worldStateStorage, final ObservableMetricsSystem metricsSystem, final long maxLayersToLoad, - final BesuContext pluginContext) { + final BesuContext pluginContext, + final EvmConfiguration evmConfiguration) { this( archive, blockchain, @@ -81,7 +86,8 @@ public CachedWorldStorageManager( maxLayersToLoad, new ConcurrentHashMap<>(), pluginContext, - metricsSystem); + metricsSystem, + evmConfiguration); } @Override @@ -140,7 +146,9 @@ public Optional getWorldState(final Hash blockHash) { .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 {}") @@ -180,7 +188,7 @@ public Optional getNearestWorldState(final BlockHeader blockHe .map( storage -> new BonsaiWorldState( // wrap the state in a layered worldstate - archive, new BonsaiWorldStateLayerStorage(storage))); + archive, new BonsaiWorldStateLayerStorage(storage), evmConfiguration)); } @Override @@ -198,7 +206,7 @@ public Optional getHeadWorldState( 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 ed81c90f439..6bc27e0cd63 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 @@ -42,6 +42,7 @@ 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; @@ -78,14 +79,20 @@ public class BonsaiWorldState public BonsaiWorldState( final BonsaiWorldStateProvider archive, - final BonsaiWorldStateKeyValueStorage worldStateStorage) { - this(worldStateStorage, archive.getCachedMerkleTrieLoader(), archive.getTrieLogManager()); + final BonsaiWorldStateKeyValueStorage worldStateStorage, + final EvmConfiguration evmConfiguration) { + this( + worldStateStorage, + archive.getCachedMerkleTrieLoader(), + archive.getTrieLogManager(), + evmConfiguration); } protected BonsaiWorldState( final BonsaiWorldStateKeyValueStorage worldStateStorage, final CachedMerkleTrieLoader cachedMerkleTrieLoader, - final TrieLogManager trieLogManager) { + final TrieLogManager trieLogManager, + final EvmConfiguration evmConfiguration) { this.worldStateStorage = worldStateStorage; this.worldStateRootHash = Hash.wrap( @@ -99,7 +106,8 @@ protected BonsaiWorldState( cachedMerkleTrieLoader.preLoadAccount( getWorldStateStorage(), worldStateRootHash, addr), (addr, value) -> - cachedMerkleTrieLoader.preLoadStorageSlot(getWorldStateStorage(), addr, value)); + cachedMerkleTrieLoader.preLoadStorageSlot(getWorldStateStorage(), addr, value), + evmConfiguration); this.cachedMerkleTrieLoader = cachedMerkleTrieLoader; 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 74299b129a9..f66fcd0f56f 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.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; } 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 f6c969d58b2..a69e920dd20 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.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 @@ private static Hash calculateGenesisStateHash(final List genesis 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 43bdff8aafa..1d4cfdf6c1b 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.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 PrivacyParameters build() { 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 72a8df3efe0..f6fd40ecdf0 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.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; @@ -48,6 +49,7 @@ public class DefaultMutableWorldState implements MutableWorldState { + private final EvmConfiguration evmConfiguration; private final WorldStateStorage worldStateStorage; private final WorldStatePreimageStorage preimageStorage; @@ -58,20 +60,25 @@ 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. @@ -82,6 +89,7 @@ public DefaultMutableWorldState(final WorldState worldState) { this.worldStateStorage = other.worldStateStorage; this.preimageStorage = other.preimageStorage; this.accountStateTrie = newAccountStateTrie(other.accountStateTrie.getRootHash()); + this.evmConfiguration = evmConfiguration; } private MerkleTrie newAccountStateTrie(final Bytes32 rootHash) { @@ -123,16 +131,9 @@ private WorldStateAccount deserializeAccount( 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 @@ -193,11 +194,6 @@ public void persist(final BlockHeader blockHeader) { 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. @@ -338,13 +334,19 @@ public String 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 @@ -435,5 +437,12 @@ public void commit() { 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 b1955561cc5..a29bd9b2ff4 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.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 Optional getMutable(final Hash rootHash, final Hash bl 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 3687643c6d9..f781e82e46e 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 @@ -31,6 +31,7 @@ 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; @@ -76,7 +77,8 @@ public static MutableBlockchain createInMemoryBlockchain( 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( @@ -90,14 +92,16 @@ public static BonsaiWorldStateProvider createBonsaiInMemoryWorldStateArchive( blockchain, cachedMerkleTrieLoader, new NoOpMetricsSystem(), - null); + null, + EvmConfiguration.DEFAULT); } 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 7a76d88d64c..91e115cffab 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.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 int getFactoryVersion() { } @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 b936ed28fa3..7cf01dbf2b4 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 @@ -47,6 +47,7 @@ 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; @@ -56,7 +57,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class BlockImportExceptionHandlingTest { +class BlockImportExceptionHandlingTest { private final MainnetTransactionProcessor transactionProcessor = mock(MainnetTransactionProcessor.class); @@ -89,13 +90,19 @@ public class BlockImportExceptionHandlingTest { // do we need to also test with a DefaultWorldStateArchive? spy( new BonsaiWorldStateProvider( - storageProvider, blockchain, cachedMerkleTrieLoader, new NoOpMetricsSystem(), null)); + storageProvider, + blockchain, + cachedMerkleTrieLoader, + new NoOpMetricsSystem(), + null, + EvmConfiguration.DEFAULT)); private final BonsaiWorldState persisted = spy( new BonsaiWorldState( (BonsaiWorldStateProvider) worldStateArchive, - (BonsaiWorldStateKeyValueStorage) worldStateStorage)); + (BonsaiWorldStateKeyValueStorage) worldStateStorage, + EvmConfiguration.DEFAULT)); private final BadBlockManager badBlockManager = new BadBlockManager(); @@ -113,7 +120,7 @@ public void setup() { } @Test - public void shouldNotBadBlockWhenInternalErrorDuringPersisting() { + void shouldNotBadBlockWhenInternalErrorDuringPersisting() { Mockito.doThrow(new StorageException("database problem")).when(persisted).persist(any()); Mockito.doReturn(persisted).when(worldStateArchive).getMutable(); @@ -153,7 +160,7 @@ public void shouldNotBadBlockWhenInternalErrorDuringPersisting() { } @Test - public void shouldNotBadBlockWhenInternalErrorOnBlockLookup() { + void shouldNotBadBlockWhenInternalErrorOnBlockLookup() { Block goodBlock = new BlockDataGenerator() @@ -179,7 +186,7 @@ public void shouldNotBadBlockWhenInternalErrorOnBlockLookup() { any(), eq(HeaderValidationMode.DETACHED_ONLY))) .thenReturn(true); - assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(0); + assertThat(badBlockManager.getBadBlocks()).isEmpty(); mainnetBlockValidator.validateAndProcessBlock( protocolContext, goodBlock, @@ -189,7 +196,7 @@ public void shouldNotBadBlockWhenInternalErrorOnBlockLookup() { } @Test - public void shouldNotBadBlockWhenInternalErrorDuringValidateHeader() { + void shouldNotBadBlockWhenInternalErrorDuringValidateHeader() { Block goodBlock = new BlockDataGenerator() @@ -218,7 +225,7 @@ public void shouldNotBadBlockWhenInternalErrorDuringValidateHeader() { } @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 da12289afa8..1cfb52b1968 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 @@ -64,6 +64,7 @@ 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; @@ -147,7 +148,8 @@ public void createStorage() { 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 b492b67ffdb..84a776ac5f9 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 @@ -42,6 +42,7 @@ 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; @@ -62,7 +63,7 @@ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -public class BonsaiWorldStateArchiveTest { +class BonsaiWorldStateArchiveTest { final BlockHeaderTestFixture blockBuilder = new BlockHeaderTestFixture(); @Mock Blockchain blockchain; @@ -87,7 +88,7 @@ public void setUp() { } @Test - public void testGetMutableReturnPersistedStateWhenNeeded() { + void testGetMutableReturnPersistedStateWhenNeeded() { final BlockHeader chainHead = blockBuilder.number(0).buildHeader(); when(segmentedKeyValueStorage.get(TRIE_BRANCH_STORAGE, WORLD_ROOT_HASH_KEY)) @@ -103,14 +104,15 @@ public void testGetMutableReturnPersistedStateWhenNeeded() { 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()), @@ -118,7 +120,8 @@ public void testGetMutableReturnEmptyWhenLoadMoreThanLimitLayersBack() { 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); @@ -127,14 +130,15 @@ public void testGetMutableReturnEmptyWhenLoadMoreThanLimitLayersBack() { } @Test - public void testGetMutableWhenLoadLessThanLimitLayersBack() { + void testGetMutableWhenLoadLessThanLimitLayersBack() { bonsaiWorldStateArchive = new BonsaiWorldStateProvider( 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); @@ -149,9 +153,8 @@ public void testGetMutableWhenLoadLessThanLimitLayersBack() { .containsInstanceOf(BonsaiWorldState.class); } - @SuppressWarnings({"unchecked", "rawtypes"}) @Test - public void testGetMutableWithStorageInconsistencyRollbackTheState() { + void testGetMutableWithStorageInconsistencyRollbackTheState() { doAnswer(__ -> Optional.of(mock(TrieLogLayer.class))) .when(trieLogManager) @@ -165,10 +168,11 @@ public void testGetMutableWithStorageInconsistencyRollbackTheState() { 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); @@ -179,7 +183,7 @@ public void testGetMutableWithStorageInconsistencyRollbackTheState() { // @SuppressWarnings({"unchecked", "rawtypes"}) @Test - public void testGetMutableWithStorageConsistencyNotRollbackTheState() { + void testGetMutableWithStorageConsistencyNotRollbackTheState() { var worldStateStorage = new BonsaiWorldStateKeyValueStorage(storageProvider, new NoOpMetricsSystem()); @@ -189,12 +193,13 @@ public void testGetMutableWithStorageConsistencyNotRollbackTheState() { 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); @@ -203,9 +208,8 @@ public void testGetMutableWithStorageConsistencyNotRollbackTheState() { 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(); @@ -225,27 +229,27 @@ public void testGetMutableWithStorageConsistencyToRollbackAndRollForwardTheState 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(); @@ -264,10 +268,11 @@ public void testGetMutableWithRollbackNotOverrideTrieLogLayer() { 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(); @@ -276,9 +281,9 @@ public void testGetMutableWithRollbackNotOverrideTrieLogLayer() { 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 0e2fb0c0d1a..435e856362f 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 @@ -36,6 +36,7 @@ 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; @@ -132,7 +133,12 @@ void createStorage() { new CachedMerkleTrieLoader(new NoOpMetricsSystem()); archive = new BonsaiWorldStateProvider( - provider, blockchain, cachedMerkleTrieLoader, new NoOpMetricsSystem(), null); + provider, + blockchain, + cachedMerkleTrieLoader, + new NoOpMetricsSystem(), + null, + EvmConfiguration.DEFAULT); accountStorage = provider.getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE); codeStorage = provider.getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.CODE_STORAGE); @@ -152,7 +158,8 @@ void createStorage() { blockchain, secondOptimizedMerkleTrieLoader, new NoOpMetricsSystem(), - null); + null, + EvmConfiguration.DEFAULT); secondAccountStorage = secondProvider.getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE); secondCodeStorage = @@ -171,7 +178,9 @@ void simpleRollForwardTest() { 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)); @@ -183,7 +192,8 @@ void simpleRollForwardTest() { final BonsaiWorldState secondWorldState = new BonsaiWorldState( secondArchive, - new BonsaiWorldStateKeyValueStorage(secondProvider, new NoOpMetricsSystem())); + new BonsaiWorldStateKeyValueStorage(secondProvider, new NoOpMetricsSystem()), + EvmConfiguration.DEFAULT); final BonsaiWorldStateUpdateAccumulator secondUpdater = (BonsaiWorldStateUpdateAccumulator) secondWorldState.updater(); @@ -212,7 +222,9 @@ void simpleRollForwardTest() { 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)); @@ -232,7 +244,8 @@ void rollForwardTwice() { final BonsaiWorldState secondWorldState = new BonsaiWorldState( secondArchive, - new BonsaiWorldStateKeyValueStorage(secondProvider, new NoOpMetricsSystem())); + new BonsaiWorldStateKeyValueStorage(secondProvider, new NoOpMetricsSystem()), + EvmConfiguration.DEFAULT); final BonsaiWorldStateUpdateAccumulator secondUpdater = (BonsaiWorldStateUpdateAccumulator) secondWorldState.updater(); @@ -262,7 +275,9 @@ void rollForwardTwice() { 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)); @@ -289,7 +304,8 @@ void rollBackOnce() { 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 46e5b6af9f8..c88aec57675 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 @@ -31,6 +31,7 @@ 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; @@ -55,10 +56,17 @@ public static void main(final String[] arg) throws IOException { new CachedMerkleTrieLoader(new NoOpMetricsSystem()); final BonsaiWorldStateProvider archive = new BonsaiWorldStateProvider( - provider, null, cachedMerkleTrieLoader, new NoOpMetricsSystem(), null); + provider, + null, + cachedMerkleTrieLoader, + new NoOpMetricsSystem(), + null, + EvmConfiguration.DEFAULT); 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 35ce1b77c06..5d4f7134409 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 @@ -27,6 +27,7 @@ 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 org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.util.concurrent.atomic.AtomicBoolean; @@ -39,7 +40,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class TrieLogManagerTests { +class TrieLogManagerTests { BlockHeader blockHeader = new BlockHeaderTestFixture().buildHeader(); @@ -51,7 +52,9 @@ public class TrieLogManagerTests { @Mock BonsaiWorldStateProvider archive; @Mock Blockchain blockchain; BonsaiWorldStateUpdateAccumulator bonsaiUpdater = - spy(new BonsaiWorldStateUpdateAccumulator(worldState, (__, ___) -> {}, (__, ___) -> {})); + spy( + new BonsaiWorldStateUpdateAccumulator( + worldState, (__, ___) -> {}, (__, ___) -> {}, EvmConfiguration.DEFAULT)); TrieLogManager trieLogManager; @@ -64,11 +67,12 @@ public void setup() { bonsaiWorldStateKeyValueStorage, new NoOpMetricsSystem(), 512, - null); + null, + EvmConfiguration.DEFAULT); } @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 bc632e0f62a..4e423563416 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.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 @@ void commitAndPersist() { 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 c91ea5dcb08..28997591f73 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.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.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 void mark_marksAllExpectedNodes() { 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 void mark_marksAllExpectedNodes() { } // 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 void sweepBefore_shouldSweepStateRootFirst() { } @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 74c6364383e..e69a56e498a 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.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.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 void tearDown() throws Exception { @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 void downloadEmptyWorldState() { Stream.generate( () -> EthProtocolManagerTestUtil.createPeer(ethProtocolManager, header.getNumber())) .limit(5) - .collect(Collectors.toList()); + .toList(); final InMemoryTasksPriorityQueues taskCollection = new InMemoryTasksPriorityQueues<>(); @@ -193,7 +194,7 @@ public void downloadEmptyWorldState() { @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 void downloadAlreadyAvailableWorldState() { Stream.generate( () -> EthProtocolManagerTestUtil.createPeer(ethProtocolManager, header.getNumber())) .limit(5) - .collect(Collectors.toList()); + .toList(); final InMemoryTasksPriorityQueues taskCollection = new InMemoryTasksPriorityQueues<>(); @@ -233,7 +234,7 @@ public void downloadAlreadyAvailableWorldState() { @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 void canRecoverFromTimeouts() { // 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 void canRecoverFromTimeouts() { @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 void doesNotRequestKnownCodeFromNetwork() { .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 void doesNotRequestKnownCodeFromNetwork() { @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 @@ private void testCancellation(final boolean shouldCancelFuture) { Stream.generate( () -> EthProtocolManagerTestUtil.createPeer(ethProtocolManager, header.getNumber())) .limit(5) - .collect(Collectors.toList()); + .toList(); final InMemoryTasksPriorityQueues taskCollection = new InMemoryTasksPriorityQueues<>(); @@ -452,12 +455,13 @@ private void testCancellation(final boolean shouldCancelFuture) { @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 void doesNotRequestKnownAccountTrieNodesFromNetwork() { 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 void doesNotRequestKnownAccountTrieNodesFromNetwork() { .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 void doesNotRequestKnownAccountTrieNodesFromNetwork() { @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 void doesNotRequestKnownStorageTrieNodesFromNetwork() { 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 void doesNotRequestKnownStorageTrieNodesFromNetwork() { .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 void doesNotRequestKnownStorageTrieNodesFromNetwork() { @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 void stalledDownloader() { 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 void stalledDownloader() { @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 void resumesFromNonEmptyQueue() { 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 void resumesFromNonEmptyQueue() { .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 void resumesFromNonEmptyQueue() { // 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 @@ private void downloadAvailableWorldStateFromPeers( 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 @@ private void downloadAvailableWorldStateFromPeers( 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 @@ private void downloadAvailableWorldStateFromPeers( 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/BlockchainModule.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainModule.java index 91cdad76a18..0109d6c819a 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.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 @@ MutableWorldState getMutableWorldState( 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/EvmToolCommandOptionsModule.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommandOptionsModule.java index 9c97c8d36f8..b8df5fa4a95 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 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 @@ BesuConfiguration provideBesuConfiguration() { 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 6bd2ee52ac7..596c9eddc7f 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.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 @@ GenesisConfigOptions provideGenesisConfigOptions(final GenesisConfigFile genesis 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 5445fe2b43d..e7c576c5852 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 @@ BlockHeaderFunctions blockHashFunction() { 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 @@ ProtocolSchedule provideProtocolSchedule( 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 8b636b9a8c0..2f51f7d2e35 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.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.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,62 @@ void handleT8nRequest( 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"), + // objectMapper + // .getTypeFactory() + // .constructMapType(TreeMap.class, String.class, + // ReferenceTestWorldState.AccountMock.class)); + 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 006a806e216..40f7bb07987 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.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.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 void run() { } 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 c8349b7f790..e346b5abf6d 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.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 BonsaiReferenceTestUpdateAccumulator( 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 0e23f5406af..72cbb2f9324 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,6 +26,7 @@ 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; @@ -48,15 +49,18 @@ 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 TrieLogManager trieLogManager, - final BonsaiPreImageProxy preImageProxy) { - super(worldStateStorage, cachedMerkleTrieLoader, trieLogManager); + final BonsaiPreImageProxy preImageProxy, + final EvmConfiguration evmConfiguration) { + super(worldStateStorage, cachedMerkleTrieLoader, trieLogManager, EvmConfiguration.DEFAULT); this.refTestStorage = worldStateStorage; this.preImageProxy = preImageProxy; + this.evmConfiguration = evmConfiguration; setAccumulator( new BonsaiReferenceTestUpdateAccumulator( this, @@ -65,14 +69,15 @@ protected BonsaiReferenceTestWorldState( getWorldStateStorage(), worldStateRootHash, addr), (addr, value) -> cachedMerkleTrieLoader.preLoadStorageSlot(getWorldStateStorage(), addr, value), - preImageProxy)); + preImageProxy, + evmConfiguration)); } @Override public ReferenceTestWorldState copy() { var layerCopy = new BonsaiReferenceTestWorldStateStorage(worldStateStorage, preImageProxy); return new BonsaiReferenceTestWorldState( - layerCopy, cachedMerkleTrieLoader, trieLogManager, preImageProxy); + layerCopy, cachedMerkleTrieLoader, trieLogManager, preImageProxy, evmConfiguration); } /** @@ -95,6 +100,13 @@ protected void verifyWorldStateRoot(final Hash calculatedStateRoot, final BlockH @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(); @@ -109,7 +121,11 @@ public static BonsaiReferenceTestWorldState create( final BonsaiReferenceTestWorldState worldState = new BonsaiReferenceTestWorldState( - worldStateStorage, cachedMerkleTrieLoader, trieLogManager, preImageProxy); + worldStateStorage, + cachedMerkleTrieLoader, + trieLogManager, + preImageProxy, + evmConfiguration); final WorldUpdater updater = worldState.updater(); for (final Map.Entry entry : accounts.entrySet()) { @@ -144,7 +160,9 @@ public void saveTrieLog( public void addCachedLayer( final BlockHeader blockHeader, final Hash worldStateRootHash, - final BonsaiWorldState forWorldState) {} + final BonsaiWorldState forWorldState) { + // world state tests don't cache + } @Override public boolean containWorldStateStorage(final Hash blockHash) { @@ -173,7 +191,9 @@ public long getMaxLayersToLoad() { } @Override - public void reset() {} + public void reset() { + // reference tests don't cache layers, no need to reset + } @Override public Optional getTrieLogLayer(final Hash blockHash) { 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 ec0b058ba99..45771578e14 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.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 825d7a2cd4b..3d4db1edaab 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.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 @@ static void insertAccount( @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 9d6f7982d8d..7a881987468 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,14 @@ 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.account.AccountState; 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 +61,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 { @@ -107,6 +111,13 @@ public static void executeTest(final BlockchainReferenceTestCaseSpec spec) { spec.getWorldStateArchive() .getMutable(genesisBlockHeader.getStateRoot(), genesisBlockHeader.getHash()) .get(); + // don't world states that have empty accounts + assumeThat( + worldState + .streamAccounts(Bytes32.ZERO, Integer.MAX_VALUE) + .anyMatch(AccountState::isEmpty)) + .withFailMessage("Empty account detected") + .isFalse(); assertThat(worldState.rootHash()).isEqualTo(genesisBlockHeader.getStateRoot()); final ProtocolSchedule schedule = 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 83388fe2526..afbc09900de 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,16 +15,21 @@ 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; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder; -import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Transaction; -import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; @@ -35,19 +40,12 @@ 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.ethereum.worldstate.DefaultMutableWorldState; import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.account.AccountState; import org.hyperledger.besu.evm.log.Log; -import org.hyperledger.besu.evm.worldstate.WorldState; 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(); @@ -125,6 +123,14 @@ public static Collection generateTestParametersForConfig(final String[ } public static void executeTest(final GeneralStateTestCaseEipSpec spec) { + // don't world states that have empty accounts + assumeThat( + spec.getInitialWorldState() + .streamAccounts(Bytes32.ZERO, Integer.MAX_VALUE) + .anyMatch(AccountState::isEmpty)) + .withFailMessage("Empty account detected") + .isFalse(); + final BlockHeader blockHeader = spec.getBlockHeader(); final ReferenceTestWorldState initialWorldState = spec.getInitialWorldState(); final Transaction transaction = spec.getTransaction(); 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 4d29bfd213e..f9c9a6d6887 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 @@ -165,7 +165,8 @@ private boolean buildContext( 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/UndoMap.java b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoMap.java index e68f1ba31a5..543583786b4 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 @@ -75,9 +75,9 @@ public long lastUpdate() { return 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() { 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 index f9c54c42412..3bbdc242e1e 100644 --- a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java @@ -43,6 +43,7 @@ public UndoNavigableMap(final NavigableMap 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 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 19533a538a5..c3ad7ba17aa 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 EvmConfiguration(final long jumpDestCacheWeightKB) { 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/worldstate/AbstractWorldUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java index c86acb990f7..6b00ec5e689 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 @@ -18,6 +18,7 @@ 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 HashSet<>()); @@ -50,9 +53,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 JournaledUpdater<>(this); - // 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 index 9d2bd060d41..28a5fd1f38d 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java @@ -228,6 +228,7 @@ public boolean hasCode() { /** * Mark the account as deleted/not deleted + * * @param accountDeleted delete or don't delete this account. */ public void setDeleted(final boolean accountDeleted) { @@ -239,6 +240,7 @@ public void setDeleted(final boolean accountDeleted) { /** * Is the account marked as deleted? + * * @return is the account deleted? */ public Boolean getDeleted() { 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 index 85acf5ed204..bdec559e071 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java @@ -20,6 +20,7 @@ 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; @@ -34,6 +35,7 @@ */ public class JournaledUpdater implements WorldUpdater { + final EvmConfiguration evmConfiguration; final WorldUpdater parentWorld; final AbstractWorldUpdater rootWorld; final UndoMap accounts; @@ -44,10 +46,12 @@ public class JournaledUpdater implements WorldUpdater { * Instantiates a new Stacked updater. * * @param world the world + * @param evmConfiguration the EVM Configuration parameters */ @SuppressWarnings("unchecked") - public JournaledUpdater(final WorldUpdater world) { + public JournaledUpdater(final WorldUpdater world, final EvmConfiguration evmConfiguration) { parentWorld = world; + this.evmConfiguration = evmConfiguration; if (world instanceof JournaledUpdater) { JournaledUpdater journaledUpdater = (JournaledUpdater) world; accounts = journaledUpdater.accounts; @@ -175,6 +179,6 @@ public Account get(final Address address) { @Override public WorldUpdater updater() { - return new JournaledUpdater(this); + 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 3d585151c56..471a807e700 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 @@ 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 From 00defced21ef3f10658a631dc249fbadb87f98d3 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 13 Oct 2023 10:42:44 -0600 Subject: [PATCH 05/10] cleanup and expose to evmtool Signed-off-by: Danno Ferrin --- .../controller/BesuControllerBuilder.java | 25 +----------------- .../tracing/diff/StateDiffGenerator.java | 2 +- .../worldstate/DefaultMutableWorldState.java | 26 ++++++------------- .../core/InMemoryKeyValueStorageProvider.java | 7 ++++- .../besu/evmtool/EvmToolCommand.java | 16 +++++++++--- .../besu/evmtool/T8nServerSubCommand.java | 7 ----- .../hyperledger/besu/evm/internal/Words.java | 19 -------------- 7 files changed, 28 insertions(+), 74 deletions(-) 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 bea18e90eaa..6167432b4fc 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -129,83 +129,60 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides /** The Sync config. */ protected SynchronizerConfiguration syncConfig; - /** The Ethereum wire protocol configuration. */ protected EthProtocolConfiguration ethereumWireProtocolConfiguration; - /** The Transaction pool configuration. */ protected TransactionPoolConfiguration transactionPoolConfiguration; - /** The Network id. */ protected BigInteger networkId; - /** The Mining parameters. */ protected MiningParameters miningParameters; - /** The Metrics system. */ protected ObservableMetricsSystem metricsSystem; - /** The Privacy parameters. */ protected PrivacyParameters privacyParameters; - /** The Pki block creation configuration. */ protected Optional pkiBlockCreationConfiguration = Optional.empty(); - /** The Data directory. */ protected Path dataDirectory; - /** The Clock. */ protected Clock clock; - /** The Node key. */ protected NodeKey nodeKey; - /** The Is revert reason enabled. */ protected boolean isRevertReasonEnabled; - /** The Gas limit calculator. */ GasLimitCalculator gasLimitCalculator; - /** The Storage provider. */ protected StorageProvider storageProvider; - /** The Is pruning enabled. */ protected boolean isPruningEnabled; - /** The Pruner configuration. */ protected PrunerConfiguration prunerConfiguration; - /** The Required blocks. */ protected Map requiredBlocks = Collections.emptyMap(); - /** The Reorg logging threshold. */ protected long reorgLoggingThreshold; - /** The Data storage configuration. */ protected DataStorageConfiguration dataStorageConfiguration = DataStorageConfiguration.DEFAULT_CONFIG; - /** The Message permissioning providers. */ protected List messagePermissioningProviders = Collections.emptyList(); - /** The Evm configuration. */ protected EvmConfiguration evmConfiguration; - /** The Max peers. */ protected int maxPeers; private int peerLowerBound; private int maxRemotelyInitiatedPeers; - /** The Chain pruner configuration. */ protected ChainPrunerConfiguration chainPrunerConfiguration = ChainPrunerConfiguration.DEFAULT; private NetworkingConfiguration networkingConfiguration; private Boolean randomPeerPriority; private Optional transactionSelectorFactory = Optional.empty(); - /** the Dagger configured context that can provide dependencies */ protected Optional besuComponent = Optional.empty(); @@ -1081,7 +1058,7 @@ WorldStateArchive createWorldStateArchive( metricsSystem, besuComponent.map(BesuComponent::getBesuPluginContext).orElse(null), evmConfiguration); - default -> { + case FOREST -> { final WorldStatePreimageStorage preimageStorage = storageProvider.createWorldStatePreimageStorage(); yield new DefaultWorldStateArchive(worldStateStorage, preimageStorage, evmConfiguration); 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 3212d92403c..ffc816db736 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 @@ -60,7 +60,7 @@ public Stream generateStateDiff(final TransactionTrace transactionTrace) // calculate storage diff final Map storageDiff = new TreeMap<>(); for (final Map.Entry entry : - ((MutableAccount) 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/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldState.java index f6fd40ecdf0..d50f702af31 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 @@ -315,24 +315,14 @@ public NavigableMap storageEntriesFrom( @Override public String toString() { - return "AccountState" - + "{" - + "address=" - + getAddress() - + ", " - + "nonce=" - + getNonce() - + ", " - + "balance=" - + getBalance() - + ", " - + "storageRoot=" - + getStorageRoot() - + ", " - + "codeHash=" - + getCodeHash() - + ", " - + "}"; + final StringBuilder builder = new StringBuilder(); + builder.append("AccountState").append("{"); + builder.append("address=").append(getAddress()).append(", "); + builder.append("nonce=").append(getNonce()).append(", "); + builder.append("balance=").append(getBalance()).append(", "); + builder.append("storageRoot=").append(getStorageRoot()).append(", "); + builder.append("codeHash=").append(getCodeHash()).append(", "); + return builder.append("}").toString(); } private Optional getStorageTrieKeyPreimage(final Bytes32 trieKey) { 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 f781e82e46e..e10d2048fbb 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 @@ -83,6 +83,11 @@ public static DefaultWorldStateArchive createInMemoryWorldStateArchive() { 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 = @@ -93,7 +98,7 @@ public static BonsaiWorldStateProvider createBonsaiInMemoryWorldStateArchive( cachedMerkleTrieLoader, new NoOpMetricsSystem(), null, - EvmConfiguration.DEFAULT); + evmConfiguration); } public static MutableWorldState createInMemoryWorldState() { 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 4f2c2a5a5fb..743c215ecbb 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 @@ void setBytes(final String optionValue) { 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 void run() { 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/T8nServerSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nServerSubCommand.java index 2f51f7d2e35..a0ef6384b08 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 @@ -117,13 +117,6 @@ void handleT8nRequest( ReferenceTestEnv referenceTestEnv = objectMapper.convertValue(input.get("env"), ReferenceTestEnv.class); - // Map accounts = - // objectMapper.convertValue( - // input.get("alloc"), - // objectMapper - // .getTypeFactory() - // .constructMapType(TreeMap.class, String.class, - // ReferenceTestWorldState.AccountMock.class)); Map accounts = objectMapper.convertValue(input.get("alloc"), new TypeReference<>() {}); 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 71d599d0a2b..821406c8aa4 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 @@ static long clampedMultiply(final long a, final long b) { } } - /** - * 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 * From 830ab3d715a47e2764fb716829f59cb9f79b46b4 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 13 Oct 2023 11:15:01 -0600 Subject: [PATCH 06/10] expose worldstate status on startup Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/cli/BesuCommand.java | 1 + .../cli/ConfigurationOverviewBuilder.java | 15 ++++++++++++ .../cli/ConfigurationOverviewBuilderTest.java | 23 +++++++++++++++++++ 3 files changed, 39 insertions(+) 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 75c44198877..ae5371a0e8d 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -3548,6 +3548,7 @@ private String generateConfigurationOverview() { } builder.setTxPoolImplementation(buildTransactionPoolConfiguration().getTxPoolImplementation()); + builder.setWorldStateUpdateMode(unstableEvmOptions.toDomainObject().worldUpdaterMode()); return builder.build(); } 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 e017caa9ddf..3d2491986ac 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 @@ import org.hyperledger.besu.BesuInfo; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; +import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.util.log.FramedLogMessage; import org.hyperledger.besu.util.platform.PlatformDetector; @@ -49,6 +50,7 @@ public class ConfigurationOverviewBuilder { private String engineJwtFilePath; private boolean isHighSpec = false; private TransactionPoolConfiguration.Implementation txPoolImplementation; + private EvmConfiguration.WorldUpdaterMode worldStateUpdateMode; private Map environment; /** @@ -179,6 +181,18 @@ public ConfigurationOverviewBuilder setTxPoolImplementation( return this; } + /** + * Sets the world state updater mode + * + * @param worldStateUpdateMode the world state updater mode + * @return the buikder + */ + public ConfigurationOverviewBuilder setWorldStateUpdateMode( + final EvmConfiguration.WorldUpdaterMode worldStateUpdateMode) { + this.worldStateUpdateMode = worldStateUpdateMode; + return this; + } + /** * Sets the engine jwt file path. * @@ -255,6 +269,7 @@ public String build() { } lines.add("Using " + txPoolImplementation + " transaction pool implementation"); + lines.add("Using " + worldStateUpdateMode + " worldstate update mode"); lines.add(""); lines.add("Host:"); 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 4fff2b36f58..7642e14c946 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.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 @@ void setTxPoolImplementationLegacy() { 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"); + } } From a3ba879203f6bab5f1b48105e6ef0b8f2f4e71cc Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 13 Oct 2023 22:43:08 -0600 Subject: [PATCH 07/10] fix original value retrieval Signed-off-by: Danno Ferrin --- .../besu/evm/worldstate/JournaledAccount.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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 index 28a5fd1f38d..e527e5301d9 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java @@ -278,11 +278,10 @@ public UInt256 getStorageValue(final UInt256 key) { @Override public UInt256 getOriginalStorageValue(final UInt256 key) { - if (storageWasCleared) { - return getStorageValue(key); - } else { - return account.getOriginalStorageValue(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 From fae36d41c24c2d0c42012d300fec5e33792b40ec Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 25 Oct 2023 16:48:36 -0600 Subject: [PATCH 08/10] add in hedges against state tests for journaled mode Signed-off-by: Danno Ferrin --- .../vm/BlockchainReferenceTestTools.java | 27 +++++++++++------ .../vm/GeneralStateReferenceTestTools.java | 29 ++++++++++++------- .../java/org/hyperledger/besu/evm/EVM.java | 12 ++++++++ 3 files changed, 49 insertions(+), 19 deletions(-) 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 c39c54fc23f..84e605d3489 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 @@ -30,7 +30,10 @@ 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; @@ -77,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_*"); @@ -104,14 +107,6 @@ public static void executeTest(final BlockchainReferenceTestCaseSpec spec) { spec.getWorldStateArchive() .getMutable(genesisBlockHeader.getStateRoot(), genesisBlockHeader.getHash()) .get(); - // don't world states that have empty accounts - assumeThat( - worldState - .streamAccounts(Bytes32.ZERO, Integer.MAX_VALUE) - .anyMatch(AccountState::isEmpty)) - .withFailMessage("Empty account detected") - .isFalse(); - assertThat(worldState.rootHash()).isEqualTo(genesisBlockHeader.getStateRoot()); final ProtocolSchedule schedule = REFERENCE_TEST_PROTOCOL_SCHEDULES.getByName(spec.getNetwork()); @@ -130,6 +125,20 @@ public static void executeTest(final BlockchainReferenceTestCaseSpec spec) { 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.PARIS.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 3dd8c872292..61a607adf3d 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 @@ -40,8 +40,11 @@ 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; @@ -103,7 +106,7 @@ private static ProtocolSpec protocolSpec(final String name) { 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-.*"); @@ -120,17 +123,23 @@ public static Collection generateTestParametersForConfig(final String[ } public static void executeTest(final GeneralStateTestCaseEipSpec spec) { - // don't world states that have empty accounts - assumeThat( - spec.getInitialWorldState() - .streamAccounts(Bytes32.ZERO, Integer.MAX_VALUE) - .anyMatch(AccountState::isEmpty)) - .withFailMessage("Empty account detected") - .isFalse(); - 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.PARIS.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 @@ -155,7 +164,7 @@ public static void executeTest(final GeneralStateTestCaseEipSpec spec) { 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/evm/src/main/java/org/hyperledger/besu/evm/EVM.java b/evm/src/main/java/org/hyperledger/besu/evm/EVM.java index a77b4ac93a7..4ce73ef9cc6 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 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 int getMaxEOFVersion() { 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 * From a37fd7b05a1ccf882c6c25dd462ac4786fe1e72e Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 26 Oct 2023 11:05:28 -0600 Subject: [PATCH 09/10] Use 5723 fork demarcations. Signed-off-by: Danno Ferrin --- .../besu/ethereum/vm/BlockchainReferenceTestTools.java | 2 +- .../besu/ethereum/vm/GeneralStateReferenceTestTools.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 84e605d3489..fea3d8671da 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 @@ -134,7 +134,7 @@ public static void executeTest(final BlockchainReferenceTestCaseSpec spec) { .anyMatch(AccountState::isEmpty)) .withFailMessage("Journaled account configured and empty account detected") .isFalse(); - assumeThat(EvmSpecVersion.PARIS.compareTo(evm.getEvmVersion()) > 0) + assumeThat(EvmSpecVersion.SPURIOUS_DRAGON.compareTo(evm.getEvmVersion()) > 0) .withFailMessage("Journaled account configured and fork prior to the merge specified") .isFalse(); } 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 61a607adf3d..e0b0c7332e7 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 @@ -136,7 +136,7 @@ public static void executeTest(final GeneralStateTestCaseEipSpec spec) { .anyMatch(AccountState::isEmpty)) .withFailMessage("Journaled account configured and empty account detected") .isFalse(); - assumeThat(EvmSpecVersion.PARIS.compareTo(evm.getEvmVersion()) > 0) + assumeThat(EvmSpecVersion.SPURIOUS_DRAGON.compareTo(evm.getEvmVersion()) > 0) .withFailMessage("Journaled account configured and fork prior to the merge specified") .isFalse(); } From 9493c70bd26e5de60c2396b800adc34972063986 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 30 Oct 2023 12:29:16 -0600 Subject: [PATCH 10/10] review comments Signed-off-by: Danno Ferrin --- .../cli/ConfigurationOverviewBuilder.java | 2 +- .../besu/collections/undo/UndoList.java | 2 +- .../besu/collections/undo/UndoMap.java | 2 +- .../besu/collections/undo/UndoSet.java | 2 +- .../besu/collections/undo/UndoTable.java | 2 +- .../besu/collections/undo/Undoable.java | 2 +- ...ularExponentiationPrecompiledContract.java | 45 +++++++------------ 7 files changed, 23 insertions(+), 34 deletions(-) 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 a1b75c3d7d4..8b25f6377eb 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilder.java @@ -187,7 +187,7 @@ public ConfigurationOverviewBuilder setTxPoolImplementation( * Sets the world state updater mode * * @param worldStateUpdateMode the world state updater mode - * @return the buikder + * @return the builder */ public ConfigurationOverviewBuilder setWorldStateUpdateMode( final EvmConfiguration.WorldUpdaterMode worldStateUpdateMode) { 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 b1eef4ec547..72cdc56fee1 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 @@ -142,7 +142,7 @@ public void undo(final long mark) { @Override public long lastUpdate() { - return undoLog.get(undoLog.size() - 1).level; + return undoLog.isEmpty() ? 0L : undoLog.get(undoLog.size() - 1).level; } @Override 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 543583786b4..08a20b447ca 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 @@ -72,7 +72,7 @@ public void undo(final long mark) { @Override public long lastUpdate() { - return undoLog.get(undoLog.size() - 1).level; + return undoLog.isEmpty() ? 0L : undoLog.get(undoLog.size() - 1).level; } /** 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 a1ff6fd0ca7..d1f018ac14f 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 @@ -81,7 +81,7 @@ public void undo(final long mark) { @Override public long lastUpdate() { - return undoLog.get(undoLog.size() - 1).level; + return undoLog.isEmpty() ? 0L : undoLog.get(undoLog.size() - 1).level; } @Override 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 abe71c83695..0109ccdc8f7 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 @@ -88,7 +88,7 @@ public void undo(final long mark) { @Override public long lastUpdate() { - return undoLog.get(undoLog.size() - 1).level; + return undoLog.isEmpty() ? 0L : undoLog.get(undoLog.size() - 1).level; } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/collections/undo/Undoable.java b/evm/src/main/java/org/hyperledger/besu/collections/undo/Undoable.java index 12b1e3dbb97..274bbc2297b 100644 --- a/evm/src/main/java/org/hyperledger/besu/collections/undo/Undoable.java +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/Undoable.java @@ -50,7 +50,7 @@ static long incrementMarkStatic() { /** * The last time this object was updated. Any undo requrests greater than this mark will result in - * no chantes. + * no changes. * * @return The most recent mark. */ diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java index 55769acdb6b..ffb82f078cd 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java @@ -99,6 +99,21 @@ public long gasRequirement(final Bytes input) { @Override public PrecompileContractResult computePrecompile( final Bytes input, @Nonnull final MessageFrame messageFrame) { + if (useNative) { + return computeNative(input); + } else { + return computeDefault(input); + } + } + + /** + * Compute default precompile contract. + * + * @param input the input + * @return the precompile contract result + */ + @Nonnull + public PrecompileContractResult computeDefault(final Bytes input) { final int baseLength = clampedToInt(baseLength(input)); final int exponentLength = clampedToInt(exponentLength(input)); final int modulusLength = clampedToInt(modulusLength(input)); @@ -114,31 +129,6 @@ public PrecompileContractResult computePrecompile( final BigInteger exp = extractParameter(input, exponentOffset, exponentLength); final BigInteger mod = extractParameter(input, modulusOffset, modulusLength); - if (useNative && mod.getLowestSetBit() > 0) { - return computeNative(input, modulusLength); - } else { - return computeDefault(input, base, exp, mod, modulusLength); - } - } - - /** - * Compute default precompile contract. - * - * @param input the input - * @param base base of the exponent - * @param exp the exponent - * @param mod the modulus - * @param modulusLength the length of the modulus, in bytes - * @return the precompile contract result - */ - @Nonnull - public PrecompileContractResult computeDefault( - final Bytes input, - final BigInteger base, - final BigInteger exp, - final BigInteger mod, - final int modulusLength) { - final Bytes modExp; // Result must be the length of the modulus. final MutableBytes result = MutableBytes.create(modulusLength); @@ -255,11 +245,10 @@ private static long square(final long n) { * Compute native precompile contract. * * @param input the input - * @param modulusLength length, in bytes, of the modulus * @return the precompile contract result */ - public PrecompileContractResult computeNative( - final @Nonnull Bytes input, final int modulusLength) { + public PrecompileContractResult computeNative(final @Nonnull Bytes input) { + final int modulusLength = clampedToInt(modulusLength(input)); final IntByReference o_len = new IntByReference(modulusLength); final byte[] result = new byte[modulusLength];