Skip to content

Commit

Permalink
Hotfix 23.10.3-hotfix Log/Address Trielog rolling failure (hyperledge…
Browse files Browse the repository at this point in the history
…r#6315)

* trigger a BWS if we do not have a valid worldstate available during fcU
raise log rolling failure loglevel to error from debug

Signed-off-by: garyschulte <garyschulte@gmail.com>

* fixing on selfdestruct

Signed-off-by: Karim Taam <karim.t2am@gmail.com>
  • Loading branch information
garyschulte authored and jflo committed Jan 8, 2024
1 parent 781349d commit e9ce09f
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 60 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Support for Cancun fork on testnets
- Cancun forkids for non-mainnets [#6322](https://github.com/hyperledger/besu/pull/6322)

### Bug fixes
- Hotfix for selfdestruct preimages on bonsai [#6359]((https://github.com/hyperledger/besu/pull/6359)
- mitigation for trielog failure [#6315]((https://github.com/hyperledger/besu/pull/6315)

## 23.10.3

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,12 +492,21 @@ private boolean canRetryBlockCreation(final Throwable throwable) {
@Override
public Optional<BlockHeader> getOrSyncHeadByHash(final Hash headHash, final Hash finalizedHash) {
final var chain = protocolContext.getBlockchain();
final var maybeHeadHeader = chain.getBlockHeader(headHash);

if (maybeHeadHeader.isPresent()) {
final var maybeHead =
chain
.getBlockHeader(headHash)
// ensure we have the corresponding worldstate also:
.filter(
headHeader ->
protocolContext
.getWorldStateArchive()
.isWorldStateAvailable(
headHeader.getStateRoot(), headHeader.getBlockHash()));

if (maybeHead.isPresent()) {
LOG.atDebug()
.setMessage("BlockHeader {} is already present in blockchain")
.addArgument(maybeHeadHeader.get()::toLogString)
.setMessage("BlockHeader and world state for {} is already present")
.addArgument(maybeHead.get()::toLogString)
.log();
} else {
LOG.atDebug()
Expand All @@ -509,7 +518,7 @@ public Optional<BlockHeader> getOrSyncHeadByHash(final Hash headHash, final Hash
.syncBackwardsUntil(headHash)
.thenRun(() -> updateFinalized(finalizedHash));
}
return maybeHeadHeader;
return maybeHead;
}

private void updateFinalized(final Hash finalizedHash) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ Optional<MutableWorldState> rollMutableStateToBlockHash(
} catch (final Exception e) {
// if we fail we must clean up the updater
bonsaiUpdater.reset();
LOG.debug(
LOG.error(
"State rolling failed on "
+ mutableState.getWorldStateStorage().getClass().getSimpleName()
+ " for block hash "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,59 +341,55 @@ private void clearStorage(
final Optional<BonsaiWorldStateKeyValueStorage.BonsaiUpdater> maybeStateUpdater,
final BonsaiWorldStateUpdateAccumulator worldStateUpdater) {

maybeStateUpdater.ifPresent(
bonsaiUpdater -> {
for (final Address address : worldStateUpdater.getStorageToClear()) {
// because we are clearing persisted values we need the account root as persisted
final BonsaiAccount oldAccount =
worldStateStorage
.getAccount(address.addressHash())
.map(
bytes -> BonsaiAccount.fromRLP(BonsaiWorldState.this, address, bytes, true))
.orElse(null);
if (oldAccount == null) {
// This is when an account is both created and deleted within the scope of the same
// block. A not-uncommon DeFi bot pattern.
continue;
}
final Hash addressHash = address.addressHash();
final MerkleTrie<Bytes, Bytes> storageTrie =
createTrie(
(location, key) -> getStorageTrieNode(addressHash, location, key),
oldAccount.getStorageRoot());
try {

final StorageConsumingMap<StorageSlotKey, BonsaiValue<UInt256>> storageToDelete =
worldStateUpdater.getStorageToUpdate().get(address);
Map<Bytes32, Bytes> entriesToDelete = storageTrie.entriesFrom(Bytes32.ZERO, 256);
while (!entriesToDelete.isEmpty()) {
entriesToDelete.forEach(
(k, v) -> {
final StorageSlotKey storageSlotKey =
new StorageSlotKey(Hash.wrap(k), Optional.empty());
final UInt256 slotValue =
UInt256.fromBytes(Bytes32.leftPad(RLP.decodeValue(v)));
bonsaiUpdater.removeStorageValueBySlotHash(
address.addressHash(), storageSlotKey.getSlotHash());
storageToDelete
.computeIfAbsent(
storageSlotKey, key -> new BonsaiValue<>(slotValue, null, true))
.setPrior(slotValue);
});
entriesToDelete.keySet().forEach(storageTrie::remove);
if (entriesToDelete.size() == 256) {
entriesToDelete = storageTrie.entriesFrom(Bytes32.ZERO, 256);
} else {
break;
}
}
} catch (MerkleTrieException e) {
// need to throw to trigger the heal
throw new MerkleTrieException(
e.getMessage(), Optional.of(Address.wrap(address)), e.getHash(), e.getLocation());
}
for (final Address address : worldStateUpdater.getStorageToClear()) {
// because we are clearing persisted values we need the account root as persisted
final BonsaiAccount oldAccount =
worldStateStorage
.getAccount(address.addressHash())
.map(bytes -> BonsaiAccount.fromRLP(BonsaiWorldState.this, address, bytes, true))
.orElse(null);
if (oldAccount == null) {
// This is when an account is both created and deleted within the scope of the same
// block. A not-uncommon DeFi bot pattern.
continue;
}
final Hash addressHash = address.addressHash();
final MerkleTrie<Bytes, Bytes> storageTrie =
createTrie(
(location, key) -> getStorageTrieNode(addressHash, location, key),
oldAccount.getStorageRoot());
try {
final StorageConsumingMap<StorageSlotKey, BonsaiValue<UInt256>> storageToDelete =
worldStateUpdater.getStorageToUpdate().get(address);
Map<Bytes32, Bytes> entriesToDelete = storageTrie.entriesFrom(Bytes32.ZERO, 256);
while (!entriesToDelete.isEmpty()) {
entriesToDelete.forEach(
(k, v) -> {
final StorageSlotKey storageSlotKey =
new StorageSlotKey(Hash.wrap(k), Optional.empty());
final UInt256 slotValue = UInt256.fromBytes(Bytes32.leftPad(RLP.decodeValue(v)));
maybeStateUpdater.ifPresent(
bonsaiUpdater ->
bonsaiUpdater.removeStorageValueBySlotHash(
address.addressHash(), storageSlotKey.getSlotHash()));
storageToDelete
.computeIfAbsent(
storageSlotKey, key -> new BonsaiValue<>(slotValue, null, true))
.setPrior(slotValue);
});
entriesToDelete.keySet().forEach(storageTrie::remove);
if (entriesToDelete.size() == 256) {
entriesToDelete = storageTrie.entriesFrom(Bytes32.ZERO, 256);
} else {
break;
}
});
}
} catch (MerkleTrieException e) {
// need to throw to trigger the heal
throw new MerkleTrieException(
e.getMessage(), Optional.of(Address.wrap(address)), e.getHash(), e.getLocation());
}
}
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version=24.1.0-RC2
version=24.1.0

org.gradle.welcome=never
# Set exports/opens flags required by Google Java Format and ErrorProne plugins. (JEP-396)
Expand Down

0 comments on commit e9ce09f

Please sign in to comment.