From ef12c1411c0bfc5833f4725d74ee8b961f9d4bc7 Mon Sep 17 00:00:00 2001 From: ahamlat Date: Mon, 13 Feb 2023 19:23:20 +0100 Subject: [PATCH] Fix Layered World State issue (#5076) * Use the copy during prepareTrieLog instead of saveTrieLog * add final flag on BonsaiWorldStateUpdater * Use a copy of BonsaiInMemoryWorldState inside prepareTrieLog * add link to persisted worldstate storage * fix tests * Make a copy of the worldstate after committing changes to the trielog * spotless + remove maybeUnSubscribe in setNextWorldView * subscribe storage on layered worldstate * fix null issue * not close layered worldstate during getAccount * clean code * Add changelog entry --------- Signed-off-by: Ameziane H Signed-off-by: Karim TAAM Co-authored-by: Karim TAAM --- CHANGELOG.md | 2 +- .../ethereum/api/query/BlockchainQueries.java | 13 +++++-- .../bonsai/AbstractTrieLogManager.java | 13 ++++--- .../bonsai/BonsaiLayeredWorldState.java | 38 +++++++++++++++++-- .../AbstractPendingTransactionsSorter.java | 6 ++- 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8221247ad42..73d0b045af6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ tests are updated to use EC private keys instead of RSA keys. ### Bug Fixes - Mitigation fix for stale bonsai code storage leading to log rolling issues on contract recreates [#4906](https://github.com/hyperledger/besu/pull/4906) - +- Ensure latest cached layered worldstate is subscribed to storage, fix problem with RPC calls using 'latest' [#5076](https://github.com/hyperledger/besu/pull/5076) ## 23.1.0-RC1 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() { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsSorter.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsSorter.java index 1f3c938ea99..cea31614f29 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsSorter.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsSorter.java @@ -428,7 +428,11 @@ private TransactionAddedStatus addTransaction( LOG, "Transaction {} not added because nonce too far in the future for sender {}", transaction::toTraceLog, - maybeSenderAccount::toString); + () -> + maybeSenderAccount + .map(Account::getAddress) + .map(Address::toString) + .orElse("unknown")); return NONCE_TOO_FAR_IN_FUTURE_FOR_SENDER; }