Skip to content

Commit 496be2f

Browse files
authored
Linear Leios (#455)
* sim-rs: extract generic sim infrastructure * sim-rs: make message types protocol-specific * sim-rs: scaffold linear leios * sim-rs: add TXs to linear leios * sim-rs: add RBs to linear leios * sim-rs: Add TXs to RBs in linear leios * sim-rs: propagate RB headers and bodies separately * sim-rs: generate and propagate EBs * sim-rs: generate and propagate votes * sim-rs: add certs on-chain * sim-rs: prune EB TXs from mempool * sim-rs: handle conflicts * sim-rs: support random tx sampling * sim-rs: only include EBs if enough time has passed * sim-rs: only filter EBs from mempool when enough time has passed * sim-rs: only consider blocks on-chain when we have bodies * sim-rs: add linear leios README * sim-rs: EB producers may vote for their EBs * sim-rs: Fix EB propagation * Update README
1 parent 018ce30 commit 496be2f

File tree

15 files changed

+2018
-531
lines changed

15 files changed

+2018
-531
lines changed

data/simulation/config.d.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ export interface Config {
7676
* If false, RBs will only contain a cert.
7777
*/
7878
"praos-fallback-enabled": boolean;
79+
80+
// Linear Leios specific configuration
81+
/**
82+
* How long the EB voting stage is allowed to last.
83+
* Should be more than 3x leios-header-diffusion-time-ms.
84+
* Matches L_vote from the paper.
85+
*/
86+
"linear-vote-stage-length-slots": bigint;
87+
88+
/**
89+
* How long after the EB voting stage are votes allowed to diffuse.
90+
* Matches L_diff from the paper.
91+
*/
92+
"linear-diffuse-stage-length-slots": bigint;
93+
7994
// Transaction Configuration
8095
/** Only supported by Rust simulation. */
8196
"tx-generation-distribution": Distribution;
@@ -155,6 +170,7 @@ export interface Config {
155170
"eb-validation-cpu-time-ms": number;
156171
"eb-size-bytes-constant": bigint;
157172
"eb-size-bytes-per-ib": bigint;
173+
"eb-body-avg-size-bytes": bigint;
158174
/** Only supported by Haskell simulation. */
159175
"eb-diffusion-strategy": DiffusionStrategy;
160176
/** Only supported by Haskell simulation. */
@@ -198,6 +214,7 @@ export interface Config {
198214
"vote-generation-probability": number;
199215
"vote-generation-cpu-time-ms-constant": number;
200216
"vote-generation-cpu-time-ms-per-ib": number;
217+
"vote-generation-cpu-time-ms-per-tx": number;
201218
"vote-validation-cpu-time-ms": number;
202219
"vote-threshold": bigint;
203220
"vote-bundle-size-bytes-constant": bigint;
@@ -277,7 +294,9 @@ export enum LeiosVariant {
277294
/** Full Leios Without IBs: EBs reference TXs directly, as well as other EBs */
278295
FullWithoutIbs = "full-without-ibs",
279296
/** Full Leios With TX References: IBs only contain references to TXs instead of the whole body */
280-
FullWithTXReferences = "full-with-tx-references"
297+
FullWithTXReferences = "full-with-tx-references",
298+
/** Linear Leios: Leios with as little concurrency as possible. */
299+
Linear = "linear",
281300
}
282301

283302
export enum MempoolSamplingStrategy {

data/simulation/config.default.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ leios-mempool-aggressive-pruning: false
3636
praos-chain-quality: 40
3737
praos-fallback-enabled: true
3838

39+
linear-vote-stage-length-slots: 5
40+
linear-diffuse-stage-length-slots: 5
41+
3942
################################################################################
4043
# Transaction Configuration
4144
################################################################################
@@ -132,6 +135,7 @@ eb-generation-probability: 1.5
132135
eb-size-bytes-constant: 240
133136
# IB hash
134137
eb-size-bytes-per-ib: 32
138+
eb-body-avg-size-bytes: 0
135139
# Collecting the IBs to reference and cryptography are the main tasks.
136140
# A comparable task is maybe mempool snapshotting.
137141
# Sec 2.3 Forging, of the benchmark cluster report, lists
@@ -184,6 +188,8 @@ vote-threshold: 300
184188
vote-generation-cpu-time-ms-constant: 164e-3
185189
# No benchmark yet.
186190
vote-generation-cpu-time-ms-per-ib: 0
191+
# No benchmark yet.
192+
vote-generation-cpu-time-ms-per-tx: 0
187193
# vote-spec#"Verify vote" 0.8*670e-3 + 0.2*1.4
188194
vote-validation-cpu-time-ms: 816e-3
189195
# The `Vote` structure counted in the -per-eb already identifies slot

data/simulation/config.schema.json

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,13 @@
7373
"type": "object"
7474
},
7575
"LeiosVariant": {
76-
"enum": ["short", "full", "full-without-ibs", "full-with-tx-references"],
76+
"enum": [
77+
"short",
78+
"full",
79+
"full-without-ibs",
80+
"full-with-tx-references",
81+
"linear"
82+
],
7783
"type": "string"
7884
},
7985
"LogNormalDistribution": {
@@ -143,6 +149,11 @@
143149
"$ref": "#/definitions/CleanupPolicies",
144150
"description": "Only supported by Haskell simulation."
145151
},
152+
"eb-body-avg-size-bytes": {
153+
"additionalProperties": false,
154+
"properties": {},
155+
"type": "number"
156+
},
146157
"eb-diffusion-max-bodies-to-request": {
147158
"additionalProperties": false,
148159
"description": "Only supported by Haskell simulation.",
@@ -302,6 +313,18 @@
302313
"description": "Determines whether a Leios pipeline has separate Vote (Send) and Vote (Recv) stages.\nIf this is set to `true`, it is recommended to set `leios-stage-active-voting-slots`\nto be equal to `leios-stage-length-slots`.\n\nOnly supported by Haskell simulation.",
303314
"type": "boolean"
304315
},
316+
"linear-diffuse-stage-length-slots": {
317+
"additionalProperties": false,
318+
"description": "How long after the EB voting stage are votes allowed to diffuse.\nMatches L_diff from the paper.",
319+
"properties": {},
320+
"type": "number"
321+
},
322+
"linear-vote-stage-length-slots": {
323+
"additionalProperties": false,
324+
"description": "How long the EB voting stage is allowed to last.\nShould be more than 3x leios-header-diffusion-time-ms.\nMatches L_vote from the paper.",
325+
"properties": {},
326+
"type": "number"
327+
},
305328
"multiplex-mini-protocols": {
306329
"description": "Only supported by Haskell simulation.",
307330
"type": "boolean"
@@ -435,6 +458,9 @@
435458
"vote-generation-cpu-time-ms-per-ib": {
436459
"type": "number"
437460
},
461+
"vote-generation-cpu-time-ms-per-tx": {
462+
"type": "number"
463+
},
438464
"vote-generation-probability": {
439465
"type": "number"
440466
},
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
leios-variant: linear

sim-rs/LINEAR_LEIOS.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Linear Leios (rust simulation)
2+
3+
To run it, set `leios-variant` to `linear`.
4+
5+
The log file schema is currently identical to every other variant (though `pipeline` is always 0).
6+
7+
## Description
8+
9+
Whenever a node creates an RB, it also creates an EB. The RB header contains a reference to this new EB. If the RB producer has a certificate for the parent RB’s EB, it will include that certificate in the RB body.
10+
11+
RB headers are diffused separately from bodies. When a node receives an RB header, it checks whether that RB should be the new head of its chain. If so, it will request the RB body and the referenced EB (from the first peer which announces them).
12+
13+
When a node receives an RB body, it immediately removes all referenced/conflicting transactions from its mempool. If the RB has an EB certificate, it also removes that EB’s transactions from its mempool (note that this should be redundant based on the procedure below).
14+
15+
When a node receives an EB body, it immediately runs a VRF lottery to decide how many times it can vote for that EB; if it has any votes, it will transmit them to all peers. If the EB has been certified after L_vote + L_diff slots have passed, the node removes all of its transactions from the mempool (under the assumption that the EB will make it on-chain).
16+
17+
## New parameters
18+
19+
|Name|Description|Default value|
20+
|---|---|---|
21+
|`linear-vote-stage-length-slots`|How many slots the EB voting stage is allowed to last. For equivocation protection, this should be at least 3 * delta_hdr (which is currently 1 second).|5|
22+
|`linear-diffuse-stage-length-slots`|How many slots are votes allowed to diffuse.|5|
23+
|`eb-body-avg-size-bytes`|If `simulate-transactions` is false, this controls the size of the EBs we generate.|0|
24+
|`vote-generation-cpu-time-ms-per-tx`|A per-transaction CPU cost to apply when generating new vote bundles.|0|
25+
26+
## Not yet implemented
27+
- Freshest first delivery is not implemented for EBs, though EBs are created infrequently enough that this likely doesn't matter.
28+
- We are not yet applying voting rules; if you’re allowed to vote, you will always vote.
29+
- We are not yet accounting for equivocation.
30+
- Nodes are supposed to wait until the diffuse stage to vote for an EB, they are currently voting as soon as they can.

sim-rs/parameters/linear.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
leios-variant: linear
2+
timestamp-resolution-ms: 0.05

sim-rs/sim-cli/src/events/liveness.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ impl LivenessMonitor {
9393
}
9494
Event::VTBundleGenerated { votes, .. } => {
9595
for (eb_id, count) in &votes.0 {
96-
let eb = self.ebs.get_mut(eb_id).unwrap();
96+
let Some(eb) = self.ebs.get_mut(eb_id) else {
97+
continue;
98+
};
9799
eb.votes += *count as u64;
98100
if eb.votes >= self.vote_threshold {
99101
for ib_id in &eb.ibs {

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

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ pub struct RawParameters {
7272
pub leios_mempool_aggressive_pruning: bool,
7373
pub praos_chain_quality: u64,
7474
pub praos_fallback_enabled: bool,
75+
pub linear_vote_stage_length_slots: u64,
76+
pub linear_diffuse_stage_length_slots: u64,
7577

7678
// Transaction configuration
7779
pub tx_generation_distribution: DistributionConfig,
@@ -117,10 +119,12 @@ pub struct RawParameters {
117119
pub eb_size_bytes_per_ib: u64,
118120
pub eb_max_age_slots: u64,
119121
pub eb_referenced_txs_max_size_bytes: u64,
122+
pub eb_body_avg_size_bytes: u64,
120123

121124
// Vote configuration
122125
pub vote_generation_probability: f64,
123126
pub vote_generation_cpu_time_ms_constant: f64,
127+
pub vote_generation_cpu_time_ms_per_tx: f64,
124128
pub vote_generation_cpu_time_ms_per_ib: f64,
125129
pub vote_validation_cpu_time_ms: f64,
126130
pub vote_threshold: u64,
@@ -151,6 +155,7 @@ pub enum LeiosVariant {
151155
Full,
152156
FullWithoutIbs,
153157
FullWithTxReferences,
158+
Linear,
154159
}
155160

156161
#[derive(Debug, Copy, Clone, Deserialize, PartialEq, Eq)]
@@ -315,7 +320,8 @@ impl From<RawTopology> for Topology {
315320
pub(crate) struct CpuTimeConfig {
316321
pub tx_validation: Duration,
317322
pub rb_generation: Duration,
318-
pub rb_validation_constant: Duration,
323+
pub rb_head_validation: Duration,
324+
pub rb_body_validation_constant: Duration,
319325
pub rb_validation_per_byte: Duration,
320326
pub ib_generation: Duration,
321327
pub ib_head_validation: Duration,
@@ -324,6 +330,7 @@ pub(crate) struct CpuTimeConfig {
324330
pub eb_generation: Duration,
325331
pub eb_validation: Duration,
326332
pub vote_generation_constant: Duration,
333+
pub vote_generation_per_tx: Duration,
327334
pub vote_generation_per_ib: Duration,
328335
pub vote_validation: Duration,
329336
pub cert_generation_constant: Duration,
@@ -336,9 +343,9 @@ impl CpuTimeConfig {
336343
Self {
337344
tx_validation: duration_ms(params.tx_validation_cpu_time_ms),
338345
rb_generation: duration_ms(params.rb_generation_cpu_time_ms),
339-
rb_validation_constant: duration_ms(
340-
params.rb_head_validation_cpu_time_ms
341-
+ params.rb_body_legacy_praos_payload_validation_cpu_time_ms_constant,
346+
rb_head_validation: duration_ms(params.rb_head_validation_cpu_time_ms),
347+
rb_body_validation_constant: duration_ms(
348+
params.rb_body_legacy_praos_payload_validation_cpu_time_ms_constant,
342349
),
343350
rb_validation_per_byte: duration_ms(
344351
params.rb_body_legacy_praos_payload_validation_cpu_time_ms_per_byte,
@@ -354,6 +361,7 @@ impl CpuTimeConfig {
354361
eb_generation: duration_ms(params.eb_generation_cpu_time_ms),
355362
eb_validation: duration_ms(params.eb_validation_cpu_time_ms),
356363
vote_generation_constant: duration_ms(params.vote_generation_cpu_time_ms_constant),
364+
vote_generation_per_tx: duration_ms(params.vote_generation_cpu_time_ms_per_tx),
357365
vote_generation_per_ib: duration_ms(params.vote_generation_cpu_time_ms_per_ib),
358366
vote_validation: duration_ms(params.vote_validation_cpu_time_ms),
359367
cert_generation_constant: duration_ms(params.cert_generation_cpu_time_ms_constant),
@@ -407,6 +415,10 @@ impl BlockSizeConfig {
407415
self.eb_constant + self.eb_per_ib * (txs + ibs + ebs) as u64
408416
}
409417

418+
pub fn linear_eb(&self, txs: &[Arc<Transaction>]) -> u64 {
419+
self.eb_constant + txs.iter().map(|tx| tx.bytes).sum::<u64>()
420+
}
421+
410422
pub fn vote_bundle(&self, ebs: usize) -> u64 {
411423
self.vote_constant + self.vote_per_eb * ebs as u64
412424
}
@@ -441,6 +453,7 @@ impl TransactionConfig {
441453
next_id: Arc::new(AtomicU64::new(0)),
442454
ib_size: params.ib_body_avg_size_bytes,
443455
rb_size: params.rb_body_legacy_praos_payload_avg_size_bytes,
456+
eb_size: params.eb_body_avg_size_bytes,
444457
})
445458
}
446459
}
@@ -462,6 +475,7 @@ pub(crate) struct MockTransactionConfig {
462475
next_id: Arc<AtomicU64>,
463476
pub ib_size: u64,
464477
pub rb_size: u64,
478+
pub eb_size: u64,
465479
}
466480

467481
impl MockTransactionConfig {
@@ -494,6 +508,7 @@ pub struct SimConfiguration {
494508
pub late_ib_inclusion: bool,
495509
pub variant: LeiosVariant,
496510
pub vote_threshold: u64,
511+
pub(crate) total_stake: u64,
497512
pub(crate) praos_fallback: bool,
498513
pub(crate) header_diffusion_time: Duration,
499514
pub(crate) ib_generation_time: Duration,
@@ -506,6 +521,8 @@ pub struct SimConfiguration {
506521
pub(crate) eb_generation_probability: f64,
507522
pub(crate) vote_probability: f64,
508523
pub(crate) vote_slot_length: u64,
524+
pub(crate) linear_vote_stage_length: u64,
525+
pub(crate) linear_diffuse_stage_length: u64,
509526
pub(crate) max_block_size: u64,
510527
pub(crate) max_ib_size: u64,
511528
pub(crate) max_eb_size: u64,
@@ -539,6 +556,7 @@ impl SimConfiguration {
539556
params.ib_shard_period_length_slots
540557
);
541558
}
559+
let total_stake = topology.nodes.iter().map(|n| n.stake).sum();
542560
Ok(Self {
543561
seed: 0,
544562
timestamp_resolution: duration_ms(params.timestamp_resolution_ms),
@@ -552,6 +570,7 @@ impl SimConfiguration {
552570
max_eb_age: params.eb_max_age_slots,
553571
late_ib_inclusion: params.leios_late_ib_inclusion,
554572
variant: params.leios_variant,
573+
total_stake,
555574
praos_fallback: params.praos_fallback_enabled,
556575
header_diffusion_time: duration_ms(params.leios_header_diffusion_time_ms),
557576
ib_generation_time: duration_ms(params.leios_ib_generation_time_ms),
@@ -565,6 +584,8 @@ impl SimConfiguration {
565584
vote_probability: params.vote_generation_probability,
566585
vote_threshold: params.vote_threshold,
567586
vote_slot_length: params.leios_stage_active_voting_slots,
587+
linear_vote_stage_length: params.linear_vote_stage_length_slots,
588+
linear_diffuse_stage_length: params.linear_diffuse_stage_length_slots,
568589
max_block_size: params.rb_body_max_size_bytes,
569590
max_ib_size: params.ib_body_max_size_bytes,
570591
max_eb_size: params.eb_referenced_txs_max_size_bytes,

0 commit comments

Comments
 (0)