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 ]
@@ -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 {
0 commit comments