From 3e17588087c7aa30e57532021d4f8a37a8542ad8 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 26 Oct 2024 13:22:33 +0100 Subject: [PATCH 1/9] Include Rewards/Mev in block logs --- .../Processing/BlockchainProcessor.cs | 16 +++++++++++++++- .../Processing/ProcessingStats.cs | 9 +++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockchainProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockchainProcessor.cs index e5812f739f2..5013cf21e4a 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockchainProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockchainProcessor.cs @@ -419,7 +419,21 @@ private void FireProcessingQueueEmpty() Metrics.LastBlockProcessingTimeInMs = blockProcessingTimeInMicrosecs / 1000; Metrics.RecoveryQueueSize = _recoveryQueue.Count; Metrics.ProcessingQueueSize = _blockQueue.Count; - _stats.UpdateStats(lastProcessed, blockProcessingTimeInMicrosecs); + + Transaction[] txs = suggestedBlock.Transactions; + Address beneficiary = suggestedBlock.Header.GasBeneficiary; + Transaction lastTx = txs.Length > 0 ? txs[^1] : null; + bool isMev = false; + if (lastTx is not null && lastTx.SenderAddress == beneficiary) + { + // Mev reward with in last tx + beneficiary = lastTx.To; + isMev = true; + } + UInt256 beforeBalance = _stateReader.GetBalance(processingBranch.Root, beneficiary); + UInt256 afterBalance = _stateReader.GetBalance(suggestedBlock.StateRoot, beneficiary); + UInt256 rewards = beforeBalance < afterBalance ? afterBalance - beforeBalance : default; + _stats.UpdateStats(lastProcessed, blockProcessingTimeInMicrosecs, rewards, isMev); } bool updateHead = !options.ContainsFlag(ProcessingOptions.DoNotUpdateHead); diff --git a/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs b/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs index b49407e54e7..32863510aad 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs @@ -45,6 +45,8 @@ internal class ProcessingStats : IThreadPoolWorkItem private long _codeDbCacheProcessing; private long _contractAnalysedProcessing; private long _createsProcessing; + private UInt256 _rewards; + private bool _isMev; public ProcessingStats(ILogger logger) { @@ -57,7 +59,7 @@ public ProcessingStats(ILogger logger) #endif } - public void UpdateStats(Block? block, long blockProcessingTimeInMicros) + public void UpdateStats(Block? block, long blockProcessingTimeInMicros, in UInt256 rewards, bool isMev) { if (block is null) { @@ -99,6 +101,8 @@ public void UpdateStats(Block? block, long blockProcessingTimeInMicros) _codeDbCacheProcessing = Db.Metrics.ThreadLocalCodeDbCache; _contractAnalysedProcessing = Evm.Metrics.ThreadLocalContractsAnalysed; _createsProcessing = Evm.Metrics.ThreadLocalCreates; + _rewards = rewards; + _isMev = isMev; GenerateReport(); } } @@ -107,6 +111,7 @@ public void UpdateStats(Block? block, long blockProcessingTimeInMicros) void IThreadPoolWorkItem.Execute() { + const long weiToEth = 1_000_000_000_000_000_000; const string resetColor = "\u001b[37m"; const string whiteText = "\u001b[97m"; const string yellowText = "\u001b[93m"; @@ -235,7 +240,7 @@ void IThreadPoolWorkItem.Execute() var recoveryQueue = Metrics.RecoveryQueueSize; var processingQueue = Metrics.ProcessingQueueSize; - _logger.Info($"- Block{(chunkBlocks > 1 ? $"s {chunkBlocks,-9:N0}" : " ")}{(chunkBlocks == 1 ? mgasColor : "")} {chunkMGas,9:F2}{resetColor} MGas | {chunkTx,8:N0} txs | calls {callsColor}{chunkCalls,6:N0}{resetColor} {darkGreyText}({chunkEmptyCalls,3:N0}){resetColor} | sload {chunkSload,7:N0} | sstore {sstoreColor}{chunkSstore,6:N0}{resetColor} | create {createsColor}{chunkCreates,3:N0}{resetColor}{(currentSelfDestructs - _lastSelfDestructs > 0 ? $"{darkGreyText}({-(currentSelfDestructs - _lastSelfDestructs),3:N0}){resetColor}" : "")}"); + _logger.Info($"- Block{(chunkBlocks > 1 ? $"s {chunkBlocks,-9:N0} " : $" {(_isMev ? "MEV" : " ")} {_rewards.ToDecimal(null) / weiToEth,5:N3}Eth")}{(chunkBlocks == 1 ? mgasColor : "")} {chunkMGas,7:F2}{resetColor} MGas | {chunkTx,8:N0} txs | calls {callsColor}{chunkCalls,6:N0}{resetColor} {darkGreyText}({chunkEmptyCalls,3:N0}){resetColor} | sload {chunkSload,7:N0} | sstore {sstoreColor}{chunkSstore,6:N0}{resetColor} | create {createsColor}{chunkCreates,3:N0}{resetColor}{(currentSelfDestructs - _lastSelfDestructs > 0 ? $"{darkGreyText}({-(currentSelfDestructs - _lastSelfDestructs),3:N0}){resetColor}" : "")}"); if (recoveryQueue > 0 || processingQueue > 0) { _logger.Info($"- Block throughput {mgasPerSecondColor}{mgasPerSecond,9:F2}{resetColor} MGas/s{(mgasPerSecond > 1000 ? "🔥" : " ")}| {txps,10:N1} tps | {bps,7:F2} Blk/s | recover {recoveryQueue,5:N0} | process {processingQueue,5:N0}"); From 34e510641f97a7a8b0ad33e09b558805a34a6b83 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 26 Oct 2024 19:26:15 +0100 Subject: [PATCH 2/9] Show mev and extra data in block logs --- .../Processing/BlockchainProcessor.cs | 9 +- .../Processing/ProcessingStats.cs | 37 ++---- .../Handlers/ForkchoiceUpdatedHandler.cs | 120 +++++++++++++++++- .../Nethermind.Merge.Plugin/IMergeConfig.cs | 3 + .../Nethermind.Merge.Plugin/MergeConfig.cs | 2 + .../Nethermind.Merge.Plugin/MergePlugin.cs | 3 +- .../Nethermind.Optimism/OptimismPlugin.cs | 3 +- 7 files changed, 144 insertions(+), 33 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockchainProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockchainProcessor.cs index 5013cf21e4a..a7dbfe6cb8e 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockchainProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockchainProcessor.cs @@ -424,7 +424,7 @@ private void FireProcessingQueueEmpty() Address beneficiary = suggestedBlock.Header.GasBeneficiary; Transaction lastTx = txs.Length > 0 ? txs[^1] : null; bool isMev = false; - if (lastTx is not null && lastTx.SenderAddress == beneficiary) + if (lastTx is not null && (lastTx.SenderAddress == beneficiary || _alternateMevPayees.Contains(lastTx.SenderAddress))) { // Mev reward with in last tx beneficiary = lastTx.To; @@ -784,6 +784,13 @@ public void Dispose() _blockTree.NewHeadBlock -= OnNewHeadBlock; } + // Help identify mev blocks when doesn't follow regular pattern + private static HashSet _alternateMevPayees = new() + { + new Address("0xa83114A443dA1CecEFC50368531cACE9F37fCCcb"), // Extra data as: beaverbuild.org + new Address("0x9FC3da866e7DF3a1c57adE1a97c9f00a70f010c8"), // Extra data as: Titan (titanbuilder.xyz) + }; + [DebuggerDisplay("Root: {Root}, Length: {BlocksToProcess.Count}")] private readonly struct ProcessingBranch { diff --git a/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs b/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs index 32863510aad..86f21a40d0c 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs @@ -16,7 +16,6 @@ namespace Nethermind.Consensus.Processing internal class ProcessingStats : IThreadPoolWorkItem { private readonly ILogger _logger; - private readonly Stopwatch _processingStopwatch = new(); private readonly Stopwatch _runStopwatch = new(); private long _lastBlockNumber; private long _lastElapsedRunningMicroseconds; @@ -27,13 +26,11 @@ internal class ProcessingStats : IThreadPoolWorkItem private long _lastTotalSLoad; private long _lastTotalSStore; private long _lastSelfDestructs; - private long _totalBlocks; private long _chunkProcessingMicroseconds; private long _lastTotalCreates; private long _lastReportMs; - private long _lastContractsAnalysed; + private long _lastContractsAnalyzed; private long _lastCachedContractsUsed; - private long _blockProcessingMicroseconds; private long _runningMicroseconds; private long _runMicroseconds; private long _reportMs; @@ -61,12 +58,7 @@ public ProcessingStats(ILogger logger) public void UpdateStats(Block? block, long blockProcessingTimeInMicros, in UInt256 rewards, bool isMev) { - if (block is null) - { - return; - } - - _processingStopwatch.Stop(); + if (block is null) return; if (_lastBlockNumber == 0) { @@ -85,7 +77,6 @@ public void UpdateStats(Block? block, long blockProcessingTimeInMicros, in UInt2 Metrics.BlockchainHeight = block.Header.Number; - _blockProcessingMicroseconds = _processingStopwatch.ElapsedMicroseconds(); _runningMicroseconds = _runStopwatch.ElapsedMicroseconds(); _runMicroseconds = (_runningMicroseconds - _lastElapsedRunningMicroseconds); @@ -123,10 +114,12 @@ void IThreadPoolWorkItem.Execute() const string blueText = "\u001b[94m"; const string darkGreyText = resetColor; // "\u001b[90m"; + Block? block = Interlocked.Exchange(ref _lastBlock, null); + if (block is null) return; + long currentSelfDestructs = Evm.Metrics.SelfDestructs; long chunkBlocks = Metrics.Blocks - _lastBlockNumber; - _totalBlocks += chunkBlocks; double chunkMicroseconds = _chunkProcessingMicroseconds; double chunkMGas = Metrics.Mgas - _lastTotalMGas; @@ -145,21 +138,16 @@ void IThreadPoolWorkItem.Execute() } } - Block? block = Interlocked.Exchange(ref _lastBlock, null); - if (block is not null && _logger.IsInfo) + if (_logger.IsInfo) { - double totalMicroseconds = _blockProcessingMicroseconds; long chunkTx = Metrics.Transactions - _lastTotalTx; long chunkCalls = _callsProcessing - _lastTotalCalls; long chunkEmptyCalls = _emptyCallsProcessing - _lastTotalEmptyCalls; long chunkCreates = _createsProcessing - _lastTotalCreates; long chunkSload = _sloadOpcodeProcessing - _lastTotalSLoad; long chunkSstore = _sstoreOpcodeProcessing - _lastTotalSStore; - long contractsAnalysed = _contractAnalysedProcessing - _lastContractsAnalysed; + long contractsAnalysed = _contractAnalysedProcessing - _lastContractsAnalyzed; long cachedContractsUsed = _codeDbCacheProcessing - _lastCachedContractsUsed; - double totalMgasPerSecond = totalMicroseconds == 0 ? -1 : Metrics.Mgas / totalMicroseconds * 1_000_000.0; - double totalTxPerSecond = totalMicroseconds == 0 ? -1 : Metrics.Transactions / totalMicroseconds * 1_000_000.0; - double totalBlocksPerSecond = totalMicroseconds == 0 ? -1 : _totalBlocks / totalMicroseconds * 1_000_000.0; double txps = chunkMicroseconds == 0 ? -1 : chunkTx / chunkMicroseconds * 1_000_000.0; double bps = chunkMicroseconds == 0 ? -1 : chunkBlocks / chunkMicroseconds * 1_000_000.0; double chunkMs = (chunkMicroseconds == 0 ? -1 : chunkMicroseconds / 1000.0); @@ -240,7 +228,7 @@ void IThreadPoolWorkItem.Execute() var recoveryQueue = Metrics.RecoveryQueueSize; var processingQueue = Metrics.ProcessingQueueSize; - _logger.Info($"- Block{(chunkBlocks > 1 ? $"s {chunkBlocks,-9:N0} " : $" {(_isMev ? "MEV" : " ")} {_rewards.ToDecimal(null) / weiToEth,5:N3}Eth")}{(chunkBlocks == 1 ? mgasColor : "")} {chunkMGas,7:F2}{resetColor} MGas | {chunkTx,8:N0} txs | calls {callsColor}{chunkCalls,6:N0}{resetColor} {darkGreyText}({chunkEmptyCalls,3:N0}){resetColor} | sload {chunkSload,7:N0} | sstore {sstoreColor}{chunkSstore,6:N0}{resetColor} | create {createsColor}{chunkCreates,3:N0}{resetColor}{(currentSelfDestructs - _lastSelfDestructs > 0 ? $"{darkGreyText}({-(currentSelfDestructs - _lastSelfDestructs),3:N0}){resetColor}" : "")}"); + _logger.Info($"- Block{(chunkBlocks > 1 ? $"s {chunkBlocks,-9:N0} " : $"{(_isMev ? " mev" : " ")} {_rewards.ToDecimal(null) / weiToEth,5:N3}Eth")}{(chunkBlocks == 1 ? mgasColor : "")} {chunkMGas,7:F2}{resetColor} MGas | {chunkTx,8:N0} txs | calls {callsColor}{chunkCalls,6:N0}{resetColor} {darkGreyText}({chunkEmptyCalls,3:N0}){resetColor} | sload {chunkSload,7:N0} | sstore {sstoreColor}{chunkSstore,6:N0}{resetColor} | create {createsColor}{chunkCreates,3:N0}{resetColor}{(currentSelfDestructs - _lastSelfDestructs > 0 ? $"{darkGreyText}({-(currentSelfDestructs - _lastSelfDestructs),3:N0}){resetColor}" : "")}"); if (recoveryQueue > 0 || processingQueue > 0) { _logger.Info($"- Block throughput {mgasPerSecondColor}{mgasPerSecond,9:F2}{resetColor} MGas/s{(mgasPerSecond > 1000 ? "🔥" : " ")}| {txps,10:N1} tps | {bps,7:F2} Blk/s | recover {recoveryQueue,5:N0} | process {processingQueue,5:N0}"); @@ -249,16 +237,10 @@ void IThreadPoolWorkItem.Execute() { _logger.Info($"- Block throughput {mgasPerSecondColor}{mgasPerSecond,9:F2}{resetColor} MGas/s{(mgasPerSecond > 1000 ? "🔥" : " ")}| {txps,10:N1} tps | {bps,7:F2} Blk/s | exec code {resetColor} from cache {cachedContractsUsed,7:N0} |{resetColor} new {contractsAnalysed,6:N0}"); } - - // Only output the total throughput in debug mode - if (_logger.IsDebug) - { - _logger.Debug($"- Total throughput {totalMgasPerSecond,9:F2} MGas/s | {totalTxPerSecond,9:F2} tps | {totalBlocksPerSecond,7:F2} Blk/s |⛽ Gas gwei: {Evm.Metrics.MinGasPrice:N2} .. {Math.Max(Evm.Metrics.MinGasPrice, Evm.Metrics.EstMedianGasPrice):N2} ({Evm.Metrics.AveGasPrice:N2}) .. {Evm.Metrics.MaxGasPrice:N2}"); - } } _lastCachedContractsUsed = _codeDbCacheProcessing; - _lastContractsAnalysed = _contractAnalysedProcessing; + _lastContractsAnalyzed = _contractAnalysedProcessing; _lastBlockNumber = Metrics.Blocks; _lastTotalMGas = Metrics.Mgas; _lastElapsedRunningMicroseconds = _runningMicroseconds; @@ -274,7 +256,6 @@ void IThreadPoolWorkItem.Execute() public void Start() { - _processingStopwatch.Start(); if (!_runStopwatch.IsRunning) { _lastReportMs = Environment.TickCount64; diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs index 066de1f63d9..52cfc1a52dd 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Text; using System.Threading; using System.Threading.Tasks; using Nethermind.Blockchain; @@ -13,6 +14,7 @@ using Nethermind.Consensus.Producers; using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Core.Threading; using Nethermind.Crypto; @@ -51,6 +53,7 @@ public class ForkchoiceUpdatedHandler : IForkchoiceUpdatedHandler private readonly bool _simulateBlockProduction; private readonly ulong _secondsPerSlot; private readonly ISyncPeerPool _syncPeerPool; + private readonly bool _showExtraData; public ForkchoiceUpdatedHandler( IBlockTree blockTree, @@ -67,7 +70,8 @@ public ForkchoiceUpdatedHandler( ISyncPeerPool syncPeerPool, ILogManager logManager, ulong secondsPerSlot, - bool simulateBlockProduction = false) + bool simulateBlockProduction = false, + bool showExtraData = true) { _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); _manualBlockFinalizationManager = manualBlockFinalizationManager ?? throw new ArgumentNullException(nameof(manualBlockFinalizationManager)); @@ -84,6 +88,7 @@ public ForkchoiceUpdatedHandler( _simulateBlockProduction = simulateBlockProduction; _secondsPerSlot = secondsPerSlot; _logger = logManager.GetClassLogger(); + _showExtraData = showExtraData; } public async Task> Handle(ForkchoiceStateV1 forkchoiceState, PayloadAttributes? payloadAttributes, int version) @@ -279,12 +284,123 @@ protected virtual bool IsNewHeadAlignedWithChain(Block newHeadBlock, ForkchoiceS if (shouldUpdateHead) { _poSSwitcher.ForkchoiceUpdated(newHeadBlock.Header, forkchoiceState.FinalizedBlockHash); - if (_logger.IsInfo) _logger.Info($"Synced Chain Head to {newHeadBlock.ToString(Block.Format.Short)}"); + if (_logger.IsInfo) _logger.Info($"Synced Chain Head to {newHeadBlock.ToString(Block.Format.Short)}{ParseExtraData(newHeadBlock.Header.ExtraData, newHeadBlock.Header.GasBeneficiary)}"); } return null; } + private string ParseExtraData(byte[] data, Address? gasBeneficiary) + { + if (!_showExtraData) return string.Empty; + + if (data is null || data.Length == 0) + { + // If no extra data just show GasBeneficiary address + return $", Address: {(gasBeneficiary?.ToString() ?? "0x")}"; + } + + // Ideally we'd prefer to show text; so convert invalid unicode + // and control chars to spaces and trim leading and trailing spaces. + string extraData = CleanUtf8ByteArray(data); + + // If the cleaned text is less than half length of input size, + // output it as hex, else output the text. + return extraData.Length > data.Length / 2 ? + $", Extra Data: {extraData}" : + $", Hex: {data.ToHexString(withZeroX: true)}"; + } + + private static StringBuilder? _extraDataStringBuilder; + + public static string CleanUtf8ByteArray(byte[] bytes) + { + int start = 0; + int lastValidEnd = 0; // Tracks the end of the last valid UTF-8 sequence + int end = bytes.Length; + bool inMiddle = false; + int firstValidStart = -1; // Tracks the first valid position + StringBuilder validStringBuilder = Interlocked.Exchange(ref _extraDataStringBuilder, null) ?? new(); + + while (start < end) + { + int sequenceLength = GetUtf8SequenceLength(bytes, start); + + if (sequenceLength == 0 || IsControlCharacter(bytes[start])) + { + // If we are already in a valid sequence, add a space for invalid or control characters + if (inMiddle) + { + validStringBuilder.Append(' '); + inMiddle = false; + } + start++; + } + else + { + if (firstValidStart == -1) firstValidStart = validStringBuilder.Length; + + // Append the valid UTF-8 sequence + validStringBuilder.Append(Encoding.UTF8.GetString(bytes, start, sequenceLength)); + start += sequenceLength; + lastValidEnd = validStringBuilder.Length; // Update last valid position + inMiddle = true; + } + } + + if (firstValidStart == -1) + return string.Empty; + + // Return only the valid portion between firstValidStart and lastValidEnd + string extraData = validStringBuilder.ToString(firstValidStart, lastValidEnd - firstValidStart); + + // Clear and repool the string builder + validStringBuilder.Clear(); + _extraDataStringBuilder = validStringBuilder; + + return extraData; + } + + // Helper function to determine the length of a valid UTF-8 sequence or return 0 for invalid + private static int GetUtf8SequenceLength(byte[] bytes, int start) + { + if (start >= bytes.Length) return 0; + + byte firstByte = bytes[start]; + + if ((firstByte & 0b1000_0000) == 0) + { + return 1; // 1-byte sequence (ASCII) + } + else if ((firstByte & 0b1110_0000) == 0b1100_0000 && start + 1 < bytes.Length && + (bytes[start + 1] & 0b1100_0000) == 0b1000_0000) + { + return 2; // 2-byte sequence + } + else if ((firstByte & 0b1111_0000) == 0b1110_0000 && start + 2 < bytes.Length && + (bytes[start + 1] & 0b1100_0000) == 0b1000_0000 && + (bytes[start + 2] & 0b1100_0000) == 0b1000_0000) + { + return 3; // 3-byte sequence + } + else if ((firstByte & 0b1111_1000) == 0b1111_0000 && start + 3 < bytes.Length && + (bytes[start + 1] & 0b1100_0000) == 0b1000_0000 && + (bytes[start + 2] & 0b1100_0000) == 0b1000_0000 && + (bytes[start + 3] & 0b1100_0000) == 0b1000_0000) + { + return 4; // 4-byte sequence + } + + return 0; // Invalid UTF-8 sequence + } + + // Helper function to check if a byte is a control character in the ASCII range + private static bool IsControlCharacter(byte b) + { + // Control characters are in the ASCII range 0x00 to 0x1F + return b >= 0x00 && b <= 0x1F; + } + protected virtual bool IsPayloadAttributesTimestampValid(Block newHeadBlock, ForkchoiceStateV1 forkchoiceState, PayloadAttributes payloadAttributes, [NotNullWhen(false)] out ResultWrapper? errorResult) { diff --git a/src/Nethermind/Nethermind.Merge.Plugin/IMergeConfig.cs b/src/Nethermind/Nethermind.Merge.Plugin/IMergeConfig.cs index fb6b7747e6a..3aefc8f47fd 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/IMergeConfig.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/IMergeConfig.cs @@ -69,4 +69,7 @@ The number of requests to the garbage collector (GC) to release the process memo [ConfigItem(Description = "[TECHNICAL] Simulate block production for every possible slot. Just for stress-testing purposes.", DefaultValue = "false", HiddenFromDocs = true)] bool SimulateBlockProduction { get; set; } + + [ConfigItem(Description = "Whether to show the block's Extra Data field in logs", DefaultValue = "true")] + bool ShowExtraData { get; set; } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergeConfig.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergeConfig.cs index f19799e5343..21e7c961811 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergeConfig.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergeConfig.cs @@ -34,5 +34,7 @@ public class MergeConfig : IMergeConfig public int NewPayloadTimeout { get; set; } = 7; public bool SimulateBlockProduction { get; set; } = false; + + public bool ShowExtraData { get; set; } = true; } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs index 81b83425ace..c4c2c48fdec 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs @@ -363,7 +363,8 @@ IBlockImprovementContextFactory CreateBlockImprovementContextFactory() _api.SyncPeerPool!, _api.LogManager, _api.Config().SecondsPerSlot, - _api.Config().SimulateBlockProduction), + _api.Config().SimulateBlockProduction, + _api.Config().ShowExtraData), new GetPayloadBodiesByHashV1Handler(_api.BlockTree, _api.LogManager), new GetPayloadBodiesByRangeV1Handler(_api.BlockTree, _api.LogManager), new GetPayloadBodiesByHashV2Handler(_api.BlockTree, _api.LogManager), diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index aa58d6f01bd..e75b295cd3c 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -257,7 +257,8 @@ public async Task InitRpcModules() _api.SyncPeerPool!, _api.LogManager, _api.Config().SecondsPerSlot, - _api.Config().SimulateBlockProduction), + _api.Config().SimulateBlockProduction, + _api.Config().ShowExtraData), new GetPayloadBodiesByHashV1Handler(_api.BlockTree, _api.LogManager), new GetPayloadBodiesByRangeV1Handler(_api.BlockTree, _api.LogManager), new GetPayloadBodiesByHashV2Handler(_api.BlockTree, _api.LogManager), From f539bffbaba18c05527decda8d7f50e2b9ba38de Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 26 Oct 2024 19:52:29 +0100 Subject: [PATCH 3/9] Use Rune --- .../Handlers/ForkchoiceUpdatedHandler.cs | 95 ++++++++----------- 1 file changed, 37 insertions(+), 58 deletions(-) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs index 52cfc1a52dd..03330aa4412 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Buffers; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -312,47 +313,59 @@ private string ParseExtraData(byte[] data, Address? gasBeneficiary) } private static StringBuilder? _extraDataStringBuilder; - public static string CleanUtf8ByteArray(byte[] bytes) { int start = 0; - int lastValidEnd = 0; // Tracks the end of the last valid UTF-8 sequence int end = bytes.Length; - bool inMiddle = false; - int firstValidStart = -1; // Tracks the first valid position + int firstValidIndex = -1; + int lastValidIndex = -1; + bool inValidSequence = false; StringBuilder validStringBuilder = Interlocked.Exchange(ref _extraDataStringBuilder, null) ?? new(); while (start < end) { - int sequenceLength = GetUtf8SequenceLength(bytes, start); + ReadOnlySpan span = bytes.AsSpan(start); - if (sequenceLength == 0 || IsControlCharacter(bytes[start])) + if (Rune.DecodeFromUtf8(span, out Rune rune, out var bytesConsumed) == OperationStatus.Done) { - // If we are already in a valid sequence, add a space for invalid or control characters - if (inMiddle) + if (IsControlCharacter(rune)) { - validStringBuilder.Append(' '); - inMiddle = false; + if (inValidSequence) + { + validStringBuilder.Append(' '); + inValidSequence = false; + } + } + else + { + if (firstValidIndex == -1) + { + firstValidIndex = validStringBuilder.Length; + } + + validStringBuilder.Append(rune.ToString()); + lastValidIndex = validStringBuilder.Length; + inValidSequence = true; } - start++; + start += bytesConsumed; } else { - if (firstValidStart == -1) firstValidStart = validStringBuilder.Length; - - // Append the valid UTF-8 sequence - validStringBuilder.Append(Encoding.UTF8.GetString(bytes, start, sequenceLength)); - start += sequenceLength; - lastValidEnd = validStringBuilder.Length; // Update last valid position - inMiddle = true; + if (inValidSequence) + { + validStringBuilder.Append(' '); + inValidSequence = false; + } + start++; // Move past the invalid byte } } - if (firstValidStart == -1) + // If no valid content was found, return an empty string + if (firstValidIndex == -1) return string.Empty; - // Return only the valid portion between firstValidStart and lastValidEnd - string extraData = validStringBuilder.ToString(firstValidStart, lastValidEnd - firstValidStart); + // Extract the valid content from the StringBuilder + string extraData = validStringBuilder.ToString(firstValidIndex, lastValidIndex - firstValidIndex); // Clear and repool the string builder validStringBuilder.Clear(); @@ -361,44 +374,10 @@ public static string CleanUtf8ByteArray(byte[] bytes) return extraData; } - // Helper function to determine the length of a valid UTF-8 sequence or return 0 for invalid - private static int GetUtf8SequenceLength(byte[] bytes, int start) - { - if (start >= bytes.Length) return 0; - - byte firstByte = bytes[start]; - - if ((firstByte & 0b1000_0000) == 0) - { - return 1; // 1-byte sequence (ASCII) - } - else if ((firstByte & 0b1110_0000) == 0b1100_0000 && start + 1 < bytes.Length && - (bytes[start + 1] & 0b1100_0000) == 0b1000_0000) - { - return 2; // 2-byte sequence - } - else if ((firstByte & 0b1111_0000) == 0b1110_0000 && start + 2 < bytes.Length && - (bytes[start + 1] & 0b1100_0000) == 0b1000_0000 && - (bytes[start + 2] & 0b1100_0000) == 0b1000_0000) - { - return 3; // 3-byte sequence - } - else if ((firstByte & 0b1111_1000) == 0b1111_0000 && start + 3 < bytes.Length && - (bytes[start + 1] & 0b1100_0000) == 0b1000_0000 && - (bytes[start + 2] & 0b1100_0000) == 0b1000_0000 && - (bytes[start + 3] & 0b1100_0000) == 0b1000_0000) - { - return 4; // 4-byte sequence - } - - return 0; // Invalid UTF-8 sequence - } - - // Helper function to check if a byte is a control character in the ASCII range - private static bool IsControlCharacter(byte b) + private static bool IsControlCharacter(Rune rune) { - // Control characters are in the ASCII range 0x00 to 0x1F - return b >= 0x00 && b <= 0x1F; + // Control characters are from U+0000 to U+001F and U+007F to U+009F + return rune.Value <= 0x001F || (rune.Value >= 0x007F && rune.Value <= 0x009F); } protected virtual bool IsPayloadAttributesTimestampValid(Block newHeadBlock, ForkchoiceStateV1 forkchoiceState, PayloadAttributes payloadAttributes, From 27d3efe4b5b3fee21031a9f9cebd0ee584325b1f Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 26 Oct 2024 20:22:03 +0100 Subject: [PATCH 4/9] Better Rune --- .../Handlers/ForkchoiceUpdatedHandler.cs | 103 ++++++++++-------- 1 file changed, 60 insertions(+), 43 deletions(-) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs index 03330aa4412..10f60d03412 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs @@ -312,71 +312,88 @@ private string ParseExtraData(byte[] data, Address? gasBeneficiary) $", Hex: {data.ToHexString(withZeroX: true)}"; } - private static StringBuilder? _extraDataStringBuilder; public static string CleanUtf8ByteArray(byte[] bytes) { - int start = 0; - int end = bytes.Length; - int firstValidIndex = -1; - int lastValidIndex = -1; - bool inValidSequence = false; - StringBuilder validStringBuilder = Interlocked.Exchange(ref _extraDataStringBuilder, null) ?? new(); - - while (start < end) + // The maximum number of UTF-16 chars is bytes.Length, but each Rune can be up to 2 chars. + // So we allocate bytes.Length to bytes.Length * 2 chars. + const int maxOutputChars = 32 * 2; + + if (bytes == null || bytes.Length == 0 || bytes.Length > 32) + return string.Empty; + + // Allocate a char buffer on the stack. + Span outputBuffer = stackalloc char[maxOutputChars]; + + int outputPos = 0; + int index = 0; + bool hasValidContent = false; + bool shouldAddSpace = false; + + while (index < bytes.Length) { - ReadOnlySpan span = bytes.AsSpan(start); + ReadOnlySpan span = bytes.AsSpan(index); - if (Rune.DecodeFromUtf8(span, out Rune rune, out var bytesConsumed) == OperationStatus.Done) + OperationStatus status = Rune.DecodeFromUtf8(span, out Rune rune, out var bytesConsumed); + if (status == OperationStatus.Done) { - if (IsControlCharacter(rune)) + if (!IsControlCharacter(rune)) { - if (inValidSequence) + if (shouldAddSpace) { - validStringBuilder.Append(' '); - inValidSequence = false; + outputBuffer[outputPos++] = ' '; + shouldAddSpace = false; } - } - else - { - if (firstValidIndex == -1) + + int charsNeeded = rune.Utf16SequenceLength; + if (outputPos + charsNeeded > outputBuffer.Length) { - firstValidIndex = validStringBuilder.Length; + // Expand output buffer + int newSize = outputBuffer.Length * 2; + char[] newBuffer = new char[newSize]; + outputBuffer.Slice(0, outputPos).CopyTo(newBuffer); + outputBuffer = newBuffer; } - validStringBuilder.Append(rune.ToString()); - lastValidIndex = validStringBuilder.Length; - inValidSequence = true; + rune.EncodeToUtf16(outputBuffer.Slice(outputPos)); + outputPos += charsNeeded; + hasValidContent = true; + } + else + { + // Control character encountered; set flag to add space if needed + if (hasValidContent) + shouldAddSpace = true; } - start += bytesConsumed; + index += bytesConsumed; + } + else if (status == OperationStatus.InvalidData) + { + // Invalid data; set flag to add space if needed + if (hasValidContent) + shouldAddSpace = true; + index++; + } + else if (status == OperationStatus.NeedMoreData) + { + // Incomplete sequence at the end; break out of the loop + break; } else { - if (inValidSequence) - { - validStringBuilder.Append(' '); - inValidSequence = false; - } - start++; // Move past the invalid byte + // Unexpected status; treat as invalid data + if (hasValidContent) + shouldAddSpace = true; + index++; } } - // If no valid content was found, return an empty string - if (firstValidIndex == -1) - return string.Empty; - - // Extract the valid content from the StringBuilder - string extraData = validStringBuilder.ToString(firstValidIndex, lastValidIndex - firstValidIndex); - - // Clear and repool the string builder - validStringBuilder.Clear(); - _extraDataStringBuilder = validStringBuilder; - - return extraData; + // Create the final string from the output buffer. + return outputPos > 0 ? new string(outputBuffer[..outputPos]) : string.Empty; } private static bool IsControlCharacter(Rune rune) { - // Control characters are from U+0000 to U+001F and U+007F to U+009F + // Control characters are U+0000 to U+001F and U+007F to U+009F return rune.Value <= 0x001F || (rune.Value >= 0x007F && rune.Value <= 0x009F); } From 0dc38849d1020310dafa5444625744dfab05d522 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sun, 27 Oct 2024 10:00:28 +0000 Subject: [PATCH 5/9] Feedback --- .../Nethermind.Config/BlocksConfig.cs | 4 + .../Nethermind.Config/IBlocksConfig.cs | 3 + .../Processing/BlockchainProcessor.cs | 24 +--- .../Processing/ProcessingStats.cs | 47 ++++++-- .../Nethermind.Core/Extensions/Bytes.cs | 87 ++++++++++++++ .../Handlers/ForkchoiceUpdatedHandler.cs | 113 +----------------- .../Handlers/NewPayloadHandler.cs | 32 ++++- .../Nethermind.Merge.Plugin/IMergeConfig.cs | 3 - .../Nethermind.Merge.Plugin/MergeConfig.cs | 2 - .../Nethermind.Merge.Plugin/MergePlugin.cs | 3 +- .../Nethermind.Optimism/OptimismPlugin.cs | 3 +- .../Nethermind.Runner/configs/chiado.cfg | 3 +- .../configs/chiado_archive.cfg | 3 +- .../Nethermind.Runner/configs/gnosis.cfg | 3 +- .../configs/gnosis_archive.cfg | 3 +- 15 files changed, 172 insertions(+), 161 deletions(-) diff --git a/src/Nethermind/Nethermind.Config/BlocksConfig.cs b/src/Nethermind/Nethermind.Config/BlocksConfig.cs index 5ca66e4c994..83413dc5094 100644 --- a/src/Nethermind/Nethermind.Config/BlocksConfig.cs +++ b/src/Nethermind/Nethermind.Config/BlocksConfig.cs @@ -53,5 +53,9 @@ public byte[] GetExtraDataBytes() { return _extraDataBytes; } + + public string GasToken { get => GasTokenTicker; set => GasTokenTicker = value; } + + public static string GasTokenTicker { get; set; } = "ETH"; } } diff --git a/src/Nethermind/Nethermind.Config/IBlocksConfig.cs b/src/Nethermind/Nethermind.Config/IBlocksConfig.cs index 72b5446a97d..ba036fbe2cd 100644 --- a/src/Nethermind/Nethermind.Config/IBlocksConfig.cs +++ b/src/Nethermind/Nethermind.Config/IBlocksConfig.cs @@ -43,5 +43,8 @@ public interface IBlocksConfig : IConfig [ConfigItem(Description = "The genesis block load timeout, in milliseconds.", DefaultValue = "40000")] int GenesisTimeoutMs { get; set; } + [ConfigItem(Description = "The ticker that gas rewards are denominated in for processing logs", DefaultValue = "ETH", HiddenFromDocs = true)] + string GasToken { get; set; } + byte[] GetExtraDataBytes(); } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockchainProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockchainProcessor.cs index a7dbfe6cb8e..5e4ed8b7f2d 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockchainProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockchainProcessor.cs @@ -90,7 +90,7 @@ public BlockchainProcessor( _blockTree.NewBestSuggestedBlock += OnNewBestBlock; _blockTree.NewHeadBlock += OnNewHeadBlock; - _stats = new ProcessingStats(_logger); + _stats = new ProcessingStats(stateReader, _logger); } private void OnNewHeadBlock(object? sender, BlockEventArgs e) @@ -420,20 +420,7 @@ private void FireProcessingQueueEmpty() Metrics.RecoveryQueueSize = _recoveryQueue.Count; Metrics.ProcessingQueueSize = _blockQueue.Count; - Transaction[] txs = suggestedBlock.Transactions; - Address beneficiary = suggestedBlock.Header.GasBeneficiary; - Transaction lastTx = txs.Length > 0 ? txs[^1] : null; - bool isMev = false; - if (lastTx is not null && (lastTx.SenderAddress == beneficiary || _alternateMevPayees.Contains(lastTx.SenderAddress))) - { - // Mev reward with in last tx - beneficiary = lastTx.To; - isMev = true; - } - UInt256 beforeBalance = _stateReader.GetBalance(processingBranch.Root, beneficiary); - UInt256 afterBalance = _stateReader.GetBalance(suggestedBlock.StateRoot, beneficiary); - UInt256 rewards = beforeBalance < afterBalance ? afterBalance - beforeBalance : default; - _stats.UpdateStats(lastProcessed, blockProcessingTimeInMicrosecs, rewards, isMev); + _stats.UpdateStats(lastProcessed, processingBranch.Root, blockProcessingTimeInMicrosecs); } bool updateHead = !options.ContainsFlag(ProcessingOptions.DoNotUpdateHead); @@ -784,13 +771,6 @@ public void Dispose() _blockTree.NewHeadBlock -= OnNewHeadBlock; } - // Help identify mev blocks when doesn't follow regular pattern - private static HashSet _alternateMevPayees = new() - { - new Address("0xa83114A443dA1CecEFC50368531cACE9F37fCCcb"), // Extra data as: beaverbuild.org - new Address("0x9FC3da866e7DF3a1c57adE1a97c9f00a70f010c8"), // Extra data as: Titan (titanbuilder.xyz) - }; - [DebuggerDisplay("Root: {Root}, Length: {BlocksToProcess.Count}")] private readonly struct ProcessingBranch { diff --git a/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs b/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs index 86f21a40d0c..3febb01a49c 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs @@ -2,19 +2,24 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; using System.Diagnostics; using System.Threading; using Nethermind.Blockchain; +using Nethermind.Config; using Nethermind.Core; +using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Int256; using Nethermind.Logging; +using Nethermind.State; namespace Nethermind.Consensus.Processing { //TODO Consult on disabling of such metrics from configuration internal class ProcessingStats : IThreadPoolWorkItem { + private readonly IStateReader _stateReader; private readonly ILogger _logger; private readonly Stopwatch _runStopwatch = new(); private long _lastBlockNumber; @@ -35,6 +40,7 @@ internal class ProcessingStats : IThreadPoolWorkItem private long _runMicroseconds; private long _reportMs; private Block? _lastBlock; + private Hash256 _lastBranchRoot; private long _sloadOpcodeProcessing; private long _sstoreOpcodeProcessing; private long _callsProcessing; @@ -42,11 +48,10 @@ internal class ProcessingStats : IThreadPoolWorkItem private long _codeDbCacheProcessing; private long _contractAnalysedProcessing; private long _createsProcessing; - private UInt256 _rewards; - private bool _isMev; - public ProcessingStats(ILogger logger) + public ProcessingStats(IStateReader stateReader, ILogger logger) { + _stateReader = stateReader; _logger = logger; // the line below just to avoid compilation errors @@ -56,7 +61,7 @@ public ProcessingStats(ILogger logger) #endif } - public void UpdateStats(Block? block, long blockProcessingTimeInMicros, in UInt256 rewards, bool isMev) + public void UpdateStats(Block? block, Hash256 branchRoot, long blockProcessingTimeInMicros) { if (block is null) return; @@ -85,6 +90,7 @@ public void UpdateStats(Block? block, long blockProcessingTimeInMicros, in UInt2 { _lastReportMs = _reportMs; _lastBlock = block; + _lastBranchRoot = branchRoot; _sloadOpcodeProcessing = Evm.Metrics.ThreadLocalSLoadOpcode; _sstoreOpcodeProcessing = Evm.Metrics.ThreadLocalSStoreOpcode; _callsProcessing = Evm.Metrics.ThreadLocalCalls; @@ -92,8 +98,6 @@ public void UpdateStats(Block? block, long blockProcessingTimeInMicros, in UInt2 _codeDbCacheProcessing = Db.Metrics.ThreadLocalCodeDbCache; _contractAnalysedProcessing = Evm.Metrics.ThreadLocalContractsAnalysed; _createsProcessing = Evm.Metrics.ThreadLocalCreates; - _rewards = rewards; - _isMev = isMev; GenerateReport(); } } @@ -117,6 +121,20 @@ void IThreadPoolWorkItem.Execute() Block? block = Interlocked.Exchange(ref _lastBlock, null); if (block is null) return; + Transaction[] txs = block.Transactions; + Address beneficiary = block.Header.GasBeneficiary; + Transaction lastTx = txs.Length > 0 ? txs[^1] : null; + bool isMev = false; + if (lastTx is not null && (lastTx.SenderAddress == beneficiary || _alternateMevPayees.Contains(lastTx.SenderAddress))) + { + // Mev reward with in last tx + beneficiary = lastTx.To; + isMev = true; + } + UInt256 beforeBalance = _stateReader.GetBalance(_lastBranchRoot, beneficiary); + UInt256 afterBalance = _stateReader.GetBalance(block.StateRoot, beneficiary); + UInt256 rewards = beforeBalance < afterBalance ? afterBalance - beforeBalance : default; + long currentSelfDestructs = Evm.Metrics.SelfDestructs; long chunkBlocks = Metrics.Blocks - _lastBlockNumber; @@ -157,7 +175,7 @@ void IThreadPoolWorkItem.Execute() if (chunkBlocks > 1) { - _logger.Info($"Processed {block.Number - chunkBlocks + 1,10}...{block.Number,9} | {chunkMs,10:N1} ms | slot {runMs,7:N0} ms |{blockGas}"); + _logger.Info($"Processed {block.Number - chunkBlocks + 1,10}...{block.Number,9} | {chunkMs,10:N1} ms | slot {runMs,7:N0} ms |{blockGas}"); } else { @@ -184,7 +202,7 @@ void IThreadPoolWorkItem.Execute() < 2000 => orangeText, _ => redText }; - _logger.Info($"Processed {block.Number,10} | {chunkColor}{chunkMs,10:N1}{resetColor} ms | slot {runMs,7:N0} ms |{blockGas}"); + _logger.Info($"Processed {block.Number,10} | {chunkColor}{chunkMs,10:N1}{resetColor} ms | slot {runMs,7:N0} ms |{blockGas}"); } string mgasPerSecondColor = (mgasPerSecond / (block.GasLimit / 1_000_000.0)) switch @@ -228,14 +246,14 @@ void IThreadPoolWorkItem.Execute() var recoveryQueue = Metrics.RecoveryQueueSize; var processingQueue = Metrics.ProcessingQueueSize; - _logger.Info($"- Block{(chunkBlocks > 1 ? $"s {chunkBlocks,-9:N0} " : $"{(_isMev ? " mev" : " ")} {_rewards.ToDecimal(null) / weiToEth,5:N3}Eth")}{(chunkBlocks == 1 ? mgasColor : "")} {chunkMGas,7:F2}{resetColor} MGas | {chunkTx,8:N0} txs | calls {callsColor}{chunkCalls,6:N0}{resetColor} {darkGreyText}({chunkEmptyCalls,3:N0}){resetColor} | sload {chunkSload,7:N0} | sstore {sstoreColor}{chunkSstore,6:N0}{resetColor} | create {createsColor}{chunkCreates,3:N0}{resetColor}{(currentSelfDestructs - _lastSelfDestructs > 0 ? $"{darkGreyText}({-(currentSelfDestructs - _lastSelfDestructs),3:N0}){resetColor}" : "")}"); + _logger.Info($" Block{(chunkBlocks > 1 ? $"s x{chunkBlocks,-9:N0} " : $"{(isMev ? " mev" : " ")} {rewards.ToDecimal(null) / weiToEth,5:N4}{BlocksConfig.GasTokenTicker,4}")}{(chunkBlocks == 1 ? mgasColor : "")} {chunkMGas,7:F2}{resetColor} MGas | {chunkTx,8:N0} txs | calls {callsColor}{chunkCalls,6:N0}{resetColor} {darkGreyText}({chunkEmptyCalls,3:N0}){resetColor} | sload {chunkSload,7:N0} | sstore {sstoreColor}{chunkSstore,6:N0}{resetColor} | create {createsColor}{chunkCreates,3:N0}{resetColor}{(currentSelfDestructs - _lastSelfDestructs > 0 ? $"{darkGreyText}({-(currentSelfDestructs - _lastSelfDestructs),3:N0}){resetColor}" : "")}"); if (recoveryQueue > 0 || processingQueue > 0) { - _logger.Info($"- Block throughput {mgasPerSecondColor}{mgasPerSecond,9:F2}{resetColor} MGas/s{(mgasPerSecond > 1000 ? "🔥" : " ")}| {txps,10:N1} tps | {bps,7:F2} Blk/s | recover {recoveryQueue,5:N0} | process {processingQueue,5:N0}"); + _logger.Info($" Block throughput {mgasPerSecondColor}{mgasPerSecond,11:F2}{resetColor} MGas/s{(mgasPerSecond > 1000 ? "🔥" : " ")}| {txps,10:N1} tps | {bps,7:F2} Blk/s | recover {recoveryQueue,5:N0} | process {processingQueue,5:N0}"); } else { - _logger.Info($"- Block throughput {mgasPerSecondColor}{mgasPerSecond,9:F2}{resetColor} MGas/s{(mgasPerSecond > 1000 ? "🔥" : " ")}| {txps,10:N1} tps | {bps,7:F2} Blk/s | exec code {resetColor} from cache {cachedContractsUsed,7:N0} |{resetColor} new {contractsAnalysed,6:N0}"); + _logger.Info($" Block throughput {mgasPerSecondColor}{mgasPerSecond,11:F2}{resetColor} MGas/s{(mgasPerSecond > 1000 ? "🔥" : " ")}| {txps,10:N1} tps | {bps,7:F2} Blk/s | exec code {resetColor} from cache {cachedContractsUsed,7:N0} |{resetColor} new {contractsAnalysed,6:N0}"); } } @@ -262,5 +280,12 @@ public void Start() _runStopwatch.Start(); } } + + // Help identify mev blocks when doesn't follow regular pattern + private static HashSet _alternateMevPayees = new() + { + new Address("0xa83114A443dA1CecEFC50368531cACE9F37fCCcb"), // Extra data as: beaverbuild.org + new Address("0x9FC3da866e7DF3a1c57adE1a97c9f00a70f010c8"), // Extra data as: Titan (titanbuilder.xyz) + }; } } diff --git a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs index 9957bd1e912..c5e2e04e94c 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs @@ -18,6 +18,7 @@ using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Int256; +using System.Buffers; namespace Nethermind.Core.Extensions { @@ -1140,5 +1141,91 @@ public static void ChangeEndianness8(Span bytes) (BinaryPrimitives.ReverseEndianness(endIth), BinaryPrimitives.ReverseEndianness(ith)); } } + + public static string ToCleanUtf8String(this byte[] bytes) + { + // The maximum number of UTF-16 chars is bytes.Length, but each Rune can be up to 2 chars. + // So we allocate bytes.Length to bytes.Length * 2 chars. + const int maxOutputChars = 32 * 2; + + if (bytes == null || bytes.Length == 0 || bytes.Length > 32) + return string.Empty; + + // Allocate a char buffer on the stack. + Span outputBuffer = stackalloc char[maxOutputChars]; + + int outputPos = 0; + int index = 0; + bool hasValidContent = false; + bool shouldAddSpace = false; + + while (index < bytes.Length) + { + ReadOnlySpan span = bytes.AsSpan(index); + + OperationStatus status = Rune.DecodeFromUtf8(span, out Rune rune, out var bytesConsumed); + if (status == OperationStatus.Done) + { + if (!IsControlCharacter(rune)) + { + if (shouldAddSpace) + { + outputBuffer[outputPos++] = ' '; + shouldAddSpace = false; + } + + int charsNeeded = rune.Utf16SequenceLength; + if (outputPos + charsNeeded > outputBuffer.Length) + { + // Expand output buffer + int newSize = outputBuffer.Length * 2; + char[] newBuffer = new char[newSize]; + outputBuffer.Slice(0, outputPos).CopyTo(newBuffer); + outputBuffer = newBuffer; + } + + rune.EncodeToUtf16(outputBuffer.Slice(outputPos)); + outputPos += charsNeeded; + hasValidContent = true; + } + else + { + // Control character encountered; set flag to add space if needed + if (hasValidContent) + shouldAddSpace = true; + } + index += bytesConsumed; + } + else if (status == OperationStatus.InvalidData) + { + // Invalid data; set flag to add space if needed + if (hasValidContent) + shouldAddSpace = true; + index++; + } + else if (status == OperationStatus.NeedMoreData) + { + // Incomplete sequence at the end; break out of the loop + break; + } + else + { + // Unexpected status; treat as invalid data + if (hasValidContent) + shouldAddSpace = true; + index++; + } + } + + // Create the final string from the output buffer. + return outputPos > 0 ? new string(outputBuffer[..outputPos]) : string.Empty; + } + + private static bool IsControlCharacter(Rune rune) + { + // Control characters are U+0000 to U+001F and U+007F to U+009F + return rune.Value <= 0x001F || (rune.Value >= 0x007F && rune.Value <= 0x009F); + } + } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs index 10f60d03412..f6ce35f2da7 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs @@ -54,7 +54,6 @@ public class ForkchoiceUpdatedHandler : IForkchoiceUpdatedHandler private readonly bool _simulateBlockProduction; private readonly ulong _secondsPerSlot; private readonly ISyncPeerPool _syncPeerPool; - private readonly bool _showExtraData; public ForkchoiceUpdatedHandler( IBlockTree blockTree, @@ -71,8 +70,7 @@ public ForkchoiceUpdatedHandler( ISyncPeerPool syncPeerPool, ILogManager logManager, ulong secondsPerSlot, - bool simulateBlockProduction = false, - bool showExtraData = true) + bool simulateBlockProduction = false) { _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); _manualBlockFinalizationManager = manualBlockFinalizationManager ?? throw new ArgumentNullException(nameof(manualBlockFinalizationManager)); @@ -89,7 +87,6 @@ public ForkchoiceUpdatedHandler( _simulateBlockProduction = simulateBlockProduction; _secondsPerSlot = secondsPerSlot; _logger = logManager.GetClassLogger(); - _showExtraData = showExtraData; } public async Task> Handle(ForkchoiceStateV1 forkchoiceState, PayloadAttributes? payloadAttributes, int version) @@ -285,118 +282,12 @@ protected virtual bool IsNewHeadAlignedWithChain(Block newHeadBlock, ForkchoiceS if (shouldUpdateHead) { _poSSwitcher.ForkchoiceUpdated(newHeadBlock.Header, forkchoiceState.FinalizedBlockHash); - if (_logger.IsInfo) _logger.Info($"Synced Chain Head to {newHeadBlock.ToString(Block.Format.Short)}{ParseExtraData(newHeadBlock.Header.ExtraData, newHeadBlock.Header.GasBeneficiary)}"); + if (_logger.IsInfo) _logger.Info($"Synced Chain Head to {newHeadBlock.ToString(Block.Format.Short)}"); } return null; } - private string ParseExtraData(byte[] data, Address? gasBeneficiary) - { - if (!_showExtraData) return string.Empty; - - if (data is null || data.Length == 0) - { - // If no extra data just show GasBeneficiary address - return $", Address: {(gasBeneficiary?.ToString() ?? "0x")}"; - } - - // Ideally we'd prefer to show text; so convert invalid unicode - // and control chars to spaces and trim leading and trailing spaces. - string extraData = CleanUtf8ByteArray(data); - - // If the cleaned text is less than half length of input size, - // output it as hex, else output the text. - return extraData.Length > data.Length / 2 ? - $", Extra Data: {extraData}" : - $", Hex: {data.ToHexString(withZeroX: true)}"; - } - - public static string CleanUtf8ByteArray(byte[] bytes) - { - // The maximum number of UTF-16 chars is bytes.Length, but each Rune can be up to 2 chars. - // So we allocate bytes.Length to bytes.Length * 2 chars. - const int maxOutputChars = 32 * 2; - - if (bytes == null || bytes.Length == 0 || bytes.Length > 32) - return string.Empty; - - // Allocate a char buffer on the stack. - Span outputBuffer = stackalloc char[maxOutputChars]; - - int outputPos = 0; - int index = 0; - bool hasValidContent = false; - bool shouldAddSpace = false; - - while (index < bytes.Length) - { - ReadOnlySpan span = bytes.AsSpan(index); - - OperationStatus status = Rune.DecodeFromUtf8(span, out Rune rune, out var bytesConsumed); - if (status == OperationStatus.Done) - { - if (!IsControlCharacter(rune)) - { - if (shouldAddSpace) - { - outputBuffer[outputPos++] = ' '; - shouldAddSpace = false; - } - - int charsNeeded = rune.Utf16SequenceLength; - if (outputPos + charsNeeded > outputBuffer.Length) - { - // Expand output buffer - int newSize = outputBuffer.Length * 2; - char[] newBuffer = new char[newSize]; - outputBuffer.Slice(0, outputPos).CopyTo(newBuffer); - outputBuffer = newBuffer; - } - - rune.EncodeToUtf16(outputBuffer.Slice(outputPos)); - outputPos += charsNeeded; - hasValidContent = true; - } - else - { - // Control character encountered; set flag to add space if needed - if (hasValidContent) - shouldAddSpace = true; - } - index += bytesConsumed; - } - else if (status == OperationStatus.InvalidData) - { - // Invalid data; set flag to add space if needed - if (hasValidContent) - shouldAddSpace = true; - index++; - } - else if (status == OperationStatus.NeedMoreData) - { - // Incomplete sequence at the end; break out of the loop - break; - } - else - { - // Unexpected status; treat as invalid data - if (hasValidContent) - shouldAddSpace = true; - index++; - } - } - - // Create the final string from the output buffer. - return outputPos > 0 ? new string(outputBuffer[..outputPos]) : string.Empty; - } - - private static bool IsControlCharacter(Rune rune) - { - // Control characters are U+0000 to U+001F and U+007F to U+009F - return rune.Value <= 0x001F || (rune.Value >= 0x007F && rune.Value <= 0x009F); - } - protected virtual bool IsPayloadAttributesTimestampValid(Block newHeadBlock, ForkchoiceStateV1 forkchoiceState, PayloadAttributes payloadAttributes, [NotNullWhen(false)] out ResultWrapper? errorResult) { diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs index a6250f5e1e7..dd38a3e47e2 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs @@ -12,6 +12,7 @@ using Nethermind.Core; using Nethermind.Core.Caching; using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; using Nethermind.Crypto; using Nethermind.Int256; using Nethermind.JsonRpc; @@ -86,15 +87,18 @@ public NewPayloadHandler( /// public async Task> HandleAsync(ExecutionPayload request) { - string requestStr = $"New Block: {request}"; - if (_logger.IsInfo) { _logger.Info($"Received {requestStr}"); } - if (!request.TryGetBlock(out Block? block, _poSSwitcher.FinalTotalDifficulty)) { - if (_logger.IsWarn) _logger.Warn($"Invalid request. Result of {requestStr}."); + if (_logger.IsWarn) _logger.Warn($"New Block Request Invalid: {request}."); return NewPayloadV1Result.Invalid(null, $"Block {request} could not be parsed as a block"); } + string requestStr = $"New Block: {request}"; + if (_logger.IsInfo) + { + _logger.Info($"Received {requestStr}, {ParseExtraData(block)}"); + } + if (!HeaderValidator.ValidateHash(block!.Header)) { if (_logger.IsWarn) _logger.Warn(InvalidBlockHelper.GetMessage(block, "invalid block hash")); @@ -451,6 +455,26 @@ private bool TryInsertDanglingBlock(Block block) return true; } + private string ParseExtraData(Block block) + { + byte[]? data = block.ExtraData; + if (data is null || data.Length == 0) + { + // If no extra data just show GasBeneficiary address + return $"Address: {(block.Header.GasBeneficiary?.ToString() ?? "0x")}"; + } + + // Ideally we'd prefer to show text; so convert invalid unicode + // and control chars to spaces and trim leading and trailing spaces. + string extraData = data.ToCleanUtf8String(); + + // If the cleaned text is less than half length of input size, + // output it as hex, else output the text. + return extraData.Length > data.Length / 2 ? + $"Extra Data: {extraData}" : + $"Hex: {data.ToHexString(withZeroX: true)}"; + } + private enum ValidationResult { Invalid, diff --git a/src/Nethermind/Nethermind.Merge.Plugin/IMergeConfig.cs b/src/Nethermind/Nethermind.Merge.Plugin/IMergeConfig.cs index 3aefc8f47fd..fb6b7747e6a 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/IMergeConfig.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/IMergeConfig.cs @@ -69,7 +69,4 @@ The number of requests to the garbage collector (GC) to release the process memo [ConfigItem(Description = "[TECHNICAL] Simulate block production for every possible slot. Just for stress-testing purposes.", DefaultValue = "false", HiddenFromDocs = true)] bool SimulateBlockProduction { get; set; } - - [ConfigItem(Description = "Whether to show the block's Extra Data field in logs", DefaultValue = "true")] - bool ShowExtraData { get; set; } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergeConfig.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergeConfig.cs index 21e7c961811..f19799e5343 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergeConfig.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergeConfig.cs @@ -34,7 +34,5 @@ public class MergeConfig : IMergeConfig public int NewPayloadTimeout { get; set; } = 7; public bool SimulateBlockProduction { get; set; } = false; - - public bool ShowExtraData { get; set; } = true; } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs index c4c2c48fdec..81b83425ace 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs @@ -363,8 +363,7 @@ IBlockImprovementContextFactory CreateBlockImprovementContextFactory() _api.SyncPeerPool!, _api.LogManager, _api.Config().SecondsPerSlot, - _api.Config().SimulateBlockProduction, - _api.Config().ShowExtraData), + _api.Config().SimulateBlockProduction), new GetPayloadBodiesByHashV1Handler(_api.BlockTree, _api.LogManager), new GetPayloadBodiesByRangeV1Handler(_api.BlockTree, _api.LogManager), new GetPayloadBodiesByHashV2Handler(_api.BlockTree, _api.LogManager), diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index e75b295cd3c..aa58d6f01bd 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -257,8 +257,7 @@ public async Task InitRpcModules() _api.SyncPeerPool!, _api.LogManager, _api.Config().SecondsPerSlot, - _api.Config().SimulateBlockProduction, - _api.Config().ShowExtraData), + _api.Config().SimulateBlockProduction), new GetPayloadBodiesByHashV1Handler(_api.BlockTree, _api.LogManager), new GetPayloadBodiesByRangeV1Handler(_api.BlockTree, _api.LogManager), new GetPayloadBodiesByHashV2Handler(_api.BlockTree, _api.LogManager), diff --git a/src/Nethermind/Nethermind.Runner/configs/chiado.cfg b/src/Nethermind/Nethermind.Runner/configs/chiado.cfg index 1dc33ff7a65..757ec3359fa 100644 --- a/src/Nethermind/Nethermind.Runner/configs/chiado.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/chiado.cfg @@ -25,7 +25,8 @@ "Blocks": { "SecondsPerSlot": 5, "BlockProductionTimeoutMs": 3000, - "TargetBlockGasLimit": 17000000 + "TargetBlockGasLimit": 17000000, + "GasToken": "XDAI" }, "Aura": { "TxPriorityContractAddress": "0x4100000000000000000000000000000000000000", diff --git a/src/Nethermind/Nethermind.Runner/configs/chiado_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/chiado_archive.cfg index 9d50f585634..413820f824b 100644 --- a/src/Nethermind/Nethermind.Runner/configs/chiado_archive.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/chiado_archive.cfg @@ -23,7 +23,8 @@ "Blocks": { "SecondsPerSlot": 5, "BlockProductionTimeoutMs": 3000, - "TargetBlockGasLimit": 17000000 + "TargetBlockGasLimit": 17000000, + "GasToken": "XDAI" }, "Receipt": { "TxLookupLimit": 0 diff --git a/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg b/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg index e98e44b71a9..0141c974265 100644 --- a/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg @@ -22,7 +22,8 @@ "Blocks": { "SecondsPerSlot": 5, "BlockProductionTimeoutMs": 3000, - "TargetBlockGasLimit": 17000000 + "TargetBlockGasLimit": 17000000, + "GasToken": "XDAI" }, "Mining": { "MinGasPrice": "1000000000" diff --git a/src/Nethermind/Nethermind.Runner/configs/gnosis_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/gnosis_archive.cfg index a963524e08b..e25fa600e47 100644 --- a/src/Nethermind/Nethermind.Runner/configs/gnosis_archive.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/gnosis_archive.cfg @@ -16,7 +16,8 @@ "Blocks": { "SecondsPerSlot": 5, "BlockProductionTimeoutMs": 3000, - "TargetBlockGasLimit": 17000000 + "TargetBlockGasLimit": 17000000, + "GasToken": "XDAI" }, "Receipt": { "TxLookupLimit": 0 From 0c42f1605d109a291dd3ef565471191e1edb3672 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sun, 27 Oct 2024 13:20:46 +0000 Subject: [PATCH 6/9] Extra --- .../Nethermind.Consensus/Processing/ProcessingStats.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs b/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs index 3febb01a49c..c975c529b83 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs @@ -286,6 +286,7 @@ public void Start() { new Address("0xa83114A443dA1CecEFC50368531cACE9F37fCCcb"), // Extra data as: beaverbuild.org new Address("0x9FC3da866e7DF3a1c57adE1a97c9f00a70f010c8"), // Extra data as: Titan (titanbuilder.xyz) + new Address("0x0b92619DdE55C0cbf828d32993a7fB004E00c84B"), // Extra data as: Builder+ www.btcs.com/builder }; } } From 0c4dfcf4c5144f43215f924520567e4bf115fd23 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 28 Oct 2024 10:07:27 +0000 Subject: [PATCH 7/9] Apply suggestions from code review Co-authored-by: Lukasz Rozmej --- src/Nethermind/Nethermind.Core/Extensions/Bytes.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs index c5e2e04e94c..61e2563bccd 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs @@ -1142,7 +1142,7 @@ public static void ChangeEndianness8(Span bytes) } } - public static string ToCleanUtf8String(this byte[] bytes) + public static string ToCleanUtf8String(this ReadOnlySpan bytes) { // The maximum number of UTF-16 chars is bytes.Length, but each Rune can be up to 2 chars. // So we allocate bytes.Length to bytes.Length * 2 chars. @@ -1191,16 +1191,14 @@ public static string ToCleanUtf8String(this byte[] bytes) else { // Control character encountered; set flag to add space if needed - if (hasValidContent) - shouldAddSpace = true; + shouldAddSpace |= hasValidContent; } index += bytesConsumed; } else if (status == OperationStatus.InvalidData) { // Invalid data; set flag to add space if needed - if (hasValidContent) - shouldAddSpace = true; + shouldAddSpace |= hasValidContent; index++; } else if (status == OperationStatus.NeedMoreData) @@ -1211,8 +1209,7 @@ public static string ToCleanUtf8String(this byte[] bytes) else { // Unexpected status; treat as invalid data - if (hasValidContent) - shouldAddSpace = true; + shouldAddSpace |= hasValidContent; index++; } } From 8672d8827832ac0e9f1028e182afd478477b3604 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 28 Oct 2024 11:09:45 +0000 Subject: [PATCH 8/9] Feedback --- .../Processing/BlockExtensions.cs | 21 ++++++++++++++++ .../Nethermind.Core/Extensions/Bytes.cs | 24 ++++++++++++------- .../Handlers/NewPayloadHandler.cs | 22 +---------------- .../Nethermind.Runner/configs/energyweb.cfg | 3 +++ .../configs/energyweb_archive.cfg | 3 +++ .../Nethermind.Runner/configs/exosama.cfg | 3 +++ .../configs/exosama_archive.cfg | 3 +++ .../Nethermind.Runner/configs/joc-mainnet.cfg | 3 ++- .../configs/joc-mainnet_archive.cfg | 3 ++- .../Nethermind.Runner/configs/joc-testnet.cfg | 3 ++- .../configs/joc-testnet_archive.cfg | 3 ++- .../Nethermind.Runner/configs/volta.cfg | 3 +++ .../configs/volta_archive.cfg | 3 +++ 13 files changed, 63 insertions(+), 34 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockExtensions.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockExtensions.cs index b63bd0dd59d..62f481c8c91 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockExtensions.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockExtensions.cs @@ -8,6 +8,7 @@ using Nethermind.Config; using Nethermind.Consensus.Producers; using Nethermind.Core; +using Nethermind.Core.Extensions; using Nethermind.State.Proofs; [assembly: InternalsVisibleTo("Nethermind.Consensus.Test")] @@ -40,5 +41,25 @@ public static bool IsByNethermindNode(this BlockHeader block) => Ascii.IsValid(block.ExtraData) && Encoding.ASCII.GetString(block.ExtraData ?? Array.Empty()) .Contains(BlocksConfig.DefaultExtraData, StringComparison.InvariantCultureIgnoreCase); + + public static string ParsedExtraData(this Block block) + { + byte[]? data = block.ExtraData; + if (data is null || data.Length == 0) + { + // If no extra data just show GasBeneficiary address + return $"Address: {(block.Header.GasBeneficiary?.ToString() ?? "0x")}"; + } + + // Ideally we'd prefer to show text; so convert invalid unicode + // and control chars to spaces and trim leading and trailing spaces. + string extraData = new ReadOnlySpan(data).ToCleanUtf8String(); + + // If the cleaned text is less than half length of input size, + // output it as hex, else output the text. + return extraData.Length > data.Length / 2 ? + $"Extra Data: {extraData}" : + $"Hex: {data.ToHexString(withZeroX: true)}"; + } } } diff --git a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs index 61e2563bccd..bbcb28b73a3 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs @@ -1152,6 +1152,7 @@ public static string ToCleanUtf8String(this ReadOnlySpan bytes) return string.Empty; // Allocate a char buffer on the stack. + char[]? charsArray = null; Span outputBuffer = stackalloc char[maxOutputChars]; int outputPos = 0; @@ -1161,7 +1162,7 @@ public static string ToCleanUtf8String(this ReadOnlySpan bytes) while (index < bytes.Length) { - ReadOnlySpan span = bytes.AsSpan(index); + ReadOnlySpan span = bytes.Slice(index); OperationStatus status = Rune.DecodeFromUtf8(span, out Rune rune, out var bytesConsumed); if (status == OperationStatus.Done) @@ -1179,9 +1180,14 @@ public static string ToCleanUtf8String(this ReadOnlySpan bytes) { // Expand output buffer int newSize = outputBuffer.Length * 2; - char[] newBuffer = new char[newSize]; + char[] newBuffer = ArrayPool.Shared.Rent(newSize); outputBuffer.Slice(0, outputPos).CopyTo(newBuffer); outputBuffer = newBuffer; + if (charsArray is not null) + { + ArrayPool.Shared.Return(charsArray); + } + charsArray = newBuffer; } rune.EncodeToUtf16(outputBuffer.Slice(outputPos)); @@ -1195,12 +1201,6 @@ public static string ToCleanUtf8String(this ReadOnlySpan bytes) } index += bytesConsumed; } - else if (status == OperationStatus.InvalidData) - { - // Invalid data; set flag to add space if needed - shouldAddSpace |= hasValidContent; - index++; - } else if (status == OperationStatus.NeedMoreData) { // Incomplete sequence at the end; break out of the loop @@ -1215,7 +1215,13 @@ public static string ToCleanUtf8String(this ReadOnlySpan bytes) } // Create the final string from the output buffer. - return outputPos > 0 ? new string(outputBuffer[..outputPos]) : string.Empty; + string outputString = outputPos > 0 ? new string(outputBuffer[..outputPos]) : string.Empty; + if (charsArray is not null) + { + ArrayPool.Shared.Return(charsArray); + } + + return outputString; } private static bool IsControlCharacter(Rune rune) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs index dd38a3e47e2..b7712ee4fe0 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs @@ -96,7 +96,7 @@ public async Task> HandleAsync(ExecutionPayload r string requestStr = $"New Block: {request}"; if (_logger.IsInfo) { - _logger.Info($"Received {requestStr}, {ParseExtraData(block)}"); + _logger.Info($"Received {requestStr}, {block.ParsedExtraData()}"); } if (!HeaderValidator.ValidateHash(block!.Header)) @@ -455,26 +455,6 @@ private bool TryInsertDanglingBlock(Block block) return true; } - private string ParseExtraData(Block block) - { - byte[]? data = block.ExtraData; - if (data is null || data.Length == 0) - { - // If no extra data just show GasBeneficiary address - return $"Address: {(block.Header.GasBeneficiary?.ToString() ?? "0x")}"; - } - - // Ideally we'd prefer to show text; so convert invalid unicode - // and control chars to spaces and trim leading and trailing spaces. - string extraData = data.ToCleanUtf8String(); - - // If the cleaned text is less than half length of input size, - // output it as hex, else output the text. - return extraData.Length > data.Length / 2 ? - $"Extra Data: {extraData}" : - $"Hex: {data.ToHexString(withZeroX: true)}"; - } - private enum ValidationResult { Invalid, diff --git a/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg b/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg index 22a34ddcd51..81b25ff5527 100644 --- a/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg @@ -26,6 +26,9 @@ "Mining": { "MinGasPrice": 1 }, + "Blocks": { + "GasToken": "EWT" + }, "Merge": { "Enabled": false } diff --git a/src/Nethermind/Nethermind.Runner/configs/energyweb_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/energyweb_archive.cfg index 051dcc33fc4..8a8727f4719 100644 --- a/src/Nethermind/Nethermind.Runner/configs/energyweb_archive.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/energyweb_archive.cfg @@ -33,6 +33,9 @@ "Receipt": { "TxLookupLimit": 0 }, + "Blocks": { + "GasToken": "EWT" + }, "Merge": { "Enabled": false } diff --git a/src/Nethermind/Nethermind.Runner/configs/exosama.cfg b/src/Nethermind/Nethermind.Runner/configs/exosama.cfg index 06b98dedc16..02d3c30e67d 100644 --- a/src/Nethermind/Nethermind.Runner/configs/exosama.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/exosama.cfg @@ -26,6 +26,9 @@ "Mining": { "MinGasPrice": 999995000 }, + "Blocks": { + "GasToken": "SAMA" + }, "Merge": { "Enabled": false } diff --git a/src/Nethermind/Nethermind.Runner/configs/exosama_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/exosama_archive.cfg index ed5c3772915..9e69483aa40 100644 --- a/src/Nethermind/Nethermind.Runner/configs/exosama_archive.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/exosama_archive.cfg @@ -27,6 +27,9 @@ "Pruning": { "Mode": "None" }, + "Blocks": { + "GasToken": "SAMA" + }, "Merge": { "Enabled": false } diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg index db085e11b66..917f1756d1a 100644 --- a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg @@ -19,7 +19,8 @@ "NodeName": "JOC-Mainnet" }, "Blocks": { - "TargetBlockGasLimit": 30000000 + "TargetBlockGasLimit": 30000000, + "GasToken": "JOC" }, "JsonRpc": { "Enabled": true diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet_archive.cfg index 5440c344f54..7a5816be536 100644 --- a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet_archive.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet_archive.cfg @@ -19,7 +19,8 @@ "NodeName": "JOC-Mainnet Archive" }, "Blocks": { - "TargetBlockGasLimit": 30000000 + "TargetBlockGasLimit": 30000000, + "GasToken": "JOC" }, "JsonRpc": { "Enabled": true diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg b/src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg index 4f8b808ea59..ed044f391e3 100644 --- a/src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg @@ -19,7 +19,8 @@ "NodeName": "JOC-Testnet" }, "Blocks": { - "TargetBlockGasLimit": 30000000 + "TargetBlockGasLimit": 30000000, + "GasToken": "JOC" }, "JsonRpc": { "Enabled": true diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-testnet_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/joc-testnet_archive.cfg index 5060d24d683..f4be5e23cbe 100644 --- a/src/Nethermind/Nethermind.Runner/configs/joc-testnet_archive.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/joc-testnet_archive.cfg @@ -19,7 +19,8 @@ "NodeName": "JOC-Testnet Archive" }, "Blocks": { - "TargetBlockGasLimit": 30000000 + "TargetBlockGasLimit": 30000000, + "GasToken": "JOC" }, "JsonRpc": { "Enabled": true diff --git a/src/Nethermind/Nethermind.Runner/configs/volta.cfg b/src/Nethermind/Nethermind.Runner/configs/volta.cfg index 24088fac369..e1cc108a237 100644 --- a/src/Nethermind/Nethermind.Runner/configs/volta.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/volta.cfg @@ -30,6 +30,9 @@ "Mining": { "MinGasPrice": 1 }, + "Blocks": { + "GasToken": "VT" + }, "Merge": { "Enabled": false } diff --git a/src/Nethermind/Nethermind.Runner/configs/volta_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/volta_archive.cfg index 83d1d2fb47f..c8282d4179b 100644 --- a/src/Nethermind/Nethermind.Runner/configs/volta_archive.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/volta_archive.cfg @@ -27,6 +27,9 @@ "Pruning": { "Mode": "None" }, + "Blocks": { + "GasToken": "VT" + }, "Merge": { "Enabled": false } From 8987f9596375e5dc1c4cb6deefa4ddbebcd1d7ea Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 28 Oct 2024 11:46:38 +0000 Subject: [PATCH 9/9] Unleash the threads! --- .../Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs index 127fa34e520..d10c9c0744a 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs @@ -51,6 +51,8 @@ public partial class EngineModuleTests [SetUp] public Task Setup() { + ThreadPool.GetMaxThreads(out int worker, out int completion); + ThreadPool.SetMinThreads(worker, completion); return KzgPolynomialCommitments.InitializeAsync(); }