Skip to content

Commit b69e330

Browse files
committed
sim-rs: test vote generation
1 parent 7f980c8 commit b69e330

File tree

1 file changed

+145
-21
lines changed

1 file changed

+145
-21
lines changed

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

Lines changed: 145 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::{collections::HashMap, sync::Arc};
1+
use std::{
2+
collections::{BTreeMap, HashMap},
3+
sync::Arc,
4+
};
25

36
use rand::{RngCore, SeedableRng};
47
use rand_chacha::ChaChaRng;
@@ -8,10 +11,10 @@ use crate::{
811
clock::{Clock, MockClockCoordinator, Timestamp},
912
config::{NodeId, RawLinkInfo, RawNode, RawTopology, SimConfiguration, TransactionConfig},
1013
events::{Event, EventTracker},
11-
model::{LinearEndorserBlock, LinearRankingBlock, Transaction},
14+
model::{LinearEndorserBlock, LinearRankingBlock, Transaction, VoteBundle},
1215
sim::{
1316
EventResult, NodeImpl,
14-
linear_leios::{CpuTask, LinearLeiosNode, Message},
17+
linear_leios::{CpuTask, LinearLeiosNode, Message, TimedEvent},
1518
lottery::{LotteryKind, MockLotteryResults},
1619
},
1720
};
@@ -98,50 +101,56 @@ fn new_node(stake: Option<u64>, producers: Vec<&'static str>) -> RawNode {
98101
}
99102

100103
struct TestDriver {
101-
sim_config: Arc<SimConfiguration>,
104+
pub config: Arc<SimConfiguration>,
102105
rng: ChaChaRng,
103106
slot: u64,
104107
time: MockClockCoordinator,
105108
nodes: HashMap<NodeId, LinearLeiosNode>,
106109
lottery: HashMap<NodeId, Arc<MockLotteryResults>>,
107110
queued: HashMap<NodeId, EventResult<LinearLeiosNode>>,
111+
events: BTreeMap<Timestamp, Vec<(NodeId, TimedEvent)>>,
108112
}
109113

110114
impl TestDriver {
111115
fn new(topology: RawTopology) -> Self {
112-
let sim_config = new_sim_config(topology);
113-
let rng = ChaChaRng::seed_from_u64(sim_config.seed);
116+
let config = new_sim_config(topology);
117+
let rng = ChaChaRng::seed_from_u64(config.seed);
114118
let slot = 0;
115119
let time = MockClockCoordinator::new();
116120
let (event_tx, _event_rx) = mpsc::unbounded_channel();
117-
let (nodes, lottery) = new_sim(sim_config.clone(), event_tx, time.clock());
121+
let (nodes, lottery) = new_sim(config.clone(), event_tx, time.clock());
118122
Self {
119-
sim_config,
123+
config,
120124
rng,
121125
slot,
122126
time,
123127
nodes,
124128
lottery,
125129
queued: HashMap::new(),
130+
events: BTreeMap::new(),
126131
}
127132
}
128133

129134
pub fn id_for(&self, name: &str) -> NodeId {
130-
self.sim_config
135+
self.config
131136
.nodes
132137
.iter()
133138
.find_map(|n| if n.name == name { Some(n.id) } else { None })
134139
.unwrap()
135140
}
136141

142+
pub fn now(&self) -> Timestamp {
143+
self.time.now()
144+
}
145+
137146
pub fn produce_tx(&mut self, node_id: NodeId, conflict: bool) -> Arc<Transaction> {
138-
let TransactionConfig::Real(tx_config) = &self.sim_config.transactions else {
147+
let TransactionConfig::Real(tx_config) = &self.config.transactions else {
139148
panic!("unexpected TX config")
140149
};
141150
let tx = Arc::new(tx_config.new_tx(&mut self.rng, Some(if conflict { 1.0 } else { 0.0 })));
142151
let node = self.nodes.get_mut(&node_id).unwrap();
143152
let events = node.handle_new_tx(tx.clone());
144-
self.queued.entry(node_id).or_default().merge(events);
153+
self.process_events(node_id, events);
145154
tx
146155
}
147156

@@ -152,12 +161,48 @@ impl TestDriver {
152161
.configure_win(LotteryKind::GenerateRB, result);
153162
}
154163

164+
pub fn win_next_vote_lottery(&mut self, node_id: NodeId, result: u64) {
165+
self.lottery
166+
.get(&node_id)
167+
.unwrap()
168+
.configure_win(LotteryKind::GenerateVote, result);
169+
}
170+
155171
pub fn next_slot(&mut self) {
156-
self.slot += 1;
157-
self.time.advance_time(Timestamp::from_secs(self.slot));
158-
for (node_id, node) in self.nodes.iter_mut() {
159-
let events = node.handle_new_slot(self.slot);
160-
self.queued.entry(*node_id).or_default().merge(events);
172+
self.advance_time_to(Timestamp::from_secs(self.slot + 1));
173+
}
174+
175+
pub fn advance_time_to(&mut self, timestamp: Timestamp) {
176+
let mut now = self.time.now();
177+
while now < timestamp {
178+
let next_slot = self.slot + 1;
179+
let next_slot_time = Timestamp::from_secs(next_slot);
180+
let mut next_event = timestamp.min(next_slot_time);
181+
if let Some((event_time, _)) = self.events.first_key_value() {
182+
next_event = next_event.min(*event_time);
183+
}
184+
self.time.advance_time(next_event);
185+
now = next_event;
186+
187+
let mut updates: HashMap<NodeId, EventResult<LinearLeiosNode>> = HashMap::new();
188+
if now == next_slot_time {
189+
for (node_id, node) in &mut self.nodes {
190+
let events = node.handle_new_slot(next_slot);
191+
updates.entry(*node_id).or_default().merge(events);
192+
}
193+
self.slot = next_slot;
194+
}
195+
if let Some(events) = self.events.remove(&next_event) {
196+
for (node_id, event) in events {
197+
let node = self.nodes.get_mut(&node_id).unwrap();
198+
let events = node.handle_timed_event(event);
199+
updates.entry(node_id).or_default().merge(events);
200+
}
201+
}
202+
203+
for (node, events) in updates {
204+
self.process_events(node, events);
205+
}
161206
}
162207
}
163208

@@ -234,7 +279,7 @@ impl TestDriver {
234279
.get_mut(&to)
235280
.unwrap()
236281
.handle_message(from, message);
237-
self.queued.entry(to).or_default().merge(events);
282+
self.process_events(to, events);
238283
}
239284

240285
pub fn expect_no_message(
@@ -261,24 +306,34 @@ impl TestDriver {
261306
{
262307
let queued = self.queued.entry(node).or_default();
263308
let mut result = None;
264-
let mut new_queued = EventResult::default();
309+
let mut events = EventResult::default();
265310
queued.tasks.retain(|t| {
266311
if result.is_some() {
267312
return true;
268313
}
269314
result = matcher(t);
270315
if result.is_some() {
271-
new_queued = self
316+
events = self
272317
.nodes
273318
.get_mut(&node)
274319
.unwrap()
275320
.handle_cpu_task(t.clone());
276321
}
277322
result.is_none()
278323
});
279-
queued.merge(new_queued);
324+
self.process_events(node, events);
280325
result.expect("no CPU tasks matching filter")
281326
}
327+
328+
fn process_events(&mut self, node: NodeId, mut events: EventResult<LinearLeiosNode>) {
329+
for (timestamp, event) in events.timed_events.drain(..) {
330+
self.events
331+
.entry(timestamp)
332+
.or_default()
333+
.push((node, event));
334+
}
335+
self.queued.entry(node).or_default().merge(events);
336+
}
282337
}
283338

284339
fn is_new_rb_task(
@@ -293,8 +348,15 @@ fn is_new_rb_task(
293348
}
294349
}
295350

351+
fn is_new_vote_task(task: &CpuTask) -> Option<Arc<VoteBundle>> {
352+
match task {
353+
CpuTask::VTBundleGenerated(vote, _) => Some(Arc::new(vote.clone())),
354+
_ => None,
355+
}
356+
}
357+
296358
#[test]
297-
fn should_propagate_transactions() {
359+
fn should_produce_rbs_without_ebs() {
298360
let topology = new_topology(vec![
299361
("node-1", new_node(Some(1000), vec!["node-2"])),
300362
("node-2", new_node(Some(1000), vec!["node-1"])),
@@ -317,6 +379,37 @@ fn should_propagate_transactions() {
317379
let (new_rb, new_eb) = sim.expect_cpu_task_matching(node1, is_new_rb_task);
318380
assert_eq!(new_rb.transactions, vec![tx1, tx2]);
319381
assert_eq!(new_eb, None);
382+
383+
sim.expect_rb_and_eb_sent(node1, node2, new_rb, None);
384+
}
385+
386+
#[test]
387+
fn should_produce_rbs_and_ebs() {
388+
let topology = new_topology(vec![
389+
("node-1", new_node(Some(1000), vec!["node-2"])),
390+
("node-2", new_node(Some(1000), vec!["node-1"])),
391+
]);
392+
let mut sim = TestDriver::new(topology);
393+
let node1 = sim.id_for("node-1");
394+
let node2 = sim.id_for("node-2");
395+
396+
// Node 1 produces three transactions, Node 2 should request them all
397+
let tx1_1 = sim.produce_tx(node1, false);
398+
sim.expect_tx_sent(node1, node2, tx1_1.clone());
399+
let tx1_2 = sim.produce_tx(node1, false);
400+
sim.expect_tx_sent(node1, node2, tx1_2.clone());
401+
let tx1_3 = sim.produce_tx(node1, false);
402+
sim.expect_tx_sent(node1, node2, tx1_3.clone());
403+
404+
sim.win_next_rb_lottery(node1, 0);
405+
sim.next_slot();
406+
let (new_rb, new_eb) = sim.expect_cpu_task_matching(node1, is_new_rb_task);
407+
assert_eq!(new_rb.transactions, vec![tx1_1, tx1_2]);
408+
let new_eb = new_eb.expect("no EB produced");
409+
assert_eq!(new_eb.txs, vec![tx1_3]);
410+
411+
sim.expect_rb_and_eb_sent(node1, node2, new_rb, Some(new_eb.clone()));
412+
sim.expect_eb_validated(node2, new_eb);
320413
}
321414

322415
#[test]
@@ -397,3 +490,34 @@ fn should_repropagate_conflicting_transactions_from_eb() {
397490
sim.expect_tx_sent(node2, node3, tx1_3);
398491
sim.expect_eb_validated(node3, eb);
399492
}
493+
494+
#[test]
495+
fn should_vote_for_eb() {
496+
let topology = new_topology(vec![
497+
("node-1", new_node(Some(1000), vec!["node-2"])),
498+
("node-2", new_node(Some(1000), vec!["node-1"])),
499+
]);
500+
let mut sim = TestDriver::new(topology);
501+
let node1 = sim.id_for("node-1");
502+
let node2 = sim.id_for("node-2");
503+
504+
let txs = (0..3)
505+
.map(|_| sim.produce_tx(node1, false))
506+
.collect::<Vec<_>>();
507+
for tx in &txs {
508+
sim.expect_tx_sent(node1, node2, tx.clone());
509+
}
510+
511+
sim.win_next_rb_lottery(node1, 0);
512+
sim.next_slot();
513+
let (rb, eb) = sim.expect_cpu_task_matching(node1, is_new_rb_task);
514+
let eb = eb.expect("node did not produce EB");
515+
516+
sim.expect_rb_and_eb_sent(node1, node2, rb.clone(), Some(eb.clone()));
517+
sim.expect_eb_validated(node2, eb.clone());
518+
519+
sim.win_next_vote_lottery(node2, 0);
520+
sim.advance_time_to(sim.now() + (sim.config.header_diffusion_time * 3));
521+
let vote = sim.expect_cpu_task_matching(node2, is_new_vote_task);
522+
assert_eq!(*vote.ebs.first_key_value().unwrap().0, eb.id());
523+
}

0 commit comments

Comments
 (0)