minimal approach to preimage hash store. missing optimizations and explicit coverage

Signed-off-by: garyschulte <garyschulte@gmail.com>
pull/7800/head
garyschulte 2 weeks ago
parent 2852e978fa
commit 955edfa748
  1. 15
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/WorldStatePreimageKeyValueStorage.java
  2. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/BonsaiSnapshotWorldStateKeyValueStorage.java
  3. 9
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/BonsaiWorldStateKeyValueStorage.java
  4. 195
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/CachingPreImageStorage.java
  5. 11
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/worldview/BonsaiWorldState.java
  6. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/DiffBasedWorldStateProvider.java
  7. 20
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/storage/DiffBasedWorldStateKeyValueStorage.java
  8. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/NoOpTrieLogManager.java
  9. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/TrieLogManager.java
  10. 42
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/DiffBasedWorldState.java
  11. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/DiffBasedWorldView.java
  12. 28
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/accumulator/DiffBasedWorldStateUpdateAccumulator.java
  13. 8
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/WorldStatePreimageStorage.java
  14. 4
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServerTest.java
  15. 3
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldState.java
  16. 2
      plugin-api/build.gradle
  17. 7
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/KeyValueStorage.java
  18. 7
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/SegmentedKeyValueStorage.java
  19. 5
      plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueSnapshot.java
  20. 5
      plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java
  21. 5
      services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/InMemoryKeyValueStorage.java
  22. 5
      services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LimitedInMemoryKeyValueStorage.java
  23. 5
      services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedInMemoryKeyValueStorage.java
  24. 5
      services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorageAdapter.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.
*
* <p>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());

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

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

@ -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<Address> streamAddressPreImages(final Bytes32 startKeyHash, final int limit);
Optional<UInt256> getStorageTrieKeyPreimage(final Bytes32 trieKey);
Optional<Address> getAccountTrieKeyPreimage(final Bytes32 trieKey);
Optional<Bytes> 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<HashKey, Bytes> 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<Address> 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<UInt256> getStorageTrieKeyPreimage(final Bytes32 trieKey) {
return Optional.ofNullable(
preImageCache.get(new HashKey(Hash.wrap(trieKey), HashSource.SLOT_KEY)))
.map(UInt256::fromBytes);
}
@Override
public Optional<Address> getAccountTrieKeyPreimage(final Bytes32 trieKey) {
return Optional.ofNullable(
preImageCache.get(new HashKey(Hash.wrap(trieKey), HashSource.ACCOUNT_ADDRESS)))
.map(Address::wrap);
}
@Override
public Optional<Bytes> 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<Bytes, Hash> 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<Address> streamAddressPreImages(final Bytes32 startKeyHash, final int limit) {
// not configured for preimage streaming
return Stream.empty();
}
@Override
public Optional<UInt256> getStorageTrieKeyPreimage(final Bytes32 trieKey) {
return getRawHashPreImage(Hash.wrap(trieKey)).map(UInt256::fromBytes);
}
@Override
public Optional<Address> getAccountTrieKeyPreimage(final Bytes32 trieKey) {
return getRawHashPreImage(Hash.wrap(trieKey)).map(Address::wrap);
}
@Override
public Optional<Bytes> getRawHashPreImage(final Hash hash) {
return preimageCache.asMap().entrySet().stream()
.filter(e -> e.getValue().equals(hash))
.findFirst()
.map(Map.Entry::getKey);
}
}
}

@ -187,22 +187,19 @@ public class BonsaiWorldState extends DiffBasedWorldState {
final MerkleTrie<Bytes, Bytes> accountTrie) {
for (final Map.Entry<Address, DiffBasedValue<BonsaiAccount>> accountUpdate :
worldStateUpdater.getAccountsToUpdate().entrySet()) {
final Bytes accountKey = accountUpdate.getKey();
final Address accountKey = accountUpdate.getKey();
final DiffBasedValue<BonsaiAccount> 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) {

@ -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(

@ -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<byte[]> getTrieLog(final Hash blockHash) {
return trieLogStorage.get(blockHash.toArrayUnsafe());
}

@ -29,7 +29,7 @@ public class NoOpTrieLogManager extends TrieLogManager {
}
@Override
public synchronized void saveTrieLog(
public synchronized Optional<TrieLog> 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

@ -60,7 +60,7 @@ public class TrieLogManager {
this.trieLogFactory = setupTrieLogFactory(pluginContext);
}
public synchronized void saveTrieLog(
public synchronized Optional<TrieLog> 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(

@ -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 =
() -> {
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<StreamableAccount> 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);

@ -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();

@ -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<ACCOUNT extends DiffB
implements DiffBasedWorldView, TrieLogAccumulator {
private static final Logger LOG =
LoggerFactory.getLogger(DiffBasedWorldStateUpdateAccumulator.class);
final Cache<UInt256, Hash> slotCache = Caffeine.newBuilder().maximumSize(4000).build();
protected final Consumer<DiffBasedValue<ACCOUNT>> accountPreloader;
protected final Consumer<StorageSlotKey> storagePreloader;
@ -260,7 +264,7 @@ public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffB
createAccount(
this,
address,
hashAndSaveAccountPreImage(address),
address.addressHash(),
nonce,
balance,
Hash.EMPTY_TRIE_HASH,
@ -480,8 +484,7 @@ public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffB
final UInt256 keyUInt = storageUpdate.getKey();
final StorageSlotKey slotKey =
new StorageSlotKey(
world.getPreImageProxy().hashAndSaveSlotKeyPreImage(keyUInt),
Optional.of(keyUInt));
slotCache.get(keyUInt, Hash::hash), Optional.of(keyUInt));
final UInt256 value = storageUpdate.getValue();
final DiffBasedValue<UInt256> pendingValue =
pendingStorageUpdates.get(slotKey);
@ -525,8 +528,7 @@ public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffB
@Override
public UInt256 getStorageValue(final Address address, final UInt256 slotKey) {
StorageSlotKey storageSlotKey =
new StorageSlotKey(
world.getPreImageProxy().hashAndSaveSlotKeyPreImage(slotKey), Optional.of(slotKey));
new StorageSlotKey(slotCache.get(slotKey, Hash::hash), Optional.of(slotKey));
return getStorageValueByStorageSlotKey(address, storageSlotKey).orElse(UInt256.ZERO);
}
@ -565,9 +567,7 @@ public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffB
public UInt256 getPriorStorageValue(final Address address, final UInt256 storageKey) {
// TODO maybe log the read into the trie layer?
StorageSlotKey storageSlotKey =
new StorageSlotKey(
world.getPreImageProxy().hashAndSaveSlotKeyPreImage(storageKey),
Optional.of(storageKey));
new StorageSlotKey(slotCache.get(storageKey, Hash::hash), Optional.of(storageKey));
final Map<StorageSlotKey, DiffBasedValue<UInt256>> localAccountStorage =
storageToUpdate.get(address);
if (localAccountStorage != null) {
@ -615,11 +615,6 @@ public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffB
return wrappedWorldView().getWorldStateStorage();
}
@Override
public CachingPreImageStorage getPreImageProxy() {
return wrappedWorldView().getPreImageProxy();
}
public void rollForward(final TrieLog layer) {
layer
.getAccountChanges()
@ -885,11 +880,6 @@ public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffB
deletedAccounts.clear();
}
protected Hash hashAndSaveAccountPreImage(final Address address) {
// default to using address static hash cache:
return getPreImageProxy().hashAndSaveAddressPreImage(address);
}
public abstract DiffBasedWorldStateUpdateAccumulator<ACCOUNT> copy();
protected abstract ACCOUNT copyAccount(final ACCOUNT account);

@ -28,6 +28,14 @@ public interface WorldStatePreimageStorage {
Optional<Address> 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 {

@ -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);

@ -248,7 +248,7 @@ public class BonsaiReferenceTestWorldState extends BonsaiWorldState
}
@Override
public synchronized void saveTrieLog(
public synchronized Optional<TrieLog> 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

@ -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')

@ -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();
}

@ -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();
}

@ -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(

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

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

@ -162,6 +162,11 @@ public class LimitedInMemoryKeyValueStorage implements KeyValueStorage {
return false;
}
@Override
public boolean isPersistent() {
return false;
}
private class MemoryTransaction implements KeyValueStorageTransaction {
private Map<Bytes, byte[]> updatedValues = new HashMap<>();

@ -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:

@ -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.");

Loading…
Cancel
Save