Skip to content

Commit 9073fe8

Browse files
committed
Draft
1 parent 1d85282 commit 9073fe8

File tree

11 files changed

+277
-69
lines changed

11 files changed

+277
-69
lines changed

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

Lines changed: 118 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,11 @@ 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<ISealValidator, EthashSealValidator>()
140+
.AddDecorator<ISealValidator, MergeSealValidator>();
137141

138142
if (isEngineTest)
139143
{
@@ -157,7 +161,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
157161

158162
try
159163
{
160-
BlockHeader parentHeader;
164+
Block parentBlock;
161165
// Genesis processing
162166
using (stateProvider.BeginScope(null))
163167
{
@@ -192,7 +196,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
192196

193197
blockTree.SuggestBlock(genesisBlock);
194198
genesisProcessed.WaitOne(_genesisProcessingTimeoutMs);
195-
parentHeader = genesisBlock.Header;
199+
parentBlock = genesisBlock;
196200

197201
// Dispose genesis block's AccountChanges
198202
genesisBlock.DisposeAccountChanges();
@@ -201,7 +205,7 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
201205
if (test.Blocks is not null)
202206
{
203207
// blockchain test
204-
parentHeader = SuggestBlocks(test, failOnInvalidRlp, blockValidator, blockTree, parentHeader);
208+
await SuggestBlocks(test, failOnInvalidRlp, blockValidator, blockTree, blockchainProcessor, parentBlock, isPostMerge);
205209
}
206210
else if (test.EngineNewPayloads is not null)
207211
{
@@ -258,56 +262,123 @@ protected async Task<EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?
258262
}
259263
}
260264

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

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

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

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

320+
void f2(object? s, BlockRemovedEventArgs e)
321+
{
322+
completion.SetResult(e);
284323
}
285-
catch (InvalidBlockException e)
324+
325+
blockchainProcessor.BlockRemoved += f2;
326+
blockTree.NewBestSuggestedBlock += f;
327+
blockTree.SuggestBlock(correctRlp[i].Block, BlockTreeSuggestOptions.ShouldProcess | BlockTreeSuggestOptions.FillBeaconBlock);
328+
blockTree.NewBestSuggestedBlock -= f;
329+
330+
if (suggested)
286331
{
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
332+
await completion.Task;
333+
if (completion.Task.Result.ProcessingResult is not ProcessingResult.Success)
334+
{
335+
error = $"Error processing block {correctRlp[i].Block.Hash}: {completion.Task.Result.Message ?? completion.Task.Result.Exception?.ToString()}";
336+
break;
337+
}
290338
}
291-
catch (Exception e)
339+
340+
blockchainProcessor.BlockRemoved -= f2;
341+
342+
parentBlock = correctRlp[i].Block;
343+
344+
if (!isPostMerge)
292345
{
293-
Assert.Fail($"Unexpected exception during processing: {e}");
346+
if (head.Number < parentBlock.Number || head.TotalDifficulty < parentBlock.TotalDifficulty || (head.TotalDifficulty is not null && head.TotalDifficulty == parentBlock.TotalDifficulty && parentBlock.Timestamp < head.Timestamp))
347+
{
348+
head = parentBlock;
349+
}
294350
}
295-
finally
351+
else
296352
{
297-
// Dispose AccountChanges to prevent memory leaks in tests
298-
correctRlp[i].Block.DisposeAccountChanges();
353+
head = parentBlock;
299354
}
300355
}
301-
else
356+
catch (Exception e)
302357
{
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
358+
Assert.Fail($"Unexpected exception during processing: {e} {e.StackTrace}");
306359
}
360+
finally
361+
{
362+
// Dispose AccountChanges to prevent memory leaks in tests
363+
correctRlp[i].Block.DisposeAccountChanges();
307364

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

313384
private async static Task RunNewPayloads(TestEngineNewPayloadsJson[]? newPayloads, IEngineRpcModule engineRpcModule)
@@ -361,9 +432,9 @@ private async static Task RunNewPayloads(TestEngineNewPayloadsJson[]? newPayload
361432
{
362433
Assert.That(suggestedBlock.Uncles[uncleIndex].Hash, Is.EqualTo(new Hash256(testBlockJson.UncleHeaders![uncleIndex].Hash)));
363434
}
364-
365-
correctRlp.Add((suggestedBlock, testBlockJson.ExpectedException));
366435
}
436+
437+
correctRlp.Add((suggestedBlock, testBlockJson.ExpectedException));
367438
}
368439
catch (Exception e)
369440
{

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)