diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/block/access/list/BlockAccessListChanges.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/block/access/list/BlockAccessListChanges.java new file mode 100644 index 00000000000..5f22dbfe4a4 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/block/access/list/BlockAccessListChanges.java @@ -0,0 +1,79 @@ +/* + * 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.mainnet.block.access.list; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.StorageSlotKey; +import org.hyperledger.besu.datatypes.Wei; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.units.bigints.UInt256; + +public final class BlockAccessListChanges { + + private BlockAccessListChanges() {} + + public static List latestChanges(final BlockAccessList blockAccessList) { + final List accountFinalChanges = new ArrayList<>(); + + for (final BlockAccessList.AccountChanges accountChanges : blockAccessList.accountChanges()) { + if (!accountChanges.hasAnyChange()) { + continue; + } + + final List storageFinalChanges = new ArrayList<>(); + for (final BlockAccessList.SlotChanges slotChanges : accountChanges.storageChanges()) { + final Optional latestStorageChange = + lastOf(slotChanges.changes()); + if (latestStorageChange.isPresent()) { + storageFinalChanges.add( + new StorageFinalChange( + slotChanges.slot(), + latestStorageChange.get().newValue() == null + ? UInt256.ZERO + : latestStorageChange.get().newValue())); + } + } + + accountFinalChanges.add( + new AccountFinalChanges( + accountChanges.address(), + lastOf(accountChanges.balanceChanges()) + .map(BlockAccessList.BalanceChange::postBalance), + lastOf(accountChanges.nonceChanges()).map(BlockAccessList.NonceChange::newNonce), + lastOf(accountChanges.codeChanges()).map(BlockAccessList.CodeChange::newCode), + storageFinalChanges)); + } + + return accountFinalChanges; + } + + public record AccountFinalChanges( + Address address, + Optional balance, + Optional nonce, + Optional code, + List storageChanges) {} + + public record StorageFinalChange(StorageSlotKey slot, UInt256 value) {} + + private static Optional lastOf(final List list) { + return list.isEmpty() ? Optional.empty() : Optional.of(list.getLast()); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/worldview/BalStateRootCalculator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/worldview/BalStateRootCalculator.java index ddca24f990a..9046345eb61 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/worldview/BalStateRootCalculator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/pathbased/bonsai/worldview/BalStateRootCalculator.java @@ -16,11 +16,9 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList; -import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.AccountChanges; -import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.SlotChanges; +import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessListChanges; import org.hyperledger.besu.ethereum.mainnet.staterootcommitter.BalRootComputation; import org.hyperledger.besu.ethereum.trie.pathbased.common.provider.WorldStateQueryParams; import org.hyperledger.besu.ethereum.trie.pathbased.common.storage.PathBasedWorldStateKeyValueStorage; @@ -29,12 +27,9 @@ import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.plugin.data.BlockHeader; -import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import org.apache.tuweni.units.bigints.UInt256; - @SuppressWarnings("rawtypes") public class BalStateRootCalculator { @@ -79,29 +74,16 @@ private static BonsaiWorldState openParentWorldState( private static void applyBalChanges( final PathBasedWorldStateUpdateAccumulator accumulator, final BlockAccessList bal) { - for (final AccountChanges changes : bal.accountChanges()) { - if (!changes.hasAnyChange()) { - continue; - } + for (final var changes : BlockAccessListChanges.latestChanges(bal)) { final Address address = changes.address(); final MutableAccount account = accumulator.getOrCreate(address); - lastOf(changes.balanceChanges()) - .ifPresent(c -> account.setBalance(Wei.wrap(c.postBalance()))); - lastOf(changes.nonceChanges()).ifPresent(c -> account.setNonce(c.newNonce())); - lastOf(changes.codeChanges()).ifPresent(c -> account.setCode(c.newCode())); + changes.balance().ifPresent(account::setBalance); + changes.nonce().ifPresent(account::setNonce); + changes.code().ifPresent(account::setCode); - for (final SlotChanges slot : changes.storageChanges()) { - lastOf(slot.changes()) - .ifPresent( - change -> - slot.slot() - .getSlotKey() - .ifPresent( - key -> { - final UInt256 value = change.newValue(); - account.setStorageValue(key, value == null ? UInt256.ZERO : value); - })); + for (final var storage : changes.storageChanges()) { + storage.slot().getSlotKey().ifPresent(key -> account.setStorageValue(key, storage.value())); } } accumulator.clearAccountsThatAreEmpty(); @@ -116,8 +98,4 @@ private static BalRootComputation computeRoot(final PathBasedWorldState worldSta updater.commit(); return new BalRootComputation(root, accumulator); } - - private static Optional lastOf(final List list) { - return list.isEmpty() ? Optional.empty() : Optional.of(list.getLast()); - } } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapDownloaderFactory.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapDownloaderFactory.java index cfe8107f72b..823d8896547 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapDownloaderFactory.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapDownloaderFactory.java @@ -92,7 +92,12 @@ public static Optional createSnapDownloader( chainSyncState != null ? new PivotSyncState(chainSyncState.pivotBlockHeader(), false) : PivotSyncState.EMPTY_SYNC_STATE; - final SnapSyncProcessState snapSyncState = new SnapSyncProcessState(pivotSyncState); + final Optional firstPivotHeader = + chainSyncState != null + ? Optional.of(chainSyncState.firstPivotBlockHeader()) + : Optional.empty(); + final SnapSyncProcessState snapSyncState = + new SnapSyncProcessState(pivotSyncState, firstPivotHeader); final InMemoryTasksPriorityQueues snapTaskCollection = createSnapWorldStateDownloaderTaskCollection(); @@ -101,6 +106,7 @@ public static Optional createSnapDownloader( ethContext, snapContext, protocolContext, + protocolSchedule, worldStateStorageCoordinator, snapTaskCollection, syncConfig.getSnapSyncConfiguration(), diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapSyncDownloader.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapSyncDownloader.java index 8d0918deabe..f31313757eb 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapSyncDownloader.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapSyncDownloader.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.ethereum.eth.sync.snapsync; +import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.sync.common.PivotSyncActions; import org.hyperledger.besu.ethereum.eth.sync.common.PivotSyncDownloader; import org.hyperledger.besu.ethereum.eth.sync.common.PivotSyncState; @@ -22,6 +23,7 @@ import org.hyperledger.besu.metrics.SyncDurationMetrics; import java.nio.file.Path; +import java.util.Optional; import java.util.concurrent.CompletableFuture; public class SnapSyncDownloader extends PivotSyncDownloader { @@ -50,7 +52,11 @@ protected CompletableFuture start(final PivotSyncState fastSyncS @Override protected PivotSyncState storeState(final PivotSyncState fastSyncState) { + final Optional firstPivotBlockHeader = + initialPivotSyncState instanceof SnapSyncProcessState snapSyncState + ? snapSyncState.getFirstPivotBlockHeader().or(fastSyncState::getPivotBlockHeader) + : fastSyncState.getPivotBlockHeader(); initialPivotSyncState = fastSyncState; - return new SnapSyncProcessState(fastSyncState); + return new SnapSyncProcessState(fastSyncState, firstPivotBlockHeader); } } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapSyncProcessState.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapSyncProcessState.java index a98654a1bb8..9aa3f1328bb 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapSyncProcessState.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapSyncProcessState.java @@ -14,10 +14,13 @@ */ package org.hyperledger.besu.ethereum.eth.sync.snapsync; +import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.SealableBlockHeader; import org.hyperledger.besu.ethereum.eth.sync.common.PivotSyncState; import org.hyperledger.besu.ethereum.eth.sync.snapsync.request.SnapDataRequest; +import java.util.Optional; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,16 +31,23 @@ public class SnapSyncProcessState extends PivotSyncState { private static final Logger LOG = LoggerFactory.getLogger(SnapSyncProcessState.class); + private final Optional firstPivotBlockHeader; private boolean isHealTrieInProgress; private boolean isHealFlatDatabaseInProgress; private boolean isWaitingBlockchain; - public SnapSyncProcessState(final PivotSyncState fastSyncState) { + public SnapSyncProcessState( + final PivotSyncState fastSyncState, final Optional firstPivotBlockHeader) { super( fastSyncState.getPivotBlockNumber(), fastSyncState.getPivotBlockHash(), fastSyncState.getPivotBlockHeader(), fastSyncState.isSourceTrusted()); + this.firstPivotBlockHeader = firstPivotBlockHeader; + } + + public Optional getFirstPivotBlockHeader() { + return firstPivotBlockHeader; } public boolean isHealTrieInProgress() { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldDownloadState.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldDownloadState.java index 5ffe2297dbb..ddb7127796b 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldDownloadState.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldDownloadState.java @@ -16,6 +16,7 @@ import static org.hyperledger.besu.ethereum.eth.sync.snapsync.request.SnapDataRequest.createAccountFlatHealingRangeRequest; import static org.hyperledger.besu.ethereum.eth.sync.snapsync.request.SnapDataRequest.createAccountTrieNodeDataRequest; +import static org.hyperledger.besu.ethereum.eth.sync.snapsync.request.SnapDataRequest.createBlockAccessListDataRequest; import static org.hyperledger.besu.ethereum.worldstate.WorldStateStorageCoordinator.applyForStrategy; import org.hyperledger.besu.ethereum.chain.BlockAddedObserver; @@ -32,6 +33,7 @@ import org.hyperledger.besu.ethereum.eth.sync.snapsync.request.heal.AccountFlatDatabaseHealingRangeRequest; import org.hyperledger.besu.ethereum.eth.sync.snapsync.request.heal.StorageFlatDatabaseHealingRangeRequest; import org.hyperledger.besu.ethereum.eth.sync.worldstate.WorldDownloadState; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.trie.RangeManager; import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.storage.BonsaiWorldStateKeyValueStorage; import org.hyperledger.besu.ethereum.worldstate.FlatDbMode; @@ -51,6 +53,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; @@ -90,6 +93,7 @@ public class SnapWorldDownloadState extends WorldDownloadState private final SnapSyncStatePersistenceManager snapContext; private final SnapSyncProcessState snapSyncState; + private final ProtocolSchedule protocolSchedule; // blockchain private final Blockchain blockchain; @@ -101,12 +105,14 @@ public class SnapWorldDownloadState extends WorldDownloadState private final AtomicBoolean trieHealStartedBefore = new AtomicBoolean(false); private final AtomicBoolean worldStateHealFinishedNotified = new AtomicBoolean(false); + private final AtomicBoolean blockAccessListHealEnqueued = new AtomicBoolean(false); public SnapWorldDownloadState( final WorldStateStorageCoordinator worldStateStorageCoordinator, final SnapSyncStatePersistenceManager snapContext, final Blockchain blockchain, final SnapSyncProcessState snapSyncState, + final ProtocolSchedule protocolSchedule, final InMemoryTasksPriorityQueues pendingRequests, final int maxRequestsWithoutProgress, final long minMillisBeforeStalling, @@ -124,6 +130,7 @@ public SnapWorldDownloadState( this.snapContext = snapContext; this.blockchain = blockchain; this.snapSyncState = snapSyncState; + this.protocolSchedule = protocolSchedule; this.metricsManager = metricsManager; this.blockObserverId = blockchain.observeBlockAdded(createBlockchainObserver()); this.ethContext = ethContext; @@ -207,6 +214,9 @@ else if (pivotBlockSelector.isBlockchainBehind()) { if (!snapSyncState.isHealFlatDatabaseInProgress() && (worldStateStorageCoordinator.isMatchingFlatMode(FlatDbMode.FULL) || worldStateStorageCoordinator.isMatchingFlatMode(FlatDbMode.ARCHIVE))) { + if (enqueueBlockAccessListsForPivotRangeIfRequired()) { + return false; + } startFlatDatabaseHeal(header); } // If the flat database healing process is in progress or the flat database mode is not FULL @@ -305,6 +315,50 @@ public synchronized void startFlatDatabaseHeal(final BlockHeader header) { createAccountFlatHealingRangeRequest(header.getStateRoot(), key, value))); } + private boolean enqueueBlockAccessListsForPivotRangeIfRequired() { + if (!blockAccessListHealEnqueued.compareAndSet(false, true)) { + return false; + } + + final Optional maybeFirstPivotHeader = snapSyncState.getFirstPivotBlockHeader(); + final Optional maybeLastPivotHeader = snapSyncState.getPivotBlockHeader(); + + LOG.debug("Starting BAL apply attempt"); + + if (maybeFirstPivotHeader.isEmpty() || maybeLastPivotHeader.isEmpty()) { + LOG.debug( + "Skipping BAL apply - firstPivotHeader={}, lastPivotHeader={}", + maybeFirstPivotHeader, + maybeLastPivotHeader); + return false; + } + + final BlockHeader firstPivotHeader = maybeFirstPivotHeader.get(); + if (!protocolSchedule.getByBlockHeader(firstPivotHeader).isBlockAccessListEnabled()) { + LOG.debug("Skipping BAL apply - BALs not enabled on first pivot {}", firstPivotHeader); + return false; + } + + final long fromBlock = firstPivotHeader.getNumber(); + final long toBlock = maybeLastPivotHeader.get().getNumber(); + if (toBlock < fromBlock) { + LOG.error("Attempted to apply BALs with fromBlock {} > {} toBlock", fromBlock, toBlock); + return false; + } + + LOG.info("Queueing block access list heal from block {} to {}", fromBlock, toBlock); + for (long blockNumber = fromBlock; blockNumber <= toBlock; blockNumber++) { + final Optional maybeBlockHeader = blockchain.getBlockHeader(blockNumber); + if (maybeBlockHeader.isPresent()) { + final BlockHeader blockHeader = maybeBlockHeader.get(); + enqueueRequest(createBlockAccessListDataRequest(blockHeader.getStateRoot(), blockHeader)); + } else { + LOG.warn("Unable to queue block access list heal for missing block {}", blockNumber); + } + } + return !pendingBlockAccessListRequests.isEmpty(); + } + @Override public synchronized void enqueueRequest(final SnapDataRequest request) { if (!internalFuture.isDone()) { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldStateDownloader.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldStateDownloader.java index 92fbcba8c79..c229fd05a18 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldStateDownloader.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldStateDownloader.java @@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.eth.sync.snapsync.request.AccountRangeDataRequest; import org.hyperledger.besu.ethereum.eth.sync.snapsync.request.SnapDataRequest; import org.hyperledger.besu.ethereum.eth.sync.worldstate.WorldStateDownloader; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.trie.RangeManager; import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.storage.BonsaiWorldStateKeyValueStorage; import org.hyperledger.besu.ethereum.worldstate.WorldStateStorageCoordinator; @@ -65,6 +66,7 @@ public class SnapWorldStateDownloader implements WorldStateDownloader { private final int maxOutstandingRequests; private final int maxNodeRequestsWithoutProgress; private final ProtocolContext protocolContext; + private final ProtocolSchedule protocolSchedule; private final WorldStateStorageCoordinator worldStateStorageCoordinator; private final AtomicReference downloadState = new AtomicReference<>(); @@ -76,6 +78,7 @@ public SnapWorldStateDownloader( final EthContext ethContext, final SnapSyncStatePersistenceManager snapContext, final ProtocolContext protocolContext, + final ProtocolSchedule protocolSchedule, final WorldStateStorageCoordinator worldStateStorageCoordinator, final InMemoryTasksPriorityQueues snapTaskCollection, final SnapSyncConfiguration snapSyncConfiguration, @@ -87,6 +90,7 @@ public SnapWorldStateDownloader( final SyncDurationMetrics syncDurationMetrics) { this.ethContext = ethContext; this.protocolContext = protocolContext; + this.protocolSchedule = protocolSchedule; this.worldStateStorageCoordinator = worldStateStorageCoordinator; this.snapContext = snapContext; this.snapTaskCollection = snapTaskCollection; @@ -160,6 +164,7 @@ public CompletableFuture run( snapContext, protocolContext.getBlockchain(), snapSyncState, + protocolSchedule, snapTaskCollection, maxNodeRequestsWithoutProgress, minMillisBeforeStalling, diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/BlockAccessListDataRequest.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/BlockAccessListDataRequest.java index 9db8415f505..74a95268a96 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/BlockAccessListDataRequest.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/BlockAccessListDataRequest.java @@ -15,18 +15,29 @@ package org.hyperledger.besu.ethereum.eth.sync.snapsync.request; import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.sync.snapsync.RequestType; import org.hyperledger.besu.ethereum.eth.sync.snapsync.SnapSyncConfiguration; import org.hyperledger.besu.ethereum.eth.sync.snapsync.SnapSyncProcessState; import org.hyperledger.besu.ethereum.eth.sync.snapsync.SnapWorldDownloadState; import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList; +import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessListChanges; +import org.hyperledger.besu.ethereum.rlp.RLP; +import org.hyperledger.besu.ethereum.trie.common.PmtStateTrieAccountValue; +import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.storage.BonsaiWorldStateKeyValueStorage; +import org.hyperledger.besu.ethereum.trie.patricia.StoredMerklePatriciaTrie; import org.hyperledger.besu.ethereum.worldstate.WorldStateStorageCoordinator; import org.hyperledger.besu.plugin.services.storage.WorldStateKeyValueStorage; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Stream; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.units.bigints.UInt256; + public class BlockAccessListDataRequest extends SnapDataRequest { private final BlockHeader blockHeader; @@ -56,10 +67,70 @@ protected int doPersist( final SnapWorldDownloadState downloadState, final SnapSyncProcessState snapSyncState, final SnapSyncConfiguration snapSyncConfiguration) { - // TODO: Collect changes from BALs to the updater and commit at the end. + if (!(updater instanceof BonsaiWorldStateKeyValueStorage.Updater bonsaiUpdater)) { + return 0; + } + + final StoredMerklePatriciaTrie accountTrie = + new StoredMerklePatriciaTrie<>( + worldStateStorageCoordinator::getAccountStateTrieNode, + Bytes32.wrap(getRootHash().getBytes()), + Function.identity(), + Function.identity()); + + blockAccessList.ifPresent( + bal -> { + for (final var accountChanges : BlockAccessListChanges.latestChanges(bal)) { + final Hash accountHash = Hash.hash(accountChanges.address().getBytes()); + + final PmtStateTrieAccountValue trieAccountValue = + accountTrie + .get(accountHash.getBytes()) + .map(RLP::input) + .map(PmtStateTrieAccountValue::readFrom) + .orElse( + new PmtStateTrieAccountValue( + 0, Wei.ZERO, Hash.EMPTY_TRIE_HASH, Hash.EMPTY)); + + final var updatedCode = accountChanges.code(); + final Hash updatedCodeHash = + updatedCode.map(Hash::hash).orElse(trieAccountValue.getCodeHash()); + updatedCode.ifPresent( + code -> bonsaiUpdater.putCode(accountHash, updatedCodeHash, code)); + + final Hash updatedStorageRoot = trieAccountValue.getStorageRoot(); + if (!accountChanges.storageChanges().isEmpty()) { + applyStorageChanges(accountHash, accountChanges, bonsaiUpdater); + } + + final PmtStateTrieAccountValue updatedValue = + new PmtStateTrieAccountValue( + accountChanges.nonce().orElse(trieAccountValue.getNonce()), + accountChanges.balance().orElse(trieAccountValue.getBalance()), + updatedStorageRoot, + updatedCodeHash); + bonsaiUpdater.putAccountInfoState(accountHash, RLP.encode(updatedValue::writeTo)); + } + }); + return 0; } + private void applyStorageChanges( + final Hash accountHash, + final BlockAccessListChanges.AccountFinalChanges accountChanges, + final BonsaiWorldStateKeyValueStorage.Updater bonsaiUpdater) { + for (final var storageChange : accountChanges.storageChanges()) { + final Hash slotHash = storageChange.slot().getSlotHash(); + final UInt256 value = storageChange.value(); + if (value.equals(UInt256.ZERO)) { + bonsaiUpdater.removeStorageValueBySlotHash(accountHash, slotHash); + } else { + bonsaiUpdater.putStorageValueBySlotHash(accountHash, slotHash, value.toBytes()); + } + } + } + @Override public boolean isResponseReceived() { return blockAccessList.isPresent(); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldDownloadStateTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldDownloadStateTest.java index 090ce726902..e2538992175 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldDownloadStateTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldDownloadStateTest.java @@ -40,6 +40,8 @@ import org.hyperledger.besu.ethereum.eth.sync.snapsync.request.BytecodeRequest; import org.hyperledger.besu.ethereum.eth.sync.snapsync.request.SnapDataRequest; import org.hyperledger.besu.ethereum.eth.sync.worldstate.WorldStateDownloadProcess; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.trie.RangeManager; import org.hyperledger.besu.ethereum.trie.forest.storage.ForestWorldStateKeyValueStorage; import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.storage.BonsaiWorldStateKeyValueStorage; @@ -95,6 +97,8 @@ public class SnapWorldDownloadStateTest { private final DynamicPivotBlockSelector dynamicPivotBlockManager = mock(DynamicPivotBlockSelector.class); private final EthContext ethContext = mock(EthContext.class); + private final ProtocolSchedule protocolSchedule = mock(ProtocolSchedule.class); + private final ProtocolSpec protocolSpec = mock(ProtocolSpec.class); private final TestClock clock = new TestClock(); private SnapWorldDownloadState downloadState; @@ -114,6 +118,8 @@ public Stream provideArguments(final ExtensionContext conte public void setUp(final DataStorageFormat storageFormat) { when(metricsManager.getMetricsSystem()).thenReturn(new NoOpMetricsSystem()); + when(protocolSchedule.getByBlockHeader(any(BlockHeader.class))).thenReturn(protocolSpec); + when(protocolSpec.getBlockAccessListFactory()).thenReturn(Optional.empty()); if (storageFormat == DataStorageFormat.BONSAI) { worldStateKeyValueStorage = @@ -133,6 +139,7 @@ public void setUp(final DataStorageFormat storageFormat) { snapContext, blockchain, snapSyncState, + protocolSchedule, pendingRequests, MAX_REQUESTS_WITHOUT_PROGRESS, MIN_MILLIS_BEFORE_STALLING,