Skip to content

Commit c11be4b

Browse files
committed
sim-rs: wait for equivocation before voting
1 parent 814d1e1 commit c11be4b

File tree

6 files changed

+65
-28
lines changed

6 files changed

+65
-28
lines changed

sim-rs/implementations/LINEAR_LEIOS.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ When a node receives an RB body, it immediately removes all referenced/conflicti
1515

1616
When a node receives an EB body, it runs lightweight validation and then propagates the body to peers. After this lightweight validation, it runs more expensive complete validation (presumably at the TX level) before voting.
1717

18+
To detect equivocation, a node will wait until at least `3 * Δhdr` after an EB was generated before voting for it.
19+
1820
When voting, a node 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).
1921

2022
## New parameters
@@ -43,5 +45,4 @@ When voting, a node runs a VRF lottery to decide how many times it can vote for
4345

4446
## Not yet implemented
4547
- Freshest first delivery is not implemented for EBs, though EBs are created infrequently enough that this likely doesn't matter.
46-
- We are not yet accounting for equivocation.
47-
- Nodes are supposed to wait until the diffuse stage to vote for an EB, they are currently voting as soon as they can.
48+
- We are not yet simulating equivocation.

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

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use tokio_util::sync::CancellationToken;
99
use tx::TransactionProducer;
1010

1111
use crate::{
12-
clock::{Clock, ClockCoordinator},
12+
clock::{Clock, ClockCoordinator, Timestamp},
1313
config::{CpuTimeConfig, LeiosVariant, NodeConfiguration, NodeId, SimConfiguration},
1414
events::EventTracker,
1515
model::Transaction,
@@ -242,33 +242,40 @@ trait SimCpuTask {
242242
fn times(&self, config: &CpuTimeConfig) -> Vec<Duration>;
243243
}
244244

245-
struct EventResult<Message: SimMessage, Task: SimCpuTask> {
246-
messages: Vec<(NodeId, Message)>,
247-
tasks: Vec<Task>,
245+
struct EventResult<N: NodeImpl> {
246+
messages: Vec<(NodeId, N::Message)>,
247+
tasks: Vec<N::Task>,
248+
timed_events: Vec<(Timestamp, N::TimedEvent)>,
248249
}
249250

250-
impl<Message: SimMessage, Task: SimCpuTask> Default for EventResult<Message, Task> {
251+
impl<N: NodeImpl> Default for EventResult<N> {
251252
fn default() -> Self {
252253
Self {
253254
messages: vec![],
254255
tasks: vec![],
256+
timed_events: vec![],
255257
}
256258
}
257259
}
258260

259-
impl<Message: SimMessage, Task: SimCpuTask> EventResult<Message, Task> {
260-
pub fn send_to(&mut self, to: NodeId, msg: Message) {
261+
impl<N: NodeImpl> EventResult<N> {
262+
pub fn send_to(&mut self, to: NodeId, msg: N::Message) {
261263
self.messages.push((to, msg));
262264
}
263265

264-
pub fn schedule_cpu_task(&mut self, task: Task) {
266+
pub fn schedule_cpu_task(&mut self, task: N::Task) {
265267
self.tasks.push(task);
266268
}
269+
270+
pub fn schedule_event(&mut self, time: Timestamp, event: N::TimedEvent) {
271+
self.timed_events.push((time, event));
272+
}
267273
}
268274

269-
trait NodeImpl {
275+
trait NodeImpl: Sized {
270276
type Message: SimMessage;
271277
type Task: SimCpuTask;
278+
type TimedEvent;
272279

273280
fn new(
274281
config: &NodeConfiguration,
@@ -278,12 +285,12 @@ trait NodeImpl {
278285
clock: Clock,
279286
) -> Self;
280287

281-
fn handle_new_slot(&mut self, slot: u64) -> EventResult<Self::Message, Self::Task>;
282-
fn handle_new_tx(&mut self, tx: Arc<Transaction>) -> EventResult<Self::Message, Self::Task>;
283-
fn handle_message(
284-
&mut self,
285-
from: NodeId,
286-
msg: Self::Message,
287-
) -> EventResult<Self::Message, Self::Task>;
288-
fn handle_cpu_task(&mut self, task: Self::Task) -> EventResult<Self::Message, Self::Task>;
288+
fn handle_new_slot(&mut self, slot: u64) -> EventResult<Self>;
289+
fn handle_new_tx(&mut self, tx: Arc<Transaction>) -> EventResult<Self>;
290+
fn handle_message(&mut self, from: NodeId, msg: Self::Message) -> EventResult<Self>;
291+
fn handle_cpu_task(&mut self, task: Self::Task) -> EventResult<Self>;
292+
fn handle_timed_event(&mut self, event: Self::TimedEvent) -> EventResult<Self> {
293+
let _ = event;
294+
EventResult::default()
295+
}
289296
}

sim-rs/sim-core/src/sim/driver.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ struct CpuTaskWrapper<Task: SimCpuTask> {
2121
cpu_time: Duration,
2222
}
2323

24-
enum NodeEvent {
24+
enum NodeEvent<T> {
2525
NewSlot(u64),
2626
CpuSubtaskCompleted(Subtask),
27+
Other(T),
2728
}
2829

2930
pub struct NodeDriver<N: NodeImpl> {
@@ -35,7 +36,7 @@ pub struct NodeDriver<N: NodeImpl> {
3536
msg_source: NetworkSource<N::Message>,
3637
msg_sink: NetworkSink<MiniProtocol, N::Message>,
3738
tx_source: mpsc::UnboundedReceiver<Arc<Transaction>>,
38-
events: BinaryHeap<FutureEvent<NodeEvent>>,
39+
events: BinaryHeap<FutureEvent<NodeEvent<N::TimedEvent>>>,
3940
tracker: EventTracker,
4041
clock: ClockBarrier,
4142
cpu: CpuTaskQueue<CpuTaskWrapper<N::Task>>,
@@ -105,6 +106,9 @@ impl<N: NodeImpl> NodeDriver<N> {
105106
};
106107
(result, false)
107108
}
109+
NodeEvent::Other(event) => {
110+
(self.node.handle_timed_event(event), false)
111+
}
108112
}
109113
}
110114
};
@@ -114,6 +118,9 @@ impl<N: NodeImpl> NodeDriver<N> {
114118
for task in result.tasks {
115119
self.schedule_cpu_task(task);
116120
}
121+
for (time, event) in result.timed_events {
122+
self.events.push(FutureEvent(time, NodeEvent::Other(event)));
123+
}
117124
if finish_task {
118125
self.clock.finish_task();
119126
}
@@ -156,10 +163,7 @@ impl<N: NodeImpl> NodeDriver<N> {
156163
}
157164
}
158165

159-
fn handle_subtask_completed(
160-
&mut self,
161-
subtask: Subtask,
162-
) -> Option<EventResult<N::Message, N::Task>> {
166+
fn handle_subtask_completed(&mut self, subtask: Subtask) -> Option<EventResult<N>> {
163167
let task_id = CpuTaskId {
164168
node: self.id,
165169
index: subtask.task_id,

sim-rs/sim-core/src/sim/leios.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,11 +372,12 @@ impl PeerInputBlockRequests {
372372
}
373373
}
374374

375-
type EventResult = super::EventResult<SimulationMessage, Task>;
375+
type EventResult = super::EventResult<LeiosNode>;
376376

377377
impl NodeImpl for LeiosNode {
378378
type Task = Task;
379379
type Message = SimulationMessage;
380+
type TimedEvent = ();
380381

381382
fn new(
382383
config: &NodeConfiguration,

sim-rs/sim-core/src/sim/linear_leios.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,10 @@ impl SimCpuTask for CpuTask {
180180
}
181181
}
182182

183+
pub enum TimedEvent {
184+
TryVote(Arc<EndorserBlock>, Timestamp),
185+
}
186+
183187
enum TransactionView {
184188
Pending,
185189
Received(Arc<Transaction>),
@@ -269,11 +273,12 @@ pub struct LinearLeiosNode {
269273
leios: NodeLeiosState,
270274
}
271275

272-
type EventResult = super::EventResult<Message, CpuTask>;
276+
type EventResult = super::EventResult<LinearLeiosNode>;
273277

274278
impl NodeImpl for LinearLeiosNode {
275279
type Message = Message;
276280
type Task = CpuTask;
281+
type TimedEvent = TimedEvent;
277282

278283
fn new(
279284
config: &NodeConfiguration,
@@ -359,6 +364,13 @@ impl NodeImpl for LinearLeiosNode {
359364
}
360365
std::mem::take(&mut self.queued)
361366
}
367+
368+
fn handle_timed_event(&mut self, event: Self::TimedEvent) -> EventResult {
369+
match event {
370+
TimedEvent::TryVote(eb, seen) => self.vote_for_endorser_block(&eb, seen),
371+
}
372+
std::mem::take(&mut self.queued)
373+
}
362374
}
363375

364376
// Transaction propagation
@@ -839,6 +851,17 @@ impl LinearLeiosNode {
839851
// Voting
840852
impl LinearLeiosNode {
841853
fn vote_for_endorser_block(&mut self, eb: &Arc<EndorserBlock>, seen: Timestamp) {
854+
let equivocation_cutoff_time =
855+
Timestamp::from_secs(eb.slot) + (self.sim_config.header_diffusion_time * 3);
856+
if eb.producer != self.id && self.clock.now() < equivocation_cutoff_time {
857+
// If we haven't waited long enough to detect equivocations,
858+
// schedule voting later.
859+
self.queued.schedule_event(
860+
equivocation_cutoff_time,
861+
TimedEvent::TryVote(eb.clone(), seen),
862+
);
863+
return;
864+
}
842865
if !self.try_vote_for_endorser_block(eb, seen) && self.sim_config.emit_conformance_events {
843866
self.tracker
844867
.track_linear_no_vote_generated(self.id, eb.id());

sim-rs/sim-core/src/sim/stracciatella.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,12 @@ pub struct StracciatellaLeiosNode {
246246
leios: NodeLeiosState,
247247
}
248248

249-
type EventResult = super::EventResult<Message, CpuTask>;
249+
type EventResult = super::EventResult<StracciatellaLeiosNode>;
250250

251251
impl NodeImpl for StracciatellaLeiosNode {
252252
type Message = Message;
253253
type Task = CpuTask;
254+
type TimedEvent = ();
254255

255256
fn new(
256257
config: &NodeConfiguration,

0 commit comments

Comments
 (0)