Skip to content

Commit 48aab94

Browse files
committed
Draft
1 parent 1d85282 commit 48aab94

File tree

11 files changed

+279
-69
lines changed

11 files changed

+279
-69
lines changed

src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs

Lines changed: 120 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
22
// SPDX-License-Identifier: LGPL-3.0-only
33

4-
using System;
5-
using System.Collections.Generic;
6-
using System.Diagnostics;
7-
using System.Linq;
8-
using System.Numerics;
9-
using System.Threading;
10-
using System.Threading.Tasks;
114
using Autofac;
125
using Nethermind.Blockchain;
136
using Nethermind.Blockchain.Find;
@@ -23,19 +16,26 @@
2316
using Nethermind.Core.Specs;
2417
using Nethermind.Core.Test.Modules;
2518
using Nethermind.Crypto;
19+
using Nethermind.Evm.State;
20+
using Nethermind.Init.Modules;
2621
using Nethermind.Int256;
22+
using Nethermind.JsonRpc;
2723
using Nethermind.Logging;
24+
using Nethermind.Merge.Plugin;
25+
using Nethermind.Merge.Plugin.Data;
2826
using Nethermind.Serialization.Rlp;
2927
using Nethermind.Specs;
3028
using Nethermind.Specs.Forks;
3129
using Nethermind.Specs.Test;
32-
using Nethermind.Evm.State;
33-
using Nethermind.Init.Modules;
3430
using NUnit.Framework;
35-
using Nethermind.Merge.Plugin.Data;
36-
using Nethermind.Merge.Plugin;
37-
using Nethermind.JsonRpc;
31+
using System;
32+
using System.Collections.Generic;
33+
using System.Diagnostics;
34+
using System.Linq;
35+
using System.Numerics;
3836
using System.Reflection;
37+
using System.Threading;
38+
using System.Threading.Tasks;
3939

4040
namespace Ethereum.Test.Base;
4141

@@ -50,7 +50,7 @@ static BlockchainTestBase()
5050
{
5151
DifficultyCalculator = new DifficultyCalculatorWrapper();
5252
_logManager ??= LimboLogs.Instance;
53-
_logger = _logManager.GetClassLogger();
53+
_logger = TestLogManager.Instance.GetClassLogger();
5454
}
5555

5656
[SetUp]
@@ -133,7 +133,12 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
133133
.AddSingleton(specProvider)
134134
.AddSingleton(_logManager)
135135
.AddSingleton(rewardCalculator)
136-
.AddSingleton<IDifficultyCalculator>(DifficultyCalculator);
136+
.AddSingleton<IDifficultyCalculator>(DifficultyCalculator)
137+
.AddSingleton<IRewardCalculatorSource, RewardCalculator>()
138+
.AddSingleton<IEthash, Ethash>()
139+
.AddSingleton<IPoSSwitcher, PoSSwitcher>()
140+
.AddSingleton<ISealValidator, EthashSealValidator>()
141+
.AddDecorator<ISealValidator, MergeSealValidator>();
137142

138143
if (isEngineTest)
139144
{
@@ -148,6 +153,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
148153
IBlockTree blockTree = container.Resolve<IBlockTree>();
149154
IBlockValidator blockValidator = container.Resolve<IBlockValidator>();
150155
blockchainProcessor.Start();
156+
IPoSSwitcher poSSwitcher = container.Resolve<IPoSSwitcher>();
151157

152158
// Register tracer if provided for blocktest tracing
153159
if (tracer is not null)
@@ -157,7 +163,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
157163

158164
try
159165
{
160-
BlockHeader parentHeader;
166+
Block parentBlock;
161167
// Genesis processing
162168
using (stateProvider.BeginScope(null))
163169
{
@@ -192,7 +198,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
192198

193199
blockTree.SuggestBlock(genesisBlock);
194200
genesisProcessed.WaitOne(_genesisProcessingTimeoutMs);
195-
parentHeader = genesisBlock.Header;
201+
parentBlock = genesisBlock;
196202

197203
// Dispose genesis block's AccountChanges
198204
genesisBlock.DisposeAccountChanges();
@@ -201,7 +207,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
201207
if (test.Blocks is not null)
202208
{
203209
// blockchain test
204-
parentHeader = SuggestBlocks(test, failOnInvalidRlp, blockValidator, blockTree, parentHeader);
210+
await SuggestBlocks(poSSwitcher, test, failOnInvalidRlp, blockValidator, blockTree, blockchainProcessor, parentBlock, isPostMerge);
205211
}
206212
else if (test.EngineNewPayloads is not null)
207213
{
@@ -258,56 +264,123 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
258264
}
259265
}
260266

261-
private static BlockHeader SuggestBlocks(BlockchainTest test, bool failOnInvalidRlp, IBlockValidator blockValidator, IBlockTree blockTree, BlockHeader parentHeader)
267+
private static async Task<BlockHeader> SuggestBlocks(IPoSSwitcher _poSSwitcher, BlockchainTest test, bool failOnInvalidRlp, IBlockValidator blockValidator, IBlockTree blockTree, BlockchainProcessor blockchainProcessor, Block parentBlock, bool isPostMerge)
262268
{
263269
List<(Block Block, string ExpectedException)> correctRlp = DecodeRlps(test, failOnInvalidRlp);
270+
string? error = null;
271+
string? expectsException = null;
272+
Block head = parentBlock;
273+
264274
for (int i = 0; i < correctRlp.Count; i++)
265275
{
276+
error = null;
266277
// Mimic the actual behaviour where block goes through validating sync manager
267-
correctRlp[i].Block.Header.IsPostMerge = correctRlp[i].Block.Difficulty == 0;
278+
correctRlp[i].Block.Header.IsPostMerge = _poSSwitcher.IsPostMerge(correctRlp[i].Block.Header);
279+
Assert.That(correctRlp[i].Block.Hash, Is.Not.Null, $"null hash in {test.Name} block {i}");
280+
expectsException = correctRlp[i].ExpectedException;
281+
try
282+
{
283+
// For tests with reorgs, find the actual parent header from block tree
284+
if (parentBlock.Hash != correctRlp[i].Block.ParentHash)
285+
{
286+
var oldParentBlock = parentBlock;
287+
parentBlock = blockTree.FindBlock(correctRlp[i].Block.ParentHash);
288+
// repeats new payload handler assertion
289+
if (parentBlock is null)
290+
{
291+
error = $"Parent block {correctRlp[i].Block.ParentHash} not found for block {correctRlp[i].Block.Hash}";
292+
parentBlock = oldParentBlock;
293+
continue;
294+
}
295+
// if (!isPostMerge)
268296

269-
// For tests with reorgs, find the actual parent header from block tree
270-
parentHeader = blockTree.FindHeader(correctRlp[i].Block.ParentHash) ?? parentHeader;
297+
blockTree.UpdateMainChain([parentBlock], true, true);
298+
}
271299

272-
Assert.That(correctRlp[i].Block.Hash, Is.Not.Null, $"null hash in {test.Name} block {i}");
300+
// Validate block structure first (mimics SyncServer validation)
301+
bool validationResult = blockValidator.ValidateSuggestedBlock(correctRlp[i].Block, parentBlock.Header, out string? validationError);
273302

274-
bool expectsException = correctRlp[i].ExpectedException is not null;
275-
// Validate block structure first (mimics SyncServer validation)
276-
if (blockValidator.ValidateSuggestedBlock(correctRlp[i].Block, parentHeader, out string? validationError))
277-
{
278-
Assert.That(!expectsException, $"Expected block {correctRlp[i].Block.Hash} to fail with '{correctRlp[i].ExpectedException}', but it passed validation");
279-
try
303+
if (!validationResult)
304+
{
305+
error = validationError;
306+
continue;
307+
}
308+
309+
//if (blockTree.Head.Number >= correctRlp[i].Block.Number)
310+
//{
311+
// continue;
312+
//}
313+
314+
bool suggested = false;
315+
TaskCompletionSource<BlockRemovedEventArgs> completion = new TaskCompletionSource<BlockRemovedEventArgs>(TaskCreationOptions.RunContinuationsAsynchronously);
316+
317+
void f(object? s, BlockEventArgs e)
280318
{
281-
// All validations passed, suggest the block
282-
blockTree.SuggestBlock(correctRlp[i].Block);
319+
suggested = true;
320+
}
283321

322+
void f2(object? s, BlockRemovedEventArgs e)
323+
{
324+
completion.SetResult(e);
284325
}
285-
catch (InvalidBlockException e)
326+
327+
blockchainProcessor.BlockRemoved += f2;
328+
blockTree.NewBestSuggestedBlock += f;
329+
blockTree.SuggestBlock(correctRlp[i].Block, BlockTreeSuggestOptions.ShouldProcess | BlockTreeSuggestOptions.FillBeaconBlock);
330+
blockTree.NewBestSuggestedBlock -= f;
331+
332+
if (suggested)
286333
{
287-
// Exception thrown during block processing
288-
Assert.That(expectsException, $"Unexpected invalid block {correctRlp[i].Block.Hash}: {validationError}, Exception: {e}");
289-
// else: Expected to fail and did fail via exception → this is correct behavior
334+
await completion.Task;
335+
if (completion.Task.Result.ProcessingResult is not ProcessingResult.Success)
336+
{
337+
error = $"Error processing block {correctRlp[i].Block.Hash}: {completion.Task.Result.Message ?? completion.Task.Result.Exception?.ToString()}";
338+
break;
339+
}
290340
}
291-
catch (Exception e)
341+
342+
blockchainProcessor.BlockRemoved -= f2;
343+
344+
parentBlock = correctRlp[i].Block;
345+
346+
if (!isPostMerge)
292347
{
293-
Assert.Fail($"Unexpected exception during processing: {e}");
348+
if (head.Number < parentBlock.Number || head.TotalDifficulty < parentBlock.TotalDifficulty || (head.TotalDifficulty is not null && head.TotalDifficulty == parentBlock.TotalDifficulty && parentBlock.Timestamp < head.Timestamp))
349+
{
350+
head = parentBlock;
351+
}
294352
}
295-
finally
353+
else
296354
{
297-
// Dispose AccountChanges to prevent memory leaks in tests
298-
correctRlp[i].Block.DisposeAccountChanges();
355+
head = parentBlock;
299356
}
300357
}
301-
else
358+
catch (Exception e)
302359
{
303-
// Validation FAILED
304-
Assert.That(expectsException, $"Unexpected invalid block {correctRlp[i].Block.Hash}: {validationError}");
305-
// else: Expected to fail and did fail → this is correct behavior
360+
Assert.Fail($"Unexpected exception during processing: {e} {e.StackTrace}");
306361
}
362+
finally
363+
{
364+
// Dispose AccountChanges to prevent memory leaks in tests
365+
correctRlp[i].Block.DisposeAccountChanges();
307366

308-
parentHeader = correctRlp[i].Block.Header;
367+
if (error is null)
368+
{
369+
Assert.That(expectsException, Is.Null, $"Unexpected valid block, expected failure: {expectsException}");
370+
}
371+
else
372+
{
373+
Assert.That(expectsException, Is.Not.Null, $"Unexpected invalid block: {error}");
374+
}
375+
}
309376
}
310-
return parentHeader;
377+
blockTree.UpdateMainChain([head], true, true);
378+
return head.Header;
379+
}
380+
381+
private static void BlockTree_NewBestSuggestedBlock(object? sender, BlockEventArgs e)
382+
{
383+
throw new NotImplementedException();
311384
}
312385

313386
private async static Task RunNewPayloads(TestEngineNewPayloadsJson[]? newPayloads, IEngineRpcModule engineRpcModule)
@@ -361,9 +434,9 @@ private async static Task RunNewPayloads(TestEngineNewPayloadsJson[]? newPayload
361434
{
362435
Assert.That(suggestedBlock.Uncles[uncleIndex].Hash, Is.EqualTo(new Hash256(testBlockJson.UncleHeaders![uncleIndex].Hash)));
363436
}
364-
365-
correctRlp.Add((suggestedBlock, testBlockJson.ExpectedException));
366437
}
438+
439+
correctRlp.Add((suggestedBlock, testBlockJson.ExpectedException));
367440
}
368441
catch (Exception e)
369442
{

src/Nethermind/Nethermind.Consensus/Processing/BlockHashEventArgs.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
1+
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
22
// SPDX-License-Identifier: LGPL-3.0-only
33

44
using System;
@@ -22,6 +22,7 @@ public BlockHashEventArgs(Hash256 blockHash, ProcessingResult processingResult,
2222

2323
public enum ProcessingResult
2424
{
25+
None,
2526
/// <summary>
2627
/// Processing was successful
2728
/// </summary>

src/Nethermind/Nethermind.Consensus/Processing/IBlockProcessingQueueExtensions.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Threading;
55
using System.Threading.Tasks;
6+
using Nethermind.Core.Crypto;
67
using Nethermind.Core.Events;
78

89
namespace Nethermind.Consensus.Processing;
@@ -18,4 +19,14 @@ await Wait.ForEvent(cancellationToken,
1819
e => blockProcessingQueue.ProcessingQueueEmpty -= e);
1920
}
2021
}
22+
23+
public static async Task<ProcessingResult> WaitForBlockProcessing(this IBlockProcessingQueue blockProcessingQueue, Hash256 blockHash, CancellationToken cancellationToken = default)
24+
{
25+
var res = await Wait.ForEventCondition<BlockRemovedEventArgs>(cancellationToken,
26+
e => blockProcessingQueue.BlockRemoved += e,
27+
e => blockProcessingQueue.BlockRemoved -= e,
28+
e => e.BlockHash == blockHash).ConfigureAwait(false);
29+
30+
return res.ProcessingResult;
31+
}
2132
}

src/Nethermind/Nethermind.Consensus/Stateless/StatelessBlockProcessingEnv.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,13 @@
1111
using Nethermind.Consensus.Rewards;
1212
using Nethermind.Consensus.Validators;
1313
using Nethermind.Consensus.Withdrawals;
14-
using Nethermind.Core;
15-
using Nethermind.Core.Crypto;
1614
using Nethermind.Core.Specs;
17-
using Nethermind.Db;
1815
using Nethermind.Evm;
1916
using Nethermind.Evm.State;
2017
using Nethermind.Evm.TransactionProcessing;
2118
using Nethermind.Logging;
2219
using Nethermind.State;
2320
using Nethermind.Trie;
24-
using Nethermind.Trie.Pruning;
2521

2622
namespace Nethermind.Consensus.Stateless;
2723

0 commit comments

Comments
 (0)