Skip to content

Commit 9d09e74

Browse files
committed
Draft
1 parent 1d85282 commit 9d09e74

File tree

10 files changed

+269
-65
lines changed

10 files changed

+269
-65
lines changed

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

Lines changed: 113 additions & 46 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]
@@ -157,7 +157,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
157157

158158
try
159159
{
160-
BlockHeader parentHeader;
160+
Block parentBlock;
161161
// Genesis processing
162162
using (stateProvider.BeginScope(null))
163163
{
@@ -192,7 +192,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
192192

193193
blockTree.SuggestBlock(genesisBlock);
194194
genesisProcessed.WaitOne(_genesisProcessingTimeoutMs);
195-
parentHeader = genesisBlock.Header;
195+
parentBlock = genesisBlock;
196196

197197
// Dispose genesis block's AccountChanges
198198
genesisBlock.DisposeAccountChanges();
@@ -201,7 +201,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
201201
if (test.Blocks is not null)
202202
{
203203
// blockchain test
204-
parentHeader = SuggestBlocks(test, failOnInvalidRlp, blockValidator, blockTree, parentHeader);
204+
await SuggestBlocks(test, failOnInvalidRlp, blockValidator, blockTree, blockchainProcessor, parentBlock, isPostMerge);
205205
}
206206
else if (test.EngineNewPayloads is not null)
207207
{
@@ -258,56 +258,123 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
258258
}
259259
}
260260

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

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

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

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
297+
if (!validationResult)
298+
{
299+
error = validationError;
300+
continue;
301+
}
302+
303+
//if (blockTree.Head.Number >= correctRlp[i].Block.Number)
304+
//{
305+
// continue;
306+
//}
307+
308+
bool suggested = false;
309+
TaskCompletionSource<BlockRemovedEventArgs> completion = new TaskCompletionSource<BlockRemovedEventArgs>(TaskCreationOptions.RunContinuationsAsynchronously);
310+
311+
void f(object? s, BlockEventArgs e)
280312
{
281-
// All validations passed, suggest the block
282-
blockTree.SuggestBlock(correctRlp[i].Block);
313+
suggested = true;
314+
}
283315

316+
void f2(object? s, BlockRemovedEventArgs e)
317+
{
318+
completion.SetResult(e);
284319
}
285-
catch (InvalidBlockException e)
320+
321+
blockchainProcessor.BlockRemoved += f2;
322+
blockTree.NewBestSuggestedBlock += f;
323+
blockTree.SuggestBlock(correctRlp[i].Block, BlockTreeSuggestOptions.ShouldProcess | BlockTreeSuggestOptions.FillBeaconBlock);
324+
blockTree.NewBestSuggestedBlock -= f;
325+
326+
if (suggested)
286327
{
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
328+
await completion.Task;
329+
if (completion.Task.Result.ProcessingResult is not ProcessingResult.Success)
330+
{
331+
error = $"Error processing block {correctRlp[i].Block.Hash}: {completion.Task.Result.Message ?? completion.Task.Result.Exception?.ToString()}";
332+
break;
333+
}
290334
}
291-
catch (Exception e)
335+
336+
blockchainProcessor.BlockRemoved -= f2;
337+
338+
parentBlock = correctRlp[i].Block;
339+
340+
if (!isPostMerge)
292341
{
293-
Assert.Fail($"Unexpected exception during processing: {e}");
342+
if (head.Number < parentBlock.Number || head.TotalDifficulty < parentBlock.TotalDifficulty || (head.TotalDifficulty is not null && head.TotalDifficulty == parentBlock.TotalDifficulty && parentBlock.Timestamp < head.Timestamp))
343+
{
344+
head = parentBlock;
345+
}
294346
}
295-
finally
347+
else
296348
{
297-
// Dispose AccountChanges to prevent memory leaks in tests
298-
correctRlp[i].Block.DisposeAccountChanges();
349+
head = parentBlock;
299350
}
300351
}
301-
else
352+
catch (Exception e)
302353
{
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
354+
Assert.Fail($"Unexpected exception during processing: {e} {e.StackTrace}");
306355
}
356+
finally
357+
{
358+
// Dispose AccountChanges to prevent memory leaks in tests
359+
correctRlp[i].Block.DisposeAccountChanges();
307360

308-
parentHeader = correctRlp[i].Block.Header;
361+
if (error is null)
362+
{
363+
Assert.That(expectsException, Is.Null, $"Unexpected valid block, expected failure: {expectsException}");
364+
}
365+
else
366+
{
367+
Assert.That(expectsException, Is.Not.Null, $"Unexpected invalid block: {error}");
368+
}
369+
}
309370
}
310-
return parentHeader;
371+
blockTree.UpdateMainChain([head], true, true);
372+
return head.Header;
373+
}
374+
375+
private static void BlockTree_NewBestSuggestedBlock(object? sender, BlockEventArgs e)
376+
{
377+
throw new NotImplementedException();
311378
}
312379

313380
private async static Task RunNewPayloads(TestEngineNewPayloadsJson[]? newPayloads, IEngineRpcModule engineRpcModule)
@@ -361,9 +428,9 @@ private async static Task RunNewPayloads(TestEngineNewPayloadsJson[]? newPayload
361428
{
362429
Assert.That(suggestedBlock.Uncles[uncleIndex].Hash, Is.EqualTo(new Hash256(testBlockJson.UncleHeaders![uncleIndex].Hash)));
363430
}
364-
365-
correctRlp.Add((suggestedBlock, testBlockJson.ExpectedException));
366431
}
432+
433+
correctRlp.Add((suggestedBlock, testBlockJson.ExpectedException));
367434
}
368435
catch (Exception e)
369436
{

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)