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 ;
114using Autofac ;
125using Nethermind . Blockchain ;
136using Nethermind . Blockchain . Find ;
2316using Nethermind . Core . Specs ;
2417using Nethermind . Core . Test . Modules ;
2518using Nethermind . Crypto ;
19+ using Nethermind . Evm . State ;
20+ using Nethermind . Init . Modules ;
2621using Nethermind . Int256 ;
22+ using Nethermind . JsonRpc ;
2723using Nethermind . Logging ;
24+ using Nethermind . Merge . Plugin ;
25+ using Nethermind . Merge . Plugin . Data ;
2826using Nethermind . Serialization . Rlp ;
2927using Nethermind . Specs ;
3028using Nethermind . Specs . Forks ;
3129using Nethermind . Specs . Test ;
32- using Nethermind . Evm . State ;
33- using Nethermind . Init . Modules ;
3430using 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 ;
3836using System . Reflection ;
37+ using System . Threading ;
38+ using System . Threading . Tasks ;
3939
4040namespace 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 {
0 commit comments