diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java index be0cb1ab51e..776f5905397 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java @@ -855,12 +855,19 @@ public Optional getAndMapWorldState( .getBlockHeader(blockHash) .flatMap( blockHeader -> { - try (var ws = + try (final var worldState = worldStateArchive .getMutable(blockHeader.getStateRoot(), blockHeader.getHash(), false) + .map( + ws -> { + if (!ws.isPersistable()) { + return ws.copy(); + } + return ws; + }) .orElse(null)) { - if (ws != null) { - return Optional.ofNullable(mapper.apply(ws)); + if (worldState != null) { + return Optional.ofNullable(mapper.apply(worldState)); } } catch (Exception ex) { LOG.error("failed worldstate query for " + blockHash.toShortHexString(), ex); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/AbstractTrieLogManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/AbstractTrieLogManager.java index 7ac086bbaa1..4aa07bcf936 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/AbstractTrieLogManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/AbstractTrieLogManager.java @@ -97,16 +97,17 @@ protected abstract void addCachedLayer( @VisibleForTesting TrieLogLayer prepareTrieLog( - final BlockHeader blockHeader, - final Hash worldStateRootHash, + final BlockHeader forBlockHeader, + final Hash forWorldStateRootHash, final BonsaiWorldStateUpdater localUpdater, final BonsaiWorldStateArchive worldStateArchive, final BonsaiPersistedWorldState forWorldState) { - debugLambda(LOG, "Adding layered world state for {}", blockHeader::toLogString); - final TrieLogLayer trieLog = localUpdater.generateTrieLog(blockHeader.getBlockHash()); + debugLambda(LOG, "Adding layered world state for {}", forBlockHeader::toLogString); + final TrieLogLayer trieLog = localUpdater.generateTrieLog(forBlockHeader.getBlockHash()); trieLog.freeze(); - addCachedLayer(blockHeader, worldStateRootHash, trieLog, worldStateArchive, forWorldState); - scrubCachedLayers(blockHeader.getNumber()); + addCachedLayer( + forBlockHeader, forWorldStateRootHash, trieLog, worldStateArchive, forWorldState); + scrubCachedLayers(forBlockHeader.getNumber()); return trieLog; } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiLayeredWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiLayeredWorldState.java index 7ac14a37e77..4a45def378a 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiLayeredWorldState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiLayeredWorldState.java @@ -40,8 +40,13 @@ import org.apache.tuweni.units.bigints.UInt256; /** A World State backed first by trie log layer and then by another world state. */ -public class BonsaiLayeredWorldState implements MutableWorldState, BonsaiWorldView, WorldState { - private Optional nextWorldView; +public class BonsaiLayeredWorldState + implements MutableWorldState, + BonsaiWorldView, + WorldState, + BonsaiWorldStateKeyValueStorage.BonsaiStorageSubscriber { + private Optional nextWorldView = Optional.empty(); + private Optional newtWorldViewSubscribeId = Optional.empty(); protected final long height; protected final TrieLogLayer trieLog; private final Hash worldStateRootHash; @@ -58,7 +63,7 @@ public class BonsaiLayeredWorldState implements MutableWorldState, BonsaiWorldVi final TrieLogLayer trieLog) { this.blockchain = blockchain; this.archive = archive; - this.nextWorldView = nextWorldView; + this.setNextWorldView(nextWorldView); this.height = height; this.worldStateRootHash = worldStateRootHash; this.trieLog = trieLog; @@ -76,7 +81,34 @@ public Optional getNextWorldView() { } public void setNextWorldView(final Optional nextWorldView) { + maybeUnSubscribe(); // unsubscribe the old view this.nextWorldView = nextWorldView; + maybeSubscribe(); // subscribe the next view + } + + private void maybeSubscribe() { + nextWorldView + .filter(BonsaiPersistedWorldState.class::isInstance) + .map(BonsaiPersistedWorldState.class::cast) + .ifPresent( + worldState -> { + newtWorldViewSubscribeId = Optional.of(worldState.worldStateStorage.subscribe(this)); + }); + } + + private void maybeUnSubscribe() { + nextWorldView + .filter(BonsaiPersistedWorldState.class::isInstance) + .map(BonsaiPersistedWorldState.class::cast) + .ifPresent( + worldState -> { + newtWorldViewSubscribeId.ifPresent(worldState.worldStateStorage::unSubscribe); + }); + } + + @Override + public void close() throws Exception { + maybeUnSubscribe(); } public TrieLogLayer getTrieLog() {