diff --git a/blast_cli/src/configure.rs b/blast_cli/src/configure.rs index a66dcb8..8601d84 100644 --- a/blast_cli/src/configure.rs +++ b/blast_cli/src/configure.rs @@ -318,7 +318,7 @@ impl BlastTab for ConfigureTab { 2 } - fn update_runtime_data(&mut self) { + fn update_runtime_data(&mut self, _: Option>, _: Option>, _: Option>, _: u64, _: u64, _: f64) { return; } diff --git a/blast_cli/src/load.rs b/blast_cli/src/load.rs index c1a0a7e..2962c39 100644 --- a/blast_cli/src/load.rs +++ b/blast_cli/src/load.rs @@ -81,7 +81,7 @@ impl BlastTab for LoadTab { 1 } - fn update_runtime_data(&mut self) { + fn update_runtime_data(&mut self, _: Option>, _: Option>, _: Option>, _: u64, _: u64, _: f64) { return; } diff --git a/blast_cli/src/main.rs b/blast_cli/src/main.rs index 639885e..3198e43 100644 --- a/blast_cli/src/main.rs +++ b/blast_cli/src/main.rs @@ -315,7 +315,15 @@ async fn run(terminal: &mut Terminal, mut blast_cli: BlastCli) -> } if last_tick.elapsed() >= tick_rate { - current.update_runtime_data(); + let blast_stats = blast_cli.blast.get_stats().await; + match blast_stats { + Some(s) => { + let events_list: Vec = blast_cli.blast.get_events(); + let activity_list: Vec = blast_cli.blast.get_activity(); + current.update_runtime_data(Some(events_list), Some(activity_list), Some(s.stats), s.frame, s.total_frames, s.success_rate); + }, + None => {} + } last_tick = Instant::now(); } } diff --git a/blast_cli/src/new.rs b/blast_cli/src/new.rs index 463358f..71f3746 100644 --- a/blast_cli/src/new.rs +++ b/blast_cli/src/new.rs @@ -96,7 +96,7 @@ impl BlastTab for NewTab { 0 } - fn update_runtime_data(&mut self) { + fn update_runtime_data(&mut self, _: Option>, _: Option>, _: Option>, _: u64, _: u64, _: f64) { return; } diff --git a/blast_cli/src/run.rs b/blast_cli/src/run.rs index 952911f..cb7d00b 100644 --- a/blast_cli/src/run.rs +++ b/blast_cli/src/run.rs @@ -20,34 +20,20 @@ pub struct RunTab { pub progress: f64, pub window: [f64; 2], pub success_rate_data: [(f64, f64); 21], - add: bool + pub points: usize } impl RunTab { pub fn new() -> Self { - // TODO: this is a placeholder, initialize with the actual saved simulations - let mut events_list: Vec = Vec::new(); - let mut activity_list: Vec = Vec::new(); - let mut stats_list: Vec = Vec::new(); - events_list.push(String::from("10s OpenChannel (blast_lnd0000 --> blast_lnd0001: 2000msat)")); - events_list.push(String::from("20s CloseChannel (0)")); - activity_list.push(String::from("blast_ldk0000 --> blast_lnd0004: 2000msat, 5s")); - activity_list.push(String::from("blast_ldk0001 --> blast_lnd0005: 1000msat, 15s")); - activity_list.push(String::from("blast_ldk0002 --> blast_lnd0006: 8000msat, 10s")); - activity_list.push(String::from("blast_ldk0003 --> blast_lnd0007: 5000msat, 25s")); - stats_list.push(String::from("Number of Nodes: 15")); - stats_list.push(String::from("Total Payment Attempts: 76")); - stats_list.push(String::from("Payment Success Rate: 100%")); - Self { - events: StatefulList::with_items(events_list), - activity: StatefulList::with_items(activity_list), - stats: StatefulList::with_items(stats_list), + events: StatefulList::with_items(Vec::new()), + activity: StatefulList::with_items(Vec::new()), + stats: StatefulList::with_items(Vec::new()), current_section: RunSection::Events, progress: 0.0, window: [0.0, 20.0], success_rate_data: [(0.0, 0.0); 21], - add: true + points: 0 } } } @@ -77,7 +63,7 @@ impl BlastTab for RunTab { .block(Block::new().title("Simulation Progress:")) .filled_style(Style::default().fg(Color::LightBlue)) .line_set(symbols::line::THICK) - .ratio(self.progress/100.0); + .ratio(self.progress); frame.render_widget(line_gauge, layout[1]); let layout2 = Layout::new( @@ -244,22 +230,30 @@ impl BlastTab for RunTab { 3 } - fn update_runtime_data(&mut self) { - self.progress = self.progress + 1.0; - self.window[0] += 1.0; - self.window[1] += 1.0; - for i in 0..20 { - self.success_rate_data[i] = self.success_rate_data[i + 1]; - } + fn update_runtime_data(&mut self, events: Option>, activity: Option>, stats: Option>, frame: u64, num_frames: u64, succes_rate: f64) { + self.events.items = events.unwrap_or(Vec::new()); + self.activity.items = activity.unwrap_or(Vec::new()); + self.stats.items = stats.unwrap_or(Vec::new()); - if self.add { - self.success_rate_data[20] = (self.window[1], self.success_rate_data[19].1 + 5.0); + if frame >= num_frames { + self.progress = 1.0; } else { - self.success_rate_data[20] = (self.window[1], self.success_rate_data[19].1 - 5.0); + self.progress = frame as f64 / num_frames as f64; } - if self.window[1] % 10.0 == 0.0 { - self.add = !self.add; + if self.points <= 20 { + self.window[0] = 0.0; + self.window[1] = 20.0; + self.success_rate_data[self.points] = (frame as f64, succes_rate); + self.points += 1; + } else { + self.window[0] = frame as f64 - 20.0; + self.window[1] = frame as f64; + for i in 0..20 { + self.success_rate_data[i] = self.success_rate_data[i + 1]; + } + + self.success_rate_data[20] = (frame as f64, succes_rate); } } diff --git a/blast_cli/src/shared.rs b/blast_cli/src/shared.rs index 1fd44a9..d1ec436 100644 --- a/blast_cli/src/shared.rs +++ b/blast_cli/src/shared.rs @@ -96,7 +96,7 @@ pub trait BlastTab { fn close(&mut self); fn process(&mut self, key: KeyEvent) -> ProcessResult; fn get_index(&self) -> usize; - fn update_runtime_data(&mut self); + fn update_runtime_data(&mut self, events: Option>, activity: Option>, stats: Option>, frame: u64, num_frames: u64, succes_rate: f64); fn update_config_data(&mut self, data1: Option>, data2: Option>, data3: Option>, data4: Option>); fn esc_operation(&mut self) -> ProcessResult; } diff --git a/blast_core/src/blast_event_manager.rs b/blast_core/src/blast_event_manager.rs index 022b725..47a7356 100644 --- a/blast_core/src/blast_event_manager.rs +++ b/blast_core/src/blast_event_manager.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use std::fmt; use std::collections::HashMap; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::{AtomicBool, Ordering, AtomicU64}; use std::time::Duration; use std::fs::File; use std::io::BufReader; @@ -31,6 +31,7 @@ pub struct BlastEventManager { running: Arc, events: HashMap>, bitcoin_rpc: Option, + frame_number: Arc } impl Clone for BlastEventManager { @@ -41,7 +42,8 @@ impl Clone for BlastEventManager { bitcoin_rpc: match Client::new(crate::BITCOIND_RPC, Auth::UserPass(String::from(crate::BITCOIND_USER), String::from(crate::BITCOIND_PASS))) { Ok(c) => Some(c), Err(_) => None - } + }, + frame_number: self.frame_number.clone() } } } @@ -55,7 +57,8 @@ impl BlastEventManager { bitcoin_rpc: match Client::new(crate::BITCOIND_RPC, Auth::UserPass(String::from(crate::BITCOIND_USER), String::from(crate::BITCOIND_PASS))) { Ok(c) => Some(c), Err(_) => None - } + }, + frame_number: Arc::new(AtomicU64::new(0)) }; event_manager @@ -64,17 +67,26 @@ impl BlastEventManager { /// Start the event thread pub async fn start(&mut self, sender: Sender) -> Result<(), Error> { self.running.store(true, Ordering::SeqCst); - let mut frame_num = 0; + self.frame_number.store(0, Ordering::SeqCst); loop { // Make sure the blast simulation is still running and exit when it has shutdown if !self.running.load(Ordering::SeqCst) { + self.frame_number.store(0, Ordering::SeqCst); break; } - log::info!("BlastEventManager running frame number {}", frame_num); + let frame = self.frame_number.load(Ordering::SeqCst); + + if frame >= crate::TOTAL_FRAMES { + self.running.store(false, Ordering::SeqCst); + self.frame_number.store(0, Ordering::SeqCst); + break; + } + + log::info!("BlastEventManager running frame number {}", frame); // If it is time to mine new bocks, use the bitcoind RPC client to generate new blocks - if frame_num % MINE_RATE == 0 { + if frame % MINE_RATE == 0 { match crate::mine_blocks(&mut self.bitcoin_rpc, BLOCKS_PER_MINE) { Ok(_) => {}, Err(e) => return Err(anyhow::Error::msg(e)), @@ -82,8 +94,8 @@ impl BlastEventManager { } // If there are events to run this frame, get the list of events and send them - if self.events.contains_key(&frame_num) { - let current_events = &self.events[&frame_num]; + if self.events.contains_key(&frame) { + let current_events = &self.events[&frame]; let current_events_iter = current_events.iter(); for e in current_events_iter { log::info!("BlastEventManager sending event {}", e); @@ -94,7 +106,7 @@ impl BlastEventManager { } // Wait until the next frame - frame_num = frame_num + 1; + self.frame_number.fetch_add(1, Ordering::SeqCst); tokio::time::sleep(Duration::from_secs(FRAME_RATE)).await; } Ok(()) @@ -106,6 +118,14 @@ impl BlastEventManager { self.running.store(false, Ordering::SeqCst); } + /// Reset the event manager when the current blast network is shutdown + pub fn reset(&mut self) { + log::info!("BlastEventManager resetting."); + self.running.store(false, Ordering::SeqCst); + self.events.clear(); + self.frame_number.store(0, Ordering::SeqCst); + } + /// Get all of the events in json format pub fn get_event_json(&self) -> Result { match serde_json::to_string(&self.events) { @@ -217,6 +237,11 @@ impl BlastEventManager { events } + /// Get the current frame number + pub fn get_frame_number(&self) -> u64 { + self.frame_number.load(Ordering::SeqCst) + } + /// Validate that the correct args were given fn validate_args(&self, args: Option>, event: BlastEvent) -> Result, String> { match args { diff --git a/blast_core/src/blast_simln_manager.rs b/blast_core/src/blast_simln_manager.rs index 66997dd..bea1165 100644 --- a/blast_core/src/blast_simln_manager.rs +++ b/blast_core/src/blast_simln_manager.rs @@ -210,7 +210,7 @@ impl BlastSimLnManager { let sim = Simulation::new( clients, validated_activities, - None, + Some(crate::TOTAL_FRAMES as u32), EXPECTED_PAYMENT_AMOUNT, ACTIVITY_MULTIPLIER, Some(WriteResults { @@ -255,6 +255,13 @@ impl BlastSimLnManager { }; } + /// Reset the simln manager when the current blast network is shutdown + pub fn reset(&mut self) { + log::info!("BlastEventManager resetting."); + self.data.activity.clear(); + self.data.nodes.clear(); + } + /// Get all the nodes pub fn get_nodes(&self) -> Vec { let mut ids = Vec::::new(); @@ -267,4 +274,14 @@ impl BlastSimLnManager { } ids } + + pub fn get_success_rate(&self) -> f64 { + // TODO implement + 30.0 + } + + pub fn get_attempts(&self) -> u64 { + // TODO implement + 53 + } } diff --git a/blast_core/src/lib.rs b/blast_core/src/lib.rs index c595206..67fb72c 100644 --- a/blast_core/src/lib.rs +++ b/blast_core/src/lib.rs @@ -47,6 +47,9 @@ pub const BITCOIND_PASS: &str = "pass"; /// The directory to save simulations in pub const BLAST_SIM_DIR: &str = ".blast/blast_sims"; +/// TODO: make this configurable +pub const TOTAL_FRAMES: u64 = 40; + /// The Blast struct is the main public interface that can be used to run a simulation. pub struct Blast { blast_model_manager: BlastModelManager, @@ -63,6 +66,14 @@ pub struct BlastNetwork { model_map: HashMap } +/// The BlastStats struct holds statistics about the currently running simulation +pub struct BlastStats { + pub total_frames: u64, + pub frame: u64, + pub success_rate: f64, + pub stats: Vec +} + impl Blast { /// Create a new Blast object with a new BlastModelManager. pub fn new() -> Self { @@ -141,6 +152,9 @@ impl Blast { Err(e) => return Err(format!("Could not stop bitcoind: {}", e)), }; + self.blast_event_manager.reset(); + self.blast_simln_manager.reset(); + Ok(()) } @@ -419,6 +433,26 @@ impl Blast { self.blast_simln_manager.get_nodes() } + /// Get some basic simulation stats + pub async fn get_stats(&mut self) -> Option { + let f = self.blast_event_manager.get_frame_number(); + if f == 0 { + None + } else { + let sr = self.blast_simln_manager.get_success_rate(); + let pa = self.blast_simln_manager.get_attempts(); + let mut stats_list: Vec = Vec::new(); + stats_list.push(format!("Number of Nodes: {}", self.get_nodes().len())); + stats_list.push(format!("Number of Channels: {}", self.get_channels().await.len())); + stats_list.push(format!("Number of Events : {}", self.get_events().len())); + stats_list.push(format!("Payment Attempts: {}", pa)); + stats_list.push(format!("Payment Success Rate: {}", sr)); + + let stats = BlastStats{total_frames: TOTAL_FRAMES, frame: f, success_rate: sr, stats: stats_list}; + Some(stats) + } + } + /// Get the public key of a node pub async fn get_pub_key(&mut self, node_id: String) -> Result { match self.blast_model_manager.get_pub_key(node_id).await {