Skip to content

Commit f47dfb3

Browse files
authored
Merge pull request #259 from sangbida/sangbida/distinct-rng
Implement Distinct RNG Sequences
2 parents 55cc91a + 9daf52c commit f47dfb3

File tree

2 files changed

+67
-23
lines changed

2 files changed

+67
-23
lines changed

simln-lib/src/lib.rs

Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ use bitcoin::Network;
77
use csv::WriterBuilder;
88
use lightning::ln::features::NodeFeatures;
99
use lightning::ln::PaymentHash;
10-
use rand::rngs::StdRng;
1110
use rand::{Rng, RngCore, SeedableRng};
1211
use rand_chacha::ChaCha8Rng;
1312
use random_activity::RandomActivityError;
1413
use serde::{Deserialize, Serialize};
1514
use std::collections::HashSet;
1615
use std::fmt::{Display, Formatter};
16+
use std::hash::{DefaultHasher, Hash, Hasher};
1717
use std::marker::Send;
1818
use std::path::PathBuf;
1919
use std::sync::Mutex as StdMutex;
@@ -527,16 +527,24 @@ type MutRngType = Arc<StdMutex<dyn RngCore + Send>>;
527527
struct MutRng(MutRngType);
528528

529529
impl MutRng {
530-
/// Creates a new MutRng given an optional `u64` argument. If `seed_opt` is `Some`,
531-
/// random activity generation in the simulator occurs near-deterministically.
532-
/// If it is `None`, activity generation is truly random, and based on a
533-
/// non-deterministic source of entropy.
534-
pub fn new(seed_opt: Option<u64>) -> Self {
535-
if let Some(seed) = seed_opt {
536-
Self(Arc::new(StdMutex::new(ChaCha8Rng::seed_from_u64(seed))))
537-
} else {
538-
Self(Arc::new(StdMutex::new(StdRng::from_entropy())))
539-
}
530+
/// Creates a new MutRng given an optional `u64` seed and optional pubkey. If `seed_opt` is `Some`, random activity
531+
/// generation in the simulator occurs near-deterministically.
532+
/// If it is `None`, activity generation is truly random, and based on a non-deterministic source of entropy.
533+
/// If a pubkey is provided, it will be used to salt the seed to ensure each node gets a deterministic but
534+
/// different RNG sequence.
535+
pub fn new(seed_opt: Option<(u64, Option<&PublicKey>)>) -> Self {
536+
let seed = match seed_opt {
537+
Some((seed, Some(pubkey))) => {
538+
let mut hasher = DefaultHasher::new();
539+
let mut combined = pubkey.serialize().to_vec();
540+
combined.extend_from_slice(&seed.to_le_bytes());
541+
combined.hash(&mut hasher);
542+
hasher.finish()
543+
},
544+
Some((seed, None)) => seed,
545+
None => rand::random(),
546+
};
547+
Self(Arc::new(StdMutex::new(ChaCha8Rng::seed_from_u64(seed))))
540548
}
541549
}
542550

@@ -552,8 +560,8 @@ pub struct SimulationCfg {
552560
activity_multiplier: f64,
553561
/// Configurations for printing results to CSV. Results are not written if this option is None.
554562
write_results: Option<WriteResults>,
555-
/// Random number generator created from fixed seed.
556-
seeded_rng: MutRng,
563+
/// Optional seed for deterministic random number generation.
564+
seed: Option<u64>,
557565
}
558566

559567
impl SimulationCfg {
@@ -569,7 +577,7 @@ impl SimulationCfg {
569577
expected_payment_msat,
570578
activity_multiplier,
571579
write_results,
572-
seeded_rng: MutRng::new(seed),
580+
seed,
573581
}
574582
}
575583
}
@@ -985,10 +993,11 @@ impl<C: Clock + 'static> Simulation<C> {
985993
active_nodes.insert(node_info.pubkey, (node_info, capacity));
986994
}
987995

996+
// Create a network generator with a shared RNG for all nodes.
988997
let network_generator = Arc::new(Mutex::new(
989998
NetworkGraphView::new(
990999
active_nodes.values().cloned().collect(),
991-
self.cfg.seeded_rng.clone(),
1000+
MutRng::new(self.cfg.seed.map(|seed| (seed, None))),
9921001
)
9931002
.map_err(SimulationError::RandomActivityError)?,
9941003
));
@@ -999,6 +1008,9 @@ impl<C: Clock + 'static> Simulation<C> {
9991008
);
10001009

10011010
for (node_info, capacity) in active_nodes.values() {
1011+
// Create a salted RNG for this node based on its pubkey.
1012+
let seed_opt = self.cfg.seed.map(|seed| (seed, Some(&node_info.pubkey)));
1013+
let salted_rng = MutRng::new(seed_opt);
10021014
generators.push(ExecutorKit {
10031015
source_info: node_info.clone(),
10041016
network_generator: network_generator.clone(),
@@ -1007,7 +1019,7 @@ impl<C: Clock + 'static> Simulation<C> {
10071019
*capacity,
10081020
self.cfg.expected_payment_msat,
10091021
self.cfg.activity_multiplier,
1010-
self.cfg.seeded_rng.clone(),
1022+
salted_rng,
10111023
)
10121024
.map_err(SimulationError::RandomActivityError)?,
10131025
),
@@ -1040,7 +1052,7 @@ impl<C: Clock + 'static> Simulation<C> {
10401052

10411053
// Generate a consumer for the receiving end of the channel. It takes the event receiver that it'll pull
10421054
// events from and the results sender to report the events it has triggered for further monitoring.
1043-
// ce: consume event
1055+
// ce: consume event.
10441056
let ce_listener = self.shutdown_listener.clone();
10451057
let ce_shutdown = self.shutdown_trigger.clone();
10461058
let ce_output_sender = output_sender.clone();
@@ -1568,8 +1580,8 @@ mod tests {
15681580
let seeds = vec![u64::MIN, u64::MAX];
15691581

15701582
for seed in seeds {
1571-
let mut_rng_1 = MutRng::new(Some(seed));
1572-
let mut_rng_2 = MutRng::new(Some(seed));
1583+
let mut_rng_1 = MutRng::new(Some((seed, None)));
1584+
let mut_rng_2 = MutRng::new(Some((seed, None)));
15731585

15741586
let mut rng_1 = mut_rng_1.0.lock().unwrap();
15751587
let mut rng_2 = mut_rng_2.0.lock().unwrap();
@@ -1589,6 +1601,38 @@ mod tests {
15891601
assert_ne!(rng_1.next_u64(), rng_2.next_u64())
15901602
}
15911603

1604+
#[test]
1605+
fn create_salted_mut_rng() {
1606+
let (_, pk1) = test_utils::get_random_keypair();
1607+
let (_, pk2) = test_utils::get_random_keypair();
1608+
1609+
let salted_rng_1 = MutRng::new(Some((42, Some(&pk1))));
1610+
let salted_rng_2 = MutRng::new(Some((42, Some(&pk2))));
1611+
1612+
let mut seq1 = Vec::new();
1613+
let mut seq2 = Vec::new();
1614+
1615+
let mut rng1 = salted_rng_1.0.lock().unwrap();
1616+
let mut rng2 = salted_rng_2.0.lock().unwrap();
1617+
1618+
for _ in 0..10 {
1619+
seq1.push(rng1.next_u64());
1620+
seq2.push(rng2.next_u64());
1621+
}
1622+
1623+
assert_ne!(seq1, seq2);
1624+
1625+
let salted_rng1_again = MutRng::new(Some((42, Some(&pk1))));
1626+
let mut rng1_again = salted_rng1_again.0.lock().unwrap();
1627+
let mut seq1_again = Vec::new();
1628+
1629+
for _ in 0..10 {
1630+
seq1_again.push(rng1_again.next_u64());
1631+
}
1632+
1633+
assert_eq!(seq1, seq1_again);
1634+
}
1635+
15921636
mock! {
15931637
pub Generator {}
15941638

simln-lib/src/random_activity.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ mod tests {
308308
#[test]
309309
fn test_new() {
310310
// Check that we need, at least, two nodes
311-
let rng = MutRng::new(Some(u64::MAX));
311+
let rng = MutRng::new(Some((u64::MAX, None)));
312312
for i in 0..2 {
313313
assert!(matches!(
314314
NetworkGraphView::new(create_nodes(i, 42 * (i as u64 + 1)), rng.clone()),
@@ -362,7 +362,7 @@ mod tests {
362362
nodes.extend(create_nodes(big_node_count, big_node_capacity));
363363
let big_node = nodes.last().unwrap().0.pubkey;
364364

365-
let rng = MutRng::new(Some(u64::MAX));
365+
let rng = MutRng::new(Some((u64::MAX, None)));
366366
let view = NetworkGraphView::new(nodes, rng).unwrap();
367367

368368
for _ in 0..10 {
@@ -380,7 +380,7 @@ mod tests {
380380
// For the payment activity generator to fail during construction either the provided capacity must fail validation or the exponential
381381
// distribution must fail building given the inputs. The former will be thoroughly tested in its own unit test, but we'll test some basic cases
382382
// here. Mainly, if the `capacity < expected_payment_amnt / 2`, the generator will fail building
383-
let rng = MutRng::new(Some(u64::MAX));
383+
let rng = MutRng::new(Some((u64::MAX, None)));
384384
let expected_payment = get_random_int(1, 100);
385385
assert!(RandomPaymentActivity::new(
386386
2 * expected_payment,
@@ -453,7 +453,7 @@ mod tests {
453453
// All of them will yield a sigma squared smaller than 0, which we have a sanity check for.
454454
let expected_payment = get_random_int(1, 100);
455455
let source_capacity = 2 * expected_payment;
456-
let rng = MutRng::new(Some(u64::MAX));
456+
let rng = MutRng::new(Some((u64::MAX, None)));
457457
let pag =
458458
RandomPaymentActivity::new(source_capacity, expected_payment, 1.0, rng).unwrap();
459459

0 commit comments

Comments
 (0)