Make healing an action of the WorldStateArchive (#7862)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/7863/head
Fabio Di Fabio 2 weeks ago committed by GitHub
parent 62b2c500c4
commit 62b8b4e1d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 17
      besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java
  2. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/MainnetBlockValidator.java
  3. 15
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/BonsaiWorldStateProvider.java
  4. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/forest/ForestWorldStateArchive.java
  5. 20
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/WorldStateArchive.java
  6. 5
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryKeyValueStorageProvider.java
  7. 39
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/WorldStateHealerHelper.java
  8. 4
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/AbstractIsolationTests.java
  9. 22
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/BonsaiWorldStateProviderTest.java

@ -93,6 +93,7 @@ import org.hyperledger.besu.ethereum.trie.forest.ForestWorldStateArchive;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
import org.hyperledger.besu.ethereum.worldstate.DiffBasedSubStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.DiffBasedSubStorageConfiguration;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive.WorldStateHealer;
import org.hyperledger.besu.ethereum.worldstate.WorldStateKeyValueStorage; import org.hyperledger.besu.ethereum.worldstate.WorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage; import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorageCoordinator; import org.hyperledger.besu.ethereum.worldstate.WorldStateStorageCoordinator;
@ -113,6 +114,7 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -589,9 +591,14 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
.map(BesuComponent::getCachedMerkleTrieLoader) .map(BesuComponent::getCachedMerkleTrieLoader)
.orElseGet(() -> new BonsaiCachedMerkleTrieLoader(metricsSystem)); .orElseGet(() -> new BonsaiCachedMerkleTrieLoader(metricsSystem));
final var worldStateHealerSupplier = new AtomicReference<WorldStateHealer>();
final WorldStateArchive worldStateArchive = final WorldStateArchive worldStateArchive =
createWorldStateArchive( createWorldStateArchive(
worldStateStorageCoordinator, blockchain, bonsaiCachedMerkleTrieLoader); worldStateStorageCoordinator,
blockchain,
bonsaiCachedMerkleTrieLoader,
worldStateHealerSupplier::get);
if (maybeStoredGenesisBlockHash.isEmpty()) { if (maybeStoredGenesisBlockHash.isEmpty()) {
genesisState.writeStateTo(worldStateArchive.getMutable()); genesisState.writeStateTo(worldStateArchive.getMutable());
@ -713,6 +720,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
ethProtocolManager, ethProtocolManager,
pivotBlockSelector); pivotBlockSelector);
worldStateHealerSupplier.set(synchronizer::healWorldState);
ethPeers.setTrailingPeerRequirementsSupplier(synchronizer::calculateTrailingPeerRequirements); ethPeers.setTrailingPeerRequirementsSupplier(synchronizer::calculateTrailingPeerRequirements);
if (syncConfig.getSyncMode() == SyncMode.SNAP if (syncConfig.getSyncMode() == SyncMode.SNAP
@ -1101,7 +1110,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
WorldStateArchive createWorldStateArchive( WorldStateArchive createWorldStateArchive(
final WorldStateStorageCoordinator worldStateStorageCoordinator, final WorldStateStorageCoordinator worldStateStorageCoordinator,
final Blockchain blockchain, final Blockchain blockchain,
final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader) { final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader,
final Supplier<WorldStateHealer> worldStateHealerSupplier) {
return switch (dataStorageConfiguration.getDataStorageFormat()) { return switch (dataStorageConfiguration.getDataStorageFormat()) {
case BONSAI -> { case BONSAI -> {
final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage = final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage =
@ -1116,7 +1126,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
.getMaxLayersToLoad()), .getMaxLayersToLoad()),
bonsaiCachedMerkleTrieLoader, bonsaiCachedMerkleTrieLoader,
besuComponent.map(BesuComponent::getBesuPluginContext).orElse(null), besuComponent.map(BesuComponent::getBesuPluginContext).orElse(null),
evmConfiguration); evmConfiguration,
worldStateHealerSupplier);
} }
case FOREST -> { case FOREST -> {
final WorldStatePreimageStorage preimageStorage = final WorldStatePreimageStorage preimageStorage =

@ -181,7 +181,7 @@ public class MainnetBlockValidator implements BlockValidator {
Optional.of(new BlockProcessingOutputs(worldState, receipts, maybeRequests))); Optional.of(new BlockProcessingOutputs(worldState, receipts, maybeRequests)));
} }
} catch (MerkleTrieException ex) { } catch (MerkleTrieException ex) {
context.getSynchronizer().healWorldState(ex.getMaybeAddress(), ex.getLocation()); context.getWorldStateArchive().heal(ex.getMaybeAddress(), ex.getLocation());
return new BlockProcessingResult(Optional.empty(), ex); return new BlockProcessingResult(Optional.empty(), ex);
} catch (StorageException ex) { } catch (StorageException ex) {
var retval = new BlockProcessingResult(Optional.empty(), ex); var retval = new BlockProcessingResult(Optional.empty(), ex);

@ -34,6 +34,7 @@ import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
@ -44,6 +45,7 @@ public class BonsaiWorldStateProvider extends DiffBasedWorldStateProvider {
private static final Logger LOG = LoggerFactory.getLogger(BonsaiWorldStateProvider.class); private static final Logger LOG = LoggerFactory.getLogger(BonsaiWorldStateProvider.class);
private final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader; private final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader;
private final Supplier<WorldStateHealer> worldStateHealerSupplier;
public BonsaiWorldStateProvider( public BonsaiWorldStateProvider(
final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage, final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage,
@ -51,9 +53,11 @@ public class BonsaiWorldStateProvider extends DiffBasedWorldStateProvider {
final Optional<Long> maxLayersToLoad, final Optional<Long> maxLayersToLoad,
final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader, final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader,
final BesuContext pluginContext, final BesuContext pluginContext,
final EvmConfiguration evmConfiguration) { final EvmConfiguration evmConfiguration,
final Supplier<WorldStateHealer> worldStateHealerSupplier) {
super(worldStateKeyValueStorage, blockchain, maxLayersToLoad, pluginContext); super(worldStateKeyValueStorage, blockchain, maxLayersToLoad, pluginContext);
this.bonsaiCachedMerkleTrieLoader = bonsaiCachedMerkleTrieLoader; this.bonsaiCachedMerkleTrieLoader = bonsaiCachedMerkleTrieLoader;
this.worldStateHealerSupplier = worldStateHealerSupplier;
provideCachedWorldStorageManager( provideCachedWorldStorageManager(
new BonsaiCachedWorldStorageManager( new BonsaiCachedWorldStorageManager(
this, worldStateKeyValueStorage, this::cloneBonsaiWorldStateConfig)); this, worldStateKeyValueStorage, this::cloneBonsaiWorldStateConfig));
@ -69,9 +73,11 @@ public class BonsaiWorldStateProvider extends DiffBasedWorldStateProvider {
final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage, final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage,
final Blockchain blockchain, final Blockchain blockchain,
final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader, final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader,
final EvmConfiguration evmConfiguration) { final EvmConfiguration evmConfiguration,
final Supplier<WorldStateHealer> worldStateHealerSupplier) {
super(worldStateKeyValueStorage, blockchain, trieLogManager); super(worldStateKeyValueStorage, blockchain, trieLogManager);
this.bonsaiCachedMerkleTrieLoader = bonsaiCachedMerkleTrieLoader; this.bonsaiCachedMerkleTrieLoader = bonsaiCachedMerkleTrieLoader;
this.worldStateHealerSupplier = worldStateHealerSupplier;
provideCachedWorldStorageManager(bonsaiCachedWorldStorageManager); provideCachedWorldStorageManager(bonsaiCachedWorldStorageManager);
loadPersistedState( loadPersistedState(
new BonsaiWorldState( new BonsaiWorldState(
@ -151,4 +157,9 @@ public class BonsaiWorldStateProvider extends DiffBasedWorldStateProvider {
private DiffBasedWorldStateConfig cloneBonsaiWorldStateConfig() { private DiffBasedWorldStateConfig cloneBonsaiWorldStateConfig() {
return new DiffBasedWorldStateConfig(defaultWorldStateConfig); return new DiffBasedWorldStateConfig(defaultWorldStateConfig);
} }
@Override
public void heal(final Optional<Address> maybeAccountToRepair, final Bytes location) {
worldStateHealerSupplier.get().heal(maybeAccountToRepair, location);
}
} }

@ -112,6 +112,11 @@ public class ForestWorldStateArchive implements WorldStateArchive {
blockHeader.getStateRoot(), accountAddress, accountStorageKeys)); blockHeader.getStateRoot(), accountAddress, accountStorageKeys));
} }
@Override
public void heal(final Optional<Address> maybeAccountToRepair, final Bytes location) {
// no heal needed for Forest
}
@Override @Override
public void close() { public void close() {
// no op // no op

@ -64,4 +64,24 @@ public interface WorldStateArchive extends Closeable {
final Address accountAddress, final Address accountAddress,
final List<UInt256> accountStorageKeys, final List<UInt256> accountStorageKeys,
final Function<Optional<WorldStateProof>, ? extends Optional<U>> mapper); final Function<Optional<WorldStateProof>, ? extends Optional<U>> mapper);
/**
* Heal the world state to fix inconsistency
*
* @param maybeAccountToRepair the optional account to repair
* @param location the location of the inconsistency
*/
void heal(Optional<Address> maybeAccountToRepair, Bytes location);
/** A world state healer */
@FunctionalInterface
interface WorldStateHealer {
/**
* Heal the world state to fix inconsistency
*
* @param maybeAccountToRepair the optional account to repair
* @param location the location of the inconsistency
*/
void heal(Optional<Address> maybeAccountToRepair, Bytes location);
}
} }

@ -14,6 +14,8 @@
*/ */
package org.hyperledger.besu.ethereum.core; package org.hyperledger.besu.ethereum.core;
import static org.hyperledger.besu.ethereum.core.WorldStateHealerHelper.throwingWorldStateHealerSupplier;
import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain; import org.hyperledger.besu.ethereum.chain.DefaultBlockchain;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
@ -105,7 +107,8 @@ public class InMemoryKeyValueStorageProvider extends KeyValueStorageProvider {
Optional.empty(), Optional.empty(),
bonsaiCachedMerkleTrieLoader, bonsaiCachedMerkleTrieLoader,
null, null,
evmConfiguration); evmConfiguration,
throwingWorldStateHealerSupplier());
} }
public static MutableWorldState createInMemoryWorldState() { public static MutableWorldState createInMemoryWorldState() {

@ -0,0 +1,39 @@
/*
* Copyright contributors to 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.core;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive.WorldStateHealer;
import java.util.Optional;
import java.util.function.Supplier;
import org.apache.tuweni.bytes.Bytes;
public class WorldStateHealerHelper {
public static WorldStateHealer throwingHealer(
final Optional<Address> maybeAccountToRepair, final Bytes location) {
throw new RuntimeException(
"World state needs to be healed: "
+ maybeAccountToRepair.map(address -> "account to repair: " + address).orElse("")
+ " location: "
+ location.toHexString());
}
public static Supplier<WorldStateHealer> throwingWorldStateHealerSupplier() {
return () -> WorldStateHealerHelper::throwingHealer;
}
}

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.trie.diffbased.bonsai; package org.hyperledger.besu.ethereum.trie.diffbased.bonsai;
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryBlockchain; import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryBlockchain;
import static org.hyperledger.besu.ethereum.core.WorldStateHealerHelper.throwingWorldStateHealerSupplier;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -167,7 +168,8 @@ public abstract class AbstractIsolationTests {
Optional.of(16L), Optional.of(16L),
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()), new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
null, null,
EvmConfiguration.DEFAULT); EvmConfiguration.DEFAULT,
throwingWorldStateHealerSupplier());
var ws = archive.getMutable(); var ws = archive.getMutable();
genesisState.writeStateTo(ws); genesisState.writeStateTo(ws);
protocolContext = new ProtocolContext(blockchain, archive, null, new BadBlockManager()); protocolContext = new ProtocolContext(blockchain, archive, null, new BadBlockManager());

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.trie.diffbased.bonsai; package org.hyperledger.besu.ethereum.trie.diffbased.bonsai;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.core.WorldStateHealerHelper.throwingWorldStateHealerSupplier;
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.BLOCKCHAIN; import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.BLOCKCHAIN;
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.TRIE_BRANCH_STORAGE; import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.TRIE_BRANCH_STORAGE;
import static org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedWorldStateKeyValueStorage.WORLD_BLOCK_HASH_KEY; import static org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedWorldStateKeyValueStorage.WORLD_BLOCK_HASH_KEY;
@ -111,7 +112,8 @@ class BonsaiWorldStateProviderTest {
DataStorageConfiguration.DEFAULT_BONSAI_CONFIG), DataStorageConfiguration.DEFAULT_BONSAI_CONFIG),
blockchain, blockchain,
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()), new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT); EvmConfiguration.DEFAULT,
throwingWorldStateHealerSupplier());
assertThat(bonsaiWorldStateArchive.getMutable(chainHead, true)) assertThat(bonsaiWorldStateArchive.getMutable(chainHead, true))
.containsInstanceOf(BonsaiWorldState.class); .containsInstanceOf(BonsaiWorldState.class);
@ -129,7 +131,8 @@ class BonsaiWorldStateProviderTest {
Optional.of(512L), Optional.of(512L),
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()), new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
null, null,
EvmConfiguration.DEFAULT); EvmConfiguration.DEFAULT,
throwingWorldStateHealerSupplier());
final BlockHeader blockHeader = blockBuilder.number(0).buildHeader(); final BlockHeader blockHeader = blockBuilder.number(0).buildHeader();
final BlockHeader chainHead = blockBuilder.number(512).buildHeader(); final BlockHeader chainHead = blockBuilder.number(512).buildHeader();
when(blockchain.getChainHeadHeader()).thenReturn(chainHead); when(blockchain.getChainHeadHeader()).thenReturn(chainHead);
@ -150,7 +153,8 @@ class BonsaiWorldStateProviderTest {
DataStorageConfiguration.DEFAULT_BONSAI_CONFIG), DataStorageConfiguration.DEFAULT_BONSAI_CONFIG),
blockchain, blockchain,
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()), new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT); EvmConfiguration.DEFAULT,
throwingWorldStateHealerSupplier());
final BlockHeader blockHeader = blockBuilder.number(0).buildHeader(); final BlockHeader blockHeader = blockBuilder.number(0).buildHeader();
final BlockHeader chainHead = blockBuilder.number(511).buildHeader(); final BlockHeader chainHead = blockBuilder.number(511).buildHeader();
final BonsaiWorldState mockWorldState = mock(BonsaiWorldState.class); final BonsaiWorldState mockWorldState = mock(BonsaiWorldState.class);
@ -185,7 +189,8 @@ class BonsaiWorldStateProviderTest {
worldStateKeyValueStorage, worldStateKeyValueStorage,
blockchain, blockchain,
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()), new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT)); EvmConfiguration.DEFAULT,
throwingWorldStateHealerSupplier()));
final BlockHeader blockHeader = blockBuilder.number(0).buildHeader(); final BlockHeader blockHeader = blockBuilder.number(0).buildHeader();
when(blockchain.getBlockHeader(blockHeader.getHash())).thenReturn(Optional.of(blockHeader)); when(blockchain.getBlockHeader(blockHeader.getHash())).thenReturn(Optional.of(blockHeader));
@ -214,7 +219,8 @@ class BonsaiWorldStateProviderTest {
worldStateKeyValueStorage, worldStateKeyValueStorage,
blockchain, blockchain,
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()), new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT)); EvmConfiguration.DEFAULT,
throwingWorldStateHealerSupplier()));
final BlockHeader blockHeader = blockBuilder.number(0).buildHeader(); final BlockHeader blockHeader = blockBuilder.number(0).buildHeader();
@ -254,7 +260,8 @@ class BonsaiWorldStateProviderTest {
worldStateKeyValueStorage, worldStateKeyValueStorage,
blockchain, blockchain,
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()), new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT)); EvmConfiguration.DEFAULT,
throwingWorldStateHealerSupplier()));
// initial persisted state hash key // initial persisted state hash key
when(blockchain.getBlockHeader(Hash.ZERO)).thenReturn(Optional.of(blockHeaderChainA)); when(blockchain.getBlockHeader(Hash.ZERO)).thenReturn(Optional.of(blockHeaderChainA));
@ -297,7 +304,8 @@ class BonsaiWorldStateProviderTest {
DataStorageConfiguration.DEFAULT_BONSAI_CONFIG), DataStorageConfiguration.DEFAULT_BONSAI_CONFIG),
blockchain, blockchain,
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()), new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
EvmConfiguration.DEFAULT)); EvmConfiguration.DEFAULT,
throwingWorldStateHealerSupplier()));
// initial persisted state hash key // initial persisted state hash key
when(blockchain.getBlockHeader(Hash.ZERO)).thenReturn(Optional.of(blockHeaderChainA)); when(blockchain.getBlockHeader(Hash.ZERO)).thenReturn(Optional.of(blockHeaderChainA));

Loading…
Cancel
Save