From 7c9bcddb1758f25c13a358f58989fb7d8fc17fa1 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 12 Sep 2024 17:05:55 +0100 Subject: [PATCH 1/7] Prewarm tx addresses in parallel --- .../BeaconBlockRoot/BeaconBlockRootHandler.cs | 9 + .../IBeaconBlockRootHandler.cs | 1 + .../Processing/BlockCachePreWarmer.cs | 183 ++++++++++++------ .../Processing/BlockProcessor.cs | 2 +- .../Processing/IBlockCachePreWarmer.cs | 2 +- .../Nethermind.State/IWorldState.cs | 1 + src/Nethermind/Nethermind.State/WorldState.cs | 1 + 7 files changed, 142 insertions(+), 57 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs index 78e482250b6..d1ab34c6971 100644 --- a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs +++ b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs @@ -15,6 +15,15 @@ public class BeaconBlockRootHandler(ITransactionProcessor processor) : IBeaconBl { private const long GasLimit = 30_000_000L; + public Address? BeaconRootsAddress(Block block, IReleaseSpec spec) + { + BlockHeader? header = block.Header; + var canInsertBeaconRoot = spec.IsBeaconBlockRootAvailable + && !header.IsGenesis + && header.ParentBeaconBlockRoot is not null; + return !canInsertBeaconRoot ? null : spec.Eip4788ContractAddress ?? Eip4788Constants.BeaconRootsAddress; + } + public void StoreBeaconRoot(Block block, IReleaseSpec spec) { BlockHeader? header = block.Header; diff --git a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs index 047af4ffb99..afba41f1401 100644 --- a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs +++ b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs @@ -7,5 +7,6 @@ namespace Nethermind.Blockchain.BeaconBlockRoot; public interface IBeaconBlockRootHandler { + Address? BeaconRootsAddress(Block block, IReleaseSpec spec); void StoreBeaconRoot(Block block, IReleaseSpec spec); } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs index 3f4e8d08b16..8b24c4f0531 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs @@ -24,7 +24,7 @@ public class BlockCachePreWarmer(ReadOnlyTxProcessingEnvFactory envFactory, ISpe private readonly ObjectPool _systemTransactionPool = new DefaultObjectPool(new DefaultPooledObjectPolicy(), Environment.ProcessorCount); private readonly ILogger _logger = logManager.GetClassLogger(); - public Task PreWarmCaches(Block suggestedBlock, Hash256? parentStateRoot, CancellationToken cancellationToken = default) + public Task PreWarmCaches(Block suggestedBlock, Hash256? parentStateRoot, Address? beaconRootsAddress, CancellationToken cancellationToken = default) { if (targetWorldState is not null) { @@ -36,7 +36,7 @@ public Task PreWarmCaches(Block suggestedBlock, Hash256? parentStateRoot, Cancel if (!IsGenesisBlock(parentStateRoot) && Environment.ProcessorCount > 2 && !cancellationToken.IsCancellationRequested) { // Do not pass cancellation token to the task, we don't want exceptions to be thrown in main processing thread - return Task.Run(() => PreWarmCachesParallel(suggestedBlock, parentStateRoot, cancellationToken)); + return Task.Run(() => PreWarmCachesParallel(suggestedBlock, parentStateRoot, beaconRootsAddress, cancellationToken)); } } @@ -44,13 +44,13 @@ public Task PreWarmCaches(Block suggestedBlock, Hash256? parentStateRoot, Cancel } // Parent state root is null for genesis block - private bool IsGenesisBlock(Hash256? parentStateRoot) => parentStateRoot is null; + private static bool IsGenesisBlock(Hash256? parentStateRoot) => parentStateRoot is null; public void ClearCaches() => targetWorldState?.ClearCache(); public Task ClearCachesInBackground() => targetWorldState?.ClearCachesInBackground() ?? Task.CompletedTask; - private void PreWarmCachesParallel(Block suggestedBlock, Hash256 parentStateRoot, CancellationToken cancellationToken) + private void PreWarmCachesParallel(Block suggestedBlock, Hash256 parentStateRoot, Address? beaconRootsAddress, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return; @@ -65,8 +65,12 @@ private void PreWarmCachesParallel(Block suggestedBlock, Hash256 parentStateRoot if (_logger.IsDebug) _logger.Debug($"Started pre-warming caches for block {suggestedBlock.Number}."); ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = physicalCoreCount - 1, CancellationToken = cancellationToken }; - IReleaseSpec spec = specProvider.GetSpec(suggestedBlock.Header); + // Run address warmer ahead of transactions warmer, but queue to ThreadPool so it doesn't block the txs + ThreadPool.UnsafeQueueUserWorkItem( + new AddressWarmer(parallelOptions, suggestedBlock, parentStateRoot, beaconRootsAddress, this), preferLocal: false); + + IReleaseSpec spec = specProvider.GetSpec(suggestedBlock.Header); WarmupTransactions(parallelOptions, spec, suggestedBlock, parentStateRoot); WarmupWithdrawals(parallelOptions, spec, suggestedBlock, parentStateRoot); @@ -76,75 +80,144 @@ private void PreWarmCachesParallel(Block suggestedBlock, Hash256 parentStateRoot { if (_logger.IsDebug) _logger.Debug($"Pre-warming caches cancelled for block {suggestedBlock.Number}."); } + } - void WarmupWithdrawals(ParallelOptions parallelOptions, IReleaseSpec spec, Block block, Hash256 stateRoot) + private void WarmupWithdrawals(ParallelOptions parallelOptions, IReleaseSpec spec, Block block, Hash256 stateRoot) + { + if (parallelOptions.CancellationToken.IsCancellationRequested) return; + if (spec.WithdrawalsEnabled && block.Withdrawals is not null) { - if (parallelOptions.CancellationToken.IsCancellationRequested) return; - if (spec.WithdrawalsEnabled && block.Withdrawals is not null) - { - int progress = 0; - Parallel.For(0, block.Withdrawals.Length, parallelOptions, - _ => + int progress = 0; + Parallel.For(0, block.Withdrawals.Length, parallelOptions, + _ => + { + IReadOnlyTxProcessorSource env = _envPool.Get(); + int i = 0; + try { - IReadOnlyTxProcessorSource env = _envPool.Get(); - int i = 0; - try - { - using IReadOnlyTxProcessingScope scope = env.Build(stateRoot); - // Process withdrawals in sequential order, rather than partitioning scheme from Parallel.For - // Interlocked.Increment returns the incremented value, so subtract 1 to start at 0 - i = Interlocked.Increment(ref progress) - 1; - scope.WorldState.WarmUp(block.Withdrawals[i].Address); - } - catch (Exception ex) - { - if (_logger.IsDebug) _logger.Error($"Error pre-warming withdrawal {i}", ex); - } - finally - { - _envPool.Return(env); - } - }); + using IReadOnlyTxProcessingScope scope = env.Build(stateRoot); + // Process withdrawals in sequential order, rather than partitioning scheme from Parallel.For + // Interlocked.Increment returns the incremented value, so subtract 1 to start at 0 + i = Interlocked.Increment(ref progress) - 1; + scope.WorldState.WarmUp(block.Withdrawals[i].Address); + } + catch (Exception ex) + { + if (_logger.IsDebug) _logger.Error($"Error pre-warming withdrawal {i}", ex); + } + finally + { + _envPool.Return(env); + } + }); + } + } + + private void WarmupTransactions(ParallelOptions parallelOptions, IReleaseSpec spec, Block block, Hash256 stateRoot) + { + if (parallelOptions.CancellationToken.IsCancellationRequested) return; + + int progress = 0; + Parallel.For(0, block.Transactions.Length, parallelOptions, _ => + { + using ThreadExtensions.Disposable handle = Thread.CurrentThread.BoostPriority(); + IReadOnlyTxProcessorSource env = _envPool.Get(); + SystemTransaction systemTransaction = _systemTransactionPool.Get(); + Transaction? tx = null; + try + { + // Process transactions in sequential order, rather than partitioning scheme from Parallel.For + // Interlocked.Increment returns the incremented value, so subtract 1 to start at 0 + int i = Interlocked.Increment(ref progress) - 1; + // If the transaction has already been processed or being processed, exit early + if (block.TransactionProcessed > i) return; + + tx = block.Transactions[i]; + tx.CopyTo(systemTransaction); + using IReadOnlyTxProcessingScope scope = env.Build(stateRoot); + if (spec.UseTxAccessLists) + { + scope.WorldState.WarmUp(tx.AccessList); // eip-2930 + } + TransactionResult result = scope.TransactionProcessor.Trace(systemTransaction, new BlockExecutionContext(block.Header.Clone()), NullTxTracer.Instance); + if (_logger.IsTrace) _logger.Trace($"Finished pre-warming cache for tx[{i}] {tx.Hash} with {result}"); + } + catch (Exception ex) + { + if (_logger.IsDebug) _logger.Error($"Error pre-warming cache {tx?.Hash}", ex); + } + finally + { + _systemTransactionPool.Return(systemTransaction); + _envPool.Return(env); + } + }); + } + + private class AddressWarmer(ParallelOptions parallelOptions, Block block, Hash256 stateRoot, Address? beaconRootsAddress, BlockCachePreWarmer preWarmer) + : IThreadPoolWorkItem + { + private readonly ParallelOptions ParallelOptions = parallelOptions; + private readonly Block Block = block; + private readonly Hash256 StateRoot = stateRoot; + private readonly BlockCachePreWarmer PreWarmer = preWarmer; + private readonly Address? BeaconRootsAddress = beaconRootsAddress; + + void IThreadPoolWorkItem.Execute() + { + IReadOnlyTxProcessorSource env = PreWarmer._envPool.Get(); + try + { + using IReadOnlyTxProcessingScope scope = env.Build(StateRoot); + WarmupAddresses(ParallelOptions, Block, scope); + } + catch (Exception ex) + { + if (PreWarmer._logger.IsInfo) PreWarmer._logger.Error($"Error pre-warming addresses", ex); + } + finally + { + PreWarmer._envPool.Return(env); } } - void WarmupTransactions(ParallelOptions parallelOptions, IReleaseSpec spec, Block block, Hash256 stateRoot) + private void WarmupAddresses(ParallelOptions parallelOptions, Block block, IReadOnlyTxProcessingScope scope) { if (parallelOptions.CancellationToken.IsCancellationRequested) return; + // Warm up BeaconRootsAddress first as is first used + if (BeaconRootsAddress is not null) + { + scope.WorldState.WarmUp(BeaconRootsAddress); + // Contract accesses this storage cell + scope.WorldState.WarmUp(new StorageCell(BeaconRootsAddress, block.Timestamp % 8191)); + } + int progress = 0; - Parallel.For(0, block.Transactions.Length, parallelOptions, _ => + Parallel.For(0, block.Transactions.Length, parallelOptions, + _ => { - using ThreadExtensions.Disposable handle = Thread.CurrentThread.BoostPriority(); - IReadOnlyTxProcessorSource env = _envPool.Get(); - SystemTransaction systemTransaction = _systemTransactionPool.Get(); - Transaction? tx = null; + int i = 0; try { - // Process transactions in sequential order, rather than partitioning scheme from Parallel.For + // Process addresses in sequential order, rather than partitioning scheme from Parallel.For // Interlocked.Increment returns the incremented value, so subtract 1 to start at 0 - int i = Interlocked.Increment(ref progress) - 1; - // If the transaction has already been processed or being processed, exit early - if (block.TransactionProcessed > i) return; - - tx = block.Transactions[i]; - tx.CopyTo(systemTransaction); - using IReadOnlyTxProcessingScope scope = env.Build(stateRoot); - if (spec.UseTxAccessLists) + i = Interlocked.Increment(ref progress) - 1; + Transaction tx = block.Transactions[i]; + Address? sender = tx.SenderAddress; + if (sender is not null) + { + scope.WorldState.WarmUp(sender); + } + Address to = tx.To; + if (to is not null) { - scope.WorldState.WarmUp(tx.AccessList); // eip-2930 + scope.WorldState.WarmUp(to); } - TransactionResult result = scope.TransactionProcessor.Trace(systemTransaction, new BlockExecutionContext(block.Header.Clone()), NullTxTracer.Instance); - if (_logger.IsTrace) _logger.Trace($"Finished pre-warming cache for tx[{i}] {tx.Hash} with {result}"); } catch (Exception ex) { - if (_logger.IsDebug) _logger.Error($"Error pre-warming cache {tx?.Hash}", ex); - } - finally - { - _systemTransactionPool.Return(systemTransaction); - _envPool.Return(env); + if (PreWarmer._logger.IsInfo) PreWarmer._logger.Error($"Error pre-warming addresses {i}", ex); } }); } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 53e0fc89a30..a512a43fd37 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -110,7 +110,7 @@ the previous head state.*/ using CancellationTokenSource cancellationTokenSource = new(); Task? preWarmTask = suggestedBlock.Transactions.Length < 3 ? null - : preWarmer?.PreWarmCaches(suggestedBlock, preBlockStateRoot!, cancellationTokenSource.Token); + : preWarmer?.PreWarmCaches(suggestedBlock, preBlockStateRoot!, _beaconBlockRootHandler.BeaconRootsAddress(suggestedBlock, _specProvider.GetSpec(suggestedBlock.Header)), cancellationTokenSource.Token); (Block processedBlock, TxReceipt[] receipts) = ProcessOne(suggestedBlock, options, blockTracer); // Block is processed, we can cancel the prewarm task if (preWarmTask is not null) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs b/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs index aa6d9769005..cffb2a1ceb4 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs @@ -10,7 +10,7 @@ namespace Nethermind.Consensus.Processing; public interface IBlockCachePreWarmer { - Task PreWarmCaches(Block suggestedBlock, Hash256 parentStateRoot, CancellationToken cancellationToken = default); + Task PreWarmCaches(Block suggestedBlock, Hash256 parentStateRoot, Address? beaconRootsAddress, CancellationToken cancellationToken = default); void ClearCaches(); Task ClearCachesInBackground(); } diff --git a/src/Nethermind/Nethermind.State/IWorldState.cs b/src/Nethermind/Nethermind.State/IWorldState.cs index a7b0aa82cc7..ef42ea7f5b0 100644 --- a/src/Nethermind/Nethermind.State/IWorldState.cs +++ b/src/Nethermind/Nethermind.State/IWorldState.cs @@ -72,6 +72,7 @@ public interface IWorldState : IJournal, IReadOnlyStateProvider Snapshot IJournal.TakeSnapshot() => TakeSnapshot(); void WarmUp(AccessList? accessList); void WarmUp(Address address); + void WarmUp(StorageCell address); /// /// Clear all storage at specified address /// diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index d83f2d7c71e..26f89fba22b 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -127,6 +127,7 @@ public void WarmUp(AccessList? accessList) } public void WarmUp(Address address) => _stateProvider.WarmUp(address); + public void WarmUp(StorageCell address) => _persistentStorageProvider.WarmUp(address, isEmpty: false); public void ClearStorage(Address address) { _persistentStorageProvider.ClearStorage(address); From 11d2570b71701aa7dfb6c9ab7a70540f4ba5c2c0 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 12 Sep 2024 17:09:09 +0100 Subject: [PATCH 2/7] Logs only in debug --- .../Nethermind.Consensus/Processing/BlockCachePreWarmer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs index 8b24c4f0531..21963af8646 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs @@ -173,7 +173,7 @@ void IThreadPoolWorkItem.Execute() } catch (Exception ex) { - if (PreWarmer._logger.IsInfo) PreWarmer._logger.Error($"Error pre-warming addresses", ex); + if (PreWarmer._logger.IsDebug) PreWarmer._logger.Error($"Error pre-warming addresses", ex); } finally { @@ -217,7 +217,7 @@ private void WarmupAddresses(ParallelOptions parallelOptions, Block block, IRead } catch (Exception ex) { - if (PreWarmer._logger.IsInfo) PreWarmer._logger.Error($"Error pre-warming addresses {i}", ex); + if (PreWarmer._logger.IsDebug) PreWarmer._logger.Error($"Error pre-warming addresses {i}", ex); } }); } From 6c98bb500af4ffa3991119e250bf63248130dfc9 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 12 Sep 2024 19:29:15 +0100 Subject: [PATCH 3/7] Kick off ThreadPool work item faster --- .../Processing/BlockCachePreWarmer.cs | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs index 21963af8646..21a06408ccf 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs @@ -33,10 +33,16 @@ public Task PreWarmCaches(Block suggestedBlock, Hash256? parentStateRoot, Addres if (_logger.IsWarn) _logger.Warn("Caches are not empty. Clearing them."); } - if (!IsGenesisBlock(parentStateRoot) && Environment.ProcessorCount > 2 && !cancellationToken.IsCancellationRequested) + var physicalCoreCount = RuntimeInformation.PhysicalCoreCount; + if (!IsGenesisBlock(parentStateRoot) && physicalCoreCount > 2 && !cancellationToken.IsCancellationRequested) { + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = physicalCoreCount - 1, CancellationToken = cancellationToken }; + + // Run address warmer ahead of transactions warmer, but queue to ThreadPool so it doesn't block the txs + ThreadPool.UnsafeQueueUserWorkItem( + new AddressWarmer(parallelOptions, suggestedBlock, parentStateRoot, beaconRootsAddress, this), preferLocal: false); // Do not pass cancellation token to the task, we don't want exceptions to be thrown in main processing thread - return Task.Run(() => PreWarmCachesParallel(suggestedBlock, parentStateRoot, beaconRootsAddress, cancellationToken)); + return Task.Run(() => PreWarmCachesParallel(suggestedBlock, parentStateRoot, beaconRootsAddress, parallelOptions, cancellationToken)); } } @@ -50,26 +56,14 @@ public Task PreWarmCaches(Block suggestedBlock, Hash256? parentStateRoot, Addres public Task ClearCachesInBackground() => targetWorldState?.ClearCachesInBackground() ?? Task.CompletedTask; - private void PreWarmCachesParallel(Block suggestedBlock, Hash256 parentStateRoot, Address? beaconRootsAddress, CancellationToken cancellationToken) + private void PreWarmCachesParallel(Block suggestedBlock, Hash256 parentStateRoot, Address? beaconRootsAddress, ParallelOptions parallelOptions, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return; try { - var physicalCoreCount = RuntimeInformation.PhysicalCoreCount; - if (physicalCoreCount < 2) - { - if (_logger.IsDebug) _logger.Debug("Physical core count is less than 2. Skipping pre-warming."); - return; - } if (_logger.IsDebug) _logger.Debug($"Started pre-warming caches for block {suggestedBlock.Number}."); - ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = physicalCoreCount - 1, CancellationToken = cancellationToken }; - - // Run address warmer ahead of transactions warmer, but queue to ThreadPool so it doesn't block the txs - ThreadPool.UnsafeQueueUserWorkItem( - new AddressWarmer(parallelOptions, suggestedBlock, parentStateRoot, beaconRootsAddress, this), preferLocal: false); - IReleaseSpec spec = specProvider.GetSpec(suggestedBlock.Header); WarmupTransactions(parallelOptions, spec, suggestedBlock, parentStateRoot); WarmupWithdrawals(parallelOptions, spec, suggestedBlock, parentStateRoot); From f7739dc8a6bdc2f67f164f59abfce9a097662204 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 14 Sep 2024 05:14:21 +0100 Subject: [PATCH 4/7] Feedback --- .../BeaconBlockRoot/BeaconBlockRootHandler.cs | 30 ++++++++++------- .../IBeaconBlockRootHandler.cs | 3 +- .../Processing/BlockCachePreWarmer.cs | 20 ++++++------ .../Processing/BlockProcessor.cs | 32 +++++++++++++------ .../Processing/IBlockCachePreWarmer.cs | 3 +- 5 files changed, 55 insertions(+), 33 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs index d1ab34c6971..56a0b2790d4 100644 --- a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs +++ b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs @@ -15,34 +15,42 @@ public class BeaconBlockRootHandler(ITransactionProcessor processor) : IBeaconBl { private const long GasLimit = 30_000_000L; - public Address? BeaconRootsAddress(Block block, IReleaseSpec spec) + public (Address? toAddress, AccessList? accessList) BeaconRootsAccessList(Block block, IReleaseSpec spec) { BlockHeader? header = block.Header; - var canInsertBeaconRoot = spec.IsBeaconBlockRootAvailable + bool canInsertBeaconRoot = spec.IsBeaconBlockRootAvailable && !header.IsGenesis && header.ParentBeaconBlockRoot is not null; - return !canInsertBeaconRoot ? null : spec.Eip4788ContractAddress ?? Eip4788Constants.BeaconRootsAddress; + + Address? eip4788ContractAddress = canInsertBeaconRoot ? + spec.Eip4788ContractAddress ?? Eip4788Constants.BeaconRootsAddress : + null; + + return (eip4788ContractAddress, + eip4788ContractAddress is null ? + null : + new AccessList.Builder() + .AddAddress(eip4788ContractAddress) + .AddStorage(block.Timestamp % 8191) + .Build()); } public void StoreBeaconRoot(Block block, IReleaseSpec spec) { - BlockHeader? header = block.Header; - var canInsertBeaconRoot = spec.IsBeaconBlockRootAvailable - && !header.IsGenesis - && header.ParentBeaconBlockRoot is not null; + (Address? toAddress, AccessList? accessList) = BeaconRootsAccessList(block, spec); - if (canInsertBeaconRoot) + if (toAddress is not null) { - Address beaconRootsAddress = spec.Eip4788ContractAddress ?? Eip4788Constants.BeaconRootsAddress; + BlockHeader? header = block.Header; Transaction transaction = new() { Value = UInt256.Zero, Data = header.ParentBeaconBlockRoot.Bytes.ToArray(), - To = beaconRootsAddress, + To = toAddress, SenderAddress = Address.SystemUser, GasLimit = GasLimit, GasPrice = UInt256.Zero, - AccessList = new AccessList.Builder().AddAddress(beaconRootsAddress).Build() + AccessList = accessList }; transaction.Hash = transaction.CalculateHash(); diff --git a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs index afba41f1401..4f195236763 100644 --- a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs +++ b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs @@ -2,11 +2,12 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core; +using Nethermind.Core.Eip2930; using Nethermind.Core.Specs; namespace Nethermind.Blockchain.BeaconBlockRoot; public interface IBeaconBlockRootHandler { - Address? BeaconRootsAddress(Block block, IReleaseSpec spec); + (Address? toAddress, AccessList? accessList) BeaconRootsAccessList(Block block, IReleaseSpec spec); void StoreBeaconRoot(Block block, IReleaseSpec spec); } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs index 21a06408ccf..32f2e8eb07b 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs @@ -15,6 +15,7 @@ using Nethermind.Evm.TransactionProcessing; using Nethermind.Logging; using Nethermind.State; +using Nethermind.Core.Eip2930; namespace Nethermind.Consensus.Processing; @@ -24,7 +25,7 @@ public class BlockCachePreWarmer(ReadOnlyTxProcessingEnvFactory envFactory, ISpe private readonly ObjectPool _systemTransactionPool = new DefaultObjectPool(new DefaultPooledObjectPolicy(), Environment.ProcessorCount); private readonly ILogger _logger = logManager.GetClassLogger(); - public Task PreWarmCaches(Block suggestedBlock, Hash256? parentStateRoot, Address? beaconRootsAddress, CancellationToken cancellationToken = default) + public Task PreWarmCaches(Block suggestedBlock, Hash256? parentStateRoot, AccessList? systemTxAccessList, CancellationToken cancellationToken = default) { if (targetWorldState is not null) { @@ -40,9 +41,9 @@ public Task PreWarmCaches(Block suggestedBlock, Hash256? parentStateRoot, Addres // Run address warmer ahead of transactions warmer, but queue to ThreadPool so it doesn't block the txs ThreadPool.UnsafeQueueUserWorkItem( - new AddressWarmer(parallelOptions, suggestedBlock, parentStateRoot, beaconRootsAddress, this), preferLocal: false); + new AddressWarmer(parallelOptions, suggestedBlock, parentStateRoot, systemTxAccessList, this), preferLocal: false); // Do not pass cancellation token to the task, we don't want exceptions to be thrown in main processing thread - return Task.Run(() => PreWarmCachesParallel(suggestedBlock, parentStateRoot, beaconRootsAddress, parallelOptions, cancellationToken)); + return Task.Run(() => PreWarmCachesParallel(suggestedBlock, parentStateRoot, parallelOptions, cancellationToken)); } } @@ -56,7 +57,7 @@ public Task PreWarmCaches(Block suggestedBlock, Hash256? parentStateRoot, Addres public Task ClearCachesInBackground() => targetWorldState?.ClearCachesInBackground() ?? Task.CompletedTask; - private void PreWarmCachesParallel(Block suggestedBlock, Hash256 parentStateRoot, Address? beaconRootsAddress, ParallelOptions parallelOptions, CancellationToken cancellationToken) + private void PreWarmCachesParallel(Block suggestedBlock, Hash256 parentStateRoot, ParallelOptions parallelOptions, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return; @@ -148,14 +149,14 @@ private void WarmupTransactions(ParallelOptions parallelOptions, IReleaseSpec sp }); } - private class AddressWarmer(ParallelOptions parallelOptions, Block block, Hash256 stateRoot, Address? beaconRootsAddress, BlockCachePreWarmer preWarmer) + private class AddressWarmer(ParallelOptions parallelOptions, Block block, Hash256 stateRoot, AccessList? systemTxAccessList, BlockCachePreWarmer preWarmer) : IThreadPoolWorkItem { private readonly ParallelOptions ParallelOptions = parallelOptions; private readonly Block Block = block; private readonly Hash256 StateRoot = stateRoot; private readonly BlockCachePreWarmer PreWarmer = preWarmer; - private readonly Address? BeaconRootsAddress = beaconRootsAddress; + private readonly AccessList? SystemTxAccessList = systemTxAccessList; void IThreadPoolWorkItem.Execute() { @@ -179,12 +180,9 @@ private void WarmupAddresses(ParallelOptions parallelOptions, Block block, IRead { if (parallelOptions.CancellationToken.IsCancellationRequested) return; - // Warm up BeaconRootsAddress first as is first used - if (BeaconRootsAddress is not null) + if (SystemTxAccessList is not null) { - scope.WorldState.WarmUp(BeaconRootsAddress); - // Contract accesses this storage cell - scope.WorldState.WarmUp(new StorageCell(BeaconRootsAddress, block.Timestamp % 8191)); + scope.WorldState.WarmUp(SystemTxAccessList); } int progress = 0; diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index a512a43fd37..bbda547c658 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -18,6 +18,7 @@ using Nethermind.Consensus.Withdrawals; using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Core.Eip2930; using Nethermind.Core.Specs; using Nethermind.Crypto; using Nethermind.Evm; @@ -107,17 +108,30 @@ the previous head state.*/ BlockProcessing?.Invoke(this, new BlockEventArgs(suggestedBlock)); } - using CancellationTokenSource cancellationTokenSource = new(); - Task? preWarmTask = suggestedBlock.Transactions.Length < 3 - ? null - : preWarmer?.PreWarmCaches(suggestedBlock, preBlockStateRoot!, _beaconBlockRootHandler.BeaconRootsAddress(suggestedBlock, _specProvider.GetSpec(suggestedBlock.Header)), cancellationTokenSource.Token); - (Block processedBlock, TxReceipt[] receipts) = ProcessOne(suggestedBlock, options, blockTracer); - // Block is processed, we can cancel the prewarm task - if (preWarmTask is not null) + Block processedBlock; + TxReceipt[] receipts; + + Task? preWarmTask = null; + bool skipPrewarming = preWarmer is null || suggestedBlock.Transactions.Length < 3; + if (!skipPrewarming) + { + using CancellationTokenSource cancellationTokenSource = new(); + (_, AccessList? accessList) = _beaconBlockRootHandler.BeaconRootsAccessList(suggestedBlock, _specProvider.GetSpec(suggestedBlock.Header)); + preWarmTask = preWarmer?.PreWarmCaches(suggestedBlock, preBlockStateRoot!, accessList, cancellationTokenSource.Token); + + (processedBlock, receipts) = ProcessOne(suggestedBlock, options, blockTracer); + // Block is processed, we can cancel the prewarm task + if (preWarmTask is not null) + { + preWarmTask = preWarmTask.ContinueWith(_clearCaches).Unwrap(); + } + cancellationTokenSource.Cancel(); + } + else { - preWarmTask = preWarmTask.ContinueWith(_clearCaches).Unwrap(); + (processedBlock, receipts) = ProcessOne(suggestedBlock, options, blockTracer); } - cancellationTokenSource.Cancel(); + processedBlocks[i] = processedBlock; // be cautious here as AuRa depends on processing diff --git a/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs b/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs index cffb2a1ceb4..c4ab4d2f3d3 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs @@ -5,12 +5,13 @@ using System.Threading.Tasks; using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Core.Eip2930; namespace Nethermind.Consensus.Processing; public interface IBlockCachePreWarmer { - Task PreWarmCaches(Block suggestedBlock, Hash256 parentStateRoot, Address? beaconRootsAddress, CancellationToken cancellationToken = default); + Task PreWarmCaches(Block suggestedBlock, Hash256 parentStateRoot, AccessList? systemTxAccessList, CancellationToken cancellationToken = default); void ClearCaches(); Task ClearCachesInBackground(); } From d9874b0c3666724e4da86f8c030165e8fd6ce789 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 14 Sep 2024 05:33:15 +0100 Subject: [PATCH 5/7] Prewarmer is not null --- .../Nethermind.Consensus/Processing/BlockProcessor.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index bbda547c658..4423c36d262 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -117,14 +117,11 @@ the previous head state.*/ { using CancellationTokenSource cancellationTokenSource = new(); (_, AccessList? accessList) = _beaconBlockRootHandler.BeaconRootsAccessList(suggestedBlock, _specProvider.GetSpec(suggestedBlock.Header)); - preWarmTask = preWarmer?.PreWarmCaches(suggestedBlock, preBlockStateRoot!, accessList, cancellationTokenSource.Token); + preWarmTask = preWarmer.PreWarmCaches(suggestedBlock, preBlockStateRoot, accessList, cancellationTokenSource.Token); (processedBlock, receipts) = ProcessOne(suggestedBlock, options, blockTracer); // Block is processed, we can cancel the prewarm task - if (preWarmTask is not null) - { - preWarmTask = preWarmTask.ContinueWith(_clearCaches).Unwrap(); - } + preWarmTask = preWarmTask.ContinueWith(_clearCaches).Unwrap(); cancellationTokenSource.Cancel(); } else From 5de4f1e78646628ad930144c42b66c3ccabe6057 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 14 Sep 2024 05:37:13 +0100 Subject: [PATCH 6/7] Reduce changes --- src/Nethermind/Nethermind.State/IWorldState.cs | 1 - src/Nethermind/Nethermind.State/WorldState.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Nethermind/Nethermind.State/IWorldState.cs b/src/Nethermind/Nethermind.State/IWorldState.cs index ef42ea7f5b0..a7b0aa82cc7 100644 --- a/src/Nethermind/Nethermind.State/IWorldState.cs +++ b/src/Nethermind/Nethermind.State/IWorldState.cs @@ -72,7 +72,6 @@ public interface IWorldState : IJournal, IReadOnlyStateProvider Snapshot IJournal.TakeSnapshot() => TakeSnapshot(); void WarmUp(AccessList? accessList); void WarmUp(Address address); - void WarmUp(StorageCell address); /// /// Clear all storage at specified address /// diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index 26f89fba22b..d83f2d7c71e 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -127,7 +127,6 @@ public void WarmUp(AccessList? accessList) } public void WarmUp(Address address) => _stateProvider.WarmUp(address); - public void WarmUp(StorageCell address) => _persistentStorageProvider.WarmUp(address, isEmpty: false); public void ClearStorage(Address address) { _persistentStorageProvider.ClearStorage(address); From c81e2189f547807a457f97a44a5306a44cae908f Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 14 Sep 2024 07:46:47 +0100 Subject: [PATCH 7/7] Don't include StorageCell in tx AccessList --- .../BeaconBlockRoot/BeaconBlockRootHandler.cs | 25 ++++++++++++------- .../IBeaconBlockRootHandler.cs | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs index 56a0b2790d4..c464091ba49 100644 --- a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs +++ b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs @@ -15,7 +15,7 @@ public class BeaconBlockRootHandler(ITransactionProcessor processor) : IBeaconBl { private const long GasLimit = 30_000_000L; - public (Address? toAddress, AccessList? accessList) BeaconRootsAccessList(Block block, IReleaseSpec spec) + public (Address? toAddress, AccessList? accessList) BeaconRootsAccessList(Block block, IReleaseSpec spec, bool includeStorageCells = true) { BlockHeader? header = block.Header; bool canInsertBeaconRoot = spec.IsBeaconBlockRootAvailable @@ -26,18 +26,25 @@ public class BeaconBlockRootHandler(ITransactionProcessor processor) : IBeaconBl spec.Eip4788ContractAddress ?? Eip4788Constants.BeaconRootsAddress : null; - return (eip4788ContractAddress, - eip4788ContractAddress is null ? - null : - new AccessList.Builder() - .AddAddress(eip4788ContractAddress) - .AddStorage(block.Timestamp % 8191) - .Build()); + if (eip4788ContractAddress is null) + { + return (null, null); + } + + var builder = new AccessList.Builder() + .AddAddress(eip4788ContractAddress); + + if (includeStorageCells) + { + builder.AddStorage(block.Timestamp % 8191); + } + + return (eip4788ContractAddress, builder.Build()); } public void StoreBeaconRoot(Block block, IReleaseSpec spec) { - (Address? toAddress, AccessList? accessList) = BeaconRootsAccessList(block, spec); + (Address? toAddress, AccessList? accessList) = BeaconRootsAccessList(block, spec, includeStorageCells: false); if (toAddress is not null) { diff --git a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs index 4f195236763..d6e3ec0f5ed 100644 --- a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs +++ b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs @@ -8,6 +8,6 @@ namespace Nethermind.Blockchain.BeaconBlockRoot; public interface IBeaconBlockRootHandler { - (Address? toAddress, AccessList? accessList) BeaconRootsAccessList(Block block, IReleaseSpec spec); + (Address? toAddress, AccessList? accessList) BeaconRootsAccessList(Block block, IReleaseSpec spec, bool includeStorageCells = true); void StoreBeaconRoot(Block block, IReleaseSpec spec); }