@@ -4,13 +4,16 @@ use bitcoin::Network;
4
4
use csv:: WriterBuilder ;
5
5
use lightning:: ln:: features:: NodeFeatures ;
6
6
use lightning:: ln:: PaymentHash ;
7
- use rand:: Rng ;
7
+ use rand:: rngs:: StdRng ;
8
+ use rand:: { Rng , RngCore , SeedableRng } ;
9
+ use rand_chacha:: ChaCha8Rng ;
8
10
use random_activity:: RandomActivityError ;
9
11
use serde:: { Deserialize , Serialize } ;
10
12
use std:: collections:: HashSet ;
11
13
use std:: fmt:: { Display , Formatter } ;
12
14
use std:: marker:: Send ;
13
15
use std:: path:: PathBuf ;
16
+ use std:: sync:: Mutex as StdMutex ;
14
17
use std:: time:: { SystemTimeError , UNIX_EPOCH } ;
15
18
use std:: { collections:: HashMap , sync:: Arc , time:: SystemTime } ;
16
19
use thiserror:: Error ;
@@ -235,6 +238,8 @@ pub enum SimulationError {
235
238
MpscChannelError ( String ) ,
236
239
#[ error( "Payment Generation Error: {0}" ) ]
237
240
PaymentGenerationError ( PaymentGenerationError ) ,
241
+ #[ error( "Destination Generation Error: {0}" ) ]
242
+ DestinationGenerationError ( DestinationGenerationError ) ,
238
243
}
239
244
240
245
#[ derive( Debug , Error ) ]
@@ -304,10 +309,17 @@ pub trait LightningNode: Send {
304
309
async fn list_channels ( & mut self ) -> Result < Vec < u64 > , LightningError > ;
305
310
}
306
311
312
+ #[ derive( Debug , Error ) ]
313
+ #[ error( "Destination generation error: {0}" ) ]
314
+ pub struct DestinationGenerationError ( String ) ;
315
+
307
316
pub trait DestinationGenerator : Send {
308
317
/// choose_destination picks a destination node within the network, returning the node's information and its
309
318
/// 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 > ;
311
323
}
312
324
313
325
#[ derive( Debug , Error ) ]
@@ -322,7 +334,7 @@ pub trait PaymentGenerator: Display + Send {
322
334
fn payment_count ( & self ) -> Option < u64 > ;
323
335
324
336
/// 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 > ;
326
338
327
339
/// Returns a payment amount based, with a destination capacity optionally provided to inform the amount picked.
328
340
fn payment_amount (
@@ -435,6 +447,36 @@ enum SimulationOutput {
435
447
SendPaymentFailure ( Payment , PaymentResult ) ,
436
448
}
437
449
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
+
438
480
#[ derive( Clone ) ]
439
481
pub struct Simulation {
440
482
/// The lightning node that is being simulated.
@@ -453,6 +495,8 @@ pub struct Simulation {
453
495
activity_multiplier : f64 ,
454
496
/// Configurations for printing results to CSV. Results are not written if this option is None.
455
497
write_results : Option < WriteResults > ,
498
+ /// Random number generator created from fixed seed.
499
+ seeded_rng : MutRng ,
456
500
}
457
501
458
502
#[ derive( Clone ) ]
@@ -462,7 +506,7 @@ pub struct WriteResults {
462
506
/// The number of activity results to batch before printing in CSV.
463
507
pub batch_size : u32 ,
464
508
}
465
- ///
509
+
466
510
/// ExecutorKit contains the components required to spin up an activity configured by the user, to be used to
467
511
/// spin up the appropriate producers and consumers for the activity.
468
512
struct ExecutorKit {
@@ -481,6 +525,7 @@ impl Simulation {
481
525
expected_payment_msat : u64 ,
482
526
activity_multiplier : f64 ,
483
527
write_results : Option < WriteResults > ,
528
+ seed : Option < u64 > ,
484
529
) -> Self {
485
530
let ( shutdown_trigger, shutdown_listener) = triggered:: trigger ( ) ;
486
531
Self {
@@ -492,6 +537,7 @@ impl Simulation {
492
537
expected_payment_msat,
493
538
activity_multiplier,
494
539
write_results,
540
+ seeded_rng : MutRng :: new ( seed) ,
495
541
}
496
542
}
497
543
@@ -823,8 +869,11 @@ impl Simulation {
823
869
}
824
870
825
871
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 ) ?,
828
877
) ) ;
829
878
830
879
log:: info!(
@@ -841,6 +890,7 @@ impl Simulation {
841
890
* capacity,
842
891
self . expected_payment_msat ,
843
892
self . activity_multiplier ,
893
+ self . seeded_rng . clone ( ) ,
844
894
)
845
895
. map_err ( SimulationError :: RandomActivityError ) ?,
846
896
) ,
@@ -1047,11 +1097,11 @@ async fn produce_events<N: DestinationGenerator + ?Sized, A: PaymentGenerator +
1047
1097
}
1048
1098
start
1049
1099
} 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
1055
1105
} ;
1056
1106
1057
1107
select ! {
@@ -1062,7 +1112,7 @@ async fn produce_events<N: DestinationGenerator + ?Sized, A: PaymentGenerator +
1062
1112
// Wait until our time to next payment has elapsed then execute a random amount payment to a random
1063
1113
// destination.
1064
1114
_ = 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 ) ? ;
1066
1116
1067
1117
// Only proceed with a payment if the amount is non-zero, otherwise skip this round. If we can't get
1068
1118
// 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(
1327
1377
1328
1378
Ok ( ( ) )
1329
1379
}
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