1- use std:: { collections:: HashMap , sync:: Arc } ;
1+ use std:: {
2+ collections:: { BTreeMap , HashMap } ,
3+ sync:: Arc ,
4+ } ;
25
36use rand:: { RngCore , SeedableRng } ;
47use 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
100103struct 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
110114impl 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
284339fn 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