Skip to content

Commit 3bcb1a1

Browse files
committed
sim-rs: support late IB inclusion
1 parent 0169763 commit 3bcb1a1

File tree

2 files changed

+64
-32
lines changed

2 files changed

+64
-32
lines changed

sim-rs/sim-core/src/config.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ pub struct RawParameters {
6060
// Leios protocol configuration
6161
pub leios_stage_length_slots: u64,
6262
pub leios_stage_active_voting_slots: u64,
63+
pub leios_late_ib_inclusion: bool,
6364
pub leios_header_diffusion_time_ms: f64,
6465
pub praos_chain_quality: u64,
6566

@@ -417,6 +418,7 @@ pub struct SimConfiguration {
417418
pub links: Vec<LinkConfiguration>,
418419
pub stage_length: u64,
419420
pub max_eb_age: u64,
421+
pub late_ib_inclusion: bool,
420422
pub(crate) variant: LeiosVariant,
421423
pub(crate) header_diffusion_time: Duration,
422424
pub(crate) relay_strategy: RelayStrategy,
@@ -442,11 +444,14 @@ impl SimConfiguration {
442444
Self {
443445
seed: 0,
444446
slots: None,
445-
aggregate_events: false,
446447
emit_conformance_events: false,
447-
nodes: topology.nodes,
448+
aggregate_events: false,
448449
trace_nodes: HashSet::new(),
450+
nodes: topology.nodes,
449451
links: topology.links,
452+
stage_length: params.leios_stage_length_slots,
453+
max_eb_age: params.eb_max_age_slots,
454+
late_ib_inclusion: params.leios_late_ib_inclusion,
450455
variant: params.leios_variant,
451456
header_diffusion_time: duration_ms(params.leios_header_diffusion_time_ms),
452457
relay_strategy: params.relay_strategy,
@@ -458,12 +463,10 @@ impl SimConfiguration {
458463
vote_threshold: params.vote_threshold,
459464
vote_slot_length: params.leios_stage_active_voting_slots,
460465
max_block_size: params.rb_body_max_size_bytes,
461-
stage_length: params.leios_stage_length_slots,
462466
max_ib_size: params.ib_body_max_size_bytes,
463467
ib_diffusion_strategy: params.ib_diffusion_strategy,
464468
max_ib_requests_per_peer: params.ib_diffusion_max_bodies_to_request as usize,
465469
ib_shards: params.ib_shards,
466-
max_eb_age: params.eb_max_age_slots,
467470
cpu_times: CpuTimeConfig::new(&params),
468471
sizes: BlockSizeConfig::new(&params),
469472
transactions: TransactionConfig::new(&params),

sim-rs/sim-core/src/sim/node.rs

+57-28
Original file line numberDiff line numberDiff line change
@@ -578,17 +578,16 @@ impl Node {
578578
pipeline,
579579
producer: self.id,
580580
});
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 {
585582
slot,
586583
pipeline,
587584
producer: self.id,
588-
bytes,
589-
ibs,
590-
ebs,
585+
bytes: 0,
586+
ibs: vec![],
587+
ebs: vec![],
591588
};
589+
self.try_filling_eb(&mut eb);
590+
eb.bytes = self.sim_config.sizes.eb(eb.ibs.len(), eb.ebs.len());
592591
self.schedule_cpu_task(CpuTaskType::EBBlockGenerated(eb));
593592
// A node should only generate at most 1 EB per slot
594593
return;
@@ -1319,7 +1318,28 @@ impl Node {
13191318
Ok(())
13201319
}
13211320

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> {
13231343
self.leios
13241344
.ibs_by_pipeline
13251345
.get(&pipeline)
@@ -1389,14 +1409,14 @@ impl Node {
13891409

13901410
fn should_vote_for(&self, eb: &EndorserBlock) -> Result<(), NoVoteReason> {
13911411
let mut ib_set = HashSet::new();
1412+
let mut pipelines_referenced_by_ibs = HashSet::new();
1413+
13921414
for ib in &eb.ibs {
13931415
if !matches!(self.leios.ibs.get(ib), Some(InputBlockState::Received(_))) {
13941416
return Err(NoVoteReason::MissingIB);
13951417
}
1396-
if ib.pipeline != eb.pipeline {
1397-
return Err(NoVoteReason::InvalidSlot);
1398-
}
13991418
ib_set.insert(*ib);
1419+
pipelines_referenced_by_ibs.insert(ib.pipeline);
14001420
}
14011421

14021422
if let Some(ibs) = self.leios.ibs_by_pipeline.get(&eb.pipeline) {
@@ -1409,31 +1429,40 @@ impl Node {
14091429

14101430
// If this EB is meant to reference other EBs, validate that it references whatever it needs
14111431
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> =
14131433
eb.ebs.iter().map(|id| id.pipeline).collect();
1434+
14141435
for expected_pipeline in expected_referenced_pipelines {
1415-
let Some(certified_at) = self
1436+
let saw_certified_eb = self
14161437
.leios
14171438
.earliest_eb_cert_times_by_pipeline
14181439
.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.
14341451
return Err(NoVoteReason::MissingEB);
14351452
}
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+
}
14361459
}
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);
14371466
}
14381467

14391468
// If this EB _does_ reference other EBs, make sure we trust them

0 commit comments

Comments
 (0)