#[test]
#[cfg(feature = "shuttle")]
fn test_node_with_random_operations() {
use lightning_signer::channel::ChannelId;
use lightning_signer::node::Node;
use lightning_signer::util::status::Status;
use lightning_signer::util::test_utils::make_node;
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use shuttle::sync::{Arc, Mutex};
use shuttle::thread;
struct TestState {
channel_ids: Mutex<Vec<ChannelId>>,
}
impl TestState {
fn new() -> Self {
TestState { channel_ids: Mutex::new(Vec::new()) }
}
fn add_channel(&self, channel_id: ChannelId) {
let mut ids = self.channel_ids.lock().unwrap();
ids.push(channel_id);
}
fn get_channel_id(&self, index: usize) -> Option<ChannelId> {
let ids = self.channel_ids.lock().unwrap();
ids.get(index).cloned()
}
fn remove_channel_id(&self, index: usize) -> Option<ChannelId> {
let mut ids = self.channel_ids.lock().unwrap();
if index < ids.len() {
Some(ids.remove(index))
} else {
None
}
}
fn channel_count(&self) -> usize {
let ids = self.channel_ids.lock().unwrap();
ids.len()
}
}
#[derive(Clone, Debug)]
enum Operation {
CreateChannel,
GetChannel(usize),
ForgetChannel(usize),
GetAllChannels,
}
fn perform_operation(
node: &Arc<Node>,
state: &Arc<TestState>,
op: &Operation,
) -> Result<(), Status> {
match op {
Operation::CreateChannel => {
let (channel_id, _) = node.new_channel_with_random_id(node)?;
state.add_channel(channel_id);
Ok(())
}
Operation::GetChannel(idx) => {
let channel_count = state.channel_count();
if channel_count > 0 {
let idx = idx % channel_count;
if let Some(channel_id) = state.get_channel_id(idx) {
let channel = node.get_channel(&channel_id)?;
let _lock = channel.lock().unwrap();
}
}
Ok(())
}
Operation::ForgetChannel(idx) => {
let channel_count = state.channel_count();
if channel_count > 0 {
let idx = idx % channel_count;
if let Some(channel_id) = state.remove_channel_id(idx) {
node.forget_channel(&channel_id)?;
}
}
Ok(())
}
Operation::GetAllChannels => {
let _channels = node.get_channels();
Ok(())
}
}
}
const OPS_PER_THREAD: usize = 32;
const THREAD_COUNT: usize = 4;
const SEED: u64 = 12345;
let mut rng = StdRng::seed_from_u64(SEED);
let thread_operations: Vec<Vec<Operation>> = (0..THREAD_COUNT)
.map(|_| {
(0..OPS_PER_THREAD)
.map(|_| {
let op_type = rng.gen_range(0..4);
match op_type {
0 => Operation::CreateChannel,
1 => Operation::GetChannel(rng.gen_range(0..5)),
2 => Operation::ForgetChannel(rng.gen_range(0..5)),
_ => Operation::GetAllChannels,
}
})
.collect::<Vec<_>>()
})
.collect();
shuttle::check_random(
move || {
let (_, node, _) = make_node();
let node = Arc::new(node);
let state = Arc::new(TestState::new());
let mut handles = Vec::new();
for (_, ops) in thread_operations.iter().enumerate() {
let node_clone = node.clone();
let state_clone = state.clone();
let ops = ops.clone();
let handle = thread::spawn(move || {
for op in &ops {
let _ = perform_operation(&node_clone, &state_clone, op);
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
},
1000,
);
}