@@ -578,17 +578,16 @@ impl Node {
578
578
pipeline,
579
579
producer : self . id ,
580
580
} ) ;
581
- let ibs = self . select_ibs_for_eb ( pipeline) ;
582
- let ebs = self . select_ebs_for_eb ( pipeline) ;
583
- let bytes = self . sim_config . sizes . eb ( ibs. len ( ) , ebs. len ( ) ) ;
584
- let eb = EndorserBlock {
581
+ let mut eb = EndorserBlock {
585
582
slot,
586
583
pipeline,
587
584
producer : self . id ,
588
- bytes,
589
- ibs,
590
- ebs,
585
+ bytes : 0 ,
586
+ ibs : vec ! [ ] ,
587
+ ebs : vec ! [ ] ,
591
588
} ;
589
+ self . try_filling_eb ( & mut eb) ;
590
+ eb. bytes = self . sim_config . sizes . eb ( eb. ibs . len ( ) , eb. ebs . len ( ) ) ;
592
591
self . schedule_cpu_task ( CpuTaskType :: EBBlockGenerated ( eb) ) ;
593
592
// A node should only generate at most 1 EB per slot
594
593
return ;
@@ -1319,7 +1318,28 @@ impl Node {
1319
1318
Ok ( ( ) )
1320
1319
}
1321
1320
1322
- fn select_ibs_for_eb ( & mut self , pipeline : u64 ) -> Vec < InputBlockId > {
1321
+ fn try_filling_eb ( & mut self , eb : & mut EndorserBlock ) {
1322
+ eb. ibs = self . select_ibs_from_pipeline ( eb. pipeline ) ;
1323
+ eb. ebs = self . select_ebs_for_eb ( eb. pipeline ) ;
1324
+ if !self . sim_config . late_ib_inclusion {
1325
+ return ;
1326
+ }
1327
+ let Some ( expected_referenced_pipelines) = self . pipelines_referenced_by_ebs ( eb. pipeline )
1328
+ else {
1329
+ return ;
1330
+ } ;
1331
+
1332
+ let pipelines_referenced_by_ebs: HashSet < u64 > =
1333
+ eb. ebs . iter ( ) . map ( |eb| eb. pipeline ) . collect ( ) ;
1334
+ for pipeline in expected_referenced_pipelines {
1335
+ if !pipelines_referenced_by_ebs. contains ( & pipeline) {
1336
+ let mut pipeline_ibs = self . select_ibs_from_pipeline ( pipeline) ;
1337
+ eb. ibs . append ( & mut pipeline_ibs) ;
1338
+ }
1339
+ }
1340
+ }
1341
+
1342
+ fn select_ibs_from_pipeline ( & self , pipeline : u64 ) -> Vec < InputBlockId > {
1323
1343
self . leios
1324
1344
. ibs_by_pipeline
1325
1345
. get ( & pipeline)
@@ -1389,14 +1409,14 @@ impl Node {
1389
1409
1390
1410
fn should_vote_for ( & self , eb : & EndorserBlock ) -> Result < ( ) , NoVoteReason > {
1391
1411
let mut ib_set = HashSet :: new ( ) ;
1412
+ let mut pipelines_referenced_by_ibs = HashSet :: new ( ) ;
1413
+
1392
1414
for ib in & eb. ibs {
1393
1415
if !matches ! ( self . leios. ibs. get( ib) , Some ( InputBlockState :: Received ( _) ) ) {
1394
1416
return Err ( NoVoteReason :: MissingIB ) ;
1395
1417
}
1396
- if ib. pipeline != eb. pipeline {
1397
- return Err ( NoVoteReason :: InvalidSlot ) ;
1398
- }
1399
1418
ib_set. insert ( * ib) ;
1419
+ pipelines_referenced_by_ibs. insert ( ib. pipeline ) ;
1400
1420
}
1401
1421
1402
1422
if let Some ( ibs) = self . leios . ibs_by_pipeline . get ( & eb. pipeline ) {
@@ -1409,31 +1429,40 @@ impl Node {
1409
1429
1410
1430
// If this EB is meant to reference other EBs, validate that it references whatever it needs
1411
1431
if let Some ( expected_referenced_pipelines) = self . pipelines_referenced_by_ebs ( eb. pipeline ) {
1412
- let actual_referenced_pipelines : HashSet < u64 > =
1432
+ let pipelines_referenced_by_ebs : HashSet < u64 > =
1413
1433
eb. ebs . iter ( ) . map ( |id| id. pipeline ) . collect ( ) ;
1434
+
1414
1435
for expected_pipeline in expected_referenced_pipelines {
1415
- let Some ( certified_at ) = self
1436
+ let saw_certified_eb = self
1416
1437
. leios
1417
1438
. earliest_eb_cert_times_by_pipeline
1418
1439
. get ( & expected_pipeline)
1419
- else {
1420
- // We don't require an EB referenced from this pipeline if none of that pipeline's EBs have been certified yet,
1421
- continue ;
1422
- } ;
1423
-
1424
- let last_pipeline_slot = ( expected_pipeline + 1 ) * self . sim_config . stage_length - 1 ;
1425
- let cutoff = Timestamp :: from_secs ( last_pipeline_slot)
1426
- . checked_sub_duration ( self . sim_config . header_diffusion_time )
1427
- . unwrap_or_default ( ) ;
1428
- if certified_at > & cutoff {
1429
- // Don't need an EB from this pipeline if it was certified so recently that it may not have propagated.
1430
- continue ;
1431
- }
1432
-
1433
- if !actual_referenced_pipelines. contains ( & expected_pipeline) {
1440
+ . is_some_and ( |certified_at| {
1441
+ let last_pipeline_slot =
1442
+ ( expected_pipeline + 1 ) * self . sim_config . stage_length - 1 ;
1443
+ let cutoff = Timestamp :: from_secs ( last_pipeline_slot)
1444
+ . checked_sub_duration ( self . sim_config . header_diffusion_time )
1445
+ . unwrap_or_default ( ) ;
1446
+ certified_at <= & cutoff
1447
+ } ) ;
1448
+
1449
+ if saw_certified_eb && !pipelines_referenced_by_ebs. contains ( & expected_pipeline) {
1450
+ // We saw at least one certified EB for this pipeline, so we should have included one.
1434
1451
return Err ( NoVoteReason :: MissingEB ) ;
1435
1452
}
1453
+ if pipelines_referenced_by_ebs. contains ( & expected_pipeline)
1454
+ && pipelines_referenced_by_ibs. contains ( & expected_pipeline)
1455
+ {
1456
+ // No pipeline should be referenced by both an EB and any IBs
1457
+ return Err ( NoVoteReason :: ExtraIB ) ;
1458
+ }
1436
1459
}
1460
+ } else if pipelines_referenced_by_ibs
1461
+ . iter ( )
1462
+ . any ( |pipeline| * pipeline != eb. pipeline )
1463
+ {
1464
+ // This EB should only reference IBs from its own pipeline
1465
+ return Err ( NoVoteReason :: InvalidSlot ) ;
1437
1466
}
1438
1467
1439
1468
// If this EB _does_ reference other EBs, make sure we trust them
0 commit comments