Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

candidate fix for missing parent worldstate #4094

Merged
merged 12 commits into from
Jul 18, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.methods.JsonRpcMethods;
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
import org.hyperledger.besu.ethereum.bonsai.BonsaiWorldStateArchive;
import org.hyperledger.besu.ethereum.bonsai.BonsaiWorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.bonsai.TrieLogManager;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.BlockchainStorage;
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain;
Expand Down Expand Up @@ -602,7 +604,12 @@ private WorldStateArchive createWorldStateArchive(
switch (dataStorageConfiguration.getDataStorageFormat()) {
case BONSAI:
return new BonsaiWorldStateArchive(
storageProvider, blockchain, dataStorageConfiguration.getBonsaiMaxLayersToLoad());
new TrieLogManager(
blockchain,
(BonsaiWorldStateKeyValueStorage) worldStateStorage,
dataStorageConfiguration.getBonsaiMaxLayersToLoad()),
storageProvider,
blockchain);
case FOREST:
default:
final WorldStatePreimageStorage preimageStorage =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.hyperledger.besu.crypto.NodeKey;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.bonsai.BonsaiWorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
Expand All @@ -46,6 +47,7 @@
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;

import java.math.BigInteger;
Expand Down Expand Up @@ -83,6 +85,7 @@ public class BesuControllerBuilderTest {
@Mock StorageProvider storageProvider;
@Mock GasLimitCalculator gasLimitCalculator;
@Mock WorldStateStorage worldStateStorage;
@Mock BonsaiWorldStateKeyValueStorage bonsaiWorldStateStorage;
@Mock WorldStatePreimageStorage worldStatePreimageStorage;

BigInteger networkId = BigInteger.ONE;
Expand Down Expand Up @@ -128,7 +131,11 @@ public void setup() {
when(worldStatePreimageStorage.updater())
.thenReturn(mock(WorldStatePreimageStorage.Updater.class));
when(worldStateStorage.updater()).thenReturn(mock(WorldStateStorage.Updater.class));

BonsaiWorldStateKeyValueStorage.Updater bonsaiUpdater =
mock(BonsaiWorldStateKeyValueStorage.Updater.class);
when(bonsaiUpdater.getTrieLogStorageTransaction())
.thenReturn(mock(KeyValueStorageTransaction.class));
when(bonsaiWorldStateStorage.updater()).thenReturn(bonsaiUpdater);
besuControllerBuilder = visitWithMockConfigs(new MainnetBesuControllerBuilder());
}

Expand All @@ -152,6 +159,8 @@ BesuControllerBuilder visitWithMockConfigs(final BesuControllerBuilder builder)

@Test
public void shouldDisablePruningIfBonsaiIsEnabled() {
when(storageProvider.createWorldStateStorage(DataStorageFormat.BONSAI))
.thenReturn(bonsaiWorldStateStorage);
besuControllerBuilder
.isPruningEnabled(true)
.dataStorageConfiguration(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

public class BonsaiInMemoryWorldState extends BonsaiPersistedWorldState {

private boolean isPersisted = false;

public BonsaiInMemoryWorldState(
final BonsaiWorldStateArchive archive,
final BonsaiWorldStateKeyValueStorage worldStateStorage) {
Expand All @@ -29,9 +31,16 @@ public BonsaiInMemoryWorldState(

@Override
public Hash rootHash() {
if (isPersisted) {
return worldStateRootHash;
}
return rootHash(updater.copy());
}

public Hash rootHash(final BonsaiWorldStateUpdater localUpdater) {
final BonsaiWorldStateKeyValueStorage.Updater updater = worldStateStorage.updater();
try {
final Hash calculatedRootHash = calculateRootHash(updater);
final Hash calculatedRootHash = calculateRootHash(updater, localUpdater);
return Hash.wrap(calculatedRootHash);
} finally {
updater.rollback();
Expand All @@ -41,13 +50,12 @@ public Hash rootHash() {
@Override
public void persist(final BlockHeader blockHeader) {
final BonsaiWorldStateUpdater localUpdater = updater.copy();
try {
final Hash newWorldStateRootHash = rootHash();
prepareTrieLog(blockHeader, localUpdater, newWorldStateRootHash);
worldStateBlockHash = blockHeader.getHash();
worldStateRootHash = newWorldStateRootHash;
} finally {
localUpdater.reset();
}
final Hash newWorldStateRootHash = rootHash(localUpdater);
archive
.getTrieLogManager()
.saveTrieLog(archive, localUpdater, newWorldStateRootHash, blockHeader);
worldStateRootHash = newWorldStateRootHash;
worldStateBlockHash = blockHeader.getBlockHash();
isPersisted = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.trie.StoredMerklePatriciaTrie;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.evm.account.Account;
Expand Down Expand Up @@ -100,10 +99,6 @@ public BonsaiWorldStateKeyValueStorage getWorldStateStorage() {
return worldStateStorage;
}

protected Hash calculateRootHash(final BonsaiWorldStateKeyValueStorage.Updater stateUpdater) {
return calculateRootHash(stateUpdater, updater.copy());
}

protected Hash calculateRootHash(
final BonsaiWorldStateKeyValueStorage.Updater stateUpdater,
final BonsaiWorldStateUpdater worldStateUpdater) {
Expand Down Expand Up @@ -252,14 +247,16 @@ public void persist(final BlockHeader blockHeader) {
// then persist the TrieLog for that transition.
// If specified but not a direct descendant simply store the new block hash.
if (blockHeader != null) {
// do not overwrite a trielog layer that already exists in the database.
// if it's only in memory we need to save it
// for example, like that in case of reorg we don't replace a trielog layer
if (worldStateStorage.getTrieLog(blockHeader.getHash()).isEmpty()) {
final TrieLogLayer trieLog =
prepareTrieLog(blockHeader, localUpdater, newWorldStateRootHash);
persistTrieLog(blockHeader, newWorldStateRootHash, trieLog, stateUpdater);
if (!newWorldStateRootHash.equals(blockHeader.getStateRoot())) {
throw new RuntimeException(
"World State Root does not match expected value, header "
+ blockHeader.getStateRoot().toHexString()
+ " calculated "
+ newWorldStateRootHash.toHexString());
}
archive
.getTrieLogManager()
.saveTrieLog(archive, localUpdater, newWorldStateRootHash, blockHeader);
stateUpdater
.getTrieBranchStorageTransaction()
.put(WORLD_BLOCK_HASH_KEY, blockHeader.getHash().toArrayUnsafe());
Expand All @@ -283,46 +280,6 @@ public void persist(final BlockHeader blockHeader) {
updater.reset();
}
}
if (blockHeader != null) {
archive.scrubLayeredCache(blockHeader.getNumber());
}
}

protected TrieLogLayer prepareTrieLog(
final BlockHeader blockHeader,
final BonsaiWorldStateUpdater localUpdater,
final Hash currentWorldStateRootHash) {

if (!currentWorldStateRootHash.equals(blockHeader.getStateRoot())) {
throw new RuntimeException(
"World State Root does not match expected value, header "
+ blockHeader.getStateRoot().toHexString()
+ " calculated "
+ currentWorldStateRootHash.toHexString());
}

debugLambda(LOG, "Adding layered world state for {}", blockHeader::toLogString);
final TrieLogLayer trieLog = localUpdater.generateTrieLog(blockHeader.getBlockHash());
trieLog.freeze();
archive.addLayeredWorldState(this, blockHeader, currentWorldStateRootHash, trieLog);
return trieLog;
}

private void persistTrieLog(
final BlockHeader blockHeader,
final Hash worldStateRootHash,
final TrieLogLayer trieLog,
final BonsaiWorldStateKeyValueStorage.Updater stateUpdater) {
debugLambda(
LOG,
"Persisting trie log for block hash {} and world state root {}",
blockHeader::toLogString,
worldStateRootHash::toHexString);
final BytesValueRLPOutput rlpLog = new BytesValueRLPOutput();
trieLog.writeTo(rlpLog);
stateUpdater
.getTrieLogStorageTransaction()
.put(blockHeader.getHash().toArrayUnsafe(), rlpLog.encoded().toArrayUnsafe());
}

@Override
Expand Down
Loading