From 955edfa7481a0b4a37255558c9487294b425da2f Mon Sep 17 00:00:00 2001 From: garyschulte Date: Mon, 11 Nov 2024 23:08:08 -0800 Subject: [PATCH] minimal approach to preimage hash store. missing optimizations and explicit coverage Signed-off-by: garyschulte --- .../WorldStatePreimageKeyValueStorage.java | 15 +- ...nsaiSnapshotWorldStateKeyValueStorage.java | 5 +- .../BonsaiWorldStateKeyValueStorage.java | 9 +- .../storage/CachingPreImageStorage.java | 195 ------------------ .../bonsai/worldview/BonsaiWorldState.java | 11 +- .../common/DiffBasedWorldStateProvider.java | 4 + .../DiffBasedWorldStateKeyValueStorage.java | 20 +- .../common/trielog/NoOpTrieLogManager.java | 3 +- .../common/trielog/TrieLogManager.java | 4 +- .../common/worldview/DiffBasedWorldState.java | 44 ++-- .../common/worldview/DiffBasedWorldView.java | 4 - .../DiffBasedWorldStateUpdateAccumulator.java | 28 +-- .../worldstate/WorldStatePreimageStorage.java | 8 + .../eth/manager/snap/SnapServerTest.java | 4 +- .../BonsaiReferenceTestWorldState.java | 3 +- plugin-api/build.gradle | 2 +- .../services/storage/KeyValueStorage.java | 7 + .../storage/SegmentedKeyValueStorage.java | 7 + .../RocksDBColumnarKeyValueSnapshot.java | 5 + .../RocksDBColumnarKeyValueStorage.java | 5 + .../kvstore/InMemoryKeyValueStorage.java | 5 + .../LimitedInMemoryKeyValueStorage.java | 5 + .../SegmentedInMemoryKeyValueStorage.java | 5 + .../SegmentedKeyValueStorageAdapter.java | 5 + 24 files changed, 135 insertions(+), 268 deletions(-) delete mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/CachingPreImageStorage.java diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/WorldStatePreimageKeyValueStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/WorldStatePreimageKeyValueStorage.java index 51a72bf760..b312e23c0c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/WorldStatePreimageKeyValueStorage.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/WorldStatePreimageKeyValueStorage.java @@ -25,16 +25,6 @@ import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; -/** - * This store is used in Forest storage format, and expects to use an Updater to insert and commit - * changes to storage, adjacent to worldstate commits. By not being part of the worldstate - * transaction and commit data in this implementation can become out-of-sync (by failing to commit - * preimages by hash) on abnormal exits of besu. - * - *

This implementation remains here for the deprecated forest storage format, and should be - * retired along with Forest. - */ -@Deprecated public class WorldStatePreimageKeyValueStorage implements WorldStatePreimageStorage { private final KeyValueStorage keyValueStorage; @@ -59,6 +49,11 @@ public class WorldStatePreimageKeyValueStorage implements WorldStatePreimageStor .map(val -> Address.wrap(Bytes.wrap(val))); } + @Override + public boolean canSupportStreaming() { + return keyValueStorage.isPersistent(); + } + @Override public Updater updater() { return new Updater(keyValueStorage.startTransaction()); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/BonsaiSnapshotWorldStateKeyValueStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/BonsaiSnapshotWorldStateKeyValueStorage.java index 2437118d64..c03487b7cb 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/BonsaiSnapshotWorldStateKeyValueStorage.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/BonsaiSnapshotWorldStateKeyValueStorage.java @@ -44,7 +44,10 @@ public class BonsaiSnapshotWorldStateKeyValueStorage extends BonsaiWorldStateKey final SnappedKeyValueStorage segmentedWorldStateStorage, final KeyValueStorage trieLogStorage) { super( - parentWorldStateStorage.flatDbStrategyProvider, segmentedWorldStateStorage, trieLogStorage); + parentWorldStateStorage.flatDbStrategyProvider, + segmentedWorldStateStorage, + trieLogStorage, + parentWorldStateStorage.getPreimageStorage()); this.parentWorldStateStorage = parentWorldStateStorage; this.subscribeParentId = parentWorldStateStorage.subscribe(this); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/BonsaiWorldStateKeyValueStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/BonsaiWorldStateKeyValueStorage.java index 159b626f9e..3dd048cd74 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/BonsaiWorldStateKeyValueStorage.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/BonsaiWorldStateKeyValueStorage.java @@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.FlatDbMode; import org.hyperledger.besu.ethereum.worldstate.StateTrieAccountValue; import org.hyperledger.besu.ethereum.worldstate.WorldStateKeyValueStorage; +import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; @@ -58,7 +59,8 @@ public class BonsaiWorldStateKeyValueStorage extends DiffBasedWorldStateKeyValue provider.getStorageBySegmentIdentifiers( List.of( ACCOUNT_INFO_STATE, CODE_STORAGE, ACCOUNT_STORAGE_STORAGE, TRIE_BRANCH_STORAGE)), - provider.getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.TRIE_LOG_STORAGE)); + provider.getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.TRIE_LOG_STORAGE), + provider.createWorldStatePreimageStorage()); this.flatDbStrategyProvider = new BonsaiFlatDbStrategyProvider(metricsSystem, dataStorageConfiguration); flatDbStrategyProvider.loadFlatDbStrategy(composedWorldStateStorage); @@ -67,8 +69,9 @@ public class BonsaiWorldStateKeyValueStorage extends DiffBasedWorldStateKeyValue public BonsaiWorldStateKeyValueStorage( final BonsaiFlatDbStrategyProvider flatDbStrategyProvider, final SegmentedKeyValueStorage composedWorldStateStorage, - final KeyValueStorage trieLogStorage) { - super(composedWorldStateStorage, trieLogStorage); + final KeyValueStorage trieLogStorage, + final WorldStatePreimageStorage preimageStorage) { + super(composedWorldStateStorage, trieLogStorage, preimageStorage); this.flatDbStrategyProvider = flatDbStrategyProvider; } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/CachingPreImageStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/CachingPreImageStorage.java deleted file mode 100644 index f5cc73e4d7..0000000000 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/CachingPreImageStorage.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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.ethereum.trie.diffbased.bonsai.storage; - -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Hash; - -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Stream; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.bytes.Bytes32; -import org.apache.tuweni.units.bigints.UInt256; - -/** Acts as both a Hasher and PreImageStorage for Bonsai storage format. */ -public interface CachingPreImageStorage { - - enum HashSource { - ACCOUNT_ADDRESS((byte) 0x00), - SLOT_KEY((byte) 0x01); - byte internal_type; - - HashSource(final byte type) { - internal_type = type; - } - - byte getTypeSuffix() { - return internal_type; - } - - static HashSource wrap(final byte val) { - if (val == 0x00) { - return ACCOUNT_ADDRESS; - } else if (val == 0x01) { - return SLOT_KEY; - } else { - throw new NoSuchElementException(String.format("Value %x is not a preimage source", val)); - } - } - } - - record HashKey(Hash hashValue, HashSource source) {} - - /** - * If this value is not already present, save in preImage store and return the hash value. - * - * @param value value to hash - * @return Hash of value - */ - Hash hashAndSaveAddressPreImage(final Bytes value); - - Hash hashAndSaveSlotKeyPreImage(final UInt256 keyUInt); - - Stream

streamAddressPreImages(final Bytes32 startKeyHash, final int limit); - - Optional getStorageTrieKeyPreimage(final Bytes32 trieKey); - - Optional
getAccountTrieKeyPreimage(final Bytes32 trieKey); - - Optional getRawHashPreImage(final Hash hash); - - /** - * This method indicates whether this Pre-Image store is "complete", meaning it has all of the - * hash preimages for all entries in the state trie. - * - * @return boolean indicating whether the pre-image store is complete or not - */ - default boolean canSupportStreaming() { - return false; - } - - /** - * A caching PreImageProxy suitable for ReferenceTestWorldState which saves hashes in an unbounded - * BiMap. - */ - class UnboundedPreImageStorage implements CachingPreImageStorage { - BiMap preImageCache = HashBiMap.create(); - - @Override - public Hash hashAndSaveAddressPreImage(final Bytes value) { - return preImageCache - .inverse() - .computeIfAbsent( - value, v -> new HashKey(Address.wrap(v).addressHash(), HashSource.ACCOUNT_ADDRESS)) - .hashValue(); - } - - @Override - public Hash hashAndSaveSlotKeyPreImage(final UInt256 value) { - return preImageCache - .inverse() - .computeIfAbsent(value, v -> new HashKey(Hash.hash(v), HashSource.SLOT_KEY)) - .hashValue(); - } - - @Override - public Stream
streamAddressPreImages(final Bytes32 startKeyHash, final int limit) { - final Hash startHash = Hash.wrap(startKeyHash); - return preImageCache.entrySet().stream() - .filter(entry -> entry.getKey().source() == HashSource.ACCOUNT_ADDRESS) - .filter(entry -> entry.getKey().hashValue().compareTo(startHash) >= 0) - .map(e -> Address.wrap(e.getValue())) - .limit(limit); - } - - @Override - public Optional getStorageTrieKeyPreimage(final Bytes32 trieKey) { - return Optional.ofNullable( - preImageCache.get(new HashKey(Hash.wrap(trieKey), HashSource.SLOT_KEY))) - .map(UInt256::fromBytes); - } - - @Override - public Optional
getAccountTrieKeyPreimage(final Bytes32 trieKey) { - return Optional.ofNullable( - preImageCache.get(new HashKey(Hash.wrap(trieKey), HashSource.ACCOUNT_ADDRESS))) - .map(Address::wrap); - } - - @Override - public Optional getRawHashPreImage(final Hash hash) { - return Stream.of( - preImageCache.get(new HashKey(hash, HashSource.ACCOUNT_ADDRESS)), - preImageCache.get(new HashKey(hash, HashSource.SLOT_KEY))) - .filter(Objects::nonNull) - .findAny(); - } - - @Override - public boolean canSupportStreaming() { - return true; - } - } - - class CachingOnlyPreImageStorage implements CachingPreImageStorage { - - // TODO: config max size perhaps - private static final Cache preimageCache = - Caffeine.newBuilder().maximumSize(10000).build(); - - @Override - public Hash hashAndSaveAddressPreImage(final Bytes value) { - // defer to the static Address hash map used by the evm - return preimageCache.get(value, v -> Address.wrap(value).addressHash()); - } - - @Override - public Hash hashAndSaveSlotKeyPreImage(final UInt256 keyUInt) { - return preimageCache.get(keyUInt, Hash::hash); - } - - @Override - public Stream
streamAddressPreImages(final Bytes32 startKeyHash, final int limit) { - // not configured for preimage streaming - return Stream.empty(); - } - - @Override - public Optional getStorageTrieKeyPreimage(final Bytes32 trieKey) { - return getRawHashPreImage(Hash.wrap(trieKey)).map(UInt256::fromBytes); - } - - @Override - public Optional
getAccountTrieKeyPreimage(final Bytes32 trieKey) { - return getRawHashPreImage(Hash.wrap(trieKey)).map(Address::wrap); - } - - @Override - public Optional getRawHashPreImage(final Hash hash) { - return preimageCache.asMap().entrySet().stream() - .filter(e -> e.getValue().equals(hash)) - .findFirst() - .map(Map.Entry::getKey); - } - } -} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/worldview/BonsaiWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/worldview/BonsaiWorldState.java index d43f585b45..c1d32e14ab 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/worldview/BonsaiWorldState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/worldview/BonsaiWorldState.java @@ -187,22 +187,19 @@ public class BonsaiWorldState extends DiffBasedWorldState { final MerkleTrie accountTrie) { for (final Map.Entry> accountUpdate : worldStateUpdater.getAccountsToUpdate().entrySet()) { - final Bytes accountKey = accountUpdate.getKey(); + final Address accountKey = accountUpdate.getKey(); final DiffBasedValue bonsaiValue = accountUpdate.getValue(); final BonsaiAccount updatedAccount = bonsaiValue.getUpdated(); try { if (updatedAccount == null) { - final Hash addressHash = getPreImageProxy().hashAndSaveAddressPreImage(accountKey); - accountTrie.remove(addressHash); + accountTrie.remove(accountKey.addressHash()); maybeStateUpdater.ifPresent( - bonsaiUpdater -> bonsaiUpdater.removeAccountInfoState(addressHash)); + bonsaiUpdater -> bonsaiUpdater.removeAccountInfoState(accountKey.addressHash())); } else { final Hash addressHash = updatedAccount.getAddressHash(); final Bytes accountValue = updatedAccount.serializeAccount(); maybeStateUpdater.ifPresent( - bonsaiUpdater -> - bonsaiUpdater.putAccountInfoState( - getPreImageProxy().hashAndSaveAddressPreImage(accountKey), accountValue)); + bonsaiUpdater -> bonsaiUpdater.putAccountInfoState(addressHash, accountValue)); accountTrie.put(addressHash, accountValue); } } catch (MerkleTrieException e) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/DiffBasedWorldStateProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/DiffBasedWorldStateProvider.java index 75b370c1cd..e401c244ac 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/DiffBasedWorldStateProvider.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/DiffBasedWorldStateProvider.java @@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWo import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldStateConfig; import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; +import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage; import org.hyperledger.besu.ethereum.worldstate.WorldStateStorageCoordinator; import org.hyperledger.besu.evm.worldstate.WorldState; import org.hyperledger.besu.plugin.BesuContext; @@ -51,6 +52,7 @@ public abstract class DiffBasedWorldStateProvider implements WorldStateArchive { protected final Blockchain blockchain; protected final TrieLogManager trieLogManager; + protected final WorldStatePreimageStorage preimageStorage; protected DiffBasedCachedWorldStorageManager cachedWorldStorageManager; protected DiffBasedWorldState persistedState; @@ -71,6 +73,7 @@ public abstract class DiffBasedWorldStateProvider implements WorldStateArchive { worldStateKeyValueStorage, maxLayersToLoad.orElse(DiffBasedCachedWorldStorageManager.RETAINED_LAYERS), pluginContext); + this.preimageStorage = worldStateKeyValueStorage.getPreimageStorage(); this.blockchain = blockchain; this.defaultWorldStateConfig = new DiffBasedWorldStateConfig(); } @@ -85,6 +88,7 @@ public abstract class DiffBasedWorldStateProvider implements WorldStateArchive { this.trieLogManager = trieLogManager; this.blockchain = blockchain; this.defaultWorldStateConfig = new DiffBasedWorldStateConfig(); + preimageStorage = worldStateKeyValueStorage.getPreimageStorage(); } protected void provideCachedWorldStorageManager( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/storage/DiffBasedWorldStateKeyValueStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/storage/DiffBasedWorldStateKeyValueStorage.java index 4043089fc6..eb94eadc05 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/storage/DiffBasedWorldStateKeyValueStorage.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/storage/DiffBasedWorldStateKeyValueStorage.java @@ -24,12 +24,12 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.BonsaiAccount; -import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.CachingPreImageStorage; import org.hyperledger.besu.ethereum.trie.diffbased.common.StorageSubscriber; import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.flat.FlatDbStrategy; import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldView; import org.hyperledger.besu.ethereum.worldstate.FlatDbMode; import org.hyperledger.besu.ethereum.worldstate.WorldStateKeyValueStorage; +import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage; import org.hyperledger.besu.evm.account.AccountStorageEntry; import org.hyperledger.besu.evm.worldstate.WorldState; import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; @@ -64,10 +64,7 @@ public abstract class DiffBasedWorldStateKeyValueStorage private static final Logger LOG = LoggerFactory.getLogger(DiffBasedWorldStateKeyValueStorage.class); - // TODO: config this based on dataStorageConfiguration, use real storage where appropriate - // currently hard-coded to Unbounded preimage store so ref/spec tests pass - private static final CachingPreImageStorage preImageProxy = - new CachingPreImageStorage.UnboundedPreImageStorage(); + private final WorldStatePreimageStorage preImageProxy; // 0x776f726c64526f6f74 public static final byte[] WORLD_ROOT_HASH_KEY = "worldRoot".getBytes(StandardCharsets.UTF_8); @@ -90,13 +87,16 @@ public abstract class DiffBasedWorldStateKeyValueStorage ACCOUNT_INFO_STATE, CODE_STORAGE, ACCOUNT_STORAGE_STORAGE, TRIE_BRANCH_STORAGE)); this.trieLogStorage = provider.getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.TRIE_LOG_STORAGE); + this.preImageProxy = provider.createWorldStatePreimageStorage(); } public DiffBasedWorldStateKeyValueStorage( final SegmentedKeyValueStorage composedWorldStateStorage, - final KeyValueStorage trieLogStorage) { + final KeyValueStorage trieLogStorage, + final WorldStatePreimageStorage preImageStorage) { this.composedWorldStateStorage = composedWorldStateStorage; this.trieLogStorage = trieLogStorage; + this.preImageProxy = preImageStorage; } public abstract FlatDbMode getFlatDbMode(); @@ -110,14 +110,14 @@ public abstract class DiffBasedWorldStateKeyValueStorage return composedWorldStateStorage; } - public CachingPreImageStorage getPreImageProxy() { - return preImageProxy; - } - public KeyValueStorage getTrieLogStorage() { return trieLogStorage; } + public WorldStatePreimageStorage getPreimageStorage() { + return preImageProxy; + } + public Optional getTrieLog(final Hash blockHash) { return trieLogStorage.get(blockHash.toArrayUnsafe()); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/NoOpTrieLogManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/NoOpTrieLogManager.java index 8c8c2acbfc..edea97f4e9 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/NoOpTrieLogManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/NoOpTrieLogManager.java @@ -29,7 +29,7 @@ public class NoOpTrieLogManager extends TrieLogManager { } @Override - public synchronized void saveTrieLog( + public synchronized Optional saveTrieLog( final DiffBasedWorldStateUpdateAccumulator localUpdater, final Hash forWorldStateRootHash, final BlockHeader forBlockHeader, @@ -37,6 +37,7 @@ public class NoOpTrieLogManager extends TrieLogManager { // notify trie log added observers, synchronously TrieLog trieLog = trieLogFactory.create(localUpdater, forBlockHeader); trieLogObservers.forEach(o -> o.onTrieLogAdded(new TrieLogAddedEvent(trieLog))); + return Optional.of(trieLog); } @Override diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/TrieLogManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/TrieLogManager.java index 70d45f546d..04e12867a0 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/TrieLogManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/TrieLogManager.java @@ -60,7 +60,7 @@ public class TrieLogManager { this.trieLogFactory = setupTrieLogFactory(pluginContext); } - public synchronized void saveTrieLog( + public synchronized Optional saveTrieLog( final DiffBasedWorldStateUpdateAccumulator localUpdater, final Hash forWorldStateRootHash, final BlockHeader forBlockHeader, @@ -80,6 +80,7 @@ public class TrieLogManager { trieLogObservers.forEach(o -> o.onTrieLogAdded(new TrieLogAddedEvent(trieLog))); success = true; + return Optional.of(trieLog); } finally { if (success) { stateUpdater.commit(); @@ -88,6 +89,7 @@ public class TrieLogManager { } } } + return Optional.empty(); } private TrieLog prepareTrieLog( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/DiffBasedWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/DiffBasedWorldState.java index 5eacb87f77..46c226afa9 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/DiffBasedWorldState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/DiffBasedWorldState.java @@ -23,7 +23,6 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.StorageSlotKey; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.MutableWorldState; -import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.CachingPreImageStorage; import org.hyperledger.besu.ethereum.trie.diffbased.common.StorageSubscriber; import org.hyperledger.besu.ethereum.trie.diffbased.common.cache.DiffBasedCachedWorldStorageManager; import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedLayeredWorldStateKeyValueStorage; @@ -118,11 +117,6 @@ public abstract class DiffBasedWorldState return !(worldStateKeyValueStorage instanceof DiffBasedSnapshotWorldStateKeyValueStorage); } - @Override - public CachingPreImageStorage getPreImageProxy() { - return worldStateKeyValueStorage.getPreImageProxy(); - } - /** * Reset the worldState to this block header * @@ -166,7 +160,6 @@ public abstract class DiffBasedWorldState final DiffBasedWorldStateKeyValueStorage.Updater stateUpdater = worldStateKeyValueStorage.updater(); Runnable saveTrieLog = () -> {}; - try { final Hash calculatedRootHash; @@ -190,11 +183,40 @@ public abstract class DiffBasedWorldState verifyWorldStateRoot(calculatedRootHash, blockHeader); saveTrieLog = () -> { - trieLogManager.saveTrieLog(localCopy, calculatedRootHash, blockHeader, this); + var trieLog = + trieLogManager.saveTrieLog(localCopy, calculatedRootHash, blockHeader, this); // not save a frozen state in the cache if (!worldStateConfig.isFrozen()) { cachedWorldStorageManager.addCachedLayer(blockHeader, calculatedRootHash, this); } + + // TODO: maybe move this, make conditional so we don't affect performance + // if we are not tracking preimages. using the trielog probably is going to get us + // duplicates + // because we will get updates in addition to creates :frown: + if (trieLog.isPresent()) { + var log = trieLog.get(); + var preImageUpdater = worldStateKeyValueStorage.getPreimageStorage().updater(); + log.getAccountChanges() + .keySet() + .forEach( + acct -> + preImageUpdater.putAccountTrieKeyPreimage(acct.addressHash(), acct)); + localCopy.getStorageToUpdate().values().stream() + .flatMap(z -> z.keySet().stream()) + .filter( + z -> { + // TODO: we should add logic here to prevent writing + // common slot keys + return z.getSlotKey().isPresent(); + }) + .distinct() + .forEach( + slot -> { + preImageUpdater.putStorageTrieKeyPreimage( + slot.getSlotHash(), slot.getSlotKey().get()); + }); + } }; stateUpdater @@ -304,12 +326,6 @@ public abstract class DiffBasedWorldState public abstract Stream streamAccounts( final Bytes32 startKeyHash, final int limit); - // { - // return preImageProxy - // .streamAddressPreImages(startKeyHash, limit) - // .map(address -> new StreamableAccount(Optional.of(address), get(address))); - // } - @Override public UInt256 getPriorStorageValue(final Address address, final UInt256 storageKey) { return getStorageValue(address, storageKey); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/DiffBasedWorldView.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/DiffBasedWorldView.java index 49d72c6a11..7ecc49e286 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/DiffBasedWorldView.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/DiffBasedWorldView.java @@ -18,7 +18,6 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.StorageSlotKey; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; -import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.CachingPreImageStorage; import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedWorldStateKeyValueStorage; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.evm.worldstate.WorldView; @@ -58,9 +57,6 @@ public interface DiffBasedWorldView extends WorldView { boolean isPersisted(); - // TODO: comments and naming - CachingPreImageStorage getPreImageProxy(); - DiffBasedWorldStateKeyValueStorage getWorldStateStorage(); WorldUpdater updater(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/accumulator/DiffBasedWorldStateUpdateAccumulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/accumulator/DiffBasedWorldStateUpdateAccumulator.java index efa9002f7a..266bfcb50a 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/accumulator/DiffBasedWorldStateUpdateAccumulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/accumulator/DiffBasedWorldStateUpdateAccumulator.java @@ -21,7 +21,6 @@ import org.hyperledger.besu.datatypes.StorageSlotKey; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.trie.MerkleTrieException; -import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.CachingPreImageStorage; import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedAccount; import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedValue; import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedWorldStateKeyValueStorage; @@ -49,6 +48,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; @@ -61,6 +62,9 @@ public abstract class DiffBasedWorldStateUpdateAccumulator slotCache = Caffeine.newBuilder().maximumSize(4000).build(); + protected final Consumer> accountPreloader; protected final Consumer storagePreloader; @@ -260,7 +264,7 @@ public abstract class DiffBasedWorldStateUpdateAccumulator pendingValue = pendingStorageUpdates.get(slotKey); @@ -525,8 +528,7 @@ public abstract class DiffBasedWorldStateUpdateAccumulator> localAccountStorage = storageToUpdate.get(address); if (localAccountStorage != null) { @@ -615,11 +615,6 @@ public abstract class DiffBasedWorldStateUpdateAccumulator copy(); protected abstract ACCOUNT copyAccount(final ACCOUNT account); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/WorldStatePreimageStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/WorldStatePreimageStorage.java index fde6572aa8..a48aa47c79 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/WorldStatePreimageStorage.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/WorldStatePreimageStorage.java @@ -28,6 +28,14 @@ public interface WorldStatePreimageStorage { Optional
getAccountTrieKeyPreimage(Bytes32 trieKey); + /** + * This method indicates whether this Pre-Image store is "complete", meaning it has all of the + * hash preimages for all entries in the state trie. + * + * @return boolean indicating whether the pre-image store is complete or not + */ + boolean canSupportStreaming(); + Updater updater(); interface Updater { diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServerTest.java index 2844603429..f558b44b4e 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServerTest.java @@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.eth.messages.snap.TrieNodesMessage; import org.hyperledger.besu.ethereum.proof.WorldStateProofProvider; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.rlp.RLP; +import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValueStorage; import org.hyperledger.besu.ethereum.trie.CompactEncoding; import org.hyperledger.besu.ethereum.trie.MerkleTrie; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldStateKeyValueStorage; @@ -99,7 +100,8 @@ public class SnapServerTest { } }, storage, - new InMemoryKeyValueStorage()); + new InMemoryKeyValueStorage(), + new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage())); final WorldStateStorageCoordinator storageCoordinator = new WorldStateStorageCoordinator(inMemoryStorage); 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 8fb5ce7079..d476ea8fd6 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 @@ -248,7 +248,7 @@ public class BonsaiReferenceTestWorldState extends BonsaiWorldState } @Override - public synchronized void saveTrieLog( + public synchronized Optional saveTrieLog( final DiffBasedWorldStateUpdateAccumulator localUpdater, final Hash forWorldStateRootHash, final BlockHeader forBlockHeader, @@ -257,6 +257,7 @@ public class BonsaiReferenceTestWorldState extends BonsaiWorldState TrieLog trieLog = trieLogFactory.create(localUpdater, forBlockHeader); trieLogCache.put(forBlockHeader.getHash(), trieLogFactory.serialize(trieLog)); trieLogObservers.forEach(o -> o.onTrieLogAdded(new TrieLogAddedEvent(trieLog))); + return Optional.of(trieLog); } @Override diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index 5733908d57..929e4bda90 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -71,7 +71,7 @@ Calculated : ${currentHash} tasks.register('checkAPIChanges', FileStateChecker) { description = "Checks that the API for the Plugin-API project does not change without deliberate thought" files = sourceSets.main.allJava.files - knownHash = 'TFtDm/CF5Z36eh4XYAwuiPKyo1pm+da/DIzpQYQ//EU=' + knownHash = 'fM8N/NxUB5CBc/rtAhPDUIGtnkbs1Bb9CFLLeVnqouQ=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/KeyValueStorage.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/KeyValueStorage.java index 2cd933e2f8..cc3e616c06 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/KeyValueStorage.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/KeyValueStorage.java @@ -143,4 +143,11 @@ public interface KeyValueStorage extends Closeable { * @return boolean indicating whether the storage is closed. */ boolean isClosed(); + + /** + * Is this storage persistent or ephemeral. + * + * @return whether this storage type is persistent or not. + */ + boolean isPersistent(); } diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/SegmentedKeyValueStorage.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/SegmentedKeyValueStorage.java index 4f734c7c97..340a863de0 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/SegmentedKeyValueStorage.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/SegmentedKeyValueStorage.java @@ -195,4 +195,11 @@ public interface SegmentedKeyValueStorage extends Closeable { return value.map(Bytes::wrap); } } + + /** + * Whether this storage is persistent or ephemeral. + * + * @return if storage is persistent. + */ + boolean isPersistent(); } diff --git a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueSnapshot.java b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueSnapshot.java index 72976a9d84..69ebdcb454 100644 --- a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueSnapshot.java +++ b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueSnapshot.java @@ -157,6 +157,11 @@ public class RocksDBColumnarKeyValueSnapshot return closed.get(); } + @Override + public boolean isPersistent() { + return false; + } + @Override public void clear(final SegmentIdentifier segment) { throw new UnsupportedOperationException( diff --git a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java index 0bf107ddb0..d9648945ec 100644 --- a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java +++ b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java @@ -482,4 +482,9 @@ public abstract class RocksDBColumnarKeyValueStorage implements SegmentedKeyValu return String.format("'%s'(%s)", name, Bytes.of(id).toHexString()); } } + + @Override + public boolean isPersistent() { + return true; + } } diff --git a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/InMemoryKeyValueStorage.java b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/InMemoryKeyValueStorage.java index 930783d7be..bc98322af8 100644 --- a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/InMemoryKeyValueStorage.java +++ b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/InMemoryKeyValueStorage.java @@ -101,4 +101,9 @@ public class InMemoryKeyValueStorage extends SegmentedKeyValueStorageAdapter { public void dump(final PrintStream ps) { ((SegmentedInMemoryKeyValueStorage) storage).dump(ps); } + + @Override + public boolean isPersistent() { + return false; + } } diff --git a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LimitedInMemoryKeyValueStorage.java b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LimitedInMemoryKeyValueStorage.java index aa52e49284..6afae23860 100644 --- a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LimitedInMemoryKeyValueStorage.java +++ b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LimitedInMemoryKeyValueStorage.java @@ -162,6 +162,11 @@ public class LimitedInMemoryKeyValueStorage implements KeyValueStorage { return false; } + @Override + public boolean isPersistent() { + return false; + } + private class MemoryTransaction implements KeyValueStorageTransaction { private Map updatedValues = new HashMap<>(); diff --git a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedInMemoryKeyValueStorage.java b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedInMemoryKeyValueStorage.java index a7cedca362..12e30d8803 100644 --- a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedInMemoryKeyValueStorage.java +++ b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedInMemoryKeyValueStorage.java @@ -310,6 +310,11 @@ public class SegmentedInMemoryKeyValueStorage return false; } + @Override + public boolean isPersistent() { + return false; + } + @Override public SegmentedInMemoryKeyValueStorage takeSnapshot() { // need to clone the submaps also: diff --git a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorageAdapter.java b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorageAdapter.java index 28c2d4c796..e85fb542b0 100644 --- a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorageAdapter.java +++ b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorageAdapter.java @@ -130,6 +130,11 @@ public class SegmentedKeyValueStorageAdapter implements KeyValueStorage { return storage.isClosed(); } + @Override + public boolean isPersistent() { + return storage.isPersistent(); + } + private void throwIfClosed() { if (storage.isClosed()) { LOG.error("Attempting to use a closed Storage instance.");