diff --git a/execution_chain/core/chain/forked_chain/chain_private.nim b/execution_chain/core/chain/forked_chain/chain_private.nim index 0f997d1222..437707bfe0 100644 --- a/execution_chain/core/chain/forked_chain/chain_private.nim +++ b/execution_chain/core/chain/forked_chain/chain_private.nim @@ -1,5 +1,5 @@ # nimbus-execution-client -# Copyright (c) 2025 Status Research & Development GmbH +# Copyright (c) 2025-2026 Status Research & Development GmbH # Licensed under either of # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) # * MIT license ([LICENSE-MIT](LICENSE-MIT)) @@ -53,6 +53,30 @@ proc writeBaggage*( generatedBal.get(), ) +proc applyBlockAccessList(vmState: BaseVMState, header: Header, bal: BlockAccessListRef) = + # This function assumes that the bal has been validated and therefore + # each of the change lists should already be sorted in ascending order + # by blockAccessIndex. + # We only need to apply the last state change for each of the change lists + # since these will overwrite earlier changes anyway. + vmState.mutateLedger: + for accChanges in bal[]: + if accChanges.balanceChanges.len() > 0: + db.setBalance(accChanges.address, accChanges.balanceChanges[^1].postBalance) + if accChanges.nonceChanges.len() > 0: + db.setNonce(accChanges.address, accChanges.nonceChanges[^1].newNonce) + if accChanges.codeChanges.len() > 0: + db.setCode(accChanges.address, accChanges.codeChanges[^1].newCode) + + for storageChange in accChanges.storageChanges: + if storageChange.changes.len() > 0: + db.setStorage(accChanges.address, storageChange.slot, storageChange.changes[^1].newValue) + + db.persist( + clearEmptyAccount = vmState.com.isSpuriousOrLater(header.number), + clearCache = true + ) + proc processBlock*( c: ForkedChainRef, parentBlk: BlockRef, @@ -108,7 +132,11 @@ proc processBlock*( c.badBlocks.put(blkHash, (blk, vmState.blockAccessList)) return err(error) - if not vmState.com.statelessProviderEnabled: + if finalized and blockAccessList.isSome(): + # The bal is validated against the header balHash in validateHeaderAndKinship + # so here we skip processing of the block and simply apply the bal state diff. + vmState.applyBlockAccessList(header, blockAccessList.get()) + elif not vmState.com.statelessProviderEnabled: processBlock() else: # Clear the caches before executing the block to ensure we collect the correct diff --git a/execution_chain/core/chain/forked_chain/chain_serialize.nim b/execution_chain/core/chain/forked_chain/chain_serialize.nim index 03e4cf190f..98703458e1 100644 --- a/execution_chain/core/chain/forked_chain/chain_serialize.nim +++ b/execution_chain/core/chain/forked_chain/chain_serialize.nim @@ -1,5 +1,5 @@ # nimbus-execution-client -# Copyright (c) 2025 Status Research & Development GmbH +# Copyright (c) 2025-2026 Status Research & Development GmbH # Licensed under either of # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) # * MIT license ([LICENSE-MIT](LICENSE-MIT)) @@ -19,7 +19,8 @@ import ../../../db/core_db, ../../../db/fcu_db, ../../../db/storage_types, - ../../../utils/utils + ../../../utils/utils, + ../../../common logScope: topics = "forked chain" @@ -125,6 +126,11 @@ proc replayBlock(fc: ForkedChainRef; let parentFrame = parent.txFrame txFrame = parentFrame.txFrameBegin() + blockAccessList = + if fc.com.isAmsterdamOrLater(blk.header.timestamp): + ?parentFrame.getBlockAccessList(blk.hash) + else: + Opt.none(BlockAccessListRef) # Set finalized to true in order to skip the stateroot check when replaying the # block because the blocks should have already been checked previously during @@ -133,7 +139,7 @@ proc replayBlock(fc: ForkedChainRef; parent, txFrame, blk.blk, - Opt.none(BlockAccessListRef), + blockAccessList, blk.hash, finalized = true ).valueOr: