From 48aab94b7aee97115bb27b4db1fa760f6d830f24 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Thu, 27 Nov 2025 16:09:36 +0300 Subject: [PATCH 1/2] Draft --- .../Ethereum.Test.Base/BlockchainTestBase.cs | 167 +++++++++++++----- .../Processing/BlockHashEventArgs.cs | 3 +- .../IBlockProcessingQueueExtensions.cs | 11 ++ .../Stateless/StatelessBlockProcessingEnv.cs | 4 - .../Validators/BlockValidator.cs | 130 +++++++++++++- .../Validators/HeaderValidator.cs | 8 +- src/Nethermind/Nethermind.Core/Block.cs | 4 +- .../Nethermind.Core/Events/WaitForEvent.cs | 10 +- .../OptimismHeaderValidator.cs | 6 +- .../ChainSpecBasedSpecProvider.cs | 2 +- .../Nethermind.Specs/Forks/15_Paris.cs | 3 +- 11 files changed, 279 insertions(+), 69 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index 374f738ab79..ea3a50d2042 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -1,13 +1,6 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Numerics; -using System.Threading; -using System.Threading.Tasks; using Autofac; using Nethermind.Blockchain; using Nethermind.Blockchain.Find; @@ -23,19 +16,26 @@ using Nethermind.Core.Specs; using Nethermind.Core.Test.Modules; using Nethermind.Crypto; +using Nethermind.Evm.State; +using Nethermind.Init.Modules; using Nethermind.Int256; +using Nethermind.JsonRpc; using Nethermind.Logging; +using Nethermind.Merge.Plugin; +using Nethermind.Merge.Plugin.Data; using Nethermind.Serialization.Rlp; using Nethermind.Specs; using Nethermind.Specs.Forks; using Nethermind.Specs.Test; -using Nethermind.Evm.State; -using Nethermind.Init.Modules; using NUnit.Framework; -using Nethermind.Merge.Plugin.Data; -using Nethermind.Merge.Plugin; -using Nethermind.JsonRpc; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Numerics; using System.Reflection; +using System.Threading; +using System.Threading.Tasks; namespace Ethereum.Test.Base; @@ -50,7 +50,7 @@ static BlockchainTestBase() { DifficultyCalculator = new DifficultyCalculatorWrapper(); _logManager ??= LimboLogs.Instance; - _logger = _logManager.GetClassLogger(); + _logger = TestLogManager.Instance.GetClassLogger(); } [SetUp] @@ -133,7 +133,12 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? .AddSingleton(specProvider) .AddSingleton(_logManager) .AddSingleton(rewardCalculator) - .AddSingleton(DifficultyCalculator); + .AddSingleton(DifficultyCalculator) + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddDecorator(); if (isEngineTest) { @@ -148,6 +153,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? IBlockTree blockTree = container.Resolve(); IBlockValidator blockValidator = container.Resolve(); blockchainProcessor.Start(); + IPoSSwitcher poSSwitcher = container.Resolve(); // Register tracer if provided for blocktest tracing if (tracer is not null) @@ -157,7 +163,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? try { - BlockHeader parentHeader; + Block parentBlock; // Genesis processing using (stateProvider.BeginScope(null)) { @@ -192,7 +198,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? blockTree.SuggestBlock(genesisBlock); genesisProcessed.WaitOne(_genesisProcessingTimeoutMs); - parentHeader = genesisBlock.Header; + parentBlock = genesisBlock; // Dispose genesis block's AccountChanges genesisBlock.DisposeAccountChanges(); @@ -201,7 +207,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? if (test.Blocks is not null) { // blockchain test - parentHeader = SuggestBlocks(test, failOnInvalidRlp, blockValidator, blockTree, parentHeader); + await SuggestBlocks(poSSwitcher, test, failOnInvalidRlp, blockValidator, blockTree, blockchainProcessor, parentBlock, isPostMerge); } else if (test.EngineNewPayloads is not null) { @@ -258,56 +264,123 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? } } - private static BlockHeader SuggestBlocks(BlockchainTest test, bool failOnInvalidRlp, IBlockValidator blockValidator, IBlockTree blockTree, BlockHeader parentHeader) + private static async Task SuggestBlocks(IPoSSwitcher _poSSwitcher, BlockchainTest test, bool failOnInvalidRlp, IBlockValidator blockValidator, IBlockTree blockTree, BlockchainProcessor blockchainProcessor, Block parentBlock, bool isPostMerge) { List<(Block Block, string ExpectedException)> correctRlp = DecodeRlps(test, failOnInvalidRlp); + string? error = null; + string? expectsException = null; + Block head = parentBlock; + for (int i = 0; i < correctRlp.Count; i++) { + error = null; // Mimic the actual behaviour where block goes through validating sync manager - correctRlp[i].Block.Header.IsPostMerge = correctRlp[i].Block.Difficulty == 0; + correctRlp[i].Block.Header.IsPostMerge = _poSSwitcher.IsPostMerge(correctRlp[i].Block.Header); + Assert.That(correctRlp[i].Block.Hash, Is.Not.Null, $"null hash in {test.Name} block {i}"); + expectsException = correctRlp[i].ExpectedException; + try + { + // For tests with reorgs, find the actual parent header from block tree + if (parentBlock.Hash != correctRlp[i].Block.ParentHash) + { + var oldParentBlock = parentBlock; + parentBlock = blockTree.FindBlock(correctRlp[i].Block.ParentHash); + // repeats new payload handler assertion + if (parentBlock is null) + { + error = $"Parent block {correctRlp[i].Block.ParentHash} not found for block {correctRlp[i].Block.Hash}"; + parentBlock = oldParentBlock; + continue; + } + // if (!isPostMerge) - // For tests with reorgs, find the actual parent header from block tree - parentHeader = blockTree.FindHeader(correctRlp[i].Block.ParentHash) ?? parentHeader; + blockTree.UpdateMainChain([parentBlock], true, true); + } - Assert.That(correctRlp[i].Block.Hash, Is.Not.Null, $"null hash in {test.Name} block {i}"); + // Validate block structure first (mimics SyncServer validation) + bool validationResult = blockValidator.ValidateSuggestedBlock(correctRlp[i].Block, parentBlock.Header, out string? validationError); - bool expectsException = correctRlp[i].ExpectedException is not null; - // Validate block structure first (mimics SyncServer validation) - if (blockValidator.ValidateSuggestedBlock(correctRlp[i].Block, parentHeader, out string? validationError)) - { - Assert.That(!expectsException, $"Expected block {correctRlp[i].Block.Hash} to fail with '{correctRlp[i].ExpectedException}', but it passed validation"); - try + if (!validationResult) + { + error = validationError; + continue; + } + + //if (blockTree.Head.Number >= correctRlp[i].Block.Number) + //{ + // continue; + //} + + bool suggested = false; + TaskCompletionSource completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + void f(object? s, BlockEventArgs e) { - // All validations passed, suggest the block - blockTree.SuggestBlock(correctRlp[i].Block); + suggested = true; + } + void f2(object? s, BlockRemovedEventArgs e) + { + completion.SetResult(e); } - catch (InvalidBlockException e) + + blockchainProcessor.BlockRemoved += f2; + blockTree.NewBestSuggestedBlock += f; + blockTree.SuggestBlock(correctRlp[i].Block, BlockTreeSuggestOptions.ShouldProcess | BlockTreeSuggestOptions.FillBeaconBlock); + blockTree.NewBestSuggestedBlock -= f; + + if (suggested) { - // Exception thrown during block processing - Assert.That(expectsException, $"Unexpected invalid block {correctRlp[i].Block.Hash}: {validationError}, Exception: {e}"); - // else: Expected to fail and did fail via exception → this is correct behavior + await completion.Task; + if (completion.Task.Result.ProcessingResult is not ProcessingResult.Success) + { + error = $"Error processing block {correctRlp[i].Block.Hash}: {completion.Task.Result.Message ?? completion.Task.Result.Exception?.ToString()}"; + break; + } } - catch (Exception e) + + blockchainProcessor.BlockRemoved -= f2; + + parentBlock = correctRlp[i].Block; + + if (!isPostMerge) { - Assert.Fail($"Unexpected exception during processing: {e}"); + if (head.Number < parentBlock.Number || head.TotalDifficulty < parentBlock.TotalDifficulty || (head.TotalDifficulty is not null && head.TotalDifficulty == parentBlock.TotalDifficulty && parentBlock.Timestamp < head.Timestamp)) + { + head = parentBlock; + } } - finally + else { - // Dispose AccountChanges to prevent memory leaks in tests - correctRlp[i].Block.DisposeAccountChanges(); + head = parentBlock; } } - else + catch (Exception e) { - // Validation FAILED - Assert.That(expectsException, $"Unexpected invalid block {correctRlp[i].Block.Hash}: {validationError}"); - // else: Expected to fail and did fail → this is correct behavior + Assert.Fail($"Unexpected exception during processing: {e} {e.StackTrace}"); } + finally + { + // Dispose AccountChanges to prevent memory leaks in tests + correctRlp[i].Block.DisposeAccountChanges(); - parentHeader = correctRlp[i].Block.Header; + if (error is null) + { + Assert.That(expectsException, Is.Null, $"Unexpected valid block, expected failure: {expectsException}"); + } + else + { + Assert.That(expectsException, Is.Not.Null, $"Unexpected invalid block: {error}"); + } + } } - return parentHeader; + blockTree.UpdateMainChain([head], true, true); + return head.Header; + } + + private static void BlockTree_NewBestSuggestedBlock(object? sender, BlockEventArgs e) + { + throw new NotImplementedException(); } private async static Task RunNewPayloads(TestEngineNewPayloadsJson[]? newPayloads, IEngineRpcModule engineRpcModule) @@ -361,9 +434,9 @@ private async static Task RunNewPayloads(TestEngineNewPayloadsJson[]? newPayload { Assert.That(suggestedBlock.Uncles[uncleIndex].Hash, Is.EqualTo(new Hash256(testBlockJson.UncleHeaders![uncleIndex].Hash))); } - - correctRlp.Add((suggestedBlock, testBlockJson.ExpectedException)); } + + correctRlp.Add((suggestedBlock, testBlockJson.ExpectedException)); } catch (Exception e) { diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockHashEventArgs.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockHashEventArgs.cs index f28e835aaa1..b1b01022047 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockHashEventArgs.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockHashEventArgs.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -22,6 +22,7 @@ public BlockHashEventArgs(Hash256 blockHash, ProcessingResult processingResult, public enum ProcessingResult { + None, /// /// Processing was successful /// diff --git a/src/Nethermind/Nethermind.Consensus/Processing/IBlockProcessingQueueExtensions.cs b/src/Nethermind/Nethermind.Consensus/Processing/IBlockProcessingQueueExtensions.cs index 36309b4d35e..dbaa66825ef 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/IBlockProcessingQueueExtensions.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/IBlockProcessingQueueExtensions.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; +using Nethermind.Core.Crypto; using Nethermind.Core.Events; namespace Nethermind.Consensus.Processing; @@ -18,4 +19,14 @@ await Wait.ForEvent(cancellationToken, e => blockProcessingQueue.ProcessingQueueEmpty -= e); } } + + public static async Task WaitForBlockProcessing(this IBlockProcessingQueue blockProcessingQueue, Hash256 blockHash, CancellationToken cancellationToken = default) + { + var res = await Wait.ForEventCondition(cancellationToken, + e => blockProcessingQueue.BlockRemoved += e, + e => blockProcessingQueue.BlockRemoved -= e, + e => e.BlockHash == blockHash).ConfigureAwait(false); + + return res.ProcessingResult; + } } diff --git a/src/Nethermind/Nethermind.Consensus/Stateless/StatelessBlockProcessingEnv.cs b/src/Nethermind/Nethermind.Consensus/Stateless/StatelessBlockProcessingEnv.cs index 976667fae20..3ea34ae98bd 100644 --- a/src/Nethermind/Nethermind.Consensus/Stateless/StatelessBlockProcessingEnv.cs +++ b/src/Nethermind/Nethermind.Consensus/Stateless/StatelessBlockProcessingEnv.cs @@ -11,17 +11,13 @@ using Nethermind.Consensus.Rewards; using Nethermind.Consensus.Validators; using Nethermind.Consensus.Withdrawals; -using Nethermind.Core; -using Nethermind.Core.Crypto; using Nethermind.Core.Specs; -using Nethermind.Db; using Nethermind.Evm; using Nethermind.Evm.State; using Nethermind.Evm.TransactionProcessing; using Nethermind.Logging; using Nethermind.State; using Nethermind.Trie; -using Nethermind.Trie.Pruning; namespace Nethermind.Consensus.Stateless; diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index 95332e5d22a..a529a28e692 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -1,8 +1,9 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; using System.Diagnostics.CodeAnalysis; +using System.Numerics; using System.Text; using Nethermind.Blockchain; using Nethermind.Core; @@ -75,7 +76,8 @@ private bool ValidateBlock(Block block, BlockHeader? parent, out stri ValidateUncles(block, spec, validateHashes, ref errorMessage) && ValidateHeader(block, parent, ref errorMessage) && ValidateTxRootMatchesTxs(block, validateHashes, ref errorMessage) && - ValidateWithdrawals(block, spec, validateHashes, ref errorMessage); + ValidateWithdrawals(block, spec, validateHashes, ref errorMessage);// && + //ValidateDifficulty(block, parent, spec, ref errorMessage); } private bool ValidateHeader(Block block, BlockHeader? parent, ref string? errorMessage) @@ -275,6 +277,130 @@ protected virtual bool ValidateWithdrawals(Block block, IReleaseSpec spec, bool return true; } + //protected virtual bool ValidateDifficulty(Block block, BlockHeader parentBlockHeader, IReleaseSpec spec, ref string? errorMessage) + //{ + // if (block.Header.IsPostMerge) + // { + // if (block.Difficulty != 0) + // { + // errorMessage = "Bad #1"; + // return false; + // } + + + // if (block.TotalDifficulty is not null) + // { + // errorMessage = "Bad #2"; + // return false; + // } + // } + // else + // { + // if (block.Difficulty == 0) + // { + // errorMessage = "Bad #3"; + // return false; + // } + + + // if (block.TotalDifficulty == 0) + // { + // errorMessage = "Bad #4"; + // return false; + // } + + + // if (block.TotalDifficulty is not null && block.TotalDifficulty != block.Header.Difficulty + parentBlockHeader.TotalDifficulty) + // { + // errorMessage = "Bad #5"; + // return false; + // } + + // if (block.TotalDifficulty is not null && new EthashDifficultyCalculator(specProvider).Calculate(block.Header, parentBlockHeader) != block.Difficulty) + // { + // errorMessage = "Bad #6"; + // return false; + // } + // } + + // return true; + //} + + internal class EthashDifficultyCalculator : IDifficultyCalculator + { + // Note: block 200000 is when the difficulty bomb was introduced but we did not spec it in any release info, just hardcoded it + public const int InitialDifficultyBombBlock = 200000; + private readonly ISpecProvider _specProvider; + + public EthashDifficultyCalculator(ISpecProvider specProvider) + { + _specProvider = specProvider; + } + + private const long OfGenesisBlock = 131_072; + + public UInt256 Calculate(BlockHeader header, BlockHeader parent) => + Calculate(parent.Difficulty, + parent.Timestamp, + header.Timestamp, + header.Number, + parent.UnclesHash != Keccak.OfAnEmptySequenceRlp); + + public UInt256 Calculate( + in UInt256 parentDifficulty, + ulong parentTimestamp, + ulong currentTimestamp, + long blockNumber, + bool parentHasUncles) + { + IReleaseSpec spec = _specProvider.GetSpec(blockNumber, currentTimestamp); + if (spec.FixedDifficulty is not null && blockNumber != 0) + { + return (UInt256)spec.FixedDifficulty.Value; + } + + BigInteger baseIncrease = BigInteger.Divide((BigInteger)parentDifficulty, spec.DifficultyBoundDivisor); + BigInteger timeAdjustment = TimeAdjustment(spec, (BigInteger)parentTimestamp, (BigInteger)currentTimestamp, parentHasUncles); + BigInteger timeBomb = TimeBomb(spec, blockNumber); + return (UInt256)BigInteger.Max( + OfGenesisBlock, + (BigInteger)parentDifficulty + + timeAdjustment * baseIncrease + + timeBomb); + } + + private static BigInteger TimeAdjustment( + IReleaseSpec spec, + BigInteger parentTimestamp, + BigInteger currentTimestamp, + bool parentHasUncles) + { + if (spec.IsEip100Enabled) + { + return BigInteger.Max((parentHasUncles ? 2 : BigInteger.One) - BigInteger.Divide(currentTimestamp - parentTimestamp, 9), -99); + } + + if (spec.IsEip2Enabled) + { + return BigInteger.Max(BigInteger.One - BigInteger.Divide(currentTimestamp - parentTimestamp, 10), -99); + } + + if (spec.IsTimeAdjustmentPostOlympic) + { + return currentTimestamp < parentTimestamp + 13 ? BigInteger.One : BigInteger.MinusOne; + } + + return currentTimestamp < parentTimestamp + 7 ? BigInteger.One : BigInteger.MinusOne; + } + + private static BigInteger TimeBomb(IReleaseSpec spec, long blockNumber) + { + blockNumber -= spec.DifficultyBombDelay; + + return blockNumber < InitialDifficultyBombBlock ? BigInteger.Zero : BigInteger.Pow(2, (int)(BigInteger.Divide(blockNumber, 100000) - 2)); + } + } + protected virtual bool ValidateTransactions(Block block, IReleaseSpec spec, ref string? errorMessage) { Transaction[] transactions = block.Transactions; diff --git a/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs index 43322f640a7..b4c007fd0a6 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -85,7 +85,7 @@ protected virtual bool Validate(BlockHeader header, BlockHeader? pare return ValidateFieldLimit(header, ref error) && ValidateHash(header, ref error) && ValidateExtraData(header, spec = _specProvider.GetSpec(header), isUncle, ref error) - && (orphaned || ValidateParent(header, parent, ref error)) + && (orphaned || ValidateParent(header, parent, ref error, ref orphaned)) && (orphaned || ValidateTotalDifficulty(header, parent, ref error)) && (orphaned || ValidateSeal(header, parent, isUncle, ref error)) && ValidateGasUsed(header, ref error) @@ -164,7 +164,7 @@ protected virtual bool ValidateGasUsed(BlockHeader header, ref string? error) return true; } - protected virtual bool ValidateParent(BlockHeader header, BlockHeader? parent, ref string? error) + protected virtual bool ValidateParent(BlockHeader header, BlockHeader? parent, ref string? error, ref bool orphaned) { if (parent is null) { @@ -182,6 +182,8 @@ protected virtual bool ValidateParent(BlockHeader header, BlockHeader? parent, r error = BlockErrorMessages.InvalidGenesisBlock; return false; } + + orphaned = true; } else if (parent.Hash != header.ParentHash) { diff --git a/src/Nethermind/Nethermind.Core/Block.cs b/src/Nethermind/Nethermind.Core/Block.cs index 8d53097d519..489a85c4d66 100644 --- a/src/Nethermind/Nethermind.Core/Block.cs +++ b/src/Nethermind/Nethermind.Core/Block.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -15,7 +15,7 @@ namespace Nethermind.Core; -[DebuggerDisplay("{Hash} ({Number})")] +[DebuggerDisplay("{Hash} ({Number}) > {ParentHash} {Difficulty}/{TotalDifficulty}")] public class Block { public Block(BlockHeader header, BlockBody body) diff --git a/src/Nethermind/Nethermind.Core/Events/WaitForEvent.cs b/src/Nethermind/Nethermind.Core/Events/WaitForEvent.cs index 6ac85b0835e..f36a92d230a 100644 --- a/src/Nethermind/Nethermind.Core/Events/WaitForEvent.cs +++ b/src/Nethermind/Nethermind.Core/Events/WaitForEvent.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -9,18 +9,18 @@ namespace Nethermind.Core.Events; public static class Wait { - public static async Task ForEventCondition( + public static async Task ForEventCondition( CancellationToken cancellationToken, Action> register, Action> unregister, Func condition) { - TaskCompletionSource completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + TaskCompletionSource completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); void handler(object? sender, T t) { if (condition(t)) { - completion.TrySetResult(); + completion.TrySetResult(t); } } @@ -30,7 +30,7 @@ void handler(object? sender, T t) { await using (cancellationToken.Register(() => completion.TrySetCanceled(cancellationToken))) { - await completion.Task; + return await completion.Task; } } finally diff --git a/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs b/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs index 18d8845935b..be276a51b47 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System.Diagnostics.CodeAnalysis; using Nethermind.Blockchain; using Nethermind.Consensus; using Nethermind.Consensus.Validators; @@ -21,7 +20,8 @@ public class PreBedrockHeaderValidator( protected override bool Validate(BlockHeader header, BlockHeader? parent, bool isUncle, out string? error) { error = null; - return typeof(TOrphaned) == typeof(OnFlag) || ValidateParent(header, parent, ref error); + bool orphaned = false; + return typeof(TOrphaned) == typeof(OnFlag) || ValidateParent(header, parent, ref error, ref orphaned); } } diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index 9ab4f76e408..0a61674c1a8 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -182,7 +182,7 @@ private static ForkActivation[] CreateTransitionActivations(SortedSet tran protected virtual ReleaseSpec CreateReleaseSpec(ChainSpec chainSpec, long releaseStartBlock, ulong? releaseStartTimestamp = null) { ReleaseSpec releaseSpec = CreateEmptyReleaseSpec(); - releaseSpec.MaximumUncleCount = 2; + releaseSpec.MaximumUncleCount = releaseStartTimestamp is not null ? 2 : 0; releaseSpec.DifficultyBoundDivisor = 1; releaseSpec.IsTimeAdjustmentPostOlympic = true; // TODO: this is Duration, review releaseSpec.MaximumExtraDataSize = chainSpec.Parameters.MaximumExtraDataSize; diff --git a/src/Nethermind/Nethermind.Specs/Forks/15_Paris.cs b/src/Nethermind/Nethermind.Specs/Forks/15_Paris.cs index abdecfbeb20..cab10e56efa 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/15_Paris.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/15_Paris.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System.Threading; @@ -13,6 +13,7 @@ public class Paris : GrayGlacier protected Paris() { Name = "Paris"; + MaximumUncleCount = 0; } public new static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, static () => new Paris()); From 69816444f963b25e111197d02c8feb04502bbe36 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Fri, 28 Nov 2025 14:45:18 +0300 Subject: [PATCH 2/2] Uncles=0 starting from Shanghai --- .../Nethermind.Ethash.Test/ChainSpecTest.cs | 4 +- .../ChainSpecBasedSpecProviderTests.cs | 198 ++++++++++-------- .../ChainSpecBasedSpecProvider.cs | 2 +- .../Nethermind.Specs/Forks/15_Paris.cs | 1 - .../Nethermind.Specs/Forks/16_Shanghai.cs | 3 +- .../Nethermind.Specs/HoodiSpecProvider.cs | 2 +- 6 files changed, 115 insertions(+), 95 deletions(-) diff --git a/src/Nethermind/Nethermind.Ethash.Test/ChainSpecTest.cs b/src/Nethermind/Nethermind.Ethash.Test/ChainSpecTest.cs index 1997faa4a28..254c6c69427 100644 --- a/src/Nethermind/Nethermind.Ethash.Test/ChainSpecTest.cs +++ b/src/Nethermind/Nethermind.Ethash.Test/ChainSpecTest.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -158,7 +158,7 @@ void TestTransitions(ForkActivation activation, Action changes) r.MaxCodeSize = long.MaxValue; r.Eip1559TransitionBlock = 15590L; r.IsTimeAdjustmentPostOlympic = true; - r.MaximumUncleCount = 2; + r.MaximumUncleCount = 0; r.WithdrawalTimestamp = ulong.MaxValue; r.Eip4844TransitionTimestamp = ulong.MaxValue; }); diff --git a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs index a1f1d9606ef..66ef7c60fcd 100644 --- a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs +++ b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Nethermind.Config; using Nethermind.Consensus.AuRa.Config; using Nethermind.Core; @@ -64,6 +65,7 @@ public void Timestamp_activation_equal_to_genesis_timestamp_loads_correctly(long expectedSpec.Eip1559TransitionBlock = 0; expectedSpec.DifficultyBombDelay = 0; expectedSpec.IsEip3855Enabled = isEip3855Enabled; + expectedSpec.MaximumUncleCount = 0; TestSpecProvider testProvider = TestSpecProvider.Instance; testProvider.NextForkSpec = expectedSpec; testProvider.TerminalTotalDifficulty = 0; @@ -167,39 +169,45 @@ public static IEnumerable SepoliaActivations } } - [TestCaseSource(nameof(SepoliaActivations))] - public void Sepolia_loads_properly(ForkActivation forkActivation) + [Test] + public void Sepolia_loads_properly() { - ChainSpec chainSpec = LoadChainSpecFromChainFolder("sepolia"); - ChainSpecBasedSpecProvider provider = new(chainSpec); - SepoliaSpecProvider sepolia = SepoliaSpecProvider.Instance; - - CompareSpecs(sepolia, provider, forkActivation); - using (Assert.EnterMultipleScope()) + foreach (var testCase in SepoliaActivations) { - Assert.That(provider.TerminalTotalDifficulty, Is.EqualTo(SepoliaSpecProvider.Instance.TerminalTotalDifficulty)); - Assert.That(provider.GenesisSpec.Eip1559TransitionBlock, Is.Zero); - Assert.That(provider.GenesisSpec.DifficultyBombDelay, Is.EqualTo(long.MaxValue)); - Assert.That(provider.ChainId, Is.EqualTo(BlockchainIds.Sepolia)); - Assert.That(provider.NetworkId, Is.EqualTo(BlockchainIds.Sepolia)); + var forkActivation = (ForkActivation)testCase.Arguments[0]!; - IEnumerable timestamps = GetTransitionTimestamps(chainSpec.Parameters); - foreach (ulong t in timestamps) + ChainSpec chainSpec = LoadChainSpecFromChainFolder("sepolia"); + ChainSpecBasedSpecProvider provider = new(chainSpec); + SepoliaSpecProvider sepolia = SepoliaSpecProvider.Instance; + + CompareSpecs(sepolia, provider, forkActivation); + using (Assert.EnterMultipleScope()) { - Assert.That(ValidateSlotByTimestamp(t, SepoliaSpecProvider.BeaconChainGenesisTimestampConst), Is.True); + Assert.That(provider.TerminalTotalDifficulty, Is.EqualTo(SepoliaSpecProvider.Instance.TerminalTotalDifficulty)); + Assert.That(provider.GenesisSpec.Eip1559TransitionBlock, Is.Zero); + Assert.That(provider.GenesisSpec.DifficultyBombDelay, Is.EqualTo(long.MaxValue)); + Assert.That(provider.ChainId, Is.EqualTo(BlockchainIds.Sepolia)); + Assert.That(provider.NetworkId, Is.EqualTo(BlockchainIds.Sepolia)); + + IEnumerable timestamps = GetTransitionTimestamps(chainSpec.Parameters); + foreach (ulong t in timestamps) + { + Assert.That(ValidateSlotByTimestamp(t, SepoliaSpecProvider.BeaconChainGenesisTimestampConst), Is.True); + } } - } - IReleaseSpec postCancunSpec = provider.GetSpec((2, SepoliaSpecProvider.CancunTimestamp)); - VerifyCancunSpecificsForMainnetAndSepolia(postCancunSpec); + IReleaseSpec postCancunSpec = provider.GetSpec((2, SepoliaSpecProvider.CancunTimestamp)); + VerifyCancunSpecificsForMainnetAndSepolia(postCancunSpec); - IReleaseSpec postPragueSpec = provider.GetSpec((2, SepoliaSpecProvider.PragueTimestamp)); - VerifyPragueSpecificsForMainnetHoodiAndSepolia(provider.ChainId, postPragueSpec); + IReleaseSpec postPragueSpec = provider.GetSpec((2, SepoliaSpecProvider.PragueTimestamp)); + VerifyPragueSpecificsForMainnetHoodiAndSepolia(provider.ChainId, postPragueSpec); + + IReleaseSpec postOsakaSpec = provider.GetSpec((2, SepoliaSpecProvider.OsakaTimestamp)); + IReleaseSpec postBPO1Spec = provider.GetSpec((2, SepoliaSpecProvider.BPO1Timestamp)); + IReleaseSpec postBPO2Spec = provider.GetSpec((2, SepoliaSpecProvider.BPO2Timestamp)); + VerifyOsakaSpecificsForMainnetHoleskyHoodiAndSepolia(provider.ChainId, postOsakaSpec, postBPO1Spec, postBPO2Spec); + } - IReleaseSpec postOsakaSpec = provider.GetSpec((2, SepoliaSpecProvider.OsakaTimestamp)); - IReleaseSpec postBPO1Spec = provider.GetSpec((2, SepoliaSpecProvider.BPO1Timestamp)); - IReleaseSpec postBPO2Spec = provider.GetSpec((2, SepoliaSpecProvider.BPO2Timestamp)); - VerifyOsakaSpecificsForMainnetHoleskyHoodiAndSepolia(provider.ChainId, postOsakaSpec, postBPO1Spec, postBPO2Spec); } private static void VerifyCancunSpecificsForMainnetAndSepolia(IReleaseSpec spec) @@ -282,33 +290,39 @@ public static IEnumerable HoodiActivations } } - [TestCaseSource(nameof(HoodiActivations))] - public void Hoodi_loads_properly(ForkActivation forkActivation) + [Test] + public void Hoodi_loads_properly() { - ChainSpec chainSpec = LoadChainSpecFromChainFolder("hoodi"); - ChainSpecBasedSpecProvider provider = new(chainSpec); - ISpecProvider hardCodedSpec = HoodiSpecProvider.Instance; - - CompareSpecs(hardCodedSpec, provider, forkActivation); - using (Assert.EnterMultipleScope()) + foreach (TestCaseData testCase in HoodiActivations) { - Assert.That(provider.TerminalTotalDifficulty, Is.EqualTo(hardCodedSpec.TerminalTotalDifficulty)); - Assert.That(provider.GenesisSpec.Eip1559TransitionBlock, Is.Zero); - Assert.That(provider.GenesisSpec.DifficultyBombDelay, Is.Zero); - Assert.That(provider.ChainId, Is.EqualTo(BlockchainIds.Hoodi)); - Assert.That(provider.NetworkId, Is.EqualTo(BlockchainIds.Hoodi)); - } + ForkActivation forkActivation = (ForkActivation)testCase.Arguments[0]!; - IReleaseSpec postCancunSpec = provider.GetSpec((2, HoodiSpecProvider.CancunTimestamp)); - VerifyCancunSpecificsForMainnetAndSepolia(postCancunSpec); + ChainSpec chainSpec = LoadChainSpecFromChainFolder("hoodi"); + ChainSpecBasedSpecProvider provider = new(chainSpec); + ISpecProvider hardCodedSpec = HoodiSpecProvider.Instance; - IReleaseSpec postPragueSpec = provider.GetSpec((2, HoodiSpecProvider.PragueTimestamp)); - VerifyPragueSpecificsForMainnetHoodiAndSepolia(provider.ChainId, postPragueSpec); + CompareSpecs(hardCodedSpec, provider, forkActivation); + using (Assert.EnterMultipleScope()) + { + Assert.That(provider.TerminalTotalDifficulty, Is.EqualTo(hardCodedSpec.TerminalTotalDifficulty)); + Assert.That(provider.GenesisSpec.Eip1559TransitionBlock, Is.Zero); + Assert.That(provider.GenesisSpec.DifficultyBombDelay, Is.Zero); + Assert.That(provider.ChainId, Is.EqualTo(BlockchainIds.Hoodi)); + Assert.That(provider.NetworkId, Is.EqualTo(BlockchainIds.Hoodi)); + } + + IReleaseSpec postCancunSpec = provider.GetSpec((2, HoodiSpecProvider.CancunTimestamp)); + VerifyCancunSpecificsForMainnetAndSepolia(postCancunSpec); + + IReleaseSpec postPragueSpec = provider.GetSpec((2, HoodiSpecProvider.PragueTimestamp)); + VerifyPragueSpecificsForMainnetHoodiAndSepolia(provider.ChainId, postPragueSpec); + + IReleaseSpec postOsakaSpec = provider.GetSpec((2, HoodiSpecProvider.OsakaTimestamp)); + IReleaseSpec postBPO1Spec = provider.GetSpec((2, HoodiSpecProvider.BPO1Timestamp)); + IReleaseSpec postBPO2Spec = provider.GetSpec((2, HoodiSpecProvider.BPO2Timestamp)); + VerifyOsakaSpecificsForMainnetHoleskyHoodiAndSepolia(provider.ChainId, postOsakaSpec, postBPO1Spec, postBPO2Spec); + } - IReleaseSpec postOsakaSpec = provider.GetSpec((2, HoodiSpecProvider.OsakaTimestamp)); - IReleaseSpec postBPO1Spec = provider.GetSpec((2, HoodiSpecProvider.BPO1Timestamp)); - IReleaseSpec postBPO2Spec = provider.GetSpec((2, HoodiSpecProvider.BPO2Timestamp)); - VerifyOsakaSpecificsForMainnetHoleskyHoodiAndSepolia(provider.ChainId, postOsakaSpec, postBPO1Spec, postBPO2Spec); } @@ -558,56 +572,62 @@ public static IEnumerable MainnetActivations } } - [TestCaseSource(nameof(MainnetActivations))] - public void Mainnet_loads_properly(ForkActivation forkActivation) + [Test] + public void Mainnet_loads_properly() { - ChainSpec chainSpec = LoadChainSpecFromChainFolder("foundation"); - ChainSpecBasedSpecProvider provider = new(chainSpec); - MainnetSpecProvider mainnet = MainnetSpecProvider.Instance; + foreach (TestCaseData testCase in MainnetActivations) + { + var forkActivation = (ForkActivation)testCase.Arguments[0]!; - CompareSpecs(mainnet, provider, forkActivation, CompareSpecsOptions.CheckDifficultyBomb); - using (Assert.EnterMultipleScope()) - { - Assert.That(provider.GetSpec((MainnetSpecProvider.SpuriousDragonBlockNumber, null)).MaxCodeSize, Is.EqualTo(CodeSizeConstants.MaxCodeSizeEip170)); - Assert.That(provider.GetSpec((MainnetSpecProvider.SpuriousDragonBlockNumber, null)).MaxInitCodeSize, Is.EqualTo(2 * CodeSizeConstants.MaxCodeSizeEip170)); - Assert.That(provider.GetSpec((ForkActivation)(long.MaxValue - 1)).IsEip2537Enabled, Is.False); - Assert.That(provider.GenesisSpec.Eip1559TransitionBlock, Is.EqualTo(MainnetSpecProvider.LondonBlockNumber)); - Assert.That(provider.GetSpec((ForkActivation)4_369_999).DifficultyBombDelay, Is.EqualTo(0_000_000)); - Assert.That(provider.GetSpec((ForkActivation)4_370_000).DifficultyBombDelay, Is.EqualTo(3_000_000)); - Assert.That(provider.GetSpec((ForkActivation)7_279_999).DifficultyBombDelay, Is.EqualTo(3_000_000)); - Assert.That(provider.GetSpec((ForkActivation)7_279_999).DifficultyBombDelay, Is.EqualTo(3_000_000)); - Assert.That(provider.GetSpec((ForkActivation)7_280_000).DifficultyBombDelay, Is.EqualTo(5_000_000)); - Assert.That(provider.GetSpec((ForkActivation)9_199_999).DifficultyBombDelay, Is.EqualTo(5_000_000)); - Assert.That(provider.GetSpec((ForkActivation)9_200_000).DifficultyBombDelay, Is.EqualTo(9_000_000)); - Assert.That(provider.GetSpec((ForkActivation)12_000_000).DifficultyBombDelay, Is.EqualTo(9_000_000)); - Assert.That(provider.GetSpec((ForkActivation)12_964_999).DifficultyBombDelay, Is.EqualTo(9_000_000)); - Assert.That(provider.GetSpec((ForkActivation)12_965_000).DifficultyBombDelay, Is.EqualTo(9_700_000)); - Assert.That(provider.GetSpec((ForkActivation)13_772_999).DifficultyBombDelay, Is.EqualTo(9_700_000)); - Assert.That(provider.GetSpec((ForkActivation)13_773_000).DifficultyBombDelay, Is.EqualTo(10_700_000)); - Assert.That(provider.GetSpec((ForkActivation)15_049_999).DifficultyBombDelay, Is.EqualTo(10_700_000)); - Assert.That(provider.GetSpec((ForkActivation)15_050_000).DifficultyBombDelay, Is.EqualTo(11_400_000)); - Assert.That(provider.GetSpec((ForkActivation)99_414_000).DifficultyBombDelay, Is.EqualTo(11_400_000)); - Assert.That(provider.TerminalTotalDifficulty, Is.EqualTo(MainnetSpecProvider.Instance.TerminalTotalDifficulty)); - Assert.That(provider.ChainId, Is.EqualTo(BlockchainIds.Mainnet)); - Assert.That(provider.NetworkId, Is.EqualTo(BlockchainIds.Mainnet)); + ChainSpec chainSpec = LoadChainSpecFromChainFolder("foundation"); + ChainSpecBasedSpecProvider provider = new(chainSpec); + MainnetSpecProvider mainnet = MainnetSpecProvider.Instance; - IEnumerable timestamps = GetTransitionTimestamps(chainSpec.Parameters); - foreach (ulong t in timestamps) + CompareSpecs(mainnet, provider, forkActivation, CompareSpecsOptions.CheckDifficultyBomb); + + using (Assert.EnterMultipleScope()) { - Assert.That(ValidateSlotByTimestamp(t, MainnetSpecProvider.BeaconChainGenesisTimestampConst), Is.True); + Assert.That(provider.GetSpec((MainnetSpecProvider.SpuriousDragonBlockNumber, null)).MaxCodeSize, Is.EqualTo(CodeSizeConstants.MaxCodeSizeEip170)); + Assert.That(provider.GetSpec((MainnetSpecProvider.SpuriousDragonBlockNumber, null)).MaxInitCodeSize, Is.EqualTo(2 * CodeSizeConstants.MaxCodeSizeEip170)); + Assert.That(provider.GetSpec((ForkActivation)(long.MaxValue - 1)).IsEip2537Enabled, Is.False); + Assert.That(provider.GenesisSpec.Eip1559TransitionBlock, Is.EqualTo(MainnetSpecProvider.LondonBlockNumber)); + Assert.That(provider.GetSpec((ForkActivation)4_369_999).DifficultyBombDelay, Is.EqualTo(0_000_000)); + Assert.That(provider.GetSpec((ForkActivation)4_370_000).DifficultyBombDelay, Is.EqualTo(3_000_000)); + Assert.That(provider.GetSpec((ForkActivation)7_279_999).DifficultyBombDelay, Is.EqualTo(3_000_000)); + Assert.That(provider.GetSpec((ForkActivation)7_279_999).DifficultyBombDelay, Is.EqualTo(3_000_000)); + Assert.That(provider.GetSpec((ForkActivation)7_280_000).DifficultyBombDelay, Is.EqualTo(5_000_000)); + Assert.That(provider.GetSpec((ForkActivation)9_199_999).DifficultyBombDelay, Is.EqualTo(5_000_000)); + Assert.That(provider.GetSpec((ForkActivation)9_200_000).DifficultyBombDelay, Is.EqualTo(9_000_000)); + Assert.That(provider.GetSpec((ForkActivation)12_000_000).DifficultyBombDelay, Is.EqualTo(9_000_000)); + Assert.That(provider.GetSpec((ForkActivation)12_964_999).DifficultyBombDelay, Is.EqualTo(9_000_000)); + Assert.That(provider.GetSpec((ForkActivation)12_965_000).DifficultyBombDelay, Is.EqualTo(9_700_000)); + Assert.That(provider.GetSpec((ForkActivation)13_772_999).DifficultyBombDelay, Is.EqualTo(9_700_000)); + Assert.That(provider.GetSpec((ForkActivation)13_773_000).DifficultyBombDelay, Is.EqualTo(10_700_000)); + Assert.That(provider.GetSpec((ForkActivation)15_049_999).DifficultyBombDelay, Is.EqualTo(10_700_000)); + Assert.That(provider.GetSpec((ForkActivation)15_050_000).DifficultyBombDelay, Is.EqualTo(11_400_000)); + Assert.That(provider.GetSpec((ForkActivation)99_414_000).DifficultyBombDelay, Is.EqualTo(11_400_000)); + Assert.That(provider.TerminalTotalDifficulty, Is.EqualTo(MainnetSpecProvider.Instance.TerminalTotalDifficulty)); + Assert.That(provider.ChainId, Is.EqualTo(BlockchainIds.Mainnet)); + Assert.That(provider.NetworkId, Is.EqualTo(BlockchainIds.Mainnet)); + + IEnumerable timestamps = GetTransitionTimestamps(chainSpec.Parameters); + foreach (ulong t in timestamps) + { + Assert.That(ValidateSlotByTimestamp(t, MainnetSpecProvider.BeaconChainGenesisTimestampConst), Is.True); + } } - } - IReleaseSpec postCancunSpec = provider.GetSpec(MainnetSpecProvider.CancunActivation); - IReleaseSpec postPragueSpec = provider.GetSpec(MainnetSpecProvider.PragueActivation); - IReleaseSpec postOsakaSpec = provider.GetSpec(MainnetSpecProvider.OsakaActivation); - IReleaseSpec postBPO1Spec = provider.GetSpec(MainnetSpecProvider.BPO1Activation); - IReleaseSpec postBPO2Spec = provider.GetSpec(MainnetSpecProvider.BPO2Activation); + IReleaseSpec postCancunSpec = provider.GetSpec(MainnetSpecProvider.CancunActivation); + IReleaseSpec postPragueSpec = provider.GetSpec(MainnetSpecProvider.PragueActivation); + IReleaseSpec postOsakaSpec = provider.GetSpec(MainnetSpecProvider.OsakaActivation); + IReleaseSpec postBPO1Spec = provider.GetSpec(MainnetSpecProvider.BPO1Activation); + IReleaseSpec postBPO2Spec = provider.GetSpec(MainnetSpecProvider.BPO2Activation); - VerifyCancunSpecificsForMainnetAndSepolia(postCancunSpec); - VerifyPragueSpecificsForMainnetHoodiAndSepolia(provider.ChainId, postPragueSpec); - VerifyOsakaSpecificsForMainnetHoleskyHoodiAndSepolia(provider.ChainId, postOsakaSpec, postBPO1Spec, postBPO2Spec); + VerifyCancunSpecificsForMainnetAndSepolia(postCancunSpec); + VerifyPragueSpecificsForMainnetHoodiAndSepolia(provider.ChainId, postPragueSpec); + VerifyOsakaSpecificsForMainnetHoleskyHoodiAndSepolia(provider.ChainId, postOsakaSpec, postBPO1Spec, postBPO2Spec); + } } [Flags] diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index 0a61674c1a8..8d7e6fbd766 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -182,7 +182,6 @@ private static ForkActivation[] CreateTransitionActivations(SortedSet tran protected virtual ReleaseSpec CreateReleaseSpec(ChainSpec chainSpec, long releaseStartBlock, ulong? releaseStartTimestamp = null) { ReleaseSpec releaseSpec = CreateEmptyReleaseSpec(); - releaseSpec.MaximumUncleCount = releaseStartTimestamp is not null ? 2 : 0; releaseSpec.DifficultyBoundDivisor = 1; releaseSpec.IsTimeAdjustmentPostOlympic = true; // TODO: this is Duration, review releaseSpec.MaximumExtraDataSize = chainSpec.Parameters.MaximumExtraDataSize; @@ -238,6 +237,7 @@ protected virtual ReleaseSpec CreateReleaseSpec(ChainSpec chainSpec, long releas releaseSpec.IsEip1153Enabled = (chainSpec.Parameters.Eip1153TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; releaseSpec.IsEip3651Enabled = (chainSpec.Parameters.Eip3651TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; + releaseSpec.MaximumUncleCount = releaseSpec.IsEip3651Enabled ? 0 : 2; releaseSpec.IsEip3855Enabled = (chainSpec.Parameters.Eip3855TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; releaseSpec.IsEip3860Enabled = (chainSpec.Parameters.Eip3860TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; releaseSpec.IsEip4895Enabled = (chainSpec.Parameters.Eip4895TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; diff --git a/src/Nethermind/Nethermind.Specs/Forks/15_Paris.cs b/src/Nethermind/Nethermind.Specs/Forks/15_Paris.cs index cab10e56efa..12d402e46ae 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/15_Paris.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/15_Paris.cs @@ -13,7 +13,6 @@ public class Paris : GrayGlacier protected Paris() { Name = "Paris"; - MaximumUncleCount = 0; } public new static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, static () => new Paris()); diff --git a/src/Nethermind/Nethermind.Specs/Forks/16_Shanghai.cs b/src/Nethermind/Nethermind.Specs/Forks/16_Shanghai.cs index b5af32c5d07..06f602fbc79 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/16_Shanghai.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/16_Shanghai.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System.Threading; @@ -13,6 +13,7 @@ public class Shanghai : Paris protected Shanghai() { Name = "Shanghai"; + MaximumUncleCount = 0; IsEip3651Enabled = true; IsEip3855Enabled = true; IsEip3860Enabled = true; diff --git a/src/Nethermind/Nethermind.Specs/HoodiSpecProvider.cs b/src/Nethermind/Nethermind.Specs/HoodiSpecProvider.cs index e9e328fe113..81c899c8dbd 100644 --- a/src/Nethermind/Nethermind.Specs/HoodiSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/HoodiSpecProvider.cs @@ -54,7 +54,7 @@ public void UpdateMergeTransitionInfo(long? blockNumber, UInt256? terminalTotalD public ForkActivation? MergeBlockNumber { get; private set; } = (0, GenesisTimestamp); public ulong TimestampFork => ShanghaiTimestamp; public UInt256? TerminalTotalDifficulty { get; private set; } = 0; - public IReleaseSpec GenesisSpec { get; } = London.Instance; + public IReleaseSpec GenesisSpec { get; } = Shanghai.Instance; public ForkActivation[] TransitionActivations { get; } = [ (1, ShanghaiTimestamp),