diff --git a/CHANGELOG.md b/CHANGELOG.md index 13d78dc36ab..7ad0f0eba69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Deprecated `--min-block-occupancy-ratio` for removal and make it noop. That option, that is ignored on PoS networks, is related to the deprecated PoW, and allowed to broadcast a mined block as soon as it reached a satisfying fill threshold. The option is still recognized, but it has no effect and will be completely removed in a future release. [#10036](https://github.com/besu-eth/besu/pull/10036) - Plugin API - Removed `TransactionSelectionResult.BLOCK_OCCUPANCY_ABOVE_THRESHOLD`, in general it could be replaced with `BLOCK_FULL` +- Experimental Bonsai Archive column families have changed to improve performance during bonsai to archive migration. If you are using the Bonsai archive you will need to do a full resync [#10058](https://github.com/besu-eth/besu/pull/10058/changes) ### Upcoming Breaking Changes - RPC changes to enhance compatibility with other ELs diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueSegmentIdentifier.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueSegmentIdentifier.java index c072528fd5e..7968f3cc1d6 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueSegmentIdentifier.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueSegmentIdentifier.java @@ -51,6 +51,18 @@ public enum KeyValueSegmentIdentifier implements SegmentIdentifier { true, false, true), + ACCOUNT_INFO_STATE_FREEZER( + "ACCOUNT_INFO_STATE_FREEZER".getBytes(StandardCharsets.UTF_8), + EnumSet.of(X_BONSAI_ARCHIVE), + true, + false, + true), + ACCOUNT_STORAGE_FREEZER( + "ACCOUNT_STORAGE_FREEZER".getBytes(StandardCharsets.UTF_8), + EnumSet.of(X_BONSAI_ARCHIVE), + true, + false, + true), VARIABLES(new byte[] {11}), // formerly GOQUORUM_PRIVATE_WORLD_STATE // previously supported GoQuorum private states diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/storage/flat/BonsaiArchiveFlatDbStrategy.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/storage/flat/BonsaiArchiveFlatDbStrategy.java index 8e6f33669b1..a9ea82c151b 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/storage/flat/BonsaiArchiveFlatDbStrategy.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/storage/flat/BonsaiArchiveFlatDbStrategy.java @@ -14,10 +14,10 @@ */ package org.hyperledger.besu.ethereum.trie.pathbased.bonsai.storage.flat; -import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE; import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_ARCHIVE; +import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_FREEZER; import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_STORAGE_ARCHIVE; -import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_STORAGE_STORAGE; +import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_STORAGE_FREEZER; import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.TRIE_BRANCH_STORAGE; import static org.hyperledger.besu.ethereum.trie.pathbased.common.storage.PathBasedWorldStateKeyValueStorage.WORLD_BLOCK_NUMBER_KEY; @@ -133,7 +133,7 @@ public Optional getFlatAccount( // Find the nearest account state for this address and block context Optional nearestAccount = storage - .getNearestBefore(ACCOUNT_INFO_STATE, keyNearest) + .getNearestBefore(ACCOUNT_INFO_STATE_ARCHIVE, keyNearest) .filter( found -> accountHash.getBytes().commonPrefixLength(found.key()) @@ -143,7 +143,7 @@ public Optional getFlatAccount( if (nearestAccount.isEmpty()) { accountFound = storage - .getNearestBefore(ACCOUNT_INFO_STATE_ARCHIVE, keyNearest) + .getNearestBefore(ACCOUNT_INFO_STATE_FREEZER, keyNearest) .filter( found -> accountHash.getBytes().commonPrefixLength(found.key()) @@ -180,7 +180,7 @@ protected Stream> accountsToPairStream( final Stream> stream = storage .streamFromKey( - ACCOUNT_INFO_STATE, + ACCOUNT_INFO_STATE_ARCHIVE, calculateArchiveKeyNoContextMinSuffix(startKeyHash.toArrayUnsafe()), calculateArchiveKeyNoContextMaxSuffix(endKeyHash.toArrayUnsafe())) .map(e -> Bytes.of(calculateArchiveKeyNoContextMaxSuffix(trimSuffix(e.getKey())))) @@ -190,7 +190,11 @@ protected Stream> accountsToPairStream( new Pair<>( Bytes32.wrap(trimSuffix(e.toArrayUnsafe())), Bytes.of( - storage.getNearestBefore(ACCOUNT_INFO_STATE, e).get().value().get()))); + storage + .getNearestBefore(ACCOUNT_INFO_STATE_ARCHIVE, e) + .get() + .value() + .get()))); return stream; } @@ -200,7 +204,7 @@ protected Stream> accountsToPairStream( final Stream> stream = storage .streamFromKey( - ACCOUNT_INFO_STATE, + ACCOUNT_INFO_STATE_ARCHIVE, calculateArchiveKeyNoContextMinSuffix(startKeyHash.toArrayUnsafe())) .map(e -> Bytes.of(calculateArchiveKeyNoContextMaxSuffix(trimSuffix(e.getKey())))) .distinct() @@ -209,7 +213,11 @@ protected Stream> accountsToPairStream( new Pair( Bytes32.wrap(trimSuffix(e.toArrayUnsafe())), Bytes.of( - storage.getNearestBefore(ACCOUNT_INFO_STATE, e).get().value().get()))); + storage + .getNearestBefore(ACCOUNT_INFO_STATE_ARCHIVE, e) + .get() + .value() + .get()))); return stream; } @@ -221,7 +229,7 @@ protected Stream> storageToPairStream( final Function valueMapper) { return storage .streamFromKey( - ACCOUNT_STORAGE_STORAGE, + ACCOUNT_STORAGE_ARCHIVE, calculateArchiveKeyNoContextMinSuffix( calculateNaturalSlotKey(accountHash, Hash.wrap(Bytes32.wrap(startKeyHash))))) .map(e -> Bytes.of(calculateArchiveKeyNoContextMaxSuffix(trimSuffix(e.getKey())))) @@ -234,7 +242,7 @@ protected Stream> storageToPairStream( valueMapper.apply( Bytes.of( storage - .getNearestBefore(ACCOUNT_STORAGE_STORAGE, key) + .getNearestBefore(ACCOUNT_STORAGE_ARCHIVE, key) .get() .value() .get()) @@ -250,7 +258,7 @@ protected Stream> storageToPairStream( final Function valueMapper) { return storage .streamFromKey( - ACCOUNT_STORAGE_STORAGE, + ACCOUNT_STORAGE_ARCHIVE, calculateArchiveKeyNoContextMinSuffix( calculateNaturalSlotKey(accountHash, Hash.wrap(Bytes32.wrap(startKeyHash)))), calculateArchiveKeyNoContextMaxSuffix( @@ -265,7 +273,7 @@ protected Stream> storageToPairStream( valueMapper.apply( Bytes.of( storage - .getNearestBefore(ACCOUNT_STORAGE_STORAGE, key) + .getNearestBefore(ACCOUNT_STORAGE_ARCHIVE, key) .get() .value() .get()) @@ -287,7 +295,7 @@ public void putFlatAccount( calculateArchiveKeyWithMinSuffix( getStateArchiveContextForWrite(storage).get(), accountHash.getBytes().toArrayUnsafe()); - transaction.put(ACCOUNT_INFO_STATE, keySuffixed, accountValue.toArrayUnsafe()); + transaction.put(ACCOUNT_INFO_STATE_ARCHIVE, keySuffixed, accountValue.toArrayUnsafe()); } @Override @@ -301,7 +309,7 @@ public void removeFlatAccount( calculateArchiveKeyWithMinSuffix( getStateArchiveContextForWrite(storage).get(), accountHash.getBytes().toArrayUnsafe()); - transaction.put(ACCOUNT_INFO_STATE, keySuffixed, DELETED_ACCOUNT_VALUE); + transaction.put(ACCOUNT_INFO_STATE_ARCHIVE, keySuffixed, DELETED_ACCOUNT_VALUE); } private byte[] trimSuffix(final byte[] suffixedAddress) { @@ -332,7 +340,7 @@ public Optional getFlatStorageValueByStorageSlotKey( // Find the nearest storage for this address, slot key hash, and block context Optional nearestStorage = storage - .getNearestBefore(ACCOUNT_STORAGE_STORAGE, keyNearest) + .getNearestBefore(ACCOUNT_STORAGE_ARCHIVE, keyNearest) .filter( found -> Bytes.of(naturalKey).commonPrefixLength(found.key()) >= naturalKey.length); @@ -341,7 +349,7 @@ public Optional getFlatStorageValueByStorageSlotKey( // Check the archived storage as old state is moved out of the primary DB segment storageFound = storage - .getNearestBefore(ACCOUNT_STORAGE_ARCHIVE, keyNearest) + .getNearestBefore(ACCOUNT_STORAGE_FREEZER, keyNearest) // don't return accounts that do not have a matching account hash .filter( found -> @@ -389,7 +397,7 @@ public void putFlatAccountStorageValueByStorageSlotHash( byte[] keyNearest = calculateArchiveKeyWithMinSuffix(getStateArchiveContextForWrite(storage).get(), naturalKey); - transaction.put(ACCOUNT_STORAGE_STORAGE, keyNearest, storageValue.toArrayUnsafe()); + transaction.put(ACCOUNT_STORAGE_ARCHIVE, keyNearest, storageValue.toArrayUnsafe()); } /* @@ -408,7 +416,7 @@ public void removeFlatAccountStorageValueByStorageSlotHash( byte[] keySuffixed = calculateArchiveKeyWithMinSuffix(getStateArchiveContextForWrite(storage).get(), naturalKey); - transaction.put(ACCOUNT_STORAGE_STORAGE, keySuffixed, DELETED_STORAGE_VALUE); + transaction.put(ACCOUNT_STORAGE_ARCHIVE, keySuffixed, DELETED_STORAGE_VALUE); } public static byte[] calculateNaturalSlotKey(final Hash accountHash, final Hash slotHash) { @@ -433,6 +441,27 @@ public static Bytes calculateArchiveKeyWithMaxSuffix( return Bytes.of(calculateArchiveKeyWithSuffix(context, naturalKey, MAX_BLOCK_SUFFIX)); } + @Override + public void clearAll(final SegmentedKeyValueStorage storage) { + clearArchiveSegments(storage); + // Then call parent to clear other segments + super.clearAll(storage); + } + + @Override + public void resetOnResync(final SegmentedKeyValueStorage storage) { + clearArchiveSegments(storage); + // Then call parent to reset other segments + super.resetOnResync(storage); + } + + private static void clearArchiveSegments(final SegmentedKeyValueStorage storage) { + storage.clear(ACCOUNT_INFO_STATE_ARCHIVE); + storage.clear(ACCOUNT_STORAGE_ARCHIVE); + storage.clear(ACCOUNT_INFO_STATE_FREEZER); + storage.clear(ACCOUNT_STORAGE_FREEZER); + } + // TODO JF: move this out of this class so can be used with ArchiveCodeStorageStrategy without // being static public static byte[] calculateArchiveKeyWithSuffix( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/pathbased/common/storage/PathBasedWorldStateKeyValueStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/pathbased/common/storage/PathBasedWorldStateKeyValueStorage.java index 57274456da8..363a057fdca 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/pathbased/common/storage/PathBasedWorldStateKeyValueStorage.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/pathbased/common/storage/PathBasedWorldStateKeyValueStorage.java @@ -16,7 +16,9 @@ import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE; import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_ARCHIVE; +import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_FREEZER; import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_STORAGE_ARCHIVE; +import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_STORAGE_FREEZER; import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_STORAGE_STORAGE; import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.CODE_STORAGE; import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.TRIE_BRANCH_STORAGE; @@ -216,9 +218,9 @@ public boolean pruneTrieLog(final Hash blockHash) { } /** - * Move old account state from the primary DB segment to the archive segment that will only be - * used for historic state queries. This prevents performance degradation over time for writes to - * the primary DB segments. + * Move old account state from the primary DB archive segment to the archive freezer segment that + * will only be used for historic state queries. This prevents performance degradation over time + * for writes to the primary archive DB segments. * * @param previousBlockHeader the block header for the previous block, used to get the "nearest * before" state @@ -243,7 +245,7 @@ public int archivePreviousAccountState( // Move all entries that match this address hash to the archive DB segment while ((nextMatch = composedWorldStateStorage - .getNearestBefore(ACCOUNT_INFO_STATE, previousKey) + .getNearestBefore(ACCOUNT_INFO_STATE_ARCHIVE, previousKey) .filter( found -> found.value().isPresent() @@ -254,8 +256,8 @@ public int archivePreviousAccountState( .forEach( (nearestKey) -> { moveDBEntry( - ACCOUNT_INFO_STATE, ACCOUNT_INFO_STATE_ARCHIVE, + ACCOUNT_INFO_STATE_FREEZER, nearestKey.key().toArrayUnsafe(), nearestKey.value().get()); archivedStateCount.getAndIncrement(); @@ -287,9 +289,9 @@ public int archivePreviousAccountState( } /** - * Move old storage state from the primary DB segment to the archive segment that will only be - * used for historic state queries. This prevents performance degradation over time for writes to - * the primary DB segments. + * Move old storage state from the primary archive DB segment to the archive freezer segment that + * will only be used for historic state queries. This prevents performance degradation over time + * for writes to the primary DB segments. * * @param previousBlockHeader the block header for the previous block, used to get the "nearest * before" state @@ -315,7 +317,7 @@ public int archivePreviousStorageState( // to the archive DB segment while ((nextMatch = composedWorldStateStorage - .getNearestBefore(ACCOUNT_STORAGE_STORAGE, previousKey) + .getNearestBefore(ACCOUNT_STORAGE_ARCHIVE, previousKey) .filter( found -> found.value().isPresent() @@ -338,8 +340,8 @@ public int archivePreviousStorageState( .log(); } moveDBEntry( - ACCOUNT_STORAGE_STORAGE, ACCOUNT_STORAGE_ARCHIVE, + ACCOUNT_STORAGE_FREEZER, nearestKey.key().toArrayUnsafe(), nearestKey.value().get()); archivedStorageCount.getAndIncrement(); @@ -395,7 +397,7 @@ private void moveDBEntry( public Optional getLatestArchivedBlock() { return composedWorldStateStorage - .get(ACCOUNT_INFO_STATE_ARCHIVE, ARCHIVED_BLOCKS) + .get(ACCOUNT_INFO_STATE_FREEZER, ARCHIVED_BLOCKS) .map(Bytes::wrap) .map(Bytes::toLong); } @@ -403,7 +405,7 @@ public Optional getLatestArchivedBlock() { public void setLatestArchivedBlock(final Long blockNumber) { SegmentedKeyValueStorageTransaction tx = composedWorldStateStorage.startTransaction(); tx.put( - ACCOUNT_INFO_STATE_ARCHIVE, + ACCOUNT_INFO_STATE_FREEZER, ARCHIVED_BLOCKS, Bytes.ofUnsignedLong(blockNumber).toArrayUnsafe()); tx.commit(); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/storage/BonsaiWorldStateKeyValueStorageTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/storage/BonsaiWorldStateKeyValueStorageTest.java index 11c740999a3..944dca41967 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/storage/BonsaiWorldStateKeyValueStorageTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/storage/BonsaiWorldStateKeyValueStorageTest.java @@ -15,7 +15,9 @@ package org.hyperledger.besu.ethereum.trie.pathbased.bonsai.storage; import static org.assertj.core.api.Assertions.assertThat; +import static org.bouncycastle.util.Arrays.concatenate; import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE; +import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_ARCHIVE; import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.TRIE_BRANCH_STORAGE; import static org.hyperledger.besu.ethereum.trie.pathbased.common.storage.PathBasedWorldStateKeyValueStorage.WORLD_BLOCK_NUMBER_KEY; import static org.hyperledger.besu.ethereum.trie.pathbased.common.storage.PathBasedWorldStateKeyValueStorage.WORLD_ROOT_HASH_KEY; @@ -78,18 +80,17 @@ public static Collection flatDbMode() { new Object[][] {{FlatDbMode.FULL}, {FlatDbMode.PARTIAL}, {FlatDbMode.ARCHIVE}}); } - public static Stream flatDbModeAndKeyMapper() { + public static Stream flatDbModeKeyMapperAndSegment() { Function flatDBKey = (key) -> key; // No-op // For archive we want <32-byte-hex>000000000000000n where n is the current archive block number Function flatDBArchiveKey = - (key) -> - org.bouncycastle.util.Arrays.concatenate(key, Bytes.ofUnsignedLong(2).toArrayUnsafe()); + (key) -> concatenate(key, Bytes.ofUnsignedLong(2).toArrayUnsafe()); return Stream.of( - Arguments.of(FlatDbMode.FULL, flatDBKey), - Arguments.of(FlatDbMode.PARTIAL, flatDBKey), - Arguments.of(FlatDbMode.ARCHIVE, flatDBArchiveKey)); + Arguments.of(FlatDbMode.FULL, flatDBKey, ACCOUNT_INFO_STATE), + Arguments.of(FlatDbMode.PARTIAL, flatDBKey, ACCOUNT_INFO_STATE), + Arguments.of(FlatDbMode.ARCHIVE, flatDBArchiveKey, ACCOUNT_INFO_STATE_ARCHIVE)); } public static Collection flatDbModeAndCodeStorageMode() { @@ -461,9 +462,11 @@ void clear_reloadFlatDbStrategy(final FlatDbMode flatDbMode) { } @ParameterizedTest - @MethodSource("flatDbModeAndKeyMapper") + @MethodSource("flatDbModeKeyMapperAndSegment") void clear_putGetAccountFlatDbStrategy( - final FlatDbMode flatDbMode, final Function keyMapper) { + final FlatDbMode flatDbMode, + final Function keyMapper, + final KeyValueSegmentIdentifier segment) { final BonsaiWorldStateKeyValueStorage storage = spy(setUp(flatDbMode)); // save world state root hash @@ -493,9 +496,7 @@ void clear_putGetAccountFlatDbStrategy( // and flat archive DB // and we want to ensure keys put to the archive DB include the archive block context/suffix byte[] lookupKey = keyMapper.apply(account.addressHash().getBytes().toArrayUnsafe()); - assertThat( - Bytes.wrap( - storage.getComposedWorldStateStorage().get(ACCOUNT_INFO_STATE, lookupKey).get())) + assertThat(Bytes.wrap(storage.getComposedWorldStateStorage().get(segment, lookupKey).get())) .isEqualTo( Bytes.fromHexString( "0xF84E823D98887B5E41A364EA8BFCA056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470")); @@ -518,9 +519,11 @@ void clear_putGetAccountFlatDbStrategy( } @ParameterizedTest - @MethodSource({"flatDbModeAndKeyMapper"}) + @MethodSource({"flatDbModeKeyMapperAndSegment"}) void clear_streamFlatAccounts( - final FlatDbMode flatDbMode, final Function keyMapper) { + final FlatDbMode flatDbMode, + final Function keyMapper, + final KeyValueSegmentIdentifier segment) { final BonsaiWorldStateKeyValueStorage storage = spy(setUp(flatDbMode)); // save world state root hash @@ -549,19 +552,13 @@ void clear_streamFlatAccounts( // Convert the key to lookup the entry we expect to find in K/V storage. No-op for everything // except ARCHIVE, which needs to append the 000000000000000x suffix to the key byte[] lookupKey = keyMapper.apply(account1.addressHash().getBytes().toArrayUnsafe()); - assertThat( - Bytes32.wrap( - storage.getComposedWorldStateStorage().get(ACCOUNT_INFO_STATE, lookupKey).get())) + assertThat(Bytes32.wrap(storage.getComposedWorldStateStorage().get(segment, lookupKey).get())) .isEqualTo(account1Value); lookupKey = keyMapper.apply(account2.addressHash().getBytes().toArrayUnsafe()); - assertThat( - Bytes32.wrap( - storage.getComposedWorldStateStorage().get(ACCOUNT_INFO_STATE, lookupKey).get())) + assertThat(Bytes32.wrap(storage.getComposedWorldStateStorage().get(segment, lookupKey).get())) .isEqualTo(account2Value); lookupKey = keyMapper.apply(account3.addressHash().getBytes().toArrayUnsafe()); - assertThat( - Bytes32.wrap( - storage.getComposedWorldStateStorage().get(ACCOUNT_INFO_STATE, lookupKey).get())) + assertThat(Bytes32.wrap(storage.getComposedWorldStateStorage().get(segment, lookupKey).get())) .isEqualTo(account3Value); // Streaming the entire range to ensure we get all 3 accounts back diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/storage/flat/BonsaiArchiveFlatDbStrategyTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/storage/flat/BonsaiArchiveFlatDbStrategyTest.java index dabf34d14d7..b3b85185f35 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/storage/flat/BonsaiArchiveFlatDbStrategyTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/storage/flat/BonsaiArchiveFlatDbStrategyTest.java @@ -15,7 +15,9 @@ package org.hyperledger.besu.ethereum.trie.pathbased.bonsai.storage.flat; import static org.assertj.core.api.Assertions.assertThat; -import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE; +import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_ARCHIVE; +import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_FREEZER; +import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_STORAGE_FREEZER; import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.TRIE_BRANCH_STORAGE; import static org.hyperledger.besu.ethereum.trie.pathbased.common.storage.PathBasedWorldStateKeyValueStorage.WORLD_BLOCK_NUMBER_KEY; @@ -30,6 +32,7 @@ import java.util.Optional; import org.apache.tuweni.bytes.Bytes; +import org.bouncycastle.util.Arrays; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -57,7 +60,7 @@ public void genesisBlockUsesZeroSuffixWhenWorldBlockNumberKeyNotSet() { final byte[] expectedKey = Bytes.concatenate(accountHash.getBytes(), Bytes.ofUnsignedLong(0)).toArrayUnsafe(); - final Optional storedValue = storage.get(ACCOUNT_INFO_STATE, expectedKey); + final Optional storedValue = storage.get(ACCOUNT_INFO_STATE_ARCHIVE, expectedKey); assertThat(storedValue).isPresent(); assertThat(Bytes.wrap(storedValue.get())).isEqualTo(accountValue); @@ -77,14 +80,14 @@ public void block1UsesOneSuffixWhenWorldBlockNumberKeyIsZero() { final byte[] expectedKey = Bytes.concatenate(accountHash.getBytes(), Bytes.ofUnsignedLong(1)).toArrayUnsafe(); - final Optional storedValue = storage.get(ACCOUNT_INFO_STATE, expectedKey); + final Optional storedValue = storage.get(ACCOUNT_INFO_STATE_ARCHIVE, expectedKey); assertThat(storedValue).isPresent(); assertThat(Bytes.wrap(storedValue.get())).isEqualTo(accountValue); final byte[] genesisKey = Bytes.concatenate(accountHash.getBytes(), Bytes.ofUnsignedLong(0)).toArrayUnsafe(); - assertThat(storage.get(ACCOUNT_INFO_STATE, genesisKey)).isEmpty(); + assertThat(storage.get(ACCOUNT_INFO_STATE_ARCHIVE, genesisKey)).isEmpty(); } @Test @@ -101,7 +104,7 @@ public void block2UsesTwoSuffixWhenWorldBlockNumberKeyIsOne() { final byte[] expectedKey = Bytes.concatenate(accountHash.getBytes(), Bytes.ofUnsignedLong(2)).toArrayUnsafe(); - final Optional storedValue = storage.get(ACCOUNT_INFO_STATE, expectedKey); + final Optional storedValue = storage.get(ACCOUNT_INFO_STATE_ARCHIVE, expectedKey); assertThat(storedValue).isPresent(); assertThat(Bytes.wrap(storedValue.get())).isEqualTo(accountValue); @@ -129,8 +132,8 @@ public void genesisAndBlock1AccountsDoNotOverwrite() { final byte[] block1Key = Bytes.concatenate(accountHash.getBytes(), Bytes.ofUnsignedLong(1)).toArrayUnsafe(); - final Optional genesisValue = storage.get(ACCOUNT_INFO_STATE, genesisKey); - final Optional block1Value = storage.get(ACCOUNT_INFO_STATE, block1Key); + final Optional genesisValue = storage.get(ACCOUNT_INFO_STATE_ARCHIVE, genesisKey); + final Optional block1Value = storage.get(ACCOUNT_INFO_STATE_ARCHIVE, block1Key); assertThat(genesisValue).isPresent(); assertThat(Bytes.wrap(genesisValue.get())).isEqualTo(genesisAccountValue); @@ -176,12 +179,92 @@ public void sequentialBlocksUseIncrementingSuffixes() { for (long blockNum = 0; blockNum <= 3; blockNum++) { final byte[] key = Bytes.concatenate(accountHash.getBytes(), Bytes.ofUnsignedLong(blockNum)).toArrayUnsafe(); - final Optional value = storage.get(ACCOUNT_INFO_STATE, key); + final Optional value = storage.get(ACCOUNT_INFO_STATE_ARCHIVE, key); assertThat(value).as("Block " + blockNum + " should have stored value").isPresent(); assertThat(Bytes.wrap(value.get())).isEqualTo(expectedValues[(int) blockNum]); } } + @Test + public void clearAll_removesDataFromAccountInfoStateFreezer() { + byte[] accountKey = + Hash.fromHexString("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") + .getBytes() + .toArrayUnsafe(); + byte[] accountValue = Bytes.fromHexString("0xAABBCCDD").toArrayUnsafe(); + SegmentedKeyValueStorageTransaction tx = storage.startTransaction(); + tx.put(ACCOUNT_INFO_STATE_FREEZER, accountKey, accountValue); + tx.commit(); + + assertThat(storage.get(ACCOUNT_INFO_STATE_FREEZER, accountKey)).isNotEmpty(); + + archiveFlatDbStrategy.clearAll(storage); + + assertThat(storage.get(ACCOUNT_INFO_STATE_FREEZER, accountKey)).isEmpty(); + } + + @Test + public void clearAll_removesDataFromAccountStorageFreezer() { + byte[] storageKey = + Arrays.concatenate( + Hash.fromHexString("0x1111111111111111111111111111111111111111111111111111111111111111") + .getBytes() + .toArrayUnsafe(), + Hash.fromHexString("0x2222222222222222222222222222222222222222222222222222222222222222") + .getBytes() + .toArrayUnsafe()); + byte[] storageValue = Bytes.fromHexString("0xdeadbeef").toArrayUnsafe(); + SegmentedKeyValueStorageTransaction tx = storage.startTransaction(); + tx.put(ACCOUNT_STORAGE_FREEZER, storageKey, storageValue); + tx.commit(); + + assertThat(storage.get(ACCOUNT_STORAGE_FREEZER, storageKey)).isNotEmpty(); + + archiveFlatDbStrategy.clearAll(storage); + + assertThat(storage.get(ACCOUNT_STORAGE_FREEZER, storageKey)).isEmpty(); + } + + @Test + public void resetOnResync_removesDataFromAccountInfoStateFreezer() { + byte[] accountKey = + Hash.fromHexString("0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890") + .getBytes() + .toArrayUnsafe(); + byte[] accountValue = Bytes.fromHexString("0x11223344").toArrayUnsafe(); + SegmentedKeyValueStorageTransaction tx = storage.startTransaction(); + tx.put(ACCOUNT_INFO_STATE_FREEZER, accountKey, accountValue); + tx.commit(); + + assertThat(storage.get(ACCOUNT_INFO_STATE_FREEZER, accountKey)).isNotEmpty(); + + archiveFlatDbStrategy.resetOnResync(storage); + + assertThat(storage.get(ACCOUNT_INFO_STATE_FREEZER, accountKey)).isEmpty(); + } + + @Test + public void resetOnResync_removesDataFromAccountStorageFreezer() { + byte[] storageKey = + Arrays.concatenate( + Hash.fromHexString("0x3333333333333333333333333333333333333333333333333333333333333333") + .getBytes() + .toArrayUnsafe(), + Hash.fromHexString("0x4444444444444444444444444444444444444444444444444444444444444444") + .getBytes() + .toArrayUnsafe()); + byte[] storageValue = Bytes.fromHexString("0xcafebabe").toArrayUnsafe(); + SegmentedKeyValueStorageTransaction tx = storage.startTransaction(); + tx.put(ACCOUNT_STORAGE_FREEZER, storageKey, storageValue); + tx.commit(); + + assertThat(storage.get(ACCOUNT_STORAGE_FREEZER, storageKey)).isNotEmpty(); + + archiveFlatDbStrategy.resetOnResync(storage); + + assertThat(storage.get(ACCOUNT_STORAGE_FREEZER, storageKey)).isEmpty(); + } + private void setWorldBlockNumber(final long blockNumber) { final SegmentedKeyValueStorageTransaction tx = storage.startTransaction(); tx.put( diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/common/trielog/ArchiverTests.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/worldview/BonsaiArchiverTests.java similarity index 96% rename from ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/common/trielog/ArchiverTests.java rename to ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/worldview/BonsaiArchiverTests.java index df49f147080..95b381dfb54 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/common/trielog/ArchiverTests.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/worldview/BonsaiArchiverTests.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.ethereum.trie.pathbased.common.trielog; +package org.hyperledger.besu.ethereum.trie.pathbased.bonsai.worldview; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -41,8 +41,8 @@ import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.cache.CodeCache; import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.storage.BonsaiPreImageProxy; import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.storage.BonsaiWorldStateKeyValueStorage; -import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.worldview.BonsaiArchiver; -import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.worldview.BonsaiWorldState; +import org.hyperledger.besu.ethereum.trie.pathbased.common.trielog.TrieLogLayer; +import org.hyperledger.besu.ethereum.trie.pathbased.common.trielog.TrieLogManager; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.FlatDbMode; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; @@ -66,7 +66,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class ArchiverTests { +public class BonsaiArchiverTests { // Number of blocks in the chain. This is different to the number of blocks // we have successfully archived state for @@ -109,7 +109,7 @@ public Optional load(final Hash blockHash) { @SuppressWarnings("BannedMethod") @BeforeEach public void setup() { - Configurator.setLevel(LogManager.getLogger(ArchiverTests.class).getName(), Level.TRACE); + Configurator.setLevel(LogManager.getLogger(BonsaiArchiverTests.class).getName(), Level.TRACE); worldStateStorage = Mockito.mock(BonsaiWorldStateKeyValueStorage.class); blockchain = Mockito.mock(Blockchain.class); trieLogManager = Mockito.mock(TrieLogManager.class); @@ -705,8 +705,8 @@ public Optional load(final Long blockNumber) { // Generate some trie logs to return for a specific block - // For state to be moved from the primary DB segment to the archive DB segment, we need the - // primary DB segment to have the account in already + // For state to be moved from the archive DB segment to the archive freezer DB segment, we need + // the archive DB segment to have the account in already SegmentedKeyValueStorageTransaction tx = testWorldStateStorage.getComposedWorldStateStorage().startTransaction(); final BonsaiAccount block150Account = @@ -747,7 +747,7 @@ public Optional load(final Long blockNumber) { BytesValueRLPOutput out = new BytesValueRLPOutput(); block150Account.writeTo(out); tx.put( - KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE, + KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_ARCHIVE, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), Bytes.fromHexString("0x0000000000000096").toArrayUnsafe()), @@ -755,7 +755,7 @@ public Optional load(final Long blockNumber) { out = new BytesValueRLPOutput(); block151Account.writeTo(out); tx.put( - KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE, + KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_ARCHIVE, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), Bytes.fromHexString("0x0000000000000097").toArrayUnsafe()), @@ -763,7 +763,7 @@ public Optional load(final Long blockNumber) { out = new BytesValueRLPOutput(); block152Account.writeTo(out); tx.put( - KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE, + KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_ARCHIVE, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), Bytes.fromHexString("0x0000000000000098").toArrayUnsafe()), @@ -852,18 +852,18 @@ public Optional load(final Hash blockHash) { // We should have marked up to block 200 as archived assertThat(testWorldStateStorage.getLatestArchivedBlock().get()).isEqualTo(200); - // Only the latest/current state of the account should be in the primary DB segment + // Only the latest/current state of the account should be in the archive DB segment assertThat( testWorldStateStorage.getComposedWorldStateStorage().stream( - KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE) + KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_ARCHIVE) .count()) .isEqualTo(1); - // Both the previous account states should be in the archive segment, plus the special key that - // records the latest archived block + // Both the previous account states should be in the archive freezer segment, plus the special + // key that records the latest archived block assertThat( testWorldStateStorage.getComposedWorldStateStorage().stream( - KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_ARCHIVE) + KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_FREEZER) .count()) .isEqualTo(3); @@ -872,7 +872,7 @@ public Optional load(final Hash blockHash) { testWorldStateStorage .getComposedWorldStateStorage() .containsKey( - KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_ARCHIVE, + KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_FREEZER, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), Bytes.fromHexString("0x0000000000000096").toArrayUnsafe()))) @@ -881,7 +881,7 @@ public Optional load(final Hash blockHash) { testWorldStateStorage .getComposedWorldStateStorage() .containsKey( - KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_ARCHIVE, + KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_FREEZER, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), Bytes.fromHexString("0x0000000000000097").toArrayUnsafe()))) @@ -890,7 +890,7 @@ public Optional load(final Hash blockHash) { testWorldStateStorage .getComposedWorldStateStorage() .containsKey( - KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE, + KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_ARCHIVE, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), Bytes.fromHexString("0x0000000000000098").toArrayUnsafe()))) @@ -945,36 +945,36 @@ public Optional load(final Long blockNumber) { // Generate some trie logs to return for a specific block - // For storage to be moved from the primary DB segment to the archive DB segment, we need the - // primary DB segment to have the storage in already + // For storage to be moved from the archive DB segment to the archive freezer DB segment, we + // need the archive DB segment to have the storage in already SegmentedKeyValueStorageTransaction tx = testWorldStateStorage.getComposedWorldStateStorage().startTransaction(); StorageSlotKey slotKey = new StorageSlotKey(UInt256.fromHexString("0x1")); // The key for a bonsai-archive flat DB storage entry is suffixed with the block number where // that state change took place, hence the "0x0000000000000096" suffix to the address hash below tx.put( - KeyValueSegmentIdentifier.ACCOUNT_STORAGE_STORAGE, + KeyValueSegmentIdentifier.ACCOUNT_STORAGE_ARCHIVE, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), slotKey.getSlotHash().getBytes().toArrayUnsafe(), Bytes.fromHexString("0x0000000000000096").toArrayUnsafe()), Bytes.fromHexString("0x0123").toArrayUnsafe()); tx.put( - KeyValueSegmentIdentifier.ACCOUNT_STORAGE_STORAGE, + KeyValueSegmentIdentifier.ACCOUNT_STORAGE_ARCHIVE, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), slotKey.getSlotHash().getBytes().toArrayUnsafe(), Bytes.fromHexString("0x0000000000000097").toArrayUnsafe()), Bytes.fromHexString("0x0234").toArrayUnsafe()); tx.put( - KeyValueSegmentIdentifier.ACCOUNT_STORAGE_STORAGE, + KeyValueSegmentIdentifier.ACCOUNT_STORAGE_ARCHIVE, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), slotKey.getSlotHash().getBytes().toArrayUnsafe(), Bytes.fromHexString("0x0000000000000098").toArrayUnsafe()), Bytes.fromHexString("0x0345").toArrayUnsafe()); tx.put( - KeyValueSegmentIdentifier.ACCOUNT_STORAGE_STORAGE, + KeyValueSegmentIdentifier.ACCOUNT_STORAGE_ARCHIVE, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), slotKey.getSlotHash().getBytes().toArrayUnsafe(), @@ -1080,17 +1080,17 @@ public Optional load(final Hash blockHash) { // We should have marked up to block 200 as archived assertThat(testWorldStateStorage.getLatestArchivedBlock().get()).isEqualTo(200); - // Only the latest/current state of the account should be in the primary DB segment + // Only the latest/current state of the account should be in the archive DB segment assertThat( testWorldStateStorage.getComposedWorldStateStorage().stream( - KeyValueSegmentIdentifier.ACCOUNT_STORAGE_STORAGE) + KeyValueSegmentIdentifier.ACCOUNT_STORAGE_ARCHIVE) .count()) .isEqualTo(1); - // All 3 previous storage states should be in the storage archiver + // All 3 previous storage states should be in the storage archive freezer assertThat( testWorldStateStorage.getComposedWorldStateStorage().stream( - KeyValueSegmentIdentifier.ACCOUNT_STORAGE_ARCHIVE) + KeyValueSegmentIdentifier.ACCOUNT_STORAGE_FREEZER) .count()) .isEqualTo(3); @@ -1099,7 +1099,7 @@ public Optional load(final Hash blockHash) { testWorldStateStorage .getComposedWorldStateStorage() .containsKey( - KeyValueSegmentIdentifier.ACCOUNT_STORAGE_ARCHIVE, + KeyValueSegmentIdentifier.ACCOUNT_STORAGE_FREEZER, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), slotKey.getSlotHash().getBytes().toArrayUnsafe(), @@ -1109,7 +1109,7 @@ public Optional load(final Hash blockHash) { testWorldStateStorage .getComposedWorldStateStorage() .containsKey( - KeyValueSegmentIdentifier.ACCOUNT_STORAGE_ARCHIVE, + KeyValueSegmentIdentifier.ACCOUNT_STORAGE_FREEZER, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), slotKey.getSlotHash().getBytes().toArrayUnsafe(), @@ -1119,7 +1119,7 @@ public Optional load(final Hash blockHash) { testWorldStateStorage .getComposedWorldStateStorage() .containsKey( - KeyValueSegmentIdentifier.ACCOUNT_STORAGE_ARCHIVE, + KeyValueSegmentIdentifier.ACCOUNT_STORAGE_FREEZER, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), slotKey.getSlotHash().getBytes().toArrayUnsafe(), @@ -1129,7 +1129,7 @@ public Optional load(final Hash blockHash) { testWorldStateStorage .getComposedWorldStateStorage() .containsKey( - KeyValueSegmentIdentifier.ACCOUNT_STORAGE_STORAGE, + KeyValueSegmentIdentifier.ACCOUNT_STORAGE_ARCHIVE, Arrays.concatenate( address.addressHash().getBytes().toArrayUnsafe(), slotKey.getSlotHash().getBytes().toArrayUnsafe(),