@ -15,6 +15,7 @@
package org.hyperledger.besu.cli.subcommands.storage ;
import static java.util.Collections.singletonList ;
import static org.hyperledger.besu.ethereum.worldstate.DataStorageFormat.BONSAI ;
import static org.junit.jupiter.api.Assertions.assertArrayEquals ;
import static org.junit.jupiter.api.Assertions.assertEquals ;
@ -27,8 +28,11 @@ import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader ;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture ;
import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider ;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput ;
import org.hyperledger.besu.ethereum.storage.StorageProvider ;
import org.hyperledger.besu.ethereum.trie.bonsai.storage.BonsaiWorldStateKeyValueStorage ;
import org.hyperledger.besu.ethereum.trie.bonsai.trielog.TrieLogFactoryImpl ;
import org.hyperledger.besu.ethereum.trie.bonsai.trielog.TrieLogLayer ;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration ;
import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration ;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem ;
@ -36,11 +40,12 @@ import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import java.io.IOException ;
import java.nio.file.Files ;
import java.nio.file.Path ;
import java.util.List ;
import java.util.Map ;
import java.util.Optional ;
import java.util.stream.Collectors ;
import org.apache.tuweni.bytes.Bytes ;
import org.junit.jupiter.api.AfterEach ;
import org.junit.jupiter.api.BeforeAll ;
import org.junit.jupiter.api.BeforeEach ;
import org.junit.jupiter.api.Test ;
import org.junit.jupiter.api.extension.ExtendWith ;
@ -56,17 +61,14 @@ class TrieLogHelperTest {
@Mock private MutableBlockchain blockchain ;
@TempDir static Path dataDir ;
Path test ;
static BlockHeader blockHeader1 ;
static BlockHeader blockHeader2 ;
static BlockHeader blockHeader3 ;
static BlockHeader blockHeader4 ;
static BlockHeader blockHeader5 ;
@BeforeAll
public static void setup ( ) throws IOException {
@BeforeEach
public void setup ( ) throws IOException {
blockHeader1 = new BlockHeaderTestFixture ( ) . number ( 1 ) . buildHeader ( ) ;
blockHeader2 = new BlockHeaderTestFixture ( ) . number ( 2 ) . buildHeader ( ) ;
@ -78,33 +80,33 @@ class TrieLogHelperTest {
new BonsaiWorldStateKeyValueStorage (
storageProvider , new NoOpMetricsSystem ( ) , DataStorageConfiguration . DEFAULT_CONFIG ) ;
createTrieLog ( blockHeader1 ) ;
var updater = inMemoryWorldState . updater ( ) ;
updater
. getTrieLogStorageTransaction ( )
. put ( blockHeader1 . getHash ( ) . toArrayUnsafe ( ) , Bytes . fromHexString ( "0x01" ) . toArrayUnsafe ( ) ) ;
. put ( blockHeader1 . getHash ( ) . toArrayUnsafe ( ) , createTrieLog ( blockHeader1 ) ) ;
updater
. getTrieLogStorageTransaction ( )
. put ( blockHeader2 . getHash ( ) . toArrayUnsafe ( ) , Bytes . fromHexString ( "0x02" ) . toArrayUnsafe ( ) ) ;
. put ( blockHeader2 . getHash ( ) . toArrayUnsafe ( ) , createTrieLog ( blockHeader2 ) ) ;
updater
. getTrieLogStorageTransaction ( )
. put ( blockHeader3 . getHash ( ) . toArrayUnsafe ( ) , Bytes . fromHexString ( "0x03" ) . toArrayUnsafe ( ) ) ;
. put ( blockHeader3 . getHash ( ) . toArrayUnsafe ( ) , createTrieLog ( blockHeader3 ) ) ;
updater
. getTrieLogStorageTransaction ( )
. put ( blockHeader4 . getHash ( ) . toArrayUnsafe ( ) , Bytes . fromHexString ( "0x04" ) . toArrayUnsafe ( ) ) ;
. put ( blockHeader4 . getHash ( ) . toArrayUnsafe ( ) , createTrieLog ( blockHeader4 ) ) ;
updater
. getTrieLogStorageTransaction ( )
. put ( blockHeader5 . getHash ( ) . toArrayUnsafe ( ) , Bytes . fromHexString ( "0x05" ) . toArrayUnsafe ( ) ) ;
. put ( blockHeader5 . getHash ( ) . toArrayUnsafe ( ) , createTrieLog ( blockHeader5 ) ) ;
updater . getTrieLogStorageTransaction ( ) . commit ( ) ;
}
@BeforeEach
void createDirectory ( ) throws IOException {
Files . createDirectories ( dataDir . resolve ( "database" ) ) ;
}
@AfterEach
void deleteDirectory ( ) throws IOException {
Files . deleteIfExists ( dataDir . resolve ( "database" ) ) ;
private static byte [ ] createTrieLog ( final BlockHeader blockHeader ) {
TrieLogLayer trieLogLayer = new TrieLogLayer ( ) ;
trieLogLayer . setBlockHash ( blockHeader . getBlockHash ( ) ) ;
final BytesValueRLPOutput rlpLog = new BytesValueRLPOutput ( ) ;
TrieLogFactoryImpl . writeTo ( trieLogLayer , rlpLog ) ;
return rlpLog . encoded ( ) . toArrayUnsafe ( ) ;
}
void mockBlockchainBase ( ) {
@ -114,7 +116,8 @@ class TrieLogHelperTest {
}
@Test
public void prune ( ) {
public void prune ( final @TempDir Path dataDir ) throws IOException {
Files . createDirectories ( dataDir . resolve ( "database" ) ) ;
DataStorageConfiguration dataStorageConfiguration =
ImmutableDataStorageConfiguration . builder ( )
@ -134,14 +137,11 @@ class TrieLogHelperTest {
// assert trie logs that will be pruned exist before prune call
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader1 . getHash ( ) ) . get ( ) ,
Bytes . fromHexString ( "0x01" ) . toArrayUnsafe ( ) ) ;
inMemoryWorldState . getTrieLog ( blockHeader1 . getHash ( ) ) . get ( ) , createTrieLog ( blockHeader1 ) ) ;
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader2 . getHash ( ) ) . get ( ) ,
Bytes . fromHexString ( "0x02" ) . toArrayUnsafe ( ) ) ;
inMemoryWorldState . getTrieLog ( blockHeader2 . getHash ( ) ) . get ( ) , createTrieLog ( blockHeader2 ) ) ;
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader3 . getHash ( ) ) . get ( ) ,
Bytes . fromHexString ( "0x03" ) . toArrayUnsafe ( ) ) ;
inMemoryWorldState . getTrieLog ( blockHeader3 . getHash ( ) ) . get ( ) , createTrieLog ( blockHeader3 ) ) ;
TrieLogHelper . prune ( dataStorageConfiguration , inMemoryWorldState , blockchain , dataDir ) ;
@ -151,18 +151,15 @@ class TrieLogHelperTest {
// assert retained trie logs are in the DB
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader3 . getHash ( ) ) . get ( ) ,
Bytes . fromHexString ( "0x03" ) . toArrayUnsafe ( ) ) ;
inMemoryWorldState . getTrieLog ( blockHeader3 . getHash ( ) ) . get ( ) , createTrieLog ( blockHeader3 ) ) ;
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader4 . getHash ( ) ) . get ( ) ,
Bytes . fromHexString ( "0x04" ) . toArrayUnsafe ( ) ) ;
inMemoryWorldState . getTrieLog ( blockHeader4 . getHash ( ) ) . get ( ) , createTrieLog ( blockHeader4 ) ) ;
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader5 . getHash ( ) ) . get ( ) ,
Bytes . fromHexString ( "0x05" ) . toArrayUnsafe ( ) ) ;
inMemoryWorldState . getTrieLog ( blockHeader5 . getHash ( ) ) . get ( ) , createTrieLog ( blockHeader5 ) ) ;
}
@Test
public void cantPruneIfNoFinalizedIsFound ( ) {
public void cantPruneIfNoFinalizedIsFound ( final @TempDir Path dataDir ) {
DataStorageConfiguration dataStorageConfiguration =
ImmutableDataStorageConfiguration . builder ( )
. dataStorageFormat ( BONSAI )
@ -184,7 +181,7 @@ class TrieLogHelperTest {
}
@Test
public void cantPruneIfUserRetainsMoreLayerThanExistingChainLength ( ) {
public void cantPruneIfUserRetainsMoreLayerThanExistingChainLength ( final @TempDir Path dataDir ) {
DataStorageConfiguration dataStorageConfiguration =
ImmutableDataStorageConfiguration . builder ( )
. dataStorageFormat ( BONSAI )
@ -205,7 +202,7 @@ class TrieLogHelperTest {
}
@Test
public void cantPruneIfUserRequiredFurtherThanFinalized ( ) {
public void cantPruneIfUserRequiredFurtherThanFinalized ( final @TempDir Path dataDir ) {
DataStorageConfiguration dataStorageConfiguration =
ImmutableDataStorageConfiguration . builder ( )
@ -227,8 +224,7 @@ class TrieLogHelperTest {
}
@Test
public void exceptionWhileSavingFileStopsPruneProcess ( ) throws IOException {
Files . delete ( dataDir . resolve ( "database" ) ) ;
public void exceptionWhileSavingFileStopsPruneProcess ( final @TempDir Path dataDir ) {
DataStorageConfiguration dataStorageConfiguration =
ImmutableDataStorageConfiguration . builder ( )
@ -244,23 +240,121 @@ class TrieLogHelperTest {
assertThrows (
RuntimeException . class ,
( ) - >
TrieLogHelper . prune ( dataStorageConfiguration , inMemoryWorldState , blockchain , dataDir ) ) ;
TrieLogHelper . prune (
dataStorageConfiguration ,
inMemoryWorldState ,
blockchain ,
dataDir . resolve ( "unknownPath" ) ) ) ;
// assert all trie logs are still in the DB
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader1 . getHash ( ) ) . get ( ) ,
Bytes . fromHexString ( "0x01" ) . toArrayUnsafe ( ) ) ;
inMemoryWorldState . getTrieLog ( blockHeader1 . getHash ( ) ) . get ( ) , createTrieLog ( blockHeader1 ) ) ;
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader2 . getHash ( ) ) . get ( ) , createTrieLog ( blockHeader2 ) ) ;
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader3 . getHash ( ) ) . get ( ) , createTrieLog ( blockHeader3 ) ) ;
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader4 . getHash ( ) ) . get ( ) , createTrieLog ( blockHeader4 ) ) ;
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader5 . getHash ( ) ) . get ( ) , createTrieLog ( blockHeader5 ) ) ;
}
@Test
public void exportedTrieMatchesDbTrieLog ( final @TempDir Path dataDir ) throws IOException {
TrieLogHelper . exportTrieLog (
inMemoryWorldState ,
singletonList ( blockHeader1 . getHash ( ) ) ,
dataDir . resolve ( "trie-log-dump" ) ) ;
var trieLog =
TrieLogHelper . readTrieLogsAsRlpFromFile ( dataDir . resolve ( "trie-log-dump" ) . toString ( ) )
. entrySet ( )
. stream ( )
. findFirst ( )
. get ( ) ;
assertArrayEquals ( trieLog . getKey ( ) , blockHeader1 . getHash ( ) . toArrayUnsafe ( ) ) ;
assertArrayEquals (
trieLog . getValue ( ) , inMemoryWorldState . getTrieLog ( blockHeader1 . getHash ( ) ) . get ( ) ) ;
}
@Test
public void exportedMultipleTriesMatchDbTrieLogs ( final @TempDir Path dataDir ) throws IOException {
TrieLogHelper . exportTrieLog (
inMemoryWorldState ,
List . of ( blockHeader1 . getHash ( ) , blockHeader2 . getHash ( ) , blockHeader3 . getHash ( ) ) ,
dataDir . resolve ( "trie-log-dump" ) ) ;
var trieLogs =
TrieLogHelper . readTrieLogsAsRlpFromFile ( dataDir . resolve ( "trie-log-dump" ) . toString ( ) )
. entrySet ( )
. stream ( )
. collect ( Collectors . toMap ( e - > Bytes . wrap ( e . getKey ( ) ) , Map . Entry : : getValue ) ) ;
assertArrayEquals (
trieLogs . get ( blockHeader1 . getHash ( ) ) ,
inMemoryWorldState . getTrieLog ( blockHeader1 . getHash ( ) ) . get ( ) ) ;
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader2 . getHash ( ) ) . get ( ) ,
Bytes . fromHexString ( "0x02" ) . toArrayUnsafe ( ) ) ;
trieLogs . get ( blockHeader2 . getHash ( ) ) ,
inMemoryWorldState . getTrieLog ( blockHeader2 . getHash ( ) ) . get ( ) ) ;
assertArrayEquals (
trieLogs . get ( blockHeader3 . getHash ( ) ) ,
inMemoryWorldState . getTrieLog ( blockHeader3 . getHash ( ) ) . get ( ) ) ;
}
@Test
public void importedTrieLogMatchesDbTrieLog ( final @TempDir Path dataDir ) throws IOException {
StorageProvider tempStorageProvider = new InMemoryKeyValueStorageProvider ( ) ;
BonsaiWorldStateKeyValueStorage inMemoryWorldState2 =
new BonsaiWorldStateKeyValueStorage (
tempStorageProvider , new NoOpMetricsSystem ( ) , DataStorageConfiguration . DEFAULT_CONFIG ) ;
TrieLogHelper . exportTrieLog (
inMemoryWorldState ,
singletonList ( blockHeader1 . getHash ( ) ) ,
dataDir . resolve ( "trie-log-dump" ) ) ;
var trieLog =
TrieLogHelper . readTrieLogsAsRlpFromFile ( dataDir . resolve ( "trie-log-dump" ) . toString ( ) ) ;
var updater = inMemoryWorldState2 . updater ( ) ;
trieLog . forEach ( ( k , v ) - > updater . getTrieLogStorageTransaction ( ) . put ( k , v ) ) ;
updater . getTrieLogStorageTransaction ( ) . commit ( ) ;
assertArrayEquals (
inMemoryWorldState2 . getTrieLog ( blockHeader1 . getHash ( ) ) . get ( ) ,
inMemoryWorldState . getTrieLog ( blockHeader1 . getHash ( ) ) . get ( ) ) ;
}
@Test
public void importedMultipleTriesMatchDbTrieLogs ( final @TempDir Path dataDir ) throws IOException {
StorageProvider tempStorageProvider = new InMemoryKeyValueStorageProvider ( ) ;
BonsaiWorldStateKeyValueStorage inMemoryWorldState2 =
new BonsaiWorldStateKeyValueStorage (
tempStorageProvider , new NoOpMetricsSystem ( ) , DataStorageConfiguration . DEFAULT_CONFIG ) ;
TrieLogHelper . exportTrieLog (
inMemoryWorldState ,
List . of ( blockHeader1 . getHash ( ) , blockHeader2 . getHash ( ) , blockHeader3 . getHash ( ) ) ,
dataDir . resolve ( "trie-log-dump" ) ) ;
var trieLog =
TrieLogHelper . readTrieLogsAsRlpFromFile ( dataDir . resolve ( "trie-log-dump" ) . toString ( ) ) ;
var updater = inMemoryWorldState2 . updater ( ) ;
trieLog . forEach ( ( k , v ) - > updater . getTrieLogStorageTransaction ( ) . put ( k , v ) ) ;
updater . getTrieLogStorageTransaction ( ) . commit ( ) ;
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader3 . getHash ( ) ) . get ( ) ,
Bytes . fromHexString ( "0x03" ) . toArrayUnsafe ( ) ) ;
inMemoryWorldState2 . getTrieLog ( blockHeader1 . getHash ( ) ) . get ( ) ,
inMemoryWorldState . getTrieLog ( blockHeader1 . getHash ( ) ) . get ( ) ) ;
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader4 . getHash ( ) ) . get ( ) ,
Bytes . fromHexString ( "0x04" ) . toArrayUnsafe ( ) ) ;
inMemoryWorldState2 . getTrieLog ( blockHeader2 . getHash ( ) ) . get ( ) ,
inMemoryWorldState . getTrieLog ( blockHeader2 . getHash ( ) ) . get ( ) ) ;
assertArrayEquals (
inMemoryWorldState . getTrieLog ( blockHeader5 . getHash ( ) ) . get ( ) ,
Bytes . fromHexString ( "0x05" ) . toArrayUnsafe ( ) ) ;
inMemoryWorldState2 . getTrieLog ( blockHeader3 . getHash ( ) ) . get ( ) ,
inMemoryWorldState . getTrieLog ( blockHeader3 . getHash ( ) ) . get ( ) ) ;
}
}