1818using Nethermind . Core . Test . Blockchain ;
1919using Nethermind . Core . Test . Builders ;
2020using Nethermind . Evm . TransactionProcessing ;
21+ using Nethermind . Int256 ;
2122using Nethermind . JsonRpc . Test . Modules ;
2223using Nethermind . Logging ;
2324using Nethermind . Specs ;
2425using Nethermind . Specs . Forks ;
26+ using Nethermind . Specs . Test ;
2527using Nethermind . Evm . State ;
2628using Nethermind . TxPool ;
2729using NSubstitute ;
@@ -208,40 +210,132 @@ public void BranchProcessor_unsubscribes_from_TransactionsExecuted_after_process
208210 }
209211
210212 [ Test , MaxTime ( Timeout . MaxTestTime ) ]
211- [ TestCaseSource ( nameof ( BlockValidationTransactionsExecutor_bal_validation_cases ) ) ]
212- public void BlockValidationTransactionsExecutor_validates_bal_only_when_validation_enabled (
213+ [ TestCaseSource ( nameof ( BlockProcessor_bal_validation_cases ) ) ]
214+ public void BlockProcessor_validates_bal_after_execution_requests_only_when_validation_enabled (
213215 ProcessingOptions processingOptions ,
214216 bool shouldValidateBlockAccessList )
215217 {
216218 TrackingBlockAccessListWorldState stateProvider = new ( TestWorldStateFactory . CreateForTest ( ) ) ;
217- stateProvider . LoadSuggestedBlockAccessList ( new BlockAccessList ( ) , 37_568 ) ;
219+ List < string > events = [ ] ;
220+ stateProvider . OnValidate = ( index , gasRemaining ) => events . Add ( $ "bal-{ index } :{ gasRemaining } ") ;
218221
219222 ITransactionProcessorAdapter transactionProcessor = Substitute . For < ITransactionProcessorAdapter > ( ) ;
220- transactionProcessor . Execute ( Arg . Any < Transaction > ( ) , Arg . Any < ITxTracer > ( ) ) . Returns ( static callInfo =>
223+ transactionProcessor . Execute ( Arg . Any < Transaction > ( ) , Arg . Any < ITxTracer > ( ) ) . Returns ( callInfo =>
221224 {
225+ events . Add ( "tx" ) ;
222226 Transaction transaction = callInfo . Arg < Transaction > ( ) ;
223227 transaction . SpentGas = 63_586 ;
224228 transaction . BlockGasUsed = 37_568 ;
225229 return TransactionResult . Ok ;
226230 } ) ;
227231
228- BlockProcessor . BlockValidationTransactionsExecutor txExecutor = new ( transactionProcessor , stateProvider ) ;
229- Block block = Build . A . Block . WithTransactions ( Build . A . Transaction . SignedAndResolved ( ) . TestObject ) . TestObject ;
230- BlockReceiptsTracer receiptsTracer = new ( ) ;
231- receiptsTracer . StartNewBlockTrace ( block ) ;
232+ IExecutionRequestsProcessor executionRequestsProcessor = Substitute . For < IExecutionRequestsProcessor > ( ) ;
233+ executionRequestsProcessor
234+ . When ( static x => x . ProcessExecutionRequests ( Arg . Any < Block > ( ) , Arg . Any < IWorldState > ( ) , Arg . Any < TxReceipt [ ] > ( ) , Arg . Any < IReleaseSpec > ( ) ) )
235+ . Do ( _ => events . Add ( "requests" ) ) ;
236+
237+ OverridableReleaseSpec spec = new ( London . Instance ) { IsEip7928Enabled = true } ;
238+ BlockProcessor processor = CreateBalTestBlockProcessor ( stateProvider , transactionProcessor , executionRequestsProcessor , spec ) ;
239+ Block block = Build . A . Block
240+ . WithTransactions ( Build . A . Transaction . SignedAndResolved ( ) . TestObject )
241+ . WithGasUsed ( 37_568 )
242+ . WithBlockAccessList ( new BlockAccessList ( ) )
243+ . TestObject ;
232244
233- txExecutor . ProcessTransactions ( block , processingOptions , receiptsTracer , CancellationToken . None ) ;
245+ using IDisposable _ = stateProvider . BeginScope ( null ) ;
246+ processor . ProcessOne ( block , processingOptions , NullBlockTracer . Instance , spec , CancellationToken . None ) ;
234247
235248 if ( shouldValidateBlockAccessList )
236249 {
237- Assert . That ( stateProvider . ValidatedGasRemaining , Is . EqualTo ( new long [ ] { 37_568L , 0L } ) ) ;
250+ Assert . That ( events , Is . EqualTo ( new [ ] { "tx" , "requests" , "bal-0:37568" , "bal-1:0" } ) ) ;
238251 }
239252 else
240253 {
241- Assert . That ( stateProvider . ValidatedGasRemaining , Is . Empty ) ;
254+ Assert . That ( events , Is . EqualTo ( new [ ] { "tx" , "requests" } ) ) ;
242255 }
243256 }
244257
258+ [ Test , MaxTime ( Timeout . MaxTestTime ) ]
259+ public void BlockProcessor_prioritizes_execution_request_error_over_bal_validation ( )
260+ {
261+ TrackingBlockAccessListWorldState stateProvider = new ( TestWorldStateFactory . CreateForTest ( ) ) ;
262+ ITransactionProcessorAdapter transactionProcessor = SuccessfulTransactionProcessor ( ) ;
263+ IExecutionRequestsProcessor executionRequestsProcessor = Substitute . For < IExecutionRequestsProcessor > ( ) ;
264+ executionRequestsProcessor
265+ . When ( static x => x . ProcessExecutionRequests ( Arg . Any < Block > ( ) , Arg . Any < IWorldState > ( ) , Arg . Any < TxReceipt [ ] > ( ) , Arg . Any < IReleaseSpec > ( ) ) )
266+ . Do ( callInfo => throw new InvalidBlockException ( callInfo . Arg < Block > ( ) , "DepositsInvalid: Invalid deposit event layout: test" ) ) ;
267+
268+ OverridableReleaseSpec spec = new ( London . Instance ) { IsEip7928Enabled = true } ;
269+ BlockProcessor processor = CreateBalTestBlockProcessor ( stateProvider , transactionProcessor , executionRequestsProcessor , spec ) ;
270+ Block block = Build . A . Block
271+ . WithTransactions ( Build . A . Transaction . SignedAndResolved ( ) . TestObject )
272+ . WithGasUsed ( 37_568 )
273+ . WithBlockAccessList ( new BlockAccessList ( ) )
274+ . TestObject ;
275+
276+ using IDisposable _ = stateProvider . BeginScope ( null ) ;
277+ InvalidBlockException exception = Assert . Throws < InvalidBlockException > (
278+ ( ) => processor . ProcessOne ( block , ProcessingOptions . None , NullBlockTracer . Instance , spec , CancellationToken . None ) ) ! ;
279+
280+ Assert . That ( exception . Message , Does . StartWith ( "DepositsInvalid: Invalid deposit event layout" ) ) ;
281+ Assert . That ( stateProvider . ValidatedGasRemaining , Is . Empty ) ;
282+ }
283+
284+ [ Test , MaxTime ( Timeout . MaxTestTime ) ]
285+ public void BlockProcessor_prioritizes_transaction_error_over_bal_item_gas_limit ( )
286+ {
287+ TrackingBlockAccessListWorldState stateProvider = new ( TestWorldStateFactory . CreateForTest ( ) ) ;
288+ ITransactionProcessorAdapter transactionProcessor = Substitute . For < ITransactionProcessorAdapter > ( ) ;
289+ transactionProcessor . Execute ( Arg . Any < Transaction > ( ) , Arg . Any < ITxTracer > ( ) ) . Returns ( TransactionResult . BlockGasLimitExceeded ) ;
290+ IExecutionRequestsProcessor executionRequestsProcessor = Substitute . For < IExecutionRequestsProcessor > ( ) ;
291+
292+ OverridableReleaseSpec spec = new ( London . Instance ) { IsEip7928Enabled = true } ;
293+ BlockProcessor processor = CreateBalTestBlockProcessor ( stateProvider , transactionProcessor , executionRequestsProcessor , spec ) ;
294+ Block block = Build . A . Block
295+ . WithTransactions ( Build . A . Transaction . SignedAndResolved ( ) . TestObject )
296+ . WithGasLimit ( 21_000 )
297+ . WithGasUsed ( 21_000 )
298+ . WithBlockAccessList ( BlockAccessListWithAccountReads ( 15 ) )
299+ . TestObject ;
300+
301+ using IDisposable _ = stateProvider . BeginScope ( null ) ;
302+ Nethermind . Blockchain . InvalidTransactionException exception = Assert . Throws < Nethermind . Blockchain . InvalidTransactionException > (
303+ ( ) => processor . ProcessOne ( block , ProcessingOptions . None , NullBlockTracer . Instance , spec , CancellationToken . None ) ) ! ;
304+
305+ Assert . That ( exception . Message , Does . Contain ( "Block gas limit exceeded" ) ) ;
306+ Assert . That ( stateProvider . ValidatedGasRemaining , Is . Empty ) ;
307+ executionRequestsProcessor
308+ . DidNotReceive ( )
309+ . ProcessExecutionRequests ( Arg . Any < Block > ( ) , Arg . Any < IWorldState > ( ) , Arg . Any < TxReceipt [ ] > ( ) , Arg . Any < IReleaseSpec > ( ) ) ;
310+ }
311+
312+ [ Test , MaxTime ( Timeout . MaxTestTime ) ]
313+ public void BlockProcessor_validates_bal_item_gas_limit_after_transactions ( )
314+ {
315+ TrackingBlockAccessListWorldState stateProvider = new ( TestWorldStateFactory . CreateForTest ( ) ) ;
316+ ITransactionProcessorAdapter transactionProcessor = SuccessfulTransactionProcessor ( blockGasUsed : 21_000 ) ;
317+ IExecutionRequestsProcessor executionRequestsProcessor = Substitute . For < IExecutionRequestsProcessor > ( ) ;
318+
319+ OverridableReleaseSpec spec = new ( London . Instance ) { IsEip7928Enabled = true } ;
320+ BlockProcessor processor = CreateBalTestBlockProcessor ( stateProvider , transactionProcessor , executionRequestsProcessor , spec ) ;
321+ Block block = Build . A . Block
322+ . WithTransactions ( Build . A . Transaction . SignedAndResolved ( ) . TestObject )
323+ . WithGasLimit ( 21_000 )
324+ . WithGasUsed ( 21_000 )
325+ . WithBlockAccessList ( BlockAccessListWithAccountReads ( 15 ) )
326+ . TestObject ;
327+
328+ using IDisposable _ = stateProvider . BeginScope ( null ) ;
329+ InvalidBlockException exception = Assert . Throws < InvalidBlockException > (
330+ ( ) => processor . ProcessOne ( block , ProcessingOptions . None , NullBlockTracer . Instance , spec , CancellationToken . None ) ) ! ;
331+
332+ Assert . That ( exception . Message , Does . StartWith ( "BlockAccessListGasLimitExceeded" ) ) ;
333+ Assert . That ( stateProvider . ValidatedGasRemaining , Is . Empty ) ;
334+ executionRequestsProcessor
335+ . Received ( 1 )
336+ . ProcessExecutionRequests ( Arg . Any < Block > ( ) , Arg . Any < IWorldState > ( ) , Arg . Any < TxReceipt [ ] > ( ) , Arg . Any < IReleaseSpec > ( ) ) ;
337+ }
338+
245339 [ TestCase ( 2_000 , false ) ]
246340 [ TestCase ( 1_999 , true ) ]
247341 [ MaxTime ( Timeout . MaxTestTime ) ]
@@ -467,6 +561,7 @@ private sealed class TrackingBlockAccessListWorldState(IWorldState innerWorldSta
467561 public bool TracingEnabled { get ; set ; }
468562 public BlockAccessList GeneratedBlockAccessList { get ; set ; } = new ( ) ;
469563 public List < long > ValidatedGasRemaining { get ; } = [ ] ;
564+ public Action < ushort , long > ? OnValidate { get ; set ; }
470565
471566 private long _gasUsed ;
472567
@@ -480,7 +575,10 @@ public long GasUsed()
480575 => _gasUsed ;
481576
482577 public void ValidateBlockAccessList ( BlockHeader block , ushort index , long gasRemaining )
483- => ValidatedGasRemaining . Add ( gasRemaining ) ;
578+ {
579+ OnValidate ? . Invoke ( index , gasRemaining ) ;
580+ ValidatedGasRemaining . Add ( gasRemaining ) ;
581+ }
484582
485583 public void SetBlockAccessList ( Block block , IReleaseSpec spec ) { }
486584 public IDisposable BeginSystemAccountReadSuppression ( ) => EmptyDisposable . Instance ;
@@ -492,11 +590,57 @@ public void Dispose() { }
492590 }
493591 }
494592
495- public static IEnumerable < TestCaseData > BlockValidationTransactionsExecutor_bal_validation_cases ( )
593+ private static BlockProcessor CreateBalTestBlockProcessor (
594+ IWorldState stateProvider ,
595+ ITransactionProcessorAdapter transactionProcessor ,
596+ IExecutionRequestsProcessor executionRequestsProcessor ,
597+ IReleaseSpec spec )
598+ {
599+ ITransactionProcessor systemTransactionProcessor = Substitute . For < ITransactionProcessor > ( ) ;
600+ return new (
601+ new TestSingleReleaseSpecProvider ( spec ) ,
602+ TestBlockValidator . AlwaysValid ,
603+ NoBlockRewards . Instance ,
604+ new BlockProcessor . BlockValidationTransactionsExecutor ( transactionProcessor , stateProvider ) ,
605+ stateProvider ,
606+ NullReceiptStorage . Instance ,
607+ new BeaconBlockRootHandler ( systemTransactionProcessor , stateProvider ) ,
608+ Substitute . For < IBlockhashStore > ( ) ,
609+ LimboLogs . Instance ,
610+ new WithdrawalProcessor ( stateProvider , LimboLogs . Instance ) ,
611+ executionRequestsProcessor ) ;
612+ }
613+
614+ private static ITransactionProcessorAdapter SuccessfulTransactionProcessor ( long blockGasUsed = 37_568 )
615+ {
616+ ITransactionProcessorAdapter transactionProcessor = Substitute . For < ITransactionProcessorAdapter > ( ) ;
617+ transactionProcessor . Execute ( Arg . Any < Transaction > ( ) , Arg . Any < ITxTracer > ( ) ) . Returns ( callInfo =>
618+ {
619+ Transaction transaction = callInfo . Arg < Transaction > ( ) ;
620+ transaction . SpentGas = blockGasUsed ;
621+ transaction . BlockGasUsed = blockGasUsed ;
622+ return TransactionResult . Ok ;
623+ } ) ;
624+
625+ return transactionProcessor ;
626+ }
627+
628+ private static BlockAccessList BlockAccessListWithAccountReads ( int count )
629+ {
630+ BlockAccessList blockAccessList = new ( ) ;
631+ for ( int i = 0 ; i < count ; i ++ )
632+ {
633+ blockAccessList . AddAccountRead ( Address . FromNumber ( ( UInt256 ) ( ulong ) ( i + 1 ) ) ) ;
634+ }
635+
636+ return blockAccessList ;
637+ }
638+
639+ public static IEnumerable < TestCaseData > BlockProcessor_bal_validation_cases ( )
496640 {
497641 yield return new TestCaseData ( ProcessingOptions . None , true )
498- . SetName ( "BlockValidationTransactionsExecutor_uses_block_gas_for_bal_validation_budget " ) ;
642+ . SetName ( "BlockProcessor_uses_block_gas_for_bal_validation_budget " ) ;
499643 yield return new TestCaseData ( ProcessingOptions . NoValidation , false )
500- . SetName ( "BlockValidationTransactionsExecutor_skips_bal_validation_when_no_validation_requested " ) ;
644+ . SetName ( "BlockProcessor_skips_bal_validation_when_no_validation_requested " ) ;
501645 }
502646}
0 commit comments