diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/flat/ArchiveCodeStorageStrategy.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/flat/ArchiveCodeStorageStrategy.java deleted file mode 100644 index ad4219faae7..00000000000 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/storage/flat/ArchiveCodeStorageStrategy.java +++ /dev/null @@ -1,101 +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.flat; - -import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.CODE_STORAGE; -import static org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.flat.ArchiveFlatDbStrategy.DELETED_CODE_VALUE; -import static org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.flat.ArchiveFlatDbStrategy.calculateArchiveKeyWithMinSuffix; - -import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.ethereum.bonsai.BonsaiContext; -import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.flat.CodeStorageStrategy; -import org.hyperledger.besu.plugin.services.storage.SegmentedKeyValueStorage; -import org.hyperledger.besu.plugin.services.storage.SegmentedKeyValueStorageTransaction; - -import java.util.Optional; - -import org.apache.tuweni.bytes.Bytes; -import org.bouncycastle.util.Arrays; - -public class ArchiveCodeStorageStrategy implements CodeStorageStrategy { - - private final BonsaiContext context; - - public ArchiveCodeStorageStrategy(final BonsaiContext context) { - this.context = context; - } - - /* - * Retrieves the code data for the given code hash and account hash and block context. - */ - @Override - public Optional getFlatCode( - final Hash codeHash, final Hash accountHash, final SegmentedKeyValueStorage storage) { - if (codeHash.equals(Hash.EMPTY)) { - return Optional.of(Bytes.EMPTY); - } else { - - // keyNearest, use MAX_BLOCK_SUFFIX in the absence of a block context: - Bytes keyNearest = - ArchiveFlatDbStrategy.calculateArchiveKeyWithMaxSuffix( - context, accountHash.toArrayUnsafe()); - - // use getNearest() with an account key that is suffixed by the block context - final Optional codeFound = - storage - .getNearestBefore(CODE_STORAGE, keyNearest) - // return empty when we find a "deleted value key" - .filter( - found -> - !Arrays.areEqual( - DELETED_CODE_VALUE, found.value().orElse(DELETED_CODE_VALUE))) - // map NearestKey to Bytes-wrapped value - .flatMap(SegmentedKeyValueStorage.NearestKeyValue::wrapBytes) - // check codeHash to sanity check the value and ensure we have the correct nearestKey: - .filter(b -> Hash.hash(b).equals(codeHash)); - - return codeFound; - } - } - - /* - * Puts the code data for the given code hash and account hash and block context. - */ - @Override - public void putFlatCode( - final SegmentedKeyValueStorageTransaction transaction, - final Hash accountHash, - final Hash codeHash, - final Bytes code) { - // key suffixed with block context, or MIN_BLOCK_SUFFIX if we have no context: - byte[] keySuffixed = calculateArchiveKeyWithMinSuffix(context, accountHash.toArrayUnsafe()); - - transaction.put(CODE_STORAGE, keySuffixed, code.toArrayUnsafe()); - } - - /* - * Adds a "deleted key" code entry for the given account hash and block context. - */ - @Override - public void removeFlatCode( - final SegmentedKeyValueStorageTransaction transaction, - final Hash accountHash, - final Hash codeHash) { - // insert a key suffixed with block context, with 'deleted account' value - byte[] keySuffixed = calculateArchiveKeyWithMinSuffix(context, accountHash.toArrayUnsafe()); - - transaction.put(CODE_STORAGE, keySuffixed, DELETED_CODE_VALUE); - } -} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/storage/flat/FlatDbStrategyProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/storage/flat/FlatDbStrategyProvider.java index 5640deb3c4f..dbcae20c07d 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/storage/flat/FlatDbStrategyProvider.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/storage/flat/FlatDbStrategyProvider.java @@ -19,7 +19,6 @@ import static org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedWorldStateKeyValueStorage.WORLD_ROOT_HASH_KEY; import org.hyperledger.besu.ethereum.bonsai.BonsaiContext; -import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.flat.ArchiveCodeStorageStrategy; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.flat.ArchiveFlatDbStrategy; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.flat.FullFlatDbStrategy; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.flat.PartialFlatDbStrategy; @@ -70,8 +69,7 @@ public void loadFlatDbStrategy(final SegmentedKeyValueStorage composedWorldState } else if (flatDbMode == FlatDbMode.ARCHIVE) { final BonsaiContext context = new BonsaiContext(); this.flatDbStrategy = - new ArchiveFlatDbStrategy( - context, metricsSystem, new ArchiveCodeStorageStrategy(context)); + new ArchiveFlatDbStrategy(context, metricsSystem, codeStorageStrategy); } else { this.flatDbStrategy = new PartialFlatDbStrategy(metricsSystem, codeStorageStrategy); } @@ -175,9 +173,16 @@ public FlatDbStrategy getFlatDbStrategy( public void upgradeToFullFlatDbMode(final SegmentedKeyValueStorage composedWorldStateStorage) { final SegmentedKeyValueStorageTransaction transaction = composedWorldStateStorage.startTransaction(); - LOG.info("setting FlatDbStrategy to FULL"); - transaction.put( - TRIE_BRANCH_STORAGE, FLAT_DB_MODE, FlatDbMode.FULL.getVersion().toArrayUnsafe()); + if (dataStorageConfiguration.getDataStorageFormat() == DataStorageFormat.BONSAI) { + LOG.info("setting FlatDbStrategy to FULL"); + transaction.put( + TRIE_BRANCH_STORAGE, FLAT_DB_MODE, FlatDbMode.FULL.getVersion().toArrayUnsafe()); + } else if (dataStorageConfiguration.getDataStorageFormat() + == DataStorageFormat.BONSAI_ARCHIVE) { + LOG.info("setting FlatDbStrategy to ARCHIVE"); + transaction.put( + TRIE_BRANCH_STORAGE, FLAT_DB_MODE, FlatDbMode.ARCHIVE.getVersion().toArrayUnsafe()); + } transaction.commit(); loadFlatDbStrategy(composedWorldStateStorage); // force reload of flat db reader strategy } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/common/storage/flat/FlatDbStrategyProviderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/common/storage/flat/FlatDbStrategyProviderTest.java index b3c709b89f2..12ee5b1ef07 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/common/storage/flat/FlatDbStrategyProviderTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/common/storage/flat/FlatDbStrategyProviderTest.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier; +import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.flat.ArchiveFlatDbStrategy; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.flat.FullFlatDbStrategy; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.flat.PartialFlatDbStrategy; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; @@ -45,6 +46,9 @@ class FlatDbStrategyProviderTest { private final FlatDbStrategyProvider flatDbStrategyProvider = new FlatDbStrategyProvider(new NoOpMetricsSystem(), DataStorageConfiguration.DEFAULT_CONFIG); + private final FlatDbStrategyProvider archiveFlatDbStrategyProvider = + new FlatDbStrategyProvider( + new NoOpMetricsSystem(), DataStorageConfiguration.DEFAULT_BONSAI_ARCHIVE_CONFIG); private final SegmentedKeyValueStorage composedWorldStateStorage = new SegmentedInMemoryKeyValueStorage( List.of( @@ -75,10 +79,25 @@ void upgradesFlatDbStrategyToFullFlatDbMode() { assertThat(flatDbStrategyProvider.flatDbStrategy).isNotNull(); assertThat(flatDbStrategyProvider.getFlatDbStrategy(composedWorldStateStorage)) .isInstanceOf(FullFlatDbStrategy.class); + assertThat(flatDbStrategyProvider.getFlatDbStrategy(composedWorldStateStorage)) + .isNotInstanceOf(ArchiveFlatDbStrategy.class); assertThat(flatDbStrategyProvider.flatDbStrategy.codeStorageStrategy) .isInstanceOf(CodeHashCodeStorageStrategy.class); } + @Test + void upgradesFlatDbStrategyToArchiveFlatDbMode() { + updateFlatDbMode(FlatDbMode.PARTIAL); + + archiveFlatDbStrategyProvider.upgradeToFullFlatDbMode(composedWorldStateStorage); + assertThat(archiveFlatDbStrategyProvider.flatDbMode).isEqualTo(FlatDbMode.ARCHIVE); + assertThat(archiveFlatDbStrategyProvider.flatDbStrategy).isNotNull(); + assertThat(archiveFlatDbStrategyProvider.getFlatDbStrategy(composedWorldStateStorage)) + .isInstanceOf(ArchiveFlatDbStrategy.class); + assertThat(archiveFlatDbStrategyProvider.flatDbStrategy.codeStorageStrategy) + .isInstanceOf(CodeHashCodeStorageStrategy.class); + } + @ParameterizedTest @ValueSource(booleans = {false, true}) void emptyDbCreatesFlatDbStrategyUsingCodeByHashConfig(final boolean codeByHashEnabled) { @@ -104,6 +123,31 @@ void emptyDbCreatesFlatDbStrategyUsingCodeByHashConfig(final boolean codeByHashE .isInstanceOf(expectedCodeStorageClass); } + @ParameterizedTest + @ValueSource(booleans = {false, true}) + void emptyDbCreatesArchiveFlatDbStrategyUsingCodeByHashConfig(final boolean codeByHashEnabled) { + final DataStorageConfiguration dataStorageConfiguration = + ImmutableDataStorageConfiguration.builder() + .dataStorageFormat(DataStorageFormat.BONSAI_ARCHIVE) + .bonsaiMaxLayersToLoad(DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD) + .unstable( + ImmutableDataStorageConfiguration.Unstable.builder() + .bonsaiCodeStoredByCodeHashEnabled(codeByHashEnabled) + .build()) + .build(); + final FlatDbStrategyProvider flatDbStrategyProvider = + new FlatDbStrategyProvider(new NoOpMetricsSystem(), dataStorageConfiguration); + + flatDbStrategyProvider.loadFlatDbStrategy(composedWorldStateStorage); + final Class expectedCodeStorageClass = + codeByHashEnabled + ? CodeHashCodeStorageStrategy.class + : AccountHashCodeStorageStrategy.class; + assertThat(flatDbStrategyProvider.flatDbMode).isEqualTo(FlatDbMode.ARCHIVE); + assertThat(flatDbStrategyProvider.flatDbStrategy.codeStorageStrategy) + .isInstanceOf(expectedCodeStorageClass); + } + @ParameterizedTest @ValueSource(booleans = {false, true}) void existingAccountHashDbUsesAccountHash(final boolean codeByHashEnabled) { @@ -134,6 +178,36 @@ void existingAccountHashDbUsesAccountHash(final boolean codeByHashEnabled) { .isInstanceOf(AccountHashCodeStorageStrategy.class); } + @ParameterizedTest + @ValueSource(booleans = {false, true}) + void existingAccountHashArchiveDbUsesAccountHash(final boolean codeByHashEnabled) { + final DataStorageConfiguration dataStorageConfiguration = + ImmutableDataStorageConfiguration.builder() + .dataStorageFormat(DataStorageFormat.BONSAI_ARCHIVE) + .bonsaiMaxLayersToLoad(DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD) + .unstable( + ImmutableDataStorageConfiguration.Unstable.builder() + .bonsaiCodeStoredByCodeHashEnabled(codeByHashEnabled) + .build()) + .build(); + final FlatDbStrategyProvider flatDbStrategyProvider = + new FlatDbStrategyProvider(new NoOpMetricsSystem(), dataStorageConfiguration); + + final SegmentedKeyValueStorageTransaction transaction = + composedWorldStateStorage.startTransaction(); + final AccountHashCodeStorageStrategy accountHashCodeStorageStrategy = + new AccountHashCodeStorageStrategy(); + // key representing account hash just needs to not be the code hash + final Hash accountHash = Hash.wrap(Bytes32.fromHexString("0001")); + accountHashCodeStorageStrategy.putFlatCode(transaction, accountHash, null, Bytes.of(2)); + transaction.commit(); + + flatDbStrategyProvider.loadFlatDbStrategy(composedWorldStateStorage); + assertThat(flatDbStrategyProvider.flatDbMode).isEqualTo(FlatDbMode.ARCHIVE); + assertThat(flatDbStrategyProvider.flatDbStrategy.codeStorageStrategy) + .isInstanceOf(AccountHashCodeStorageStrategy.class); + } + @ParameterizedTest @ValueSource(booleans = {false, true}) void existingCodeHashDbUsesCodeHash(final boolean codeByHashEnabled) { @@ -163,6 +237,35 @@ void existingCodeHashDbUsesCodeHash(final boolean codeByHashEnabled) { .isInstanceOf(CodeHashCodeStorageStrategy.class); } + @ParameterizedTest + @ValueSource(booleans = {false, true}) + void existingCodeHashArchiveDbUsesCodeHash(final boolean codeByHashEnabled) { + final DataStorageConfiguration dataStorageConfiguration = + ImmutableDataStorageConfiguration.builder() + .dataStorageFormat(DataStorageFormat.BONSAI_ARCHIVE) + .bonsaiMaxLayersToLoad(DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD) + .unstable( + ImmutableDataStorageConfiguration.Unstable.builder() + .bonsaiCodeStoredByCodeHashEnabled(codeByHashEnabled) + .build()) + .build(); + final FlatDbStrategyProvider flatDbStrategyProvider = + new FlatDbStrategyProvider(new NoOpMetricsSystem(), dataStorageConfiguration); + + final SegmentedKeyValueStorageTransaction transaction = + composedWorldStateStorage.startTransaction(); + + final CodeHashCodeStorageStrategy codeHashCodeStorageStrategy = + new CodeHashCodeStorageStrategy(); + codeHashCodeStorageStrategy.putFlatCode(transaction, null, Hash.hash(Bytes.of(1)), Bytes.of(1)); + transaction.commit(); + + flatDbStrategyProvider.loadFlatDbStrategy(composedWorldStateStorage); + assertThat(flatDbStrategyProvider.flatDbMode).isEqualTo(FlatDbMode.ARCHIVE); + assertThat(flatDbStrategyProvider.flatDbStrategy.codeStorageStrategy) + .isInstanceOf(CodeHashCodeStorageStrategy.class); + } + @Test void downgradesFlatDbStrategyToPartiallyFlatDbMode() { updateFlatDbMode(FlatDbMode.FULL); @@ -174,6 +277,17 @@ void downgradesFlatDbStrategyToPartiallyFlatDbMode() { .isInstanceOf(PartialFlatDbStrategy.class); } + @Test + void downgradesArchiveFlatDbStrategyToPartiallyFlatDbMode() { + updateFlatDbMode(FlatDbMode.ARCHIVE); + + flatDbStrategyProvider.downgradeToPartialFlatDbMode(composedWorldStateStorage); + assertThat(flatDbStrategyProvider.flatDbMode).isEqualTo(FlatDbMode.PARTIAL); + assertThat(flatDbStrategyProvider.flatDbStrategy).isNotNull(); + assertThat(flatDbStrategyProvider.getFlatDbStrategy(composedWorldStateStorage)) + .isInstanceOf(PartialFlatDbStrategy.class); + } + private void updateFlatDbMode(final FlatDbMode flatDbMode) { final SegmentedKeyValueStorageTransaction transaction = composedWorldStateStorage.startTransaction();