@@ -4,13 +4,16 @@ use bitcoin::Network;
44use csv:: WriterBuilder ;
55use lightning:: ln:: features:: NodeFeatures ;
66use lightning:: ln:: PaymentHash ;
7- use rand:: Rng ;
7+ use rand:: rngs:: StdRng ;
8+ use rand:: { Rng , RngCore , SeedableRng } ;
9+ use rand_chacha:: ChaCha8Rng ;
810use random_activity:: RandomActivityError ;
911use serde:: { Deserialize , Serialize } ;
1012use std:: collections:: HashSet ;
1113use std:: fmt:: { Display , Formatter } ;
1214use std:: marker:: Send ;
1315use std:: path:: PathBuf ;
16+ use std:: sync:: Mutex as StdMutex ;
1417use std:: time:: { SystemTimeError , UNIX_EPOCH } ;
1518use std:: { collections:: HashMap , sync:: Arc , time:: SystemTime } ;
1619use thiserror:: Error ;
@@ -235,6 +238,8 @@ pub enum SimulationError {
235238 MpscChannelError ( String ) ,
236239 #[ error( "Payment Generation Error: {0}" ) ]
237240 PaymentGenerationError ( PaymentGenerationError ) ,
241+ #[ error( "Destination Generation Error: {0}" ) ]
242+ DestinationGenerationError ( DestinationGenerationError ) ,
238243}
239244
240245#[ derive( Debug , Error ) ]
@@ -304,10 +309,17 @@ pub trait LightningNode: Send {
304309 async fn list_channels ( & mut self ) -> Result < Vec < u64 > , LightningError > ;
305310}
306311
312+ #[ derive( Debug , Error ) ]
313+ #[ error( "Destination generation error: {0}" ) ]
314+ pub struct DestinationGenerationError ( String ) ;
315+
307316pub trait DestinationGenerator : Send {
308317 /// choose_destination picks a destination node within the network, returning the node's information and its
309318 /// capacity (if available).
310- fn choose_destination ( & self , source : PublicKey ) -> ( NodeInfo , Option < u64 > ) ;
319+ fn choose_destination (
320+ & self ,
321+ source : PublicKey ,
322+ ) -> Result < ( NodeInfo , Option < u64 > ) , DestinationGenerationError > ;
311323}
312324
313325#[ derive( Debug , Error ) ]
@@ -322,7 +334,7 @@ pub trait PaymentGenerator: Display + Send {
322334 fn payment_count ( & self ) -> Option < u64 > ;
323335
324336 /// Returns the number of seconds that a node should wait until firing its next payment.
325- fn next_payment_wait ( & self ) -> time:: Duration ;
337+ fn next_payment_wait ( & self ) -> Result < time:: Duration , PaymentGenerationError > ;
326338
327339 /// Returns a payment amount based, with a destination capacity optionally provided to inform the amount picked.
328340 fn payment_amount (
@@ -435,6 +447,36 @@ enum SimulationOutput {
435447 SendPaymentFailure ( Payment , PaymentResult ) ,
436448}
437449
450+ /// MutRngType is a convenient type alias for any random number generator (RNG) type that
451+ /// allows shared and exclusive access. This is necessary because a single RNG
452+ /// is to be shared across multiple `DestinationGenerator`s and `PaymentGenerator`s
453+ /// for deterministic outcomes.
454+ ///
455+ /// **Note**: `StdMutex`, i.e. (`std::sync::Mutex`), is used here to avoid making the traits
456+ /// `DestinationGenerator` and `PaymentGenerator` async.
457+ type MutRngType = Arc < StdMutex < Box < dyn RngCore + Send > > > ;
458+
459+ /// Newtype for `MutRngType` to encapsulate and hide implementation details for
460+ /// creating new `MutRngType` types. Provides convenient API for the same purpose.
461+ #[ derive( Clone ) ]
462+ struct MutRng ( pub MutRngType ) ;
463+
464+ impl MutRng {
465+ /// Creates a new MutRng given an optional `u64` argument. If `seed_opt` is `Some`,
466+ /// random activity generation in the simulator occurs near-deterministically.
467+ /// If it is `None`, activity generation is truly random, and based on a
468+ /// non-deterministic source of entropy.
469+ pub fn new ( seed_opt : Option < u64 > ) -> Self {
470+ if let Some ( seed) = seed_opt {
471+ Self ( Arc :: new ( StdMutex :: new (
472+ Box :: new ( ChaCha8Rng :: seed_from_u64 ( seed) ) as Box < dyn RngCore + Send > ,
473+ ) ) )
474+ } else {
475+ Self ( Arc :: new ( StdMutex :: new ( Box :: new ( StdRng :: from_entropy ( ) ) ) ) )
476+ }
477+ }
478+ }
479+
438480#[ derive( Clone ) ]
439481pub struct Simulation {
440482 /// The lightning node that is being simulated.
@@ -453,6 +495,8 @@ pub struct Simulation {
453495 activity_multiplier : f64 ,
454496 /// Configurations for printing results to CSV. Results are not written if this option is None.
455497 write_results : Option < WriteResults > ,
498+ /// Random number generator created from fixed seed.
499+ seeded_rng : MutRng ,
456500}
457501
458502#[ derive( Clone ) ]
@@ -462,7 +506,7 @@ pub struct WriteResults {
462506 /// The number of activity results to batch before printing in CSV.
463507 pub batch_size : u32 ,
464508}
465- ///
509+
466510/// ExecutorKit contains the components required to spin up an activity configured by the user, to be used to
467511/// spin up the appropriate producers and consumers for the activity.
468512struct ExecutorKit {
@@ -481,6 +525,7 @@ impl Simulation {
481525 expected_payment_msat : u64 ,
482526 activity_multiplier : f64 ,
483527 write_results : Option < WriteResults > ,
528+ seed : Option < u64 > ,
484529 ) -> Self {
485530 let ( shutdown_trigger, shutdown_listener) = triggered:: trigger ( ) ;
486531 Self {
@@ -492,6 +537,7 @@ impl Simulation {
492537 expected_payment_msat,
493538 activity_multiplier,
494539 write_results,
540+ seeded_rng : MutRng :: new ( seed) ,
495541 }
496542 }
497543
@@ -823,8 +869,11 @@ impl Simulation {
823869 }
824870
825871 let network_generator = Arc :: new ( Mutex :: new (
826- NetworkGraphView :: new ( active_nodes. values ( ) . cloned ( ) . collect ( ) )
827- . map_err ( SimulationError :: RandomActivityError ) ?,
872+ NetworkGraphView :: new (
873+ active_nodes. values ( ) . cloned ( ) . collect ( ) ,
874+ self . seeded_rng . clone ( ) ,
875+ )
876+ . map_err ( SimulationError :: RandomActivityError ) ?,
828877 ) ) ;
829878
830879 log:: info!(
@@ -841,6 +890,7 @@ impl Simulation {
841890 * capacity,
842891 self . expected_payment_msat ,
843892 self . activity_multiplier ,
893+ self . seeded_rng . clone ( ) ,
844894 )
845895 . map_err ( SimulationError :: RandomActivityError ) ?,
846896 ) ,
@@ -1047,11 +1097,11 @@ async fn produce_events<N: DestinationGenerator + ?Sized, A: PaymentGenerator +
10471097 }
10481098 start
10491099 } else {
1050- log :: debug! (
1051- "Next payment for {source} in {:?}." ,
1052- node_generator . next_payment_wait ( )
1053- ) ;
1054- node_generator . next_payment_wait ( )
1100+ let wait = node_generator
1101+ . next_payment_wait ( )
1102+ . map_err ( SimulationError :: PaymentGenerationError ) ? ;
1103+ log :: debug! ( "Next payment for {source} in {:?}." , wait ) ;
1104+ wait
10551105 } ;
10561106
10571107 select ! {
@@ -1062,7 +1112,7 @@ async fn produce_events<N: DestinationGenerator + ?Sized, A: PaymentGenerator +
10621112 // Wait until our time to next payment has elapsed then execute a random amount payment to a random
10631113 // destination.
10641114 _ = time:: sleep( wait) => {
1065- let ( destination, capacity) = network_generator. lock( ) . await . choose_destination( source. pubkey) ;
1115+ let ( destination, capacity) = network_generator. lock( ) . await . choose_destination( source. pubkey) . map_err ( SimulationError :: DestinationGenerationError ) ? ;
10661116
10671117 // Only proceed with a payment if the amount is non-zero, otherwise skip this round. If we can't get
10681118 // a payment amount something has gone wrong (because we should have validated that we can always
@@ -1327,3 +1377,34 @@ async fn track_payment_result(
13271377
13281378 Ok ( ( ) )
13291379}
1380+
1381+ #[ cfg( test) ]
1382+ mod tests {
1383+ use crate :: MutRng ;
1384+
1385+ #[ test]
1386+ fn create_seeded_mut_rng ( ) {
1387+ let seeds = vec ! [ u64 :: MIN , u64 :: MAX ] ;
1388+
1389+ for seed in seeds {
1390+ let mut_rng_1 = MutRng :: new ( Some ( seed) ) ;
1391+ let mut_rng_2 = MutRng :: new ( Some ( seed) ) ;
1392+
1393+ let mut rng_1 = mut_rng_1. 0 . lock ( ) . unwrap ( ) ;
1394+ let mut rng_2 = mut_rng_2. 0 . lock ( ) . unwrap ( ) ;
1395+
1396+ assert_eq ! ( rng_1. next_u64( ) , rng_2. next_u64( ) )
1397+ }
1398+ }
1399+
1400+ #[ test]
1401+ fn create_unseeded_mut_rng ( ) {
1402+ let mut_rng_1 = MutRng :: new ( None ) ;
1403+ let mut_rng_2 = MutRng :: new ( None ) ;
1404+
1405+ let mut rng_1 = mut_rng_1. 0 . lock ( ) . unwrap ( ) ;
1406+ let mut rng_2 = mut_rng_2. 0 . lock ( ) . unwrap ( ) ;
1407+
1408+ assert_ne ! ( rng_1. next_u64( ) , rng_2. next_u64( ) )
1409+ }
1410+ }
0 commit comments