From 983bc50b6c4da11629051f87e9c55616fdfe844c Mon Sep 17 00:00:00 2001 From: Ignacio Duart Date: Sat, 25 Nov 2023 10:07:21 +0100 Subject: [PATCH 01/12] Create server to register and subscribe peer data --- Cargo.lock | 3 + Cargo.toml | 2 + crates/core/Cargo.toml | 5 +- crates/core/build.rs | 16 + crates/core/src/client_events/websocket.rs | 10 +- crates/core/src/config.rs | 83 +- crates/core/src/generated/mod.rs | 168 +++ .../core/src/generated/topology_generated.rs | 1216 +++++++++++++++++ crates/core/src/lib.rs | 3 + crates/core/src/node.rs | 19 +- .../core/src/node/network_bridge/in_memory.rs | 5 +- .../src/node/network_bridge/p2p_protoc.rs | 5 +- crates/core/src/node/p2p_impl.rs | 6 +- crates/core/src/node/testing_impl.rs | 11 +- .../src/node/testing_impl/inter_process.rs | 7 +- crates/core/src/operations/get.rs | 6 +- crates/core/src/operations/put.rs | 4 +- crates/core/src/operations/subscribe.rs | 6 +- crates/core/src/ring.rs | 9 +- .../{node/network_event_log.rs => tracing.rs} | 89 +- crates/fdev/Cargo.toml | 2 + crates/fdev/src/main.rs | 1 + crates/fdev/src/network_metrics_server.rs | 282 ++++ crates/fdev/src/testing.rs | 611 +-------- crates/fdev/src/testing/multiple_process.rs | 492 +++++++ crates/fdev/src/testing/network.rs | 3 + crates/fdev/src/testing/single_process.rs | 85 ++ schemas/flatbuffers/topology.fbs | 41 + stdlib | 2 +- 29 files changed, 2467 insertions(+), 725 deletions(-) create mode 100644 crates/core/build.rs create mode 100644 crates/core/src/generated/mod.rs create mode 100644 crates/core/src/generated/topology_generated.rs rename crates/core/src/{node/network_event_log.rs => tracing.rs} (93%) create mode 100644 crates/fdev/src/network_metrics_server.rs create mode 100644 crates/fdev/src/testing/multiple_process.rs create mode 100644 crates/fdev/src/testing/network.rs create mode 100644 crates/fdev/src/testing/single_process.rs create mode 100644 schemas/flatbuffers/topology.fbs diff --git a/Cargo.lock b/Cargo.lock index 47c9c74af..51e1b9499 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1453,9 +1453,11 @@ name = "fdev" version = "0.0.7" dependencies = [ "anyhow", + "axum", "bincode", "bs58", "clap", + "dashmap", "either", "fastrand 2.0.1", "freenet", @@ -1564,6 +1566,7 @@ dependencies = [ "directories", "either", "fastrand 2.0.1", + "flatbuffers", "freenet-stdlib", "futures", "itertools 0.11.0", diff --git a/Cargo.toml b/Cargo.toml index c9ebab582..ad062cd43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,9 @@ bs58 = "0.5" chacha20poly1305 = "0.10" chrono = { version = "0.4", default-features = true } clap = "4" +crossbeam = "0.8.2" ctrlc = { version = "3.4" } +dashmap = "^5.5" either = "1.8" fastrand = "2" futures = "0.3" diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 6ae5af7c9..f5c1fe6fe 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -28,13 +28,14 @@ chrono = { workspace = true } clap = { features = ["derive", "env"], workspace = true } config = { features = ["toml"], version = "0.13.0" } cookie = "0.17" -crossbeam = "0.8.2" +crossbeam = { workspace = true } ctrlc = { features = ["termination"], workspace = true } -dashmap = "^5.5" +dashmap = { workspace = true } delegate = "0.10" directories = "5" either = { features = ["serde"], workspace = true } fastrand = { workspace = true } +flatbuffers = "23.5.26" futures = "0.3.21" itertools = "0.11" libp2p = { default-features = false, features = ["autonat", "dns", "ed25519", "identify", "macros", "noise", "ping", "tcp", "tokio", "yamux"], version = "0.52.3" } diff --git a/crates/core/build.rs b/crates/core/build.rs new file mode 100644 index 000000000..12d9cb97e --- /dev/null +++ b/crates/core/build.rs @@ -0,0 +1,16 @@ +use std::process::Command; + +fn main() { + let status = Command::new("flatc") + .arg("--rust") + .arg("-o") + .arg("src/generated") + .arg("../../schemas/flatbuffers/topology.fbs") + .status(); + if let Err(err) = status { + println!("failed compiling flatbuffers schema: {err}"); + println!("refer to https://github.com/google/flatbuffers to install the flatc compiler"); + } else { + let _ = Command::new("cargo").arg("fmt").status(); + } +} diff --git a/crates/core/src/client_events/websocket.rs b/crates/core/src/client_events/websocket.rs index 25b43a9d5..c63872f3c 100644 --- a/crates/core/src/client_events/websocket.rs +++ b/crates/core/src/client_events/websocket.rs @@ -234,13 +234,13 @@ async fn websocket_interface( ) -> Result<(), DynError> { let (mut response_rx, client_id) = new_client_connection(&request_sender).await?; let (mut tx, mut rx) = ws.split(); - let listeners: Arc)>>> = + let contract_updates: Arc)>>> = Arc::new(Mutex::new(VecDeque::new())); loop { - let active_listeners = listeners.clone(); + let contract_updates_cp = contract_updates.clone(); let listeners_task = async move { loop { - let mut lock = active_listeners.lock().await; + let mut lock = contract_updates_cp.lock().await; let active_listeners = &mut *lock; for _ in 0..active_listeners.len() { if let Some((key, mut listener)) = active_listeners.pop_front() { @@ -287,7 +287,7 @@ async fn websocket_interface( tokio::select! { biased; msg = async { process_host_response(response_rx.recv().await, client_id, encoding_protoc, &mut tx).await } => { - let active_listeners = listeners.clone(); + let active_listeners = contract_updates.clone(); if let Some(NewSubscription { key, callback }) = msg? { tracing::debug!(cli_id = %client_id, contract = %key, "added new notification listener"); let active_listeners = &mut *active_listeners.lock().await; @@ -357,7 +357,7 @@ async fn process_client_request( Ok(Message::Binary(data)) => data, Ok(Message::Text(data)) => data.into_bytes(), Ok(Message::Close(_)) => return Err(None), - Ok(Message::Ping(_)) => return Ok(Some(Message::Pong(vec![0, 3, 2]))), + Ok(Message::Ping(ping)) => return Ok(Some(Message::Pong(ping))), Ok(m) => { tracing::debug!(msg = ?m, "received random message"); return Ok(None); diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index 1a089383a..4a12c76ed 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -234,7 +234,7 @@ impl Config { } fn load_conf() -> std::io::Result { - let settings = config::Config::builder() + let settings: config::Config = config::Config::builder() .add_source(config::Environment::with_prefix("FREENET")) .build() .unwrap(); @@ -367,85 +367,6 @@ pub fn set_logger() { return; } - tracer::init_tracer().expect("failed tracing initialization") - } -} - -#[cfg(feature = "trace")] -mod tracer { - use tracing_subscriber::{Layer, Registry}; - - use crate::DynError; - - pub fn init_tracer() -> Result<(), DynError> { - let default_filter = if cfg!(any(test, debug_assertions)) { - tracing_subscriber::filter::LevelFilter::DEBUG - } else { - tracing_subscriber::filter::LevelFilter::INFO - }; - let filter_layer = tracing_subscriber::EnvFilter::builder() - .with_default_directive(default_filter.into()) - .from_env_lossy() - .add_directive("stretto=off".parse().expect("infallible")) - .add_directive("sqlx=error".parse().expect("infallible")); - - // use opentelemetry_sdk::propagation::TraceContextPropagator; - use tracing_subscriber::layer::SubscriberExt; - - let disabled_logs = std::env::var("FREENET_DISABLE_LOGS").is_ok(); - let to_stderr = std::env::var("FREENET_LOG_TO_STDERR").is_ok(); - let layers = { - let fmt_layer = tracing_subscriber::fmt::layer().with_level(true); - let fmt_layer = if cfg!(any(test, debug_assertions)) { - fmt_layer.with_file(true).with_line_number(true) - } else { - fmt_layer - }; - let fmt_layer = if to_stderr { - fmt_layer.with_writer(std::io::stderr).boxed() - } else { - fmt_layer.boxed() - }; - - #[cfg(feature = "trace-ot")] - { - let disabled_ot_traces = std::env::var("FREENET_DISABLE_TRACES").is_ok(); - let identifier = if let Ok(peer) = std::env::var("FREENET_PEER_ID") { - format!("freenet-core-{peer}") - } else { - "freenet-core".to_string() - }; - let tracing_ot_layer = { - // Connect the Jaeger OT tracer with the tracing middleware - let ot_jaeger_tracer = - opentelemetry_jaeger::config::agent::AgentPipeline::default() - .with_service_name(identifier) - .install_simple()?; - // Get a tracer which will route OT spans to a Jaeger agent - tracing_opentelemetry::layer().with_tracer(ot_jaeger_tracer) - }; - if !disabled_logs && !disabled_ot_traces { - fmt_layer.and_then(tracing_ot_layer).boxed() - } else if !disabled_ot_traces { - tracing_ot_layer.boxed() - } else { - return Ok(()); - } - } - #[cfg(not(feature = "trace-ot"))] - { - if disabled_logs { - return Ok(()); - } - fmt_layer.boxed() - } - }; - let filtered = layers.with_filter(filter_layer); - // Create a subscriber which includes the tracing Jaeger OT layer and a fmt layer - let subscriber = Registry::default().with(filtered); - - // Set the global subscriber - tracing::subscriber::set_global_default(subscriber).expect("Error setting subscriber"); - Ok(()) + crate::tracing::tracer::init_tracer().expect("failed tracing initialization") } } diff --git a/crates/core/src/generated/mod.rs b/crates/core/src/generated/mod.rs new file mode 100644 index 000000000..60b8973da --- /dev/null +++ b/crates/core/src/generated/mod.rs @@ -0,0 +1,168 @@ +#![allow(unused_imports, dead_code, clippy::all)] + +#[cfg(feature = "trace")] +pub(crate) mod topology_generated; +pub use topology_generated::*; + +use crate::node::PeerId; + +pub trait TryFromFbs<'a>: Sized + 'a { + fn try_decode_fbs(buf: &'a [u8]) -> Result; +} + +pub enum PeerChange<'a> { + AddedConnection(topology::AddedConnection<'a>), + RemovedConnection(topology::RemovedConnection<'a>), + Error(topology::Error<'a>), +} + +impl PeerChange<'_> { + pub fn current_state_msg<'a>( + to: PeerId, + to_location: f64, + connections: impl Iterator, + ) -> Vec { + let mut buf = flatbuffers::FlatBufferBuilder::new(); + let to = buf.create_string(to.to_string().as_str()); + let connections = connections + .map(|(from, from_location)| { + let from = Some(buf.create_string(from.to_string().as_str())); + topology::AddedConnection::create( + &mut buf, + &topology::AddedConnectionArgs { + from, + from_location: *from_location, + to: Some(to), + to_location, + }, + ) + }) + .collect::>(); + let connections = buf.create_vector(&connections); + let msg = topology::PeerChange::create( + &mut buf, + &topology::PeerChangeArgs { + change_type: topology::PeerChangeType::AddedConnection, + change: None, + current_state: Some(connections), + }, + ); + buf.finish_minimal(msg); + buf.finished_data().to_vec() + } + + pub fn added_connection_msg( + (from, from_location): (PeerId, f64), + (to, to_location): (PeerId, f64), + ) -> Vec { + let mut buf = flatbuffers::FlatBufferBuilder::new(); + let from = Some(buf.create_string(from.to_string().as_str())); + let to = Some(buf.create_string(to.to_string().as_str())); + let add_conn = topology::AddedConnection::create( + &mut buf, + &topology::AddedConnectionArgs { + from, + from_location, + to, + to_location, + }, + ); + let msg = topology::PeerChange::create( + &mut buf, + &topology::PeerChangeArgs { + change_type: topology::PeerChangeType::AddedConnection, + change: Some(add_conn.as_union_value()), + current_state: None, + }, + ); + buf.finish_minimal(msg); + buf.finished_data().to_vec() + } + + pub fn removed_connection_msg(at: PeerId, from: PeerId) -> Vec { + let mut buf = flatbuffers::FlatBufferBuilder::new(); + let at = Some(buf.create_string(at.to_string().as_str())); + let from = Some(buf.create_string(from.to_string().as_str())); + let remove_conn = topology::RemovedConnection::create( + &mut buf, + &topology::RemovedConnectionArgs { at, from }, + ); + let msg = topology::PeerChange::create( + &mut buf, + &topology::PeerChangeArgs { + change_type: topology::PeerChangeType::RemovedConnection, + change: Some(remove_conn.as_union_value()), + current_state: None, + }, + ); + buf.finish_minimal(msg); + buf.finished_data().to_vec() + } +} + +impl<'a> TryFromFbs<'a> for PeerChange<'a> { + fn try_decode_fbs(buf: &'a [u8]) -> Result { + let req = flatbuffers::root::(&buf)?; + match req.change_type() { + topology::PeerChangeType::AddedConnection => { + let req = req.change_as_added_connection().ok_or_else(|| { + flatbuffers::InvalidFlatbuffer::InconsistentUnion { + field: "change_type", + field_type: "PeerChangeType", + error_trace: Default::default(), + } + })?; + Ok(Self::AddedConnection(req)) + } + topology::PeerChangeType::RemovedConnection => { + let req = req.change_as_removed_connection().ok_or_else(|| { + flatbuffers::InvalidFlatbuffer::InconsistentUnion { + field: "change_type", + field_type: "PeerChangeType", + error_trace: Default::default(), + } + })?; + Ok(Self::RemovedConnection(req)) + } + topology::PeerChangeType::Error => { + let req = req.change_as_error().ok_or_else(|| { + flatbuffers::InvalidFlatbuffer::InconsistentUnion { + field: "change_type", + field_type: "PeerChangeType", + error_trace: Default::default(), + } + })?; + Ok(Self::Error(req)) + } + _ => unreachable!(), + } + } +} + +impl<'a> topology::ControllerResponse<'a> { + pub fn into_fbs_bytes(result: Result, String>) -> Vec { + let mut builder = flatbuffers::FlatBufferBuilder::new(); + let (response_type, response) = match result { + Ok(msg) => { + let message = msg.as_ref().map(|s| builder.create_string(s)); + let response = topology::Ok::create(&mut builder, &topology::OkArgs { message }); + (topology::Response::Ok, response.as_union_value()) + } + Err(msg) => { + let message = Some(builder.create_string(&msg)); + let response = + topology::Error::create(&mut builder, &topology::ErrorArgs { message }); + (topology::Response::Error, response.as_union_value()) + } + }; + let response = topology::ControllerResponse::create( + &mut builder, + &topology::ControllerResponseArgs { + response_type, + response: Some(response), + }, + ); + builder.finish(response, None); + builder.finished_data().to_vec() + } +} diff --git a/crates/core/src/generated/topology_generated.rs b/crates/core/src/generated/topology_generated.rs new file mode 100644 index 000000000..0dc1bda18 --- /dev/null +++ b/crates/core/src/generated/topology_generated.rs @@ -0,0 +1,1216 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +// @generated + +use core::cmp::Ordering; +use core::mem; + +extern crate flatbuffers; +use self::flatbuffers::{EndianScalar, Follow}; + +#[allow(unused_imports, dead_code)] +pub mod topology { + + use core::cmp::Ordering; + use core::mem; + + extern crate flatbuffers; + use self::flatbuffers::{EndianScalar, Follow}; + + #[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." + )] + pub const ENUM_MIN_PEER_CHANGE_TYPE: u8 = 0; + #[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." + )] + pub const ENUM_MAX_PEER_CHANGE_TYPE: u8 = 3; + #[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." + )] + #[allow(non_camel_case_types)] + pub const ENUM_VALUES_PEER_CHANGE_TYPE: [PeerChangeType; 4] = [ + PeerChangeType::NONE, + PeerChangeType::AddedConnection, + PeerChangeType::RemovedConnection, + PeerChangeType::Error, + ]; + + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] + #[repr(transparent)] + pub struct PeerChangeType(pub u8); + #[allow(non_upper_case_globals)] + impl PeerChangeType { + pub const NONE: Self = Self(0); + pub const AddedConnection: Self = Self(1); + pub const RemovedConnection: Self = Self(2); + pub const Error: Self = Self(3); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 3; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::NONE, + Self::AddedConnection, + Self::RemovedConnection, + Self::Error, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NONE => Some("NONE"), + Self::AddedConnection => Some("AddedConnection"), + Self::RemovedConnection => Some("RemovedConnection"), + Self::Error => Some("Error"), + _ => None, + } + } + } + impl core::fmt::Debug for PeerChangeType { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } + } + impl<'a> flatbuffers::Follow<'a> for PeerChangeType { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = flatbuffers::read_scalar_at::(buf, loc); + Self(b) + } + } + + impl flatbuffers::Push for PeerChangeType { + type Output = PeerChangeType; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + flatbuffers::emplace_scalar::(dst, self.0); + } + } + + impl flatbuffers::EndianScalar for PeerChangeType { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } + } + + impl<'a> flatbuffers::Verifiable for PeerChangeType { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + u8::run_verifier(v, pos) + } + } + + impl flatbuffers::SimpleToVerifyInSlice for PeerChangeType {} + pub struct PeerChangeTypeUnionTableOffset {} + + #[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." + )] + pub const ENUM_MIN_RESPONSE: u8 = 0; + #[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." + )] + pub const ENUM_MAX_RESPONSE: u8 = 2; + #[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." + )] + #[allow(non_camel_case_types)] + pub const ENUM_VALUES_RESPONSE: [Response; 3] = [Response::NONE, Response::Error, Response::Ok]; + + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] + #[repr(transparent)] + pub struct Response(pub u8); + #[allow(non_upper_case_globals)] + impl Response { + pub const NONE: Self = Self(0); + pub const Error: Self = Self(1); + pub const Ok: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[Self::NONE, Self::Error, Self::Ok]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NONE => Some("NONE"), + Self::Error => Some("Error"), + Self::Ok => Some("Ok"), + _ => None, + } + } + } + impl core::fmt::Debug for Response { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } + } + impl<'a> flatbuffers::Follow<'a> for Response { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = flatbuffers::read_scalar_at::(buf, loc); + Self(b) + } + } + + impl flatbuffers::Push for Response { + type Output = Response; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + flatbuffers::emplace_scalar::(dst, self.0); + } + } + + impl flatbuffers::EndianScalar for Response { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } + } + + impl<'a> flatbuffers::Verifiable for Response { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + u8::run_verifier(v, pos) + } + } + + impl flatbuffers::SimpleToVerifyInSlice for Response {} + pub struct ResponseUnionTableOffset {} + + pub enum AddedConnectionOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct AddedConnection<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for AddedConnection<'a> { + type Inner = AddedConnection<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> AddedConnection<'a> { + pub const VT_FROM: flatbuffers::VOffsetT = 4; + pub const VT_FROM_LOCATION: flatbuffers::VOffsetT = 6; + pub const VT_TO: flatbuffers::VOffsetT = 8; + pub const VT_TO_LOCATION: flatbuffers::VOffsetT = 10; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + AddedConnection { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args AddedConnectionArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = AddedConnectionBuilder::new(_fbb); + builder.add_to_location(args.to_location); + builder.add_from_location(args.from_location); + if let Some(x) = args.to { + builder.add_to(x); + } + if let Some(x) = args.from { + builder.add_from(x); + } + builder.finish() + } + + #[inline] + pub fn from(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(AddedConnection::VT_FROM, None) + .unwrap() + } + } + #[inline] + pub fn from_location(&self) -> f64 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(AddedConnection::VT_FROM_LOCATION, Some(0.0)) + .unwrap() + } + } + #[inline] + pub fn to(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(AddedConnection::VT_TO, None) + .unwrap() + } + } + #[inline] + pub fn to_location(&self) -> f64 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(AddedConnection::VT_TO_LOCATION, Some(0.0)) + .unwrap() + } + } + } + + impl flatbuffers::Verifiable for AddedConnection<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("from", Self::VT_FROM, true)? + .visit_field::("from_location", Self::VT_FROM_LOCATION, false)? + .visit_field::>("to", Self::VT_TO, true)? + .visit_field::("to_location", Self::VT_TO_LOCATION, false)? + .finish(); + Ok(()) + } + } + pub struct AddedConnectionArgs<'a> { + pub from: Option>, + pub from_location: f64, + pub to: Option>, + pub to_location: f64, + } + impl<'a> Default for AddedConnectionArgs<'a> { + #[inline] + fn default() -> Self { + AddedConnectionArgs { + from: None, // required field + from_location: 0.0, + to: None, // required field + to_location: 0.0, + } + } + } + + pub struct AddedConnectionBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> AddedConnectionBuilder<'a, 'b> { + #[inline] + pub fn add_from(&mut self, from: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(AddedConnection::VT_FROM, from); + } + #[inline] + pub fn add_from_location(&mut self, from_location: f64) { + self.fbb_ + .push_slot::(AddedConnection::VT_FROM_LOCATION, from_location, 0.0); + } + #[inline] + pub fn add_to(&mut self, to: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(AddedConnection::VT_TO, to); + } + #[inline] + pub fn add_to_location(&mut self, to_location: f64) { + self.fbb_ + .push_slot::(AddedConnection::VT_TO_LOCATION, to_location, 0.0); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, + ) -> AddedConnectionBuilder<'a, 'b> { + let start = _fbb.start_table(); + AddedConnectionBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, AddedConnection::VT_FROM, "from"); + self.fbb_.required(o, AddedConnection::VT_TO, "to"); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for AddedConnection<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("AddedConnection"); + ds.field("from", &self.from()); + ds.field("from_location", &self.from_location()); + ds.field("to", &self.to()); + ds.field("to_location", &self.to_location()); + ds.finish() + } + } + pub enum RemovedConnectionOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct RemovedConnection<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for RemovedConnection<'a> { + type Inner = RemovedConnection<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> RemovedConnection<'a> { + pub const VT_AT: flatbuffers::VOffsetT = 4; + pub const VT_FROM: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + RemovedConnection { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args RemovedConnectionArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = RemovedConnectionBuilder::new(_fbb); + if let Some(x) = args.from { + builder.add_from(x); + } + if let Some(x) = args.at { + builder.add_at(x); + } + builder.finish() + } + + #[inline] + pub fn at(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(RemovedConnection::VT_AT, None) + .unwrap() + } + } + #[inline] + pub fn from(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(RemovedConnection::VT_FROM, None) + .unwrap() + } + } + } + + impl flatbuffers::Verifiable for RemovedConnection<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("at", Self::VT_AT, true)? + .visit_field::>("from", Self::VT_FROM, true)? + .finish(); + Ok(()) + } + } + pub struct RemovedConnectionArgs<'a> { + pub at: Option>, + pub from: Option>, + } + impl<'a> Default for RemovedConnectionArgs<'a> { + #[inline] + fn default() -> Self { + RemovedConnectionArgs { + at: None, // required field + from: None, // required field + } + } + } + + pub struct RemovedConnectionBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> RemovedConnectionBuilder<'a, 'b> { + #[inline] + pub fn add_at(&mut self, at: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(RemovedConnection::VT_AT, at); + } + #[inline] + pub fn add_from(&mut self, from: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(RemovedConnection::VT_FROM, from); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, + ) -> RemovedConnectionBuilder<'a, 'b> { + let start = _fbb.start_table(); + RemovedConnectionBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, RemovedConnection::VT_AT, "at"); + self.fbb_.required(o, RemovedConnection::VT_FROM, "from"); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for RemovedConnection<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("RemovedConnection"); + ds.field("at", &self.at()); + ds.field("from", &self.from()); + ds.finish() + } + } + pub enum ErrorOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct Error<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for Error<'a> { + type Inner = Error<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> Error<'a> { + pub const VT_MESSAGE: flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Error { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args ErrorArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = ErrorBuilder::new(_fbb); + if let Some(x) = args.message { + builder.add_message(x); + } + builder.finish() + } + + #[inline] + pub fn message(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Error::VT_MESSAGE, None) + .unwrap() + } + } + } + + impl flatbuffers::Verifiable for Error<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>( + "message", + Self::VT_MESSAGE, + true, + )? + .finish(); + Ok(()) + } + } + pub struct ErrorArgs<'a> { + pub message: Option>, + } + impl<'a> Default for ErrorArgs<'a> { + #[inline] + fn default() -> Self { + ErrorArgs { + message: None, // required field + } + } + } + + pub struct ErrorBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> ErrorBuilder<'a, 'b> { + #[inline] + pub fn add_message(&mut self, message: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Error::VT_MESSAGE, message); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> ErrorBuilder<'a, 'b> { + let start = _fbb.start_table(); + ErrorBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, Error::VT_MESSAGE, "message"); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for Error<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Error"); + ds.field("message", &self.message()); + ds.finish() + } + } + pub enum PeerChangeOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct PeerChange<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for PeerChange<'a> { + type Inner = PeerChange<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> PeerChange<'a> { + pub const VT_CURRENT_STATE: flatbuffers::VOffsetT = 4; + pub const VT_CHANGE_TYPE: flatbuffers::VOffsetT = 6; + pub const VT_CHANGE: flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + PeerChange { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args PeerChangeArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = PeerChangeBuilder::new(_fbb); + if let Some(x) = args.change { + builder.add_change(x); + } + if let Some(x) = args.current_state { + builder.add_current_state(x); + } + builder.add_change_type(args.change_type); + builder.finish() + } + + #[inline] + pub fn current_state( + &self, + ) -> Option>>> + { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(PeerChange::VT_CURRENT_STATE, None) + } + } + #[inline] + pub fn change_type(&self) -> PeerChangeType { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(PeerChange::VT_CHANGE_TYPE, Some(PeerChangeType::NONE)) + .unwrap() + } + } + #[inline] + pub fn change(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + PeerChange::VT_CHANGE, + None, + ) + } + } + #[inline] + #[allow(non_snake_case)] + pub fn change_as_added_connection(&self) -> Option> { + if self.change_type() == PeerChangeType::AddedConnection { + self.change().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { AddedConnection::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn change_as_removed_connection(&self) -> Option> { + if self.change_type() == PeerChangeType::RemovedConnection { + self.change().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { RemovedConnection::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn change_as_error(&self) -> Option> { + if self.change_type() == PeerChangeType::Error { + self.change().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { Error::init_from_table(t) } + }) + } else { + None + } + } + } + + impl flatbuffers::Verifiable for PeerChange<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>>>("current_state", Self::VT_CURRENT_STATE, false)? + .visit_union::("change_type", Self::VT_CHANGE_TYPE, "change", Self::VT_CHANGE, false, |key, v, pos| { + match key { + PeerChangeType::AddedConnection => v.verify_union_variant::>("PeerChangeType::AddedConnection", pos), + PeerChangeType::RemovedConnection => v.verify_union_variant::>("PeerChangeType::RemovedConnection", pos), + PeerChangeType::Error => v.verify_union_variant::>("PeerChangeType::Error", pos), + _ => Ok(()), + } + })? + .finish(); + Ok(()) + } + } + pub struct PeerChangeArgs<'a> { + pub current_state: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub change_type: PeerChangeType, + pub change: Option>, + } + impl<'a> Default for PeerChangeArgs<'a> { + #[inline] + fn default() -> Self { + PeerChangeArgs { + current_state: None, + change_type: PeerChangeType::NONE, + change: None, + } + } + } + + pub struct PeerChangeBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> PeerChangeBuilder<'a, 'b> { + #[inline] + pub fn add_current_state( + &mut self, + current_state: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_.push_slot_always::>( + PeerChange::VT_CURRENT_STATE, + current_state, + ); + } + #[inline] + pub fn add_change_type(&mut self, change_type: PeerChangeType) { + self.fbb_.push_slot::( + PeerChange::VT_CHANGE_TYPE, + change_type, + PeerChangeType::NONE, + ); + } + #[inline] + pub fn add_change(&mut self, change: flatbuffers::WIPOffset) { + self.fbb_ + .push_slot_always::>(PeerChange::VT_CHANGE, change); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> PeerChangeBuilder<'a, 'b> { + let start = _fbb.start_table(); + PeerChangeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for PeerChange<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("PeerChange"); + ds.field("current_state", &self.current_state()); + ds.field("change_type", &self.change_type()); + match self.change_type() { + PeerChangeType::AddedConnection => { + if let Some(x) = self.change_as_added_connection() { + ds.field("change", &x) + } else { + ds.field( + "change", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + PeerChangeType::RemovedConnection => { + if let Some(x) = self.change_as_removed_connection() { + ds.field("change", &x) + } else { + ds.field( + "change", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + PeerChangeType::Error => { + if let Some(x) = self.change_as_error() { + ds.field("change", &x) + } else { + ds.field( + "change", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + _ => { + let x: Option<()> = None; + ds.field("change", &x) + } + }; + ds.finish() + } + } + pub enum OkOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct Ok<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for Ok<'a> { + type Inner = Ok<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> Ok<'a> { + pub const VT_MESSAGE: flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Ok { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args OkArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = OkBuilder::new(_fbb); + if let Some(x) = args.message { + builder.add_message(x); + } + builder.finish() + } + + #[inline] + pub fn message(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Ok::VT_MESSAGE, None) + } + } + } + + impl flatbuffers::Verifiable for Ok<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>( + "message", + Self::VT_MESSAGE, + false, + )? + .finish(); + Ok(()) + } + } + pub struct OkArgs<'a> { + pub message: Option>, + } + impl<'a> Default for OkArgs<'a> { + #[inline] + fn default() -> Self { + OkArgs { message: None } + } + } + + pub struct OkBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> OkBuilder<'a, 'b> { + #[inline] + pub fn add_message(&mut self, message: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Ok::VT_MESSAGE, message); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> OkBuilder<'a, 'b> { + let start = _fbb.start_table(); + OkBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for Ok<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Ok"); + ds.field("message", &self.message()); + ds.finish() + } + } + pub enum ControllerResponseOffset {} + #[derive(Copy, Clone, PartialEq)] + + pub struct ControllerResponse<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for ControllerResponse<'a> { + type Inner = ControllerResponse<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } + + impl<'a> ControllerResponse<'a> { + pub const VT_RESPONSE_TYPE: flatbuffers::VOffsetT = 4; + pub const VT_RESPONSE: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + ControllerResponse { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args ControllerResponseArgs, + ) -> flatbuffers::WIPOffset> { + let mut builder = ControllerResponseBuilder::new(_fbb); + if let Some(x) = args.response { + builder.add_response(x); + } + builder.add_response_type(args.response_type); + builder.finish() + } + + #[inline] + pub fn response_type(&self) -> Response { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(ControllerResponse::VT_RESPONSE_TYPE, Some(Response::NONE)) + .unwrap() + } + } + #[inline] + pub fn response(&self) -> flatbuffers::Table<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + ControllerResponse::VT_RESPONSE, + None, + ) + .unwrap() + } + } + #[inline] + #[allow(non_snake_case)] + pub fn response_as_error(&self) -> Option> { + if self.response_type() == Response::Error { + let u = self.response(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + Some(unsafe { Error::init_from_table(u) }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn response_as_ok(&self) -> Option> { + if self.response_type() == Response::Ok { + let u = self.response(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + Some(unsafe { Ok::init_from_table(u) }) + } else { + None + } + } + } + + impl flatbuffers::Verifiable for ControllerResponse<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_union::( + "response_type", + Self::VT_RESPONSE_TYPE, + "response", + Self::VT_RESPONSE, + true, + |key, v, pos| match key { + Response::Error => v + .verify_union_variant::>( + "Response::Error", + pos, + ), + Response::Ok => v.verify_union_variant::>( + "Response::Ok", + pos, + ), + _ => Ok(()), + }, + )? + .finish(); + Ok(()) + } + } + pub struct ControllerResponseArgs { + pub response_type: Response, + pub response: Option>, + } + impl<'a> Default for ControllerResponseArgs { + #[inline] + fn default() -> Self { + ControllerResponseArgs { + response_type: Response::NONE, + response: None, // required field + } + } + } + + pub struct ControllerResponseBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> ControllerResponseBuilder<'a, 'b> { + #[inline] + pub fn add_response_type(&mut self, response_type: Response) { + self.fbb_.push_slot::( + ControllerResponse::VT_RESPONSE_TYPE, + response_type, + Response::NONE, + ); + } + #[inline] + pub fn add_response( + &mut self, + response: flatbuffers::WIPOffset, + ) { + self.fbb_.push_slot_always::>( + ControllerResponse::VT_RESPONSE, + response, + ); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, + ) -> ControllerResponseBuilder<'a, 'b> { + let start = _fbb.start_table(); + ControllerResponseBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_ + .required(o, ControllerResponse::VT_RESPONSE, "response"); + flatbuffers::WIPOffset::new(o.value()) + } + } + + impl core::fmt::Debug for ControllerResponse<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("ControllerResponse"); + ds.field("response_type", &self.response_type()); + match self.response_type() { + Response::Error => { + if let Some(x) = self.response_as_error() { + ds.field("response", &x) + } else { + ds.field( + "response", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + Response::Ok => { + if let Some(x) = self.response_as_ok() { + ds.field("response", &x) + } else { + ds.field( + "response", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + _ => { + let x: Option<()> = None; + ds.field("response", &x) + } + }; + ds.finish() + } + } +} // pub mod topology diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index cd837abcc..63c7fd215 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,6 +1,7 @@ pub(crate) mod client_events; pub mod config; mod contract; +pub mod generated; mod message; mod node; mod operations; @@ -11,6 +12,7 @@ mod runtime; #[cfg(feature = "websocket")] pub mod server; mod topology; +mod tracing; pub mod util; type DynError = Box; @@ -29,6 +31,7 @@ pub mod dev_tool { pub use crate::config::Config; pub use client_events::{test::MemoryEventsGen, ClientEventsProxy, ClientId, OpenRequest}; pub use contract::{storages::Storage, Executor, OperationMode}; + pub use flatbuffers; pub use node::{ testing_impl::{EventChain, NodeLabel, SimNetwork, SimPeer}, InitPeerNode, InterProcessConnManager, NodeConfig, PeerCliConfig, PeerId, diff --git a/crates/core/src/node.rs b/crates/core/src/node.rs index 8bdbfdb0c..1d082691a 100644 --- a/crates/core/src/node.rs +++ b/crates/core/src/node.rs @@ -11,6 +11,7 @@ use std::{ fmt::Display, net::{IpAddr, Ipv4Addr}, path::PathBuf, + str::FromStr, sync::Arc, time::Duration, }; @@ -21,7 +22,7 @@ use libp2p::{identity, multiaddr::Protocol, Multiaddr, PeerId as Libp2pPeerId}; use serde::{Deserialize, Serialize}; use tracing::Instrument; -use self::{network_event_log::NetEventLog, p2p_impl::NodeP2P}; +use self::p2p_impl::NodeP2P; use crate::{ client_events::{BoxedClient, ClientEventsProxy, ClientId, OpenRequest}, config::Config, @@ -37,6 +38,7 @@ use crate::{ }, ring::{Location, PeerKeyLocation}, router::{RouteEvent, RouteOutcome}, + tracing::{EventRegister, NetEventLog, NetEventRegister}, util::ExponentialBackoff, DynError, }; @@ -44,13 +46,10 @@ use crate::{ use crate::operations::handle_op_request; pub use network_bridge::inter_process::InterProcessConnManager; pub(crate) use network_bridge::{ConnectionError, EventLoopNotificationsSender, NetworkBridge}; -#[cfg(feature = "trace-ot")] -pub(crate) use network_event_log::CombinedRegister; -pub(crate) use network_event_log::{EventRegister, NetEventRegister}; + pub(crate) use op_state_manager::{OpManager, OpNotAvailable}; mod network_bridge; -mod network_event_log; mod op_state_manager; mod p2p_impl; pub(crate) mod testing_impl; @@ -191,7 +190,7 @@ impl NodeConfig { let event_register = { #[cfg(feature = "trace-ot")] { - use super::node::network_event_log::OTEventRegister; + use super::tracing::{CombinedRegister, OTEventRegister}; CombinedRegister::new([ Box::new(EventRegister::new()), Box::new(OTEventRegister::new()), @@ -794,6 +793,14 @@ impl From for PeerId { } } +impl FromStr for PeerId { + type Err = libp2p_identity::ParseError; + + fn from_str(s: &str) -> Result { + Ok(Self(Libp2pPeerId::from_str(s)?)) + } +} + mod serialization { use libp2p::PeerId as Libp2pPeerId; use serde::{Deserialize, Deserializer, Serialize, Serializer}; diff --git a/crates/core/src/node/network_bridge/in_memory.rs b/crates/core/src/node/network_bridge/in_memory.rs index d4547fcf4..d70aaf139 100644 --- a/crates/core/src/node/network_bridge/in_memory.rs +++ b/crates/core/src/node/network_bridge/in_memory.rs @@ -16,9 +16,8 @@ use super::{ConnectionError, NetworkBridge, PeerId}; use crate::{ config::GlobalExecutor, message::NetMessage, - node::{ - network_event_log::NetEventLog, testing_impl::NetworkBridgeExt, NetEventRegister, OpManager, - }, + node::{testing_impl::NetworkBridgeExt, NetEventRegister, OpManager}, + tracing::NetEventLog, }; pub(in crate::node) struct MemoryConnManager { diff --git a/crates/core/src/node/network_bridge/p2p_protoc.rs b/crates/core/src/node/network_bridge/p2p_protoc.rs index 9296e6898..5586159be 100644 --- a/crates/core/src/node/network_bridge/p2p_protoc.rs +++ b/crates/core/src/node/network_bridge/p2p_protoc.rs @@ -46,11 +46,12 @@ use crate::{ contract::{ClientResponsesSender, ExecutorToEventLoopChannel, NetworkEventListenerHalve}, message::{NetMessage, NodeEvent, Transaction, TransactionType}, node::{ - handle_cancelled_op, join_ring_request, network_event_log::NetEventLog, process_message, - InitPeerNode, NetEventRegister, NodeConfig, OpManager, PeerId as FreenetPeerId, + handle_cancelled_op, join_ring_request, process_message, InitPeerNode, NetEventRegister, + NodeConfig, OpManager, PeerId as FreenetPeerId, }, operations::OpError, ring::PeerKeyLocation, + tracing::NetEventLog, util::IterExt, }; diff --git a/crates/core/src/node/p2p_impl.rs b/crates/core/src/node/p2p_impl.rs index 9ce45d30c..1f247ca28 100644 --- a/crates/core/src/node/p2p_impl.rs +++ b/crates/core/src/node/p2p_impl.rs @@ -177,7 +177,7 @@ mod test { client_events::test::MemoryEventsGen, config::GlobalExecutor, contract::MemoryContractHandler, - node::{network_event_log, testing_impl::get_free_port, InitPeerNode}, + node::{testing_impl::get_free_port, InitPeerNode}, ring::Location, }; @@ -238,7 +238,7 @@ mod test { config, peer1_key, [Box::new(user_events)], - network_event_log::TestEventListener::new(), + crate::tracing::TestEventListener::new(), "ping-listener".into(), ) .await?, @@ -259,7 +259,7 @@ mod test { config, peer2_key, [Box::new(user_events)], - network_event_log::TestEventListener::new(), + crate::tracing::TestEventListener::new(), "ping-dialer".into(), ) .await diff --git a/crates/core/src/node/testing_impl.rs b/crates/core/src/node/testing_impl.rs index 44e63fbc9..8e39cbccb 100644 --- a/crates/core/src/node/testing_impl.rs +++ b/crates/core/src/node/testing_impl.rs @@ -17,14 +17,15 @@ use tokio::sync::{mpsc, watch}; use tracing::{info, Instrument}; #[cfg(feature = "trace-ot")] -use crate::node::network_event_log::CombinedRegister; +use crate::tracing::CombinedRegister; use crate::{ client_events::test::{MemoryEventsGen, RandomEventGenerator}, config::GlobalExecutor, contract, message::{NetMessage, NodeEvent}, - node::{network_event_log::TestEventListener, InitPeerNode, NetEventRegister, NodeConfig}, + node::{InitPeerNode, NetEventRegister, NodeConfig}, ring::{Distance, Location, PeerKeyLocation}, + tracing::TestEventListener, }; mod in_memory; @@ -425,7 +426,7 @@ impl SimNetwork { let event_listener = { #[cfg(feature = "trace-ot")] { - use super::network_event_log::OTEventRegister; + use crate::tracing::OTEventRegister; CombinedRegister::new([ self.event_listener.trait_clone(), Box::new(OTEventRegister::new()), @@ -483,7 +484,7 @@ impl SimNetwork { let event_listener = { #[cfg(feature = "trace-ot")] { - use super::network_event_log::OTEventRegister; + use crate::tracing::OTEventRegister; CombinedRegister::new([ self.event_listener.trait_clone(), Box::new(OTEventRegister::new()), @@ -1109,7 +1110,7 @@ where NodeEvent::DropConnection(peer) => { tracing::info!("Dropping connection to {peer}"); event_register.register_events(Either::Left( - crate::node::network_event_log::NetEventLog::disconnected(&peer), + crate::tracing::NetEventLog::disconnected(&peer), )); op_storage.ring.prune_connection(peer); continue; diff --git a/crates/core/src/node/testing_impl/inter_process.rs b/crates/core/src/node/testing_impl/inter_process.rs index 48824a63e..a66b44b09 100644 --- a/crates/core/src/node/testing_impl/inter_process.rs +++ b/crates/core/src/node/testing_impl/inter_process.rs @@ -9,8 +9,9 @@ use crate::{ dev_tool::{ClientEventsProxy, NodeConfig}, node::{ network_bridge::{inter_process::InterProcessConnManager, EventLoopNotifications}, - EventRegister, NetEventRegister, OpManager, + OpManager, }, + tracing::{EventRegister, NetEventRegister}, }; #[derive(Serialize, Deserialize)] @@ -27,8 +28,8 @@ impl SimPeer { let event_register = { #[cfg(feature = "trace-ot")] { - use crate::node::network_event_log::OTEventRegister; - crate::node::CombinedRegister::new([ + use crate::tracing::{CombinedRegister, OTEventRegister}; + CombinedRegister::new([ Box::new(EventRegister::new()), Box::new(OTEventRegister::new()), ]) diff --git a/crates/core/src/operations/get.rs b/crates/core/src/operations/get.rs index ce7ab17d6..15d0f4417 100644 --- a/crates/core/src/operations/get.rs +++ b/crates/core/src/operations/get.rs @@ -277,7 +277,7 @@ impl Operation for GetOp { let new_htl = htl - 1; let Some(new_target) = op_storage .ring - .closest_caching(&key, [&sender.peer].as_slice()) + .closest_potentially_caching(&key, [&sender.peer].as_slice()) else { tracing::warn!( tx = %id, @@ -398,7 +398,7 @@ impl Operation for GetOp { skip_list.push(target.peer); if let Some(target) = op_storage .ring - .closest_caching(key, skip_list.as_slice()) + .closest_potentially_caching(key, skip_list.as_slice()) .into_iter() .next() { @@ -713,7 +713,7 @@ pub(crate) async fn request_get( ( op_storage .ring - .closest_caching(key, EMPTY) + .closest_potentially_caching(key, EMPTY) .into_iter() .next() .ok_or(RingError::EmptyRing)?, diff --git a/crates/core/src/operations/put.rs b/crates/core/src/operations/put.rs index d09ac6831..04709e8a2 100644 --- a/crates/core/src/operations/put.rs +++ b/crates/core/src/operations/put.rs @@ -675,7 +675,7 @@ pub(crate) async fn request_put( // - and the value to put let target = op_storage .ring - .closest_caching(&key, [&sender.peer].as_slice()) + .closest_potentially_caching(&key, [&sender.peer].as_slice()) .into_iter() .next() .ok_or(RingError::EmptyRing)?; @@ -771,7 +771,7 @@ async fn forward_changes( let key = contract.key(); let contract_loc = Location::from(&key); const EMPTY: &[PeerId] = &[]; - let forward_to = op_storage.ring.closest_caching(&key, EMPTY); + let forward_to = op_storage.ring.closest_potentially_caching(&key, EMPTY); let own_loc = op_storage.ring.own_location().location.expect("infallible"); if let Some(peer) = forward_to { let other_loc = peer.location.as_ref().expect("infallible"); diff --git a/crates/core/src/operations/subscribe.rs b/crates/core/src/operations/subscribe.rs index 7bf7a5fdd..b75acadb1 100644 --- a/crates/core/src/operations/subscribe.rs +++ b/crates/core/src/operations/subscribe.rs @@ -151,7 +151,7 @@ impl Operation for SubscribeOp { let Some(new_target) = op_storage .ring - .closest_caching(key, [&sender.peer].as_slice()) + .closest_potentially_caching(key, [&sender.peer].as_slice()) else { tracing::warn!(tx = %id, "No peer found while trying getting contract {key}"); return Err(OpError::RingError(RingError::NoCachingPeers(key.clone()))); @@ -231,7 +231,7 @@ impl Operation for SubscribeOp { skip_list.push(sender.peer); if let Some(target) = op_storage .ring - .closest_caching(key, skip_list.as_slice()) + .closest_potentially_caching(key, skip_list.as_slice()) .into_iter() .next() { @@ -351,7 +351,7 @@ pub(crate) async fn request_subscribe( ( op_storage .ring - .closest_caching(key, EMPTY) + .closest_potentially_caching(key, EMPTY) .into_iter() .next() .ok_or_else(|| RingError::NoCachingPeers(key.clone()))?, diff --git a/crates/core/src/ring.rs b/crates/core/src/ring.rs index 36edef23f..1df1634a1 100644 --- a/crates/core/src/ring.rs +++ b/crates/core/src/ring.rs @@ -39,13 +39,12 @@ use tracing::Instrument; use crate::message::TransactionType; use crate::topology::{AcquisitionStrategy, TopologyManager}; +use crate::tracing::{EventRegister, NetEventRegister}; use crate::util::Contains; use crate::{ config::GlobalExecutor, message::Transaction, - node::{ - self, EventLoopNotificationsSender, EventRegister, NetEventRegister, NodeConfig, PeerId, - }, + node::{self, EventLoopNotificationsSender, NodeConfig, PeerId}, operations::connect, router::Router, DynError, @@ -320,7 +319,7 @@ impl Ring { let should_route = std::any::type_name::() == std::any::type_name::() || std::any::type_name::() - == std::any::type_name::>(); + == std::any::type_name::>(); #[cfg(not(feature = "trace-ot"))] let should_route = std::any::type_name::() == std::any::type_name::(); @@ -466,7 +465,7 @@ impl Ring { /// Return the most optimal peer caching a given contract. #[inline] - pub fn closest_caching( + pub fn closest_potentially_caching( &self, contract_key: &ContractKey, skip_list: impl Contains, diff --git a/crates/core/src/node/network_event_log.rs b/crates/core/src/tracing.rs similarity index 93% rename from crates/core/src/node/network_event_log.rs rename to crates/core/src/tracing.rs index 5e89b643f..f04b12425 100644 --- a/crates/core/src/node/network_event_log.rs +++ b/crates/core/src/tracing.rs @@ -14,11 +14,11 @@ use tokio::{ }, }; -use super::PeerId; use crate::{ config::GlobalExecutor, contract::StoreResponse, message::{NetMessage, Transaction}, + node::PeerId, operations::{connect, get::GetMsg, put::PutMsg, subscribe::SubscribeMsg}, ring::{Location, PeerKeyLocation}, router::RouteEvent, @@ -26,10 +26,10 @@ use crate::{ }; #[cfg(feature = "trace-ot")] -pub(super) use opentelemetry_tracer::OTEventRegister; -pub(super) use test::TestEventListener; +pub(crate) use opentelemetry_tracer::OTEventRegister; +pub(crate) use test::TestEventListener; -use super::OpManager; +use crate::node::OpManager; #[derive(Debug, Clone, Copy)] struct ListenerLogId(usize); @@ -773,7 +773,7 @@ mod opentelemetry_tracer { } #[derive(Clone)] - pub(in crate::node) struct OTEventRegister { + pub(crate) struct OTEventRegister { log_sender: mpsc::Sender, finished_tx_notifier: mpsc::Sender, } @@ -990,6 +990,85 @@ enum PutEvent { }, } +#[cfg(feature = "trace")] +pub(crate) mod tracer { + use tracing_subscriber::{Layer, Registry}; + + use crate::DynError; + + pub fn init_tracer() -> Result<(), DynError> { + let default_filter = if cfg!(any(test, debug_assertions)) { + tracing_subscriber::filter::LevelFilter::DEBUG + } else { + tracing_subscriber::filter::LevelFilter::INFO + }; + let filter_layer = tracing_subscriber::EnvFilter::builder() + .with_default_directive(default_filter.into()) + .from_env_lossy() + .add_directive("stretto=off".parse().expect("infallible")) + .add_directive("sqlx=error".parse().expect("infallible")); + + // use opentelemetry_sdk::propagation::TraceContextPropagator; + use tracing_subscriber::layer::SubscriberExt; + + let disabled_logs = std::env::var("FREENET_DISABLE_LOGS").is_ok(); + let to_stderr = std::env::var("FREENET_LOG_TO_STDERR").is_ok(); + let layers = { + let fmt_layer = tracing_subscriber::fmt::layer().with_level(true); + let fmt_layer = if cfg!(any(test, debug_assertions)) { + fmt_layer.with_file(true).with_line_number(true) + } else { + fmt_layer + }; + let fmt_layer = if to_stderr { + fmt_layer.with_writer(std::io::stderr).boxed() + } else { + fmt_layer.boxed() + }; + + #[cfg(feature = "trace-ot")] + { + let disabled_ot_traces = std::env::var("FREENET_DISABLE_TRACES").is_ok(); + let identifier = if let Ok(peer) = std::env::var("FREENET_PEER_ID") { + format!("freenet-core-{peer}") + } else { + "freenet-core".to_string() + }; + let tracing_ot_layer = { + // Connect the Jaeger OT tracer with the tracing middleware + let ot_jaeger_tracer = + opentelemetry_jaeger::config::agent::AgentPipeline::default() + .with_service_name(identifier) + .install_simple()?; + // Get a tracer which will route OT spans to a Jaeger agent + tracing_opentelemetry::layer().with_tracer(ot_jaeger_tracer) + }; + if !disabled_logs && !disabled_ot_traces { + fmt_layer.and_then(tracing_ot_layer).boxed() + } else if !disabled_ot_traces { + tracing_ot_layer.boxed() + } else { + return Ok(()); + } + } + #[cfg(not(feature = "trace-ot"))] + { + if disabled_logs { + return Ok(()); + } + fmt_layer.boxed() + } + }; + let filtered = layers.with_filter(filter_layer); + // Create a subscriber which includes the tracing Jaeger OT layer and a fmt layer + let subscriber = Registry::default().with(filtered); + + // Set the global subscriber + tracing::subscriber::set_global_default(subscriber).expect("Error setting subscriber"); + Ok(()) + } +} + pub(super) mod test { use std::{ collections::HashMap, diff --git a/crates/fdev/Cargo.toml b/crates/fdev/Cargo.toml index 004e29289..bcabed675 100644 --- a/crates/fdev/Cargo.toml +++ b/crates/fdev/Cargo.toml @@ -10,9 +10,11 @@ repository = "https://github.com/freenet/freenet" [dependencies] anyhow = "1" +axum = { default-features = false, features = ["headers", "http1", "matched-path", "query", "tower-log", "ws"], version = "0.6" } bincode = "1" bs58 = { workspace = true } clap = { workspace = true, features = ["derive", "env"] } +dashmap = { workspace = true } either = { workspace = true } fastrand = { workspace = true } futures = { workspace = true } diff --git a/crates/fdev/src/main.rs b/crates/fdev/src/main.rs index fe8b3d3db..278d6cccb 100644 --- a/crates/fdev/src/main.rs +++ b/crates/fdev/src/main.rs @@ -8,6 +8,7 @@ mod commands; mod config; mod inspect; mod new_package; +pub(crate) mod network_metrics_server; mod testing; mod util; mod wasm_runtime; diff --git a/crates/fdev/src/network_metrics_server.rs b/crates/fdev/src/network_metrics_server.rs new file mode 100644 index 000000000..eaaf5a201 --- /dev/null +++ b/crates/fdev/src/network_metrics_server.rs @@ -0,0 +1,282 @@ +use std::{net::Ipv4Addr, path::PathBuf, str::FromStr, sync::Arc}; + +use axum::{ + body::Body, + extract::{ + ws::{Message, WebSocket}, + State, WebSocketUpgrade, + }, + http::StatusCode, + response::{IntoResponse, Response}, + routing::get, + Router, Server, +}; +use dashmap::DashMap; +use freenet::{ + dev_tool::PeerId, + generated::{topology::ControllerResponse, PeerChange, TryFromFbs}, +}; +use futures::{SinkExt, StreamExt}; +use serde::{Deserialize, Serialize}; + +pub async fn run_server(data_dir: Option) -> anyhow::Result<()> { + const DEFAULT_PORT: u16 = 55010; + + let port = std::env::var("PORT") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(DEFAULT_PORT); + + let (changes, rx) = tokio::sync::broadcast::channel(100); + let router = Router::new() + .route("/", get(home)) + .route("/push-stats/", get(push_stats)) + .route("/pull-stats/", get(pull_stats)) + .with_state(Arc::new(ServerState { + changes, + peer_data: DashMap::new(), + })); + if let Some(data_dir) = data_dir { + tokio::task::spawn(async move { + if let Err(err) = record_saver(data_dir, rx).await { + tracing::error!(error = %err, "Record saver failed"); + } + }); + } + + Server::bind(&(Ipv4Addr::LOCALHOST, port).into()) + .serve(router.into_make_service()) + .await?; + Ok(()) +} + +async fn home() -> Response { + Response::builder() + .status(StatusCode::FOUND) + .header("Location", "/pull-stats/") + .body(Body::empty()) + .expect("should be valid response") + .into_response() +} + +async fn push_stats( + ws: WebSocketUpgrade, + State(state): State>, +) -> axum::response::Response { + let on_upgrade = move |ws: WebSocket| async move { + if let Err(error) = push_interface(ws, state).await { + tracing::error!("{error}"); + } + }; + ws.on_upgrade(on_upgrade) +} + +async fn push_interface(ws: WebSocket, state: Arc) -> anyhow::Result<()> { + let (mut tx, mut rx) = ws.split(); + while let Some(msg) = rx.next().await { + match msg { + Ok(msg) => { + let msg = match msg { + Message::Binary(data) => data, + Message::Text(data) => data.into_bytes(), + Message::Close(_) => break, + Message::Ping(ping) => { + tx.send(Message::Pong(ping)).await?; + continue; + } + _ => continue, + }; + match PeerChange::try_decode_fbs(&msg) { + Ok(PeerChange::Error(err)) => { + tracing::error!(error = %err.message(), "Received error from peer"); + break; + } + Ok(change) => { + if let Err(err) = state.save_record(change) { + tracing::error!(error = %err, "Failed saving report"); + tx.send(Message::Binary(ControllerResponse::into_fbs_bytes(Err( + format!("{err}"), + )))) + .await?; + } + } + Err(decoding_error) => { + tracing::error!(error = %decoding_error, "Failed to decode message"); + tx.send(Message::Binary(ControllerResponse::into_fbs_bytes(Err( + format!("{decoding_error}"), + )))) + .await?; + } + } + } + Err(e) => { + tracing::debug!("websocket error: {}", e); + break; + } + } + } + Ok(()) +} + +async fn pull_stats( + ws: WebSocketUpgrade, + State(state): State>, +) -> axum::response::Response { + let on_upgrade = move |ws: WebSocket| async move { + if let Err(error) = pull_interface(ws, state).await { + tracing::error!("{error}"); + } + }; + ws.on_upgrade(on_upgrade) +} + +async fn pull_interface(ws: WebSocket, state: Arc) -> anyhow::Result<()> { + let (mut tx, _) = ws.split(); + for peer in state.peer_data.iter() { + let msg = PeerChange::current_state_msg( + *peer.key(), + peer.value().location, + peer.value().connections.iter(), + ); + tx.send(Message::Binary(msg)).await?; + } + let mut changes = state.changes.subscribe(); + while let Ok(msg) = changes.recv().await { + match msg { + Change::AddedConnection { from, to } => { + let msg = PeerChange::added_connection_msg(from, to); + tx.send(Message::Binary(msg)).await?; + } + Change::RemovedConnection { from, at } => { + let msg = PeerChange::removed_connection_msg(at, from); + tx.send(Message::Binary(msg)).await?; + } + } + } + Ok(()) +} + +struct ServerState { + changes: tokio::sync::broadcast::Sender, + peer_data: DashMap, +} + +struct PeerData { + connections: Vec<(PeerId, f64)>, + location: f64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +enum Change { + AddedConnection { + from: (PeerId, f64), + to: (PeerId, f64), + }, + RemovedConnection { + from: PeerId, + at: PeerId, + }, +} + +impl ServerState { + fn save_record(&self, change: PeerChange<'_>) -> Result<(), anyhow::Error> { + match change { + PeerChange::AddedConnection(added) => { + let from_peer_id = PeerId::from_str(added.from())?; + let from_loc = added.from_location(); + + let to_peer_id = PeerId::from_str(added.to())?; + let to_loc = added.to_location(); + + match self.peer_data.entry(from_peer_id) { + dashmap::mapref::entry::Entry::Occupied(mut occ) => { + occ.get_mut().connections.push((to_peer_id, to_loc)); + } + dashmap::mapref::entry::Entry::Vacant(vac) => { + vac.insert(PeerData { + connections: vec![(to_peer_id, to_loc)], + location: from_loc, + }); + } + } + + match self.peer_data.entry(to_peer_id) { + dashmap::mapref::entry::Entry::Occupied(mut occ) => { + occ.get_mut().connections.push((from_peer_id, from_loc)); + } + dashmap::mapref::entry::Entry::Vacant(vac) => { + vac.insert(PeerData { + connections: vec![(from_peer_id, from_loc)], + location: to_loc, + }); + } + } + + let _ = self.changes.send(Change::AddedConnection { + from: (from_peer_id, from_loc), + to: (to_peer_id, to_loc), + }); + } + PeerChange::RemovedConnection(removed) => { + let from_peer_id = PeerId::from_str(removed.from())?; + let at_peer_id = PeerId::from_str(removed.at())?; + + if let Some(mut entry) = self.peer_data.get_mut(&from_peer_id) { + entry + .connections + .retain(|(peer_id, _)| peer_id != &at_peer_id); + } + + if let Some(mut entry) = self.peer_data.get_mut(&at_peer_id) { + entry + .connections + .retain(|(peer_id, _)| peer_id != &from_peer_id); + } + + let _ = self.changes.send(Change::RemovedConnection { + from: from_peer_id, + at: at_peer_id, + }); + } + _ => unreachable!(), + } + Ok(()) + } +} + +async fn record_saver( + data_dir: PathBuf, + mut incoming_rec: tokio::sync::broadcast::Receiver, +) -> anyhow::Result<()> { + use tokio::io::AsyncWriteExt; + if !data_dir.exists() { + std::fs::create_dir_all(&data_dir)?; + } + let mut fs = tokio::fs::OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(data_dir.join("network-metrics")) + .await?; + + let mut batch = Vec::with_capacity(1024); + while let Ok(record) = incoming_rec.recv().await { + batch.push(record); + if batch.len() > 100 { + let batch = std::mem::replace(&mut batch, Vec::with_capacity(1024)); + let result = tokio::task::spawn_blocking(move || { + let mut serialized = Vec::with_capacity(batch.len() * 128); + for rec in batch { + let rec = serde_json::to_vec(&rec).unwrap(); + serialized.push(rec); + } + serialized + }) + .await?; + for rec in result { + fs.write_all(&rec).await?; + } + } + } + Ok(()) +} diff --git a/crates/fdev/src/testing.rs b/crates/fdev/src/testing.rs index 0fd124ad5..cd3e175eb 100644 --- a/crates/fdev/src/testing.rs +++ b/crates/fdev/src/testing.rs @@ -1,8 +1,12 @@ -use std::time::Duration; +use std::{path::PathBuf, time::Duration}; use anyhow::Error; use freenet::dev_tool::SimNetwork; +mod multiple_process; +mod network; +mod single_process; + pub(crate) use multiple_process::Process; /// Testing framework for running Freenet network simulations. @@ -46,6 +50,9 @@ pub struct TestConfig { /// Time in milliseconds to wait for the next event to be executed. #[arg(long)] event_wait_time: Option, + /// If provided, the execution data will be saved in this directory. + #[arg(long)] + execution_data: Option, #[clap(subcommand)] /// Execution mode for the test. pub command: TestMode, @@ -92,6 +99,12 @@ pub enum TestMode { } pub(crate) async fn test_framework(base_config: TestConfig) -> anyhow::Result<(), Error> { + let exec_data_path = base_config.execution_data.clone(); + tokio::task::spawn(async move { + if let Err(err) = crate::network_metrics_server::run_server(exec_data_path).await { + tracing::error!(error = %err, "Network metrics server failed"); + } + }); match &base_config.command { TestMode::SingleProcess => { single_process::run(&base_config).await?; @@ -100,7 +113,7 @@ pub(crate) async fn test_framework(base_config: TestConfig) -> anyhow::Result<() multiple_process::run(&base_config, config).await?; } TestMode::Network => { - todo!() + network::run(&base_config).await?; } } Ok(()) @@ -130,597 +143,3 @@ async fn config_sim_network(base_config: &TestConfig) -> anyhow::Result Vec { - let mut args = Vec::new(); - - args.push("test".to_owned()); - - if let Some(name) = &self.name { - args.push("--name".to_owned()); - args.push(name.to_string()); - } - - args.push("--seed".to_owned()); - args.push(seed.to_string()); - - args.push("--gateways".to_owned()); - args.push(self.gateways.to_string()); - args.push("--nodes".to_owned()); - args.push(self.nodes.to_string()); - args.push("--ring-max-htl".to_owned()); - args.push(self.ring_max_htl.to_string()); - args.push("--rnd-if-htl-above".to_owned()); - args.push(self.rnd_if_htl_above.to_string()); - args.push("--max-connections".to_owned()); - args.push(self.max_connections.to_string()); - args.push("--min-connections".to_owned()); - args.push(self.min_connections.to_string()); - - if let Some(max_contract_number) = self.max_contract_number { - args.push("--max_contract_number".to_owned()); - args.push(max_contract_number.to_string()); - } - - args.push("multi-process".to_owned()); - args.push("--mode".to_owned()); - args.push("child".to_owned()); - - args - } - } - - #[derive(clap::Parser, Clone)] - pub struct MultiProcessConfig { - #[arg(long, default_value_t = Process::Supervisor)] - pub mode: Process, - #[arg(long)] - id: Option, - } - - #[derive(Default, Clone, clap::ValueEnum)] - pub enum Process { - #[default] - Supervisor, - Child, - } - - impl Display for Process { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Supervisor => write!(f, "supervisor"), - Self::Child => write!(f, "child"), - } - } - } - - pub(super) async fn run( - config: &super::TestConfig, - cmd_config: &MultiProcessConfig, - ) -> anyhow::Result<(), Error> { - match cmd_config.mode { - Process::Supervisor => supervisor(config).await, - Process::Child => { - child(config, cmd_config.id.expect("id should be set for child")).await - } - } - } - - async fn supervisor(config: &super::TestConfig) -> anyhow::Result<(), Error> { - let mut simulated_network = super::config_sim_network(config).await?; - simulated_network.debug(); // set to avoid deleting temp dirs created - let peers = simulated_network.build_peers(); - - // let (user_ev_controller, event_rx) = tokio::sync::watch::channel((0, PeerId::random())); - let (user_ev_controller, event_rx) = tokio::sync::mpsc::channel(1); - - let seed = config.seed(); - let mut supervisor = Supervisor { - processes: HashMap::new(), - queued: HashMap::new(), - sending: FuturesUnordered::new(), - responses: FuturesUnordered::new(), - event_rx: Some(event_rx), - }; - let cmd_args = config.subprocess_command(seed); - for (label, node) in &peers { - let mut subprocess = SubProcess::start(&cmd_args, label, node.peer_id)?; - subprocess.config(node).await?; - supervisor.processes.insert(node.peer_id, subprocess); - } - - let peers = peers - .into_iter() - .map(|(label, config)| (label, config.peer_id)) - .collect(); - let mut events = EventChain::new(peers, user_ev_controller, config.events, true); - let next_event_wait_time = config - .event_wait_time - .map(Duration::from_millis) - .unwrap_or(Duration::from_millis(200)); - let (connectivity_timeout, network_connection_percent) = - config.get_connection_check_params(); - let events_generated = tokio::task::spawn(async move { - tracing::info!( - "Waiting for network to be sufficiently connected ({}ms timeout, {}%)", - connectivity_timeout.as_millis(), - network_connection_percent * 100.0 - ); - simulated_network - .check_partial_connectivity(connectivity_timeout, network_connection_percent)?; - tracing::info!("Network is sufficiently connected, start sending events"); - while events.next().await.is_some() { - tokio::time::sleep(next_event_wait_time).await; - } - Ok::<_, super::Error>(()) - }); - - let ctrl_c = tokio::signal::ctrl_c(); - - let supervisor_task = tokio::task::spawn(supervisor.start_simulation()); - - tokio::pin!(events_generated); - tokio::pin!(supervisor_task); - tokio::pin!(ctrl_c); - - loop { - tokio::select! { - _ = &mut ctrl_c /* SIGINT handling */ => { - break; - } - res = &mut events_generated => { - match res? { - Ok(()) => { - tracing::info!("Test events generated successfully"); - *events_generated = tokio::task::spawn(futures::future::pending::>()); - continue; - } - Err(err) => { - return Err(err); - } - } - } - finalized = &mut supervisor_task => { - match finalized? { - Ok(_) => { - tracing::info!("Test finalized successfully"); - break; - } - Err(e) => { - tracing::error!("Test finalized with error: {}", e); - return Err(e); - } - } - } - } - } - - Ok(()) - } - - type ChildResponses = Vec<(PeerId, Vec)>; - - /// Event driver for the supervisor process. - struct Supervisor { - processes: HashMap, - queued: HashMap>, - sending: FuturesUnordered>>, - responses: - FuturesUnordered>>, - event_rx: Option>, - } - - impl Supervisor { - async fn start_simulation(mut self) -> anyhow::Result<()> { - let ctrl_c = tokio::signal::ctrl_c(); - tokio::pin!(ctrl_c); - - let mut event_rx = self.event_rx.take().expect("should be set"); - let mut finished_events = false; - - for child_stdout in self - .processes - .values_mut() - .map(|sp| sp.child.stdout.take().expect("should be set")) - { - self.responses - .push(SubProcess::get_child_responses(child_stdout).boxed()); - } - - loop { - tokio::select! { - _ = &mut ctrl_c /* SIGINT handling */ => { - break; - } - res = self.responses.next(), if !self.responses.is_empty() => { - let (child_stdout, responses) = match res { - Some(Ok(res)) => res, - Some(Err(err)) => { - tracing::error!("Error processing responses: {err}"); - return Err(err); - } - None => { - continue; - } - }; - self.responses.push(SubProcess::get_child_responses(child_stdout).boxed()); - self.process_responses(responses); - } - completed_send = self.sending.next(), if !self.sending.is_empty() => { - let mut subprocess = match completed_send { - Some(Ok(res)) => res, - Some(Err(err)) => { - tracing::error!("Error sending message: {err}"); - return Err(err); - } - None => { - continue; - } - }; - let peer_queue = &mut *self.queued.entry(subprocess.id).or_default(); - if !peer_queue.is_empty() { - let n = rand::thread_rng().gen_range(0..=peer_queue.len()); - let messages = peer_queue.drain(..n).collect::>(); - let task = async move { - let stdin = subprocess.child.stdin.as_mut().expect("not taken"); - tracing::debug!(peer = %subprocess.id, "Draining {} messages from queue", n + 1); - for pending in messages { - pending.send(stdin).await?; - } - Ok(subprocess) - }.boxed(); - self.sending.push(task); - } else { - self.processes.insert(subprocess.id, subprocess); - } - } - event = event_rx.recv(), if !finished_events => { - let Some((event, peer)) = event else { - tracing::info!("No more events"); - finished_events = true; - continue; - }; - let Some(mut subprocess) = self.processes.remove(&peer) else { - self.queued.entry(peer).or_default().push_back(IPCMessage::FiredEvent(event)); - continue; - }; - let mut pending = self.queued.remove(&peer).unwrap_or_default(); - pending.push_back(IPCMessage::FiredEvent(event)); - let task = async move { - for msg in pending { - msg.send(subprocess.child.stdin.as_mut().expect("not taken")) - .await?; - } - Ok(subprocess) - }.boxed(); - self.sending.push(task); - } - } - } - for (_, subprocess) in self.processes.drain() { - subprocess.close().await; - } - tracing::info!("Simulation finished"); - Ok(()) - } - - fn process_responses(&mut self, responses: ChildResponses) { - for (target, data) in responses { - if let Some(mut target) = self.processes.remove(&target) { - tracing::debug!(to = %target.id, "Sending message"); - let task = async move { - target.send_msg(IPCMessage::Data(data)).await?; - Ok(target) - } - .boxed(); - self.sending.push(task); - } else { - tracing::debug!(%target, "Queuing message"); - self.queued - .entry(target) - .or_default() - .push_back(IPCMessage::Data(data)); - } - } - } - } - - struct SubProcess { - child: tokio::process::Child, - id: PeerId, - } - - impl SubProcess { - fn start( - cmd_args: &[String], - label: &NodeLabel, - id: PeerId, - ) -> anyhow::Result { - let child = Command::new("fdev") - .kill_on_drop(true) - .args(cmd_args) - .arg("--id") - .arg(label.number().to_string()) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::inherit()) - .spawn()?; - Ok(Self { child, id }) - } - - async fn config(&mut self, config: &NodeConfig) -> anyhow::Result<(), Error> { - let input = self.get_input().ok_or_else(|| anyhow!("closed"))?; - let serialize = bincode::serialize(&config)?; - input - .write_all(&(serialize.len() as u32).to_le_bytes()) - .await?; - input.write_all(&serialize).await?; - Ok(()) - } - - #[inline] - async fn send_msg(&mut self, msg: IPCMessage) -> anyhow::Result<()> { - msg.send(self.child.stdin.as_mut().expect("not taken")) - .await?; - Ok(()) - } - - async fn get_child_responses( - mut stdout: ChildStdout, - ) -> anyhow::Result<(ChildStdout, Vec<(PeerId, Vec)>)> { - let mut returned = vec![]; - loop { - match InterProcessConnManager::pull_msg(&mut stdout).await { - Ok(Some((target, data))) => { - returned.push((target, data)); - } - Ok(None) if !returned.is_empty() => { - break; - } - Ok(None) => {} - Err(err) => { - return Err(err.into()); - } - } - } - tracing::debug!(len = %returned.len(), "Returning messages"); - Ok((stdout, returned)) - } - - async fn close(mut self) { - let _ = self.child.kill().await; - } - - #[inline] - fn get_input(&mut self) -> Option<&mut tokio::process::ChildStdin> { - self.child.stdin.as_mut() - } - } - - async fn child(config: &super::TestConfig, id: usize) -> anyhow::Result<()> { - // write logs to stderr so stdout and stdin are free of unexpected data - std::env::set_var("FREENET_LOG_TO_STDERR", "1"); - - let (user_ev_controller, mut receiver_ch) = - tokio::sync::watch::channel((0, PeerId::random())); - receiver_ch.borrow_and_update(); - let mut input = BufReader::new(tokio::io::stdin()); - let node_config = Child::get_config(&mut input).await?; - let this_child = Child { - input, - user_ev_controller, - peer_id: node_config.peer_id, - }; - std::env::set_var("FREENET_PEER_ID", node_config.peer_id.to_string()); - freenet::config::set_logger(); - let mut event_generator = MemoryEventsGen::::new_with_seed( - receiver_ch.clone(), - node_config.peer_id, - config.seed.expect("seed should be set for child process"), - ); - event_generator.rng_params( - id, - config.gateways + config.nodes, - config.max_contract_number.unwrap_or(config.nodes * 10), - config.events as usize, - ); - let config = SimPeer::from(node_config); - tokio::task::spawn(this_child.event_loop()); - config.start_child(event_generator).await?; - Ok(()) - } - - /// Controller for a child process. - struct Child { - input: BufReader, - user_ev_controller: tokio::sync::watch::Sender<(u32, PeerId)>, - peer_id: PeerId, - } - - impl Child { - async fn get_config(input: &mut BufReader) -> anyhow::Result { - let mut buf = [0u8; 4]; - input.read_exact(&mut buf).await?; - let config_len = u32::from_le_bytes(buf); - let mut config_buf = vec![0u8; config_len as usize]; - input.read_exact(&mut config_buf).await?; - let node_config: NodeConfig = bincode::deserialize(&config_buf)?; - Ok(node_config) - } - - async fn event_loop(mut self) -> anyhow::Result<()> { - loop { - match IPCMessage::recv(&mut self.input).await { - Err(err) => { - tracing::error!( - "Error at peer {} while receiving message: {err}", - self.peer_id - ); - break Err(err); - } - Ok(IPCMessage::FiredEvent(id)) => { - self.user_ev_controller.send((id, self.peer_id))?; - } - Ok(IPCMessage::Data(data)) => { - InterProcessConnManager::push_msg(data); - } - } - } - } - } - - enum IPCMessage { - FiredEvent(u32), - Data(Vec), - } - - impl IPCMessage { - async fn send(self, out: &mut tokio::process::ChildStdin) -> anyhow::Result<()> { - match self { - Self::FiredEvent(id) => { - out.write_u8(0).await?; - out.write_all(&id.to_le_bytes()).await?; - } - Self::Data(data) => { - out.write_u8(1).await?; - out.write_all(&(data.len() as u32).to_le_bytes()).await?; - out.write_all(&data).await?; - } - } - Ok(()) - } - - async fn recv(input: &mut BufReader) -> anyhow::Result { - let marker = input.read_u8().await?; - match marker { - 0 => { - let mut buf = [0u8; 4]; - input.read_exact(&mut buf).await?; - let event_id = u32::from_le_bytes(buf); - Ok(Self::FiredEvent(event_id)) - } - 1 => { - let mut buf = [0u8; 4]; - input.read_exact(&mut buf).await?; - let data_len = u32::from_le_bytes(buf); - let mut data = vec![0u8; data_len as usize]; - input.read_exact(&mut data).await?; - Ok(Self::Data(data)) - } - _ => unimplemented!(), - } - } - } -} - -mod single_process { - use std::time::Duration; - - use futures::StreamExt; - use tokio::signal; - - pub(super) async fn run(config: &super::TestConfig) -> anyhow::Result<(), super::Error> { - let mut simulated_network = super::config_sim_network(config).await?; - - let join_handles = simulated_network - .start_with_rand_gen::( - config.seed(), - config.max_contract_number.unwrap_or(config.nodes * 10), - config.events as usize, - ) - .await; - - let events = config.events; - let next_event_wait_time = config - .event_wait_time - .map(Duration::from_millis) - .unwrap_or(Duration::from_millis(200)); - let (connectivity_timeout, network_connection_percent) = - config.get_connection_check_params(); - let events_generated = tokio::task::spawn(async move { - tracing::info!( - "Waiting for network to be sufficiently connected ({}ms timeout, {}%)", - connectivity_timeout.as_millis(), - network_connection_percent * 100.0 - ); - simulated_network - .check_partial_connectivity(connectivity_timeout, network_connection_percent)?; - let mut stream = simulated_network.event_chain(events, None); - while stream.next().await.is_some() { - tokio::time::sleep(next_event_wait_time).await; - } - Ok::<_, super::Error>(()) - }); - - let join_peer_tasks = async move { - let mut futs = futures::stream::FuturesUnordered::from_iter(join_handles); - while let Some(join_handle) = futs.next().await { - join_handle??; - } - Ok::<_, super::Error>(()) - }; - - let ctrl_c = signal::ctrl_c(); - - tokio::pin!(events_generated); - tokio::pin!(join_peer_tasks); - tokio::pin!(ctrl_c); - - loop { - tokio::select! { - _ = &mut ctrl_c /* SIGINT handling */ => { - break; - } - res = &mut events_generated => { - match res? { - Ok(()) => { - tracing::info!("Test events generated successfully"); - *events_generated = tokio::task::spawn(futures::future::pending::>()); - continue; - } - Err(err) => { - return Err(err); - } - } - } - finalized = &mut join_peer_tasks => { - match finalized { - Ok(_) => { - tracing::info!("Test finalized successfully"); - break; - } - Err(e) => { - tracing::error!("Test finalized with error: {}", e); - return Err(e); - } - } - } - } - } - - Ok(()) - } -} diff --git a/crates/fdev/src/testing/multiple_process.rs b/crates/fdev/src/testing/multiple_process.rs new file mode 100644 index 000000000..dfa17896a --- /dev/null +++ b/crates/fdev/src/testing/multiple_process.rs @@ -0,0 +1,492 @@ +use std::{ + collections::{HashMap, VecDeque}, + fmt::Display, + process::Stdio, + time::Duration, +}; + +use anyhow::anyhow; +use freenet::dev_tool::{ + EventChain, InterProcessConnManager, MemoryEventsGen, NodeConfig, NodeLabel, PeerId, SimPeer, +}; +use futures::{future::BoxFuture, stream::FuturesUnordered, FutureExt, StreamExt}; +use rand::Rng; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt, BufReader, Stdin}, + process::{ChildStdout, Command}, +}; + +use super::Error; + +impl super::TestConfig { + fn subprocess_command(&self, seed: u64) -> Vec { + let mut args = Vec::new(); + + args.push("test".to_owned()); + + if let Some(name) = &self.name { + args.push("--name".to_owned()); + args.push(name.to_string()); + } + + args.push("--seed".to_owned()); + args.push(seed.to_string()); + + args.push("--gateways".to_owned()); + args.push(self.gateways.to_string()); + args.push("--nodes".to_owned()); + args.push(self.nodes.to_string()); + args.push("--ring-max-htl".to_owned()); + args.push(self.ring_max_htl.to_string()); + args.push("--rnd-if-htl-above".to_owned()); + args.push(self.rnd_if_htl_above.to_string()); + args.push("--max-connections".to_owned()); + args.push(self.max_connections.to_string()); + args.push("--min-connections".to_owned()); + args.push(self.min_connections.to_string()); + + if let Some(max_contract_number) = self.max_contract_number { + args.push("--max_contract_number".to_owned()); + args.push(max_contract_number.to_string()); + } + + args.push("multi-process".to_owned()); + args.push("--mode".to_owned()); + args.push("child".to_owned()); + + args + } +} + +#[derive(clap::Parser, Clone)] +pub struct MultiProcessConfig { + #[arg(long, default_value_t = Process::Supervisor)] + pub mode: Process, + #[arg(long)] + id: Option, +} + +#[derive(Default, Clone, clap::ValueEnum)] +pub enum Process { + #[default] + Supervisor, + Child, +} + +impl Display for Process { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Supervisor => write!(f, "supervisor"), + Self::Child => write!(f, "child"), + } + } +} + +pub(super) async fn run( + config: &super::TestConfig, + cmd_config: &MultiProcessConfig, +) -> anyhow::Result<(), Error> { + match cmd_config.mode { + Process::Supervisor => supervisor(config).await, + Process::Child => child(config, cmd_config.id.expect("id should be set for child")).await, + } +} + +async fn supervisor(config: &super::TestConfig) -> anyhow::Result<(), Error> { + let mut simulated_network = super::config_sim_network(config).await?; + simulated_network.debug(); // set to avoid deleting temp dirs created + let peers = simulated_network.build_peers(); + + // let (user_ev_controller, event_rx) = tokio::sync::watch::channel((0, PeerId::random())); + let (user_ev_controller, event_rx) = tokio::sync::mpsc::channel(1); + + let seed = config.seed(); + let mut supervisor = Supervisor { + processes: HashMap::new(), + queued: HashMap::new(), + sending: FuturesUnordered::new(), + responses: FuturesUnordered::new(), + event_rx: Some(event_rx), + }; + let cmd_args = config.subprocess_command(seed); + for (label, node) in &peers { + let mut subprocess = SubProcess::start(&cmd_args, label, node.peer_id)?; + subprocess.config(node).await?; + supervisor.processes.insert(node.peer_id, subprocess); + } + + let peers = peers + .into_iter() + .map(|(label, config)| (label, config.peer_id)) + .collect(); + let mut events = EventChain::new(peers, user_ev_controller, config.events, true); + let next_event_wait_time = config + .event_wait_time + .map(Duration::from_millis) + .unwrap_or(Duration::from_millis(200)); + let (connectivity_timeout, network_connection_percent) = config.get_connection_check_params(); + let events_generated = tokio::task::spawn(async move { + tracing::info!( + "Waiting for network to be sufficiently connected ({}ms timeout, {}%)", + connectivity_timeout.as_millis(), + network_connection_percent * 100.0 + ); + simulated_network + .check_partial_connectivity(connectivity_timeout, network_connection_percent)?; + tracing::info!("Network is sufficiently connected, start sending events"); + while events.next().await.is_some() { + tokio::time::sleep(next_event_wait_time).await; + } + Ok::<_, super::Error>(()) + }); + + let ctrl_c = tokio::signal::ctrl_c(); + + let supervisor_task = tokio::task::spawn(supervisor.start_simulation()); + + tokio::pin!(events_generated); + tokio::pin!(supervisor_task); + tokio::pin!(ctrl_c); + + loop { + tokio::select! { + _ = &mut ctrl_c /* SIGINT handling */ => { + break; + } + res = &mut events_generated => { + match res? { + Ok(()) => { + tracing::info!("Test events generated successfully"); + *events_generated = tokio::task::spawn(futures::future::pending::>()); + continue; + } + Err(err) => { + return Err(err); + } + } + } + finalized = &mut supervisor_task => { + match finalized? { + Ok(_) => { + tracing::info!("Test finalized successfully"); + break; + } + Err(e) => { + tracing::error!("Test finalized with error: {}", e); + return Err(e); + } + } + } + } + } + + Ok(()) +} + +type ChildResponses = Vec<(PeerId, Vec)>; + +/// Event driver for the supervisor process. +struct Supervisor { + processes: HashMap, + queued: HashMap>, + sending: FuturesUnordered>>, + responses: FuturesUnordered>>, + event_rx: Option>, +} + +impl Supervisor { + async fn start_simulation(mut self) -> anyhow::Result<()> { + let ctrl_c = tokio::signal::ctrl_c(); + tokio::pin!(ctrl_c); + + let mut event_rx = self.event_rx.take().expect("should be set"); + let mut finished_events = false; + + for child_stdout in self + .processes + .values_mut() + .map(|sp| sp.child.stdout.take().expect("should be set")) + { + self.responses + .push(SubProcess::get_child_responses(child_stdout).boxed()); + } + + loop { + tokio::select! { + _ = &mut ctrl_c /* SIGINT handling */ => { + break; + } + res = self.responses.next(), if !self.responses.is_empty() => { + let (child_stdout, responses) = match res { + Some(Ok(res)) => res, + Some(Err(err)) => { + tracing::error!("Error processing responses: {err}"); + return Err(err); + } + None => { + continue; + } + }; + self.responses.push(SubProcess::get_child_responses(child_stdout).boxed()); + self.process_responses(responses); + } + completed_send = self.sending.next(), if !self.sending.is_empty() => { + let mut subprocess = match completed_send { + Some(Ok(res)) => res, + Some(Err(err)) => { + tracing::error!("Error sending message: {err}"); + return Err(err); + } + None => { + continue; + } + }; + let peer_queue = &mut *self.queued.entry(subprocess.id).or_default(); + if !peer_queue.is_empty() { + let n = rand::thread_rng().gen_range(0..=peer_queue.len()); + let messages = peer_queue.drain(..n).collect::>(); + let task = async move { + let stdin = subprocess.child.stdin.as_mut().expect("not taken"); + tracing::debug!(peer = %subprocess.id, "Draining {} messages from queue", n + 1); + for pending in messages { + pending.send(stdin).await?; + } + Ok(subprocess) + }.boxed(); + self.sending.push(task); + } else { + self.processes.insert(subprocess.id, subprocess); + } + } + event = event_rx.recv(), if !finished_events => { + let Some((event, peer)) = event else { + tracing::info!("No more events"); + finished_events = true; + continue; + }; + let Some(mut subprocess) = self.processes.remove(&peer) else { + self.queued.entry(peer).or_default().push_back(IPCMessage::FiredEvent(event)); + continue; + }; + let mut pending = self.queued.remove(&peer).unwrap_or_default(); + pending.push_back(IPCMessage::FiredEvent(event)); + let task = async move { + for msg in pending { + msg.send(subprocess.child.stdin.as_mut().expect("not taken")) + .await?; + } + Ok(subprocess) + }.boxed(); + self.sending.push(task); + } + } + } + for (_, subprocess) in self.processes.drain() { + subprocess.close().await; + } + tracing::info!("Simulation finished"); + Ok(()) + } + + fn process_responses(&mut self, responses: ChildResponses) { + for (target, data) in responses { + if let Some(mut target) = self.processes.remove(&target) { + tracing::debug!(to = %target.id, "Sending message"); + let task = async move { + target.send_msg(IPCMessage::Data(data)).await?; + Ok(target) + } + .boxed(); + self.sending.push(task); + } else { + tracing::debug!(%target, "Queuing message"); + self.queued + .entry(target) + .or_default() + .push_back(IPCMessage::Data(data)); + } + } + } +} + +struct SubProcess { + child: tokio::process::Child, + id: PeerId, +} + +impl SubProcess { + fn start(cmd_args: &[String], label: &NodeLabel, id: PeerId) -> anyhow::Result { + let child = Command::new("fdev") + .kill_on_drop(true) + .args(cmd_args) + .arg("--id") + .arg(label.number().to_string()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn()?; + Ok(Self { child, id }) + } + + async fn config(&mut self, config: &NodeConfig) -> anyhow::Result<(), Error> { + let input = self.get_input().ok_or_else(|| anyhow!("closed"))?; + let serialize = bincode::serialize(&config)?; + input + .write_all(&(serialize.len() as u32).to_le_bytes()) + .await?; + input.write_all(&serialize).await?; + Ok(()) + } + + #[inline] + async fn send_msg(&mut self, msg: IPCMessage) -> anyhow::Result<()> { + msg.send(self.child.stdin.as_mut().expect("not taken")) + .await?; + Ok(()) + } + + async fn get_child_responses( + mut stdout: ChildStdout, + ) -> anyhow::Result<(ChildStdout, Vec<(PeerId, Vec)>)> { + let mut returned = vec![]; + loop { + match InterProcessConnManager::pull_msg(&mut stdout).await { + Ok(Some((target, data))) => { + returned.push((target, data)); + } + Ok(None) if !returned.is_empty() => { + break; + } + Ok(None) => {} + Err(err) => { + return Err(err.into()); + } + } + } + tracing::debug!(len = %returned.len(), "Returning messages"); + Ok((stdout, returned)) + } + + async fn close(mut self) { + let _ = self.child.kill().await; + } + + #[inline] + fn get_input(&mut self) -> Option<&mut tokio::process::ChildStdin> { + self.child.stdin.as_mut() + } +} + +async fn child(config: &super::TestConfig, id: usize) -> anyhow::Result<()> { + // write logs to stderr so stdout and stdin are free of unexpected data + std::env::set_var("FREENET_LOG_TO_STDERR", "1"); + + let (user_ev_controller, mut receiver_ch) = tokio::sync::watch::channel((0, PeerId::random())); + receiver_ch.borrow_and_update(); + let mut input = BufReader::new(tokio::io::stdin()); + let node_config = Child::get_config(&mut input).await?; + let this_child = Child { + input, + user_ev_controller, + peer_id: node_config.peer_id, + }; + std::env::set_var("FREENET_PEER_ID", node_config.peer_id.to_string()); + freenet::config::set_logger(); + let mut event_generator = MemoryEventsGen::::new_with_seed( + receiver_ch.clone(), + node_config.peer_id, + config.seed.expect("seed should be set for child process"), + ); + event_generator.rng_params( + id, + config.gateways + config.nodes, + config.max_contract_number.unwrap_or(config.nodes * 10), + config.events as usize, + ); + let config = SimPeer::from(node_config); + tokio::task::spawn(this_child.event_loop()); + config.start_child(event_generator).await?; + Ok(()) +} + +/// Controller for a child process. +struct Child { + input: BufReader, + user_ev_controller: tokio::sync::watch::Sender<(u32, PeerId)>, + peer_id: PeerId, +} + +impl Child { + async fn get_config(input: &mut BufReader) -> anyhow::Result { + let mut buf = [0u8; 4]; + input.read_exact(&mut buf).await?; + let config_len = u32::from_le_bytes(buf); + let mut config_buf = vec![0u8; config_len as usize]; + input.read_exact(&mut config_buf).await?; + let node_config: NodeConfig = bincode::deserialize(&config_buf)?; + Ok(node_config) + } + + async fn event_loop(mut self) -> anyhow::Result<()> { + loop { + match IPCMessage::recv(&mut self.input).await { + Err(err) => { + tracing::error!( + "Error at peer {} while receiving message: {err}", + self.peer_id + ); + break Err(err); + } + Ok(IPCMessage::FiredEvent(id)) => { + self.user_ev_controller.send((id, self.peer_id))?; + } + Ok(IPCMessage::Data(data)) => { + InterProcessConnManager::push_msg(data); + } + } + } + } +} + +enum IPCMessage { + FiredEvent(u32), + Data(Vec), +} + +impl IPCMessage { + async fn send(self, out: &mut tokio::process::ChildStdin) -> anyhow::Result<()> { + match self { + Self::FiredEvent(id) => { + out.write_u8(0).await?; + out.write_all(&id.to_le_bytes()).await?; + } + Self::Data(data) => { + out.write_u8(1).await?; + out.write_all(&(data.len() as u32).to_le_bytes()).await?; + out.write_all(&data).await?; + } + } + Ok(()) + } + + async fn recv(input: &mut BufReader) -> anyhow::Result { + let marker = input.read_u8().await?; + match marker { + 0 => { + let mut buf = [0u8; 4]; + input.read_exact(&mut buf).await?; + let event_id = u32::from_le_bytes(buf); + Ok(Self::FiredEvent(event_id)) + } + 1 => { + let mut buf = [0u8; 4]; + input.read_exact(&mut buf).await?; + let data_len = u32::from_le_bytes(buf); + let mut data = vec![0u8; data_len as usize]; + input.read_exact(&mut data).await?; + Ok(Self::Data(data)) + } + _ => unimplemented!(), + } + } +} diff --git a/crates/fdev/src/testing/network.rs b/crates/fdev/src/testing/network.rs new file mode 100644 index 000000000..01ea3a33f --- /dev/null +++ b/crates/fdev/src/testing/network.rs @@ -0,0 +1,3 @@ +pub(super) async fn run(_config: &super::TestConfig) -> anyhow::Result<()> { + todo!() +} diff --git a/crates/fdev/src/testing/single_process.rs b/crates/fdev/src/testing/single_process.rs new file mode 100644 index 000000000..287f11086 --- /dev/null +++ b/crates/fdev/src/testing/single_process.rs @@ -0,0 +1,85 @@ +use std::time::Duration; + +use futures::StreamExt; +use tokio::signal; + +pub(super) async fn run(config: &super::TestConfig) -> anyhow::Result<(), super::Error> { + let mut simulated_network = super::config_sim_network(config).await?; + + let join_handles = simulated_network + .start_with_rand_gen::( + config.seed(), + config.max_contract_number.unwrap_or(config.nodes * 10), + config.events as usize, + ) + .await; + + let events = config.events; + let next_event_wait_time = config + .event_wait_time + .map(Duration::from_millis) + .unwrap_or(Duration::from_millis(200)); + let (connectivity_timeout, network_connection_percent) = config.get_connection_check_params(); + let events_generated = tokio::task::spawn(async move { + tracing::info!( + "Waiting for network to be sufficiently connected ({}ms timeout, {}%)", + connectivity_timeout.as_millis(), + network_connection_percent * 100.0 + ); + simulated_network + .check_partial_connectivity(connectivity_timeout, network_connection_percent)?; + let mut stream = simulated_network.event_chain(events, None); + while stream.next().await.is_some() { + tokio::time::sleep(next_event_wait_time).await; + } + Ok::<_, super::Error>(()) + }); + + let join_peer_tasks = async move { + let mut futs = futures::stream::FuturesUnordered::from_iter(join_handles); + while let Some(join_handle) = futs.next().await { + join_handle??; + } + Ok::<_, super::Error>(()) + }; + + let ctrl_c = signal::ctrl_c(); + + tokio::pin!(events_generated); + tokio::pin!(join_peer_tasks); + tokio::pin!(ctrl_c); + + loop { + tokio::select! { + _ = &mut ctrl_c /* SIGINT handling */ => { + break; + } + res = &mut events_generated => { + match res? { + Ok(()) => { + tracing::info!("Test events generated successfully"); + *events_generated = tokio::task::spawn(futures::future::pending::>()); + continue; + } + Err(err) => { + return Err(err); + } + } + } + finalized = &mut join_peer_tasks => { + match finalized { + Ok(_) => { + tracing::info!("Test finalized successfully"); + break; + } + Err(e) => { + tracing::error!("Test finalized with error: {}", e); + return Err(e); + } + } + } + } + } + + Ok(()) +} diff --git a/schemas/flatbuffers/topology.fbs b/schemas/flatbuffers/topology.fbs new file mode 100644 index 000000000..dfdf2eea2 --- /dev/null +++ b/schemas/flatbuffers/topology.fbs @@ -0,0 +1,41 @@ +namespace topology; + +table AddedConnection { + from: string(required); // encoded PeerId + from_location: float64; + to: string(required); // encoded PeerId + to_location: float64; +} + +table RemovedConnection { + at: string(required); + from: string(required); +} + +table Error { + message: string(required); +} + +union PeerChangeType { + AddedConnection, + RemovedConnection, + Error, +} + +table PeerChange { + current_state: [AddedConnection]; + change: PeerChangeType; +} + +table Ok { + message: string; +} + +union Response { + Error, + Ok +} + +table ControllerResponse { + response:Response(required); +} diff --git a/stdlib b/stdlib index 398295388..75d3d955b 160000 --- a/stdlib +++ b/stdlib @@ -1 +1 @@ -Subproject commit 3982953881f11092e61b000431fa13b3235f61d4 +Subproject commit 75d3d955bdcc9c6afc419e1744efddb749868615 From db6e5ae65a969e58e81fa795e66b097976d25710 Mon Sep 17 00:00:00 2001 From: Ignacio Duart Date: Tue, 28 Nov 2023 11:51:25 +0100 Subject: [PATCH 02/12] Rename all instances of op_storage --- crates/core/src/node.rs | 76 +++++++++---------- crates/core/src/node/p2p_impl.rs | 10 +-- crates/core/src/node/testing_impl.rs | 22 +++--- .../core/src/node/testing_impl/in_memory.rs | 20 ++--- .../src/node/testing_impl/inter_process.rs | 8 +- crates/core/src/operations.rs | 24 +++--- crates/core/src/operations/connect.rs | 54 ++++++------- crates/core/src/operations/get.rs | 34 ++++----- crates/core/src/operations/put.rs | 76 +++++++++---------- crates/core/src/operations/subscribe.rs | 32 ++++---- crates/core/src/operations/update.rs | 4 +- 11 files changed, 180 insertions(+), 180 deletions(-) diff --git a/crates/core/src/node.rs b/crates/core/src/node.rs index 1d082691a..5fe682e26 100644 --- a/crates/core/src/node.rs +++ b/crates/core/src/node.rs @@ -304,7 +304,7 @@ async fn join_ring_request( backoff: Option, peer_key: PeerId, gateway: &PeerKeyLocation, - op_storage: &OpManager, + op_manager: &OpManager, conn_manager: &mut CM, ) -> Result<(), OpError> where @@ -312,7 +312,7 @@ where { let tx_id = Transaction::new::(); let mut op = - connect::initial_request(peer_key, *gateway, op_storage.ring.max_hops_to_live, tx_id); + connect::initial_request(peer_key, *gateway, op_manager.ring.max_hops_to_live, tx_id); if let Some(mut backoff) = backoff { // backoff to retry later in case it failed tracing::warn!("Performing a new join, attempt {}", backoff.retries() + 1); @@ -322,13 +322,13 @@ where } op.backoff = Some(backoff); } - connect::connect_request(tx_id, op_storage, conn_manager, op).await?; + connect::connect_request(tx_id, op_manager, conn_manager, op).await?; Ok(()) } /// Process client events. async fn client_event_handling( - op_storage: Arc, + op_manager: Arc, mut client_events: ClientEv, mut client_responses: ClientResponses, node_controller: tokio::sync::mpsc::Sender, @@ -356,7 +356,7 @@ async fn client_event_handling( node_controller.send(NodeEvent::Disconnect { cause: cause.clone() }).await.ok(); break; } - process_open_request(req, op_storage.clone()).await; + process_open_request(req, op_manager.clone()).await; } res = client_responses.recv() => { if let Some((cli_id, res)) = res { @@ -374,7 +374,7 @@ async fn client_event_handling( } #[inline] -async fn process_open_request(request: OpenRequest<'static>, op_storage: Arc) { +async fn process_open_request(request: OpenRequest<'static>, op_manager: Arc) { // this will indirectly start actions on the local contract executor let fut = async move { let client_id = request.client_id; @@ -388,16 +388,16 @@ async fn process_open_request(request: OpenRequest<'static>, op_storage: Arc { // Initialize a put op. tracing::debug!( - this_peer = %op_storage.ring.peer_key, + this_peer = %op_manager.ring.peer_key, "Received put from user event", ); let op = put::start_op( contract, related_contracts, state, - op_storage.ring.max_hops_to_live, + op_manager.ring.max_hops_to_live, ); - if let Err(err) = put::request_put(&op_storage, op, Some(client_id)).await { + if let Err(err) = put::request_put(&op_manager, op, Some(client_id)).await { tracing::error!("{}", err); } } @@ -407,7 +407,7 @@ async fn process_open_request(request: OpenRequest<'static>, op_storage: Arc { // FIXME: DO THIS tracing::debug!( - this_peer = %op_storage.ring.peer_key, + this_peer = %op_manager.ring.peer_key, "Received update from user event", ); } @@ -417,11 +417,11 @@ async fn process_open_request(request: OpenRequest<'static>, op_storage: Arc { // Initialize a get op. tracing::debug!( - this_peer = %op_storage.ring.peer_key, + this_peer = %op_manager.ring.peer_key, "Received get from user event", ); let op = get::start_op(key, contract); - if let Err(err) = get::request_get(&op_storage, op, Some(client_id)).await { + if let Err(err) = get::request_get(&op_manager, op, Some(client_id)).await { tracing::error!("{}", err); } } @@ -431,7 +431,7 @@ async fn process_open_request(request: OpenRequest<'static>, op_storage: Arc, op_storage: Arc, op_storage: Arc { + ($op:expr, $id:expr, $op_manager:ident) => { tracing::debug!( tx = %$id, - this_peer = %$op_storage.ring.peer_key, + this_peer = %$op_manager.ring.peer_key, concat!("Handling ", $op, " request"), ); }; @@ -501,7 +501,7 @@ macro_rules! log_handling_msg { async fn report_result( tx: Option, op_result: Result, OpError>, - op_storage: &OpManager, + op_manager: &OpManager, executor_callback: Option>, client_req_handler_callback: Option<(ClientId, ClientResponsesSender)>, event_listener: &mut dyn NetEventRegister, @@ -534,18 +534,18 @@ async fn report_result( event_listener .register_events(Either::Left(NetEventLog::route_event( op_res.id(), - op_storage, + op_manager, &event, ))) .await; - op_storage.ring.routing_finished(event); + op_manager.ring.routing_finished(event); } // todo: handle failures, need to track timeouts and other potential failures // OpOutcome::ContractOpFailure { // target_peer: Some(target_peer), // contract_location, // } => { - // op_storage.ring.routing_finished(RouteEvent { + // op_manager.ring.routing_finished(RouteEvent { // peer: *target_peer, // contract_location, // outcome: RouteOutcome::Failure, @@ -561,7 +561,7 @@ async fn report_result( Err(err) => { // just mark the operation as completed so no redundant messages are processed for this transaction anymore if let Some(tx) = tx { - op_storage.completed(tx); + op_manager.completed(tx); } #[cfg(any(debug_assertions, test))] { @@ -607,7 +607,7 @@ macro_rules! handle_op_not_available { async fn process_message( msg: NetMessage, - op_storage: Arc, + op_manager: Arc, mut conn_manager: CB, mut event_listener: Box, executor_callback: Option>, @@ -620,14 +620,14 @@ async fn process_message( let tx = Some(*msg.id()); event_listener - .register_events(NetEventLog::from_inbound_msg(&msg, &op_storage)) + .register_events(NetEventLog::from_inbound_msg(&msg, &op_manager)) .await; loop { match &msg { NetMessage::Connect(op) => { - // log_handling_msg!("join", op.id(), op_storage); + // log_handling_msg!("join", op.id(), op_manager); let op_result = handle_op_request::( - &op_storage, + &op_manager, &mut conn_manager, op, client_id, @@ -637,7 +637,7 @@ async fn process_message( break report_result( tx, op_result, - &op_storage, + &op_manager, executor_callback, cli_req, &mut *event_listener, @@ -645,9 +645,9 @@ async fn process_message( .await; } NetMessage::Put(op) => { - // log_handling_msg!("put", *op.id(), op_storage); + // log_handling_msg!("put", *op.id(), op_manager); let op_result = handle_op_request::( - &op_storage, + &op_manager, &mut conn_manager, op, client_id, @@ -657,7 +657,7 @@ async fn process_message( break report_result( tx, op_result, - &op_storage, + &op_manager, executor_callback, cli_req, &mut *event_listener, @@ -665,9 +665,9 @@ async fn process_message( .await; } NetMessage::Get(op) => { - // log_handling_msg!("get", op.id(), op_storage); + // log_handling_msg!("get", op.id(), op_manager); let op_result = handle_op_request::( - &op_storage, + &op_manager, &mut conn_manager, op, client_id, @@ -677,7 +677,7 @@ async fn process_message( break report_result( tx, op_result, - &op_storage, + &op_manager, executor_callback, cli_req, &mut *event_listener, @@ -685,9 +685,9 @@ async fn process_message( .await; } NetMessage::Subscribe(op) => { - // log_handling_msg!("subscribe", op.id(), op_storage); + // log_handling_msg!("subscribe", op.id(), op_manager); let op_result = handle_op_request::( - &op_storage, + &op_manager, &mut conn_manager, op, client_id, @@ -697,7 +697,7 @@ async fn process_message( break report_result( tx, op_result, - &op_storage, + &op_manager, executor_callback, cli_req, &mut *event_listener, @@ -712,7 +712,7 @@ async fn process_message( async fn handle_cancelled_op( tx: Transaction, peer_key: PeerId, - op_storage: &OpManager, + op_manager: &OpManager, conn_manager: &mut CM, ) -> Result<(), OpError> where @@ -721,7 +721,7 @@ where if let TransactionType::Connect = tx.transaction_type() { // the attempt to join the network failed, this could be a fatal error since the node // is useless without connecting to the network, we will retry with exponential backoff - match op_storage.pop(&tx) { + match op_manager.pop(&tx) { Ok(Some(OpEnum::Connect(op))) if op.has_backoff() => { let ConnectOp { gateway, backoff, .. @@ -729,7 +729,7 @@ where if let Some(gateway) = gateway { let backoff = backoff.expect("infallible"); tracing::warn!("Retry connecting to gateway {}", gateway.peer); - join_ring_request(Some(backoff), peer_key, &gateway, op_storage, conn_manager) + join_ring_request(Some(backoff), peer_key, &gateway, op_manager, conn_manager) .await?; } } diff --git a/crates/core/src/node/p2p_impl.rs b/crates/core/src/node/p2p_impl.rs index 1f247ca28..9782ac295 100644 --- a/crates/core/src/node/p2p_impl.rs +++ b/crates/core/src/node/p2p_impl.rs @@ -93,14 +93,14 @@ impl NodeP2P { let (ch_outbound, ch_inbound) = contract::contract_handler_channel(); let (client_responses, cli_response_sender) = contract::ClientResponses::channel(); - let op_storage = Arc::new(OpManager::new( + let op_manager = Arc::new(OpManager::new( notification_tx, ch_outbound, &builder, &gateways, event_register.clone(), )?); - let (executor_listener, executor_sender) = contract::executor_channel(op_storage.clone()); + let (executor_listener, executor_sender) = contract::executor_channel(op_manager.clone()); let contract_handler = CH::build(ch_inbound, executor_sender, ch_builder) .await .map_err(|e| anyhow::anyhow!(e))?; @@ -110,7 +110,7 @@ impl NodeP2P { P2pConnManager::build( transport, &builder, - op_storage.clone(), + op_manager.clone(), event_register, private_key, )? @@ -125,7 +125,7 @@ impl NodeP2P { let (node_controller_tx, node_controller_rx) = tokio::sync::mpsc::channel(1); GlobalExecutor::spawn( client_event_handling( - op_storage.clone(), + op_manager.clone(), clients, client_responses, node_controller_tx, @@ -137,7 +137,7 @@ impl NodeP2P { peer_key, conn_manager, notification_channel, - op_manager: op_storage, + op_manager, is_gateway: builder.location.is_some(), executor_listener, cli_response_sender, diff --git a/crates/core/src/node/testing_impl.rs b/crates/core/src/node/testing_impl.rs index 8e39cbccb..fae772a35 100644 --- a/crates/core/src/node/testing_impl.rs +++ b/crates/core/src/node/testing_impl.rs @@ -972,7 +972,7 @@ where is_gateway: bool, gateways: Vec, parent_span: Option, - op_storage: Arc, + op_manager: Arc, conn_manager: NB, /// Set on creation, taken on run user_events: Option, @@ -992,7 +992,7 @@ where None, config.peer_key, gateway, - &config.op_storage, + &config.op_manager, &mut config.conn_manager, ) .await?; @@ -1019,7 +1019,7 @@ where let (node_controller_tx, node_controller_rx) = tokio::sync::mpsc::channel(1); GlobalExecutor::spawn( super::client_event_handling( - config.op_storage.clone(), + config.op_manager.clone(), config.user_events.take().expect("should be set"), client_responses, node_controller_tx, @@ -1044,7 +1044,7 @@ async fn run_event_listener( is_gateway, gateways, parent_span, - op_storage, + op_manager, mut conn_manager, mut notification_channel, mut event_register, @@ -1078,7 +1078,7 @@ where if let Ok(Either::Left(NetMessage::Aborted(tx))) = msg { let tx_type = tx.transaction_type(); let res = - super::handle_cancelled_op(tx, peer_key, &op_storage, &mut conn_manager).await; + super::handle_cancelled_op(tx, peer_key, &op_manager, &mut conn_manager).await; match res { Err(crate::operations::OpError::MaxRetriesExceeded(_, _)) if tx_type == crate::message::TransactionType::Connect && !is_gateway => @@ -1089,7 +1089,7 @@ where None, peer_key, gateway, - &op_storage, + &op_manager, &mut conn_manager, ) .await? @@ -1110,9 +1110,9 @@ where NodeEvent::DropConnection(peer) => { tracing::info!("Dropping connection to {peer}"); event_register.register_events(Either::Left( - crate::tracing::NetEventLog::disconnected(&peer), + crate::tracing::NetEventLog::disconnected(&op_manager.ring, &peer), )); - op_storage.ring.prune_connection(peer); + op_manager.ring.prune_connection(peer); continue; } NodeEvent::Disconnect { cause: Some(cause) } => { @@ -1131,7 +1131,7 @@ where super::report_result( None, Err(err.into()), - &op_storage, + &op_manager, None, None, &mut *event_register as &mut _, @@ -1141,7 +1141,7 @@ where } }; - let op_storage = op_storage.clone(); + let op_manager = op_manager.clone(); let event_listener = event_register.trait_clone(); let span = { @@ -1166,7 +1166,7 @@ where let msg = super::process_message( msg, - op_storage, + op_manager, conn_manager.clone(), event_listener, None, diff --git a/crates/core/src/node/testing_impl/in_memory.rs b/crates/core/src/node/testing_impl/in_memory.rs index 1bb41b849..da8699c20 100644 --- a/crates/core/src/node/testing_impl/in_memory.rs +++ b/crates/core/src/node/testing_impl/in_memory.rs @@ -34,7 +34,7 @@ impl Builder { let (ops_ch_channel, ch_channel) = contract::contract_handler_channel(); let _guard = parent_span.enter(); - let op_storage = Arc::new(OpManager::new( + let op_manager = Arc::new(OpManager::new( notification_tx, ops_ch_channel, &self.config, @@ -42,7 +42,7 @@ impl Builder { self.event_register.clone(), )?); std::mem::drop(_guard); - let (_executor_listener, executor_sender) = executor_channel(op_storage.clone()); + let (_executor_listener, executor_sender) = executor_channel(op_manager.clone()); let contract_handler = MemoryContractHandler::build(ch_channel, executor_sender, self.contract_handler_name) .await @@ -50,8 +50,8 @@ impl Builder { let conn_manager = MemoryConnManager::new( self.peer_key, - self.event_register.trait_clone(), - op_storage.clone(), + self.event_register.clone(), + op_manager.clone(), self.add_noise, ); @@ -64,7 +64,7 @@ impl Builder { is_gateway, gateways, parent_span: Some(parent_span), - op_storage, + op_manager, conn_manager, user_events: Some(user_events), notification_channel, @@ -101,10 +101,10 @@ where for (contract, state) in contracts { let key: ContractKey = contract.key(); let parameters = contract.params(); - self.op_storage + self.op_manager .notify_contract_handler(ContractHandlerEvent::Cache(contract.clone()), None) .await?; - self.op_storage + self.op_manager .notify_contract_handler( ContractHandlerEvent::PutQuery { key: key.clone(), @@ -118,14 +118,14 @@ where tracing::debug!( "Appended contract {} to peer {}", key, - self.op_storage.ring.peer_key + self.op_manager.ring.peer_key ); - self.op_storage.ring.contract_cached(&key); + self.op_manager.ring.contract_cached(&key); if let Some(subscribers) = contract_subscribers.get(&key) { // add contract subscribers for subscriber in subscribers { if self - .op_storage + .op_manager .ring .add_subscriber(&key, *subscriber) .is_err() diff --git a/crates/core/src/node/testing_impl/inter_process.rs b/crates/core/src/node/testing_impl/inter_process.rs index a66b44b09..4c115e2c1 100644 --- a/crates/core/src/node/testing_impl/inter_process.rs +++ b/crates/core/src/node/testing_impl/inter_process.rs @@ -57,14 +57,14 @@ impl SimPeer { let (notification_channel, notification_tx) = EventLoopNotifications::channel(); let (ops_ch_channel, ch_channel) = contract::contract_handler_channel(); - let op_storage = Arc::new(OpManager::new( + let op_manager = Arc::new(OpManager::new( notification_tx, ops_ch_channel, &self.config, &gateways, event_register.clone(), )?); - let (_executor_listener, executor_sender) = contract::executor_channel(op_storage.clone()); + let (_executor_listener, executor_sender) = contract::executor_channel(op_manager.clone()); let contract_handler = MemoryContractHandler::build( ch_channel, executor_sender, @@ -73,7 +73,7 @@ impl SimPeer { .await .map_err(|e| anyhow::anyhow!(e))?; - let conn_manager = InterProcessConnManager::new(); + let conn_manager = InterProcessConnManager::new(event_register.clone(), op_manager.clone()); GlobalExecutor::spawn( contract::contract_handling(contract_handler) @@ -82,7 +82,7 @@ impl SimPeer { let running_node = super::RunnerConfig { peer_key: self.config.peer_id, - op_storage, + op_manager, gateways, notification_channel, conn_manager, diff --git a/crates/core/src/operations.rs b/crates/core/src/operations.rs index 175032dc9..cfa7d043d 100644 --- a/crates/core/src/operations.rs +++ b/crates/core/src/operations.rs @@ -33,7 +33,7 @@ pub(crate) struct OpInitialization { } pub(crate) async fn handle_op_request( - op_storage: &OpManager, + op_manager: &OpManager, network_bridge: &mut NB, msg: &Op::Message, client_id: Option, @@ -45,16 +45,16 @@ where let sender; let tx = *msg.id(); let result = { - let OpInitialization { sender: s, op } = Op::load_or_init(op_storage, msg).await?; + let OpInitialization { sender: s, op } = Op::load_or_init(op_manager, msg).await?; sender = s; - op.process_message(network_bridge, op_storage, msg, client_id) + op.process_message(network_bridge, op_manager, msg, client_id) .await }; - handle_op_result(op_storage, network_bridge, result, tx, sender).await + handle_op_result(op_manager, network_bridge, result, tx, sender).await } async fn handle_op_result( - op_storage: &OpManager, + op_manager: &OpManager, network_bridge: &mut CB, result: Result, tx_id: Transaction, @@ -85,7 +85,7 @@ where if let Some(target) = msg.target().cloned() { network_bridge.send(&target.peer, msg).await?; } - op_storage.push(id, updated_state).await?; + op_manager.push(id, updated_state).await?; } Ok(OperationResult { @@ -93,7 +93,7 @@ where state: Some(final_state), }) if final_state.finalized() => { // operation finished_completely with result - op_storage.completed(tx_id); + op_manager.completed(tx_id); return Ok(Some(final_state)); } Ok(OperationResult { @@ -102,13 +102,13 @@ where }) => { // interim state let id = *updated_state.id(); - op_storage.push(id, updated_state).await?; + op_manager.push(id, updated_state).await?; } Ok(OperationResult { return_msg: Some(msg), state: None, }) => { - op_storage.completed(tx_id); + op_manager.completed(tx_id); // finished the operation at this node, informing back if let Some(target) = msg.target().cloned() { network_bridge.send(&target.peer, msg).await?; @@ -119,7 +119,7 @@ where state: None, }) => { // operation finished_completely - op_storage.completed(tx_id); + op_manager.completed(tx_id); } } Ok(None) @@ -262,7 +262,7 @@ where type Result; fn load_or_init<'a>( - op_storage: &'a OpManager, + op_manager: &'a OpManager, msg: &'a Self::Message, ) -> BoxFuture<'a, Result, OpError>>; @@ -272,7 +272,7 @@ where fn process_message<'a, CB: NetworkBridge>( self, conn_manager: &'a mut CB, - op_storage: &'a OpManager, + op_manager: &'a OpManager, input: &'a Self::Message, client_id: Option, ) -> Pin> + Send + 'a>>; diff --git a/crates/core/src/operations/connect.rs b/crates/core/src/operations/connect.rs index 0738d81c7..45ac1d5ed 100644 --- a/crates/core/src/operations/connect.rs +++ b/crates/core/src/operations/connect.rs @@ -56,13 +56,13 @@ impl Operation for ConnectOp { type Result = ConnectResult; fn load_or_init<'a>( - op_storage: &'a OpManager, + op_manager: &'a OpManager, msg: &'a Self::Message, ) -> BoxFuture<'a, Result, OpError>> { async move { let sender; let tx = *msg.id(); - match op_storage.pop(msg.id()) { + match op_manager.pop(msg.id()) { Ok(Some(OpEnum::Connect(connect_op))) => { sender = msg.sender().cloned(); // was an existing operation, the other peer messaged back @@ -72,7 +72,7 @@ impl Operation for ConnectOp { }) } Ok(Some(op)) => { - let _ = op_storage.push(tx, op).await; + let _ = op_manager.push(tx, op).await; Err(OpError::OpNotPresent(tx)) } Ok(None) => { @@ -83,7 +83,7 @@ impl Operation for ConnectOp { .. } ) { - Some(Box::new(op_storage.ring.own_location())) + Some(Box::new(op_manager.ring.own_location())) } else { None }; @@ -118,7 +118,7 @@ impl Operation for ConnectOp { fn process_message<'a, NB: NetworkBridge>( self, network_bridge: &'a mut NB, - op_storage: &'a OpManager, + op_manager: &'a OpManager, input: &'a Self::Message, _client_id: Option, ) -> Pin> + Send + 'a>> { @@ -137,7 +137,7 @@ impl Operation for ConnectOp { }, id, } => { - let own_loc = op_storage.ring.own_location(); + let own_loc = op_manager.ring.own_location(); let PeerKeyLocation { peer: this_peer, location: Some(_), @@ -153,7 +153,7 @@ impl Operation for ConnectOp { joiner = %joiner.peer, "Got queried for new connections from joiner", ); - if let Some(desirable_peer) = op_storage + if let Some(desirable_peer) = op_manager .ring .closest_to_location(*ideal_location, &[joiner.peer]) { @@ -233,7 +233,7 @@ impl Operation for ConnectOp { // todo: location should be based on your public IP let new_location = assigned_location.unwrap_or_else(Location::random); - let accepted_by = if op_storage.ring.should_accept(new_location) { + let accepted_by = if op_manager.ring.should_accept(new_location) { tracing::debug!(tx = %id, %joiner, "Accepting connection from"); HashSet::from_iter([*this_peer]) } else { @@ -247,7 +247,7 @@ impl Operation for ConnectOp { }; if let Some(mut updated_state) = forward_conn( *id, - &op_storage.ring, + &op_manager.ring, network_bridge, (new_peer_loc, new_peer_loc), *hops_to_live, @@ -303,7 +303,7 @@ impl Operation for ConnectOp { accepted_by, }, } => { - let own_loc = op_storage.ring.own_location(); + let own_loc = op_manager.ring.own_location(); let mut accepted_by = accepted_by.clone(); tracing::debug!( tx = %id, @@ -313,7 +313,7 @@ impl Operation for ConnectOp { %hops_to_live, "Proxy connect request received to connect with peer", ); - if op_storage + if op_manager .ring .should_accept(joiner.location.ok_or(ConnectionError::LocationUnknown)?) { @@ -332,7 +332,7 @@ impl Operation for ConnectOp { skip_list.push(own_loc.peer); if let Some(mut updated_state) = forward_conn( *id, - &op_storage.ring, + &op_manager.ring, network_bridge, (*sender, *joiner), *hops_to_live, @@ -425,12 +425,12 @@ impl Operation for ConnectOp { location = %your_location, "Updating assigned location" ); - op_storage.ring.update_location(Some(*your_location)); + op_manager.ring.update_location(Some(*your_location)); for other_peer in accepted_by.iter().filter(|pl| pl.peer != target.peer) { let _ = propagate_oc_to_accepted_peers( network_bridge, - op_storage, + op_manager, gateway, other_peer, ConnectMsg::Response { @@ -458,7 +458,7 @@ impl Operation for ConnectOp { gateway: self.gateway, backoff: self.backoff, }; - op_storage + op_manager .notify_op_change( NetMessage::Aborted(*id), OpEnum::Connect(op.into()), @@ -486,7 +486,7 @@ impl Operation for ConnectOp { target: original_target, new_location, }) => { - let own_loc = op_storage.ring.own_location(); + let own_loc = op_manager.ring.own_location(); let target_is_joiner = new_peer_id == original_target.peer; previously_accepted.extend(accepted_by.iter().copied()); @@ -550,7 +550,7 @@ impl Operation for ConnectOp { Some(ConnectState::AwaitingNewConnection { query_target }) => { let joiner = *target; if accepted_by.is_empty() { - op_storage + op_manager .ring .live_tx_tracker .missing_candidate_peers(query_target) @@ -561,7 +561,7 @@ impl Operation for ConnectOp { for peer in accepted_by { propagate_oc_to_accepted_peers( network_bridge, - op_storage, + op_manager, *sender, peer, ConnectMsg::Response { @@ -624,7 +624,7 @@ impl Operation for ConnectOp { } network_bridge.add_connection(sender.peer).await?; - op_storage.ring.add_connection( + op_manager.ring.add_connection( sender.location.ok_or(ConnectionError::LocationUnknown)?, sender.peer, ); @@ -650,11 +650,11 @@ impl Operation for ConnectOp { tracing::info!( tx = %id, at = %target.peer, - assigned_location = ?op_storage.ring.own_location().location, + assigned_location = ?op_manager.ring.own_location().location, "Successfully completed connection", ); network_bridge.add_connection(sender.peer).await?; - op_storage.ring.add_connection( + op_manager.ring.add_connection( sender.location.ok_or(ConnectionError::LocationUnknown)?, sender.peer, ); @@ -720,20 +720,20 @@ fn try_returning_proxy_connection( async fn propagate_oc_to_accepted_peers( network_bridge: &mut NB, - op_storage: &OpManager, + op_manager: &OpManager, sender: PeerKeyLocation, other_peer: &PeerKeyLocation, msg: ConnectMsg, ) -> Result<(), OpError> { let id = msg.id(); - if op_storage.ring.should_accept( + if op_manager.ring.should_accept( other_peer .location .ok_or(ConnectionError::LocationUnknown)?, ) { tracing::info!(tx = %id, from = %sender.peer, to = %other_peer.peer, "Established connection"); network_bridge.add_connection(other_peer.peer).await?; - op_storage.ring.add_connection( + op_manager.ring.add_connection( other_peer .location .ok_or(ConnectionError::LocationUnknown)?, @@ -833,7 +833,7 @@ pub(crate) fn initial_request( /// Join ring routine, called upon performing a join operation for this node. pub(crate) async fn connect_request( tx: Transaction, - op_storage: &OpManager, + op_manager: &OpManager, conn_bridge: &mut NB, join_op: ConnectOp, ) -> Result<(), OpError> @@ -857,7 +857,7 @@ where ); conn_bridge.add_connection(gateway.peer).await?; - let assigned_location = op_storage.ring.own_location().location; + let assigned_location = op_manager.ring.own_location().location; let join_req = NetMessage::from(messages::ConnectMsg::Request { id: tx, msg: messages::ConnectRequest::StartReq { @@ -869,7 +869,7 @@ where }, }); conn_bridge.send(&gateway.peer, join_req).await?; - op_storage + op_manager .push( tx, OpEnum::Connect(Box::new(ConnectOp { diff --git a/crates/core/src/operations/get.rs b/crates/core/src/operations/get.rs index 15d0f4417..2419b14fb 100644 --- a/crates/core/src/operations/get.rs +++ b/crates/core/src/operations/get.rs @@ -138,7 +138,7 @@ impl Operation for GetOp { type Result = GetResult; fn load_or_init<'a>( - op_storage: &'a OpManager, + op_manager: &'a OpManager, msg: &'a Self::Message, ) -> BoxFuture<'a, Result, OpError>> { async move { @@ -147,13 +147,13 @@ impl Operation for GetOp { sender = Some(peer_key_loc.peer); }; let tx = *msg.id(); - match op_storage.pop(msg.id()) { + match op_manager.pop(msg.id()) { Ok(Some(OpEnum::Get(get_op))) => { Ok(OpInitialization { op: get_op, sender }) // was an existing operation, other peer messaged back } Ok(Some(op)) => { - let _ = op_storage.push(tx, op).await; + let _ = op_manager.push(tx, op).await; Err(OpError::OpNotPresent(tx)) } Ok(None) => { @@ -181,7 +181,7 @@ impl Operation for GetOp { fn process_message<'a, NB: NetworkBridge>( self, conn_manager: &'a mut NB, - op_storage: &'a OpManager, + op_manager: &'a OpManager, input: &'a Self::Message, client_id: Option, ) -> Pin> + Send + 'a>> { @@ -212,7 +212,7 @@ impl Operation for GetOp { first_response_time: None, step: Default::default(), }); - let own_loc = op_storage.ring.own_location(); + let own_loc = op_manager.ring.own_location(); return_msg = Some(GetMsg::SeekNode { key: key.clone(), id: *id, @@ -235,7 +235,7 @@ impl Operation for GetOp { let key: ContractKey = key.clone(); let fetch_contract = *fetch_contract; - let is_cached_contract = op_storage.ring.is_contract_cached(&key); + let is_cached_contract = op_manager.ring.is_contract_cached(&key); if let Some(s) = stats.as_mut() { s.caching_peer = Some(*target); } @@ -266,7 +266,7 @@ impl Operation for GetOp { state: None, contract: None, }, - sender: op_storage.ring.own_location(), + sender: op_manager.ring.own_location(), target: *sender, // return to requester }), None, @@ -275,7 +275,7 @@ impl Operation for GetOp { } let new_htl = htl - 1; - let Some(new_target) = op_storage + let Some(new_target) = op_manager .ring .closest_potentially_caching(&key, [&sender.peer].as_slice()) else { @@ -307,7 +307,7 @@ impl Operation for GetOp { } else if let ContractHandlerEvent::GetResponse { key: returned_key, response: value, - } = op_storage + } = op_manager .notify_contract_handler( ContractHandlerEvent::GetQuery { key: key.clone(), @@ -396,7 +396,7 @@ impl Operation for GetOp { if retries < MAX_RETRIES { // no response received from this peer, so skip it in the next iteration skip_list.push(target.peer); - if let Some(target) = op_storage + if let Some(target) = op_manager .ring .closest_potentially_caching(key, skip_list.as_slice()) .into_iter() @@ -472,7 +472,7 @@ impl Operation for GetOp { if require_contract { if let Some(contract) = &contract { // store contract first - let res = op_storage + let res = op_manager .notify_contract_handler( ContractHandlerEvent::Cache(contract.clone()), client_id, @@ -480,7 +480,7 @@ impl Operation for GetOp { .await?; match res { ContractHandlerEvent::CacheResult(Ok(_)) => { - op_storage.ring.contract_cached(&key); + op_manager.ring.contract_cached(&key); } ContractHandlerEvent::CacheResult(Err(err)) => { return Err(OpError::ContractError(err)); @@ -504,7 +504,7 @@ impl Operation for GetOp { stats, }; - op_storage + op_manager .notify_op_change( NetMessage::from(GetMsg::ReturnGet { id, @@ -525,7 +525,7 @@ impl Operation for GetOp { } let parameters = contract.as_ref().map(|c| c.params()); - let res = op_storage + let res = op_manager .notify_contract_handler( ContractHandlerEvent::PutQuery { key: key.clone(), @@ -701,7 +701,7 @@ enum GetState { /// Request to get the current value from a contract. pub(crate) async fn request_get( - op_storage: &OpManager, + op_manager: &OpManager, get_op: GetOp, client_id: Option, ) -> Result<(), OpError> { @@ -711,7 +711,7 @@ pub(crate) async fn request_get( // - a location in the network where the contract resides // - and the key of the contract value to get ( - op_storage + op_manager .ring .closest_potentially_caching(key, EMPTY) .into_iter() @@ -758,7 +758,7 @@ pub(crate) async fn request_get( }), }; - op_storage + op_manager .notify_op_change(NetMessage::from(msg), OpEnum::Get(op), client_id) .await?; } diff --git a/crates/core/src/operations/put.rs b/crates/core/src/operations/put.rs index 04709e8a2..609a52e1a 100644 --- a/crates/core/src/operations/put.rs +++ b/crates/core/src/operations/put.rs @@ -124,7 +124,7 @@ impl Operation for PutOp { type Result = PutResult; fn load_or_init<'a>( - op_storage: &'a OpManager, + op_manager: &'a OpManager, msg: &'a Self::Message, ) -> BoxFuture<'a, Result, OpError>> { async move { @@ -134,13 +134,13 @@ impl Operation for PutOp { }; let tx = *msg.id(); - match op_storage.pop(msg.id()) { + match op_manager.pop(msg.id()) { Ok(Some(OpEnum::Put(put_op))) => { // was an existing operation, the other peer messaged back Ok(OpInitialization { op: put_op, sender }) } Ok(Some(op)) => { - let _ = op_storage.push(tx, op).await; + let _ = op_manager.push(tx, op).await; Err(OpError::OpNotPresent(tx)) } Ok(None) => { @@ -167,7 +167,7 @@ impl Operation for PutOp { fn process_message<'a, NB: NetworkBridge>( self, conn_manager: &'a mut NB, - op_storage: &'a OpManager, + op_manager: &'a OpManager, input: &'a Self::Message, client_id: Option, ) -> Pin> + Send + 'a>> { @@ -185,7 +185,7 @@ impl Operation for PutOp { htl, target, } => { - let sender = op_storage.ring.own_location(); + let sender = op_manager.ring.own_location(); let key = contract.key(); tracing::debug!( @@ -219,7 +219,7 @@ impl Operation for PutOp { target, } => { let key = contract.key(); - let is_cached_contract = op_storage.ring.is_contract_cached(&key); + let is_cached_contract = op_manager.ring.is_contract_cached(&key); tracing::debug!( tx = %id, @@ -229,12 +229,12 @@ impl Operation for PutOp { ); if !is_cached_contract - && op_storage + && op_manager .ring .within_caching_distance(&Location::from(&key)) { tracing::debug!(tx = %id, %key, "Contract not cached @ peer {}", target.peer); - match try_to_cache_contract(op_storage, contract, &key, client_id).await { + match try_to_cache_contract(op_manager, contract, &key, client_id).await { Ok(_) => {} Err(err) => return Err(err), } @@ -253,7 +253,7 @@ impl Operation for PutOp { tracing::debug!(tx = %id, "Attempting contract value update"); let parameters = contract.params(); let new_value = put_contract( - op_storage, + op_manager, key.clone(), value.clone(), related_contracts.clone(), @@ -277,7 +277,7 @@ impl Operation for PutOp { if let Some(new_htl) = htl.checked_sub(1) { // forward changes in the contract to nodes closer to the contract location, if possible forward_changes( - op_storage, + op_manager, conn_manager, contract, new_value.clone(), @@ -287,7 +287,7 @@ impl Operation for PutOp { .await; } - let broadcast_to = op_storage + let broadcast_to = op_manager .ring .subscribers_of(&key) .map(|i| i.value().to_vec()) @@ -301,7 +301,7 @@ impl Operation for PutOp { match try_to_broadcast( (*id, client_id), - op_storage, + op_manager, self.state, broadcast_to, key.clone(), @@ -324,11 +324,11 @@ impl Operation for PutOp { sender, sender_subscribers, } => { - let target = op_storage.ring.own_location(); + let target = op_manager.ring.own_location(); tracing::debug!("Attempting contract value update"); let new_value = put_contract( - op_storage, + op_manager, key.clone(), new_value.clone(), RelatedContracts::default(), @@ -338,7 +338,7 @@ impl Operation for PutOp { .await?; tracing::debug!("Contract successfully updated"); - let broadcast_to = op_storage + let broadcast_to = op_manager .ring .subscribers_of(key) .map(|i| { @@ -359,7 +359,7 @@ impl Operation for PutOp { match try_to_broadcast( (*id, client_id), - op_storage, + op_manager, self.state, broadcast_to, key.clone(), @@ -382,7 +382,7 @@ impl Operation for PutOp { new_value, parameters, } => { - let sender = op_storage.ring.own_location(); + let sender = op_manager.ring.own_location(); let mut broadcasted_to = *broadcasted_to; let mut broadcasting = Vec::with_capacity(broadcast_to.len()); @@ -447,7 +447,7 @@ impl Operation for PutOp { _ => return Err(OpError::invalid_transition(self.id)), }; tracing::info!( - this_peer = %op_storage.ring.peer_key, + this_peer = %op_manager.ring.peer_key, "Peer completed contract value put", ); } @@ -458,7 +458,7 @@ impl Operation for PutOp { htl, } => { let key = contract.key(); - let peer_loc = op_storage.ring.own_location(); + let peer_loc = op_manager.ring.own_location(); tracing::debug!( %key, @@ -466,12 +466,12 @@ impl Operation for PutOp { "Forwarding changes, trying put the contract" ); - let cached_contract = op_storage.ring.is_contract_cached(&key); - let within_caching_dist = op_storage + let cached_contract = op_manager.ring.is_contract_cached(&key); + let within_caching_dist = op_manager .ring .within_caching_distance(&Location::from(&key)); if !cached_contract && within_caching_dist { - match try_to_cache_contract(op_storage, contract, &key, client_id).await { + match try_to_cache_contract(op_manager, contract, &key, client_id).await { Ok(_) => {} Err(err) => return Err(err), } @@ -484,7 +484,7 @@ impl Operation for PutOp { } // after the contract has been cached, push the update query let new_value = put_contract( - op_storage, + op_manager, key, new_value.clone(), RelatedContracts::default(), @@ -496,7 +496,7 @@ impl Operation for PutOp { // if successful, forward to the next closest peers (if any) if let Some(new_htl) = htl.checked_sub(1) { forward_changes( - op_storage, + op_manager, conn_manager, contract, new_value, @@ -530,17 +530,17 @@ fn build_op_result( } pub(super) async fn try_to_cache_contract<'a>( - op_storage: &'a OpManager, + op_manager: &'a OpManager, contract: &ContractContainer, key: &ContractKey, client_id: Option, ) -> Result<(), OpError> { // this node does not have the contract, so instead store the contract and execute the put op. - let res = op_storage + let res = op_manager .notify_contract_handler(ContractHandlerEvent::Cache(contract.clone()), client_id) .await?; if let ContractHandlerEvent::CacheResult(Ok(_)) = res { - op_storage.ring.contract_cached(key); + op_manager.ring.contract_cached(key); tracing::debug!("Contract successfully cached"); Ok(()) } else { @@ -553,7 +553,7 @@ pub(super) async fn try_to_cache_contract<'a>( async fn try_to_broadcast( (id, client_id): (Transaction, Option), - op_storage: &OpManager, + op_manager: &OpManager, state: Option, broadcast_to: Vec, key: ContractKey, @@ -590,7 +590,7 @@ async fn try_to_broadcast( state: new_state, stats: None, }; - op_storage + op_manager .notify_op_change( NetMessage::from(return_msg.unwrap()), OpEnum::Put(op), @@ -658,7 +658,7 @@ enum PutState { /// Request to insert/update a value into a contract. pub(crate) async fn request_put( - op_storage: &OpManager, + op_manager: &OpManager, mut put_op: PutOp, client_id: Option, ) -> Result<(), OpError> { @@ -668,12 +668,12 @@ pub(crate) async fn request_put( return Err(OpError::UnexpectedOpState); }; - let sender = op_storage.ring.own_location(); + let sender = op_manager.ring.own_location(); // the initial request must provide: // - a peer as close as possible to the contract location // - and the value to put - let target = op_storage + let target = op_manager .ring .closest_potentially_caching(&key, [&sender.peer].as_slice()) .into_iter() @@ -709,7 +709,7 @@ pub(crate) async fn request_put( stats: put_op.stats, }; - op_storage + op_manager .notify_op_change(NetMessage::from(msg), OpEnum::Put(op), client_id) .await?; } @@ -720,7 +720,7 @@ pub(crate) async fn request_put( } async fn put_contract( - op_storage: &OpManager, + op_manager: &OpManager, key: ContractKey, state: WrappedState, related_contracts: RelatedContracts<'static>, @@ -728,7 +728,7 @@ async fn put_contract( client_id: Option, ) -> Result { // after the contract has been cached, push the update query - match op_storage + match op_manager .notify_contract_handler( ContractHandlerEvent::PutQuery { key, @@ -759,7 +759,7 @@ async fn put_contract( /// Communicate changes in the contract to other peers nearby the contract location. /// This operation is "fire and forget" and the node does not keep track if is successful or not. async fn forward_changes( - op_storage: &OpManager, + op_manager: &OpManager, conn_manager: &CB, contract: &ContractContainer, new_value: WrappedState, @@ -771,8 +771,8 @@ async fn forward_changes( let key = contract.key(); let contract_loc = Location::from(&key); const EMPTY: &[PeerId] = &[]; - let forward_to = op_storage.ring.closest_potentially_caching(&key, EMPTY); - let own_loc = op_storage.ring.own_location().location.expect("infallible"); + let forward_to = op_manager.ring.closest_potentially_caching(&key, EMPTY); + let own_loc = op_manager.ring.own_location().location.expect("infallible"); if let Some(peer) = forward_to { let other_loc = peer.location.as_ref().expect("infallible"); let other_distance = contract_loc.distance(other_loc); diff --git a/crates/core/src/operations/subscribe.rs b/crates/core/src/operations/subscribe.rs index b75acadb1..1c789a4e0 100644 --- a/crates/core/src/operations/subscribe.rs +++ b/crates/core/src/operations/subscribe.rs @@ -53,7 +53,7 @@ impl Operation for SubscribeOp { type Result = SubscribeResult; fn load_or_init<'a>( - op_storage: &'a OpManager, + op_manager: &'a OpManager, msg: &'a Self::Message, ) -> BoxFuture<'a, Result, OpError>> { async move { @@ -63,7 +63,7 @@ impl Operation for SubscribeOp { }; let id = *msg.id(); - match op_storage.pop(msg.id()) { + match op_manager.pop(msg.id()) { Ok(Some(OpEnum::Subscribe(subscribe_op))) => { // was an existing operation, the other peer messaged back Ok(OpInitialization { @@ -72,7 +72,7 @@ impl Operation for SubscribeOp { }) } Ok(Some(op)) => { - let _ = op_storage.push(id, op).await; + let _ = op_manager.push(id, op).await; Err(OpError::OpNotPresent(id)) } Ok(None) => { @@ -98,7 +98,7 @@ impl Operation for SubscribeOp { fn process_message<'a, NB: NetworkBridge>( self, conn_manager: &'a mut NB, - op_storage: &'a OpManager, + op_manager: &'a OpManager, input: &'a Self::Message, client_id: Option, ) -> Pin> + Send + 'a>> { @@ -113,7 +113,7 @@ impl Operation for SubscribeOp { self.state, Some(SubscribeState::AwaitingResponse { .. }) )); - let sender = op_storage.ring.own_location(); + let sender = op_manager.ring.own_location(); new_state = self.state; return_msg = Some(SubscribeMsg::SeekNode { id: *id, @@ -132,7 +132,7 @@ impl Operation for SubscribeOp { skip_list, htl, } => { - let sender = op_storage.ring.own_location(); + let sender = op_manager.ring.own_location(); let return_err = || -> OperationResult { OperationResult { return_msg: Some(NetMessage::from(SubscribeMsg::ReturnSub { @@ -146,10 +146,10 @@ impl Operation for SubscribeOp { } }; - if !op_storage.ring.is_contract_cached(key) { + if !op_manager.ring.is_contract_cached(key) { tracing::debug!(tx = %id, "Contract {} not found at {}, trying other peer", key, target.peer); - let Some(new_target) = op_storage + let Some(new_target) = op_manager .ring .closest_potentially_caching(key, [&sender.peer].as_slice()) else { @@ -181,7 +181,7 @@ impl Operation for SubscribeOp { .into(), ) .await?; - } else if op_storage.ring.add_subscriber(key, *subscriber).is_err() { + } else if op_manager.ring.add_subscriber(key, *subscriber).is_err() { // max number of subscribers for this contract reached return Ok(return_err()); } @@ -229,13 +229,13 @@ impl Operation for SubscribeOp { }) => { if retries < MAX_RETRIES { skip_list.push(sender.peer); - if let Some(target) = op_storage + if let Some(target) = op_manager .ring .closest_potentially_caching(key, skip_list.as_slice()) .into_iter() .next() { - let subscriber = op_storage.ring.own_location(); + let subscriber = op_manager.ring.own_location(); return_msg = Some(SubscribeMsg::SeekNode { id: *id, key: key.clone(), @@ -278,7 +278,7 @@ impl Operation for SubscribeOp { provider = %sender.peer, "Subscribed to contract" ); - op_storage.ring.add_subscription(key.clone()); + op_manager.ring.add_subscription(key.clone()); // fixme: should inform back to the network event loop in case a client is waiting for response let _ = client_id; new_state = Some(SubscribeState::Completed); @@ -337,19 +337,19 @@ enum SubscribeState { /// Request to subscribe to value changes from a contract. pub(crate) async fn request_subscribe( - op_storage: &OpManager, + op_manager: &OpManager, sub_op: SubscribeOp, client_id: Option, ) -> Result<(), OpError> { let (target, _id) = if let Some(SubscribeState::PrepareRequest { id, key }) = &sub_op.state { - if !op_storage.ring.is_contract_cached(key) { + if !op_manager.ring.is_contract_cached(key) { return Err(OpError::ContractError(ContractError::ContractNotFound( key.clone(), ))); } const EMPTY: &[PeerId] = &[]; ( - op_storage + op_manager .ring .closest_potentially_caching(key, EMPTY) .into_iter() @@ -372,7 +372,7 @@ pub(crate) async fn request_subscribe( id, state: new_state, }; - op_storage + op_manager .notify_op_change(NetMessage::from(msg), OpEnum::Subscribe(op), client_id) .await?; } diff --git a/crates/core/src/operations/update.rs b/crates/core/src/operations/update.rs index e0ebce09d..6e87cae6e 100644 --- a/crates/core/src/operations/update.rs +++ b/crates/core/src/operations/update.rs @@ -36,7 +36,7 @@ impl Operation for UpdateOp { type Result = UpdateResult; fn load_or_init<'a>( - _op_storage: &'a crate::node::OpManager, + _op_manager: &'a crate::node::OpManager, _msg: &'a Self::Message, ) -> BoxFuture<'a, Result, OpError>> { todo!() @@ -49,7 +49,7 @@ impl Operation for UpdateOp { fn process_message<'a, NB: NetworkBridge>( self, _conn_manager: &'a mut NB, - _op_storage: &'a crate::node::OpManager, + _op_manager: &'a crate::node::OpManager, _input: &Self::Message, _client_id: Option, ) -> std::pin::Pin< From 8db430df2e21a82009881318b732a466e27588d7 Mon Sep 17 00:00:00 2001 From: Ignacio Duart Date: Tue, 28 Nov 2023 11:58:06 +0100 Subject: [PATCH 03/12] Register connects and disconnects at ring level Simplify event register trait to avoid unnecessary complicated clone --- .../core/src/node/network_bridge/in_memory.rs | 31 ++------- .../src/node/network_bridge/inter_process.rs | 15 ++++- .../src/node/network_bridge/p2p_protoc.rs | 66 +++---------------- crates/core/src/node/op_state_manager.rs | 9 ++- crates/core/src/ring.rs | 14 +++- 5 files changed, 45 insertions(+), 90 deletions(-) diff --git a/crates/core/src/node/network_bridge/in_memory.rs b/crates/core/src/node/network_bridge/in_memory.rs index d70aaf139..482e91f5a 100644 --- a/crates/core/src/node/network_bridge/in_memory.rs +++ b/crates/core/src/node/network_bridge/in_memory.rs @@ -20,18 +20,18 @@ use crate::{ tracing::NetEventLog, }; +#[derive(Clone)] pub(in crate::node) struct MemoryConnManager { transport: InMemoryTransport, - log_register: Arc>>, + log_register: Arc, op_manager: Arc, msg_queue: Arc>>, - peer: PeerId, } impl MemoryConnManager { pub fn new( peer: PeerId, - log_register: Box, + log_register: impl NetEventRegister, op_manager: Arc, add_noise: bool, ) -> Self { @@ -54,28 +54,9 @@ impl MemoryConnManager { Self { transport, - log_register: Arc::new(Mutex::new(log_register)), + log_register: Arc::new(log_register), op_manager, msg_queue, - peer, - } - } -} - -impl Clone for MemoryConnManager { - fn clone(&self) -> Self { - let log_register = loop { - if let Ok(lr) = self.log_register.try_lock() { - break lr.trait_clone(); - } - std::thread::sleep(Duration::from_nanos(50)); - }; - Self { - transport: self.transport.clone(), - log_register: Arc::new(Mutex::new(log_register)), - op_manager: self.op_manager.clone(), - msg_queue: self.msg_queue.clone(), - peer: self.peer, } } } @@ -84,9 +65,7 @@ impl Clone for MemoryConnManager { impl NetworkBridge for MemoryConnManager { async fn send(&self, target: &PeerId, msg: NetMessage) -> super::ConnResult<()> { self.log_register - .try_lock() - .expect("unique lock") - .register_events(NetEventLog::from_outbound_msg(&msg, &self.op_manager)) + .register_events(NetEventLog::from_outbound_msg(&msg, &self.op_manager.ring)) .await; self.op_manager.sending_transaction(target, &msg); let msg = bincode::serialize(&msg)?; diff --git a/crates/core/src/node/network_bridge/inter_process.rs b/crates/core/src/node/network_bridge/inter_process.rs index dafdfdc8c..44c1fa2ed 100644 --- a/crates/core/src/node/network_bridge/inter_process.rs +++ b/crates/core/src/node/network_bridge/inter_process.rs @@ -11,7 +11,8 @@ use tokio::{ use crate::{ message::NetMessage, - node::{testing_impl::NetworkBridgeExt, PeerId}, + node::{testing_impl::NetworkBridgeExt, OpManager, PeerId}, + tracing::{NetEventLog, NetEventRegister}, }; use super::{ConnectionError, NetworkBridge}; @@ -23,15 +24,22 @@ static INCOMING_DATA: OnceLock> = OnceLock::new(); #[derive(Clone)] pub struct InterProcessConnManager { recv: Receiver, + log_register: Arc, + op_manager: Arc, output: Arc>>, } impl InterProcessConnManager { - pub(in crate::node) fn new() -> Self { + pub(in crate::node) fn new( + log_register: impl NetEventRegister, + op_manager: Arc, + ) -> Self { let (sender, recv) = tokio::sync::watch::channel(vec![]); INCOMING_DATA.set(sender).expect("shouldn't be set"); Self { recv, + log_register: Arc::new(log_register), + op_manager, output: Arc::new(Mutex::new(BufWriter::new(tokio::io::stdout()))), } } @@ -81,6 +89,9 @@ impl NetworkBridgeExt for InterProcessConnManager { impl NetworkBridge for InterProcessConnManager { async fn send(&self, target: &PeerId, msg: NetMessage) -> super::ConnResult<()> { tracing::debug!(%target, ?msg, "sending network message out"); + self.log_register + .register_events(NetEventLog::from_outbound_msg(&msg, &self.op_manager.ring)) + .await; let data = bincode::serialize(&(*target, msg))?; let output = &mut *self.output.lock().await; output.write_all(&(data.len() as u32).to_le_bytes()).await?; diff --git a/crates/core/src/node/network_bridge/p2p_protoc.rs b/crates/core/src/node/network_bridge/p2p_protoc.rs index 5586159be..1cda18763 100644 --- a/crates/core/src/node/network_bridge/p2p_protoc.rs +++ b/crates/core/src/node/network_bridge/p2p_protoc.rs @@ -5,7 +5,6 @@ use std::{ pin::Pin, sync::Arc, task::Poll, - time::Duration, }; use asynchronous_codec::{BytesMut, Framed}; @@ -32,10 +31,7 @@ use libp2p::{ }, InboundUpgrade, Multiaddr, OutboundUpgrade, PeerId as Libp2pPeerId, Swarm, }; -use tokio::sync::{ - mpsc::{self, Receiver, Sender}, - Mutex, -}; +use tokio::sync::mpsc::{self, Receiver, Sender}; use tracing::Instrument; use unsigned_varint::codec::UviBytes; @@ -125,12 +121,13 @@ fn multiaddr_from_connection(conn: (IpAddr, u16)) -> Multiaddr { type P2pBridgeEvent = Either<(FreenetPeerId, Box), NodeEvent>; +#[derive(Clone)] pub(crate) struct P2pBridge { active_net_connections: Arc>, accepted_peers: Arc>, ev_listener_tx: Sender, op_manager: Arc, - log_register: Arc>>, + log_register: Arc, } impl P2pBridge { @@ -147,51 +144,7 @@ impl P2pBridge { accepted_peers: Arc::new(DashSet::new()), ev_listener_tx: sender, op_manager, - log_register: Arc::new(Mutex::new(Box::new(event_register))), - } - } -} - -#[cfg(any(debug_assertions, test))] -static CONTESTED_BRIDGE_CLONES: std::sync::atomic::AtomicUsize = - std::sync::atomic::AtomicUsize::new(0); - -#[cfg(any(debug_assertions, test))] -static TOTAL_BRIDGE_CLONES: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); - -impl Clone for P2pBridge { - fn clone(&self) -> Self { - let log_register = loop { - if let Ok(lr) = self.log_register.try_lock() { - #[cfg(any(debug_assertions, test))] - { - TOTAL_BRIDGE_CLONES.fetch_add(1, std::sync::atomic::Ordering::AcqRel); - } - break lr.trait_clone(); - } - #[cfg(any(debug_assertions, test))] - { - let contested = - CONTESTED_BRIDGE_CLONES.fetch_add(1, std::sync::atomic::Ordering::AcqRel); - - if contested % 100 == 0 { - let total = TOTAL_BRIDGE_CLONES.load(std::sync::atomic::Ordering::Acquire); - if total > 0 { - let threshold = (total as f64 * 0.01) as usize; - if contested / total > threshold { - tracing::debug!("p2p bridge clone contested more than 1% of the time"); - } - } - } - } - std::thread::sleep(Duration::from_nanos(50)); - }; - Self { - active_net_connections: self.active_net_connections.clone(), - accepted_peers: self.accepted_peers.clone(), - ev_listener_tx: self.ev_listener_tx.clone(), - op_manager: self.op_manager.clone(), - log_register: Arc::new(Mutex::new(log_register)), + log_register: Arc::new(event_register), } } } @@ -216,18 +169,17 @@ impl NetworkBridge for P2pBridge { .await .map_err(|_| ConnectionError::SendNotCompleted)?; self.log_register - .try_lock() - .expect("single reference") - .register_events(Either::Left(NetEventLog::disconnected(peer))) + .register_events(Either::Left(NetEventLog::disconnected( + &self.op_manager.ring, + peer, + ))) .await; Ok(()) } async fn send(&self, target: &FreenetPeerId, msg: NetMessage) -> super::ConnResult<()> { self.log_register - .try_lock() - .expect("single reference") - .register_events(NetEventLog::from_outbound_msg(&msg, &self.op_manager)); + .register_events(NetEventLog::from_outbound_msg(&msg, &self.op_manager.ring)); self.op_manager.sending_transaction(target, &msg); self.ev_listener_tx .send(Left((*target, Box::new(msg)))) diff --git a/crates/core/src/node/op_state_manager.rs b/crates/core/src/node/op_state_manager.rs index 71d2a677e..4a10d06a5 100644 --- a/crates/core/src/node/op_state_manager.rs +++ b/crates/core/src/node/op_state_manager.rs @@ -59,14 +59,19 @@ pub(crate) struct OpManager { } impl OpManager { - pub(super) fn new( + pub(super) fn new( notification_channel: EventLoopNotificationsSender, contract_handler: ContractHandlerChannel, config: &NodeConfig, gateways: &[PeerKeyLocation], event_register: ER, ) -> Result { - let ring = Ring::new::(config, gateways, notification_channel.clone())?; + let ring = Ring::new( + config, + gateways, + notification_channel.clone(), + event_register.clone(), + )?; let ops = Arc::new(Ops::default()); let (new_transactions, rx) = tokio::sync::mpsc::channel(100); diff --git a/crates/core/src/ring.rs b/crates/core/src/ring.rs index 1df1634a1..c552ed35c 100644 --- a/crates/core/src/ring.rs +++ b/crates/core/src/ring.rs @@ -39,7 +39,7 @@ use tracing::Instrument; use crate::message::TransactionType; use crate::topology::{AcquisitionStrategy, TopologyManager}; -use crate::tracing::{EventRegister, NetEventRegister}; +use crate::tracing::{EventRegister, NetEventLog, NetEventRegister}; use crate::util::Contains; use crate::{ config::GlobalExecutor, @@ -197,6 +197,7 @@ pub(crate) struct Ring { // A peer which has been blacklisted to perform actions regarding a given contract. // todo: add blacklist // contract_blacklist: Arc>>, + event_register: Box, } // /// A data type that represents the fact that a peer has been blacklisted @@ -223,10 +224,11 @@ impl Ring { /// connection of a peer in the network). const MAX_HOPS_TO_LIVE: usize = 10; - pub fn new( + pub fn new( config: &NodeConfig, gateways: &[PeerKeyLocation], event_loop_notifier: EventLoopNotificationsSender, + event_register: ER, ) -> Result, anyhow::Error> { let (live_tx_tracker, missing_candidate_rx) = LiveTransactionTracker::new(); @@ -260,7 +262,7 @@ impl Ring { }; let router = Arc::new(RwLock::new(Router::new(&[]))); - GlobalExecutor::spawn(Self::refresh_router::(router.clone())); + GlobalExecutor::spawn(Self::refresh_router::(router.clone())); // Just initialize with a fake location, this will be later updated when the peer has an actual location assigned. let topology_manager = RwLock::new(TopologyManager::new(Location::new(0.0))); @@ -282,6 +284,7 @@ impl Ring { subscriptions: RwLock::new(Vec::new()), open_connections: AtomicUsize::new(0), live_tx_tracker: live_tx_tracker.clone(), + event_register: Box::new(event_register), }; if let Some(loc) = config.location { @@ -290,6 +293,7 @@ impl Ring { } ring.update_location(Some(loc)); for PeerKeyLocation { peer, location } in gateways { + // FIXME: this is problematic cause gateways will take all spots then! // all gateways are aware of each other ring.add_connection((*location).unwrap(), *peer); } @@ -444,6 +448,8 @@ impl Ring { pub fn add_connection(&self, loc: Location, peer: PeerId) { let mut cbl = self.connections_by_location.write(); + self.event_register + .register_events(Either::Left(NetEventLog::connected(self, peer, loc))); cbl.entry(loc).or_default().push(Connection { location: PeerKeyLocation { peer, @@ -587,6 +593,8 @@ impl Ring { subs }); } + self.event_register + .register_events(Either::Left(NetEventLog::disconnected(self, &peer))); self.open_connections .fetch_sub(1, std::sync::atomic::Ordering::SeqCst); } From 8a1d1b9ca9bc05af67e93f654e94915767934fce Mon Sep 17 00:00:00 2001 From: Ignacio Duart Date: Tue, 28 Nov 2023 11:58:39 +0100 Subject: [PATCH 04/12] Report connects/disconnects to metrics server --- Cargo.lock | 396 ++++++++++++----- crates/core/Cargo.toml | 1 + crates/core/src/tracing.rs | 506 ++++++++++++++-------- crates/fdev/src/network_metrics_server.rs | 20 +- 4 files changed, 614 insertions(+), 309 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51e1b9499..f003b325f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "gimli 0.28.0", + "gimli 0.28.1", ] [[package]] @@ -236,7 +236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", - "event-listener", + "event-listener 2.5.3", "futures-core", ] @@ -246,27 +246,57 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ - "async-lock", + "async-lock 2.8.0", "autocfg 1.1.0", "cfg-if", "concurrent-queue", - "futures-lite", + "futures-lite 1.13.0", "log", "parking", - "polling", + "polling 2.8.0", "rustix 0.37.27", "slab", "socket2 0.4.10", "waker-fn", ] +[[package]] +name = "async-io" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6d3b15875ba253d1110c740755e246537483f152fa334f91abd7fe84c88b3ff" +dependencies = [ + "async-lock 3.1.2", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.0.1", + "parking", + "polling 3.3.1", + "rustix 0.38.25", + "slab", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "async-lock" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ - "event-listener", + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea8b3453dd7cc96711834b75400d671b73e3656975fa68d9f277163b7f7e316" +dependencies = [ + "event-listener 4.0.0", + "event-listener-strategy", + "pin-project-lite", ] [[package]] @@ -317,6 +347,16 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atomic-write-file" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae364a6c1301604bbc6dfbf8c385c47ff82301dd01eef506195a029196d8d04" +dependencies = [ + "nix 0.27.1", + "rand 0.8.5", +] + [[package]] name = "attohttpc" version = "0.24.1" @@ -662,7 +702,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -689,9 +729,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.8" +version = "4.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" +checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" dependencies = [ "clap_builder", "clap_derive", @@ -699,9 +739,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.8" +version = "4.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" +checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" dependencies = [ "anstream", "anstyle", @@ -753,9 +793,9 @@ dependencies = [ [[package]] name = "config" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" +checksum = "23738e11972c7643e4ec947840fc463b6a571afcd3e735bdfce7d03c7a784aca" dependencies = [ "async-trait", "json5", @@ -1134,7 +1174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "lock_api", "once_cell", "parking_lot_core", @@ -1142,15 +1182,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "data-encoding-macro" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" +checksum = "20c01c06f5f429efdf2bae21eb67c28b3df3cf85b7dd2d8ef09c0838dac5d33e" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -1158,9 +1198,9 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" +checksum = "0047d07f2c89b17dd631c80450d69841a6b5d7fb17278cbc43d7e4cfcf2576f3" dependencies = [ "data-encoding", "syn 1.0.109", @@ -1308,15 +1348,16 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" +checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0" dependencies = [ "curve25519-dalek", "ed25519", "rand_core 0.6.4", "serde", "sha2", + "subtle", "zeroize", ] @@ -1402,12 +1443,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1427,6 +1468,27 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.0", + "pin-project-lite", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -1481,9 +1543,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a56f0780318174bad1c127063fd0c5fdfb35398e3cd79ffaab931a6c79df80" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" [[package]] name = "filetime" @@ -1532,9 +1594,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -1593,6 +1655,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", + "tokio-tungstenite", "tower-http", "tracing", "tracing-opentelemetry", @@ -1750,6 +1813,16 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-lite" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3831c2651acb5177cbd83943f3d9c8912c5ad03c76afcc0e9511ba568ec5ebb" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.29" @@ -1862,9 +1935,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glob" @@ -1874,9 +1947,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "h2" -version = "0.3.21" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", @@ -1884,7 +1957,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap 2.1.0", "slab", "tokio", "tokio-util", @@ -1902,9 +1975,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash 0.8.6", "allocator-api2", @@ -1916,7 +1989,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -2116,23 +2189,33 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "if-addrs" -version = "0.7.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" +checksum = "cabb0019d51a643781ff15c9c8a3e5dedc365c47211270f4e8f82812fedd8f0a" dependencies = [ "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "if-watch" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb892e5777fe09e16f3d44de7802f4daa7267ecbe8c466f19d94e25bb0c303e" +checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e" dependencies = [ - "async-io", + "async-io 2.2.1", "core-foundation", "fnv", "futures", @@ -2182,7 +2265,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "serde", ] @@ -2270,12 +2353,12 @@ dependencies = [ "crossbeam-utils", "curl", "curl-sys", - "event-listener", - "futures-lite", + "event-listener 2.5.3", + "futures-lite 1.13.0", "http", "log", "once_cell", - "polling", + "polling 2.8.0", "slab", "sluice", "tracing", @@ -2319,9 +2402,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -2812,9 +2895,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.26.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" dependencies = [ "cc", "pkg-config", @@ -2869,11 +2952,11 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lru" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efa59af2ddfad1854ae27d75009d538d0998b4b2fd47083e743ac1a10e46c60" +checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" dependencies = [ - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -3386,9 +3469,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.95" +version = "0.9.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" +checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" dependencies = [ "cc", "libc", @@ -3549,7 +3632,7 @@ dependencies = [ "libc", "redox_syscall 0.4.1", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -3600,9 +3683,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" @@ -3736,6 +3819,20 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "polling" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix 0.38.25", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -3818,9 +3915,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -4350,9 +4447,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ef35bf3e7fe15a53c4ab08a998e42271eab13eb0db224126bc7bc4c4bad96d" +checksum = "af6c4b23d99685a1408194da11270ef8e9809aff951cc70ec9b17350b087e474" dependencies = [ "const-oid", "digest", @@ -4439,9 +4536,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.24" +version = "0.38.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234" +checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" dependencies = [ "bitflags 2.4.1", "errno", @@ -4452,9 +4549,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.8" +version = "0.21.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" +checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" dependencies = [ "log", "ring 0.17.5", @@ -4570,9 +4667,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] @@ -4610,9 +4707,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -4872,9 +4969,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e50c216e3624ec8e7ecd14c6a6a6370aad6ee5d8cfc3ab30b5162eeeef2ed33" +checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf" dependencies = [ "sqlx-core", "sqlx-macros", @@ -4885,9 +4982,9 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d" +checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd" dependencies = [ "ahash 0.8.6", "atoi", @@ -4897,7 +4994,7 @@ dependencies = [ "crossbeam-queue", "dotenvy", "either", - "event-listener", + "event-listener 2.5.3", "futures-channel", "futures-core", "futures-intrusive", @@ -4928,9 +5025,9 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a793bb3ba331ec8359c1853bd39eed32cdd7baaf22c35ccf5c92a7e8d1189ec" +checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5" dependencies = [ "proc-macro2", "quote", @@ -4941,10 +5038,11 @@ dependencies = [ [[package]] name = "sqlx-macros-core" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4ee1e104e00dedb6aa5ffdd1343107b0a4702e862a84320ee7cc74782d96fc" +checksum = "d0bd4519486723648186a08785143599760f7cc81c52334a55d6a83ea1e20841" dependencies = [ + "atomic-write-file", "dotenvy", "either", "heck", @@ -4966,9 +5064,9 @@ dependencies = [ [[package]] name = "sqlx-mysql" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db" +checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4" dependencies = [ "atoi", "base64 0.21.5", @@ -5008,9 +5106,9 @@ dependencies = [ [[package]] name = "sqlx-postgres" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624" +checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24" dependencies = [ "atoi", "base64 0.21.5", @@ -5047,9 +5145,9 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59dc83cf45d89c555a577694534fcd1b55c545a816c816ce51f20bbe56a4f3f" +checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490" dependencies = [ "atoi", "flume", @@ -5065,6 +5163,7 @@ dependencies = [ "sqlx-core", "tracing", "url", + "urlencoding", ] [[package]] @@ -5099,7 +5198,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51481a2abc8af47b7447b39b221ce806728e1dcbbb1354b0d5bacb1e5e1f72de" dependencies = [ "async-channel", - "async-io", + "async-io 1.13.0", "atomic", "crossbeam-channel", "futures", @@ -5229,7 +5328,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall 0.4.1", - "rustix 0.38.24", + "rustix 0.38.25", "windows-sys 0.48.0", ] @@ -5792,12 +5891,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna 0.4.0", + "idna 0.5.0", "percent-encoding", ] @@ -5821,9 +5920,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" [[package]] name = "valuable" @@ -5882,9 +5981,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "serde", @@ -5894,9 +5993,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", @@ -5932,9 +6031,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5942,9 +6041,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", @@ -5955,9 +6054,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "wasm-encoder" @@ -6152,12 +6251,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.24.0" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" -dependencies = [ - "rustls-webpki", -] +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "wg" @@ -6228,7 +6324,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" dependencies = [ "windows-core", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -6237,7 +6333,7 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -6259,7 +6355,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -6268,21 +6373,42 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.33.0" @@ -6295,6 +6421,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.33.0" @@ -6307,6 +6439,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.33.0" @@ -6319,6 +6457,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.33.0" @@ -6331,12 +6475,24 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.33.0" @@ -6349,6 +6505,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" version = "0.5.19" @@ -6456,9 +6618,9 @@ dependencies = [ [[package]] name = "yamux" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0329ef377816896f014435162bb3711ea7a07729c23d0960e6f8048b21b8fe91" +checksum = "9ed0164ae619f2dc144909a9f082187ebb5893693d8c0196e8085283ccd4b776" dependencies = [ "futures", "log", @@ -6500,9 +6662,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index f5c1fe6fe..0b108c140 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -55,6 +55,7 @@ stretto = { features = ["async", "sync"], version = "0.8" } tar = { version = "0.4.38" } thiserror = "1" tokio = { features = ["fs", "macros", "rt-multi-thread", "sync", "process"], version = "1" } +tokio-tungstenite = "0.20" tower-http = { features = ["fs", "trace"], version = "0.4" } ulid = { features = ["serde"], version = "0.4" } unsigned-varint = "0.7" diff --git a/crates/core/src/tracing.rs b/crates/core/src/tracing.rs index f04b12425..c48f12fa9 100644 --- a/crates/core/src/tracing.rs +++ b/crates/core/src/tracing.rs @@ -7,12 +7,13 @@ use futures::{future::BoxFuture, FutureExt}; use serde::{Deserialize, Serialize}; use tokio::{ fs::OpenOptions, - io::AsyncSeekExt, + net::TcpStream, sync::{ mpsc::{self}, Mutex, }, }; +use tokio_tungstenite::MaybeTlsStream; use crate::{ config::GlobalExecutor, @@ -20,7 +21,7 @@ use crate::{ message::{NetMessage, Transaction}, node::PeerId, operations::{connect, get::GetMsg, put::PutMsg, subscribe::SubscribeMsg}, - ring::{Location, PeerKeyLocation}, + ring::{Location, PeerKeyLocation, Ring}, router::RouteEvent, DynError, }; @@ -37,17 +38,11 @@ struct ListenerLogId(usize); /// A type that reacts to incoming messages from the network and records information about them. pub(crate) trait NetEventRegister: std::any::Any + Send + Sync + 'static { fn register_events<'a>( - &'a mut self, + &'a self, events: Either, Vec>>, ) -> BoxFuture<'a, ()>; - fn trait_clone(&self) -> Box; - fn as_any(&self) -> &dyn std::any::Any - where - Self: Sized, - { - self as _ - } fn notify_of_time_out(&mut self, tx: Transaction) -> BoxFuture<()>; + fn trait_clone(&self) -> Box; } #[cfg(feature = "trace-ot")] @@ -63,11 +58,11 @@ impl CombinedRegister { #[cfg(feature = "trace-ot")] impl NetEventRegister for CombinedRegister { fn register_events<'a>( - &'a mut self, + &'a self, events: Either, Vec>>, ) -> BoxFuture<'a, ()> { async move { - for registry in &mut self.0 { + for registry in &self.0 { registry.register_events(events.clone()).await; } } @@ -111,28 +106,39 @@ pub(crate) struct NetEventLog<'a> { impl<'a> NetEventLog<'a> { pub fn route_event( tx: &'a Transaction, - op_storage: &'a OpManager, + op_manager: &'a OpManager, route_event: &RouteEvent, ) -> Self { NetEventLog { tx, - peer_id: &op_storage.ring.peer_key, + peer_id: &op_manager.ring.peer_key, kind: EventKind::Route(route_event.clone()), } } - pub fn disconnected(from: &'a PeerId) -> Self { + pub fn connected(ring: &'a Ring, peer: PeerId, location: Location) -> Self { NetEventLog { tx: Transaction::NULL, - peer_id: from, - kind: EventKind::Disconnected, + peer_id: &ring.peer_key, + kind: EventKind::Connect(ConnectEvent::Connected { + this: ring.own_location(), + connected: PeerKeyLocation { + peer, + location: Some(location), + }, + }), } } - pub fn from_outbound_msg( - msg: &'a NetMessage, - op_storage: &'a OpManager, - ) -> Either> { + pub fn disconnected(ring: &'a Ring, from: &'a PeerId) -> Self { + NetEventLog { + tx: Transaction::NULL, + peer_id: &ring.peer_key, + kind: EventKind::Disconnected { from: *from }, + } + } + + pub fn from_outbound_msg(msg: &'a NetMessage, ring: &'a Ring) -> Either> { let kind = match msg { NetMessage::Connect(connect::ConnectMsg::Response { msg: @@ -143,7 +149,7 @@ impl<'a> NetEventLog<'a> { }, .. }) => { - let this_peer = op_storage.ring.own_location(); + let this_peer = ring.own_location(); if peers.contains(&this_peer) { EventKind::Connect(ConnectEvent::Connected { this: this_peer, @@ -164,7 +170,7 @@ impl<'a> NetEventLog<'a> { }, .. }) => { - let this_peer = op_storage.ring.own_location(); + let this_peer = ring.own_location(); if accepted_by.contains(&this_peer) { EventKind::Connect(ConnectEvent::Connected { this: this_peer, @@ -181,14 +187,14 @@ impl<'a> NetEventLog<'a> { }; Either::Left(NetEventLog { tx: msg.id(), - peer_id: &op_storage.ring.peer_key, + peer_id: &ring.peer_key, kind, }) } pub fn from_inbound_msg( msg: &'a NetMessage, - op_storage: &'a OpManager, + op_manager: &'a OpManager, ) -> Either> { let kind = match msg { NetMessage::Connect(connect::ConnectMsg::Response { @@ -200,7 +206,7 @@ impl<'a> NetEventLog<'a> { }, .. }) => { - let this_peer = &op_storage.ring.peer_key; + let this_peer = &op_manager.ring.peer_key; let mut events = peers .iter() .map(|peer| { @@ -241,7 +247,7 @@ impl<'a> NetEventLog<'a> { } NetMessage::Put(PutMsg::SuccessfulUpdate { new_value, .. }) => { EventKind::Put(PutEvent::PutSuccess { - requester: op_storage.ring.peer_key, + requester: op_manager.ring.peer_key, value: new_value.clone(), }) } @@ -283,7 +289,7 @@ impl<'a> NetEventLog<'a> { }; Either::Left(NetEventLog { tx: msg.id(), - peer_id: &op_storage.ring.peer_key, + peer_id: &op_manager.ring.peer_key, kind, }) } @@ -377,12 +383,16 @@ pub(crate) struct EventRegister { static NEW_RECORDS_TS: std::sync::OnceLock = std::sync::OnceLock::new(); static FILE_LOCK: Mutex<()> = Mutex::const_new(()); +const EVENT_REGISTER_BATCH_SIZE: usize = 100; + impl EventRegister { #[cfg(not(test))] const MAX_LOG_RECORDS: usize = 100_000; #[cfg(test)] const MAX_LOG_RECORDS: usize = 10_000; + const BATCH_SIZE: usize = EVENT_REGISTER_BATCH_SIZE; + pub fn new() -> Self { let (log_sender, log_recv) = mpsc::channel(1000); NEW_RECORDS_TS.get_or_init(SystemTime::now); @@ -391,104 +401,14 @@ impl EventRegister { } async fn record_logs(mut log_recv: mpsc::Receiver) { - const BATCH_SIZE: usize = 100; - - async fn num_lines(path: &Path) -> io::Result { - use tokio::fs::File; - use tokio::io::AsyncReadExt; - - let mut file = tokio::io::BufReader::new(File::open(path).await?); - let mut num_records = 0; - let mut buf = [0; 4]; // Read the u32 length prefix - - loop { - let bytes_read = file.read_exact(&mut buf).await; - if bytes_read.is_err() { - break; - } - num_records += 1; - - // Seek to the next record without reading its contents - let length = u32::from_le_bytes(buf) as u64; - if (file.seek(io::SeekFrom::Current(length as i64)).await).is_err() { - break; - } - } - - Ok(num_records) - } - - async fn truncate_records( - file: &mut tokio::fs::File, - remove_records: usize, - ) -> Result<(), Box> { - use tokio::io::{AsyncReadExt, AsyncWriteExt}; - - let _guard = FILE_LOCK.lock().await; - file.rewind().await?; - // tracing::debug!(position = file.stream_position().await.unwrap()); - let mut records_count = 0; - while records_count < remove_records { - let mut length_bytes = [0u8; 4]; - if let Err(error) = file.read_exact(&mut length_bytes).await { - if matches!(error.kind(), io::ErrorKind::UnexpectedEof) { - break; - } - let pos = file.stream_position().await; - tracing::error!(%error, ?pos, "error while trying to read file"); - return Err(error.into()); - } - let length = u32::from_be_bytes(length_bytes); - if let Err(error) = file.seek(io::SeekFrom::Current(length as i64)).await { - if matches!(error.kind(), io::ErrorKind::UnexpectedEof) { - break; - } - let pos = file.stream_position().await; - tracing::error!(%error, ?pos, "error while trying to read file"); - return Err(error.into()); - } - records_count += 1; - } - - // Copy the rest of the file to the buffer - let mut buffer = Vec::new(); - if let Err(error) = file.read_to_end(&mut buffer).await { - if !matches!(error.kind(), io::ErrorKind::UnexpectedEof) { - let pos = file.stream_position().await; - tracing::error!(%error, ?pos, "error while trying to read file"); - return Err(error.into()); - } - } - - #[cfg(test)] - { - assert!(!buffer.is_empty()); - let mut unique = std::collections::HashSet::new(); - let mut read_buf = &*buffer; - let mut length_bytes: [u8; 4] = [0u8; 4]; - let mut cursor = 0; - while read_buf.read_exact(&mut length_bytes).await.is_ok() { - let length = u32::from_be_bytes(length_bytes) as usize; - cursor += 4; - let log: NetLogMessage = - bincode::deserialize(&buffer[cursor..cursor + length]).unwrap(); - cursor += length; - read_buf = &buffer[cursor..]; - unique.insert(log.peer_id); - // tracing::debug!(?log, %cursor); - } - assert!(unique.len() > 1); - } + use futures::StreamExt; - // Seek back to the beginning and write the remaining content - file.rewind().await?; - file.write_all(&buffer).await?; + const DEFAULT_METRICS_SERVER_PORT: u16 = 55010; - // Truncate the file to the new size - file.set_len(buffer.len() as u64).await?; - file.seek(io::SeekFrom::End(0)).await?; - Ok(()) - } + let port = std::env::var("FDEV_NETWORK_METRICS_SERVER_PORT") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(DEFAULT_METRICS_SERVER_PORT); tokio::time::sleep(std::time::Duration::from_millis(200)).await; // wait for the node to start let event_log_path = crate::config::Config::conf().event_log(); @@ -505,80 +425,294 @@ impl EventRegister { } }; let mut num_written = 0; - let mut batch_buf = vec![]; - let mut log_batch = Vec::with_capacity(BATCH_SIZE); + let mut log_batch = Vec::with_capacity(Self::BATCH_SIZE); - let mut num_recs = num_lines(event_log_path.as_path()) + let mut num_recs = Self::num_lines(event_log_path.as_path()) .await .expect("non IO error"); - while let Some(log) = log_recv.recv().await { - log_batch.push(log); - - if log_batch.len() >= BATCH_SIZE { - let num_logs: usize = log_batch.len(); - let moved_batch = std::mem::replace(&mut log_batch, Vec::with_capacity(BATCH_SIZE)); - let serialization_task = tokio::task::spawn_blocking(move || { - let mut batch_serialized_data = Vec::with_capacity(BATCH_SIZE * 1024); - for log_item in &moved_batch { - let mut serialized = match bincode::serialize(log_item) { - Err(err) => { - tracing::error!("Failed serializing log: {err}"); - return Err(err); - } - Ok(serialized) => serialized, - }; - { - use byteorder::{BigEndian, WriteBytesExt}; - batch_serialized_data - .write_u32::(serialized.len() as u32) - .expect("enough memory"); - } - batch_serialized_data.append(&mut serialized); - } - Ok(batch_serialized_data) - }); + let mut ws = tokio_tungstenite::connect_async(format!("ws//127.0.0.1:{port}/push-stats/")) + .await + .map(|(ws_stream, _)| ws_stream) + .ok(); - match serialization_task.await { - Ok(Ok(serialized_data)) => { - // tracing::debug!(bytes = %serialized_data.len(), %num_logs, "serialized logs"); - batch_buf = serialized_data; - num_written += num_logs; - log_batch.clear(); // Clear the batch for new data + loop { + let ws_recv = if let Some(ws) = &mut ws { + ws.next().boxed() + } else { + futures::future::pending().boxed() + }; + tokio::select! { + log = log_recv.recv() => { + let Some(log) = log else { break; }; + if let Some(ws) = ws.as_mut() { + Self::send_to_metrics_server(ws, &log).await; } - _ => { - panic!("Failed serializing log"); + Self::persist_log(&mut log_batch, &mut num_written, &mut num_recs, &mut event_log, log).await; + } + ws_msg = ws_recv => { + if let Some((ws, ws_msg)) = ws.as_mut().zip(ws_msg) { + Self::process_ws_msg(ws, ws_msg).await; } } } + } + + // store remaining logs + let mut batch_serialized_data = Vec::with_capacity(log_batch.len() * 1024); + for log_item in log_batch { + let mut serialized = match bincode::serialize(&log_item) { + Err(err) => { + tracing::error!("Failed serializing log: {err}"); + break; + } + Ok(serialized) => serialized, + }; + { + use byteorder::{BigEndian, WriteBytesExt}; + batch_serialized_data + .write_u32::(serialized.len() as u32) + .expect("enough memory"); + } + batch_serialized_data.append(&mut serialized); + } + if !batch_serialized_data.is_empty() { + use tokio::io::AsyncWriteExt; + let _guard = FILE_LOCK.lock().await; + if let Err(err) = event_log.write_all(&batch_serialized_data).await { + tracing::error!("Failed writting to event log: {err}"); + panic!("Failed writting event log"); + } + } + } + + async fn persist_log( + log_batch: &mut Vec, + num_written: &mut usize, + num_recs: &mut usize, + event_log: &mut tokio::fs::File, + log: NetLogMessage, + ) { + log_batch.push(log); + let mut batch_buf = vec![]; - if num_written >= BATCH_SIZE { - { - use tokio::io::AsyncWriteExt; - let _guard = FILE_LOCK.lock().await; - if let Err(err) = event_log.write_all(&batch_buf).await { - tracing::error!("Failed writting to event log: {err}"); - panic!("Failed writting event log"); + if log_batch.len() >= Self::BATCH_SIZE { + let num_logs: usize = log_batch.len(); + let moved_batch = std::mem::replace(log_batch, Vec::with_capacity(Self::BATCH_SIZE)); + let serialization_task = tokio::task::spawn_blocking(move || { + let mut batch_serialized_data = Vec::with_capacity(Self::BATCH_SIZE * 1024); + for log_item in &moved_batch { + let mut serialized = match bincode::serialize(log_item) { + Err(err) => { + tracing::error!("Failed serializing log: {err}"); + return Err(err); + } + Ok(serialized) => serialized, + }; + { + use byteorder::{BigEndian, WriteBytesExt}; + batch_serialized_data + .write_u32::(serialized.len() as u32) + .expect("enough memory"); } + batch_serialized_data.append(&mut serialized); + } + Ok(batch_serialized_data) + }); + + match serialization_task.await { + Ok(Ok(serialized_data)) => { + // tracing::debug!(bytes = %serialized_data.len(), %num_logs, "serialized logs"); + batch_buf = serialized_data; + *num_written += num_logs; + log_batch.clear(); // Clear the batch for new data + } + _ => { + panic!("Failed serializing log"); + } + } + } + + if *num_written >= Self::BATCH_SIZE { + { + use tokio::io::AsyncWriteExt; + let _guard = FILE_LOCK.lock().await; + if let Err(err) = event_log.write_all(&batch_buf).await { + tracing::error!("Failed writting to event log: {err}"); + panic!("Failed writting event log"); + } + } + *num_recs += *num_written; + *num_written = 0; + } + + // Check the number of lines and truncate if needed + if *num_recs > Self::MAX_LOG_RECORDS { + const REMOVE_RECS: usize = 1000 + EVENT_REGISTER_BATCH_SIZE; // making space for 1000 new records + if let Err(err) = Self::truncate_records(event_log, REMOVE_RECS).await { + tracing::error!("Failed truncating log file: {:?}", err); + panic!("Failed truncating log file"); + } + *num_recs -= REMOVE_RECS; + } + } + + async fn send_to_metrics_server( + ws_stream: &mut tokio_tungstenite::WebSocketStream>, + send_msg: &NetLogMessage, + ) { + use crate::generated::PeerChange; + use futures::SinkExt; + use tokio_tungstenite::tungstenite::Message; + + let res = match &send_msg.kind { + EventKind::Connect(ConnectEvent::Connected { + this: + PeerKeyLocation { + peer: from_peer, + location: Some(from_loc), + }, + connected: + PeerKeyLocation { + peer: to_peer, + location: Some(to_loc), + }, + }) => { + let msg = PeerChange::added_connection_msg( + (*from_peer, from_loc.as_f64()), + (*to_peer, to_loc.as_f64()), + ); + ws_stream.send(Message::Binary(msg)).await + } + EventKind::Disconnected { from } => { + let msg = PeerChange::removed_connection_msg(*from, send_msg.peer_id); + ws_stream.send(Message::Binary(msg)).await + } + _ => Ok(()), + }; + if let Err(error) = res { + tracing::warn!(%error, "Error while sending message to network metrics server"); + } + } + + async fn process_ws_msg( + ws_stream: &mut tokio_tungstenite::WebSocketStream>, + msg: tokio_tungstenite::tungstenite::Result, + ) { + use futures::SinkExt; + use tokio_tungstenite::tungstenite::Message; + match msg { + Ok(Message::Ping(ping)) => { + let _ = ws_stream.send(Message::Pong(ping)).await; + } + Ok(Message::Close(_)) => { + if let Err(error) = ws_stream.send(Message::Close(None)).await { + tracing::warn!(%error, "Error while closing websocket with network metrics server"); } - num_recs += num_written; - num_written = 0; } + _ => {} + } + } + + async fn num_lines(path: &Path) -> io::Result { + use tokio::fs::File; + use tokio::io::{AsyncReadExt, AsyncSeekExt}; + + let mut file = tokio::io::BufReader::new(File::open(path).await?); + let mut num_records = 0; + let mut buf = [0; 4]; // Read the u32 length prefix + + loop { + let bytes_read = file.read_exact(&mut buf).await; + if bytes_read.is_err() { + break; + } + num_records += 1; + + // Seek to the next record without reading its contents + let length = u32::from_le_bytes(buf) as u64; + if (file.seek(io::SeekFrom::Current(length as i64)).await).is_err() { + break; + } + } + + Ok(num_records) + } - // Check the number of lines and truncate if needed - if num_recs > Self::MAX_LOG_RECORDS { - const REMOVE_RECS: usize = 1000 + BATCH_SIZE; // making space for 1000 new records - if let Err(err) = truncate_records(&mut event_log, REMOVE_RECS).await { - tracing::error!("Failed truncating log file: {:?}", err); - panic!("Failed truncating log file"); + async fn truncate_records( + file: &mut tokio::fs::File, + remove_records: usize, + ) -> Result<(), Box> { + use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; + + let _guard = FILE_LOCK.lock().await; + file.rewind().await?; + // tracing::debug!(position = file.stream_position().await.unwrap()); + let mut records_count = 0; + while records_count < remove_records { + let mut length_bytes = [0u8; 4]; + if let Err(error) = file.read_exact(&mut length_bytes).await { + if matches!(error.kind(), io::ErrorKind::UnexpectedEof) { + break; } - num_recs -= REMOVE_RECS; + let pos = file.stream_position().await; + tracing::error!(%error, ?pos, "error while trying to read file"); + return Err(error.into()); } + let length = u32::from_be_bytes(length_bytes); + if let Err(error) = file.seek(io::SeekFrom::Current(length as i64)).await { + if matches!(error.kind(), io::ErrorKind::UnexpectedEof) { + break; + } + let pos = file.stream_position().await; + tracing::error!(%error, ?pos, "error while trying to read file"); + return Err(error.into()); + } + records_count += 1; + } + + // Copy the rest of the file to the buffer + let mut buffer = Vec::new(); + if let Err(error) = file.read_to_end(&mut buffer).await { + if !matches!(error.kind(), io::ErrorKind::UnexpectedEof) { + let pos = file.stream_position().await; + tracing::error!(%error, ?pos, "error while trying to read file"); + return Err(error.into()); + } + } + + #[cfg(test)] + { + assert!(!buffer.is_empty()); + let mut unique = std::collections::HashSet::new(); + let mut read_buf = &*buffer; + let mut length_bytes: [u8; 4] = [0u8; 4]; + let mut cursor = 0; + while read_buf.read_exact(&mut length_bytes).await.is_ok() { + let length = u32::from_be_bytes(length_bytes) as usize; + cursor += 4; + let log: NetLogMessage = + bincode::deserialize(&buffer[cursor..cursor + length]).unwrap(); + cursor += length; + read_buf = &buffer[cursor..]; + unique.insert(log.peer_id); + // tracing::debug!(?log, %cursor); + } + assert!(unique.len() > 1); } + + // Seek back to the beginning and write the remaining content + file.rewind().await?; + file.write_all(&buffer).await?; + + // Truncate the file to the new size + file.set_len(buffer.len() as u64).await?; + file.seek(io::SeekFrom::End(0)).await?; + Ok(()) } pub async fn get_router_events(max_event_number: usize) -> Result, DynError> { - use tokio::io::AsyncReadExt; + use tokio::io::{AsyncReadExt, AsyncSeekExt}; const MAX_EVENT_HISTORY: usize = 10_000; let event_num = max_event_number.min(MAX_EVENT_HISTORY); @@ -643,7 +777,7 @@ impl EventRegister { impl NetEventRegister for EventRegister { fn register_events<'a>( - &'a mut self, + &'a self, logs: Either, Vec>>, ) -> BoxFuture<'a, ()> { async { @@ -899,7 +1033,7 @@ mod opentelemetry_tracer { impl NetEventRegister for OTEventRegister { fn register_events<'a>( - &'a mut self, + &'a self, logs: Either, Vec>>, ) -> BoxFuture<'a, ()> { async { @@ -942,7 +1076,9 @@ enum EventKind { at: PeerKeyLocation, }, Ignored, - Disconnected, + Disconnected { + from: PeerId, + }, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -1095,7 +1231,7 @@ pub(super) mod test { // force a truncation const TEST_LOGS: usize = EventRegister::MAX_LOG_RECORDS + 100; - let mut register = EventRegister::new(); + let register = EventRegister::new(); let bytes = crate::util::test::random_bytes_2mb(); let mut gen = arbitrary::Unstructured::new(&bytes); let mut transactions = vec![]; @@ -1251,7 +1387,7 @@ pub(super) mod test { let logs = self.logs.lock(); let disconnects = logs .iter() - .filter(|l| matches!(l.kind, EventKind::Disconnected)) + .filter(|l| matches!(l.kind, EventKind::Disconnected { .. })) .fold(HashMap::<_, Vec<_>>::new(), |mut map, log| { map.entry(log.peer_id).or_default().push(log.datetime); map @@ -1293,7 +1429,7 @@ pub(super) mod test { impl super::NetEventRegister for TestEventListener { fn register_events<'a>( - &'a mut self, + &'a self, logs: Either, Vec>>, ) -> BoxFuture<'a, ()> { match logs { @@ -1337,7 +1473,7 @@ pub(super) mod test { (PeerId::random(), Location::try_from(0.25)?), ]; - let mut listener = TestEventListener::new(); + let listener = TestEventListener::new(); locations.iter().for_each(|(other, location)| { listener.register_events(Either::Left(NetEventLog { tx: &tx, diff --git a/crates/fdev/src/network_metrics_server.rs b/crates/fdev/src/network_metrics_server.rs index eaaf5a201..f91b47f70 100644 --- a/crates/fdev/src/network_metrics_server.rs +++ b/crates/fdev/src/network_metrics_server.rs @@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize}; pub async fn run_server(data_dir: Option) -> anyhow::Result<()> { const DEFAULT_PORT: u16 = 55010; - let port = std::env::var("PORT") + let port = std::env::var("FDEV_NETWORK_METRICS_SERVER_PORT") .ok() .and_then(|s| s.parse().ok()) .unwrap_or(DEFAULT_PORT); @@ -252,12 +252,14 @@ async fn record_saver( if !data_dir.exists() { std::fs::create_dir_all(&data_dir)?; } - let mut fs = tokio::fs::OpenOptions::new() - .write(true) - .truncate(true) - .create(true) - .open(data_dir.join("network-metrics")) - .await?; + let mut fs = tokio::io::BufWriter::new( + tokio::fs::OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(data_dir.join("network-metrics")) + .await?, + ); let mut batch = Vec::with_capacity(1024); while let Ok(record) = incoming_rec.recv().await { @@ -278,5 +280,9 @@ async fn record_saver( } } } + for rec in batch { + let rec = serde_json::to_vec(&rec).unwrap(); + fs.write_all(&rec).await?; + } Ok(()) } From 5891133c662c7ff3914e7e47d55e31da3c347fa1 Mon Sep 17 00:00:00 2001 From: Ignacio Duart Date: Tue, 28 Nov 2023 16:18:16 +0100 Subject: [PATCH 05/12] Update axum --- Cargo.lock | 432 ++++++++++----------- Cargo.toml | 1 + crates/core/Cargo.toml | 7 +- crates/core/src/client_events/websocket.rs | 71 ++-- crates/core/src/config.rs | 2 +- crates/core/src/message.rs | 4 +- crates/core/src/server.rs | 16 +- crates/core/src/server/http_gateway.rs | 11 +- crates/core/src/server/path_handlers.rs | 3 +- crates/fdev/Cargo.toml | 2 +- 10 files changed, 258 insertions(+), 291 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f003b325f..0a142112e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -247,7 +247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock 2.8.0", - "autocfg 1.1.0", + "autocfg", "cfg-if", "concurrent-queue", "futures-lite 1.13.0", @@ -354,7 +354,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ae364a6c1301604bbc6dfbf8c385c47ff82301dd01eef506195a029196d8d04" dependencies = [ "nix 0.27.1", - "rand 0.8.5", + "rand", ] [[package]] @@ -363,20 +363,11 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2" dependencies = [ - "http", + "http 0.2.11", "log", "url", ] -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -385,20 +376,20 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.20" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +checksum = "810a80b128d70e6ed2bdf3fe8ed72c0ae56f5f5948d01c2753282dd92a84fce8" dependencies = [ "async-trait", "axum-core", "base64 0.21.5", - "bitflags 1.3.2", "bytes", "futures-util", - "headers", - "http", - "http-body", - "hyper", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.0.1", + "hyper-util", "itoa", "matchit", "memchr", @@ -407,6 +398,8 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", + "serde_json", + "serde_path_to_error", "serde_urlencoded", "sha1", "sync_wrapper", @@ -419,17 +412,20 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.3.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +checksum = "de0ddc355eab88f4955090a823715df47acf0b7660aab7a69ad5ce6301ee3b73" dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", "mime", + "pin-project-lite", "rustversion", + "sync_wrapper", "tower-layer", "tower-service", ] @@ -767,15 +763,6 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "colorchoice" version = "1.0.0" @@ -863,7 +850,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80128832c58ea9cbd041d2a759ec449224487b2c1e400453d99d244eead87a8e" dependencies = [ - "autocfg 1.1.0", + "autocfg", "cfg-if", "libc", "scopeguard", @@ -1018,7 +1005,7 @@ version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ - "autocfg 1.1.0", + "autocfg", "cfg-if", "crossbeam-utils", "memoffset 0.9.0", @@ -1051,7 +1038,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "typenum", ] @@ -1354,7 +1341,7 @@ checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0" dependencies = [ "curve25519-dalek", "ed25519", - "rand_core 0.6.4", + "rand_core", "serde", "sha2", "subtle", @@ -1527,7 +1514,7 @@ dependencies = [ "futures", "glob", "pico-args", - "rand 0.8.5", + "rand", "semver", "serde", "serde_json", @@ -1631,6 +1618,7 @@ dependencies = [ "flatbuffers", "freenet-stdlib", "futures", + "headers", "itertools 0.11.0", "itertools 0.12.0", "libp2p", @@ -1643,7 +1631,7 @@ dependencies = [ "parking_lot", "pav_regression", "pico-args", - "rand 0.8.5", + "rand", "rocksdb", "serde", "serde_json", @@ -1690,7 +1678,7 @@ dependencies = [ "futures", "js-sys", "once_cell", - "rand 0.8.5", + "rand", "semver", "serde", "serde-wasm-bindgen 0.6.1", @@ -1716,12 +1704,6 @@ dependencies = [ "libc", ] -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "funty" version = "2.0.0" @@ -1956,7 +1938,26 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.11", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d308f63daf4181410c242d34c11f928dcb3aa105852019e043c9d1f4e4368a" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 1.0.0", "indexmap 2.1.0", "slab", "tokio", @@ -1994,14 +1995,14 @@ dependencies = [ [[package]] name = "headers" -version = "0.3.9" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" dependencies = [ "base64 0.21.5", "bytes", "headers-core", - "http", + "http 1.0.0", "httpdate", "mime", "sha1", @@ -2009,11 +2010,11 @@ dependencies = [ [[package]] name = "headers-core" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http", + "http 1.0.0", ] [[package]] @@ -2086,6 +2087,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.5" @@ -2093,15 +2105,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", - "http", + "http 0.2.11", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.0.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", "pin-project-lite", ] [[package]] name = "http-range-header" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" +checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe" [[package]] name = "httparse" @@ -2125,9 +2160,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.22", + "http 0.2.11", + "http-body 0.4.5", "httparse", "httpdate", "itoa", @@ -2139,6 +2174,45 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f9214f3e703236b221f1a9cd88ec8b4adfa5296de01ab96216361f4692f56" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.0", + "http 1.0.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca339002caeb0d159cc6e023dff48e199f081e42fa039895c7c6f38b37f2e9d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.0.1", + "pin-project-lite", + "socket2 0.5.5", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.58" @@ -2238,10 +2312,10 @@ dependencies = [ "attohttpc", "bytes", "futures", - "http", - "hyper", + "http 0.2.11", + "hyper 0.14.27", "log", - "rand 0.8.5", + "rand", "tokio", "url", "xmltree", @@ -2253,7 +2327,7 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "autocfg 1.1.0", + "autocfg", "hashbrown 0.12.3", "serde", ] @@ -2355,7 +2429,7 @@ dependencies = [ "curl-sys", "event-listener 2.5.3", "futures-lite 1.13.0", - "http", + "http 0.2.11", "log", "once_cell", "polling 2.8.0", @@ -2545,7 +2619,7 @@ dependencies = [ "libp2p-swarm", "log", "quick-protobuf", - "rand 0.8.5", + "rand", ] [[package]] @@ -2580,7 +2654,7 @@ dependencies = [ "parking_lot", "pin-project", "quick-protobuf", - "rand 0.8.5", + "rand", "rw-stream-sink", "smallvec", "thiserror", @@ -2638,7 +2712,7 @@ dependencies = [ "hkdf", "multihash", "quick-protobuf", - "rand 0.8.5", + "rand", "sha2", "thiserror", "tracing", @@ -2658,7 +2732,7 @@ dependencies = [ "libp2p-identity", "libp2p-swarm", "log", - "rand 0.8.5", + "rand", "smallvec", "socket2 0.5.5", "tokio", @@ -2698,7 +2772,7 @@ dependencies = [ "multihash", "once_cell", "quick-protobuf", - "rand 0.8.5", + "rand", "sha2", "snow", "static_assertions", @@ -2721,7 +2795,7 @@ dependencies = [ "libp2p-identity", "libp2p-swarm", "log", - "rand 0.8.5", + "rand", "void", ] @@ -2741,7 +2815,7 @@ dependencies = [ "log", "parking_lot", "quinn", - "rand 0.8.5", + "rand", "ring 0.16.20", "rustls", "socket2 0.5.5", @@ -2762,7 +2836,7 @@ dependencies = [ "libp2p-identity", "libp2p-swarm", "log", - "rand 0.8.5", + "rand", "smallvec", "void", ] @@ -2784,7 +2858,7 @@ dependencies = [ "log", "multistream-select", "once_cell", - "rand 0.8.5", + "rand", "smallvec", "tokio", "void", @@ -2940,7 +3014,7 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ - "autocfg 1.1.0", + "autocfg", "scopeguard", ] @@ -3021,7 +3095,7 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" dependencies = [ - "autocfg 1.1.0", + "autocfg", "rawpointer", ] @@ -3065,7 +3139,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -3074,7 +3148,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -3192,7 +3266,7 @@ dependencies = [ "num-complex", "num-rational", "num-traits", - "rand 0.8.5", + "rand", "rand_distr", "simba", "typenum", @@ -3348,7 +3422,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", ] @@ -3365,7 +3439,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.5", + "rand", "smallvec", "zeroize", ] @@ -3385,7 +3459,7 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-traits", ] @@ -3395,7 +3469,7 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", ] @@ -3406,7 +3480,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", ] @@ -3417,7 +3491,7 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ - "autocfg 1.1.0", + "autocfg", "libm", ] @@ -3503,7 +3577,7 @@ checksum = "7f51189ce8be654f9b5f7e70e49967ed894e84a06fc35c6c042e64ac1fc5399e" dependencies = [ "async-trait", "bytes", - "http", + "http 0.2.11", "opentelemetry", ] @@ -3516,7 +3590,7 @@ dependencies = [ "async-trait", "futures-core", "futures-util", - "http", + "http 0.2.11", "isahc", "opentelemetry", "opentelemetry-http", @@ -3551,7 +3625,7 @@ dependencies = [ "opentelemetry", "ordered-float 4.1.1", "percent-encoding", - "rand 0.8.5", + "rand", "thiserror", "tokio", "tokio-stream", @@ -3809,7 +3883,7 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ - "autocfg 1.1.0", + "autocfg", "bitflags 1.3.2", "cfg-if", "concurrent-queue", @@ -4018,7 +4092,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" dependencies = [ "bytes", - "rand 0.8.5", + "rand", "ring 0.16.20", "rustc-hash", "rustls", @@ -4056,25 +4130,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi", -] - [[package]] name = "rand" version = "0.8.5" @@ -4082,18 +4137,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -4103,24 +4148,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.6.4" @@ -4137,70 +4167,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" dependencies = [ "num-traits", - "rand 0.8.5", -] - -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", + "rand", ] [[package]] @@ -4241,15 +4208,6 @@ dependencies = [ "yasna", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "redox_syscall" version = "0.3.5" @@ -4458,7 +4416,7 @@ dependencies = [ "num-traits", "pkcs1", "pkcs8", - "rand_core 0.6.4", + "rand_core", "signature", "spki", "subtle", @@ -4727,6 +4685,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.4" @@ -4840,7 +4808,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -4868,7 +4836,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -4904,7 +4872,7 @@ dependencies = [ "blake2", "chacha20poly1305", "curve25519-dalek", - "rand_core 0.6.4", + "rand_core", "ring 0.17.5", "rustc_version", "sha2", @@ -5091,7 +5059,7 @@ dependencies = [ "memchr", "once_cell", "percent-encoding", - "rand 0.8.5", + "rand", "rsa", "serde", "sha1", @@ -5130,7 +5098,7 @@ dependencies = [ "md-5", "memchr", "once_cell", - "rand 0.8.5", + "rand", "serde", "serde_json", "sha1", @@ -5188,7 +5156,7 @@ dependencies = [ "lazy_static", "nalgebra", "num-traits", - "rand 0.8.5", + "rand", ] [[package]] @@ -5204,7 +5172,7 @@ dependencies = [ "futures", "getrandom", "parking_lot", - "rand 0.8.5", + "rand", "seahash", "thiserror", "tracing", @@ -5557,16 +5525,16 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +checksum = "09e12e6351354851911bdf8c2b8f2ab15050c567d70a8b9a37ae7b8301a4080d" dependencies = [ "bitflags 2.4.1", "bytes", - "futures-core", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", "http-range-header", "httpdate", "mime", @@ -5698,7 +5666,7 @@ dependencies = [ "idna 0.2.3", "ipnet", "lazy_static", - "rand 0.8.5", + "rand", "smallvec", "socket2 0.4.10", "thiserror", @@ -5724,7 +5692,7 @@ dependencies = [ "idna 0.4.0", "ipnet", "once_cell", - "rand 0.8.5", + "rand", "smallvec", "thiserror", "tinyvec", @@ -5745,7 +5713,7 @@ dependencies = [ "lru-cache", "once_cell", "parking_lot", - "rand 0.8.5", + "rand", "resolv-conf", "smallvec", "thiserror", @@ -5769,10 +5737,10 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 0.2.11", "httparse", "log", - "rand 0.8.5", + "rand", "sha1", "thiserror", "url", @@ -5793,13 +5761,11 @@ checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "ulid" -version = "0.4.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e95a59b292ca0cf9b45be2e52294d1ca6cb24eb11b08ef4376f73f1a00c549" +checksum = "7e37c4b6cbcc59a8dcd09a6429fbc7890286bcbb79215cea7b38a3c4c0921d93" dependencies = [ - "chrono", - "lazy_static", - "rand 0.6.5", + "rand", "serde", ] @@ -6546,7 +6512,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" dependencies = [ "curve25519-dalek", - "rand_core 0.6.4", + "rand_core", "serde", "zeroize", ] @@ -6627,7 +6593,7 @@ dependencies = [ "nohash-hasher", "parking_lot", "pin-project", - "rand 0.8.5", + "rand", "static_assertions", ] diff --git a/Cargo.toml b/Cargo.toml index ad062cd43..7916ed006 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = ["crates/*"] [workspace.dependencies] arrayvec = { version = "0.7", features = ["serde"] } +axum = "0.7" blake3 = { version = "1", features = ["std", "traits-preview"] } bs58 = "0.5" chacha20poly1305 = "0.10" diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 0b108c140..db15a3a04 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -16,7 +16,7 @@ path = "src/bin/freenet.rs" anyhow = "1" asynchronous-codec = "0.6" async-trait = "0.1" -axum = { default-features = false, features = ["headers", "http1", "matched-path", "query", "tower-log", "ws"], version = "0.6" } +axum = { default-features = false, features = ["http1", "matched-path", "query", "tower-log", "ws"], workspace = true } bincode = "1" blake3 = { workspace = true } bs58 = "0.5" @@ -37,6 +37,7 @@ either = { features = ["serde"], workspace = true } fastrand = { workspace = true } flatbuffers = "23.5.26" futures = "0.3.21" +headers = "0.4" itertools = "0.11" libp2p = { default-features = false, features = ["autonat", "dns", "ed25519", "identify", "macros", "noise", "ping", "tcp", "tokio", "yamux"], version = "0.52.3" } libp2p-identity = { features = ["ed25519", "rand"], version = "0.2.7" } @@ -56,8 +57,8 @@ tar = { version = "0.4.38" } thiserror = "1" tokio = { features = ["fs", "macros", "rt-multi-thread", "sync", "process"], version = "1" } tokio-tungstenite = "0.20" -tower-http = { features = ["fs", "trace"], version = "0.4" } -ulid = { features = ["serde"], version = "0.4" } +tower-http = { features = ["fs", "trace"], version = "0.5" } +ulid = { features = ["serde"], version = "1.1" } unsigned-varint = "0.7" wasmer = { features = ["sys"], workspace = true } xz2 = { version = "0.1" } diff --git a/crates/core/src/client_events/websocket.rs b/crates/core/src/client_events/websocket.rs index c63872f3c..ce393e4cf 100644 --- a/crates/core/src/client_events/websocket.rs +++ b/crates/core/src/client_events/websocket.rs @@ -9,6 +9,7 @@ use axum::{ ws::{Message, WebSocket}, Query, WebSocketUpgrade, }, + http::StatusCode, response::{IntoResponse, Response}, routing::get, Extension, Router, @@ -18,6 +19,7 @@ use freenet_stdlib::{ prelude::*, }; use futures::{future::BoxFuture, stream::SplitSink, FutureExt, SinkExt, StreamExt}; +use headers::Header; use serde::Deserialize; use tokio::sync::{mpsc, Mutex}; @@ -116,13 +118,13 @@ impl WebSocketProxy { struct EncodingProtocolExt(EncodingProtocol); -impl axum::headers::Header for EncodingProtocolExt { +impl headers::Header for EncodingProtocolExt { fn name() -> &'static axum::http::HeaderName { static HEADER: OnceLock = OnceLock::new(); HEADER.get_or_init(|| axum::http::HeaderName::from_static("encoding-protocol")) } - fn decode<'i, I>(values: &mut I) -> Result + fn decode<'i, I>(values: &mut I) -> Result where Self: Sized, I: Iterator, @@ -134,7 +136,7 @@ impl axum::headers::Header for EncodingProtocolExt { "flatbuffers" => Some(EncodingProtocolExt(EncodingProtocol::Flatbuffers)), _ => None, }) - .ok_or_else(axum::headers::Error::invalid) + .ok_or_else(headers::Error::invalid) } fn encode>(&self, values: &mut E) { @@ -153,22 +155,18 @@ struct ConnectionInfo { encoding_protocol: Option, } -async fn connection_info( - encoding_protoc: Result< - axum::TypedHeader, - axum::extract::rejection::TypedHeaderRejection, - >, - auth_token: Result< - axum::TypedHeader>, - axum::extract::rejection::TypedHeaderRejection, - >, +async fn connection_info( Query(ConnectionInfo { auth_token: auth_token_q, encoding_protocol, }): Query, - mut req: axum::http::Request, - next: axum::middleware::Next, + mut req: axum::extract::Request, + next: axum::middleware::Next, ) -> Response { + use headers::{ + authorization::{Authorization, Bearer}, + HeaderMapExt, + }; // tracing::info!( // "headers: {:?}", // req.headers() @@ -176,30 +174,35 @@ async fn connection_info( // .flat_map(|(k, v)| v.to_str().ok().map(|v| format!("{k}: {v}"))) // .collect::>() // ); - let encoding_protoc = match encoding_protoc { - Ok(protoc) => protoc.0 .0, - Err(err) - if matches!( - err.reason(), - axum::extract::rejection::TypedHeaderRejectionReason::Missing - ) => - { - encoding_protocol.unwrap_or(EncodingProtocol::Flatbuffers) + + let encoding_protoc = match req.headers().typed_try_get::() { + Ok(Some(protoc)) => protoc.0, + Ok(None) => encoding_protocol.unwrap_or(EncodingProtocol::Flatbuffers), + Err(_error) => { + return ( + StatusCode::BAD_REQUEST, + format!( + "Incorrect `{header}` header specification", + header = EncodingProtocolExt::name() + ), + ) + .into_response() } - Err(other) => return other.into_response(), }; - let auth_token = match auth_token { - Ok(auth_token) => Some(AuthToken::from(auth_token.token().to_owned())), - Err(err) - if matches!( - err.reason(), - axum::extract::rejection::TypedHeaderRejectionReason::Missing - ) => - { - auth_token_q + let auth_token = match req.headers().typed_try_get::>() { + Ok(Some(value)) => Some(AuthToken::from(value.token().to_owned())), + Ok(None) => auth_token_q, + Err(_error) => { + return ( + StatusCode::BAD_REQUEST, + format!( + "Incorrect Bearer `{header}` header specification", + header = Authorization::::name() + ), + ) + .into_response() } - Err(other) => return other.into_response(), }; tracing::debug!( diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index 4a12c76ed..0367ef80a 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -359,7 +359,7 @@ pub fn set_logger() { .compare_exchange( false, true, - std::sync::atomic::Ordering::Acquire, + std::sync::atomic::Ordering::Release, std::sync::atomic::Ordering::SeqCst, ) .is_err() diff --git a/crates/core/src/message.rs b/crates/core/src/message.rs index f5a07dc4f..142f17914 100644 --- a/crates/core/src/message.rs +++ b/crates/core/src/message.rs @@ -371,13 +371,13 @@ mod tests { let ts_1 = Ulid::new(); assert!( tx.id.timestamp_ms() > ts_0.timestamp_ms(), - "{} <= {}", + "{:?} <= {:?}", tx.id.datetime(), ts_0.datetime() ); assert!( tx.id.timestamp_ms() < ts_1.timestamp_ms(), - "{} >= {}", + "{:?} >= {:?}", tx.id.datetime(), ts_1.datetime() ); diff --git a/crates/core/src/server.rs b/crates/core/src/server.rs index f6b801a2b..573b92378 100644 --- a/crates/core/src/server.rs +++ b/crates/core/src/server.rs @@ -47,7 +47,6 @@ pub mod local_node { use axum::Router; use freenet_stdlib::client_api::{ClientRequest, ErrorKind}; - use futures::TryFutureExt; use tower_http::trace::TraceLayer; use crate::{ @@ -59,14 +58,13 @@ pub mod local_node { use super::http_gateway::HttpGateway; fn serve(socket: SocketAddr, router: Router) { - tracing::info!("listening on {}", socket); - tokio::spawn( - axum::Server::bind(&socket) - .serve(router.into_make_service()) - .map_err(|e| { - tracing::error!("Error while running HTTP gateway server: {e}"); - }), - ); + tokio::spawn(async move { + tracing::info!("listening on {}", socket); + let listener = tokio::net::TcpListener::bind(socket).await.unwrap(); + axum::serve(listener, router).await.map_err(|e| { + tracing::error!("Error while running HTTP gateway server: {e}"); + }) + }); } pub async fn run_local_node( diff --git a/crates/core/src/server/http_gateway.rs b/crates/core/src/server/http_gateway.rs index 68d9b87ff..d3abd7964 100644 --- a/crates/core/src/server/http_gateway.rs +++ b/crates/core/src/server/http_gateway.rs @@ -86,7 +86,7 @@ async fn web_home( Extension(rs): Extension, axum::extract::State(config): axum::extract::State, ) -> Result { - use axum::headers::{Header, HeaderMapExt}; + use headers::{Header, HeaderMapExt}; let domain = config .localhost @@ -94,8 +94,7 @@ async fn web_home( .expect("non-local connections not supported yet"); let token = AuthToken::generate(); - let auth_header = - axum::headers::Authorization::::name().to_string(); + let auth_header = headers::Authorization::::name().to_string(); let cookie = cookie::Cookie::build(auth_header, format!("Bearer {}", token.as_str())) .domain(domain) .path(format!("/contract/web/{key}")) @@ -105,13 +104,13 @@ async fn web_home( .http_only(false) .finish(); - let token_header = axum::headers::Authorization::bearer(token.as_str()).unwrap(); + let token_header = headers::Authorization::bearer(token.as_str()).unwrap(); let contract_idx = path_handlers::contract_home(key, rs, token).await?; let mut response = contract_idx.into_response(); response.headers_mut().typed_insert(token_header); response.headers_mut().insert( - axum::headers::SetCookie::name(), - axum::headers::HeaderValue::from_str(&cookie.to_string()).unwrap(), + headers::SetCookie::name(), + headers::HeaderValue::from_str(&cookie.to_string()).unwrap(), ); Ok(response) diff --git a/crates/core/src/server/path_handlers.rs b/crates/core/src/server/path_handlers.rs index 905378d13..f2fbbcf4f 100644 --- a/crates/core/src/server/path_handlers.rs +++ b/crates/core/src/server/path_handlers.rs @@ -3,7 +3,6 @@ use std::path::{Path, PathBuf}; use axum::response::{Html, IntoResponse}; -use bytes::Bytes; use freenet_stdlib::{ client_api::{ClientRequest, ContractRequest, ContractResponse, HostResponse}, prelude::*, @@ -172,7 +171,7 @@ pub(super) async fn variable_content( // serve the file let mut serve_file = tower_http::services::fs::ServeFile::new(&file_path); - let fake_req = axum::http::Request::new(axum::body::Empty::::new()); + let fake_req = axum::http::Request::new(axum::body::Body::empty()); serve_file .try_call(fake_req) .await diff --git a/crates/fdev/Cargo.toml b/crates/fdev/Cargo.toml index bcabed675..2325dbe3f 100644 --- a/crates/fdev/Cargo.toml +++ b/crates/fdev/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/freenet/freenet" [dependencies] anyhow = "1" -axum = { default-features = false, features = ["headers", "http1", "matched-path", "query", "tower-log", "ws"], version = "0.6" } +axum = { default-features = false, features = ["http1", "matched-path", "query", "tower-log", "ws"], workspace = true } bincode = "1" bs58 = { workspace = true } clap = { workspace = true, features = ["derive", "env"] } From 807b0c10e589a2626519597a549d1b070c500748 Mon Sep 17 00:00:00 2001 From: Ignacio Duart Date: Tue, 28 Nov 2023 17:04:58 +0100 Subject: [PATCH 06/12] Save logs reported by peers Still doesn't flush things on close so will temporarily store all incoming records --- crates/core/src/node/p2p_impl.rs | 4 +- crates/core/src/node/testing_impl.rs | 2 +- crates/core/src/tracing.rs | 233 +++++++++++--------- crates/fdev/src/network_metrics_server.rs | 125 +++++++---- crates/fdev/src/testing.rs | 58 +++-- crates/fdev/src/testing/multiple_process.rs | 1 - 6 files changed, 257 insertions(+), 166 deletions(-) diff --git a/crates/core/src/node/p2p_impl.rs b/crates/core/src/node/p2p_impl.rs index 9782ac295..0419f2e06 100644 --- a/crates/core/src/node/p2p_impl.rs +++ b/crates/core/src/node/p2p_impl.rs @@ -238,7 +238,7 @@ mod test { config, peer1_key, [Box::new(user_events)], - crate::tracing::TestEventListener::new(), + crate::tracing::TestEventListener::new().await, "ping-listener".into(), ) .await?, @@ -259,7 +259,7 @@ mod test { config, peer2_key, [Box::new(user_events)], - crate::tracing::TestEventListener::new(), + crate::tracing::TestEventListener::new().await, "ping-dialer".into(), ) .await diff --git a/crates/core/src/node/testing_impl.rs b/crates/core/src/node/testing_impl.rs index fae772a35..a4999f803 100644 --- a/crates/core/src/node/testing_impl.rs +++ b/crates/core/src/node/testing_impl.rs @@ -338,7 +338,7 @@ impl SimNetwork { let mut net = Self { name: name.into(), clean_up_tmp_dirs: true, - event_listener: TestEventListener::new(), + event_listener: TestEventListener::new().await, labels: Vec::with_capacity(nodes + gateways), user_ev_controller: Some(user_ev_controller), receiver_ch, diff --git a/crates/core/src/tracing.rs b/crates/core/src/tracing.rs index c48f12fa9..ca947c2de 100644 --- a/crates/core/src/tracing.rs +++ b/crates/core/src/tracing.rs @@ -13,7 +13,7 @@ use tokio::{ Mutex, }, }; -use tokio_tungstenite::MaybeTlsStream; +use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; use crate::{ config::GlobalExecutor, @@ -385,6 +385,8 @@ static FILE_LOCK: Mutex<()> = Mutex::const_new(()); const EVENT_REGISTER_BATCH_SIZE: usize = 100; +const DEFAULT_METRICS_SERVER_PORT: u16 = 55010; + impl EventRegister { #[cfg(not(test))] const MAX_LOG_RECORDS: usize = 100_000; @@ -403,13 +405,6 @@ impl EventRegister { async fn record_logs(mut log_recv: mpsc::Receiver) { use futures::StreamExt; - const DEFAULT_METRICS_SERVER_PORT: u16 = 55010; - - let port = std::env::var("FDEV_NETWORK_METRICS_SERVER_PORT") - .ok() - .and_then(|s| s.parse().ok()) - .unwrap_or(DEFAULT_METRICS_SERVER_PORT); - tokio::time::sleep(std::time::Duration::from_millis(200)).await; // wait for the node to start let event_log_path = crate::config::Config::conf().event_log(); let mut event_log = match OpenOptions::new() @@ -431,10 +426,7 @@ impl EventRegister { .await .expect("non IO error"); - let mut ws = tokio_tungstenite::connect_async(format!("ws//127.0.0.1:{port}/push-stats/")) - .await - .map(|(ws_stream, _)| ws_stream) - .ok(); + let mut ws = connect_to_metrics_server().await; loop { let ws_recv = if let Some(ws) = &mut ws { @@ -446,13 +438,13 @@ impl EventRegister { log = log_recv.recv() => { let Some(log) = log else { break; }; if let Some(ws) = ws.as_mut() { - Self::send_to_metrics_server(ws, &log).await; + send_to_metrics_server(ws, &log).await; } Self::persist_log(&mut log_batch, &mut num_written, &mut num_recs, &mut event_log, log).await; } ws_msg = ws_recv => { if let Some((ws, ws_msg)) = ws.as_mut().zip(ws_msg) { - Self::process_ws_msg(ws, ws_msg).await; + received_from_metrics_server(ws, ws_msg).await; } } } @@ -557,63 +549,6 @@ impl EventRegister { } } - async fn send_to_metrics_server( - ws_stream: &mut tokio_tungstenite::WebSocketStream>, - send_msg: &NetLogMessage, - ) { - use crate::generated::PeerChange; - use futures::SinkExt; - use tokio_tungstenite::tungstenite::Message; - - let res = match &send_msg.kind { - EventKind::Connect(ConnectEvent::Connected { - this: - PeerKeyLocation { - peer: from_peer, - location: Some(from_loc), - }, - connected: - PeerKeyLocation { - peer: to_peer, - location: Some(to_loc), - }, - }) => { - let msg = PeerChange::added_connection_msg( - (*from_peer, from_loc.as_f64()), - (*to_peer, to_loc.as_f64()), - ); - ws_stream.send(Message::Binary(msg)).await - } - EventKind::Disconnected { from } => { - let msg = PeerChange::removed_connection_msg(*from, send_msg.peer_id); - ws_stream.send(Message::Binary(msg)).await - } - _ => Ok(()), - }; - if let Err(error) = res { - tracing::warn!(%error, "Error while sending message to network metrics server"); - } - } - - async fn process_ws_msg( - ws_stream: &mut tokio_tungstenite::WebSocketStream>, - msg: tokio_tungstenite::tungstenite::Result, - ) { - use futures::SinkExt; - use tokio_tungstenite::tungstenite::Message; - match msg { - Ok(Message::Ping(ping)) => { - let _ = ws_stream.send(Message::Pong(ping)).await; - } - Ok(Message::Close(_)) => { - if let Err(error) = ws_stream.send(Message::Close(None)).await { - tracing::warn!(%error, "Error while closing websocket with network metrics server"); - } - } - _ => {} - } - } - async fn num_lines(path: &Path) -> io::Result { use tokio::fs::File; use tokio::io::{AsyncReadExt, AsyncSeekExt}; @@ -797,6 +732,78 @@ impl NetEventRegister for EventRegister { } } +async fn connect_to_metrics_server() -> Option>> { + let port = std::env::var("FDEV_NETWORK_METRICS_SERVER_PORT") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(DEFAULT_METRICS_SERVER_PORT); + + tokio_tungstenite::connect_async(format!("ws://127.0.0.1:{port}/push-stats/")) + .await + .map(|(ws_stream, _)| { + tracing::info!("Connected to network metrics server"); + ws_stream + }) + .ok() +} + +async fn send_to_metrics_server( + ws_stream: &mut WebSocketStream>, + send_msg: &NetLogMessage, +) { + use crate::generated::PeerChange; + use futures::SinkExt; + use tokio_tungstenite::tungstenite::Message; + + let res = match &send_msg.kind { + EventKind::Connect(ConnectEvent::Connected { + this: + PeerKeyLocation { + peer: from_peer, + location: Some(from_loc), + }, + connected: + PeerKeyLocation { + peer: to_peer, + location: Some(to_loc), + }, + }) => { + let msg = PeerChange::added_connection_msg( + (*from_peer, from_loc.as_f64()), + (*to_peer, to_loc.as_f64()), + ); + ws_stream.send(Message::Binary(msg)).await + } + EventKind::Disconnected { from } => { + let msg = PeerChange::removed_connection_msg(*from, send_msg.peer_id); + ws_stream.send(Message::Binary(msg)).await + } + _ => Ok(()), + }; + if let Err(error) = res { + tracing::warn!(%error, "Error while sending message to network metrics server"); + } +} + +async fn received_from_metrics_server( + ws_stream: &mut tokio_tungstenite::WebSocketStream>, + msg: tokio_tungstenite::tungstenite::Result, +) { + use futures::SinkExt; + use tokio_tungstenite::tungstenite::Message; + match msg { + Ok(Message::Ping(ping)) => { + let _ = ws_stream.send(Message::Pong(ping)).await; + } + Ok(Message::Close(_)) => { + if let Err(error) = ws_stream.send(Message::Close(None)).await { + tracing::warn!(%error, "Error while closing websocket with network metrics server"); + } + } + _ => {} + } +} + #[cfg(feature = "trace-ot")] mod opentelemetry_tracer { #[cfg(not(test))] @@ -1215,7 +1222,7 @@ pub(super) mod test { }; use dashmap::DashMap; - use parking_lot::Mutex; + use tokio_tungstenite::WebSocketStream; use super::*; use crate::{node::testing_impl::NodeLabel, ring::Distance}; @@ -1265,15 +1272,20 @@ pub(super) mod test { pub(crate) struct TestEventListener { node_labels: Arc>, tx_log: Arc>>, - logs: Arc>>, + logs: Arc>>, + network_metrics_server: + Arc>>>>, } impl TestEventListener { - pub fn new() -> Self { + pub async fn new() -> Self { TestEventListener { node_labels: Arc::new(DashMap::new()), tx_log: Arc::new(DashMap::new()), - logs: Arc::new(Mutex::new(Vec::new())), + logs: Arc::new(tokio::sync::Mutex::new(Vec::new())), + network_metrics_server: Arc::new(tokio::sync::Mutex::new( + connect_to_metrics_server().await, + )), } } @@ -1282,7 +1294,9 @@ pub(super) mod test { } pub fn is_connected(&self, peer: &PeerId) -> bool { - let logs = self.logs.lock(); + let Ok(logs) = self.logs.try_lock() else { + return false; + }; logs.iter().any(|log| { &log.peer_id == peer && matches!(log.kind, EventKind::Connect(ConnectEvent::Connected { .. })) @@ -1295,7 +1309,9 @@ pub(super) mod test { for_key: &ContractKey, expected_value: &WrappedState, ) -> bool { - let logs = self.logs.lock(); + let Ok(logs) = self.logs.try_lock() else { + return false; + }; let put_ops = logs.iter().filter_map(|l| match &l.kind { EventKind::Put(ev) => Some((&l.tx, ev)), _ => None, @@ -1334,7 +1350,9 @@ pub(super) mod test { /// The contract was broadcasted from one peer to an other successfully. #[cfg(test)] pub fn contract_broadcasted(&self, for_key: &ContractKey) -> bool { - let logs = self.logs.lock(); + let Ok(logs) = self.logs.try_lock() else { + return false; + }; let put_broadcast_ops = logs.iter().filter_map(|l| match &l.kind { EventKind::Put(ev @ PutEvent::BroadcastEmitted { .. }) | EventKind::Put(ev @ PutEvent::BroadcastReceived { .. }) => Some((&l.tx, ev)), @@ -1367,7 +1385,9 @@ pub(super) mod test { } pub fn has_got_contract(&self, peer: &PeerId, expected_key: &ContractKey) -> bool { - let logs = self.logs.lock(); + let Ok(logs) = self.logs.try_lock() else { + return false; + }; logs.iter().any(|log| { &log.peer_id == peer && matches!(log.kind, EventKind::Get { ref key } if key == expected_key ) @@ -1375,7 +1395,9 @@ pub(super) mod test { } pub fn is_subscribed_to_contract(&self, peer: &PeerId, expected_key: &ContractKey) -> bool { - let logs = self.logs.lock(); + let Ok(logs) = self.logs.try_lock() else { + return false; + }; logs.iter().any(|log| { &log.peer_id == peer && matches!(log.kind, EventKind::Subscribed { ref key, .. } if key == expected_key ) @@ -1383,8 +1405,10 @@ pub(super) mod test { } /// Unique connections for a given peer and their relative distance to other peers. - pub fn connections(&self, peer: PeerId) -> impl Iterator { - let logs = self.logs.lock(); + pub fn connections(&self, peer: PeerId) -> Box> { + let Ok(logs) = self.logs.try_lock() else { + return Box::new([].into_iter()); + }; let disconnects = logs .iter() .filter(|l| matches!(l.kind, EventKind::Disconnected { .. })) @@ -1393,7 +1417,8 @@ pub(super) mod test { map }); - logs.iter() + let iter = logs + .iter() .filter_map(|l| { if let EventKind::Connect(ConnectEvent::Connected { this, connected }) = l.kind { @@ -1411,7 +1436,8 @@ pub(super) mod test { None }) .collect::>() - .into_iter() + .into_iter(); + Box::new(iter) } fn create_log(log: NetEventLog) -> (NetLogMessage, ListenerLogId) { @@ -1432,24 +1458,33 @@ pub(super) mod test { &'a self, logs: Either, Vec>>, ) -> BoxFuture<'a, ()> { - match logs { - Either::Left(log) => { - let tx = log.tx; - let (msg_log, log_id) = Self::create_log(log); - self.logs.lock().push(msg_log); - self.tx_log.entry(*tx).or_default().push(log_id); - } - Either::Right(logs) => { - let logs_list = &mut *self.logs.lock(); - for log in logs { + async { + match logs { + Either::Left(log) => { let tx = log.tx; let (msg_log, log_id) = Self::create_log(log); - logs_list.push(msg_log); + if let Some(conn) = &mut *self.network_metrics_server.lock().await { + send_to_metrics_server(conn, &msg_log).await; + } + self.logs.lock().await.push(msg_log); self.tx_log.entry(*tx).or_default().push(log_id); } + Either::Right(logs) => { + let logs_list = &mut *self.logs.lock().await; + let mut lock = self.network_metrics_server.lock().await; + for log in logs { + let tx = log.tx; + let (msg_log, log_id) = Self::create_log(log); + if let Some(conn) = &mut *lock { + send_to_metrics_server(conn, &msg_log).await; + } + logs_list.push(msg_log); + self.tx_log.entry(*tx).or_default().push(log_id); + } + } } } - async {}.boxed() + .boxed() } fn trait_clone(&self) -> Box { @@ -1461,8 +1496,8 @@ pub(super) mod test { } } - #[test] - fn test_get_connections() -> Result<(), anyhow::Error> { + #[tokio::test] + async fn test_get_connections() -> Result<(), anyhow::Error> { use crate::ring::Location; let peer_id = PeerId::random(); let loc = Location::try_from(0.5)?; @@ -1473,7 +1508,7 @@ pub(super) mod test { (PeerId::random(), Location::try_from(0.25)?), ]; - let listener = TestEventListener::new(); + let listener = TestEventListener::new().await; locations.iter().for_each(|(other, location)| { listener.register_events(Either::Left(NetEventLog { tx: &tx, diff --git a/crates/fdev/src/network_metrics_server.rs b/crates/fdev/src/network_metrics_server.rs index f91b47f70..d41b36b14 100644 --- a/crates/fdev/src/network_metrics_server.rs +++ b/crates/fdev/src/network_metrics_server.rs @@ -9,7 +9,7 @@ use axum::{ http::StatusCode, response::{IntoResponse, Response}, routing::get, - Router, Server, + Router, }; use dashmap::DashMap; use freenet::{ @@ -19,7 +19,10 @@ use freenet::{ use futures::{SinkExt, StreamExt}; use serde::{Deserialize, Serialize}; -pub async fn run_server(data_dir: Option) -> anyhow::Result<()> { +pub async fn run_server( + barrier: Arc, + changes: tokio::sync::broadcast::Sender, +) -> anyhow::Result<()> { const DEFAULT_PORT: u16 = 55010; let port = std::env::var("FDEV_NETWORK_METRICS_SERVER_PORT") @@ -27,7 +30,6 @@ pub async fn run_server(data_dir: Option) -> anyhow::Result<()> { .and_then(|s| s.parse().ok()) .unwrap_or(DEFAULT_PORT); - let (changes, rx) = tokio::sync::broadcast::channel(100); let router = Router::new() .route("/", get(home)) .route("/push-stats/", get(push_stats)) @@ -36,17 +38,13 @@ pub async fn run_server(data_dir: Option) -> anyhow::Result<()> { changes, peer_data: DashMap::new(), })); - if let Some(data_dir) = data_dir { - tokio::task::spawn(async move { - if let Err(err) = record_saver(data_dir, rx).await { - tracing::error!(error = %err, "Record saver failed"); - } - }); - } - Server::bind(&(Ipv4Addr::LOCALHOST, port).into()) - .serve(router.into_make_service()) - .await?; + tracing::info!("Starting metrics server on port {port}"); + barrier.wait().await; + let listener = tokio::net::TcpListener::bind((Ipv4Addr::LOCALHOST, port)) + .await + .unwrap(); + axum::serve(listener, router).await?; Ok(()) } @@ -110,7 +108,7 @@ async fn push_interface(ws: WebSocket, state: Arc) -> anyhow::Resul } } Err(e) => { - tracing::debug!("websocket error: {}", e); + tracing::debug!("Websocket error: {}", e); break; } } @@ -144,11 +142,11 @@ async fn pull_interface(ws: WebSocket, state: Arc) -> anyhow::Resul while let Ok(msg) = changes.recv().await { match msg { Change::AddedConnection { from, to } => { - let msg = PeerChange::added_connection_msg(from, to); + let msg = PeerChange::added_connection_msg((from.0 .0, from.1), (to.0 .0, to.1)); tx.send(Message::Binary(msg)).await?; } Change::RemovedConnection { from, at } => { - let msg = PeerChange::removed_connection_msg(at, from); + let msg = PeerChange::removed_connection_msg(at.0, from.0); tx.send(Message::Binary(msg)).await?; } } @@ -167,17 +165,41 @@ struct PeerData { } #[derive(Debug, Clone, Serialize, Deserialize)] -enum Change { +pub(crate) enum Change { AddedConnection { - from: (PeerId, f64), - to: (PeerId, f64), + from: (PeerIdHumanReadable, f64), + to: (PeerIdHumanReadable, f64), }, RemovedConnection { - from: PeerId, - at: PeerId, + from: PeerIdHumanReadable, + at: PeerIdHumanReadable, }, } +#[derive(Debug, Clone)] +pub(crate) struct PeerIdHumanReadable(PeerId); + +impl Serialize for PeerIdHumanReadable { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(&self.0.to_string()) + } +} + +impl<'de> Deserialize<'de> for PeerIdHumanReadable { + fn deserialize>(deserializer: D) -> Result { + let s = String::deserialize(deserializer)?; + Ok(PeerIdHumanReadable( + PeerId::from_str(&s).map_err(serde::de::Error::custom)?, + )) + } +} + +impl From for PeerIdHumanReadable { + fn from(peer_id: PeerId) -> Self { + Self(peer_id) + } +} + impl ServerState { fn save_record(&self, change: PeerChange<'_>) -> Result<(), anyhow::Error> { match change { @@ -213,8 +235,8 @@ impl ServerState { } let _ = self.changes.send(Change::AddedConnection { - from: (from_peer_id, from_loc), - to: (to_peer_id, to_loc), + from: (from_peer_id.into(), from_loc), + to: (to_peer_id.into(), to_loc), }); } PeerChange::RemovedConnection(removed) => { @@ -234,8 +256,8 @@ impl ServerState { } let _ = self.changes.send(Change::RemovedConnection { - from: from_peer_id, - at: at_peer_id, + from: from_peer_id.into(), + at: at_peer_id.into(), }); } _ => unreachable!(), @@ -244,7 +266,7 @@ impl ServerState { } } -async fn record_saver( +pub(crate) async fn record_saver( data_dir: PathBuf, mut incoming_rec: tokio::sync::broadcast::Receiver, ) -> anyhow::Result<()> { @@ -261,28 +283,39 @@ async fn record_saver( .await?, ); - let mut batch = Vec::with_capacity(1024); + // FIXME: this ain't flushing correctly after test ends + // let mut batch = Vec::with_capacity(1024); while let Ok(record) = incoming_rec.recv().await { - batch.push(record); - if batch.len() > 100 { - let batch = std::mem::replace(&mut batch, Vec::with_capacity(1024)); - let result = tokio::task::spawn_blocking(move || { - let mut serialized = Vec::with_capacity(batch.len() * 128); - for rec in batch { - let rec = serde_json::to_vec(&rec).unwrap(); - serialized.push(rec); - } - serialized - }) - .await?; - for rec in result { - fs.write_all(&rec).await?; - } - } - } - for rec in batch { - let rec = serde_json::to_vec(&rec).unwrap(); + let mut rec = serde_json::to_vec(&record).unwrap(); + rec.push(b'\n'); fs.write_all(&rec).await?; + fs.flush().await?; + // batch.push(record); + // if batch.len() > 0 { + // let batch = std::mem::replace(&mut batch, Vec::with_capacity(1024)); + // let result = tokio::task::spawn_blocking(move || { + // let mut serialized = Vec::with_capacity(batch.len() * 128); + // for rec in batch { + // let mut rec = serde_json::to_vec(&rec).unwrap(); + // rec.push(b'\n'); + // serialized.push(rec); + // } + // serialized + // }) + // .await?; + // for rec in result { + // fs.write_all(&rec).await?; + // fs.flush().await?; + // } + // } } + // tracing::warn!(?batch, "Saving records"); + // for rec in batch { + // let mut rec = serde_json::to_vec(&rec).unwrap(); + // rec.push(b'\n'); + // fs.write_all(&rec).await?; + // } + // fs.flush().await?; + tracing::warn!("Finished saving records"); Ok(()) } diff --git a/crates/fdev/src/testing.rs b/crates/fdev/src/testing.rs index cd3e175eb..20bdc379b 100644 --- a/crates/fdev/src/testing.rs +++ b/crates/fdev/src/testing.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, time::Duration}; +use std::{path::PathBuf, sync::Arc, time::Duration}; use anyhow::Error; use freenet::dev_tool::SimNetwork; @@ -53,6 +53,9 @@ pub struct TestConfig { /// If provided, the execution data will be saved in this directory. #[arg(long)] execution_data: Option, + /// Don't start the metrics server for this test run. + #[arg(long)] + disable_metrics: bool, #[clap(subcommand)] /// Execution mode for the test. pub command: TestMode, @@ -99,24 +102,45 @@ pub enum TestMode { } pub(crate) async fn test_framework(base_config: TestConfig) -> anyhow::Result<(), Error> { - let exec_data_path = base_config.execution_data.clone(); - tokio::task::spawn(async move { - if let Err(err) = crate::network_metrics_server::run_server(exec_data_path).await { - tracing::error!(error = %err, "Network metrics server failed"); - } - }); - match &base_config.command { - TestMode::SingleProcess => { - single_process::run(&base_config).await?; - } - TestMode::MultiProcess(config) => { - multiple_process::run(&base_config, config).await?; - } - TestMode::Network => { - network::run(&base_config).await?; + let (server, changes_recorder) = if !base_config.disable_metrics { + let (changes, rx) = tokio::sync::broadcast::channel(10000); + let changes_recorder = base_config.execution_data.clone().map(|data_dir| { + tokio::task::spawn(async move { + if let Err(err) = crate::network_metrics_server::record_saver(data_dir, rx).await { + tracing::error!(error = %err, "Record saver failed"); + } + }) + }); + let barrier = Arc::new(tokio::sync::Barrier::new(2)); + let barrier_cp = barrier.clone(); + let server = tokio::task::spawn(async move { + if let Err(err) = crate::network_metrics_server::run_server(barrier_cp, changes).await { + tracing::error!(error = %err, "Network metrics server failed"); + } + }); + barrier.wait().await; + tokio::time::sleep(Duration::from_millis(10)).await; + (Some(server), changes_recorder) + } else { + (None, None) + }; + let res = match &base_config.command { + TestMode::SingleProcess => single_process::run(&base_config).await, + TestMode::MultiProcess(config) => multiple_process::run(&base_config, config).await, + TestMode::Network => network::run(&base_config).await, + }; + if let Some(server) = server { + server.abort(); + } + if let Some(changes) = changes_recorder { + tokio::select! { + _ = tokio::signal::ctrl_c() => {} + r = changes => { + r?; + } } } - Ok(()) + res } async fn config_sim_network(base_config: &TestConfig) -> anyhow::Result { diff --git a/crates/fdev/src/testing/multiple_process.rs b/crates/fdev/src/testing/multiple_process.rs index dfa17896a..35c1ea38a 100644 --- a/crates/fdev/src/testing/multiple_process.rs +++ b/crates/fdev/src/testing/multiple_process.rs @@ -97,7 +97,6 @@ async fn supervisor(config: &super::TestConfig) -> anyhow::Result<(), Error> { simulated_network.debug(); // set to avoid deleting temp dirs created let peers = simulated_network.build_peers(); - // let (user_ev_controller, event_rx) = tokio::sync::watch::channel((0, PeerId::random())); let (user_ev_controller, event_rx) = tokio::sync::mpsc::channel(1); let seed = config.seed(); From bca8b98c5995451e4b73b74b99fafa0811e70606 Mon Sep 17 00:00:00 2001 From: Ignacio Duart Date: Tue, 28 Nov 2023 17:21:35 +0100 Subject: [PATCH 07/12] Add timestamp to saved records --- Cargo.lock | 1 + crates/fdev/Cargo.toml | 1 + crates/fdev/src/network_metrics_server.rs | 29 +++++++++++++++-------- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a142112e..442690e53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1505,6 +1505,7 @@ dependencies = [ "axum", "bincode", "bs58", + "chrono", "clap", "dashmap", "either", diff --git a/crates/fdev/Cargo.toml b/crates/fdev/Cargo.toml index 2325dbe3f..0dc515f66 100644 --- a/crates/fdev/Cargo.toml +++ b/crates/fdev/Cargo.toml @@ -13,6 +13,7 @@ anyhow = "1" axum = { default-features = false, features = ["http1", "matched-path", "query", "tower-log", "ws"], workspace = true } bincode = "1" bs58 = { workspace = true } +chrono = { workspace = true } clap = { workspace = true, features = ["derive", "env"] } dashmap = { workspace = true } either = { workspace = true } diff --git a/crates/fdev/src/network_metrics_server.rs b/crates/fdev/src/network_metrics_server.rs index d41b36b14..63e41f7f7 100644 --- a/crates/fdev/src/network_metrics_server.rs +++ b/crates/fdev/src/network_metrics_server.rs @@ -270,26 +270,35 @@ pub(crate) async fn record_saver( data_dir: PathBuf, mut incoming_rec: tokio::sync::broadcast::Receiver, ) -> anyhow::Result<()> { - use tokio::io::AsyncWriteExt; + use std::io::Write; if !data_dir.exists() { std::fs::create_dir_all(&data_dir)?; } - let mut fs = tokio::io::BufWriter::new( - tokio::fs::OpenOptions::new() + let mut fs = std::io::BufWriter::new( + std::fs::OpenOptions::new() .write(true) .truncate(true) .create(true) - .open(data_dir.join("network-metrics")) - .await?, + .open(data_dir.join("network-metrics"))?, ); + #[derive(Serialize)] + struct WithTimestamp { + timestamp: chrono::DateTime, + #[serde(flatten)] + change: Change, + } + // FIXME: this ain't flushing correctly after test ends // let mut batch = Vec::with_capacity(1024); - while let Ok(record) = incoming_rec.recv().await { - let mut rec = serde_json::to_vec(&record).unwrap(); - rec.push(b'\n'); - fs.write_all(&rec).await?; - fs.flush().await?; + while let Ok(change) = incoming_rec.recv().await { + let change = WithTimestamp { + change, + timestamp: chrono::Utc::now(), + }; + serde_json::to_writer(&mut fs, &change)?; + fs.write_all(b"\n")?; + fs.flush()?; // batch.push(record); // if batch.len() > 0 { // let batch = std::mem::replace(&mut batch, Vec::with_capacity(1024)); From da07ebe8300f123b153cc150a986d04e020fcace Mon Sep 17 00:00:00 2001 From: Ignacio Duart Date: Wed, 29 Nov 2023 00:37:59 +0100 Subject: [PATCH 08/12] Add web UI for network metrics server --- crates/core/src/generated/mod.rs | 2 +- crates/core/src/ring.rs | 7 + crates/fdev/src/config.rs | 1 + crates/fdev/src/main.rs | 11 +- crates/fdev/src/network_metrics_server.rs | 53 +- crates/fdev/src/testing.rs | 27 +- network-monitor/.gitignore | 4 + network-monitor/dist/bundle.js | 1743 +++++++++++++++++ network-monitor/dist/bundle.js.map | 1 + network-monitor/dist/index.html | 50 + network-monitor/dist/jest.config.d.ts | 3 + network-monitor/dist/jest.config.js | 15 + network-monitor/dist/jest.config.js.map | 1 + network-monitor/dist/src/app.d.ts | 1 + network-monitor/dist/src/app.js | 57 + network-monitor/dist/src/app.js.map | 1 + .../dist/src/generated/topology.d.ts | 14 + .../dist/src/generated/topology.js | 111 ++ .../dist/src/generated/topology.js.map | 1 + .../generated/topology/added-connection.d.ts | 59 + .../generated/topology/added-connection.js | 164 ++ .../topology/added-connection.js.map | 1 + .../topology/controller-response.d.ts | 48 + .../generated/topology/controller-response.js | 149 ++ .../topology/controller-response.js.map | 1 + .../dist/src/generated/topology/error.d.ts | 30 + .../dist/src/generated/topology/error.js | 114 ++ .../dist/src/generated/topology/error.js.map | 1 + .../dist/src/generated/topology/ok.d.ts | 27 + .../dist/src/generated/topology/ok.js | 113 ++ .../dist/src/generated/topology/ok.js.map | 1 + .../generated/topology/peer-change-type.d.ts | 23 + .../generated/topology/peer-change-type.js | 49 + .../topology/peer-change-type.js.map | 1 + .../src/generated/topology/peer-change.d.ts | 68 + .../src/generated/topology/peer-change.js | 205 ++ .../src/generated/topology/peer-change.js.map | 1 + .../topology/removed-connection.d.ts | 48 + .../generated/topology/removed-connection.js | 127 ++ .../topology/removed-connection.js.map | 1 + .../dist/src/generated/topology/response.d.ts | 16 + .../dist/src/generated/topology/response.js | 42 + .../src/generated/topology/response.js.map | 1 + network-monitor/dist/src/topology.d.ts | 34 + network-monitor/dist/src/topology.js | 241 +++ network-monitor/dist/src/topology.js.map | 1 + network-monitor/dist/tests/app.test.d.ts | 4 + network-monitor/dist/tests/app.test.js | 75 + network-monitor/dist/tests/app.test.js.map | 1 + network-monitor/jest.config.ts | 13 + network-monitor/package.json | 30 + network-monitor/src/app.ts | 37 + network-monitor/src/generated/topology.ts | 16 + .../generated/topology/added-connection.ts | 144 ++ .../generated/topology/controller-response.ts | 135 ++ .../src/generated/topology/error.ts | 85 + network-monitor/src/generated/topology/ok.ts | 81 + .../generated/topology/peer-change-type.ts | 60 + .../src/generated/topology/peer-change.ts | 200 ++ .../generated/topology/removed-connection.ts | 109 ++ .../src/generated/topology/response.ts | 43 + network-monitor/src/topology.ts | 356 ++++ network-monitor/tests/app.test.ts | 51 + network-monitor/tests/topology.test.ts | 84 + network-monitor/tsconfig.json | 112 ++ network-monitor/webpack.config.js | 31 + 66 files changed, 5308 insertions(+), 28 deletions(-) create mode 100644 network-monitor/.gitignore create mode 100644 network-monitor/dist/bundle.js create mode 100644 network-monitor/dist/bundle.js.map create mode 100644 network-monitor/dist/index.html create mode 100644 network-monitor/dist/jest.config.d.ts create mode 100644 network-monitor/dist/jest.config.js create mode 100644 network-monitor/dist/jest.config.js.map create mode 100644 network-monitor/dist/src/app.d.ts create mode 100644 network-monitor/dist/src/app.js create mode 100644 network-monitor/dist/src/app.js.map create mode 100644 network-monitor/dist/src/generated/topology.d.ts create mode 100644 network-monitor/dist/src/generated/topology.js create mode 100644 network-monitor/dist/src/generated/topology.js.map create mode 100644 network-monitor/dist/src/generated/topology/added-connection.d.ts create mode 100644 network-monitor/dist/src/generated/topology/added-connection.js create mode 100644 network-monitor/dist/src/generated/topology/added-connection.js.map create mode 100644 network-monitor/dist/src/generated/topology/controller-response.d.ts create mode 100644 network-monitor/dist/src/generated/topology/controller-response.js create mode 100644 network-monitor/dist/src/generated/topology/controller-response.js.map create mode 100644 network-monitor/dist/src/generated/topology/error.d.ts create mode 100644 network-monitor/dist/src/generated/topology/error.js create mode 100644 network-monitor/dist/src/generated/topology/error.js.map create mode 100644 network-monitor/dist/src/generated/topology/ok.d.ts create mode 100644 network-monitor/dist/src/generated/topology/ok.js create mode 100644 network-monitor/dist/src/generated/topology/ok.js.map create mode 100644 network-monitor/dist/src/generated/topology/peer-change-type.d.ts create mode 100644 network-monitor/dist/src/generated/topology/peer-change-type.js create mode 100644 network-monitor/dist/src/generated/topology/peer-change-type.js.map create mode 100644 network-monitor/dist/src/generated/topology/peer-change.d.ts create mode 100644 network-monitor/dist/src/generated/topology/peer-change.js create mode 100644 network-monitor/dist/src/generated/topology/peer-change.js.map create mode 100644 network-monitor/dist/src/generated/topology/removed-connection.d.ts create mode 100644 network-monitor/dist/src/generated/topology/removed-connection.js create mode 100644 network-monitor/dist/src/generated/topology/removed-connection.js.map create mode 100644 network-monitor/dist/src/generated/topology/response.d.ts create mode 100644 network-monitor/dist/src/generated/topology/response.js create mode 100644 network-monitor/dist/src/generated/topology/response.js.map create mode 100644 network-monitor/dist/src/topology.d.ts create mode 100644 network-monitor/dist/src/topology.js create mode 100644 network-monitor/dist/src/topology.js.map create mode 100644 network-monitor/dist/tests/app.test.d.ts create mode 100644 network-monitor/dist/tests/app.test.js create mode 100644 network-monitor/dist/tests/app.test.js.map create mode 100644 network-monitor/jest.config.ts create mode 100644 network-monitor/package.json create mode 100644 network-monitor/src/app.ts create mode 100644 network-monitor/src/generated/topology.ts create mode 100644 network-monitor/src/generated/topology/added-connection.ts create mode 100644 network-monitor/src/generated/topology/controller-response.ts create mode 100644 network-monitor/src/generated/topology/error.ts create mode 100644 network-monitor/src/generated/topology/ok.ts create mode 100644 network-monitor/src/generated/topology/peer-change-type.ts create mode 100644 network-monitor/src/generated/topology/peer-change.ts create mode 100644 network-monitor/src/generated/topology/removed-connection.ts create mode 100644 network-monitor/src/generated/topology/response.ts create mode 100644 network-monitor/src/topology.ts create mode 100644 network-monitor/tests/app.test.ts create mode 100644 network-monitor/tests/topology.test.ts create mode 100644 network-monitor/tsconfig.json create mode 100644 network-monitor/webpack.config.js diff --git a/crates/core/src/generated/mod.rs b/crates/core/src/generated/mod.rs index 60b8973da..515b1f761 100644 --- a/crates/core/src/generated/mod.rs +++ b/crates/core/src/generated/mod.rs @@ -42,7 +42,7 @@ impl PeerChange<'_> { let msg = topology::PeerChange::create( &mut buf, &topology::PeerChangeArgs { - change_type: topology::PeerChangeType::AddedConnection, + change_type: topology::PeerChangeType::NONE, change: None, current_state: Some(connections), }, diff --git a/crates/core/src/ring.rs b/crates/core/src/ring.rs index c552ed35c..692abb60d 100644 --- a/crates/core/src/ring.rs +++ b/crates/core/src/ring.rs @@ -291,6 +291,13 @@ impl Ring { if config.local_ip.is_none() || config.local_port.is_none() { return Err(anyhow::anyhow!("IP and port are required for gateways")); } + + /* + min 20 max 100 + GW A, number: 100 + GW B, number: 50 + */ + ring.update_location(Some(loc)); for PeerKeyLocation { peer, location } in gateways { // FIXME: this is problematic cause gateways will take all spots then! diff --git a/crates/fdev/src/config.rs b/crates/fdev/src/config.rs index 98fa68417..488fc6936 100644 --- a/crates/fdev/src/config.rs +++ b/crates/fdev/src/config.rs @@ -44,6 +44,7 @@ pub enum SubCommand { WasmRuntime(ExecutorConfig), Execute(RunCliConfig), Test(crate::testing::TestConfig), + NetworkMetricsServer(crate::network_metrics_server::ServerConfig), } impl SubCommand { diff --git a/crates/fdev/src/main.rs b/crates/fdev/src/main.rs index 278d6cccb..202a8e150 100644 --- a/crates/fdev/src/main.rs +++ b/crates/fdev/src/main.rs @@ -7,8 +7,8 @@ mod build; mod commands; mod config; mod inspect; -mod new_package; pub(crate) mod network_metrics_server; +mod new_package; mod testing; mod util; mod wasm_runtime; @@ -59,6 +59,15 @@ fn main() -> Result<(), anyhow::Error> { } }, SubCommand::Test(test_config) => testing::test_framework(test_config).await, + SubCommand::NetworkMetricsServer(server_config) => { + let (server, _) = + crate::network_metrics_server::start_server(&server_config).await; + tokio::select! { + _ = tokio::signal::ctrl_c() => {} + _ = server => {} + } + Ok(()) + } }; // todo: make all commands return concrete `thiserror` compatible errors so we can use anyhow r.map_err(|e| anyhow::format_err!(e)) diff --git a/crates/fdev/src/network_metrics_server.rs b/crates/fdev/src/network_metrics_server.rs index 63e41f7f7..db52620ed 100644 --- a/crates/fdev/src/network_metrics_server.rs +++ b/crates/fdev/src/network_metrics_server.rs @@ -1,4 +1,4 @@ -use std::{net::Ipv4Addr, path::PathBuf, str::FromStr, sync::Arc}; +use std::{net::Ipv4Addr, path::PathBuf, str::FromStr, sync::Arc, time::Duration}; use axum::{ body::Body, @@ -19,7 +19,46 @@ use freenet::{ use futures::{SinkExt, StreamExt}; use serde::{Deserialize, Serialize}; -pub async fn run_server( +/// Network metrics server. Records metrics and data from a test network that can be used for +/// analysis and visualization. +#[derive(clap::Parser, Clone)] +pub struct ServerConfig { + /// If provided, the server will save the event logs in this directory. + #[arg(long)] + pub log_directory: Option, +} + +/// Starts the server and returns a handle to the server thread +/// and a handle to the changes recorder thread if changes record path was provided. +pub async fn start_server( + config: &ServerConfig, +) -> ( + tokio::task::JoinHandle<()>, + Option>, +) { + let changes_record_path = config.log_directory.clone(); + let (changes, rx) = tokio::sync::broadcast::channel(10000); + let changes_recorder = changes_record_path.map(|data_dir| { + tokio::task::spawn(async move { + if let Err(err) = crate::network_metrics_server::record_saver(data_dir, rx).await { + tracing::error!(error = %err, "Record saver failed"); + } + }) + }); + let barrier = Arc::new(tokio::sync::Barrier::new(2)); + let barrier_cp = barrier.clone(); + let server = tokio::task::spawn(async move { + if let Err(err) = crate::network_metrics_server::run_server(barrier_cp, changes).await { + tracing::error!(error = %err, "Network metrics server failed"); + } + }); + tokio::time::sleep(Duration::from_millis(10)).await; + barrier.wait().await; + tokio::time::sleep(Duration::from_millis(10)).await; + (server, changes_recorder) +} + +async fn run_server( barrier: Arc, changes: tokio::sync::broadcast::Sender, ) -> anyhow::Result<()> { @@ -41,9 +80,7 @@ pub async fn run_server( tracing::info!("Starting metrics server on port {port}"); barrier.wait().await; - let listener = tokio::net::TcpListener::bind((Ipv4Addr::LOCALHOST, port)) - .await - .unwrap(); + let listener = tokio::net::TcpListener::bind((Ipv4Addr::LOCALHOST, port)).await?; axum::serve(listener, router).await?; Ok(()) } @@ -266,7 +303,7 @@ impl ServerState { } } -pub(crate) async fn record_saver( +async fn record_saver( data_dir: PathBuf, mut incoming_rec: tokio::sync::broadcast::Receiver, ) -> anyhow::Result<()> { @@ -274,12 +311,14 @@ pub(crate) async fn record_saver( if !data_dir.exists() { std::fs::create_dir_all(&data_dir)?; } + let log_file = data_dir.join("network-metrics"); + tracing::info!("Recording logs to {log_file:?}"); let mut fs = std::io::BufWriter::new( std::fs::OpenOptions::new() .write(true) .truncate(true) .create(true) - .open(data_dir.join("network-metrics"))?, + .open(log_file)?, ); #[derive(Serialize)] diff --git a/crates/fdev/src/testing.rs b/crates/fdev/src/testing.rs index 20bdc379b..c33ebb71d 100644 --- a/crates/fdev/src/testing.rs +++ b/crates/fdev/src/testing.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, sync::Arc, time::Duration}; +use std::{path::PathBuf, time::Duration}; use anyhow::Error; use freenet::dev_tool::SimNetwork; @@ -9,6 +9,8 @@ mod single_process; pub(crate) use multiple_process::Process; +use crate::network_metrics_server::{start_server, ServerConfig}; + /// Testing framework for running Freenet network simulations. #[derive(clap::Parser, Clone)] pub struct TestConfig { @@ -103,24 +105,11 @@ pub enum TestMode { pub(crate) async fn test_framework(base_config: TestConfig) -> anyhow::Result<(), Error> { let (server, changes_recorder) = if !base_config.disable_metrics { - let (changes, rx) = tokio::sync::broadcast::channel(10000); - let changes_recorder = base_config.execution_data.clone().map(|data_dir| { - tokio::task::spawn(async move { - if let Err(err) = crate::network_metrics_server::record_saver(data_dir, rx).await { - tracing::error!(error = %err, "Record saver failed"); - } - }) - }); - let barrier = Arc::new(tokio::sync::Barrier::new(2)); - let barrier_cp = barrier.clone(); - let server = tokio::task::spawn(async move { - if let Err(err) = crate::network_metrics_server::run_server(barrier_cp, changes).await { - tracing::error!(error = %err, "Network metrics server failed"); - } - }); - barrier.wait().await; - tokio::time::sleep(Duration::from_millis(10)).await; - (Some(server), changes_recorder) + let (s, r) = start_server(&ServerConfig { + log_directory: base_config.execution_data.clone(), + }) + .await; + (Some(s), r) } else { (None, None) }; diff --git a/network-monitor/.gitignore b/network-monitor/.gitignore new file mode 100644 index 000000000..a585cc9e6 --- /dev/null +++ b/network-monitor/.gitignore @@ -0,0 +1,4 @@ +node_modules/* +dist/**/* +!dist/**/*.html +package-lock.json \ No newline at end of file diff --git a/network-monitor/dist/bundle.js b/network-monitor/dist/bundle.js new file mode 100644 index 000000000..d38aa0a1f --- /dev/null +++ b/network-monitor/dist/bundle.js @@ -0,0 +1,1743 @@ +(() => { + "use strict"; + var t = { + 251: (t, e, n) => { + n.r(e), + n.d(e, { + Builder: () => b, + ByteBuffer: () => l, + Encoding: () => u, + FILE_IDENTIFIER_LENGTH: () => s, + SIZEOF_INT: () => r, + SIZEOF_SHORT: () => i, + SIZE_PREFIX_LENGTH: () => o, + float32: () => c, + float64: () => d, + int32: () => a, + isLittleEndian: () => h, + }); + const i = 2, + r = 4, + s = 4, + o = 4, + a = new Int32Array(2), + c = new Float32Array(a.buffer), + d = new Float64Array(a.buffer), + h = 1 === new Uint16Array(new Uint8Array([1, 0]).buffer)[0]; + var u; + !(function (t) { + (t[(t.UTF8_BYTES = 1)] = "UTF8_BYTES"), + (t[(t.UTF16_STRING = 2)] = "UTF16_STRING"); + })(u || (u = {})); + class l { + constructor(t) { + (this.bytes_ = t), + (this.position_ = 0), + (this.text_decoder_ = new TextDecoder()); + } + static allocate(t) { + return new l(new Uint8Array(t)); + } + clear() { + this.position_ = 0; + } + bytes() { + return this.bytes_; + } + position() { + return this.position_; + } + setPosition(t) { + this.position_ = t; + } + capacity() { + return this.bytes_.length; + } + readInt8(t) { + return (this.readUint8(t) << 24) >> 24; + } + readUint8(t) { + return this.bytes_[t]; + } + readInt16(t) { + return (this.readUint16(t) << 16) >> 16; + } + readUint16(t) { + return this.bytes_[t] | (this.bytes_[t + 1] << 8); + } + readInt32(t) { + return ( + this.bytes_[t] | + (this.bytes_[t + 1] << 8) | + (this.bytes_[t + 2] << 16) | + (this.bytes_[t + 3] << 24) + ); + } + readUint32(t) { + return this.readInt32(t) >>> 0; + } + readInt64(t) { + return BigInt.asIntN( + 64, + BigInt(this.readUint32(t)) + + (BigInt(this.readUint32(t + 4)) << BigInt(32)) + ); + } + readUint64(t) { + return BigInt.asUintN( + 64, + BigInt(this.readUint32(t)) + + (BigInt(this.readUint32(t + 4)) << BigInt(32)) + ); + } + readFloat32(t) { + return (a[0] = this.readInt32(t)), c[0]; + } + readFloat64(t) { + return ( + (a[h ? 0 : 1] = this.readInt32(t)), + (a[h ? 1 : 0] = this.readInt32(t + 4)), + d[0] + ); + } + writeInt8(t, e) { + this.bytes_[t] = e; + } + writeUint8(t, e) { + this.bytes_[t] = e; + } + writeInt16(t, e) { + (this.bytes_[t] = e), (this.bytes_[t + 1] = e >> 8); + } + writeUint16(t, e) { + (this.bytes_[t] = e), (this.bytes_[t + 1] = e >> 8); + } + writeInt32(t, e) { + (this.bytes_[t] = e), + (this.bytes_[t + 1] = e >> 8), + (this.bytes_[t + 2] = e >> 16), + (this.bytes_[t + 3] = e >> 24); + } + writeUint32(t, e) { + (this.bytes_[t] = e), + (this.bytes_[t + 1] = e >> 8), + (this.bytes_[t + 2] = e >> 16), + (this.bytes_[t + 3] = e >> 24); + } + writeInt64(t, e) { + this.writeInt32(t, Number(BigInt.asIntN(32, e))), + this.writeInt32( + t + 4, + Number(BigInt.asIntN(32, e >> BigInt(32))) + ); + } + writeUint64(t, e) { + this.writeUint32(t, Number(BigInt.asUintN(32, e))), + this.writeUint32( + t + 4, + Number(BigInt.asUintN(32, e >> BigInt(32))) + ); + } + writeFloat32(t, e) { + (c[0] = e), this.writeInt32(t, a[0]); + } + writeFloat64(t, e) { + (d[0] = e), + this.writeInt32(t, a[h ? 0 : 1]), + this.writeInt32(t + 4, a[h ? 1 : 0]); + } + getBufferIdentifier() { + if (this.bytes_.length < this.position_ + r + s) + throw new Error( + "FlatBuffers: ByteBuffer is too short to contain an identifier." + ); + let t = ""; + for (let e = 0; e < s; e++) + t += String.fromCharCode(this.readInt8(this.position_ + r + e)); + return t; + } + __offset(t, e) { + const n = t - this.readInt32(t); + return e < this.readInt16(n) ? this.readInt16(n + e) : 0; + } + __union(t, e) { + return (t.bb_pos = e + this.readInt32(e)), (t.bb = this), t; + } + __string(t, e) { + t += this.readInt32(t); + const n = this.readInt32(t); + t += r; + const i = this.bytes_.subarray(t, t + n); + return e === u.UTF8_BYTES ? i : this.text_decoder_.decode(i); + } + __union_with_string(t, e) { + return "string" == typeof t ? this.__string(e) : this.__union(t, e); + } + __indirect(t) { + return t + this.readInt32(t); + } + __vector(t) { + return t + this.readInt32(t) + r; + } + __vector_len(t) { + return this.readInt32(t + this.readInt32(t)); + } + __has_identifier(t) { + if (t.length != s) + throw new Error( + "FlatBuffers: file identifier must be length " + s + ); + for (let e = 0; e < s; e++) + if (t.charCodeAt(e) != this.readInt8(this.position() + r + e)) + return !1; + return !0; + } + createScalarList(t, e) { + const n = []; + for (let i = 0; i < e; ++i) { + const e = t(i); + null !== e && n.push(e); + } + return n; + } + createObjList(t, e) { + const n = []; + for (let i = 0; i < e; ++i) { + const e = t(i); + null !== e && n.push(e.unpack()); + } + return n; + } + } + class b { + constructor(t) { + let e; + (this.minalign = 1), + (this.vtable = null), + (this.vtable_in_use = 0), + (this.isNested = !1), + (this.object_start = 0), + (this.vtables = []), + (this.vector_num_elems = 0), + (this.force_defaults = !1), + (this.string_maps = null), + (this.text_encoder = new TextEncoder()), + (e = t || 1024), + (this.bb = l.allocate(e)), + (this.space = e); + } + clear() { + this.bb.clear(), + (this.space = this.bb.capacity()), + (this.minalign = 1), + (this.vtable = null), + (this.vtable_in_use = 0), + (this.isNested = !1), + (this.object_start = 0), + (this.vtables = []), + (this.vector_num_elems = 0), + (this.force_defaults = !1), + (this.string_maps = null); + } + forceDefaults(t) { + this.force_defaults = t; + } + dataBuffer() { + return this.bb; + } + asUint8Array() { + return this.bb + .bytes() + .subarray(this.bb.position(), this.bb.position() + this.offset()); + } + prep(t, e) { + t > this.minalign && (this.minalign = t); + const n = (1 + ~(this.bb.capacity() - this.space + e)) & (t - 1); + for (; this.space < n + t + e; ) { + const t = this.bb.capacity(); + (this.bb = b.growByteBuffer(this.bb)), + (this.space += this.bb.capacity() - t); + } + this.pad(n); + } + pad(t) { + for (let e = 0; e < t; e++) this.bb.writeInt8(--this.space, 0); + } + writeInt8(t) { + this.bb.writeInt8((this.space -= 1), t); + } + writeInt16(t) { + this.bb.writeInt16((this.space -= 2), t); + } + writeInt32(t) { + this.bb.writeInt32((this.space -= 4), t); + } + writeInt64(t) { + this.bb.writeInt64((this.space -= 8), t); + } + writeFloat32(t) { + this.bb.writeFloat32((this.space -= 4), t); + } + writeFloat64(t) { + this.bb.writeFloat64((this.space -= 8), t); + } + addInt8(t) { + this.prep(1, 0), this.writeInt8(t); + } + addInt16(t) { + this.prep(2, 0), this.writeInt16(t); + } + addInt32(t) { + this.prep(4, 0), this.writeInt32(t); + } + addInt64(t) { + this.prep(8, 0), this.writeInt64(t); + } + addFloat32(t) { + this.prep(4, 0), this.writeFloat32(t); + } + addFloat64(t) { + this.prep(8, 0), this.writeFloat64(t); + } + addFieldInt8(t, e, n) { + (this.force_defaults || e != n) && (this.addInt8(e), this.slot(t)); + } + addFieldInt16(t, e, n) { + (this.force_defaults || e != n) && (this.addInt16(e), this.slot(t)); + } + addFieldInt32(t, e, n) { + (this.force_defaults || e != n) && (this.addInt32(e), this.slot(t)); + } + addFieldInt64(t, e, n) { + (this.force_defaults || e !== n) && + (this.addInt64(e), this.slot(t)); + } + addFieldFloat32(t, e, n) { + (this.force_defaults || e != n) && + (this.addFloat32(e), this.slot(t)); + } + addFieldFloat64(t, e, n) { + (this.force_defaults || e != n) && + (this.addFloat64(e), this.slot(t)); + } + addFieldOffset(t, e, n) { + (this.force_defaults || e != n) && + (this.addOffset(e), this.slot(t)); + } + addFieldStruct(t, e, n) { + e != n && (this.nested(e), this.slot(t)); + } + nested(t) { + if (t != this.offset()) + throw new TypeError( + "FlatBuffers: struct must be serialized inline." + ); + } + notNested() { + if (this.isNested) + throw new TypeError( + "FlatBuffers: object serialization must not be nested." + ); + } + slot(t) { + null !== this.vtable && (this.vtable[t] = this.offset()); + } + offset() { + return this.bb.capacity() - this.space; + } + static growByteBuffer(t) { + const e = t.capacity(); + if (3221225472 & e) + throw new Error( + "FlatBuffers: cannot grow buffer beyond 2 gigabytes." + ); + const n = e << 1, + i = l.allocate(n); + return i.setPosition(n - e), i.bytes().set(t.bytes(), n - e), i; + } + addOffset(t) { + this.prep(r, 0), this.writeInt32(this.offset() - t + r); + } + startObject(t) { + this.notNested(), + null == this.vtable && (this.vtable = []), + (this.vtable_in_use = t); + for (let e = 0; e < t; e++) this.vtable[e] = 0; + (this.isNested = !0), (this.object_start = this.offset()); + } + endObject() { + if (null == this.vtable || !this.isNested) + throw new Error( + "FlatBuffers: endObject called without startObject" + ); + this.addInt32(0); + const t = this.offset(); + let e = this.vtable_in_use - 1; + for (; e >= 0 && 0 == this.vtable[e]; e--); + const n = e + 1; + for (; e >= 0; e--) + this.addInt16(0 != this.vtable[e] ? t - this.vtable[e] : 0); + this.addInt16(t - this.object_start); + const r = (n + 2) * i; + this.addInt16(r); + let s = 0; + const o = this.space; + t: for (e = 0; e < this.vtables.length; e++) { + const t = this.bb.capacity() - this.vtables[e]; + if (r == this.bb.readInt16(t)) { + for (let e = i; e < r; e += i) + if (this.bb.readInt16(o + e) != this.bb.readInt16(t + e)) + continue t; + s = this.vtables[e]; + break; + } + } + return ( + s + ? ((this.space = this.bb.capacity() - t), + this.bb.writeInt32(this.space, s - t)) + : (this.vtables.push(this.offset()), + this.bb.writeInt32( + this.bb.capacity() - t, + this.offset() - t + )), + (this.isNested = !1), + t + ); + } + finish(t, e, n) { + const i = n ? o : 0; + if (e) { + const t = e; + if ((this.prep(this.minalign, r + s + i), t.length != s)) + throw new TypeError( + "FlatBuffers: file identifier must be length " + s + ); + for (let e = s - 1; e >= 0; e--) this.writeInt8(t.charCodeAt(e)); + } + this.prep(this.minalign, r + i), + this.addOffset(t), + i && this.addInt32(this.bb.capacity() - this.space), + this.bb.setPosition(this.space); + } + finishSizePrefixed(t, e) { + this.finish(t, e, !0); + } + requiredField(t, e) { + const n = this.bb.capacity() - t, + i = n - this.bb.readInt32(n); + if (!(e < this.bb.readInt16(i) && 0 != this.bb.readInt16(i + e))) + throw new TypeError("FlatBuffers: field " + e + " must be set"); + } + startVector(t, e, n) { + this.notNested(), + (this.vector_num_elems = e), + this.prep(r, t * e), + this.prep(n, t * e); + } + endVector() { + return this.writeInt32(this.vector_num_elems), this.offset(); + } + createSharedString(t) { + if (!t) return 0; + if ( + (this.string_maps || (this.string_maps = new Map()), + this.string_maps.has(t)) + ) + return this.string_maps.get(t); + const e = this.createString(t); + return this.string_maps.set(t, e), e; + } + createString(t) { + if (null == t) return 0; + let e; + (e = t instanceof Uint8Array ? t : this.text_encoder.encode(t)), + this.addInt8(0), + this.startVector(1, e.length, 1), + this.bb.setPosition((this.space -= e.length)); + for ( + let t = 0, n = this.space, i = this.bb.bytes(); + t < e.length; + t++ + ) + i[n++] = e[t]; + return this.endVector(); + } + createObjectOffset(t) { + return null === t + ? 0 + : "string" == typeof t + ? this.createString(t) + : t.pack(this); + } + createObjectOffsetList(t) { + const e = []; + for (let n = 0; n < t.length; ++n) { + const i = t[n]; + if (null === i) + throw new TypeError( + "FlatBuffers: Argument for createObjectOffsetList cannot contain null." + ); + e.push(this.createObjectOffset(i)); + } + return e; + } + createStructOffsetList(t, e) { + return ( + e(this, t.length), + this.createObjectOffsetList(t.slice().reverse()), + this.endVector() + ); + } + } + }, + 752: function (t, e, n) { + var i = + (this && this.__createBinding) || + (Object.create + ? function (t, e, n, i) { + void 0 === i && (i = n); + var r = Object.getOwnPropertyDescriptor(e, n); + (r && + !("get" in r + ? !e.__esModule + : r.writable || r.configurable)) || + (r = { + enumerable: !0, + get: function () { + return e[n]; + }, + }), + Object.defineProperty(t, i, r); + } + : function (t, e, n, i) { + void 0 === i && (i = n), (t[i] = e[n]); + }), + r = + (this && this.__setModuleDefault) || + (Object.create + ? function (t, e) { + Object.defineProperty(t, "default", { + enumerable: !0, + value: e, + }); + } + : function (t, e) { + t.default = e; + }), + s = + (this && this.__importStar) || + function (t) { + if (t && t.__esModule) return t; + var e = {}; + if (null != t) + for (var n in t) + "default" !== n && + Object.prototype.hasOwnProperty.call(t, n) && + i(e, t, n); + return r(e, t), e; + }; + Object.defineProperty(e, "__esModule", { value: !0 }); + const o = s(n(251)), + a = s(n(210)), + c = n(960); + new WebSocket("ws://127.0.0.1:55010/pull-stats/").onmessage = function ( + t + ) { + const e = new o.ByteBuffer(new Uint8Array(t.data)), + n = a.PeerChange.getRootAsPeerChange(e); + (0, c.handleChange)(n); + }; + }, + 210: (t, e, n) => { + Object.defineProperty(e, "__esModule", { value: !0 }), + (e.Response = + e.RemovedConnectionT = + e.RemovedConnection = + e.PeerChangeType = + e.PeerChangeT = + e.PeerChange = + e.OkT = + e.Ok = + e.ErrorT = + e.Error = + e.ControllerResponseT = + e.ControllerResponse = + e.AddedConnectionT = + e.AddedConnection = + void 0); + var i = n(54); + Object.defineProperty(e, "AddedConnection", { + enumerable: !0, + get: function () { + return i.AddedConnection; + }, + }), + Object.defineProperty(e, "AddedConnectionT", { + enumerable: !0, + get: function () { + return i.AddedConnectionT; + }, + }); + var r = n(997); + Object.defineProperty(e, "ControllerResponse", { + enumerable: !0, + get: function () { + return r.ControllerResponse; + }, + }), + Object.defineProperty(e, "ControllerResponseT", { + enumerable: !0, + get: function () { + return r.ControllerResponseT; + }, + }); + var s = n(275); + Object.defineProperty(e, "Error", { + enumerable: !0, + get: function () { + return s.Error; + }, + }), + Object.defineProperty(e, "ErrorT", { + enumerable: !0, + get: function () { + return s.ErrorT; + }, + }); + var o = n(798); + Object.defineProperty(e, "Ok", { + enumerable: !0, + get: function () { + return o.Ok; + }, + }), + Object.defineProperty(e, "OkT", { + enumerable: !0, + get: function () { + return o.OkT; + }, + }); + var a = n(950); + Object.defineProperty(e, "PeerChange", { + enumerable: !0, + get: function () { + return a.PeerChange; + }, + }), + Object.defineProperty(e, "PeerChangeT", { + enumerable: !0, + get: function () { + return a.PeerChangeT; + }, + }); + var c = n(934); + Object.defineProperty(e, "PeerChangeType", { + enumerable: !0, + get: function () { + return c.PeerChangeType; + }, + }); + var d = n(727); + Object.defineProperty(e, "RemovedConnection", { + enumerable: !0, + get: function () { + return d.RemovedConnection; + }, + }), + Object.defineProperty(e, "RemovedConnectionT", { + enumerable: !0, + get: function () { + return d.RemovedConnectionT; + }, + }); + var h = n(234); + Object.defineProperty(e, "Response", { + enumerable: !0, + get: function () { + return h.Response; + }, + }); + }, + 54: function (t, e, n) { + var i = + (this && this.__createBinding) || + (Object.create + ? function (t, e, n, i) { + void 0 === i && (i = n); + var r = Object.getOwnPropertyDescriptor(e, n); + (r && + !("get" in r + ? !e.__esModule + : r.writable || r.configurable)) || + (r = { + enumerable: !0, + get: function () { + return e[n]; + }, + }), + Object.defineProperty(t, i, r); + } + : function (t, e, n, i) { + void 0 === i && (i = n), (t[i] = e[n]); + }), + r = + (this && this.__setModuleDefault) || + (Object.create + ? function (t, e) { + Object.defineProperty(t, "default", { + enumerable: !0, + value: e, + }); + } + : function (t, e) { + t.default = e; + }), + s = + (this && this.__importStar) || + function (t) { + if (t && t.__esModule) return t; + var e = {}; + if (null != t) + for (var n in t) + "default" !== n && + Object.prototype.hasOwnProperty.call(t, n) && + i(e, t, n); + return r(e, t), e; + }; + Object.defineProperty(e, "__esModule", { value: !0 }), + (e.AddedConnectionT = e.AddedConnection = void 0); + const o = s(n(251)); + class a { + constructor() { + (this.bb = null), (this.bb_pos = 0); + } + __init(t, e) { + return (this.bb_pos = t), (this.bb = e), this; + } + static getRootAsAddedConnection(t, e) { + return (e || new a()).__init( + t.readInt32(t.position()) + t.position(), + t + ); + } + static getSizePrefixedRootAsAddedConnection(t, e) { + return ( + t.setPosition(t.position() + o.SIZE_PREFIX_LENGTH), + (e || new a()).__init(t.readInt32(t.position()) + t.position(), t) + ); + } + from(t) { + const e = this.bb.__offset(this.bb_pos, 4); + return e ? this.bb.__string(this.bb_pos + e, t) : null; + } + fromLocation() { + const t = this.bb.__offset(this.bb_pos, 6); + return t ? this.bb.readFloat64(this.bb_pos + t) : 0; + } + to(t) { + const e = this.bb.__offset(this.bb_pos, 8); + return e ? this.bb.__string(this.bb_pos + e, t) : null; + } + toLocation() { + const t = this.bb.__offset(this.bb_pos, 10); + return t ? this.bb.readFloat64(this.bb_pos + t) : 0; + } + static startAddedConnection(t) { + t.startObject(4); + } + static addFrom(t, e) { + t.addFieldOffset(0, e, 0); + } + static addFromLocation(t, e) { + t.addFieldFloat64(1, e, 0); + } + static addTo(t, e) { + t.addFieldOffset(2, e, 0); + } + static addToLocation(t, e) { + t.addFieldFloat64(3, e, 0); + } + static endAddedConnection(t) { + const e = t.endObject(); + return t.requiredField(e, 4), t.requiredField(e, 8), e; + } + static createAddedConnection(t, e, n, i, r) { + return ( + a.startAddedConnection(t), + a.addFrom(t, e), + a.addFromLocation(t, n), + a.addTo(t, i), + a.addToLocation(t, r), + a.endAddedConnection(t) + ); + } + unpack() { + return new c( + this.from(), + this.fromLocation(), + this.to(), + this.toLocation() + ); + } + unpackTo(t) { + (t.from = this.from()), + (t.fromLocation = this.fromLocation()), + (t.to = this.to()), + (t.toLocation = this.toLocation()); + } + } + e.AddedConnection = a; + class c { + constructor(t = null, e = 0, n = null, i = 0) { + (this.from = t), + (this.fromLocation = e), + (this.to = n), + (this.toLocation = i); + } + pack(t) { + const e = null !== this.from ? t.createString(this.from) : 0, + n = null !== this.to ? t.createString(this.to) : 0; + return a.createAddedConnection( + t, + e, + this.fromLocation, + n, + this.toLocation + ); + } + } + e.AddedConnectionT = c; + }, + 997: function (t, e, n) { + var i = + (this && this.__createBinding) || + (Object.create + ? function (t, e, n, i) { + void 0 === i && (i = n); + var r = Object.getOwnPropertyDescriptor(e, n); + (r && + !("get" in r + ? !e.__esModule + : r.writable || r.configurable)) || + (r = { + enumerable: !0, + get: function () { + return e[n]; + }, + }), + Object.defineProperty(t, i, r); + } + : function (t, e, n, i) { + void 0 === i && (i = n), (t[i] = e[n]); + }), + r = + (this && this.__setModuleDefault) || + (Object.create + ? function (t, e) { + Object.defineProperty(t, "default", { + enumerable: !0, + value: e, + }); + } + : function (t, e) { + t.default = e; + }), + s = + (this && this.__importStar) || + function (t) { + if (t && t.__esModule) return t; + var e = {}; + if (null != t) + for (var n in t) + "default" !== n && + Object.prototype.hasOwnProperty.call(t, n) && + i(e, t, n); + return r(e, t), e; + }; + Object.defineProperty(e, "__esModule", { value: !0 }), + (e.ControllerResponseT = e.ControllerResponse = void 0); + const o = s(n(251)), + a = n(234); + class c { + constructor() { + (this.bb = null), (this.bb_pos = 0); + } + __init(t, e) { + return (this.bb_pos = t), (this.bb = e), this; + } + static getRootAsControllerResponse(t, e) { + return (e || new c()).__init( + t.readInt32(t.position()) + t.position(), + t + ); + } + static getSizePrefixedRootAsControllerResponse(t, e) { + return ( + t.setPosition(t.position() + o.SIZE_PREFIX_LENGTH), + (e || new c()).__init(t.readInt32(t.position()) + t.position(), t) + ); + } + responseType() { + const t = this.bb.__offset(this.bb_pos, 4); + return t ? this.bb.readUint8(this.bb_pos + t) : a.Response.NONE; + } + response(t) { + const e = this.bb.__offset(this.bb_pos, 6); + return e ? this.bb.__union(t, this.bb_pos + e) : null; + } + static startControllerResponse(t) { + t.startObject(2); + } + static addResponseType(t, e) { + t.addFieldInt8(0, e, a.Response.NONE); + } + static addResponse(t, e) { + t.addFieldOffset(1, e, 0); + } + static endControllerResponse(t) { + const e = t.endObject(); + return t.requiredField(e, 6), e; + } + static createControllerResponse(t, e, n) { + return ( + c.startControllerResponse(t), + c.addResponseType(t, e), + c.addResponse(t, n), + c.endControllerResponse(t) + ); + } + unpack() { + return new d( + this.responseType(), + (() => { + const t = (0, a.unionToResponse)( + this.responseType(), + this.response.bind(this) + ); + return null === t ? null : t.unpack(); + })() + ); + } + unpackTo(t) { + (t.responseType = this.responseType()), + (t.response = (() => { + const t = (0, a.unionToResponse)( + this.responseType(), + this.response.bind(this) + ); + return null === t ? null : t.unpack(); + })()); + } + } + e.ControllerResponse = c; + class d { + constructor(t = a.Response.NONE, e = null) { + (this.responseType = t), (this.response = e); + } + pack(t) { + const e = t.createObjectOffset(this.response); + return c.createControllerResponse(t, this.responseType, e); + } + } + e.ControllerResponseT = d; + }, + 275: function (t, e, n) { + var i = + (this && this.__createBinding) || + (Object.create + ? function (t, e, n, i) { + void 0 === i && (i = n); + var r = Object.getOwnPropertyDescriptor(e, n); + (r && + !("get" in r + ? !e.__esModule + : r.writable || r.configurable)) || + (r = { + enumerable: !0, + get: function () { + return e[n]; + }, + }), + Object.defineProperty(t, i, r); + } + : function (t, e, n, i) { + void 0 === i && (i = n), (t[i] = e[n]); + }), + r = + (this && this.__setModuleDefault) || + (Object.create + ? function (t, e) { + Object.defineProperty(t, "default", { + enumerable: !0, + value: e, + }); + } + : function (t, e) { + t.default = e; + }), + s = + (this && this.__importStar) || + function (t) { + if (t && t.__esModule) return t; + var e = {}; + if (null != t) + for (var n in t) + "default" !== n && + Object.prototype.hasOwnProperty.call(t, n) && + i(e, t, n); + return r(e, t), e; + }; + Object.defineProperty(e, "__esModule", { value: !0 }), + (e.ErrorT = e.Error = void 0); + const o = s(n(251)); + class a { + constructor() { + (this.bb = null), (this.bb_pos = 0); + } + __init(t, e) { + return (this.bb_pos = t), (this.bb = e), this; + } + static getRootAsError(t, e) { + return (e || new a()).__init( + t.readInt32(t.position()) + t.position(), + t + ); + } + static getSizePrefixedRootAsError(t, e) { + return ( + t.setPosition(t.position() + o.SIZE_PREFIX_LENGTH), + (e || new a()).__init(t.readInt32(t.position()) + t.position(), t) + ); + } + message(t) { + const e = this.bb.__offset(this.bb_pos, 4); + return e ? this.bb.__string(this.bb_pos + e, t) : null; + } + static startError(t) { + t.startObject(1); + } + static addMessage(t, e) { + t.addFieldOffset(0, e, 0); + } + static endError(t) { + const e = t.endObject(); + return t.requiredField(e, 4), e; + } + static createError(t, e) { + return a.startError(t), a.addMessage(t, e), a.endError(t); + } + unpack() { + return new c(this.message()); + } + unpackTo(t) { + t.message = this.message(); + } + } + e.Error = a; + class c { + constructor(t = null) { + this.message = t; + } + pack(t) { + const e = null !== this.message ? t.createString(this.message) : 0; + return a.createError(t, e); + } + } + e.ErrorT = c; + }, + 798: function (t, e, n) { + var i = + (this && this.__createBinding) || + (Object.create + ? function (t, e, n, i) { + void 0 === i && (i = n); + var r = Object.getOwnPropertyDescriptor(e, n); + (r && + !("get" in r + ? !e.__esModule + : r.writable || r.configurable)) || + (r = { + enumerable: !0, + get: function () { + return e[n]; + }, + }), + Object.defineProperty(t, i, r); + } + : function (t, e, n, i) { + void 0 === i && (i = n), (t[i] = e[n]); + }), + r = + (this && this.__setModuleDefault) || + (Object.create + ? function (t, e) { + Object.defineProperty(t, "default", { + enumerable: !0, + value: e, + }); + } + : function (t, e) { + t.default = e; + }), + s = + (this && this.__importStar) || + function (t) { + if (t && t.__esModule) return t; + var e = {}; + if (null != t) + for (var n in t) + "default" !== n && + Object.prototype.hasOwnProperty.call(t, n) && + i(e, t, n); + return r(e, t), e; + }; + Object.defineProperty(e, "__esModule", { value: !0 }), + (e.OkT = e.Ok = void 0); + const o = s(n(251)); + class a { + constructor() { + (this.bb = null), (this.bb_pos = 0); + } + __init(t, e) { + return (this.bb_pos = t), (this.bb = e), this; + } + static getRootAsOk(t, e) { + return (e || new a()).__init( + t.readInt32(t.position()) + t.position(), + t + ); + } + static getSizePrefixedRootAsOk(t, e) { + return ( + t.setPosition(t.position() + o.SIZE_PREFIX_LENGTH), + (e || new a()).__init(t.readInt32(t.position()) + t.position(), t) + ); + } + message(t) { + const e = this.bb.__offset(this.bb_pos, 4); + return e ? this.bb.__string(this.bb_pos + e, t) : null; + } + static startOk(t) { + t.startObject(1); + } + static addMessage(t, e) { + t.addFieldOffset(0, e, 0); + } + static endOk(t) { + return t.endObject(); + } + static createOk(t, e) { + return a.startOk(t), a.addMessage(t, e), a.endOk(t); + } + unpack() { + return new c(this.message()); + } + unpackTo(t) { + t.message = this.message(); + } + } + e.Ok = a; + class c { + constructor(t = null) { + this.message = t; + } + pack(t) { + const e = null !== this.message ? t.createString(this.message) : 0; + return a.createOk(t, e); + } + } + e.OkT = c; + }, + 934: (t, e, n) => { + Object.defineProperty(e, "__esModule", { value: !0 }), + (e.unionListToPeerChangeType = + e.unionToPeerChangeType = + e.PeerChangeType = + void 0); + const i = n(54), + r = n(275), + s = n(727); + var o; + !(function (t) { + (t[(t.NONE = 0)] = "NONE"), + (t[(t.AddedConnection = 1)] = "AddedConnection"), + (t[(t.RemovedConnection = 2)] = "RemovedConnection"), + (t[(t.Error = 3)] = "Error"); + })(o || (e.PeerChangeType = o = {})), + (e.unionToPeerChangeType = function (t, e) { + switch (o[t]) { + case "NONE": + default: + return null; + case "AddedConnection": + return e(new i.AddedConnection()); + case "RemovedConnection": + return e(new s.RemovedConnection()); + case "Error": + return e(new r.Error()); + } + }), + (e.unionListToPeerChangeType = function (t, e, n) { + switch (o[t]) { + case "NONE": + default: + return null; + case "AddedConnection": + return e(n, new i.AddedConnection()); + case "RemovedConnection": + return e(n, new s.RemovedConnection()); + case "Error": + return e(n, new r.Error()); + } + }); + }, + 950: function (t, e, n) { + var i = + (this && this.__createBinding) || + (Object.create + ? function (t, e, n, i) { + void 0 === i && (i = n); + var r = Object.getOwnPropertyDescriptor(e, n); + (r && + !("get" in r + ? !e.__esModule + : r.writable || r.configurable)) || + (r = { + enumerable: !0, + get: function () { + return e[n]; + }, + }), + Object.defineProperty(t, i, r); + } + : function (t, e, n, i) { + void 0 === i && (i = n), (t[i] = e[n]); + }), + r = + (this && this.__setModuleDefault) || + (Object.create + ? function (t, e) { + Object.defineProperty(t, "default", { + enumerable: !0, + value: e, + }); + } + : function (t, e) { + t.default = e; + }), + s = + (this && this.__importStar) || + function (t) { + if (t && t.__esModule) return t; + var e = {}; + if (null != t) + for (var n in t) + "default" !== n && + Object.prototype.hasOwnProperty.call(t, n) && + i(e, t, n); + return r(e, t), e; + }; + Object.defineProperty(e, "__esModule", { value: !0 }), + (e.PeerChangeT = e.PeerChange = void 0); + const o = s(n(251)), + a = n(54), + c = n(934); + class d { + constructor() { + (this.bb = null), (this.bb_pos = 0); + } + __init(t, e) { + return (this.bb_pos = t), (this.bb = e), this; + } + static getRootAsPeerChange(t, e) { + return (e || new d()).__init( + t.readInt32(t.position()) + t.position(), + t + ); + } + static getSizePrefixedRootAsPeerChange(t, e) { + return ( + t.setPosition(t.position() + o.SIZE_PREFIX_LENGTH), + (e || new d()).__init(t.readInt32(t.position()) + t.position(), t) + ); + } + currentState(t, e) { + const n = this.bb.__offset(this.bb_pos, 4); + return n + ? (e || new a.AddedConnection()).__init( + this.bb.__indirect(this.bb.__vector(this.bb_pos + n) + 4 * t), + this.bb + ) + : null; + } + currentStateLength() { + const t = this.bb.__offset(this.bb_pos, 4); + return t ? this.bb.__vector_len(this.bb_pos + t) : 0; + } + changeType() { + const t = this.bb.__offset(this.bb_pos, 6); + return t + ? this.bb.readUint8(this.bb_pos + t) + : c.PeerChangeType.NONE; + } + change(t) { + const e = this.bb.__offset(this.bb_pos, 8); + return e ? this.bb.__union(t, this.bb_pos + e) : null; + } + static startPeerChange(t) { + t.startObject(3); + } + static addCurrentState(t, e) { + t.addFieldOffset(0, e, 0); + } + static createCurrentStateVector(t, e) { + t.startVector(4, e.length, 4); + for (let n = e.length - 1; n >= 0; n--) t.addOffset(e[n]); + return t.endVector(); + } + static startCurrentStateVector(t, e) { + t.startVector(4, e, 4); + } + static addChangeType(t, e) { + t.addFieldInt8(1, e, c.PeerChangeType.NONE); + } + static addChange(t, e) { + t.addFieldOffset(2, e, 0); + } + static endPeerChange(t) { + return t.endObject(); + } + static createPeerChange(t, e, n, i) { + return ( + d.startPeerChange(t), + d.addCurrentState(t, e), + d.addChangeType(t, n), + d.addChange(t, i), + d.endPeerChange(t) + ); + } + unpack() { + return new h( + this.bb.createObjList( + this.currentState.bind(this), + this.currentStateLength() + ), + this.changeType(), + (() => { + const t = (0, c.unionToPeerChangeType)( + this.changeType(), + this.change.bind(this) + ); + return null === t ? null : t.unpack(); + })() + ); + } + unpackTo(t) { + (t.currentState = this.bb.createObjList( + this.currentState.bind(this), + this.currentStateLength() + )), + (t.changeType = this.changeType()), + (t.change = (() => { + const t = (0, c.unionToPeerChangeType)( + this.changeType(), + this.change.bind(this) + ); + return null === t ? null : t.unpack(); + })()); + } + } + e.PeerChange = d; + class h { + constructor(t = [], e = c.PeerChangeType.NONE, n = null) { + (this.currentState = t), (this.changeType = e), (this.change = n); + } + pack(t) { + const e = d.createCurrentStateVector( + t, + t.createObjectOffsetList(this.currentState) + ), + n = t.createObjectOffset(this.change); + return d.createPeerChange(t, e, this.changeType, n); + } + } + e.PeerChangeT = h; + }, + 727: function (t, e, n) { + var i = + (this && this.__createBinding) || + (Object.create + ? function (t, e, n, i) { + void 0 === i && (i = n); + var r = Object.getOwnPropertyDescriptor(e, n); + (r && + !("get" in r + ? !e.__esModule + : r.writable || r.configurable)) || + (r = { + enumerable: !0, + get: function () { + return e[n]; + }, + }), + Object.defineProperty(t, i, r); + } + : function (t, e, n, i) { + void 0 === i && (i = n), (t[i] = e[n]); + }), + r = + (this && this.__setModuleDefault) || + (Object.create + ? function (t, e) { + Object.defineProperty(t, "default", { + enumerable: !0, + value: e, + }); + } + : function (t, e) { + t.default = e; + }), + s = + (this && this.__importStar) || + function (t) { + if (t && t.__esModule) return t; + var e = {}; + if (null != t) + for (var n in t) + "default" !== n && + Object.prototype.hasOwnProperty.call(t, n) && + i(e, t, n); + return r(e, t), e; + }; + Object.defineProperty(e, "__esModule", { value: !0 }), + (e.RemovedConnectionT = e.RemovedConnection = void 0); + const o = s(n(251)); + class a { + constructor() { + (this.bb = null), (this.bb_pos = 0); + } + __init(t, e) { + return (this.bb_pos = t), (this.bb = e), this; + } + static getRootAsRemovedConnection(t, e) { + return (e || new a()).__init( + t.readInt32(t.position()) + t.position(), + t + ); + } + static getSizePrefixedRootAsRemovedConnection(t, e) { + return ( + t.setPosition(t.position() + o.SIZE_PREFIX_LENGTH), + (e || new a()).__init(t.readInt32(t.position()) + t.position(), t) + ); + } + at(t) { + const e = this.bb.__offset(this.bb_pos, 4); + return e ? this.bb.__string(this.bb_pos + e, t) : null; + } + from(t) { + const e = this.bb.__offset(this.bb_pos, 6); + return e ? this.bb.__string(this.bb_pos + e, t) : null; + } + static startRemovedConnection(t) { + t.startObject(2); + } + static addAt(t, e) { + t.addFieldOffset(0, e, 0); + } + static addFrom(t, e) { + t.addFieldOffset(1, e, 0); + } + static endRemovedConnection(t) { + const e = t.endObject(); + return t.requiredField(e, 4), t.requiredField(e, 6), e; + } + static createRemovedConnection(t, e, n) { + return ( + a.startRemovedConnection(t), + a.addAt(t, e), + a.addFrom(t, n), + a.endRemovedConnection(t) + ); + } + unpack() { + return new c(this.at(), this.from()); + } + unpackTo(t) { + (t.at = this.at()), (t.from = this.from()); + } + } + e.RemovedConnection = a; + class c { + constructor(t = null, e = null) { + (this.at = t), (this.from = e); + } + pack(t) { + const e = null !== this.at ? t.createString(this.at) : 0, + n = null !== this.from ? t.createString(this.from) : 0; + return a.createRemovedConnection(t, e, n); + } + } + e.RemovedConnectionT = c; + }, + 234: (t, e, n) => { + Object.defineProperty(e, "__esModule", { value: !0 }), + (e.unionListToResponse = e.unionToResponse = e.Response = void 0); + const i = n(275), + r = n(798); + var s; + !(function (t) { + (t[(t.NONE = 0)] = "NONE"), + (t[(t.Error = 1)] = "Error"), + (t[(t.Ok = 2)] = "Ok"); + })(s || (e.Response = s = {})), + (e.unionToResponse = function (t, e) { + switch (s[t]) { + case "NONE": + default: + return null; + case "Error": + return e(new i.Error()); + case "Ok": + return e(new r.Ok()); + } + }), + (e.unionListToResponse = function (t, e, n) { + switch (s[t]) { + case "NONE": + default: + return null; + case "Error": + return e(n, new i.Error()); + case "Ok": + return e(n, new r.Ok()); + } + }); + }, + 960: function (t, e, n) { + var i = + (this && this.__createBinding) || + (Object.create + ? function (t, e, n, i) { + void 0 === i && (i = n); + var r = Object.getOwnPropertyDescriptor(e, n); + (r && + !("get" in r + ? !e.__esModule + : r.writable || r.configurable)) || + (r = { + enumerable: !0, + get: function () { + return e[n]; + }, + }), + Object.defineProperty(t, i, r); + } + : function (t, e, n, i) { + void 0 === i && (i = n), (t[i] = e[n]); + }), + r = + (this && this.__setModuleDefault) || + (Object.create + ? function (t, e) { + Object.defineProperty(t, "default", { + enumerable: !0, + value: e, + }); + } + : function (t, e) { + t.default = e; + }), + s = + (this && this.__importStar) || + function (t) { + if (t && t.__esModule) return t; + var e = {}; + if (null != t) + for (var n in t) + "default" !== n && + Object.prototype.hasOwnProperty.call(t, n) && + i(e, t, n); + return r(e, t), e; + }; + Object.defineProperty(e, "__esModule", { value: !0 }), + (e.showConnections = + e.handleRemovedConnection = + e.handleAddedConnection = + e.handleChange = + e.peers = + void 0); + const o = s(n(210)); + function a(t) { + const n = t, + i = n.from.toString(), + r = n.to.toString(), + s = { id: i, location: n.fromLocation }, + o = { id: r, location: n.toLocation }; + e.peers[i] + ? (e.peers[i].currentLocation !== n.fromLocation && + (e.peers[i].locationHistory.push({ + location: e.peers[i].currentLocation, + timestamp: Date.now(), + }), + (e.peers[i].currentLocation = n.fromLocation)), + e.peers[r].currentLocation !== n.toLocation && + (e.peers[r].locationHistory.push({ + location: e.peers[r].currentLocation, + timestamp: Date.now(), + }), + (e.peers[r].currentLocation = n.toLocation)), + e.peers[i].connections.push(o)) + : (e.peers[i] = { + id: i, + currentLocation: n.fromLocation, + connections: [o], + history: [], + locationHistory: [], + }), + e.peers[r] + ? e.peers[r].connections.push(s) + : (e.peers[r] = { + id: r, + currentLocation: n.toLocation, + connections: [s], + history: [], + locationHistory: [], + }); + const a = { type: "Added", from: s, to: o, timestamp: Date.now() }; + e.peers[i].history.push(a), e.peers[r].history.push(a); + } + function c(t) { + const n = t, + i = n.from.toString(), + r = n.at.toString(), + s = e.peers[i].connections.findIndex((t) => t.id === r); + s > -1 && e.peers[i].connections.splice(s, 1); + const o = e.peers[r].connections.findIndex((t) => t.id === i); + o > -1 && e.peers[r].connections.splice(o, 1); + const a = { + type: "Removed", + from: { id: i, location: e.peers[i].currentLocation }, + to: { id: r, location: e.peers[r].currentLocation }, + timestamp: Date.now(), + }; + e.peers[i].history.push(a), e.peers[r].history.push(a); + } + function d(t) { + var e, n; + const i = t.id, + r = null !== (e = t.connections) && void 0 !== e ? e : [], + s = document.getElementById("overlay-div"); + if (s) { + s.innerHTML = ""; + const e = document.createElement("section"); + e.classList.add("header-section"); + const o = document.createElement("h2"); + (o.textContent = `Peer ID: ${i}, Location: ${ + null !== (n = t.currentLocation) && void 0 !== n ? n : "" + }`), + e.appendChild(o), + s.appendChild(e); + const a = document.createElement("section"); + a.classList.add("table-section"); + const c = document.createElement("table"); + c.classList.add("peers-table"); + const d = document.createElement("tr"), + h = document.createElement("th"); + h.textContent = "Peer ID"; + const u = document.createElement("th"); + (u.textContent = "Location"), + d.appendChild(h), + d.appendChild(u), + c.appendChild(d), + r.forEach((t) => { + var e, n; + const i = document.createElement("tr"), + r = document.createElement("td"); + r.textContent = + null !== (e = t.id.toString()) && void 0 !== e ? e : ""; + const s = document.createElement("td"); + (s.textContent = + null !== (n = t.location.toString()) && void 0 !== n + ? n + : ""), + i.appendChild(r), + i.appendChild(s), + c.appendChild(i); + }), + a.appendChild(c), + s.appendChild(a); + } + } + function h(t) { + let e = ''; + return ( + (e += + ""), + (e += t.history + .map( + (t) => + `` + ) + .join("")), + (e += "
TimestampTypeFromTo
${t.timestamp}${t.type}${t.from.id}${t.to.id}
"), + e + ); + } + (e.peers = {}), + (e.handleChange = function (t) { + const n = t.unpack(); + try { + switch (n.changeType) { + case o.PeerChangeType.AddedConnection: + a(n.change); + break; + case o.PeerChangeType.RemovedConnection: + c(n.change); + } + } catch (t) { + console.error(t); + } + !(function () { + const t = document.getElementById("peers-table"); + if (t) { + t.innerHTML = ""; + for (const n in e.peers) { + const i = document.createElement("tr"); + (i.onclick = () => d(e.peers[n])), + (i.onclick = () => h(e.peers[n])); + const r = document.createElement("td"); + (r.textContent = n), i.appendChild(r), t.appendChild(i); + } + } + })(); + }), + (e.handleAddedConnection = a), + (e.handleRemovedConnection = c), + (e.showConnections = d); + }, + }, + e = {}; + function n(i) { + var r = e[i]; + if (void 0 !== r) return r.exports; + var s = (e[i] = { exports: {} }); + return t[i].call(s.exports, s, s.exports, n), s.exports; + } + (n.d = (t, e) => { + for (var i in e) + n.o(e, i) && + !n.o(t, i) && + Object.defineProperty(t, i, { enumerable: !0, get: e[i] }); + }), + (n.o = (t, e) => Object.prototype.hasOwnProperty.call(t, e)), + (n.r = (t) => { + "undefined" != typeof Symbol && + Symbol.toStringTag && + Object.defineProperty(t, Symbol.toStringTag, { value: "Module" }), + Object.defineProperty(t, "__esModule", { value: !0 }); + }), + n(752); +})(); +//# sourceMappingURL=bundle.js.map diff --git a/network-monitor/dist/bundle.js.map b/network-monitor/dist/bundle.js.map new file mode 100644 index 000000000..a16902658 --- /dev/null +++ b/network-monitor/dist/bundle.js.map @@ -0,0 +1 @@ +{"version":3,"file":"bundle.js","mappings":"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","sources":["webpack://network-monitor/./node_modules/flatbuffers/mjs/constants.js","webpack://network-monitor/./node_modules/flatbuffers/mjs/utils.js","webpack://network-monitor/./node_modules/flatbuffers/mjs/encoding.js","webpack://network-monitor/./node_modules/flatbuffers/mjs/byte-buffer.js","webpack://network-monitor/./node_modules/flatbuffers/mjs/builder.js","webpack://network-monitor/./src/app.ts","webpack://network-monitor/./src/generated/topology.ts","webpack://network-monitor/./src/generated/topology/added-connection.ts","webpack://network-monitor/./src/generated/topology/controller-response.ts","webpack://network-monitor/./src/generated/topology/error.ts","webpack://network-monitor/./src/generated/topology/ok.ts","webpack://network-monitor/./src/generated/topology/peer-change-type.ts","webpack://network-monitor/./src/generated/topology/peer-change.ts","webpack://network-monitor/./src/generated/topology/removed-connection.ts","webpack://network-monitor/./src/generated/topology/response.ts","webpack://network-monitor/./src/topology.ts","webpack://network-monitor/webpack/bootstrap","webpack://network-monitor/webpack/runtime/define property getters","webpack://network-monitor/webpack/runtime/hasOwnProperty shorthand","webpack://network-monitor/webpack/runtime/make namespace object","webpack://network-monitor/webpack/startup"],"sourcesContent":["export const SIZEOF_SHORT = 2;\nexport const SIZEOF_INT = 4;\nexport const FILE_IDENTIFIER_LENGTH = 4;\nexport const SIZE_PREFIX_LENGTH = 4;\n","export const int32 = new Int32Array(2);\nexport const float32 = new Float32Array(int32.buffer);\nexport const float64 = new Float64Array(int32.buffer);\nexport const isLittleEndian = new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1;\n","export var Encoding;\n(function (Encoding) {\n Encoding[Encoding[\"UTF8_BYTES\"] = 1] = \"UTF8_BYTES\";\n Encoding[Encoding[\"UTF16_STRING\"] = 2] = \"UTF16_STRING\";\n})(Encoding || (Encoding = {}));\n","import { FILE_IDENTIFIER_LENGTH, SIZEOF_INT } from \"./constants.js\";\nimport { int32, isLittleEndian, float32, float64 } from \"./utils.js\";\nimport { Encoding } from \"./encoding.js\";\nexport class ByteBuffer {\n /**\n * Create a new ByteBuffer with a given array of bytes (`Uint8Array`)\n */\n constructor(bytes_) {\n this.bytes_ = bytes_;\n this.position_ = 0;\n this.text_decoder_ = new TextDecoder();\n }\n /**\n * Create and allocate a new ByteBuffer with a given size.\n */\n static allocate(byte_size) {\n return new ByteBuffer(new Uint8Array(byte_size));\n }\n clear() {\n this.position_ = 0;\n }\n /**\n * Get the underlying `Uint8Array`.\n */\n bytes() {\n return this.bytes_;\n }\n /**\n * Get the buffer's position.\n */\n position() {\n return this.position_;\n }\n /**\n * Set the buffer's position.\n */\n setPosition(position) {\n this.position_ = position;\n }\n /**\n * Get the buffer's capacity.\n */\n capacity() {\n return this.bytes_.length;\n }\n readInt8(offset) {\n return this.readUint8(offset) << 24 >> 24;\n }\n readUint8(offset) {\n return this.bytes_[offset];\n }\n readInt16(offset) {\n return this.readUint16(offset) << 16 >> 16;\n }\n readUint16(offset) {\n return this.bytes_[offset] | this.bytes_[offset + 1] << 8;\n }\n readInt32(offset) {\n return this.bytes_[offset] | this.bytes_[offset + 1] << 8 | this.bytes_[offset + 2] << 16 | this.bytes_[offset + 3] << 24;\n }\n readUint32(offset) {\n return this.readInt32(offset) >>> 0;\n }\n readInt64(offset) {\n return BigInt.asIntN(64, BigInt(this.readUint32(offset)) + (BigInt(this.readUint32(offset + 4)) << BigInt(32)));\n }\n readUint64(offset) {\n return BigInt.asUintN(64, BigInt(this.readUint32(offset)) + (BigInt(this.readUint32(offset + 4)) << BigInt(32)));\n }\n readFloat32(offset) {\n int32[0] = this.readInt32(offset);\n return float32[0];\n }\n readFloat64(offset) {\n int32[isLittleEndian ? 0 : 1] = this.readInt32(offset);\n int32[isLittleEndian ? 1 : 0] = this.readInt32(offset + 4);\n return float64[0];\n }\n writeInt8(offset, value) {\n this.bytes_[offset] = value;\n }\n writeUint8(offset, value) {\n this.bytes_[offset] = value;\n }\n writeInt16(offset, value) {\n this.bytes_[offset] = value;\n this.bytes_[offset + 1] = value >> 8;\n }\n writeUint16(offset, value) {\n this.bytes_[offset] = value;\n this.bytes_[offset + 1] = value >> 8;\n }\n writeInt32(offset, value) {\n this.bytes_[offset] = value;\n this.bytes_[offset + 1] = value >> 8;\n this.bytes_[offset + 2] = value >> 16;\n this.bytes_[offset + 3] = value >> 24;\n }\n writeUint32(offset, value) {\n this.bytes_[offset] = value;\n this.bytes_[offset + 1] = value >> 8;\n this.bytes_[offset + 2] = value >> 16;\n this.bytes_[offset + 3] = value >> 24;\n }\n writeInt64(offset, value) {\n this.writeInt32(offset, Number(BigInt.asIntN(32, value)));\n this.writeInt32(offset + 4, Number(BigInt.asIntN(32, value >> BigInt(32))));\n }\n writeUint64(offset, value) {\n this.writeUint32(offset, Number(BigInt.asUintN(32, value)));\n this.writeUint32(offset + 4, Number(BigInt.asUintN(32, value >> BigInt(32))));\n }\n writeFloat32(offset, value) {\n float32[0] = value;\n this.writeInt32(offset, int32[0]);\n }\n writeFloat64(offset, value) {\n float64[0] = value;\n this.writeInt32(offset, int32[isLittleEndian ? 0 : 1]);\n this.writeInt32(offset + 4, int32[isLittleEndian ? 1 : 0]);\n }\n /**\n * Return the file identifier. Behavior is undefined for FlatBuffers whose\n * schema does not include a file_identifier (likely points at padding or the\n * start of a the root vtable).\n */\n getBufferIdentifier() {\n if (this.bytes_.length < this.position_ + SIZEOF_INT +\n FILE_IDENTIFIER_LENGTH) {\n throw new Error('FlatBuffers: ByteBuffer is too short to contain an identifier.');\n }\n let result = \"\";\n for (let i = 0; i < FILE_IDENTIFIER_LENGTH; i++) {\n result += String.fromCharCode(this.readInt8(this.position_ + SIZEOF_INT + i));\n }\n return result;\n }\n /**\n * Look up a field in the vtable, return an offset into the object, or 0 if the\n * field is not present.\n */\n __offset(bb_pos, vtable_offset) {\n const vtable = bb_pos - this.readInt32(bb_pos);\n return vtable_offset < this.readInt16(vtable) ? this.readInt16(vtable + vtable_offset) : 0;\n }\n /**\n * Initialize any Table-derived type to point to the union at the given offset.\n */\n __union(t, offset) {\n t.bb_pos = offset + this.readInt32(offset);\n t.bb = this;\n return t;\n }\n /**\n * Create a JavaScript string from UTF-8 data stored inside the FlatBuffer.\n * This allocates a new string and converts to wide chars upon each access.\n *\n * To avoid the conversion to string, pass Encoding.UTF8_BYTES as the\n * \"optionalEncoding\" argument. This is useful for avoiding conversion when\n * the data will just be packaged back up in another FlatBuffer later on.\n *\n * @param offset\n * @param opt_encoding Defaults to UTF16_STRING\n */\n __string(offset, opt_encoding) {\n offset += this.readInt32(offset);\n const length = this.readInt32(offset);\n offset += SIZEOF_INT;\n const utf8bytes = this.bytes_.subarray(offset, offset + length);\n if (opt_encoding === Encoding.UTF8_BYTES)\n return utf8bytes;\n else\n return this.text_decoder_.decode(utf8bytes);\n }\n /**\n * Handle unions that can contain string as its member, if a Table-derived type then initialize it,\n * if a string then return a new one\n *\n * WARNING: strings are immutable in JS so we can't change the string that the user gave us, this\n * makes the behaviour of __union_with_string different compared to __union\n */\n __union_with_string(o, offset) {\n if (typeof o === 'string') {\n return this.__string(offset);\n }\n return this.__union(o, offset);\n }\n /**\n * Retrieve the relative offset stored at \"offset\"\n */\n __indirect(offset) {\n return offset + this.readInt32(offset);\n }\n /**\n * Get the start of data of a vector whose offset is stored at \"offset\" in this object.\n */\n __vector(offset) {\n return offset + this.readInt32(offset) + SIZEOF_INT; // data starts after the length\n }\n /**\n * Get the length of a vector whose offset is stored at \"offset\" in this object.\n */\n __vector_len(offset) {\n return this.readInt32(offset + this.readInt32(offset));\n }\n __has_identifier(ident) {\n if (ident.length != FILE_IDENTIFIER_LENGTH) {\n throw new Error('FlatBuffers: file identifier must be length ' +\n FILE_IDENTIFIER_LENGTH);\n }\n for (let i = 0; i < FILE_IDENTIFIER_LENGTH; i++) {\n if (ident.charCodeAt(i) != this.readInt8(this.position() + SIZEOF_INT + i)) {\n return false;\n }\n }\n return true;\n }\n /**\n * A helper function for generating list for obj api\n */\n createScalarList(listAccessor, listLength) {\n const ret = [];\n for (let i = 0; i < listLength; ++i) {\n const val = listAccessor(i);\n if (val !== null) {\n ret.push(val);\n }\n }\n return ret;\n }\n /**\n * A helper function for generating list for obj api\n * @param listAccessor function that accepts an index and return data at that index\n * @param listLength listLength\n * @param res result list\n */\n createObjList(listAccessor, listLength) {\n const ret = [];\n for (let i = 0; i < listLength; ++i) {\n const val = listAccessor(i);\n if (val !== null) {\n ret.push(val.unpack());\n }\n }\n return ret;\n }\n}\n","import { ByteBuffer } from \"./byte-buffer.js\";\nimport { SIZEOF_SHORT, SIZE_PREFIX_LENGTH, SIZEOF_INT, FILE_IDENTIFIER_LENGTH } from \"./constants.js\";\nexport class Builder {\n /**\n * Create a FlatBufferBuilder.\n */\n constructor(opt_initial_size) {\n /** Minimum alignment encountered so far. */\n this.minalign = 1;\n /** The vtable for the current table. */\n this.vtable = null;\n /** The amount of fields we're actually using. */\n this.vtable_in_use = 0;\n /** Whether we are currently serializing a table. */\n this.isNested = false;\n /** Starting offset of the current struct/table. */\n this.object_start = 0;\n /** List of offsets of all vtables. */\n this.vtables = [];\n /** For the current vector being built. */\n this.vector_num_elems = 0;\n /** False omits default values from the serialized data */\n this.force_defaults = false;\n this.string_maps = null;\n this.text_encoder = new TextEncoder();\n let initial_size;\n if (!opt_initial_size) {\n initial_size = 1024;\n }\n else {\n initial_size = opt_initial_size;\n }\n /**\n * @type {ByteBuffer}\n * @private\n */\n this.bb = ByteBuffer.allocate(initial_size);\n this.space = initial_size;\n }\n clear() {\n this.bb.clear();\n this.space = this.bb.capacity();\n this.minalign = 1;\n this.vtable = null;\n this.vtable_in_use = 0;\n this.isNested = false;\n this.object_start = 0;\n this.vtables = [];\n this.vector_num_elems = 0;\n this.force_defaults = false;\n this.string_maps = null;\n }\n /**\n * In order to save space, fields that are set to their default value\n * don't get serialized into the buffer. Forcing defaults provides a\n * way to manually disable this optimization.\n *\n * @param forceDefaults true always serializes default values\n */\n forceDefaults(forceDefaults) {\n this.force_defaults = forceDefaults;\n }\n /**\n * Get the ByteBuffer representing the FlatBuffer. Only call this after you've\n * called finish(). The actual data starts at the ByteBuffer's current position,\n * not necessarily at 0.\n */\n dataBuffer() {\n return this.bb;\n }\n /**\n * Get the bytes representing the FlatBuffer. Only call this after you've\n * called finish().\n */\n asUint8Array() {\n return this.bb.bytes().subarray(this.bb.position(), this.bb.position() + this.offset());\n }\n /**\n * Prepare to write an element of `size` after `additional_bytes` have been\n * written, e.g. if you write a string, you need to align such the int length\n * field is aligned to 4 bytes, and the string data follows it directly. If all\n * you need to do is alignment, `additional_bytes` will be 0.\n *\n * @param size This is the of the new element to write\n * @param additional_bytes The padding size\n */\n prep(size, additional_bytes) {\n // Track the biggest thing we've ever aligned to.\n if (size > this.minalign) {\n this.minalign = size;\n }\n // Find the amount of alignment needed such that `size` is properly\n // aligned after `additional_bytes`\n const align_size = ((~(this.bb.capacity() - this.space + additional_bytes)) + 1) & (size - 1);\n // Reallocate the buffer if needed.\n while (this.space < align_size + size + additional_bytes) {\n const old_buf_size = this.bb.capacity();\n this.bb = Builder.growByteBuffer(this.bb);\n this.space += this.bb.capacity() - old_buf_size;\n }\n this.pad(align_size);\n }\n pad(byte_size) {\n for (let i = 0; i < byte_size; i++) {\n this.bb.writeInt8(--this.space, 0);\n }\n }\n writeInt8(value) {\n this.bb.writeInt8(this.space -= 1, value);\n }\n writeInt16(value) {\n this.bb.writeInt16(this.space -= 2, value);\n }\n writeInt32(value) {\n this.bb.writeInt32(this.space -= 4, value);\n }\n writeInt64(value) {\n this.bb.writeInt64(this.space -= 8, value);\n }\n writeFloat32(value) {\n this.bb.writeFloat32(this.space -= 4, value);\n }\n writeFloat64(value) {\n this.bb.writeFloat64(this.space -= 8, value);\n }\n /**\n * Add an `int8` to the buffer, properly aligned, and grows the buffer (if necessary).\n * @param value The `int8` to add the buffer.\n */\n addInt8(value) {\n this.prep(1, 0);\n this.writeInt8(value);\n }\n /**\n * Add an `int16` to the buffer, properly aligned, and grows the buffer (if necessary).\n * @param value The `int16` to add the buffer.\n */\n addInt16(value) {\n this.prep(2, 0);\n this.writeInt16(value);\n }\n /**\n * Add an `int32` to the buffer, properly aligned, and grows the buffer (if necessary).\n * @param value The `int32` to add the buffer.\n */\n addInt32(value) {\n this.prep(4, 0);\n this.writeInt32(value);\n }\n /**\n * Add an `int64` to the buffer, properly aligned, and grows the buffer (if necessary).\n * @param value The `int64` to add the buffer.\n */\n addInt64(value) {\n this.prep(8, 0);\n this.writeInt64(value);\n }\n /**\n * Add a `float32` to the buffer, properly aligned, and grows the buffer (if necessary).\n * @param value The `float32` to add the buffer.\n */\n addFloat32(value) {\n this.prep(4, 0);\n this.writeFloat32(value);\n }\n /**\n * Add a `float64` to the buffer, properly aligned, and grows the buffer (if necessary).\n * @param value The `float64` to add the buffer.\n */\n addFloat64(value) {\n this.prep(8, 0);\n this.writeFloat64(value);\n }\n addFieldInt8(voffset, value, defaultValue) {\n if (this.force_defaults || value != defaultValue) {\n this.addInt8(value);\n this.slot(voffset);\n }\n }\n addFieldInt16(voffset, value, defaultValue) {\n if (this.force_defaults || value != defaultValue) {\n this.addInt16(value);\n this.slot(voffset);\n }\n }\n addFieldInt32(voffset, value, defaultValue) {\n if (this.force_defaults || value != defaultValue) {\n this.addInt32(value);\n this.slot(voffset);\n }\n }\n addFieldInt64(voffset, value, defaultValue) {\n if (this.force_defaults || value !== defaultValue) {\n this.addInt64(value);\n this.slot(voffset);\n }\n }\n addFieldFloat32(voffset, value, defaultValue) {\n if (this.force_defaults || value != defaultValue) {\n this.addFloat32(value);\n this.slot(voffset);\n }\n }\n addFieldFloat64(voffset, value, defaultValue) {\n if (this.force_defaults || value != defaultValue) {\n this.addFloat64(value);\n this.slot(voffset);\n }\n }\n addFieldOffset(voffset, value, defaultValue) {\n if (this.force_defaults || value != defaultValue) {\n this.addOffset(value);\n this.slot(voffset);\n }\n }\n /**\n * Structs are stored inline, so nothing additional is being added. `d` is always 0.\n */\n addFieldStruct(voffset, value, defaultValue) {\n if (value != defaultValue) {\n this.nested(value);\n this.slot(voffset);\n }\n }\n /**\n * Structures are always stored inline, they need to be created right\n * where they're used. You'll get this assertion failure if you\n * created it elsewhere.\n */\n nested(obj) {\n if (obj != this.offset()) {\n throw new TypeError('FlatBuffers: struct must be serialized inline.');\n }\n }\n /**\n * Should not be creating any other object, string or vector\n * while an object is being constructed\n */\n notNested() {\n if (this.isNested) {\n throw new TypeError('FlatBuffers: object serialization must not be nested.');\n }\n }\n /**\n * Set the current vtable at `voffset` to the current location in the buffer.\n */\n slot(voffset) {\n if (this.vtable !== null)\n this.vtable[voffset] = this.offset();\n }\n /**\n * @returns Offset relative to the end of the buffer.\n */\n offset() {\n return this.bb.capacity() - this.space;\n }\n /**\n * Doubles the size of the backing ByteBuffer and copies the old data towards\n * the end of the new buffer (since we build the buffer backwards).\n *\n * @param bb The current buffer with the existing data\n * @returns A new byte buffer with the old data copied\n * to it. The data is located at the end of the buffer.\n *\n * uint8Array.set() formally takes {Array|ArrayBufferView}, so to pass\n * it a uint8Array we need to suppress the type check:\n * @suppress {checkTypes}\n */\n static growByteBuffer(bb) {\n const old_buf_size = bb.capacity();\n // Ensure we don't grow beyond what fits in an int.\n if (old_buf_size & 0xC0000000) {\n throw new Error('FlatBuffers: cannot grow buffer beyond 2 gigabytes.');\n }\n const new_buf_size = old_buf_size << 1;\n const nbb = ByteBuffer.allocate(new_buf_size);\n nbb.setPosition(new_buf_size - old_buf_size);\n nbb.bytes().set(bb.bytes(), new_buf_size - old_buf_size);\n return nbb;\n }\n /**\n * Adds on offset, relative to where it will be written.\n *\n * @param offset The offset to add.\n */\n addOffset(offset) {\n this.prep(SIZEOF_INT, 0); // Ensure alignment is already done.\n this.writeInt32(this.offset() - offset + SIZEOF_INT);\n }\n /**\n * Start encoding a new object in the buffer. Users will not usually need to\n * call this directly. The FlatBuffers compiler will generate helper methods\n * that call this method internally.\n */\n startObject(numfields) {\n this.notNested();\n if (this.vtable == null) {\n this.vtable = [];\n }\n this.vtable_in_use = numfields;\n for (let i = 0; i < numfields; i++) {\n this.vtable[i] = 0; // This will push additional elements as needed\n }\n this.isNested = true;\n this.object_start = this.offset();\n }\n /**\n * Finish off writing the object that is under construction.\n *\n * @returns The offset to the object inside `dataBuffer`\n */\n endObject() {\n if (this.vtable == null || !this.isNested) {\n throw new Error('FlatBuffers: endObject called without startObject');\n }\n this.addInt32(0);\n const vtableloc = this.offset();\n // Trim trailing zeroes.\n let i = this.vtable_in_use - 1;\n // eslint-disable-next-line no-empty\n for (; i >= 0 && this.vtable[i] == 0; i--) { }\n const trimmed_size = i + 1;\n // Write out the current vtable.\n for (; i >= 0; i--) {\n // Offset relative to the start of the table.\n this.addInt16(this.vtable[i] != 0 ? vtableloc - this.vtable[i] : 0);\n }\n const standard_fields = 2; // The fields below:\n this.addInt16(vtableloc - this.object_start);\n const len = (trimmed_size + standard_fields) * SIZEOF_SHORT;\n this.addInt16(len);\n // Search for an existing vtable that matches the current one.\n let existing_vtable = 0;\n const vt1 = this.space;\n outer_loop: for (i = 0; i < this.vtables.length; i++) {\n const vt2 = this.bb.capacity() - this.vtables[i];\n if (len == this.bb.readInt16(vt2)) {\n for (let j = SIZEOF_SHORT; j < len; j += SIZEOF_SHORT) {\n if (this.bb.readInt16(vt1 + j) != this.bb.readInt16(vt2 + j)) {\n continue outer_loop;\n }\n }\n existing_vtable = this.vtables[i];\n break;\n }\n }\n if (existing_vtable) {\n // Found a match:\n // Remove the current vtable.\n this.space = this.bb.capacity() - vtableloc;\n // Point table to existing vtable.\n this.bb.writeInt32(this.space, existing_vtable - vtableloc);\n }\n else {\n // No match:\n // Add the location of the current vtable to the list of vtables.\n this.vtables.push(this.offset());\n // Point table to current vtable.\n this.bb.writeInt32(this.bb.capacity() - vtableloc, this.offset() - vtableloc);\n }\n this.isNested = false;\n return vtableloc;\n }\n /**\n * Finalize a buffer, poiting to the given `root_table`.\n */\n finish(root_table, opt_file_identifier, opt_size_prefix) {\n const size_prefix = opt_size_prefix ? SIZE_PREFIX_LENGTH : 0;\n if (opt_file_identifier) {\n const file_identifier = opt_file_identifier;\n this.prep(this.minalign, SIZEOF_INT +\n FILE_IDENTIFIER_LENGTH + size_prefix);\n if (file_identifier.length != FILE_IDENTIFIER_LENGTH) {\n throw new TypeError('FlatBuffers: file identifier must be length ' +\n FILE_IDENTIFIER_LENGTH);\n }\n for (let i = FILE_IDENTIFIER_LENGTH - 1; i >= 0; i--) {\n this.writeInt8(file_identifier.charCodeAt(i));\n }\n }\n this.prep(this.minalign, SIZEOF_INT + size_prefix);\n this.addOffset(root_table);\n if (size_prefix) {\n this.addInt32(this.bb.capacity() - this.space);\n }\n this.bb.setPosition(this.space);\n }\n /**\n * Finalize a size prefixed buffer, pointing to the given `root_table`.\n */\n finishSizePrefixed(root_table, opt_file_identifier) {\n this.finish(root_table, opt_file_identifier, true);\n }\n /**\n * This checks a required field has been set in a given table that has\n * just been constructed.\n */\n requiredField(table, field) {\n const table_start = this.bb.capacity() - table;\n const vtable_start = table_start - this.bb.readInt32(table_start);\n const ok = field < this.bb.readInt16(vtable_start) &&\n this.bb.readInt16(vtable_start + field) != 0;\n // If this fails, the caller will show what field needs to be set.\n if (!ok) {\n throw new TypeError('FlatBuffers: field ' + field + ' must be set');\n }\n }\n /**\n * Start a new array/vector of objects. Users usually will not call\n * this directly. The FlatBuffers compiler will create a start/end\n * method for vector types in generated code.\n *\n * @param elem_size The size of each element in the array\n * @param num_elems The number of elements in the array\n * @param alignment The alignment of the array\n */\n startVector(elem_size, num_elems, alignment) {\n this.notNested();\n this.vector_num_elems = num_elems;\n this.prep(SIZEOF_INT, elem_size * num_elems);\n this.prep(alignment, elem_size * num_elems); // Just in case alignment > int.\n }\n /**\n * Finish off the creation of an array and all its elements. The array must be\n * created with `startVector`.\n *\n * @returns The offset at which the newly created array\n * starts.\n */\n endVector() {\n this.writeInt32(this.vector_num_elems);\n return this.offset();\n }\n /**\n * Encode the string `s` in the buffer using UTF-8. If the string passed has\n * already been seen, we return the offset of the already written string\n *\n * @param s The string to encode\n * @return The offset in the buffer where the encoded string starts\n */\n createSharedString(s) {\n if (!s) {\n return 0;\n }\n if (!this.string_maps) {\n this.string_maps = new Map();\n }\n if (this.string_maps.has(s)) {\n return this.string_maps.get(s);\n }\n const offset = this.createString(s);\n this.string_maps.set(s, offset);\n return offset;\n }\n /**\n * Encode the string `s` in the buffer using UTF-8. If a Uint8Array is passed\n * instead of a string, it is assumed to contain valid UTF-8 encoded data.\n *\n * @param s The string to encode\n * @return The offset in the buffer where the encoded string starts\n */\n createString(s) {\n if (s === null || s === undefined) {\n return 0;\n }\n let utf8;\n if (s instanceof Uint8Array) {\n utf8 = s;\n }\n else {\n utf8 = this.text_encoder.encode(s);\n }\n this.addInt8(0);\n this.startVector(1, utf8.length, 1);\n this.bb.setPosition(this.space -= utf8.length);\n for (let i = 0, offset = this.space, bytes = this.bb.bytes(); i < utf8.length; i++) {\n bytes[offset++] = utf8[i];\n }\n return this.endVector();\n }\n /**\n * A helper function to pack an object\n *\n * @returns offset of obj\n */\n createObjectOffset(obj) {\n if (obj === null) {\n return 0;\n }\n if (typeof obj === 'string') {\n return this.createString(obj);\n }\n else {\n return obj.pack(this);\n }\n }\n /**\n * A helper function to pack a list of object\n *\n * @returns list of offsets of each non null object\n */\n createObjectOffsetList(list) {\n const ret = [];\n for (let i = 0; i < list.length; ++i) {\n const val = list[i];\n if (val !== null) {\n ret.push(this.createObjectOffset(val));\n }\n else {\n throw new TypeError('FlatBuffers: Argument for createObjectOffsetList cannot contain null.');\n }\n }\n return ret;\n }\n createStructOffsetList(list, startFunc) {\n startFunc(this, list.length);\n this.createObjectOffsetList(list.slice().reverse());\n return this.endVector();\n }\n}\n","import * as flatbuffers from \"flatbuffers\";\nimport * as fbTopology from \"./generated/topology\";\nimport { handleChange } from \"./topology\";\n\nconst socket = new WebSocket(\"ws://127.0.0.1:55010/pull-stats/\");\n\nsocket.onmessage = handleMessage;\n\nfunction handleMessage(event: MessageEvent) {\n const buf = new flatbuffers.ByteBuffer(new Uint8Array(event.data));\n const peerChange = fbTopology.PeerChange.getRootAsPeerChange(buf);\n handleChange(peerChange);\n}\n","// automatically generated by the FlatBuffers compiler, do not modify\n\nexport { AddedConnection, AddedConnectionT } from './topology/added-connection';\nexport { ControllerResponse, ControllerResponseT } from './topology/controller-response';\nexport { Error, ErrorT } from './topology/error';\nexport { Ok, OkT } from './topology/ok';\nexport { PeerChange, PeerChangeT } from './topology/peer-change';\nexport { PeerChangeType } from './topology/peer-change-type';\nexport { RemovedConnection, RemovedConnectionT } from './topology/removed-connection';\nexport { Response } from './topology/response';\n","// automatically generated by the FlatBuffers compiler, do not modify\n\nimport * as flatbuffers from 'flatbuffers';\n\n\n\nexport class AddedConnection implements flatbuffers.IUnpackableObject {\n bb: flatbuffers.ByteBuffer|null = null;\n bb_pos = 0;\n __init(i:number, bb:flatbuffers.ByteBuffer):AddedConnection {\n this.bb_pos = i;\n this.bb = bb;\n return this;\n}\n\nstatic getRootAsAddedConnection(bb:flatbuffers.ByteBuffer, obj?:AddedConnection):AddedConnection {\n return (obj || new AddedConnection()).__init(bb.readInt32(bb.position()) + bb.position(), bb);\n}\n\nstatic getSizePrefixedRootAsAddedConnection(bb:flatbuffers.ByteBuffer, obj?:AddedConnection):AddedConnection {\n bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);\n return (obj || new AddedConnection()).__init(bb.readInt32(bb.position()) + bb.position(), bb);\n}\n\nfrom():string|null\nfrom(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null\nfrom(optionalEncoding?:any):string|Uint8Array|null {\n const offset = this.bb!.__offset(this.bb_pos, 4);\n return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;\n}\n\nfromLocation():number {\n const offset = this.bb!.__offset(this.bb_pos, 6);\n return offset ? this.bb!.readFloat64(this.bb_pos + offset) : 0.0;\n}\n\nto():string|null\nto(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null\nto(optionalEncoding?:any):string|Uint8Array|null {\n const offset = this.bb!.__offset(this.bb_pos, 8);\n return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;\n}\n\ntoLocation():number {\n const offset = this.bb!.__offset(this.bb_pos, 10);\n return offset ? this.bb!.readFloat64(this.bb_pos + offset) : 0.0;\n}\n\nstatic startAddedConnection(builder:flatbuffers.Builder) {\n builder.startObject(4);\n}\n\nstatic addFrom(builder:flatbuffers.Builder, fromOffset:flatbuffers.Offset) {\n builder.addFieldOffset(0, fromOffset, 0);\n}\n\nstatic addFromLocation(builder:flatbuffers.Builder, fromLocation:number) {\n builder.addFieldFloat64(1, fromLocation, 0.0);\n}\n\nstatic addTo(builder:flatbuffers.Builder, toOffset:flatbuffers.Offset) {\n builder.addFieldOffset(2, toOffset, 0);\n}\n\nstatic addToLocation(builder:flatbuffers.Builder, toLocation:number) {\n builder.addFieldFloat64(3, toLocation, 0.0);\n}\n\nstatic endAddedConnection(builder:flatbuffers.Builder):flatbuffers.Offset {\n const offset = builder.endObject();\n builder.requiredField(offset, 4) // from\n builder.requiredField(offset, 8) // to\n return offset;\n}\n\nstatic createAddedConnection(builder:flatbuffers.Builder, fromOffset:flatbuffers.Offset, fromLocation:number, toOffset:flatbuffers.Offset, toLocation:number):flatbuffers.Offset {\n AddedConnection.startAddedConnection(builder);\n AddedConnection.addFrom(builder, fromOffset);\n AddedConnection.addFromLocation(builder, fromLocation);\n AddedConnection.addTo(builder, toOffset);\n AddedConnection.addToLocation(builder, toLocation);\n return AddedConnection.endAddedConnection(builder);\n}\n\nunpack(): AddedConnectionT {\n return new AddedConnectionT(\n this.from(),\n this.fromLocation(),\n this.to(),\n this.toLocation()\n );\n}\n\n\nunpackTo(_o: AddedConnectionT): void {\n _o.from = this.from();\n _o.fromLocation = this.fromLocation();\n _o.to = this.to();\n _o.toLocation = this.toLocation();\n}\n}\n\nexport class AddedConnectionT implements flatbuffers.IGeneratedObject {\nconstructor(\n public from: string|Uint8Array|null = null,\n public fromLocation: number = 0.0,\n public to: string|Uint8Array|null = null,\n public toLocation: number = 0.0\n){}\n\n\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n const from = (this.from !== null ? builder.createString(this.from!) : 0);\n const to = (this.to !== null ? builder.createString(this.to!) : 0);\n\n return AddedConnection.createAddedConnection(builder,\n from,\n this.fromLocation,\n to,\n this.toLocation\n );\n}\n}\n","// automatically generated by the FlatBuffers compiler, do not modify\n\nimport * as flatbuffers from 'flatbuffers';\n\nimport { Error, ErrorT } from '../topology/error';\nimport { Ok, OkT } from '../topology/ok';\nimport { Response, unionToResponse, unionListToResponse } from '../topology/response';\n\n\nexport class ControllerResponse implements flatbuffers.IUnpackableObject {\n bb: flatbuffers.ByteBuffer|null = null;\n bb_pos = 0;\n __init(i:number, bb:flatbuffers.ByteBuffer):ControllerResponse {\n this.bb_pos = i;\n this.bb = bb;\n return this;\n}\n\nstatic getRootAsControllerResponse(bb:flatbuffers.ByteBuffer, obj?:ControllerResponse):ControllerResponse {\n return (obj || new ControllerResponse()).__init(bb.readInt32(bb.position()) + bb.position(), bb);\n}\n\nstatic getSizePrefixedRootAsControllerResponse(bb:flatbuffers.ByteBuffer, obj?:ControllerResponse):ControllerResponse {\n bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);\n return (obj || new ControllerResponse()).__init(bb.readInt32(bb.position()) + bb.position(), bb);\n}\n\nresponseType():Response {\n const offset = this.bb!.__offset(this.bb_pos, 4);\n return offset ? this.bb!.readUint8(this.bb_pos + offset) : Response.NONE;\n}\n\nresponse(obj:any):any|null {\n const offset = this.bb!.__offset(this.bb_pos, 6);\n return offset ? this.bb!.__union(obj, this.bb_pos + offset) : null;\n}\n\nstatic startControllerResponse(builder:flatbuffers.Builder) {\n builder.startObject(2);\n}\n\nstatic addResponseType(builder:flatbuffers.Builder, responseType:Response) {\n builder.addFieldInt8(0, responseType, Response.NONE);\n}\n\nstatic addResponse(builder:flatbuffers.Builder, responseOffset:flatbuffers.Offset) {\n builder.addFieldOffset(1, responseOffset, 0);\n}\n\nstatic endControllerResponse(builder:flatbuffers.Builder):flatbuffers.Offset {\n const offset = builder.endObject();\n builder.requiredField(offset, 6) // response\n return offset;\n}\n\nstatic createControllerResponse(builder:flatbuffers.Builder, responseType:Response, responseOffset:flatbuffers.Offset):flatbuffers.Offset {\n ControllerResponse.startControllerResponse(builder);\n ControllerResponse.addResponseType(builder, responseType);\n ControllerResponse.addResponse(builder, responseOffset);\n return ControllerResponse.endControllerResponse(builder);\n}\n\nunpack(): ControllerResponseT {\n return new ControllerResponseT(\n this.responseType(),\n (() => {\n const temp = unionToResponse(this.responseType(), this.response.bind(this));\n if(temp === null) { return null; }\n return temp.unpack()\n })()\n );\n}\n\n\nunpackTo(_o: ControllerResponseT): void {\n _o.responseType = this.responseType();\n _o.response = (() => {\n const temp = unionToResponse(this.responseType(), this.response.bind(this));\n if(temp === null) { return null; }\n return temp.unpack()\n })();\n}\n}\n\nexport class ControllerResponseT implements flatbuffers.IGeneratedObject {\nconstructor(\n public responseType: Response = Response.NONE,\n public response: ErrorT|OkT|null = null\n){}\n\n\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n const response = builder.createObjectOffset(this.response);\n\n return ControllerResponse.createControllerResponse(builder,\n this.responseType,\n response\n );\n}\n}\n","// automatically generated by the FlatBuffers compiler, do not modify\n\nimport * as flatbuffers from 'flatbuffers';\n\n\n\nexport class Error implements flatbuffers.IUnpackableObject {\n bb: flatbuffers.ByteBuffer|null = null;\n bb_pos = 0;\n __init(i:number, bb:flatbuffers.ByteBuffer):Error {\n this.bb_pos = i;\n this.bb = bb;\n return this;\n}\n\nstatic getRootAsError(bb:flatbuffers.ByteBuffer, obj?:Error):Error {\n return (obj || new Error()).__init(bb.readInt32(bb.position()) + bb.position(), bb);\n}\n\nstatic getSizePrefixedRootAsError(bb:flatbuffers.ByteBuffer, obj?:Error):Error {\n bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);\n return (obj || new Error()).__init(bb.readInt32(bb.position()) + bb.position(), bb);\n}\n\nmessage():string|null\nmessage(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null\nmessage(optionalEncoding?:any):string|Uint8Array|null {\n const offset = this.bb!.__offset(this.bb_pos, 4);\n return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;\n}\n\nstatic startError(builder:flatbuffers.Builder) {\n builder.startObject(1);\n}\n\nstatic addMessage(builder:flatbuffers.Builder, messageOffset:flatbuffers.Offset) {\n builder.addFieldOffset(0, messageOffset, 0);\n}\n\nstatic endError(builder:flatbuffers.Builder):flatbuffers.Offset {\n const offset = builder.endObject();\n builder.requiredField(offset, 4) // message\n return offset;\n}\n\nstatic createError(builder:flatbuffers.Builder, messageOffset:flatbuffers.Offset):flatbuffers.Offset {\n Error.startError(builder);\n Error.addMessage(builder, messageOffset);\n return Error.endError(builder);\n}\n\nunpack(): ErrorT {\n return new ErrorT(\n this.message()\n );\n}\n\n\nunpackTo(_o: ErrorT): void {\n _o.message = this.message();\n}\n}\n\nexport class ErrorT implements flatbuffers.IGeneratedObject {\nconstructor(\n public message: string|Uint8Array|null = null\n){}\n\n\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n const message = (this.message !== null ? builder.createString(this.message!) : 0);\n\n return Error.createError(builder,\n message\n );\n}\n}\n","// automatically generated by the FlatBuffers compiler, do not modify\n\nimport * as flatbuffers from 'flatbuffers';\n\n\n\nexport class Ok implements flatbuffers.IUnpackableObject {\n bb: flatbuffers.ByteBuffer|null = null;\n bb_pos = 0;\n __init(i:number, bb:flatbuffers.ByteBuffer):Ok {\n this.bb_pos = i;\n this.bb = bb;\n return this;\n}\n\nstatic getRootAsOk(bb:flatbuffers.ByteBuffer, obj?:Ok):Ok {\n return (obj || new Ok()).__init(bb.readInt32(bb.position()) + bb.position(), bb);\n}\n\nstatic getSizePrefixedRootAsOk(bb:flatbuffers.ByteBuffer, obj?:Ok):Ok {\n bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);\n return (obj || new Ok()).__init(bb.readInt32(bb.position()) + bb.position(), bb);\n}\n\nmessage():string|null\nmessage(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null\nmessage(optionalEncoding?:any):string|Uint8Array|null {\n const offset = this.bb!.__offset(this.bb_pos, 4);\n return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;\n}\n\nstatic startOk(builder:flatbuffers.Builder) {\n builder.startObject(1);\n}\n\nstatic addMessage(builder:flatbuffers.Builder, messageOffset:flatbuffers.Offset) {\n builder.addFieldOffset(0, messageOffset, 0);\n}\n\nstatic endOk(builder:flatbuffers.Builder):flatbuffers.Offset {\n const offset = builder.endObject();\n return offset;\n}\n\nstatic createOk(builder:flatbuffers.Builder, messageOffset:flatbuffers.Offset):flatbuffers.Offset {\n Ok.startOk(builder);\n Ok.addMessage(builder, messageOffset);\n return Ok.endOk(builder);\n}\n\nunpack(): OkT {\n return new OkT(\n this.message()\n );\n}\n\n\nunpackTo(_o: OkT): void {\n _o.message = this.message();\n}\n}\n\nexport class OkT implements flatbuffers.IGeneratedObject {\nconstructor(\n public message: string|Uint8Array|null = null\n){}\n\n\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n const message = (this.message !== null ? builder.createString(this.message!) : 0);\n\n return Ok.createOk(builder,\n message\n );\n}\n}\n","// automatically generated by the FlatBuffers compiler, do not modify\n\nimport { AddedConnection, AddedConnectionT } from '../topology/added-connection';\nimport { Error, ErrorT } from '../topology/error';\nimport { RemovedConnection, RemovedConnectionT } from '../topology/removed-connection';\n\n\nexport enum PeerChangeType {\n NONE = 0,\n AddedConnection = 1,\n RemovedConnection = 2,\n Error = 3\n}\n\nexport function unionToPeerChangeType(\n type: PeerChangeType,\n accessor: (obj:AddedConnection|Error|RemovedConnection) => AddedConnection|Error|RemovedConnection|null\n): AddedConnection|Error|RemovedConnection|null {\n switch(PeerChangeType[type]) {\n case 'NONE': return null; \n case 'AddedConnection': return accessor(new AddedConnection())! as AddedConnection;\n case 'RemovedConnection': return accessor(new RemovedConnection())! as RemovedConnection;\n case 'Error': return accessor(new Error())! as Error;\n default: return null;\n }\n}\n\nexport function unionListToPeerChangeType(\n type: PeerChangeType, \n accessor: (index: number, obj:AddedConnection|Error|RemovedConnection) => AddedConnection|Error|RemovedConnection|null, \n index: number\n): AddedConnection|Error|RemovedConnection|null {\n switch(PeerChangeType[type]) {\n case 'NONE': return null; \n case 'AddedConnection': return accessor(index, new AddedConnection())! as AddedConnection;\n case 'RemovedConnection': return accessor(index, new RemovedConnection())! as RemovedConnection;\n case 'Error': return accessor(index, new Error())! as Error;\n default: return null;\n }\n}\n","// automatically generated by the FlatBuffers compiler, do not modify\n\nimport * as flatbuffers from 'flatbuffers';\n\nimport { AddedConnection, AddedConnectionT } from '../topology/added-connection';\nimport { Error, ErrorT } from '../topology/error';\nimport { PeerChangeType, unionToPeerChangeType, unionListToPeerChangeType } from '../topology/peer-change-type';\nimport { RemovedConnection, RemovedConnectionT } from '../topology/removed-connection';\n\n\nexport class PeerChange implements flatbuffers.IUnpackableObject {\n bb: flatbuffers.ByteBuffer|null = null;\n bb_pos = 0;\n __init(i:number, bb:flatbuffers.ByteBuffer):PeerChange {\n this.bb_pos = i;\n this.bb = bb;\n return this;\n}\n\nstatic getRootAsPeerChange(bb:flatbuffers.ByteBuffer, obj?:PeerChange):PeerChange {\n return (obj || new PeerChange()).__init(bb.readInt32(bb.position()) + bb.position(), bb);\n}\n\nstatic getSizePrefixedRootAsPeerChange(bb:flatbuffers.ByteBuffer, obj?:PeerChange):PeerChange {\n bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);\n return (obj || new PeerChange()).__init(bb.readInt32(bb.position()) + bb.position(), bb);\n}\n\ncurrentState(index: number, obj?:AddedConnection):AddedConnection|null {\n const offset = this.bb!.__offset(this.bb_pos, 4);\n return offset ? (obj || new AddedConnection()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;\n}\n\ncurrentStateLength():number {\n const offset = this.bb!.__offset(this.bb_pos, 4);\n return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;\n}\n\nchangeType():PeerChangeType {\n const offset = this.bb!.__offset(this.bb_pos, 6);\n return offset ? this.bb!.readUint8(this.bb_pos + offset) : PeerChangeType.NONE;\n}\n\nchange(obj:any):any|null {\n const offset = this.bb!.__offset(this.bb_pos, 8);\n return offset ? this.bb!.__union(obj, this.bb_pos + offset) : null;\n}\n\nstatic startPeerChange(builder:flatbuffers.Builder) {\n builder.startObject(3);\n}\n\nstatic addCurrentState(builder:flatbuffers.Builder, currentStateOffset:flatbuffers.Offset) {\n builder.addFieldOffset(0, currentStateOffset, 0);\n}\n\nstatic createCurrentStateVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {\n builder.startVector(4, data.length, 4);\n for (let i = data.length - 1; i >= 0; i--) {\n builder.addOffset(data[i]!);\n }\n return builder.endVector();\n}\n\nstatic startCurrentStateVector(builder:flatbuffers.Builder, numElems:number) {\n builder.startVector(4, numElems, 4);\n}\n\nstatic addChangeType(builder:flatbuffers.Builder, changeType:PeerChangeType) {\n builder.addFieldInt8(1, changeType, PeerChangeType.NONE);\n}\n\nstatic addChange(builder:flatbuffers.Builder, changeOffset:flatbuffers.Offset) {\n builder.addFieldOffset(2, changeOffset, 0);\n}\n\nstatic endPeerChange(builder:flatbuffers.Builder):flatbuffers.Offset {\n const offset = builder.endObject();\n return offset;\n}\n\nstatic createPeerChange(builder:flatbuffers.Builder, currentStateOffset:flatbuffers.Offset, changeType:PeerChangeType, changeOffset:flatbuffers.Offset):flatbuffers.Offset {\n PeerChange.startPeerChange(builder);\n PeerChange.addCurrentState(builder, currentStateOffset);\n PeerChange.addChangeType(builder, changeType);\n PeerChange.addChange(builder, changeOffset);\n return PeerChange.endPeerChange(builder);\n}\n\nunpack(): PeerChangeT {\n return new PeerChangeT(\n this.bb!.createObjList(this.currentState.bind(this), this.currentStateLength()),\n this.changeType(),\n (() => {\n const temp = unionToPeerChangeType(this.changeType(), this.change.bind(this));\n if(temp === null) { return null; }\n return temp.unpack()\n })()\n );\n}\n\n\nunpackTo(_o: PeerChangeT): void {\n _o.currentState = this.bb!.createObjList(this.currentState.bind(this), this.currentStateLength());\n _o.changeType = this.changeType();\n _o.change = (() => {\n const temp = unionToPeerChangeType(this.changeType(), this.change.bind(this));\n if(temp === null) { return null; }\n return temp.unpack()\n })();\n}\n}\n\nexport class PeerChangeT implements flatbuffers.IGeneratedObject {\nconstructor(\n public currentState: (AddedConnectionT)[] = [],\n public changeType: PeerChangeType = PeerChangeType.NONE,\n public change: AddedConnectionT|ErrorT|RemovedConnectionT|null = null\n){}\n\n\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n const currentState = PeerChange.createCurrentStateVector(builder, builder.createObjectOffsetList(this.currentState));\n const change = builder.createObjectOffset(this.change);\n\n return PeerChange.createPeerChange(builder,\n currentState,\n this.changeType,\n change\n );\n}\n}\n","// automatically generated by the FlatBuffers compiler, do not modify\n\nimport * as flatbuffers from 'flatbuffers';\n\n\n\nexport class RemovedConnection implements flatbuffers.IUnpackableObject {\n bb: flatbuffers.ByteBuffer|null = null;\n bb_pos = 0;\n __init(i:number, bb:flatbuffers.ByteBuffer):RemovedConnection {\n this.bb_pos = i;\n this.bb = bb;\n return this;\n}\n\nstatic getRootAsRemovedConnection(bb:flatbuffers.ByteBuffer, obj?:RemovedConnection):RemovedConnection {\n return (obj || new RemovedConnection()).__init(bb.readInt32(bb.position()) + bb.position(), bb);\n}\n\nstatic getSizePrefixedRootAsRemovedConnection(bb:flatbuffers.ByteBuffer, obj?:RemovedConnection):RemovedConnection {\n bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);\n return (obj || new RemovedConnection()).__init(bb.readInt32(bb.position()) + bb.position(), bb);\n}\n\nat():string|null\nat(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null\nat(optionalEncoding?:any):string|Uint8Array|null {\n const offset = this.bb!.__offset(this.bb_pos, 4);\n return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;\n}\n\nfrom():string|null\nfrom(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null\nfrom(optionalEncoding?:any):string|Uint8Array|null {\n const offset = this.bb!.__offset(this.bb_pos, 6);\n return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;\n}\n\nstatic startRemovedConnection(builder:flatbuffers.Builder) {\n builder.startObject(2);\n}\n\nstatic addAt(builder:flatbuffers.Builder, atOffset:flatbuffers.Offset) {\n builder.addFieldOffset(0, atOffset, 0);\n}\n\nstatic addFrom(builder:flatbuffers.Builder, fromOffset:flatbuffers.Offset) {\n builder.addFieldOffset(1, fromOffset, 0);\n}\n\nstatic endRemovedConnection(builder:flatbuffers.Builder):flatbuffers.Offset {\n const offset = builder.endObject();\n builder.requiredField(offset, 4) // at\n builder.requiredField(offset, 6) // from\n return offset;\n}\n\nstatic createRemovedConnection(builder:flatbuffers.Builder, atOffset:flatbuffers.Offset, fromOffset:flatbuffers.Offset):flatbuffers.Offset {\n RemovedConnection.startRemovedConnection(builder);\n RemovedConnection.addAt(builder, atOffset);\n RemovedConnection.addFrom(builder, fromOffset);\n return RemovedConnection.endRemovedConnection(builder);\n}\n\nunpack(): RemovedConnectionT {\n return new RemovedConnectionT(\n this.at(),\n this.from()\n );\n}\n\n\nunpackTo(_o: RemovedConnectionT): void {\n _o.at = this.at();\n _o.from = this.from();\n}\n}\n\nexport class RemovedConnectionT implements flatbuffers.IGeneratedObject {\nconstructor(\n public at: string|Uint8Array|null = null,\n public from: string|Uint8Array|null = null\n){}\n\n\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n const at = (this.at !== null ? builder.createString(this.at!) : 0);\n const from = (this.from !== null ? builder.createString(this.from!) : 0);\n\n return RemovedConnection.createRemovedConnection(builder,\n at,\n from\n );\n}\n}\n","// automatically generated by the FlatBuffers compiler, do not modify\n\nimport { Error, ErrorT } from '../topology/error';\nimport { Ok, OkT } from '../topology/ok';\n\n\nexport enum Response {\n NONE = 0,\n Error = 1,\n Ok = 2\n}\n\nexport function unionToResponse(\n type: Response,\n accessor: (obj:Error|Ok) => Error|Ok|null\n): Error|Ok|null {\n switch(Response[type]) {\n case 'NONE': return null; \n case 'Error': return accessor(new Error())! as Error;\n case 'Ok': return accessor(new Ok())! as Ok;\n default: return null;\n }\n}\n\nexport function unionListToResponse(\n type: Response, \n accessor: (index: number, obj:Error|Ok) => Error|Ok|null, \n index: number\n): Error|Ok|null {\n switch(Response[type]) {\n case 'NONE': return null; \n case 'Error': return accessor(index, new Error())! as Error;\n case 'Ok': return accessor(index, new Ok())! as Ok;\n default: return null;\n }\n}\n","import * as fbTopology from \"./generated/topology\";\n\nexport let peers: PeerList = {};\n\ninterface PeerList {\n [id: string]: Peer;\n}\n\ninterface Peer {\n id: string;\n currentLocation: number;\n connections: Connection[];\n history: ChangeInfo[];\n locationHistory: { location: number; timestamp: number }[];\n}\n\ninterface Connection {\n id: string;\n location: number;\n}\n\ninterface ChangeInfo {\n type: \"Added\" | \"Removed\";\n from: Connection;\n to: Connection;\n timestamp: number;\n}\n\nexport function handleChange(peerChange: fbTopology.PeerChange) {\n const unpacked = peerChange.unpack();\n try {\n switch (unpacked.changeType) {\n case fbTopology.PeerChangeType.AddedConnection:\n handleAddedConnection(unpacked.change as fbTopology.AddedConnectionT);\n break;\n case fbTopology.PeerChangeType.RemovedConnection:\n handleRemovedConnection(\n unpacked.change as fbTopology.RemovedConnectionT\n );\n break;\n }\n } catch (e) {\n console.error(e);\n }\n updateTable();\n}\n\nexport function handleAddedConnection(peerChange: fbTopology.AddedConnectionT) {\n const added = peerChange;\n const fromAdded = added.from!.toString();\n const toAdded = added.to!.toString();\n\n const fromConnection: Connection = {\n id: fromAdded,\n location: added.fromLocation,\n };\n\n const toConnection: Connection = {\n id: toAdded,\n location: added.toLocation,\n };\n\n if (peers[fromAdded]) {\n if (peers[fromAdded].currentLocation !== added.fromLocation) {\n peers[fromAdded].locationHistory.push({\n location: peers[fromAdded].currentLocation,\n timestamp: Date.now(),\n });\n peers[fromAdded].currentLocation = added.fromLocation;\n }\n\n if (peers[toAdded].currentLocation !== added.toLocation) {\n peers[toAdded].locationHistory.push({\n location: peers[toAdded].currentLocation,\n timestamp: Date.now(),\n });\n peers[toAdded].currentLocation = added.toLocation;\n }\n\n peers[fromAdded].connections.push(toConnection);\n } else {\n peers[fromAdded] = {\n id: fromAdded,\n currentLocation: added.fromLocation,\n connections: [toConnection],\n history: [],\n locationHistory: [],\n };\n }\n\n if (peers[toAdded]) {\n peers[toAdded].connections.push(fromConnection);\n } else {\n peers[toAdded] = {\n id: toAdded,\n currentLocation: added.toLocation,\n connections: [fromConnection],\n history: [],\n locationHistory: [],\n };\n }\n\n const changeInfo: ChangeInfo = {\n type: \"Added\",\n from: fromConnection,\n to: toConnection,\n timestamp: Date.now(),\n };\n\n peers[fromAdded].history.push(changeInfo);\n peers[toAdded].history.push(changeInfo);\n}\n\nexport function handleRemovedConnection(\n peerChange: fbTopology.RemovedConnectionT\n) {\n const removed = peerChange;\n const fromRemoved = removed.from!.toString();\n const atRemoved = removed.at!.toString();\n const index = peers[fromRemoved].connections.findIndex(\n (connection) => connection.id === atRemoved\n );\n\n if (index > -1) {\n peers[fromRemoved].connections.splice(index, 1);\n }\n\n const reverseIndex = peers[atRemoved].connections.findIndex(\n (connection: Connection) => connection.id === fromRemoved\n );\n\n if (reverseIndex > -1) {\n peers[atRemoved].connections.splice(reverseIndex, 1);\n }\n\n const changeInfo: ChangeInfo = {\n type: \"Removed\",\n from: {\n id: fromRemoved,\n location: peers[fromRemoved].currentLocation,\n },\n to: {\n id: atRemoved,\n location: peers[atRemoved].currentLocation,\n },\n timestamp: Date.now(),\n };\n\n peers[fromRemoved].history.push(changeInfo);\n peers[atRemoved].history.push(changeInfo);\n}\n\nfunction updateTable() {\n const table = document.getElementById(\"peers-table\");\n\n if (table) {\n table.innerHTML = \"\";\n\n for (const id in peers) {\n const row = document.createElement(\"tr\");\n row.onclick = () => showConnections(peers[id]);\n row.onclick = () => displayHistory(peers[id]);\n\n const cell = document.createElement(\"td\");\n cell.textContent = id;\n row.appendChild(cell);\n\n table.appendChild(row);\n }\n }\n}\n\nexport function showConnections(peer: Peer) {\n const id = peer.id;\n const connections: Connection[] = peer.connections ?? [];\n\n const overlayDiv = document.getElementById(\"overlay-div\");\n\n if (overlayDiv) {\n overlayDiv.innerHTML = \"\";\n\n const headerSection = document.createElement(\"section\");\n headerSection.classList.add(\"header-section\");\n\n const idLocation = document.createElement(\"h2\");\n idLocation.textContent = `Peer ID: ${id}, Location: ${\n peer.currentLocation ?? \"\"\n }`;\n headerSection.appendChild(idLocation);\n\n overlayDiv.appendChild(headerSection);\n\n const tableSection = document.createElement(\"section\");\n tableSection.classList.add(\"table-section\");\n\n const table = document.createElement(\"table\");\n\n table.classList.add(\"peers-table\");\n\n // Create the table header row\n const headerRow = document.createElement(\"tr\");\n const idHeader = document.createElement(\"th\");\n idHeader.textContent = \"Peer ID\";\n const locationHeader = document.createElement(\"th\");\n locationHeader.textContent = \"Location\";\n headerRow.appendChild(idHeader);\n headerRow.appendChild(locationHeader);\n table.appendChild(headerRow);\n\n // Create and append the table rows for all peers\n connections.forEach((connection) => {\n const row = document.createElement(\"tr\");\n const idCell = document.createElement(\"td\");\n idCell.textContent = connection.id.toString() ?? \"\";\n const locationCell = document.createElement(\"td\");\n locationCell.textContent = connection.location.toString() ?? \"\";\n row.appendChild(idCell);\n row.appendChild(locationCell);\n table.appendChild(row);\n });\n\n // Append the table to the table section\n tableSection.appendChild(table);\n\n // Append the table section to the overlay div\n overlayDiv.appendChild(tableSection);\n }\n}\n\nfunction displayHistory(peer: Peer): string {\n let historyHTML = '';\n historyHTML +=\n \"\";\n\n historyHTML += peer.history\n .map((change) => {\n return ``;\n })\n .join(\"\");\n\n historyHTML += \"
TimestampTypeFromTo
${change.timestamp}${change.type}${change.from.id}${change.to.id}
\";\n\n return historyHTML;\n}\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(752);\n"],"names":["SIZEOF_SHORT","SIZEOF_INT","FILE_IDENTIFIER_LENGTH","SIZE_PREFIX_LENGTH","int32","Int32Array","float32","Float32Array","buffer","float64","Float64Array","isLittleEndian","Uint16Array","Uint8Array","Encoding","ByteBuffer","constructor","bytes_","this","position_","text_decoder_","TextDecoder","allocate","byte_size","clear","bytes","position","setPosition","capacity","length","readInt8","offset","readUint8","readInt16","readUint16","readInt32","readUint32","readInt64","BigInt","asIntN","readUint64","asUintN","readFloat32","readFloat64","writeInt8","value","writeUint8","writeInt16","writeUint16","writeInt32","writeUint32","writeInt64","Number","writeUint64","writeFloat32","writeFloat64","getBufferIdentifier","Error","result","i","String","fromCharCode","__offset","bb_pos","vtable_offset","vtable","__union","t","bb","__string","opt_encoding","utf8bytes","subarray","UTF8_BYTES","decode","__union_with_string","o","__indirect","__vector","__vector_len","__has_identifier","ident","charCodeAt","createScalarList","listAccessor","listLength","ret","val","push","createObjList","unpack","Builder","opt_initial_size","initial_size","minalign","vtable_in_use","isNested","object_start","vtables","vector_num_elems","force_defaults","string_maps","text_encoder","TextEncoder","space","forceDefaults","dataBuffer","asUint8Array","prep","size","additional_bytes","align_size","old_buf_size","growByteBuffer","pad","addInt8","addInt16","addInt32","addInt64","addFloat32","addFloat64","addFieldInt8","voffset","defaultValue","slot","addFieldInt16","addFieldInt32","addFieldInt64","addFieldFloat32","addFieldFloat64","addFieldOffset","addOffset","addFieldStruct","nested","obj","TypeError","notNested","new_buf_size","nbb","set","startObject","numfields","endObject","vtableloc","trimmed_size","len","existing_vtable","vt1","outer_loop","vt2","j","finish","root_table","opt_file_identifier","opt_size_prefix","size_prefix","file_identifier","finishSizePrefixed","requiredField","table","field","table_start","vtable_start","startVector","elem_size","num_elems","alignment","endVector","createSharedString","s","Map","has","get","createString","utf8","encode","createObjectOffset","pack","createObjectOffsetList","list","createStructOffsetList","startFunc","slice","reverse","WebSocket","onmessage","event","buf","flatbuffers","data","peerChange","fbTopology","PeerChange","getRootAsPeerChange","handleChange","AddedConnection","AddedConnectionT","ControllerResponse","ControllerResponseT","ErrorT","Ok","OkT","PeerChangeT","PeerChangeType","RemovedConnection","RemovedConnectionT","Response","__init","getRootAsAddedConnection","getSizePrefixedRootAsAddedConnection","from","optionalEncoding","fromLocation","to","toLocation","startAddedConnection","builder","addFrom","fromOffset","addFromLocation","addTo","toOffset","addToLocation","endAddedConnection","createAddedConnection","unpackTo","_o","getRootAsControllerResponse","getSizePrefixedRootAsControllerResponse","responseType","NONE","response","startControllerResponse","addResponseType","addResponse","responseOffset","endControllerResponse","createControllerResponse","temp","unionToResponse","bind","getRootAsError","getSizePrefixedRootAsError","message","startError","addMessage","messageOffset","endError","createError","getRootAsOk","getSizePrefixedRootAsOk","startOk","endOk","createOk","type","accessor","index","getSizePrefixedRootAsPeerChange","currentState","currentStateLength","changeType","change","startPeerChange","addCurrentState","currentStateOffset","createCurrentStateVector","startCurrentStateVector","numElems","addChangeType","addChange","changeOffset","endPeerChange","createPeerChange","unionToPeerChangeType","getRootAsRemovedConnection","getSizePrefixedRootAsRemovedConnection","at","startRemovedConnection","addAt","atOffset","endRemovedConnection","createRemovedConnection","handleAddedConnection","added","fromAdded","toString","toAdded","fromConnection","id","location","toConnection","peers","currentLocation","locationHistory","timestamp","Date","now","connections","history","changeInfo","handleRemovedConnection","removed","fromRemoved","atRemoved","findIndex","connection","splice","reverseIndex","showConnections","peer","overlayDiv","document","getElementById","innerHTML","headerSection","createElement","classList","add","idLocation","textContent","appendChild","tableSection","headerRow","idHeader","locationHeader","forEach","row","idCell","locationCell","displayHistory","historyHTML","map","join","unpacked","e","console","error","onclick","cell","updateTable","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","call","d","definition","key","Object","defineProperty","enumerable","prop","prototype","hasOwnProperty","r","Symbol","toStringTag"],"sourceRoot":""} \ No newline at end of file diff --git a/network-monitor/dist/index.html b/network-monitor/dist/index.html new file mode 100644 index 000000000..f52f7085f --- /dev/null +++ b/network-monitor/dist/index.html @@ -0,0 +1,50 @@ + + + + Freenet Network Monitor + + + + + +
+
+

Freenet Network Monitor

+
+
+
+
+ + + + + + + + + + +
Peer Id Location + Connection Date + +
+
+
+ + + diff --git a/network-monitor/dist/jest.config.d.ts b/network-monitor/dist/jest.config.d.ts new file mode 100644 index 000000000..0c491e53f --- /dev/null +++ b/network-monitor/dist/jest.config.d.ts @@ -0,0 +1,3 @@ +import type { Config } from "@jest/types"; +declare const config: Config.InitialOptions; +export default config; diff --git a/network-monitor/dist/jest.config.js b/network-monitor/dist/jest.config.js new file mode 100644 index 000000000..f06dfd25c --- /dev/null +++ b/network-monitor/dist/jest.config.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const config = { + verbose: true, + transform: { + "^.+\\.tsx?$": "ts-jest", + }, + roots: ["/tests", "/src"], + moduleNameMapper: { + "^(.*)\\.js$": "$1", + }, + testEnvironment: "jsdom", +}; +exports.default = config; +//# sourceMappingURL=jest.config.js.map diff --git a/network-monitor/dist/jest.config.js.map b/network-monitor/dist/jest.config.js.map new file mode 100644 index 000000000..e777b5bc2 --- /dev/null +++ b/network-monitor/dist/jest.config.js.map @@ -0,0 +1 @@ +{"version":3,"file":"jest.config.js","sourceRoot":"","sources":["../jest.config.ts"],"names":[],"mappings":";;AACA,MAAM,MAAM,GAA0B;IACpC,OAAO,EAAE,IAAI;IACb,SAAS,EAAE;QACT,aAAa,EAAE,SAAS;KACzB;IACD,KAAK,EAAE,CAAC,iBAAiB,EAAE,eAAe,CAAC;IAC3C,gBAAgB,EAAE;QAChB,aAAa,EAAE,IAAI;KACpB;IACD,eAAe,EAAE,OAAO;CACzB,CAAC;AACF,kBAAe,MAAM,CAAC"} \ No newline at end of file diff --git a/network-monitor/dist/src/app.d.ts b/network-monitor/dist/src/app.d.ts new file mode 100644 index 000000000..cb0ff5c3b --- /dev/null +++ b/network-monitor/dist/src/app.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/network-monitor/dist/src/app.js b/network-monitor/dist/src/app.js new file mode 100644 index 000000000..6bec004f8 --- /dev/null +++ b/network-monitor/dist/src/app.js @@ -0,0 +1,57 @@ +"use strict"; +var __createBinding = + (this && this.__createBinding) || + (Object.create + ? function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if ( + !desc || + ("get" in desc ? !m.__esModule : desc.writable || desc.configurable) + ) { + desc = { + enumerable: true, + get: function () { + return m[k]; + }, + }; + } + Object.defineProperty(o, k2, desc); + } + : function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; + }); +var __setModuleDefault = + (this && this.__setModuleDefault) || + (Object.create + ? function (o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + } + : function (o, v) { + o["default"] = v; + }); +var __importStar = + (this && this.__importStar) || + function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) + for (var k in mod) + if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) + __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; + }; +Object.defineProperty(exports, "__esModule", { value: true }); +const flatbuffers = __importStar(require("flatbuffers")); +const fbTopology = __importStar(require("./generated/topology")); +const topology_1 = require("./topology"); +const socket = new WebSocket("ws://127.0.0.1:55010/pull-stats/"); +socket.onmessage = handleMessage; +function handleMessage(event) { + const buf = new flatbuffers.ByteBuffer(new Uint8Array(event.data)); + const peerChange = fbTopology.PeerChange.getRootAsPeerChange(buf); + (0, topology_1.handleChange)(peerChange); +} +//# sourceMappingURL=app.js.map diff --git a/network-monitor/dist/src/app.js.map b/network-monitor/dist/src/app.js.map new file mode 100644 index 000000000..732118737 --- /dev/null +++ b/network-monitor/dist/src/app.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/app.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yDAA2C;AAC3C,iEAAmD;AACnD,yCAA0C;AAE1C,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,kCAAkC,CAAC,CAAC;AAEjE,MAAM,CAAC,SAAS,GAAG,aAAa,CAAC;AAEjC,SAAS,aAAa,CAAC,KAAmB;IACxC,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACnE,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAClE,IAAA,uBAAY,EAAC,UAAU,CAAC,CAAC;AAC3B,CAAC"} \ No newline at end of file diff --git a/network-monitor/dist/src/generated/topology.d.ts b/network-monitor/dist/src/generated/topology.d.ts new file mode 100644 index 000000000..251f24585 --- /dev/null +++ b/network-monitor/dist/src/generated/topology.d.ts @@ -0,0 +1,14 @@ +export { AddedConnection, AddedConnectionT } from "./topology/added-connection"; +export { + ControllerResponse, + ControllerResponseT, +} from "./topology/controller-response"; +export { Error, ErrorT } from "./topology/error"; +export { Ok, OkT } from "./topology/ok"; +export { PeerChange, PeerChangeT } from "./topology/peer-change"; +export { PeerChangeType } from "./topology/peer-change-type"; +export { + RemovedConnection, + RemovedConnectionT, +} from "./topology/removed-connection"; +export { Response } from "./topology/response"; diff --git a/network-monitor/dist/src/generated/topology.js b/network-monitor/dist/src/generated/topology.js new file mode 100644 index 000000000..b1d47d346 --- /dev/null +++ b/network-monitor/dist/src/generated/topology.js @@ -0,0 +1,111 @@ +"use strict"; +// automatically generated by the FlatBuffers compiler, do not modify +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Response = + exports.RemovedConnectionT = + exports.RemovedConnection = + exports.PeerChangeType = + exports.PeerChangeT = + exports.PeerChange = + exports.OkT = + exports.Ok = + exports.ErrorT = + exports.Error = + exports.ControllerResponseT = + exports.ControllerResponse = + exports.AddedConnectionT = + exports.AddedConnection = + void 0; +var added_connection_js_1 = require("./topology/added-connection.js"); +Object.defineProperty(exports, "AddedConnection", { + enumerable: true, + get: function () { + return added_connection_js_1.AddedConnection; + }, +}); +Object.defineProperty(exports, "AddedConnectionT", { + enumerable: true, + get: function () { + return added_connection_js_1.AddedConnectionT; + }, +}); +var controller_response_js_1 = require("./topology/controller-response.js"); +Object.defineProperty(exports, "ControllerResponse", { + enumerable: true, + get: function () { + return controller_response_js_1.ControllerResponse; + }, +}); +Object.defineProperty(exports, "ControllerResponseT", { + enumerable: true, + get: function () { + return controller_response_js_1.ControllerResponseT; + }, +}); +var error_js_1 = require("./topology/error.js"); +Object.defineProperty(exports, "Error", { + enumerable: true, + get: function () { + return error_js_1.Error; + }, +}); +Object.defineProperty(exports, "ErrorT", { + enumerable: true, + get: function () { + return error_js_1.ErrorT; + }, +}); +var ok_js_1 = require("./topology/ok.js"); +Object.defineProperty(exports, "Ok", { + enumerable: true, + get: function () { + return ok_js_1.Ok; + }, +}); +Object.defineProperty(exports, "OkT", { + enumerable: true, + get: function () { + return ok_js_1.OkT; + }, +}); +var peer_change_js_1 = require("./topology/peer-change.js"); +Object.defineProperty(exports, "PeerChange", { + enumerable: true, + get: function () { + return peer_change_js_1.PeerChange; + }, +}); +Object.defineProperty(exports, "PeerChangeT", { + enumerable: true, + get: function () { + return peer_change_js_1.PeerChangeT; + }, +}); +var peer_change_type_js_1 = require("./topology/peer-change-type.js"); +Object.defineProperty(exports, "PeerChangeType", { + enumerable: true, + get: function () { + return peer_change_type_js_1.PeerChangeType; + }, +}); +var removed_connection_js_1 = require("./topology/removed-connection.js"); +Object.defineProperty(exports, "RemovedConnection", { + enumerable: true, + get: function () { + return removed_connection_js_1.RemovedConnection; + }, +}); +Object.defineProperty(exports, "RemovedConnectionT", { + enumerable: true, + get: function () { + return removed_connection_js_1.RemovedConnectionT; + }, +}); +var response_js_1 = require("./topology/response.js"); +Object.defineProperty(exports, "Response", { + enumerable: true, + get: function () { + return response_js_1.Response; + }, +}); +//# sourceMappingURL=topology.js.map diff --git a/network-monitor/dist/src/generated/topology.js.map b/network-monitor/dist/src/generated/topology.js.map new file mode 100644 index 000000000..4a48b1b28 --- /dev/null +++ b/network-monitor/dist/src/generated/topology.js.map @@ -0,0 +1 @@ +{"version":3,"file":"topology.js","sourceRoot":"","sources":["../../../src/generated/topology.ts"],"names":[],"mappings":";AAAA,qEAAqE;;;AAErE,sEAGwC;AAFtC,sHAAA,eAAe,OAAA;AACf,uHAAA,gBAAgB,OAAA;AAElB,4EAG2C;AAFzC,4HAAA,kBAAkB,OAAA;AAClB,6HAAA,mBAAmB,OAAA;AAErB,gDAAoD;AAA3C,iGAAA,KAAK,OAAA;AAAE,kGAAA,MAAM,OAAA;AACtB,0CAA2C;AAAlC,2FAAA,EAAE,OAAA;AAAE,4FAAA,GAAG,OAAA;AAChB,4DAAoE;AAA3D,4GAAA,UAAU,OAAA;AAAE,6GAAA,WAAW,OAAA;AAChC,sEAAgE;AAAvD,qHAAA,cAAc,OAAA;AACvB,0EAG0C;AAFxC,0HAAA,iBAAiB,OAAA;AACjB,2HAAA,kBAAkB,OAAA;AAEpB,sDAAkD;AAAzC,uGAAA,QAAQ,OAAA"} \ No newline at end of file diff --git a/network-monitor/dist/src/generated/topology/added-connection.d.ts b/network-monitor/dist/src/generated/topology/added-connection.d.ts new file mode 100644 index 000000000..b3dfbe755 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/added-connection.d.ts @@ -0,0 +1,59 @@ +import * as flatbuffers from "flatbuffers"; +export declare class AddedConnection + implements flatbuffers.IUnpackableObject +{ + bb: flatbuffers.ByteBuffer | null; + bb_pos: number; + __init(i: number, bb: flatbuffers.ByteBuffer): AddedConnection; + static getRootAsAddedConnection( + bb: flatbuffers.ByteBuffer, + obj?: AddedConnection + ): AddedConnection; + static getSizePrefixedRootAsAddedConnection( + bb: flatbuffers.ByteBuffer, + obj?: AddedConnection + ): AddedConnection; + from(): string | null; + from(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; + fromLocation(): number; + to(): string | null; + to(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; + toLocation(): number; + static startAddedConnection(builder: flatbuffers.Builder): void; + static addFrom( + builder: flatbuffers.Builder, + fromOffset: flatbuffers.Offset + ): void; + static addFromLocation( + builder: flatbuffers.Builder, + fromLocation: number + ): void; + static addTo( + builder: flatbuffers.Builder, + toOffset: flatbuffers.Offset + ): void; + static addToLocation(builder: flatbuffers.Builder, toLocation: number): void; + static endAddedConnection(builder: flatbuffers.Builder): flatbuffers.Offset; + static createAddedConnection( + builder: flatbuffers.Builder, + fromOffset: flatbuffers.Offset, + fromLocation: number, + toOffset: flatbuffers.Offset, + toLocation: number + ): flatbuffers.Offset; + unpack(): AddedConnectionT; + unpackTo(_o: AddedConnectionT): void; +} +export declare class AddedConnectionT implements flatbuffers.IGeneratedObject { + from: string | Uint8Array | null; + fromLocation: number; + to: string | Uint8Array | null; + toLocation: number; + constructor( + from?: string | Uint8Array | null, + fromLocation?: number, + to?: string | Uint8Array | null, + toLocation?: number + ); + pack(builder: flatbuffers.Builder): flatbuffers.Offset; +} diff --git a/network-monitor/dist/src/generated/topology/added-connection.js b/network-monitor/dist/src/generated/topology/added-connection.js new file mode 100644 index 000000000..6ea9c1c5f --- /dev/null +++ b/network-monitor/dist/src/generated/topology/added-connection.js @@ -0,0 +1,164 @@ +"use strict"; +// automatically generated by the FlatBuffers compiler, do not modify +var __createBinding = + (this && this.__createBinding) || + (Object.create + ? function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if ( + !desc || + ("get" in desc ? !m.__esModule : desc.writable || desc.configurable) + ) { + desc = { + enumerable: true, + get: function () { + return m[k]; + }, + }; + } + Object.defineProperty(o, k2, desc); + } + : function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; + }); +var __setModuleDefault = + (this && this.__setModuleDefault) || + (Object.create + ? function (o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + } + : function (o, v) { + o["default"] = v; + }); +var __importStar = + (this && this.__importStar) || + function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) + for (var k in mod) + if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) + __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; + }; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AddedConnectionT = exports.AddedConnection = void 0; +const flatbuffers = __importStar(require("flatbuffers")); +class AddedConnection { + constructor() { + this.bb = null; + this.bb_pos = 0; + } + __init(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; + } + static getRootAsAddedConnection(bb, obj) { + return (obj || new AddedConnection()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + static getSizePrefixedRootAsAddedConnection(bb, obj) { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new AddedConnection()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + from(optionalEncoding) { + const offset = this.bb.__offset(this.bb_pos, 4); + return offset + ? this.bb.__string(this.bb_pos + offset, optionalEncoding) + : null; + } + fromLocation() { + const offset = this.bb.__offset(this.bb_pos, 6); + return offset ? this.bb.readFloat64(this.bb_pos + offset) : 0.0; + } + to(optionalEncoding) { + const offset = this.bb.__offset(this.bb_pos, 8); + return offset + ? this.bb.__string(this.bb_pos + offset, optionalEncoding) + : null; + } + toLocation() { + const offset = this.bb.__offset(this.bb_pos, 10); + return offset ? this.bb.readFloat64(this.bb_pos + offset) : 0.0; + } + static startAddedConnection(builder) { + builder.startObject(4); + } + static addFrom(builder, fromOffset) { + builder.addFieldOffset(0, fromOffset, 0); + } + static addFromLocation(builder, fromLocation) { + builder.addFieldFloat64(1, fromLocation, 0.0); + } + static addTo(builder, toOffset) { + builder.addFieldOffset(2, toOffset, 0); + } + static addToLocation(builder, toLocation) { + builder.addFieldFloat64(3, toLocation, 0.0); + } + static endAddedConnection(builder) { + const offset = builder.endObject(); + builder.requiredField(offset, 4); // from + builder.requiredField(offset, 8); // to + return offset; + } + static createAddedConnection( + builder, + fromOffset, + fromLocation, + toOffset, + toLocation + ) { + AddedConnection.startAddedConnection(builder); + AddedConnection.addFrom(builder, fromOffset); + AddedConnection.addFromLocation(builder, fromLocation); + AddedConnection.addTo(builder, toOffset); + AddedConnection.addToLocation(builder, toLocation); + return AddedConnection.endAddedConnection(builder); + } + unpack() { + return new AddedConnectionT( + this.from(), + this.fromLocation(), + this.to(), + this.toLocation() + ); + } + unpackTo(_o) { + _o.from = this.from(); + _o.fromLocation = this.fromLocation(); + _o.to = this.to(); + _o.toLocation = this.toLocation(); + } +} +exports.AddedConnection = AddedConnection; +class AddedConnectionT { + constructor(from = null, fromLocation = 0.0, to = null, toLocation = 0.0) { + this.from = from; + this.fromLocation = fromLocation; + this.to = to; + this.toLocation = toLocation; + } + pack(builder) { + const from = this.from !== null ? builder.createString(this.from) : 0; + const to = this.to !== null ? builder.createString(this.to) : 0; + return AddedConnection.createAddedConnection( + builder, + from, + this.fromLocation, + to, + this.toLocation + ); + } +} +exports.AddedConnectionT = AddedConnectionT; +//# sourceMappingURL=added-connection.js.map diff --git a/network-monitor/dist/src/generated/topology/added-connection.js.map b/network-monitor/dist/src/generated/topology/added-connection.js.map new file mode 100644 index 000000000..b0db6c2d1 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/added-connection.js.map @@ -0,0 +1 @@ +{"version":3,"file":"added-connection.js","sourceRoot":"","sources":["../../../../src/generated/topology/added-connection.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/network-monitor/dist/src/generated/topology/controller-response.d.ts b/network-monitor/dist/src/generated/topology/controller-response.d.ts new file mode 100644 index 000000000..dfcfb4d69 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/controller-response.d.ts @@ -0,0 +1,48 @@ +import * as flatbuffers from "flatbuffers"; +import { ErrorT } from "../topology/error"; +import { OkT } from "../topology/ok"; +import { Response } from "../topology/response"; +export declare class ControllerResponse + implements flatbuffers.IUnpackableObject +{ + bb: flatbuffers.ByteBuffer | null; + bb_pos: number; + __init(i: number, bb: flatbuffers.ByteBuffer): ControllerResponse; + static getRootAsControllerResponse( + bb: flatbuffers.ByteBuffer, + obj?: ControllerResponse + ): ControllerResponse; + static getSizePrefixedRootAsControllerResponse( + bb: flatbuffers.ByteBuffer, + obj?: ControllerResponse + ): ControllerResponse; + responseType(): Response; + response(obj: any): any | null; + static startControllerResponse(builder: flatbuffers.Builder): void; + static addResponseType( + builder: flatbuffers.Builder, + responseType: Response + ): void; + static addResponse( + builder: flatbuffers.Builder, + responseOffset: flatbuffers.Offset + ): void; + static endControllerResponse( + builder: flatbuffers.Builder + ): flatbuffers.Offset; + static createControllerResponse( + builder: flatbuffers.Builder, + responseType: Response, + responseOffset: flatbuffers.Offset + ): flatbuffers.Offset; + unpack(): ControllerResponseT; + unpackTo(_o: ControllerResponseT): void; +} +export declare class ControllerResponseT + implements flatbuffers.IGeneratedObject +{ + responseType: Response; + response: ErrorT | OkT | null; + constructor(responseType?: Response, response?: ErrorT | OkT | null); + pack(builder: flatbuffers.Builder): flatbuffers.Offset; +} diff --git a/network-monitor/dist/src/generated/topology/controller-response.js b/network-monitor/dist/src/generated/topology/controller-response.js new file mode 100644 index 000000000..e8e3afc3c --- /dev/null +++ b/network-monitor/dist/src/generated/topology/controller-response.js @@ -0,0 +1,149 @@ +"use strict"; +// automatically generated by the FlatBuffers compiler, do not modify +var __createBinding = + (this && this.__createBinding) || + (Object.create + ? function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if ( + !desc || + ("get" in desc ? !m.__esModule : desc.writable || desc.configurable) + ) { + desc = { + enumerable: true, + get: function () { + return m[k]; + }, + }; + } + Object.defineProperty(o, k2, desc); + } + : function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; + }); +var __setModuleDefault = + (this && this.__setModuleDefault) || + (Object.create + ? function (o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + } + : function (o, v) { + o["default"] = v; + }); +var __importStar = + (this && this.__importStar) || + function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) + for (var k in mod) + if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) + __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; + }; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ControllerResponseT = exports.ControllerResponse = void 0; +const flatbuffers = __importStar(require("flatbuffers")); +const response_js_1 = require("../topology/response.js"); +class ControllerResponse { + constructor() { + this.bb = null; + this.bb_pos = 0; + } + __init(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; + } + static getRootAsControllerResponse(bb, obj) { + return (obj || new ControllerResponse()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + static getSizePrefixedRootAsControllerResponse(bb, obj) { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new ControllerResponse()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + responseType() { + const offset = this.bb.__offset(this.bb_pos, 4); + return offset + ? this.bb.readUint8(this.bb_pos + offset) + : response_js_1.Response.NONE; + } + response(obj) { + const offset = this.bb.__offset(this.bb_pos, 6); + return offset ? this.bb.__union(obj, this.bb_pos + offset) : null; + } + static startControllerResponse(builder) { + builder.startObject(2); + } + static addResponseType(builder, responseType) { + builder.addFieldInt8(0, responseType, response_js_1.Response.NONE); + } + static addResponse(builder, responseOffset) { + builder.addFieldOffset(1, responseOffset, 0); + } + static endControllerResponse(builder) { + const offset = builder.endObject(); + builder.requiredField(offset, 6); // response + return offset; + } + static createControllerResponse(builder, responseType, responseOffset) { + ControllerResponse.startControllerResponse(builder); + ControllerResponse.addResponseType(builder, responseType); + ControllerResponse.addResponse(builder, responseOffset); + return ControllerResponse.endControllerResponse(builder); + } + unpack() { + return new ControllerResponseT( + this.responseType(), + (() => { + const temp = (0, response_js_1.unionToResponse)( + this.responseType(), + this.response.bind(this) + ); + if (temp === null) { + return null; + } + return temp.unpack(); + })() + ); + } + unpackTo(_o) { + _o.responseType = this.responseType(); + _o.response = (() => { + const temp = (0, response_js_1.unionToResponse)( + this.responseType(), + this.response.bind(this) + ); + if (temp === null) { + return null; + } + return temp.unpack(); + })(); + } +} +exports.ControllerResponse = ControllerResponse; +class ControllerResponseT { + constructor(responseType = response_js_1.Response.NONE, response = null) { + this.responseType = responseType; + this.response = response; + } + pack(builder) { + const response = builder.createObjectOffset(this.response); + return ControllerResponse.createControllerResponse( + builder, + this.responseType, + response + ); + } +} +exports.ControllerResponseT = ControllerResponseT; +//# sourceMappingURL=controller-response.js.map diff --git a/network-monitor/dist/src/generated/topology/controller-response.js.map b/network-monitor/dist/src/generated/topology/controller-response.js.map new file mode 100644 index 000000000..20c5a477b --- /dev/null +++ b/network-monitor/dist/src/generated/topology/controller-response.js.map @@ -0,0 +1 @@ +{"version":3,"file":"controller-response.js","sourceRoot":"","sources":["../../../../src/generated/topology/controller-response.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/network-monitor/dist/src/generated/topology/error.d.ts b/network-monitor/dist/src/generated/topology/error.d.ts new file mode 100644 index 000000000..ec313b084 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/error.d.ts @@ -0,0 +1,30 @@ +import * as flatbuffers from "flatbuffers"; +export declare class Error implements flatbuffers.IUnpackableObject { + bb: flatbuffers.ByteBuffer | null; + bb_pos: number; + __init(i: number, bb: flatbuffers.ByteBuffer): Error; + static getRootAsError(bb: flatbuffers.ByteBuffer, obj?: Error): Error; + static getSizePrefixedRootAsError( + bb: flatbuffers.ByteBuffer, + obj?: Error + ): Error; + message(): string | null; + message(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; + static startError(builder: flatbuffers.Builder): void; + static addMessage( + builder: flatbuffers.Builder, + messageOffset: flatbuffers.Offset + ): void; + static endError(builder: flatbuffers.Builder): flatbuffers.Offset; + static createError( + builder: flatbuffers.Builder, + messageOffset: flatbuffers.Offset + ): flatbuffers.Offset; + unpack(): ErrorT; + unpackTo(_o: ErrorT): void; +} +export declare class ErrorT implements flatbuffers.IGeneratedObject { + message: string | Uint8Array | null; + constructor(message?: string | Uint8Array | null); + pack(builder: flatbuffers.Builder): flatbuffers.Offset; +} diff --git a/network-monitor/dist/src/generated/topology/error.js b/network-monitor/dist/src/generated/topology/error.js new file mode 100644 index 000000000..03fa763cd --- /dev/null +++ b/network-monitor/dist/src/generated/topology/error.js @@ -0,0 +1,114 @@ +"use strict"; +// automatically generated by the FlatBuffers compiler, do not modify +var __createBinding = + (this && this.__createBinding) || + (Object.create + ? function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if ( + !desc || + ("get" in desc ? !m.__esModule : desc.writable || desc.configurable) + ) { + desc = { + enumerable: true, + get: function () { + return m[k]; + }, + }; + } + Object.defineProperty(o, k2, desc); + } + : function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; + }); +var __setModuleDefault = + (this && this.__setModuleDefault) || + (Object.create + ? function (o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + } + : function (o, v) { + o["default"] = v; + }); +var __importStar = + (this && this.__importStar) || + function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) + for (var k in mod) + if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) + __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; + }; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ErrorT = exports.Error = void 0; +const flatbuffers = __importStar(require("flatbuffers")); +class Error { + constructor() { + this.bb = null; + this.bb_pos = 0; + } + __init(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; + } + static getRootAsError(bb, obj) { + return (obj || new Error()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + static getSizePrefixedRootAsError(bb, obj) { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new Error()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + message(optionalEncoding) { + const offset = this.bb.__offset(this.bb_pos, 4); + return offset + ? this.bb.__string(this.bb_pos + offset, optionalEncoding) + : null; + } + static startError(builder) { + builder.startObject(1); + } + static addMessage(builder, messageOffset) { + builder.addFieldOffset(0, messageOffset, 0); + } + static endError(builder) { + const offset = builder.endObject(); + builder.requiredField(offset, 4); // message + return offset; + } + static createError(builder, messageOffset) { + Error.startError(builder); + Error.addMessage(builder, messageOffset); + return Error.endError(builder); + } + unpack() { + return new ErrorT(this.message()); + } + unpackTo(_o) { + _o.message = this.message(); + } +} +exports.Error = Error; +class ErrorT { + constructor(message = null) { + this.message = message; + } + pack(builder) { + const message = + this.message !== null ? builder.createString(this.message) : 0; + return Error.createError(builder, message); + } +} +exports.ErrorT = ErrorT; +//# sourceMappingURL=error.js.map diff --git a/network-monitor/dist/src/generated/topology/error.js.map b/network-monitor/dist/src/generated/topology/error.js.map new file mode 100644 index 000000000..9832fbb1d --- /dev/null +++ b/network-monitor/dist/src/generated/topology/error.js.map @@ -0,0 +1 @@ +{"version":3,"file":"error.js","sourceRoot":"","sources":["../../../../src/generated/topology/error.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/network-monitor/dist/src/generated/topology/ok.d.ts b/network-monitor/dist/src/generated/topology/ok.d.ts new file mode 100644 index 000000000..a8af2404b --- /dev/null +++ b/network-monitor/dist/src/generated/topology/ok.d.ts @@ -0,0 +1,27 @@ +import * as flatbuffers from "flatbuffers"; +export declare class Ok implements flatbuffers.IUnpackableObject { + bb: flatbuffers.ByteBuffer | null; + bb_pos: number; + __init(i: number, bb: flatbuffers.ByteBuffer): Ok; + static getRootAsOk(bb: flatbuffers.ByteBuffer, obj?: Ok): Ok; + static getSizePrefixedRootAsOk(bb: flatbuffers.ByteBuffer, obj?: Ok): Ok; + message(): string | null; + message(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; + static startOk(builder: flatbuffers.Builder): void; + static addMessage( + builder: flatbuffers.Builder, + messageOffset: flatbuffers.Offset + ): void; + static endOk(builder: flatbuffers.Builder): flatbuffers.Offset; + static createOk( + builder: flatbuffers.Builder, + messageOffset: flatbuffers.Offset + ): flatbuffers.Offset; + unpack(): OkT; + unpackTo(_o: OkT): void; +} +export declare class OkT implements flatbuffers.IGeneratedObject { + message: string | Uint8Array | null; + constructor(message?: string | Uint8Array | null); + pack(builder: flatbuffers.Builder): flatbuffers.Offset; +} diff --git a/network-monitor/dist/src/generated/topology/ok.js b/network-monitor/dist/src/generated/topology/ok.js new file mode 100644 index 000000000..b51b70c07 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/ok.js @@ -0,0 +1,113 @@ +"use strict"; +// automatically generated by the FlatBuffers compiler, do not modify +var __createBinding = + (this && this.__createBinding) || + (Object.create + ? function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if ( + !desc || + ("get" in desc ? !m.__esModule : desc.writable || desc.configurable) + ) { + desc = { + enumerable: true, + get: function () { + return m[k]; + }, + }; + } + Object.defineProperty(o, k2, desc); + } + : function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; + }); +var __setModuleDefault = + (this && this.__setModuleDefault) || + (Object.create + ? function (o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + } + : function (o, v) { + o["default"] = v; + }); +var __importStar = + (this && this.__importStar) || + function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) + for (var k in mod) + if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) + __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; + }; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.OkT = exports.Ok = void 0; +const flatbuffers = __importStar(require("flatbuffers")); +class Ok { + constructor() { + this.bb = null; + this.bb_pos = 0; + } + __init(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; + } + static getRootAsOk(bb, obj) { + return (obj || new Ok()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + static getSizePrefixedRootAsOk(bb, obj) { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new Ok()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + message(optionalEncoding) { + const offset = this.bb.__offset(this.bb_pos, 4); + return offset + ? this.bb.__string(this.bb_pos + offset, optionalEncoding) + : null; + } + static startOk(builder) { + builder.startObject(1); + } + static addMessage(builder, messageOffset) { + builder.addFieldOffset(0, messageOffset, 0); + } + static endOk(builder) { + const offset = builder.endObject(); + return offset; + } + static createOk(builder, messageOffset) { + Ok.startOk(builder); + Ok.addMessage(builder, messageOffset); + return Ok.endOk(builder); + } + unpack() { + return new OkT(this.message()); + } + unpackTo(_o) { + _o.message = this.message(); + } +} +exports.Ok = Ok; +class OkT { + constructor(message = null) { + this.message = message; + } + pack(builder) { + const message = + this.message !== null ? builder.createString(this.message) : 0; + return Ok.createOk(builder, message); + } +} +exports.OkT = OkT; +//# sourceMappingURL=ok.js.map diff --git a/network-monitor/dist/src/generated/topology/ok.js.map b/network-monitor/dist/src/generated/topology/ok.js.map new file mode 100644 index 000000000..cad776b92 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/ok.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ok.js","sourceRoot":"","sources":["../../../../src/generated/topology/ok.ts"],"names":[],"mappings":";AAAA,qEAAqE;;;;;;;;;;;;;;;;;;;;;;;;;;AAErE,yDAA2C;AAE3C,MAAa,EAAE;IAAf;QACE,OAAE,GAAkC,IAAI,CAAC;QACzC,WAAM,GAAG,CAAC,CAAC;IA+Db,CAAC;IA9DC,MAAM,CAAC,CAAS,EAAE,EAA0B;QAC1C,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAChB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,EAA0B,EAAE,GAAQ;QACrD,OAAO,CAAC,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAC7B,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAC3C,EAAE,CACH,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,uBAAuB,CAAC,EAA0B,EAAE,GAAQ;QACjE,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,WAAW,CAAC,kBAAkB,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAC7B,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAC3C,EAAE,CACH,CAAC;IACJ,CAAC;IAID,OAAO,CAAC,gBAAsB;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,EAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACjD,OAAO,MAAM;YACX,CAAC,CAAC,IAAI,CAAC,EAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,GAAG,MAAM,EAAE,gBAAgB,CAAC;YAC3D,CAAC,CAAC,IAAI,CAAC;IACX,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,OAA4B;QACzC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IAED,MAAM,CAAC,UAAU,CACf,OAA4B,EAC5B,aAAiC;QAEjC,OAAO,CAAC,cAAc,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,OAA4B;QACvC,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;QACnC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,CAAC,QAAQ,CACb,OAA4B,EAC5B,aAAiC;QAEjC,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACpB,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QACtC,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,QAAQ,CAAC,EAAO;QACd,EAAE,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IAC9B,CAAC;CACF;AAjED,gBAiEC;AAED,MAAa,GAAG;IACd,YAAmB,UAAsC,IAAI;QAA1C,YAAO,GAAP,OAAO,CAAmC;IAAG,CAAC;IAEjE,IAAI,CAAC,OAA4B;QAC/B,MAAM,OAAO,GACX,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,OAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAElE,OAAO,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;CACF;AATD,kBASC"} \ No newline at end of file diff --git a/network-monitor/dist/src/generated/topology/peer-change-type.d.ts b/network-monitor/dist/src/generated/topology/peer-change-type.d.ts new file mode 100644 index 000000000..d0f6df9ee --- /dev/null +++ b/network-monitor/dist/src/generated/topology/peer-change-type.d.ts @@ -0,0 +1,23 @@ +import { AddedConnection } from "../topology/added-connection"; +import { Error } from "../topology/error"; +import { RemovedConnection } from "../topology/removed-connection"; +export declare enum PeerChangeType { + NONE = 0, + AddedConnection = 1, + RemovedConnection = 2, + Error = 3, +} +export declare function unionToPeerChangeType( + type: PeerChangeType, + accessor: ( + obj: AddedConnection | Error | RemovedConnection + ) => AddedConnection | Error | RemovedConnection | null +): AddedConnection | Error | RemovedConnection | null; +export declare function unionListToPeerChangeType( + type: PeerChangeType, + accessor: ( + index: number, + obj: AddedConnection | Error | RemovedConnection + ) => AddedConnection | Error | RemovedConnection | null, + index: number +): AddedConnection | Error | RemovedConnection | null; diff --git a/network-monitor/dist/src/generated/topology/peer-change-type.js b/network-monitor/dist/src/generated/topology/peer-change-type.js new file mode 100644 index 000000000..b6ac9c3ce --- /dev/null +++ b/network-monitor/dist/src/generated/topology/peer-change-type.js @@ -0,0 +1,49 @@ +"use strict"; +// automatically generated by the FlatBuffers compiler, do not modify +Object.defineProperty(exports, "__esModule", { value: true }); +exports.unionListToPeerChangeType = + exports.unionToPeerChangeType = + exports.PeerChangeType = + void 0; +const added_connection_js_1 = require("../topology/added-connection.js"); +const error_js_1 = require("../topology/error.js"); +const removed_connection_js_1 = require("../topology/removed-connection.js"); +var PeerChangeType; +(function (PeerChangeType) { + PeerChangeType[(PeerChangeType["NONE"] = 0)] = "NONE"; + PeerChangeType[(PeerChangeType["AddedConnection"] = 1)] = "AddedConnection"; + PeerChangeType[(PeerChangeType["RemovedConnection"] = 2)] = + "RemovedConnection"; + PeerChangeType[(PeerChangeType["Error"] = 3)] = "Error"; +})((PeerChangeType = exports.PeerChangeType || (exports.PeerChangeType = {}))); +function unionToPeerChangeType(type, accessor) { + switch (PeerChangeType[type]) { + case "NONE": + return null; + case "AddedConnection": + return accessor(new added_connection_js_1.AddedConnection()); + case "RemovedConnection": + return accessor(new removed_connection_js_1.RemovedConnection()); + case "Error": + return accessor(new error_js_1.Error()); + default: + return null; + } +} +exports.unionToPeerChangeType = unionToPeerChangeType; +function unionListToPeerChangeType(type, accessor, index) { + switch (PeerChangeType[type]) { + case "NONE": + return null; + case "AddedConnection": + return accessor(index, new added_connection_js_1.AddedConnection()); + case "RemovedConnection": + return accessor(index, new removed_connection_js_1.RemovedConnection()); + case "Error": + return accessor(index, new error_js_1.Error()); + default: + return null; + } +} +exports.unionListToPeerChangeType = unionListToPeerChangeType; +//# sourceMappingURL=peer-change-type.js.map diff --git a/network-monitor/dist/src/generated/topology/peer-change-type.js.map b/network-monitor/dist/src/generated/topology/peer-change-type.js.map new file mode 100644 index 000000000..cd02fb8ae --- /dev/null +++ b/network-monitor/dist/src/generated/topology/peer-change-type.js.map @@ -0,0 +1 @@ +{"version":3,"file":"peer-change-type.js","sourceRoot":"","sources":["../../../../src/generated/topology/peer-change-type.ts"],"names":[],"mappings":";AAAA,qEAAqE;;;AAErE,yEAGyC;AACzC,mDAAqD;AACrD,6EAG2C;AAE3C,IAAY,cAKX;AALD,WAAY,cAAc;IACxB,mDAAQ,CAAA;IACR,yEAAmB,CAAA;IACnB,6EAAqB,CAAA;IACrB,qDAAS,CAAA;AACX,CAAC,EALW,cAAc,GAAd,sBAAc,KAAd,sBAAc,QAKzB;AAED,SAAgB,qBAAqB,CACnC,IAAoB,EACpB,QAEuD;IAEvD,QAAQ,cAAc,CAAC,IAAI,CAAC,EAAE;QAC5B,KAAK,MAAM;YACT,OAAO,IAAI,CAAC;QACd,KAAK,iBAAiB;YACpB,OAAO,QAAQ,CAAC,IAAI,qCAAe,EAAE,CAAqB,CAAC;QAC7D,KAAK,mBAAmB;YACtB,OAAO,QAAQ,CAAC,IAAI,yCAAiB,EAAE,CAAuB,CAAC;QACjE,KAAK,OAAO;YACV,OAAO,QAAQ,CAAC,IAAI,gBAAK,EAAE,CAAW,CAAC;QACzC;YACE,OAAO,IAAI,CAAC;KACf;AACH,CAAC;AAlBD,sDAkBC;AAED,SAAgB,yBAAyB,CACvC,IAAoB,EACpB,QAGuD,EACvD,KAAa;IAEb,QAAQ,cAAc,CAAC,IAAI,CAAC,EAAE;QAC5B,KAAK,MAAM;YACT,OAAO,IAAI,CAAC;QACd,KAAK,iBAAiB;YACpB,OAAO,QAAQ,CAAC,KAAK,EAAE,IAAI,qCAAe,EAAE,CAAqB,CAAC;QACpE,KAAK,mBAAmB;YACtB,OAAO,QAAQ,CAAC,KAAK,EAAE,IAAI,yCAAiB,EAAE,CAAuB,CAAC;QACxE,KAAK,OAAO;YACV,OAAO,QAAQ,CAAC,KAAK,EAAE,IAAI,gBAAK,EAAE,CAAW,CAAC;QAChD;YACE,OAAO,IAAI,CAAC;KACf;AACH,CAAC;AApBD,8DAoBC"} \ No newline at end of file diff --git a/network-monitor/dist/src/generated/topology/peer-change.d.ts b/network-monitor/dist/src/generated/topology/peer-change.d.ts new file mode 100644 index 000000000..f0bb3b061 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/peer-change.d.ts @@ -0,0 +1,68 @@ +import * as flatbuffers from "flatbuffers"; +import { + AddedConnection, + AddedConnectionT, +} from "../topology/added-connection"; +import { ErrorT } from "../topology/error"; +import { PeerChangeType } from "../topology/peer-change-type"; +import { RemovedConnectionT } from "../topology/removed-connection"; +export declare class PeerChange + implements flatbuffers.IUnpackableObject +{ + bb: flatbuffers.ByteBuffer | null; + bb_pos: number; + __init(i: number, bb: flatbuffers.ByteBuffer): PeerChange; + static getRootAsPeerChange( + bb: flatbuffers.ByteBuffer, + obj?: PeerChange + ): PeerChange; + static getSizePrefixedRootAsPeerChange( + bb: flatbuffers.ByteBuffer, + obj?: PeerChange + ): PeerChange; + currentState(index: number, obj?: AddedConnection): AddedConnection | null; + currentStateLength(): number; + changeType(): PeerChangeType; + change(obj: any): any | null; + static startPeerChange(builder: flatbuffers.Builder): void; + static addCurrentState( + builder: flatbuffers.Builder, + currentStateOffset: flatbuffers.Offset + ): void; + static createCurrentStateVector( + builder: flatbuffers.Builder, + data: flatbuffers.Offset[] + ): flatbuffers.Offset; + static startCurrentStateVector( + builder: flatbuffers.Builder, + numElems: number + ): void; + static addChangeType( + builder: flatbuffers.Builder, + changeType: PeerChangeType + ): void; + static addChange( + builder: flatbuffers.Builder, + changeOffset: flatbuffers.Offset + ): void; + static endPeerChange(builder: flatbuffers.Builder): flatbuffers.Offset; + static createPeerChange( + builder: flatbuffers.Builder, + currentStateOffset: flatbuffers.Offset, + changeType: PeerChangeType, + changeOffset: flatbuffers.Offset + ): flatbuffers.Offset; + unpack(): PeerChangeT; + unpackTo(_o: PeerChangeT): void; +} +export declare class PeerChangeT implements flatbuffers.IGeneratedObject { + currentState: AddedConnectionT[]; + changeType: PeerChangeType; + change: AddedConnectionT | ErrorT | RemovedConnectionT | null; + constructor( + currentState?: AddedConnectionT[], + changeType?: PeerChangeType, + change?: AddedConnectionT | ErrorT | RemovedConnectionT | null + ); + pack(builder: flatbuffers.Builder): flatbuffers.Offset; +} diff --git a/network-monitor/dist/src/generated/topology/peer-change.js b/network-monitor/dist/src/generated/topology/peer-change.js new file mode 100644 index 000000000..6b75c600e --- /dev/null +++ b/network-monitor/dist/src/generated/topology/peer-change.js @@ -0,0 +1,205 @@ +"use strict"; +// automatically generated by the FlatBuffers compiler, do not modify +var __createBinding = + (this && this.__createBinding) || + (Object.create + ? function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if ( + !desc || + ("get" in desc ? !m.__esModule : desc.writable || desc.configurable) + ) { + desc = { + enumerable: true, + get: function () { + return m[k]; + }, + }; + } + Object.defineProperty(o, k2, desc); + } + : function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; + }); +var __setModuleDefault = + (this && this.__setModuleDefault) || + (Object.create + ? function (o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + } + : function (o, v) { + o["default"] = v; + }); +var __importStar = + (this && this.__importStar) || + function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) + for (var k in mod) + if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) + __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; + }; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PeerChangeT = exports.PeerChange = void 0; +const flatbuffers = __importStar(require("flatbuffers")); +const added_connection_js_1 = require("../topology/added-connection.js"); +const peer_change_type_js_1 = require("../topology/peer-change-type.js"); +class PeerChange { + constructor() { + this.bb = null; + this.bb_pos = 0; + } + __init(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; + } + static getRootAsPeerChange(bb, obj) { + return (obj || new PeerChange()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + static getSizePrefixedRootAsPeerChange(bb, obj) { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new PeerChange()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + currentState(index, obj) { + const offset = this.bb.__offset(this.bb_pos, 4); + return offset + ? (obj || new added_connection_js_1.AddedConnection()).__init( + this.bb.__indirect( + this.bb.__vector(this.bb_pos + offset) + index * 4 + ), + this.bb + ) + : null; + } + currentStateLength() { + const offset = this.bb.__offset(this.bb_pos, 4); + return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; + } + changeType() { + const offset = this.bb.__offset(this.bb_pos, 6); + return offset + ? this.bb.readUint8(this.bb_pos + offset) + : peer_change_type_js_1.PeerChangeType.NONE; + } + change(obj) { + const offset = this.bb.__offset(this.bb_pos, 8); + return offset ? this.bb.__union(obj, this.bb_pos + offset) : null; + } + static startPeerChange(builder) { + builder.startObject(3); + } + static addCurrentState(builder, currentStateOffset) { + builder.addFieldOffset(0, currentStateOffset, 0); + } + static createCurrentStateVector(builder, data) { + builder.startVector(4, data.length, 4); + for (let i = data.length - 1; i >= 0; i--) { + builder.addOffset(data[i]); + } + return builder.endVector(); + } + static startCurrentStateVector(builder, numElems) { + builder.startVector(4, numElems, 4); + } + static addChangeType(builder, changeType) { + builder.addFieldInt8( + 1, + changeType, + peer_change_type_js_1.PeerChangeType.NONE + ); + } + static addChange(builder, changeOffset) { + builder.addFieldOffset(2, changeOffset, 0); + } + static endPeerChange(builder) { + const offset = builder.endObject(); + return offset; + } + static createPeerChange( + builder, + currentStateOffset, + changeType, + changeOffset + ) { + PeerChange.startPeerChange(builder); + PeerChange.addCurrentState(builder, currentStateOffset); + PeerChange.addChangeType(builder, changeType); + PeerChange.addChange(builder, changeOffset); + return PeerChange.endPeerChange(builder); + } + unpack() { + return new PeerChangeT( + this.bb.createObjList( + this.currentState.bind(this), + this.currentStateLength() + ), + this.changeType(), + (() => { + const temp = (0, peer_change_type_js_1.unionToPeerChangeType)( + this.changeType(), + this.change.bind(this) + ); + if (temp === null) { + return null; + } + return temp.unpack(); + })() + ); + } + unpackTo(_o) { + _o.currentState = this.bb.createObjList( + this.currentState.bind(this), + this.currentStateLength() + ); + _o.changeType = this.changeType(); + _o.change = (() => { + const temp = (0, peer_change_type_js_1.unionToPeerChangeType)( + this.changeType(), + this.change.bind(this) + ); + if (temp === null) { + return null; + } + return temp.unpack(); + })(); + } +} +exports.PeerChange = PeerChange; +class PeerChangeT { + constructor( + currentState = [], + changeType = peer_change_type_js_1.PeerChangeType.NONE, + change = null + ) { + this.currentState = currentState; + this.changeType = changeType; + this.change = change; + } + pack(builder) { + const currentState = PeerChange.createCurrentStateVector( + builder, + builder.createObjectOffsetList(this.currentState) + ); + const change = builder.createObjectOffset(this.change); + return PeerChange.createPeerChange( + builder, + currentState, + this.changeType, + change + ); + } +} +exports.PeerChangeT = PeerChangeT; +//# sourceMappingURL=peer-change.js.map diff --git a/network-monitor/dist/src/generated/topology/peer-change.js.map b/network-monitor/dist/src/generated/topology/peer-change.js.map new file mode 100644 index 000000000..d301e3a72 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/peer-change.js.map @@ -0,0 +1 @@ +{"version":3,"file":"peer-change.js","sourceRoot":"","sources":["../../../../src/generated/topology/peer-change.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/network-monitor/dist/src/generated/topology/removed-connection.d.ts b/network-monitor/dist/src/generated/topology/removed-connection.d.ts new file mode 100644 index 000000000..d74fdaeb7 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/removed-connection.d.ts @@ -0,0 +1,48 @@ +import * as flatbuffers from "flatbuffers"; +export declare class RemovedConnection + implements flatbuffers.IUnpackableObject +{ + bb: flatbuffers.ByteBuffer | null; + bb_pos: number; + __init(i: number, bb: flatbuffers.ByteBuffer): RemovedConnection; + static getRootAsRemovedConnection( + bb: flatbuffers.ByteBuffer, + obj?: RemovedConnection + ): RemovedConnection; + static getSizePrefixedRootAsRemovedConnection( + bb: flatbuffers.ByteBuffer, + obj?: RemovedConnection + ): RemovedConnection; + at(): string | null; + at(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; + from(): string | null; + from(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; + static startRemovedConnection(builder: flatbuffers.Builder): void; + static addAt( + builder: flatbuffers.Builder, + atOffset: flatbuffers.Offset + ): void; + static addFrom( + builder: flatbuffers.Builder, + fromOffset: flatbuffers.Offset + ): void; + static endRemovedConnection(builder: flatbuffers.Builder): flatbuffers.Offset; + static createRemovedConnection( + builder: flatbuffers.Builder, + atOffset: flatbuffers.Offset, + fromOffset: flatbuffers.Offset + ): flatbuffers.Offset; + unpack(): RemovedConnectionT; + unpackTo(_o: RemovedConnectionT): void; +} +export declare class RemovedConnectionT + implements flatbuffers.IGeneratedObject +{ + at: string | Uint8Array | null; + from: string | Uint8Array | null; + constructor( + at?: string | Uint8Array | null, + from?: string | Uint8Array | null + ); + pack(builder: flatbuffers.Builder): flatbuffers.Offset; +} diff --git a/network-monitor/dist/src/generated/topology/removed-connection.js b/network-monitor/dist/src/generated/topology/removed-connection.js new file mode 100644 index 000000000..23750a887 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/removed-connection.js @@ -0,0 +1,127 @@ +"use strict"; +// automatically generated by the FlatBuffers compiler, do not modify +var __createBinding = + (this && this.__createBinding) || + (Object.create + ? function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if ( + !desc || + ("get" in desc ? !m.__esModule : desc.writable || desc.configurable) + ) { + desc = { + enumerable: true, + get: function () { + return m[k]; + }, + }; + } + Object.defineProperty(o, k2, desc); + } + : function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; + }); +var __setModuleDefault = + (this && this.__setModuleDefault) || + (Object.create + ? function (o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + } + : function (o, v) { + o["default"] = v; + }); +var __importStar = + (this && this.__importStar) || + function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) + for (var k in mod) + if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) + __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; + }; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RemovedConnectionT = exports.RemovedConnection = void 0; +const flatbuffers = __importStar(require("flatbuffers")); +class RemovedConnection { + constructor() { + this.bb = null; + this.bb_pos = 0; + } + __init(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; + } + static getRootAsRemovedConnection(bb, obj) { + return (obj || new RemovedConnection()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + static getSizePrefixedRootAsRemovedConnection(bb, obj) { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new RemovedConnection()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + at(optionalEncoding) { + const offset = this.bb.__offset(this.bb_pos, 4); + return offset + ? this.bb.__string(this.bb_pos + offset, optionalEncoding) + : null; + } + from(optionalEncoding) { + const offset = this.bb.__offset(this.bb_pos, 6); + return offset + ? this.bb.__string(this.bb_pos + offset, optionalEncoding) + : null; + } + static startRemovedConnection(builder) { + builder.startObject(2); + } + static addAt(builder, atOffset) { + builder.addFieldOffset(0, atOffset, 0); + } + static addFrom(builder, fromOffset) { + builder.addFieldOffset(1, fromOffset, 0); + } + static endRemovedConnection(builder) { + const offset = builder.endObject(); + builder.requiredField(offset, 4); // at + builder.requiredField(offset, 6); // from + return offset; + } + static createRemovedConnection(builder, atOffset, fromOffset) { + RemovedConnection.startRemovedConnection(builder); + RemovedConnection.addAt(builder, atOffset); + RemovedConnection.addFrom(builder, fromOffset); + return RemovedConnection.endRemovedConnection(builder); + } + unpack() { + return new RemovedConnectionT(this.at(), this.from()); + } + unpackTo(_o) { + _o.at = this.at(); + _o.from = this.from(); + } +} +exports.RemovedConnection = RemovedConnection; +class RemovedConnectionT { + constructor(at = null, from = null) { + this.at = at; + this.from = from; + } + pack(builder) { + const at = this.at !== null ? builder.createString(this.at) : 0; + const from = this.from !== null ? builder.createString(this.from) : 0; + return RemovedConnection.createRemovedConnection(builder, at, from); + } +} +exports.RemovedConnectionT = RemovedConnectionT; +//# sourceMappingURL=removed-connection.js.map diff --git a/network-monitor/dist/src/generated/topology/removed-connection.js.map b/network-monitor/dist/src/generated/topology/removed-connection.js.map new file mode 100644 index 000000000..29bb96064 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/removed-connection.js.map @@ -0,0 +1 @@ +{"version":3,"file":"removed-connection.js","sourceRoot":"","sources":["../../../../src/generated/topology/removed-connection.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/network-monitor/dist/src/generated/topology/response.d.ts b/network-monitor/dist/src/generated/topology/response.d.ts new file mode 100644 index 000000000..72c0f2570 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/response.d.ts @@ -0,0 +1,16 @@ +import { Error } from "../topology/error"; +import { Ok } from "../topology/ok"; +export declare enum Response { + NONE = 0, + Error = 1, + Ok = 2, +} +export declare function unionToResponse( + type: Response, + accessor: (obj: Error | Ok) => Error | Ok | null +): Error | Ok | null; +export declare function unionListToResponse( + type: Response, + accessor: (index: number, obj: Error | Ok) => Error | Ok | null, + index: number +): Error | Ok | null; diff --git a/network-monitor/dist/src/generated/topology/response.js b/network-monitor/dist/src/generated/topology/response.js new file mode 100644 index 000000000..e200b0a05 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/response.js @@ -0,0 +1,42 @@ +"use strict"; +// automatically generated by the FlatBuffers compiler, do not modify +Object.defineProperty(exports, "__esModule", { value: true }); +exports.unionListToResponse = + exports.unionToResponse = + exports.Response = + void 0; +const error_js_1 = require("../topology/error.js"); +const ok_js_1 = require("../topology/ok.js"); +var Response; +(function (Response) { + Response[(Response["NONE"] = 0)] = "NONE"; + Response[(Response["Error"] = 1)] = "Error"; + Response[(Response["Ok"] = 2)] = "Ok"; +})((Response = exports.Response || (exports.Response = {}))); +function unionToResponse(type, accessor) { + switch (Response[type]) { + case "NONE": + return null; + case "Error": + return accessor(new error_js_1.Error()); + case "Ok": + return accessor(new ok_js_1.Ok()); + default: + return null; + } +} +exports.unionToResponse = unionToResponse; +function unionListToResponse(type, accessor, index) { + switch (Response[type]) { + case "NONE": + return null; + case "Error": + return accessor(index, new error_js_1.Error()); + case "Ok": + return accessor(index, new ok_js_1.Ok()); + default: + return null; + } +} +exports.unionListToResponse = unionListToResponse; +//# sourceMappingURL=response.js.map diff --git a/network-monitor/dist/src/generated/topology/response.js.map b/network-monitor/dist/src/generated/topology/response.js.map new file mode 100644 index 000000000..7d0b50b91 --- /dev/null +++ b/network-monitor/dist/src/generated/topology/response.js.map @@ -0,0 +1 @@ +{"version":3,"file":"response.js","sourceRoot":"","sources":["../../../../src/generated/topology/response.ts"],"names":[],"mappings":";AAAA,qEAAqE;;;AAErE,mDAAqD;AACrD,6CAA4C;AAE5C,IAAY,QAIX;AAJD,WAAY,QAAQ;IAClB,uCAAQ,CAAA;IACR,yCAAS,CAAA;IACT,mCAAM,CAAA;AACR,CAAC,EAJW,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QAInB;AAED,SAAgB,eAAe,CAC7B,IAAc,EACd,QAAgD;IAEhD,QAAQ,QAAQ,CAAC,IAAI,CAAC,EAAE;QACtB,KAAK,MAAM;YACT,OAAO,IAAI,CAAC;QACd,KAAK,OAAO;YACV,OAAO,QAAQ,CAAC,IAAI,gBAAK,EAAE,CAAW,CAAC;QACzC,KAAK,IAAI;YACP,OAAO,QAAQ,CAAC,IAAI,UAAE,EAAE,CAAQ,CAAC;QACnC;YACE,OAAO,IAAI,CAAC;KACf;AACH,CAAC;AAdD,0CAcC;AAED,SAAgB,mBAAmB,CACjC,IAAc,EACd,QAA+D,EAC/D,KAAa;IAEb,QAAQ,QAAQ,CAAC,IAAI,CAAC,EAAE;QACtB,KAAK,MAAM;YACT,OAAO,IAAI,CAAC;QACd,KAAK,OAAO;YACV,OAAO,QAAQ,CAAC,KAAK,EAAE,IAAI,gBAAK,EAAE,CAAW,CAAC;QAChD,KAAK,IAAI;YACP,OAAO,QAAQ,CAAC,KAAK,EAAE,IAAI,UAAE,EAAE,CAAQ,CAAC;QAC1C;YACE,OAAO,IAAI,CAAC;KACf;AACH,CAAC;AAfD,kDAeC"} \ No newline at end of file diff --git a/network-monitor/dist/src/topology.d.ts b/network-monitor/dist/src/topology.d.ts new file mode 100644 index 000000000..00a6c6b9c --- /dev/null +++ b/network-monitor/dist/src/topology.d.ts @@ -0,0 +1,34 @@ +import * as fbTopology from "./generated/topology"; +export declare let peers: PeerList; +interface PeerList { + [id: string]: Peer; +} +interface Peer { + id: string; + currentLocation: number; + connections: Connection[]; + history: ChangeInfo[]; + locationHistory: { + location: number; + timestamp: number; + }[]; +} +interface Connection { + id: string; + location: number; +} +interface ChangeInfo { + type: "Added" | "Removed"; + from: Connection; + to: Connection; + timestamp: number; +} +export declare function handleChange(peerChange: fbTopology.PeerChange): void; +export declare function handleAddedConnection( + peerChange: fbTopology.AddedConnectionT +): void; +export declare function handleRemovedConnection( + peerChange: fbTopology.RemovedConnectionT +): void; +export declare function showConnections(peer: Peer): void; +export {}; diff --git a/network-monitor/dist/src/topology.js b/network-monitor/dist/src/topology.js new file mode 100644 index 000000000..c24810a15 --- /dev/null +++ b/network-monitor/dist/src/topology.js @@ -0,0 +1,241 @@ +"use strict"; +var __createBinding = + (this && this.__createBinding) || + (Object.create + ? function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if ( + !desc || + ("get" in desc ? !m.__esModule : desc.writable || desc.configurable) + ) { + desc = { + enumerable: true, + get: function () { + return m[k]; + }, + }; + } + Object.defineProperty(o, k2, desc); + } + : function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; + }); +var __setModuleDefault = + (this && this.__setModuleDefault) || + (Object.create + ? function (o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + } + : function (o, v) { + o["default"] = v; + }); +var __importStar = + (this && this.__importStar) || + function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) + for (var k in mod) + if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) + __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; + }; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.showConnections = + exports.handleRemovedConnection = + exports.handleAddedConnection = + exports.handleChange = + exports.peers = + void 0; +const fbTopology = __importStar(require("./generated/topology")); +exports.peers = {}; +function handleChange(peerChange) { + const unpacked = peerChange.unpack(); + try { + switch (unpacked.changeType) { + case fbTopology.PeerChangeType.AddedConnection: + handleAddedConnection(unpacked.change); + break; + case fbTopology.PeerChangeType.RemovedConnection: + handleRemovedConnection(unpacked.change); + break; + } + } catch (e) { + console.error(e); + } + updateTable(); +} +exports.handleChange = handleChange; +function handleAddedConnection(peerChange) { + const added = peerChange; + const fromAdded = added.from.toString(); + const toAdded = added.to.toString(); + const fromConnection = { + id: fromAdded, + location: added.fromLocation, + }; + const toConnection = { + id: toAdded, + location: added.toLocation, + }; + if (exports.peers[fromAdded]) { + if (exports.peers[fromAdded].currentLocation !== added.fromLocation) { + exports.peers[fromAdded].locationHistory.push({ + location: exports.peers[fromAdded].currentLocation, + timestamp: Date.now(), + }); + exports.peers[fromAdded].currentLocation = added.fromLocation; + } + if (exports.peers[toAdded].currentLocation !== added.toLocation) { + exports.peers[toAdded].locationHistory.push({ + location: exports.peers[toAdded].currentLocation, + timestamp: Date.now(), + }); + exports.peers[toAdded].currentLocation = added.toLocation; + } + exports.peers[fromAdded].connections.push(toConnection); + } else { + exports.peers[fromAdded] = { + id: fromAdded, + currentLocation: added.fromLocation, + connections: [toConnection], + history: [], + locationHistory: [], + }; + } + if (exports.peers[toAdded]) { + exports.peers[toAdded].connections.push(fromConnection); + } else { + exports.peers[toAdded] = { + id: toAdded, + currentLocation: added.toLocation, + connections: [fromConnection], + history: [], + locationHistory: [], + }; + } + const changeInfo = { + type: "Added", + from: fromConnection, + to: toConnection, + timestamp: Date.now(), + }; + exports.peers[fromAdded].history.push(changeInfo); + exports.peers[toAdded].history.push(changeInfo); +} +exports.handleAddedConnection = handleAddedConnection; +function handleRemovedConnection(peerChange) { + const removed = peerChange; + const fromRemoved = removed.from.toString(); + const atRemoved = removed.at.toString(); + const index = exports.peers[fromRemoved].connections.findIndex( + (connection) => connection.id === atRemoved + ); + if (index > -1) { + exports.peers[fromRemoved].connections.splice(index, 1); + } + const reverseIndex = exports.peers[atRemoved].connections.findIndex( + (connection) => connection.id === fromRemoved + ); + if (reverseIndex > -1) { + exports.peers[atRemoved].connections.splice(reverseIndex, 1); + } + const changeInfo = { + type: "Removed", + from: { + id: fromRemoved, + location: exports.peers[fromRemoved].currentLocation, + }, + to: { + id: atRemoved, + location: exports.peers[atRemoved].currentLocation, + }, + timestamp: Date.now(), + }; + exports.peers[fromRemoved].history.push(changeInfo); + exports.peers[atRemoved].history.push(changeInfo); +} +exports.handleRemovedConnection = handleRemovedConnection; +function updateTable() { + const table = document.getElementById("peers-table"); + if (table) { + table.innerHTML = ""; + for (const id in exports.peers) { + const row = document.createElement("tr"); + row.onclick = () => showConnections(exports.peers[id]); + row.onclick = () => displayHistory(exports.peers[id]); + const cell = document.createElement("td"); + cell.textContent = id; + row.appendChild(cell); + table.appendChild(row); + } + } +} +function showConnections(peer) { + var _a, _b; + const id = peer.id; + const connections = + (_a = peer.connections) !== null && _a !== void 0 ? _a : []; + const overlayDiv = document.getElementById("overlay-div"); + if (overlayDiv) { + overlayDiv.innerHTML = ""; + const headerSection = document.createElement("section"); + headerSection.classList.add("header-section"); + const idLocation = document.createElement("h2"); + idLocation.textContent = `Peer ID: ${id}, Location: ${ + (_b = peer.currentLocation) !== null && _b !== void 0 ? _b : "" + }`; + headerSection.appendChild(idLocation); + overlayDiv.appendChild(headerSection); + const tableSection = document.createElement("section"); + tableSection.classList.add("table-section"); + const table = document.createElement("table"); + table.classList.add("peers-table"); + // Create the table header row + const headerRow = document.createElement("tr"); + const idHeader = document.createElement("th"); + idHeader.textContent = "Peer ID"; + const locationHeader = document.createElement("th"); + locationHeader.textContent = "Location"; + headerRow.appendChild(idHeader); + headerRow.appendChild(locationHeader); + table.appendChild(headerRow); + // Create and append the table rows for all peers + connections.forEach((connection) => { + var _a, _b; + const row = document.createElement("tr"); + const idCell = document.createElement("td"); + idCell.textContent = + (_a = connection.id.toString()) !== null && _a !== void 0 ? _a : ""; + const locationCell = document.createElement("td"); + locationCell.textContent = + (_b = connection.location.toString()) !== null && _b !== void 0 + ? _b + : ""; + row.appendChild(idCell); + row.appendChild(locationCell); + table.appendChild(row); + }); + // Append the table to the table section + tableSection.appendChild(table); + // Append the table section to the overlay div + overlayDiv.appendChild(tableSection); + } +} +exports.showConnections = showConnections; +function displayHistory(peer) { + let historyHTML = ''; + historyHTML += + ""; + historyHTML += peer.history + .map((change) => { + return ``; + }) + .join(""); + historyHTML += "
TimestampTypeFromTo
${change.timestamp}${change.type}${change.from.id}${change.to.id}
"; + return historyHTML; +} +//# sourceMappingURL=topology.js.map diff --git a/network-monitor/dist/src/topology.js.map b/network-monitor/dist/src/topology.js.map new file mode 100644 index 000000000..fabd31d86 --- /dev/null +++ b/network-monitor/dist/src/topology.js.map @@ -0,0 +1 @@ +{"version":3,"file":"topology.js","sourceRoot":"","sources":["../../src/topology.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/network-monitor/dist/tests/app.test.d.ts b/network-monitor/dist/tests/app.test.d.ts new file mode 100644 index 000000000..9d3efbb85 --- /dev/null +++ b/network-monitor/dist/tests/app.test.d.ts @@ -0,0 +1,4 @@ +/** + * @jest-environment jsdom + */ +export {}; diff --git a/network-monitor/dist/tests/app.test.js b/network-monitor/dist/tests/app.test.js new file mode 100644 index 000000000..21accc5f0 --- /dev/null +++ b/network-monitor/dist/tests/app.test.js @@ -0,0 +1,75 @@ +"use strict"; +/** + * @jest-environment jsdom + */ +Object.defineProperty(exports, "__esModule", { value: true }); +const topology_1 = require("../src/generated/topology"); +const topology_2 = require("../src/topology"); +describe("Network Monitor App", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + test("should update peers table when receiving added connection", () => { + const mockAddedConnection = new topology_1.AddedConnectionT( + "peer1", + 0.6254, + "peer2", + 0.2875 + ); + (0, topology_2.handleAddedConnection)(mockAddedConnection); + expect(topology_2.peers).toEqual({ + peer1: { + id: "peer1", + locationHistory: [], + currentLocation: 0.6254, + connections: [{ id: "peer2", location: 0.2875 }], + history: [ + { + type: "Added", + from: { + id: "peer1", + location: 0.6254, + }, + to: { + id: "peer2", + location: 0.2875, + }, + timestamp: expect.any(Number), + }, + ], + }, + peer2: { + id: "peer2", + locationHistory: [], + currentLocation: 0.2875, + connections: [{ id: "peer1", location: 0.6254 }], + history: [ + { + type: "Added", + from: { + id: "peer1", + location: 0.6254, + }, + to: { + id: "peer2", + location: 0.2875, + }, + timestamp: expect.any(Number), + }, + ], + }, + }); + }); + test("should update peers table when receiving removed connection", () => { + const removedConnection = new topology_1.RemovedConnectionT( + "peer1", + "peer2" + ); + (0, topology_2.handleRemovedConnection)(removedConnection); + expect(topology_2.peers["peer1"].connections).toHaveLength(0); + expect(topology_2.peers["peer2"].connections).toHaveLength(0); + expect(topology_2.peers["peer1"].history).toHaveLength(2); + expect(topology_2.peers["peer2"].history).toHaveLength(2); + }); +}); +//# sourceMappingURL=app.test.js.map diff --git a/network-monitor/dist/tests/app.test.js.map b/network-monitor/dist/tests/app.test.js.map new file mode 100644 index 000000000..e28999067 --- /dev/null +++ b/network-monitor/dist/tests/app.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.test.js","sourceRoot":"","sources":["../../tests/app.test.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAEH,wDAGmC;AACnC,8CAKyB;AAEzB,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACrE,MAAM,mBAAmB,GAAG,IAAI,2BAAgB,CAC9C,OAAO,EACP,MAAM,EACN,OAAO,EACP,MAAM,CACP,CAAC;QACF,IAAA,gCAAqB,EAAC,mBAAmB,CAAC,CAAC;QAE3C,MAAM,CAAC,gBAAK,CAAC,CAAC,OAAO,CAAC;YACpB,KAAK,EAAE;gBACL,EAAE,EAAE,OAAO;gBACX,eAAe,EAAE,EAAE;gBACnB,eAAe,EAAE,MAAM;gBACvB,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;gBAChD,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,OAAO;wBACb,IAAI,EAAE;4BACJ,EAAE,EAAE,OAAO;4BACX,QAAQ,EAAE,MAAM;yBACjB;wBACD,EAAE,EAAE;4BACF,EAAE,EAAE,OAAO;4BACX,QAAQ,EAAE,MAAM;yBACjB;wBACD,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;qBAC9B;iBACF;aACF;YACD,KAAK,EAAE;gBACL,EAAE,EAAE,OAAO;gBACX,eAAe,EAAE,EAAE;gBACnB,eAAe,EAAE,MAAM;gBACvB,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;gBAChD,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,OAAO;wBACb,IAAI,EAAE;4BACJ,EAAE,EAAE,OAAO;4BACX,QAAQ,EAAE,MAAM;yBACjB;wBACD,EAAE,EAAE;4BACF,EAAE,EAAE,OAAO;4BACX,QAAQ,EAAE,MAAM;yBACjB;wBACD,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;qBAC9B;iBACF;aACF;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACvE,MAAM,iBAAiB,GAAG,IAAI,6BAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAEnE,IAAA,kCAAuB,EAAC,iBAAiB,CAAC,CAAC;QAE3C,MAAM,CAAC,gBAAK,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,gBAAK,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,gBAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,gBAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/network-monitor/jest.config.ts b/network-monitor/jest.config.ts new file mode 100644 index 000000000..900792285 --- /dev/null +++ b/network-monitor/jest.config.ts @@ -0,0 +1,13 @@ +import type { Config } from "@jest/types"; +const config: Config.InitialOptions = { + verbose: true, + transform: { + "^.+\\.tsx?$": "ts-jest", + }, + roots: ["/tests", "/src"], + moduleNameMapper: { + "^(.*)\\.js$": "$1", + }, + testEnvironment: "jsdom", +}; +export default config; diff --git a/network-monitor/package.json b/network-monitor/package.json new file mode 100644 index 000000000..961e76a57 --- /dev/null +++ b/network-monitor/package.json @@ -0,0 +1,30 @@ +{ + "name": "network-monitor", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "jest", + "build": "tsc && webpack", + "start": "webpack serve --open", + "flatc-schemas": "bash -c \"flatc --ts --gen-object-api --ts-no-import-ext -o ./src/generated ../schemas/flatbuffers/*\"" + }, + "keywords": [], + "author": "", + "license": "MIT+APACHE-2.0", + "devDependencies": { + "@types/jest": "^29.5.10", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "ts-jest": "^29.1.1", + "ts-loader": "^9.5.1", + "ts-node": "10.9.1", + "typescript": "^5.3.2", + "webpack": "^5.89.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^4.15.1" + }, + "dependencies": { + "flatbuffers": "^23.5.26" + } +} diff --git a/network-monitor/src/app.ts b/network-monitor/src/app.ts new file mode 100644 index 000000000..5de1af9da --- /dev/null +++ b/network-monitor/src/app.ts @@ -0,0 +1,37 @@ +import * as flatbuffers from "flatbuffers"; +import * as fbTopology from "./generated/topology"; +import { handleChange } from "./topology"; + +const socket = new WebSocket("ws://127.0.0.1:55010/pull-stats/"); + +socket.onmessage = handleMessage; + +function handleMessage(event: MessageEvent) { + const data = event.data as Blob; + convertBlobToUint8Array(data) + .then((uint8Array) => { + const buf = new flatbuffers.ByteBuffer(uint8Array); + const peerChange = fbTopology.PeerChange.getRootAsPeerChange(buf); + handleChange(peerChange); + }) + .catch((error) => { + console.error("Failed to handle message:", error); + }); +} + +function convertBlobToUint8Array(blob: Blob): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => { + if (reader.result instanceof ArrayBuffer) { + const arrayBuffer = reader.result; + const uint8Array = new Uint8Array(arrayBuffer); + resolve(uint8Array); + } else { + reject(new Error("Failed to convert Blob to Uint8Array.")); + } + }; + reader.onerror = reject; + reader.readAsArrayBuffer(blob); + }); +} diff --git a/network-monitor/src/generated/topology.ts b/network-monitor/src/generated/topology.ts new file mode 100644 index 000000000..d3e400b84 --- /dev/null +++ b/network-monitor/src/generated/topology.ts @@ -0,0 +1,16 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +export { AddedConnection, AddedConnectionT } from "./topology/added-connection"; +export { + ControllerResponse, + ControllerResponseT, +} from "./topology/controller-response"; +export { Error, ErrorT } from "./topology/error"; +export { Ok, OkT } from "./topology/ok"; +export { PeerChange, PeerChangeT } from "./topology/peer-change"; +export { PeerChangeType } from "./topology/peer-change-type"; +export { + RemovedConnection, + RemovedConnectionT, +} from "./topology/removed-connection"; +export { Response } from "./topology/response"; diff --git a/network-monitor/src/generated/topology/added-connection.ts b/network-monitor/src/generated/topology/added-connection.ts new file mode 100644 index 000000000..c142d3efb --- /dev/null +++ b/network-monitor/src/generated/topology/added-connection.ts @@ -0,0 +1,144 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +import * as flatbuffers from "flatbuffers"; + +export class AddedConnection + implements flatbuffers.IUnpackableObject +{ + bb: flatbuffers.ByteBuffer | null = null; + bb_pos = 0; + __init(i: number, bb: flatbuffers.ByteBuffer): AddedConnection { + this.bb_pos = i; + this.bb = bb; + return this; + } + + static getRootAsAddedConnection( + bb: flatbuffers.ByteBuffer, + obj?: AddedConnection + ): AddedConnection { + return (obj || new AddedConnection()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + + static getSizePrefixedRootAsAddedConnection( + bb: flatbuffers.ByteBuffer, + obj?: AddedConnection + ): AddedConnection { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new AddedConnection()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + + from(): string | null; + from(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; + from(optionalEncoding?: any): string | Uint8Array | null { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset + ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) + : null; + } + + fromLocation(): number { + const offset = this.bb!.__offset(this.bb_pos, 6); + return offset ? this.bb!.readFloat64(this.bb_pos + offset) : 0.0; + } + + to(): string | null; + to(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; + to(optionalEncoding?: any): string | Uint8Array | null { + const offset = this.bb!.__offset(this.bb_pos, 8); + return offset + ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) + : null; + } + + toLocation(): number { + const offset = this.bb!.__offset(this.bb_pos, 10); + return offset ? this.bb!.readFloat64(this.bb_pos + offset) : 0.0; + } + + static startAddedConnection(builder: flatbuffers.Builder) { + builder.startObject(4); + } + + static addFrom(builder: flatbuffers.Builder, fromOffset: flatbuffers.Offset) { + builder.addFieldOffset(0, fromOffset, 0); + } + + static addFromLocation(builder: flatbuffers.Builder, fromLocation: number) { + builder.addFieldFloat64(1, fromLocation, 0.0); + } + + static addTo(builder: flatbuffers.Builder, toOffset: flatbuffers.Offset) { + builder.addFieldOffset(2, toOffset, 0); + } + + static addToLocation(builder: flatbuffers.Builder, toLocation: number) { + builder.addFieldFloat64(3, toLocation, 0.0); + } + + static endAddedConnection(builder: flatbuffers.Builder): flatbuffers.Offset { + const offset = builder.endObject(); + builder.requiredField(offset, 4); // from + builder.requiredField(offset, 8); // to + return offset; + } + + static createAddedConnection( + builder: flatbuffers.Builder, + fromOffset: flatbuffers.Offset, + fromLocation: number, + toOffset: flatbuffers.Offset, + toLocation: number + ): flatbuffers.Offset { + AddedConnection.startAddedConnection(builder); + AddedConnection.addFrom(builder, fromOffset); + AddedConnection.addFromLocation(builder, fromLocation); + AddedConnection.addTo(builder, toOffset); + AddedConnection.addToLocation(builder, toLocation); + return AddedConnection.endAddedConnection(builder); + } + + unpack(): AddedConnectionT { + return new AddedConnectionT( + this.from(), + this.fromLocation(), + this.to(), + this.toLocation() + ); + } + + unpackTo(_o: AddedConnectionT): void { + _o.from = this.from(); + _o.fromLocation = this.fromLocation(); + _o.to = this.to(); + _o.toLocation = this.toLocation(); + } +} + +export class AddedConnectionT implements flatbuffers.IGeneratedObject { + constructor( + public from: string | Uint8Array | null = null, + public fromLocation: number = 0.0, + public to: string | Uint8Array | null = null, + public toLocation: number = 0.0 + ) {} + + pack(builder: flatbuffers.Builder): flatbuffers.Offset { + const from = this.from !== null ? builder.createString(this.from!) : 0; + const to = this.to !== null ? builder.createString(this.to!) : 0; + + return AddedConnection.createAddedConnection( + builder, + from, + this.fromLocation, + to, + this.toLocation + ); + } +} diff --git a/network-monitor/src/generated/topology/controller-response.ts b/network-monitor/src/generated/topology/controller-response.ts new file mode 100644 index 000000000..d3229b804 --- /dev/null +++ b/network-monitor/src/generated/topology/controller-response.ts @@ -0,0 +1,135 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +import * as flatbuffers from "flatbuffers"; + +import { Error, ErrorT } from "../topology/error"; +import { Ok, OkT } from "../topology/ok"; +import { + Response, + unionToResponse, + unionListToResponse, +} from "../topology/response"; + +export class ControllerResponse + implements flatbuffers.IUnpackableObject +{ + bb: flatbuffers.ByteBuffer | null = null; + bb_pos = 0; + __init(i: number, bb: flatbuffers.ByteBuffer): ControllerResponse { + this.bb_pos = i; + this.bb = bb; + return this; + } + + static getRootAsControllerResponse( + bb: flatbuffers.ByteBuffer, + obj?: ControllerResponse + ): ControllerResponse { + return (obj || new ControllerResponse()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + + static getSizePrefixedRootAsControllerResponse( + bb: flatbuffers.ByteBuffer, + obj?: ControllerResponse + ): ControllerResponse { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new ControllerResponse()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + + responseType(): Response { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.readUint8(this.bb_pos + offset) : Response.NONE; + } + + response(obj: any): any | null { + const offset = this.bb!.__offset(this.bb_pos, 6); + return offset ? this.bb!.__union(obj, this.bb_pos + offset) : null; + } + + static startControllerResponse(builder: flatbuffers.Builder) { + builder.startObject(2); + } + + static addResponseType(builder: flatbuffers.Builder, responseType: Response) { + builder.addFieldInt8(0, responseType, Response.NONE); + } + + static addResponse( + builder: flatbuffers.Builder, + responseOffset: flatbuffers.Offset + ) { + builder.addFieldOffset(1, responseOffset, 0); + } + + static endControllerResponse( + builder: flatbuffers.Builder + ): flatbuffers.Offset { + const offset = builder.endObject(); + builder.requiredField(offset, 6); // response + return offset; + } + + static createControllerResponse( + builder: flatbuffers.Builder, + responseType: Response, + responseOffset: flatbuffers.Offset + ): flatbuffers.Offset { + ControllerResponse.startControllerResponse(builder); + ControllerResponse.addResponseType(builder, responseType); + ControllerResponse.addResponse(builder, responseOffset); + return ControllerResponse.endControllerResponse(builder); + } + + unpack(): ControllerResponseT { + return new ControllerResponseT( + this.responseType(), + (() => { + const temp = unionToResponse( + this.responseType(), + this.response.bind(this) + ); + if (temp === null) { + return null; + } + return temp.unpack(); + })() + ); + } + + unpackTo(_o: ControllerResponseT): void { + _o.responseType = this.responseType(); + _o.response = (() => { + const temp = unionToResponse( + this.responseType(), + this.response.bind(this) + ); + if (temp === null) { + return null; + } + return temp.unpack(); + })(); + } +} + +export class ControllerResponseT implements flatbuffers.IGeneratedObject { + constructor( + public responseType: Response = Response.NONE, + public response: ErrorT | OkT | null = null + ) {} + + pack(builder: flatbuffers.Builder): flatbuffers.Offset { + const response = builder.createObjectOffset(this.response); + + return ControllerResponse.createControllerResponse( + builder, + this.responseType, + response + ); + } +} diff --git a/network-monitor/src/generated/topology/error.ts b/network-monitor/src/generated/topology/error.ts new file mode 100644 index 000000000..54afda32a --- /dev/null +++ b/network-monitor/src/generated/topology/error.ts @@ -0,0 +1,85 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +import * as flatbuffers from "flatbuffers"; + +export class Error implements flatbuffers.IUnpackableObject { + bb: flatbuffers.ByteBuffer | null = null; + bb_pos = 0; + __init(i: number, bb: flatbuffers.ByteBuffer): Error { + this.bb_pos = i; + this.bb = bb; + return this; + } + + static getRootAsError(bb: flatbuffers.ByteBuffer, obj?: Error): Error { + return (obj || new Error()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + + static getSizePrefixedRootAsError( + bb: flatbuffers.ByteBuffer, + obj?: Error + ): Error { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new Error()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + + message(): string | null; + message(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; + message(optionalEncoding?: any): string | Uint8Array | null { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset + ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) + : null; + } + + static startError(builder: flatbuffers.Builder) { + builder.startObject(1); + } + + static addMessage( + builder: flatbuffers.Builder, + messageOffset: flatbuffers.Offset + ) { + builder.addFieldOffset(0, messageOffset, 0); + } + + static endError(builder: flatbuffers.Builder): flatbuffers.Offset { + const offset = builder.endObject(); + builder.requiredField(offset, 4); // message + return offset; + } + + static createError( + builder: flatbuffers.Builder, + messageOffset: flatbuffers.Offset + ): flatbuffers.Offset { + Error.startError(builder); + Error.addMessage(builder, messageOffset); + return Error.endError(builder); + } + + unpack(): ErrorT { + return new ErrorT(this.message()); + } + + unpackTo(_o: ErrorT): void { + _o.message = this.message(); + } +} + +export class ErrorT implements flatbuffers.IGeneratedObject { + constructor(public message: string | Uint8Array | null = null) {} + + pack(builder: flatbuffers.Builder): flatbuffers.Offset { + const message = + this.message !== null ? builder.createString(this.message!) : 0; + + return Error.createError(builder, message); + } +} diff --git a/network-monitor/src/generated/topology/ok.ts b/network-monitor/src/generated/topology/ok.ts new file mode 100644 index 000000000..99c840f2d --- /dev/null +++ b/network-monitor/src/generated/topology/ok.ts @@ -0,0 +1,81 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +import * as flatbuffers from "flatbuffers"; + +export class Ok implements flatbuffers.IUnpackableObject { + bb: flatbuffers.ByteBuffer | null = null; + bb_pos = 0; + __init(i: number, bb: flatbuffers.ByteBuffer): Ok { + this.bb_pos = i; + this.bb = bb; + return this; + } + + static getRootAsOk(bb: flatbuffers.ByteBuffer, obj?: Ok): Ok { + return (obj || new Ok()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + + static getSizePrefixedRootAsOk(bb: flatbuffers.ByteBuffer, obj?: Ok): Ok { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new Ok()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + + message(): string | null; + message(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; + message(optionalEncoding?: any): string | Uint8Array | null { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset + ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) + : null; + } + + static startOk(builder: flatbuffers.Builder) { + builder.startObject(1); + } + + static addMessage( + builder: flatbuffers.Builder, + messageOffset: flatbuffers.Offset + ) { + builder.addFieldOffset(0, messageOffset, 0); + } + + static endOk(builder: flatbuffers.Builder): flatbuffers.Offset { + const offset = builder.endObject(); + return offset; + } + + static createOk( + builder: flatbuffers.Builder, + messageOffset: flatbuffers.Offset + ): flatbuffers.Offset { + Ok.startOk(builder); + Ok.addMessage(builder, messageOffset); + return Ok.endOk(builder); + } + + unpack(): OkT { + return new OkT(this.message()); + } + + unpackTo(_o: OkT): void { + _o.message = this.message(); + } +} + +export class OkT implements flatbuffers.IGeneratedObject { + constructor(public message: string | Uint8Array | null = null) {} + + pack(builder: flatbuffers.Builder): flatbuffers.Offset { + const message = + this.message !== null ? builder.createString(this.message!) : 0; + + return Ok.createOk(builder, message); + } +} diff --git a/network-monitor/src/generated/topology/peer-change-type.ts b/network-monitor/src/generated/topology/peer-change-type.ts new file mode 100644 index 000000000..20335a69f --- /dev/null +++ b/network-monitor/src/generated/topology/peer-change-type.ts @@ -0,0 +1,60 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +import { + AddedConnection, + AddedConnectionT, +} from "../topology/added-connection"; +import { Error, ErrorT } from "../topology/error"; +import { + RemovedConnection, + RemovedConnectionT, +} from "../topology/removed-connection"; + +export enum PeerChangeType { + NONE = 0, + AddedConnection = 1, + RemovedConnection = 2, + Error = 3, +} + +export function unionToPeerChangeType( + type: PeerChangeType, + accessor: ( + obj: AddedConnection | Error | RemovedConnection + ) => AddedConnection | Error | RemovedConnection | null +): AddedConnection | Error | RemovedConnection | null { + switch (PeerChangeType[type]) { + case "NONE": + return null; + case "AddedConnection": + return accessor(new AddedConnection())! as AddedConnection; + case "RemovedConnection": + return accessor(new RemovedConnection())! as RemovedConnection; + case "Error": + return accessor(new Error())! as Error; + default: + return null; + } +} + +export function unionListToPeerChangeType( + type: PeerChangeType, + accessor: ( + index: number, + obj: AddedConnection | Error | RemovedConnection + ) => AddedConnection | Error | RemovedConnection | null, + index: number +): AddedConnection | Error | RemovedConnection | null { + switch (PeerChangeType[type]) { + case "NONE": + return null; + case "AddedConnection": + return accessor(index, new AddedConnection())! as AddedConnection; + case "RemovedConnection": + return accessor(index, new RemovedConnection())! as RemovedConnection; + case "Error": + return accessor(index, new Error())! as Error; + default: + return null; + } +} diff --git a/network-monitor/src/generated/topology/peer-change.ts b/network-monitor/src/generated/topology/peer-change.ts new file mode 100644 index 000000000..195f24cfc --- /dev/null +++ b/network-monitor/src/generated/topology/peer-change.ts @@ -0,0 +1,200 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +import * as flatbuffers from "flatbuffers"; + +import { + AddedConnection, + AddedConnectionT, +} from "../topology/added-connection"; +import { Error, ErrorT } from "../topology/error"; +import { + PeerChangeType, + unionToPeerChangeType, + unionListToPeerChangeType, +} from "../topology/peer-change-type"; +import { + RemovedConnection, + RemovedConnectionT, +} from "../topology/removed-connection"; + +export class PeerChange implements flatbuffers.IUnpackableObject { + bb: flatbuffers.ByteBuffer | null = null; + bb_pos = 0; + __init(i: number, bb: flatbuffers.ByteBuffer): PeerChange { + this.bb_pos = i; + this.bb = bb; + return this; + } + + static getRootAsPeerChange( + bb: flatbuffers.ByteBuffer, + obj?: PeerChange + ): PeerChange { + return (obj || new PeerChange()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + + static getSizePrefixedRootAsPeerChange( + bb: flatbuffers.ByteBuffer, + obj?: PeerChange + ): PeerChange { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new PeerChange()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + + currentState(index: number, obj?: AddedConnection): AddedConnection | null { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset + ? (obj || new AddedConnection()).__init( + this.bb!.__indirect( + this.bb!.__vector(this.bb_pos + offset) + index * 4 + ), + this.bb! + ) + : null; + } + + currentStateLength(): number { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0; + } + + changeType(): PeerChangeType { + const offset = this.bb!.__offset(this.bb_pos, 6); + return offset + ? this.bb!.readUint8(this.bb_pos + offset) + : PeerChangeType.NONE; + } + + change(obj: any): any | null { + const offset = this.bb!.__offset(this.bb_pos, 8); + return offset ? this.bb!.__union(obj, this.bb_pos + offset) : null; + } + + static startPeerChange(builder: flatbuffers.Builder) { + builder.startObject(3); + } + + static addCurrentState( + builder: flatbuffers.Builder, + currentStateOffset: flatbuffers.Offset + ) { + builder.addFieldOffset(0, currentStateOffset, 0); + } + + static createCurrentStateVector( + builder: flatbuffers.Builder, + data: flatbuffers.Offset[] + ): flatbuffers.Offset { + builder.startVector(4, data.length, 4); + for (let i = data.length - 1; i >= 0; i--) { + builder.addOffset(data[i]!); + } + return builder.endVector(); + } + + static startCurrentStateVector( + builder: flatbuffers.Builder, + numElems: number + ) { + builder.startVector(4, numElems, 4); + } + + static addChangeType( + builder: flatbuffers.Builder, + changeType: PeerChangeType + ) { + builder.addFieldInt8(1, changeType, PeerChangeType.NONE); + } + + static addChange( + builder: flatbuffers.Builder, + changeOffset: flatbuffers.Offset + ) { + builder.addFieldOffset(2, changeOffset, 0); + } + + static endPeerChange(builder: flatbuffers.Builder): flatbuffers.Offset { + const offset = builder.endObject(); + return offset; + } + + static createPeerChange( + builder: flatbuffers.Builder, + currentStateOffset: flatbuffers.Offset, + changeType: PeerChangeType, + changeOffset: flatbuffers.Offset + ): flatbuffers.Offset { + PeerChange.startPeerChange(builder); + PeerChange.addCurrentState(builder, currentStateOffset); + PeerChange.addChangeType(builder, changeType); + PeerChange.addChange(builder, changeOffset); + return PeerChange.endPeerChange(builder); + } + + unpack(): PeerChangeT { + return new PeerChangeT( + this.bb!.createObjList( + this.currentState.bind(this), + this.currentStateLength() + ), + this.changeType(), + (() => { + const temp = unionToPeerChangeType( + this.changeType(), + this.change.bind(this) + ); + if (temp === null) { + return null; + } + return temp.unpack(); + })() + ); + } + + unpackTo(_o: PeerChangeT): void { + _o.currentState = this.bb!.createObjList( + this.currentState.bind(this), + this.currentStateLength() + ); + _o.changeType = this.changeType(); + _o.change = (() => { + const temp = unionToPeerChangeType( + this.changeType(), + this.change.bind(this) + ); + if (temp === null) { + return null; + } + return temp.unpack(); + })(); + } +} + +export class PeerChangeT implements flatbuffers.IGeneratedObject { + constructor( + public currentState: AddedConnectionT[] = [], + public changeType: PeerChangeType = PeerChangeType.NONE, + public change: AddedConnectionT | ErrorT | RemovedConnectionT | null = null + ) {} + + pack(builder: flatbuffers.Builder): flatbuffers.Offset { + const currentState = PeerChange.createCurrentStateVector( + builder, + builder.createObjectOffsetList(this.currentState) + ); + const change = builder.createObjectOffset(this.change); + + return PeerChange.createPeerChange( + builder, + currentState, + this.changeType, + change + ); + } +} diff --git a/network-monitor/src/generated/topology/removed-connection.ts b/network-monitor/src/generated/topology/removed-connection.ts new file mode 100644 index 000000000..842d84ae6 --- /dev/null +++ b/network-monitor/src/generated/topology/removed-connection.ts @@ -0,0 +1,109 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +import * as flatbuffers from "flatbuffers"; + +export class RemovedConnection + implements flatbuffers.IUnpackableObject +{ + bb: flatbuffers.ByteBuffer | null = null; + bb_pos = 0; + __init(i: number, bb: flatbuffers.ByteBuffer): RemovedConnection { + this.bb_pos = i; + this.bb = bb; + return this; + } + + static getRootAsRemovedConnection( + bb: flatbuffers.ByteBuffer, + obj?: RemovedConnection + ): RemovedConnection { + return (obj || new RemovedConnection()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + + static getSizePrefixedRootAsRemovedConnection( + bb: flatbuffers.ByteBuffer, + obj?: RemovedConnection + ): RemovedConnection { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new RemovedConnection()).__init( + bb.readInt32(bb.position()) + bb.position(), + bb + ); + } + + at(): string | null; + at(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; + at(optionalEncoding?: any): string | Uint8Array | null { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset + ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) + : null; + } + + from(): string | null; + from(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; + from(optionalEncoding?: any): string | Uint8Array | null { + const offset = this.bb!.__offset(this.bb_pos, 6); + return offset + ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) + : null; + } + + static startRemovedConnection(builder: flatbuffers.Builder) { + builder.startObject(2); + } + + static addAt(builder: flatbuffers.Builder, atOffset: flatbuffers.Offset) { + builder.addFieldOffset(0, atOffset, 0); + } + + static addFrom(builder: flatbuffers.Builder, fromOffset: flatbuffers.Offset) { + builder.addFieldOffset(1, fromOffset, 0); + } + + static endRemovedConnection( + builder: flatbuffers.Builder + ): flatbuffers.Offset { + const offset = builder.endObject(); + builder.requiredField(offset, 4); // at + builder.requiredField(offset, 6); // from + return offset; + } + + static createRemovedConnection( + builder: flatbuffers.Builder, + atOffset: flatbuffers.Offset, + fromOffset: flatbuffers.Offset + ): flatbuffers.Offset { + RemovedConnection.startRemovedConnection(builder); + RemovedConnection.addAt(builder, atOffset); + RemovedConnection.addFrom(builder, fromOffset); + return RemovedConnection.endRemovedConnection(builder); + } + + unpack(): RemovedConnectionT { + return new RemovedConnectionT(this.at(), this.from()); + } + + unpackTo(_o: RemovedConnectionT): void { + _o.at = this.at(); + _o.from = this.from(); + } +} + +export class RemovedConnectionT implements flatbuffers.IGeneratedObject { + constructor( + public at: string | Uint8Array | null = null, + public from: string | Uint8Array | null = null + ) {} + + pack(builder: flatbuffers.Builder): flatbuffers.Offset { + const at = this.at !== null ? builder.createString(this.at!) : 0; + const from = this.from !== null ? builder.createString(this.from!) : 0; + + return RemovedConnection.createRemovedConnection(builder, at, from); + } +} diff --git a/network-monitor/src/generated/topology/response.ts b/network-monitor/src/generated/topology/response.ts new file mode 100644 index 000000000..982bbfb2f --- /dev/null +++ b/network-monitor/src/generated/topology/response.ts @@ -0,0 +1,43 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +import { Error, ErrorT } from "../topology/error"; +import { Ok, OkT } from "../topology/ok"; + +export enum Response { + NONE = 0, + Error = 1, + Ok = 2, +} + +export function unionToResponse( + type: Response, + accessor: (obj: Error | Ok) => Error | Ok | null +): Error | Ok | null { + switch (Response[type]) { + case "NONE": + return null; + case "Error": + return accessor(new Error())! as Error; + case "Ok": + return accessor(new Ok())! as Ok; + default: + return null; + } +} + +export function unionListToResponse( + type: Response, + accessor: (index: number, obj: Error | Ok) => Error | Ok | null, + index: number +): Error | Ok | null { + switch (Response[type]) { + case "NONE": + return null; + case "Error": + return accessor(index, new Error())! as Error; + case "Ok": + return accessor(index, new Ok())! as Ok; + default: + return null; + } +} diff --git a/network-monitor/src/topology.ts b/network-monitor/src/topology.ts new file mode 100644 index 000000000..c8ada934f --- /dev/null +++ b/network-monitor/src/topology.ts @@ -0,0 +1,356 @@ +import * as fbTopology from "./generated/topology"; + +export let peers: PeerList = {}; + +interface PeerList { + [id: string]: Peer; +} + +interface Peer { + id: string; + currentLocation: number; + connectionTimestamp: number; + connections: Connection[]; + history: ChangeInfo[]; + locationHistory: { location: number; timestamp: number }[]; +} + +interface Connection { + id: string; + location: number; +} + +interface ChangeInfo { + type: "Added" | "Removed"; + from: Connection; + to: Connection; + timestamp: number; +} + +export function handleChange(peerChange: fbTopology.PeerChange) { + try { + const unpacked = peerChange.unpack(); + switch (unpacked.changeType) { + case fbTopology.PeerChangeType.AddedConnection: + handleAddedConnection(unpacked.change as fbTopology.AddedConnectionT); + break; + case fbTopology.PeerChangeType.RemovedConnection: + handleRemovedConnection( + unpacked.change as fbTopology.RemovedConnectionT + ); + break; + case fbTopology.PeerChangeType.NONE: + break; + case fbTopology.PeerChangeType.Error: + const error = unpacked.change as fbTopology.ErrorT; + if (error.message) { + console.error(error.message); + } + break; + } + unpacked.currentState.forEach((connection) => { + handleAddedConnection(connection); + }); + } catch (e) { + console.error(e); + } finally { + updateTable(); + } +} + +export function handleAddedConnection(peerChange: fbTopology.AddedConnectionT) { + const added = peerChange; + const fromAdded = added.from!.toString(); + const toAdded = added.to!.toString(); + + const fromConnection: Connection = { + id: fromAdded, + location: added.fromLocation, + }; + + const toConnection: Connection = { + id: toAdded, + location: added.toLocation, + }; + + if (peers[fromAdded]) { + if (peers[fromAdded].currentLocation !== added.fromLocation) { + peers[fromAdded].locationHistory.push({ + location: peers[fromAdded].currentLocation, + timestamp: Date.now(), + }); + peers[fromAdded].currentLocation = added.fromLocation; + } + + if (!peers[fromAdded].connections.some((conn) => conn.id === toAdded)) { + peers[fromAdded].connections.push(toConnection); + } + } else { + peers[fromAdded] = { + id: fromAdded, + currentLocation: added.fromLocation, + connectionTimestamp: Date.now(), + connections: [toConnection], + history: [], + locationHistory: [], + }; + } + + if (peers[toAdded]) { + if (peers[toAdded].currentLocation !== added.toLocation) { + peers[toAdded].locationHistory.push({ + location: peers[toAdded].currentLocation, + timestamp: Date.now(), + }); + peers[toAdded].currentLocation = added.toLocation; + } + + if (!peers[toAdded].connections.some((conn) => conn.id === fromAdded)) { + peers[toAdded].connections.push(toConnection); + } + } else { + peers[toAdded] = { + id: toAdded, + currentLocation: added.toLocation, + connectionTimestamp: Date.now(), + connections: [fromConnection], + history: [], + locationHistory: [], + }; + } + + const changeInfo: ChangeInfo = { + type: "Added", + from: fromConnection, + to: toConnection, + timestamp: Date.now(), + }; + + peers[fromAdded].history.push(changeInfo); + peers[toAdded].history.push(changeInfo); +} + +export function handleRemovedConnection( + peerChange: fbTopology.RemovedConnectionT +) { + const removed = peerChange; + const fromRemoved = removed.from!.toString(); + const atRemoved = removed.at!.toString(); + const index = peers[fromRemoved].connections.findIndex( + (connection) => connection.id === atRemoved + ); + + if (index > -1) { + peers[fromRemoved].connections.splice(index, 1); + } + + const reverseIndex = peers[atRemoved].connections.findIndex( + (connection: Connection) => connection.id === fromRemoved + ); + + if (reverseIndex > -1) { + peers[atRemoved].connections.splice(reverseIndex, 1); + } + + const changeInfo: ChangeInfo = { + type: "Removed", + from: { + id: fromRemoved, + location: peers[fromRemoved].currentLocation, + }, + to: { + id: atRemoved, + location: peers[atRemoved].currentLocation, + }, + timestamp: Date.now(), + }; + + peers[fromRemoved].history.push(changeInfo); + peers[atRemoved].history.push(changeInfo); +} + +function updateTable() { + const peerConnectionsDiv = document.getElementById("peer-connections")!; + + const table = document.getElementById("peers-table")!; + + const tbody = table.querySelector("tbody")!; + tbody.innerHTML = ""; + + const setDivPosition = (event: MouseEvent) => { + peerConnectionsDiv.style.display = "block"; + const rect = peerConnectionsDiv.offsetParent!.getBoundingClientRect(); + const divHeight = peerConnectionsDiv.offsetHeight; + + // Check if the div would render off the bottom of the screen + if (event.clientY + divHeight > window.innerHeight) { + // If so, position it above the mouse cursor instead + peerConnectionsDiv.style.top = `${ + event.clientY - rect.top - divHeight + }px`; + } else { + // Otherwise, position it below the mouse cursor as usual + peerConnectionsDiv.style.top = `${event.clientY - rect.top}px`; + } + + peerConnectionsDiv.style.left = `${event.clientX - rect.left + 15}px`; + }; + + for (const peer in peers) { + const peerData = peers[peer]; + const row = document.createElement("tr"); + row.addEventListener("mouseover", (event) => { + setDivPosition(event); + showConnections(peers[peer]); + }); + row.addEventListener("mousemove", (event) => { + setDivPosition(event); + }); + row.addEventListener( + "mouseout", + () => (peerConnectionsDiv.style.display = "none") + ); + + const id = document.createElement("td"); + id.textContent = peerData.id; + const location = document.createElement("td"); + location.textContent = peerData.currentLocation.toString(); + const connectionTimestamp = document.createElement("td"); + const timestamp = new Date(peerData.connectionTimestamp); + connectionTimestamp.textContent = `${timestamp.toUTCString()} (${timestamp.getMilliseconds()}ms)`; + row.appendChild(id); + row.appendChild(location); + row.appendChild(connectionTimestamp); + + // Add event listeners to each td element + const tds = row.getElementsByTagName("td"); + for (let i = 0; i < tds.length; i++) { + tds[i].addEventListener("mouseover", (event) => { + setDivPosition(event); + showConnections(peers[peer]); + }); + row.addEventListener("mousemove", (event) => { + setDivPosition(event); + }); + tds[i].addEventListener( + "mouseout", + () => (peerConnectionsDiv.style.display = "none") + ); + } + + tbody.appendChild(row); + } + + const rows = Array.from(tbody.querySelectorAll("tr")); + const sortedRows = rows.sort((a, b) => { + const cellA = a.cells[1].textContent!; + const cellB = b.cells[1].textContent!; + return cellA.localeCompare(cellB); + }); + + rows.forEach((row) => tbody.removeChild(row)); + sortedRows.forEach((row) => tbody.appendChild(row)); +} + +const sortDirections: number[] = []; + +document + .querySelector("#peers-table-h")! + .querySelectorAll("th")! + .forEach((header, index) => { + sortDirections.push(1); + tableSorting(header, index); + }); + +function tableSorting(header: HTMLTableCellElement, index: number) { + header.addEventListener("click", () => { + const tbody = + header.parentElement!.parentElement!.parentElement!.querySelector( + "tbody" + )!; + const rows = Array.from(tbody.querySelectorAll("tr")); + + const sortedRows = rows.sort((a, b) => { + const cellA = a.cells[index].textContent!; + const cellB = b.cells[index].textContent!; + + // Use a locale-sensitive string comparison for proper sorting + // Multiply by the sort direction to toggle between ascending and descending order + return cellA.localeCompare(cellB) * sortDirections[index]; + }); + + // Toggle the sort direction for the next click + sortDirections[index] = -sortDirections[index]; + const icon = header.querySelector("i")!; + if (icon.classList.contains("fa-sort-amount-down")) { + icon.classList.remove("fa-sort-amount-down"); + icon.classList.add("fa-sort-amount-up"); + } else { + icon.classList.remove("fa-sort-amount-up"); + icon.classList.add("fa-sort-amount-down"); + } + + rows.forEach((row) => tbody.removeChild(row)); + sortedRows.forEach((row) => tbody.appendChild(row)); + }); +} + +export function showConnections(peer: Peer) { + const id = peer.id; + const connections: Connection[] = peer.connections ?? []; + + // Sort connections by location + connections.sort((a, b) => a.location - b.location); + + // Find the existing peer connections table + const tableBody = document.getElementById("peer-connections-b")!; + + // Clear the existing table rows + while (tableBody.firstChild) { + tableBody.removeChild(tableBody.firstChild); + } + + // Create the table header row + const headerRow = document.createElement("tr"); + const idHeader = document.createElement("th"); + idHeader.textContent = "Neighbour Id"; + const locationHeader = document.createElement("th"); + locationHeader.textContent = "Location"; + headerRow.appendChild(idHeader); + headerRow.appendChild(locationHeader); + tableBody.appendChild(headerRow); + + // Create and append the table rows for all peers + connections.forEach((connection) => { + const row = document.createElement("tr"); + const idCell = document.createElement("td"); + idCell.textContent = connection.id.toString() ?? ""; + const locationCell = document.createElement("td"); + locationCell.textContent = connection.location.toString() ?? ""; + row.appendChild(idCell); + row.appendChild(locationCell); + tableBody.appendChild(row); + }); + + // Set title + const idLocation = document.getElementById("peer-connections-t")!; + idLocation.innerHTML = `Peer Id: ${id}, Location: ${ + peer.currentLocation ?? "" + }`; +} + +function displayHistory(peer: Peer): string { + let historyHTML = ''; + historyHTML += + ""; + + historyHTML += peer.history + .map((change) => { + return ``; + }) + .join(""); + + historyHTML += "
TimestampTypeFromTo
${change.timestamp}${change.type}${change.from.id}${change.to.id}
"; + + return historyHTML; +} diff --git a/network-monitor/tests/app.test.ts b/network-monitor/tests/app.test.ts new file mode 100644 index 000000000..33f5ec26a --- /dev/null +++ b/network-monitor/tests/app.test.ts @@ -0,0 +1,51 @@ +/** + * @jest-environment node + */ + +import * as flatbuffers from "flatbuffers"; +import * as fbTopology from "../src/generated/topology"; + +test("Flatbuffer deserialization test", () => { + // // Create a sample flatbuffer data + // const builder = new flatbuffers.Builder(); + // const nodeId = builder.createString("node1"); + // const nodeType = fbTopology.; + // const nodeOffset = fbTopology.Node.createNode(builder, nodeId, nodeType); + // const nodesOffset = fbTopology.Topology.createNodesVector(builder, [ + // nodeOffset, + // ]); + // fbTopology.Topology.startTopology(builder); + // fbTopology.Topology.addNodes(builder, nodesOffset); + // const topologyOffset = fbTopology.Topology.endTopology(builder); + // builder.finish(topologyOffset); + + // // Deserialize the flatbuffer data + // const buffer = builder.asUint8Array(); + // const topology = fbTopology.Topology.getRootAsTopology( + // new flatbuffers.ByteBuffer(buffer) + // ); + + // // Perform assertions on the deserialized data + // expect(topology.nodesLength()).toBe(1); + // const node = topology.nodes(0); + // expect(node.id()).toBe("node1"); + // expect(node.type()).toBe(fbTopology.NodeType.Router); + + const AddedConnectionMessage = new Uint8Array([ + 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 12, 0, 0, 0, 7, 0, 8, 0, 10, 0, 0, 0, + 0, 0, 0, 1, 16, 0, 0, 0, 12, 0, 28, 0, 4, 0, 12, 0, 8, 0, 20, 0, 12, 0, 0, + 0, 84, 0, 0, 0, 20, 0, 0, 0, 182, 64, 79, 180, 180, 193, 216, 63, 27, 191, + 192, 101, 40, 220, 226, 63, 52, 0, 0, 0, 49, 50, 68, 51, 75, 111, 111, 87, + 69, 106, 87, 74, 50, 53, 118, 71, 121, 66, 77, 68, 76, 78, 99, 104, 65, 105, + 97, 84, 76, 121, 81, 99, 105, 50, 53, 65, 70, 65, 71, 122, 82, 49, 71, 57, + 113, 98, 52, 86, 115, 82, 122, 66, 0, 0, 0, 0, 52, 0, 0, 0, 49, 50, 68, 51, + 75, 111, 111, 87, 75, 77, 75, 102, 99, 66, 54, 74, 100, 81, 101, 72, 107, + 112, 120, 121, 86, 70, 49, 87, 85, 85, 103, 121, 115, 85, 85, 54, 119, 66, + 71, 97, 116, 50, 49, 84, 54, 51, 109, 109, 98, 90, 53, 117, 0, 0, 0, 0, + ]); + + const buf = new flatbuffers.ByteBuffer( + new Uint8Array(AddedConnectionMessage) + ); + const peerChange = fbTopology.PeerChange.getRootAsPeerChange(buf); +}); diff --git a/network-monitor/tests/topology.test.ts b/network-monitor/tests/topology.test.ts new file mode 100644 index 000000000..aa5226734 --- /dev/null +++ b/network-monitor/tests/topology.test.ts @@ -0,0 +1,84 @@ +/** + * @jest-environment jsdom + */ + +import { + AddedConnectionT, + RemovedConnectionT, +} from "../src/generated/topology"; +import { + handleAddedConnection, + handleRemovedConnection, + peers, + showConnections, +} from "../src/topology"; + +describe("Network Monitor App", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test("should update peers table when receiving added connection", () => { + const mockAddedConnection = new AddedConnectionT( + "peer1", + 0.6254, + "peer2", + 0.2875 + ); + handleAddedConnection(mockAddedConnection); + + expect(peers).toEqual({ + peer1: { + id: "peer1", + locationHistory: [], + currentLocation: 0.6254, + connections: [{ id: "peer2", location: 0.2875 }], + history: [ + { + type: "Added", + from: { + id: "peer1", + location: 0.6254, + }, + to: { + id: "peer2", + location: 0.2875, + }, + timestamp: expect.any(Number), + }, + ], + }, + peer2: { + id: "peer2", + locationHistory: [], + currentLocation: 0.2875, + connections: [{ id: "peer1", location: 0.6254 }], + history: [ + { + type: "Added", + from: { + id: "peer1", + location: 0.6254, + }, + to: { + id: "peer2", + location: 0.2875, + }, + timestamp: expect.any(Number), + }, + ], + }, + }); + }); + + test("should update peers table when receiving removed connection", () => { + const removedConnection = new RemovedConnectionT("peer1", "peer2"); + + handleRemovedConnection(removedConnection); + + expect(peers["peer1"].connections).toHaveLength(0); + expect(peers["peer2"].connections).toHaveLength(0); + expect(peers["peer1"].history).toHaveLength(2); + expect(peers["peer2"].history).toHaveLength(2); + }); +}); diff --git a/network-monitor/tsconfig.json b/network-monitor/tsconfig.json new file mode 100644 index 000000000..83416a4a8 --- /dev/null +++ b/network-monitor/tsconfig.json @@ -0,0 +1,112 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "CommonJS" /* Specify what module code is generated. */, + "rootDir": "." /* Specify the root folder within your source files. */, + // "moduleResolution": "Node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [ + // "./src", + // "./tests" + // ] /* Allow multiple folders to be treated as one when resolving modules. */, + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + "sourceMap": true /* Create source map files for emitted JavaScript files. */, + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist" /* Specify an output folder for all emitted files. */, + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/network-monitor/webpack.config.js b/network-monitor/webpack.config.js new file mode 100644 index 000000000..6e205da5d --- /dev/null +++ b/network-monitor/webpack.config.js @@ -0,0 +1,31 @@ +const path = require("path"); + +module.exports = { + mode: "production", + entry: { + app: path.resolve(__dirname, "src", "app.ts"), + }, + devtool: "source-map", + output: { + filename: "bundle.js", + path: path.resolve(__dirname, "dist"), + }, + resolve: { + extensions: [".ts", ".tsx", ".js", ".jsx"], + }, + devServer: { + static: path.resolve(__dirname, "dist"), + compress: true, + port: 9000, + hot: true, + }, + module: { + rules: [ + { + test: /\.tsx?$/, + use: "ts-loader", + exclude: ["/node_modules/"], + }, + ], + }, +}; From a4be8188b438d14a835a0dcb45d2b15ef088292e Mon Sep 17 00:00:00 2001 From: Ignacio Duart Date: Thu, 30 Nov 2023 16:46:39 +0100 Subject: [PATCH 09/12] Display transaction history --- Cargo.lock | 12 -- Cargo.toml | 2 +- crates/core/src/generated/mod.rs | 6 +- .../core/src/generated/topology_generated.rs | 39 ++++- crates/core/src/message.rs | 2 +- crates/core/src/ring.rs | 6 - crates/core/src/tracing.rs | 1 + crates/fdev/src/network_metrics_server.rs | 26 ++- network-monitor/dist/index.html | 41 +++-- .../generated/topology/added-connection.ts | 48 ++++-- network-monitor/src/topology.ts | 155 ++++++++++++++---- network-monitor/tests/app.test.ts | 51 ------ network-monitor/tests/topology.test.ts | 12 +- schemas/flatbuffers/topology.fbs | 1 + 14 files changed, 263 insertions(+), 139 deletions(-) delete mode 100644 network-monitor/tests/app.test.ts diff --git a/Cargo.lock b/Cargo.lock index 442690e53..aa6df05e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -398,8 +398,6 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "serde_json", - "serde_path_to_error", "serde_urlencoded", "sha1", "sync_wrapper", @@ -4686,16 +4684,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_path_to_error" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" -dependencies = [ - "itoa", - "serde", -] - [[package]] name = "serde_spanned" version = "0.6.4" diff --git a/Cargo.toml b/Cargo.toml index 7916ed006..3b5340623 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = ["crates/*"] [workspace.dependencies] arrayvec = { version = "0.7", features = ["serde"] } -axum = "0.7" +axum = { version = "0.7", default-features = false } blake3 = { version = "1", features = ["std", "traits-preview"] } bs58 = "0.5" chacha20poly1305 = "0.10" diff --git a/crates/core/src/generated/mod.rs b/crates/core/src/generated/mod.rs index 515b1f761..dd5c0a55d 100644 --- a/crates/core/src/generated/mod.rs +++ b/crates/core/src/generated/mod.rs @@ -4,7 +4,7 @@ pub(crate) mod topology_generated; pub use topology_generated::*; -use crate::node::PeerId; +use crate::{message::Transaction, node::PeerId}; pub trait TryFromFbs<'a>: Sized + 'a { fn try_decode_fbs(buf: &'a [u8]) -> Result; @@ -30,6 +30,7 @@ impl PeerChange<'_> { topology::AddedConnection::create( &mut buf, &topology::AddedConnectionArgs { + transaction: None, from, from_location: *from_location, to: Some(to), @@ -52,15 +53,18 @@ impl PeerChange<'_> { } pub fn added_connection_msg( + transaction: Option>, (from, from_location): (PeerId, f64), (to, to_location): (PeerId, f64), ) -> Vec { let mut buf = flatbuffers::FlatBufferBuilder::new(); let from = Some(buf.create_string(from.to_string().as_str())); let to = Some(buf.create_string(to.to_string().as_str())); + let transaction = transaction.map(|t| buf.create_string(t.as_ref())); let add_conn = topology::AddedConnection::create( &mut buf, &topology::AddedConnectionArgs { + transaction, from, from_location, to, diff --git a/crates/core/src/generated/topology_generated.rs b/crates/core/src/generated/topology_generated.rs index 0dc1bda18..b9b9e08fb 100644 --- a/crates/core/src/generated/topology_generated.rs +++ b/crates/core/src/generated/topology_generated.rs @@ -233,10 +233,11 @@ pub mod topology { } impl<'a> AddedConnection<'a> { - pub const VT_FROM: flatbuffers::VOffsetT = 4; - pub const VT_FROM_LOCATION: flatbuffers::VOffsetT = 6; - pub const VT_TO: flatbuffers::VOffsetT = 8; - pub const VT_TO_LOCATION: flatbuffers::VOffsetT = 10; + pub const VT_TRANSACTION: flatbuffers::VOffsetT = 4; + pub const VT_FROM: flatbuffers::VOffsetT = 6; + pub const VT_FROM_LOCATION: flatbuffers::VOffsetT = 8; + pub const VT_TO: flatbuffers::VOffsetT = 10; + pub const VT_TO_LOCATION: flatbuffers::VOffsetT = 12; #[inline] pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { @@ -256,9 +257,24 @@ pub mod topology { if let Some(x) = args.from { builder.add_from(x); } + if let Some(x) = args.transaction { + builder.add_transaction(x); + } builder.finish() } + #[inline] + pub fn transaction(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>( + AddedConnection::VT_TRANSACTION, + None, + ) + } + } #[inline] pub fn from(&self) -> &'a str { // Safety: @@ -313,6 +329,11 @@ pub mod topology { ) -> Result<(), flatbuffers::InvalidFlatbuffer> { use self::flatbuffers::Verifiable; v.visit_table(pos)? + .visit_field::>( + "transaction", + Self::VT_TRANSACTION, + false, + )? .visit_field::>("from", Self::VT_FROM, true)? .visit_field::("from_location", Self::VT_FROM_LOCATION, false)? .visit_field::>("to", Self::VT_TO, true)? @@ -322,6 +343,7 @@ pub mod topology { } } pub struct AddedConnectionArgs<'a> { + pub transaction: Option>, pub from: Option>, pub from_location: f64, pub to: Option>, @@ -331,6 +353,7 @@ pub mod topology { #[inline] fn default() -> Self { AddedConnectionArgs { + transaction: None, from: None, // required field from_location: 0.0, to: None, // required field @@ -344,6 +367,13 @@ pub mod topology { start_: flatbuffers::WIPOffset, } impl<'a: 'b, 'b> AddedConnectionBuilder<'a, 'b> { + #[inline] + pub fn add_transaction(&mut self, transaction: flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::>( + AddedConnection::VT_TRANSACTION, + transaction, + ); + } #[inline] pub fn add_from(&mut self, from: flatbuffers::WIPOffset<&'b str>) { self.fbb_ @@ -386,6 +416,7 @@ pub mod topology { impl core::fmt::Debug for AddedConnection<'_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut ds = f.debug_struct("AddedConnection"); + ds.field("transaction", &self.transaction()); ds.field("from", &self.from()); ds.field("from_location", &self.from_location()); ds.field("to", &self.to()); diff --git a/crates/core/src/message.rs b/crates/core/src/message.rs index 142f17914..1e1e13c59 100644 --- a/crates/core/src/message.rs +++ b/crates/core/src/message.rs @@ -29,7 +29,7 @@ pub(crate) use sealed_msg_type::{TransactionType, TransactionTypeId}; /// /// A transaction may span different messages sent across the network. #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone, Copy)] -pub(crate) struct Transaction { +pub struct Transaction { id: Ulid, } diff --git a/crates/core/src/ring.rs b/crates/core/src/ring.rs index 692abb60d..1715e53f3 100644 --- a/crates/core/src/ring.rs +++ b/crates/core/src/ring.rs @@ -292,12 +292,6 @@ impl Ring { return Err(anyhow::anyhow!("IP and port are required for gateways")); } - /* - min 20 max 100 - GW A, number: 100 - GW B, number: 50 - */ - ring.update_location(Some(loc)); for PeerKeyLocation { peer, location } in gateways { // FIXME: this is problematic cause gateways will take all spots then! diff --git a/crates/core/src/tracing.rs b/crates/core/src/tracing.rs index ca947c2de..a225fb634 100644 --- a/crates/core/src/tracing.rs +++ b/crates/core/src/tracing.rs @@ -769,6 +769,7 @@ async fn send_to_metrics_server( }, }) => { let msg = PeerChange::added_connection_msg( + (&send_msg.tx != Transaction::NULL).then(|| send_msg.tx.to_string()), (*from_peer, from_loc.as_f64()), (*to_peer, to_loc.as_f64()), ); diff --git a/crates/fdev/src/network_metrics_server.rs b/crates/fdev/src/network_metrics_server.rs index db52620ed..fbf7cebff 100644 --- a/crates/fdev/src/network_metrics_server.rs +++ b/crates/fdev/src/network_metrics_server.rs @@ -178,8 +178,16 @@ async fn pull_interface(ws: WebSocket, state: Arc) -> anyhow::Resul let mut changes = state.changes.subscribe(); while let Ok(msg) = changes.recv().await { match msg { - Change::AddedConnection { from, to } => { - let msg = PeerChange::added_connection_msg((from.0 .0, from.1), (to.0 .0, to.1)); + Change::AddedConnection { + transaction, + from, + to, + } => { + let msg = PeerChange::added_connection_msg( + transaction.as_ref(), + (from.0 .0, from.1), + (to.0 .0, to.1), + ); tx.send(Message::Binary(msg)).await?; } Change::RemovedConnection { from, at } => { @@ -204,6 +212,9 @@ struct PeerData { #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) enum Change { AddedConnection { + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + transaction: Option, from: (PeerIdHumanReadable, f64), to: (PeerIdHumanReadable, f64), }, @@ -249,7 +260,10 @@ impl ServerState { match self.peer_data.entry(from_peer_id) { dashmap::mapref::entry::Entry::Occupied(mut occ) => { - occ.get_mut().connections.push((to_peer_id, to_loc)); + let connections = &mut occ.get_mut().connections; + connections.push((to_peer_id, to_loc)); + connections.sort_unstable_by(|a, b| a.0.cmp(&b.0)); + connections.dedup(); } dashmap::mapref::entry::Entry::Vacant(vac) => { vac.insert(PeerData { @@ -261,7 +275,10 @@ impl ServerState { match self.peer_data.entry(to_peer_id) { dashmap::mapref::entry::Entry::Occupied(mut occ) => { - occ.get_mut().connections.push((from_peer_id, from_loc)); + let connections = &mut occ.get_mut().connections; + connections.push((from_peer_id, from_loc)); + connections.sort_unstable_by(|a, b| a.0.cmp(&b.0)); + connections.dedup(); } dashmap::mapref::entry::Entry::Vacant(vac) => { vac.insert(PeerData { @@ -272,6 +289,7 @@ impl ServerState { } let _ = self.changes.send(Change::AddedConnection { + transaction: added.transaction().map(|s| s.to_owned()), from: (from_peer_id.into(), from_loc), to: (to_peer_id.into(), to_loc), }); diff --git a/network-monitor/dist/index.html b/network-monitor/dist/index.html index f52f7085f..a987a8911 100644 --- a/network-monitor/dist/index.html +++ b/network-monitor/dist/index.html @@ -25,24 +25,35 @@

Freenet Network Monitor

id="peer-connections" style="display: none; position: absolute; z-index: 1" > -

Peer Connections

- +
+

Neighbours

+
+

Connections history

+
+ +
+ + + + + + + + + +
Peer Id Location + Connection Date + +
- - - - - - - - - -
Peer Id Location - Connection Date - -
diff --git a/network-monitor/src/generated/topology/added-connection.ts b/network-monitor/src/generated/topology/added-connection.ts index c142d3efb..5d552aede 100644 --- a/network-monitor/src/generated/topology/added-connection.ts +++ b/network-monitor/src/generated/topology/added-connection.ts @@ -34,69 +34,89 @@ export class AddedConnection ); } + transaction(): string | null; + transaction( + optionalEncoding: flatbuffers.Encoding + ): string | Uint8Array | null; + transaction(optionalEncoding?: any): string | Uint8Array | null { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset + ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) + : null; + } + from(): string | null; from(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; from(optionalEncoding?: any): string | Uint8Array | null { - const offset = this.bb!.__offset(this.bb_pos, 4); + const offset = this.bb!.__offset(this.bb_pos, 6); return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null; } fromLocation(): number { - const offset = this.bb!.__offset(this.bb_pos, 6); + const offset = this.bb!.__offset(this.bb_pos, 8); return offset ? this.bb!.readFloat64(this.bb_pos + offset) : 0.0; } to(): string | null; to(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; to(optionalEncoding?: any): string | Uint8Array | null { - const offset = this.bb!.__offset(this.bb_pos, 8); + const offset = this.bb!.__offset(this.bb_pos, 10); return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null; } toLocation(): number { - const offset = this.bb!.__offset(this.bb_pos, 10); + const offset = this.bb!.__offset(this.bb_pos, 12); return offset ? this.bb!.readFloat64(this.bb_pos + offset) : 0.0; } static startAddedConnection(builder: flatbuffers.Builder) { - builder.startObject(4); + builder.startObject(5); + } + + static addTransaction( + builder: flatbuffers.Builder, + transactionOffset: flatbuffers.Offset + ) { + builder.addFieldOffset(0, transactionOffset, 0); } static addFrom(builder: flatbuffers.Builder, fromOffset: flatbuffers.Offset) { - builder.addFieldOffset(0, fromOffset, 0); + builder.addFieldOffset(1, fromOffset, 0); } static addFromLocation(builder: flatbuffers.Builder, fromLocation: number) { - builder.addFieldFloat64(1, fromLocation, 0.0); + builder.addFieldFloat64(2, fromLocation, 0.0); } static addTo(builder: flatbuffers.Builder, toOffset: flatbuffers.Offset) { - builder.addFieldOffset(2, toOffset, 0); + builder.addFieldOffset(3, toOffset, 0); } static addToLocation(builder: flatbuffers.Builder, toLocation: number) { - builder.addFieldFloat64(3, toLocation, 0.0); + builder.addFieldFloat64(4, toLocation, 0.0); } static endAddedConnection(builder: flatbuffers.Builder): flatbuffers.Offset { const offset = builder.endObject(); - builder.requiredField(offset, 4); // from - builder.requiredField(offset, 8); // to + builder.requiredField(offset, 6); // from + builder.requiredField(offset, 10); // to return offset; } static createAddedConnection( builder: flatbuffers.Builder, + transactionOffset: flatbuffers.Offset, fromOffset: flatbuffers.Offset, fromLocation: number, toOffset: flatbuffers.Offset, toLocation: number ): flatbuffers.Offset { AddedConnection.startAddedConnection(builder); + AddedConnection.addTransaction(builder, transactionOffset); AddedConnection.addFrom(builder, fromOffset); AddedConnection.addFromLocation(builder, fromLocation); AddedConnection.addTo(builder, toOffset); @@ -106,6 +126,7 @@ export class AddedConnection unpack(): AddedConnectionT { return new AddedConnectionT( + this.transaction(), this.from(), this.fromLocation(), this.to(), @@ -114,6 +135,7 @@ export class AddedConnection } unpackTo(_o: AddedConnectionT): void { + _o.transaction = this.transaction(); _o.from = this.from(); _o.fromLocation = this.fromLocation(); _o.to = this.to(); @@ -123,6 +145,7 @@ export class AddedConnection export class AddedConnectionT implements flatbuffers.IGeneratedObject { constructor( + public transaction: string | Uint8Array | null = null, public from: string | Uint8Array | null = null, public fromLocation: number = 0.0, public to: string | Uint8Array | null = null, @@ -130,11 +153,14 @@ export class AddedConnectionT implements flatbuffers.IGeneratedObject { ) {} pack(builder: flatbuffers.Builder): flatbuffers.Offset { + const transaction = + this.transaction !== null ? builder.createString(this.transaction!) : 0; const from = this.from !== null ? builder.createString(this.from!) : 0; const to = this.to !== null ? builder.createString(this.to!) : 0; return AddedConnection.createAddedConnection( builder, + transaction, from, this.fromLocation, to, diff --git a/network-monitor/src/topology.ts b/network-monitor/src/topology.ts index c8ada934f..fc731a17c 100644 --- a/network-monitor/src/topology.ts +++ b/network-monitor/src/topology.ts @@ -16,6 +16,7 @@ interface Peer { } interface Connection { + transaction: string | null; id: string; location: number; } @@ -49,7 +50,7 @@ export function handleChange(peerChange: fbTopology.PeerChange) { break; } unpacked.currentState.forEach((connection) => { - handleAddedConnection(connection); + handleAddedConnection(connection, false); }); } catch (e) { console.error(e); @@ -58,17 +59,35 @@ export function handleChange(peerChange: fbTopology.PeerChange) { } } -export function handleAddedConnection(peerChange: fbTopology.AddedConnectionT) { +export function handleAddedConnection( + peerChange: fbTopology.AddedConnectionT, + skipNonTransaction = true +) { + if (!peerChange.transaction && skipNonTransaction) { + // only add connections if they have been reported as part of a transaction + // otherwise we end up with duplicates here + return; + } const added = peerChange; const fromAdded = added.from!.toString(); const toAdded = added.to!.toString(); + let transaction: string | null; + if (typeof added.transaction === "string") { + transaction = added.transaction; + } else if (added.transaction instanceof Uint8Array) { + transaction = new TextDecoder().decode(added.transaction); + } else { + transaction = null; + } const fromConnection: Connection = { + transaction: transaction, id: fromAdded, location: added.fromLocation, }; const toConnection: Connection = { + transaction: transaction, id: toAdded, location: added.toLocation, }; @@ -126,8 +145,22 @@ export function handleAddedConnection(peerChange: fbTopology.AddedConnectionT) { timestamp: Date.now(), }; - peers[fromAdded].history.push(changeInfo); - peers[toAdded].history.push(changeInfo); + // Check if the (to, from) pair or its reverse is already present in the history + const isPresent = peers[fromAdded].history.some( + (item) => + (item.from.id === changeInfo.from.id && + item.to.id === changeInfo.to.id && + item.from.transaction === changeInfo.from.transaction) || + (item.from.id === changeInfo.to.id && + item.to.id === changeInfo.from.id && + item.from.transaction === changeInfo.from.transaction) + ); + + // Only push changeInfo if the pair is not already present + if (!isPresent) { + peers[fromAdded].history.push(changeInfo); + peers[toAdded].history.push(changeInfo); + } } export function handleRemovedConnection( @@ -155,10 +188,12 @@ export function handleRemovedConnection( const changeInfo: ChangeInfo = { type: "Removed", from: { + transaction: null, id: fromRemoved, location: peers[fromRemoved].currentLocation, }, to: { + transaction: null, id: atRemoved, location: peers[atRemoved].currentLocation, }, @@ -201,7 +236,7 @@ function updateTable() { const row = document.createElement("tr"); row.addEventListener("mouseover", (event) => { setDivPosition(event); - showConnections(peers[peer]); + showPeerData(peers[peer]); }); row.addEventListener("mousemove", (event) => { setDivPosition(event); @@ -227,7 +262,7 @@ function updateTable() { for (let i = 0; i < tds.length; i++) { tds[i].addEventListener("mouseover", (event) => { setDivPosition(event); - showConnections(peers[peer]); + showPeerData(peers[peer]); }); row.addEventListener("mousemove", (event) => { setDivPosition(event); @@ -254,13 +289,15 @@ function updateTable() { const sortDirections: number[] = []; -document - .querySelector("#peers-table-h")! - .querySelectorAll("th")! - .forEach((header, index) => { - sortDirections.push(1); - tableSorting(header, index); - }); +document.addEventListener("DOMContentLoaded", () => { + document + .querySelector("#peers-table-h")! + .querySelectorAll("th")! + .forEach((header, index) => { + sortDirections.push(1); + tableSorting(header, index); + }); +}); function tableSorting(header: HTMLTableCellElement, index: number) { header.addEventListener("click", () => { @@ -295,10 +332,19 @@ function tableSorting(header: HTMLTableCellElement, index: number) { }); } -export function showConnections(peer: Peer) { +export function showPeerData(peer: Peer) { const id = peer.id; const connections: Connection[] = peer.connections ?? []; + // Set title + const peerDataHeader = document.getElementById("peer-connections-h")!; + peerDataHeader.innerHTML = ` +
+ Peer Id: ${id}
+ Location: ${peer.currentLocation ?? ""} +
+ `; + // Sort connections by location connections.sort((a, b) => a.location - b.location); @@ -332,25 +378,74 @@ export function showConnections(peer: Peer) { tableBody.appendChild(row); }); - // Set title - const idLocation = document.getElementById("peer-connections-t")!; - idLocation.innerHTML = `Peer Id: ${id}, Location: ${ - peer.currentLocation ?? "" - }`; + displayHistory(peer); } -function displayHistory(peer: Peer): string { - let historyHTML = ''; - historyHTML += - ""; +function displayHistory(peer: Peer) { + const peerConnections = document.getElementById("peer-connections")!; - historyHTML += peer.history - .map((change) => { - return ``; - }) - .join(""); + // Remove the existing table if it exists + const existingTable = peerConnections.querySelector("#connection-history"); + if (existingTable) { + existingTable.remove(); + } - historyHTML += "
TimestampTypeFromTo
${change.timestamp}${change.type}${change.from.id}${change.to.id}
"; + // Create a new table + const table = document.createElement("table"); + table.id = "connection-history"; + table.classList.add("table", "is-striped", "block", "is-bordered"); + table.style.overflowWrap = "break-word"; + + // Create the table header row + const thead = document.createElement("thead"); + const headerRow = document.createElement("tr"); + const typeHeader = document.createElement("th"); + typeHeader.textContent = "Type"; + const fromHeader = document.createElement("th"); + fromHeader.textContent = "From"; + const toHeader = document.createElement("th"); + toHeader.textContent = "To"; + const dateHeader = document.createElement("th"); + dateHeader.textContent = "Date"; + const transaction = document.createElement("th"); + transaction.textContent = "Transaction"; + headerRow.appendChild(typeHeader); + headerRow.appendChild(fromHeader); + headerRow.appendChild(toHeader); + headerRow.appendChild(dateHeader); + headerRow.appendChild(transaction); + thead.appendChild(headerRow); + table.appendChild(thead); + + // Create the table body + const tbody = document.createElement("tbody"); + const historyRows = peer.history.map((change) => { + const row = document.createElement("tr"); + const typeCell = document.createElement("td"); + typeCell.textContent = change.type; + const fromCell = document.createElement("td"); + fromCell.textContent = change.from.id.slice(-8); // Show last 8 characters + const toCell = document.createElement("td"); + toCell.textContent = change.to.id.slice(-8); // Show last 8 characters + const dateColumn = document.createElement("td"); + const date = new Date(change.timestamp); + dateColumn.textContent = `${date.toUTCString()} (${date.getMilliseconds()}ms)`; + const transactionCell = document.createElement("td"); + transactionCell.textContent = change.from.transaction + ? change.from.transaction + : ""; + row.appendChild(typeCell); + row.appendChild(fromCell); + row.appendChild(toCell); + row.appendChild(dateColumn); + row.appendChild(transactionCell); + return row; + }); + historyRows.forEach((row) => { + tbody.appendChild(row); + }); + table.appendChild(tbody); - return historyHTML; + // Append the new table to the peerConnections element + peerConnections.appendChild(table); } diff --git a/network-monitor/tests/app.test.ts b/network-monitor/tests/app.test.ts deleted file mode 100644 index 33f5ec26a..000000000 --- a/network-monitor/tests/app.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @jest-environment node - */ - -import * as flatbuffers from "flatbuffers"; -import * as fbTopology from "../src/generated/topology"; - -test("Flatbuffer deserialization test", () => { - // // Create a sample flatbuffer data - // const builder = new flatbuffers.Builder(); - // const nodeId = builder.createString("node1"); - // const nodeType = fbTopology.; - // const nodeOffset = fbTopology.Node.createNode(builder, nodeId, nodeType); - // const nodesOffset = fbTopology.Topology.createNodesVector(builder, [ - // nodeOffset, - // ]); - // fbTopology.Topology.startTopology(builder); - // fbTopology.Topology.addNodes(builder, nodesOffset); - // const topologyOffset = fbTopology.Topology.endTopology(builder); - // builder.finish(topologyOffset); - - // // Deserialize the flatbuffer data - // const buffer = builder.asUint8Array(); - // const topology = fbTopology.Topology.getRootAsTopology( - // new flatbuffers.ByteBuffer(buffer) - // ); - - // // Perform assertions on the deserialized data - // expect(topology.nodesLength()).toBe(1); - // const node = topology.nodes(0); - // expect(node.id()).toBe("node1"); - // expect(node.type()).toBe(fbTopology.NodeType.Router); - - const AddedConnectionMessage = new Uint8Array([ - 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 12, 0, 0, 0, 7, 0, 8, 0, 10, 0, 0, 0, - 0, 0, 0, 1, 16, 0, 0, 0, 12, 0, 28, 0, 4, 0, 12, 0, 8, 0, 20, 0, 12, 0, 0, - 0, 84, 0, 0, 0, 20, 0, 0, 0, 182, 64, 79, 180, 180, 193, 216, 63, 27, 191, - 192, 101, 40, 220, 226, 63, 52, 0, 0, 0, 49, 50, 68, 51, 75, 111, 111, 87, - 69, 106, 87, 74, 50, 53, 118, 71, 121, 66, 77, 68, 76, 78, 99, 104, 65, 105, - 97, 84, 76, 121, 81, 99, 105, 50, 53, 65, 70, 65, 71, 122, 82, 49, 71, 57, - 113, 98, 52, 86, 115, 82, 122, 66, 0, 0, 0, 0, 52, 0, 0, 0, 49, 50, 68, 51, - 75, 111, 111, 87, 75, 77, 75, 102, 99, 66, 54, 74, 100, 81, 101, 72, 107, - 112, 120, 121, 86, 70, 49, 87, 85, 85, 103, 121, 115, 85, 85, 54, 119, 66, - 71, 97, 116, 50, 49, 84, 54, 51, 109, 109, 98, 90, 53, 117, 0, 0, 0, 0, - ]); - - const buf = new flatbuffers.ByteBuffer( - new Uint8Array(AddedConnectionMessage) - ); - const peerChange = fbTopology.PeerChange.getRootAsPeerChange(buf); -}); diff --git a/network-monitor/tests/topology.test.ts b/network-monitor/tests/topology.test.ts index aa5226734..354b42366 100644 --- a/network-monitor/tests/topology.test.ts +++ b/network-monitor/tests/topology.test.ts @@ -10,7 +10,6 @@ import { handleAddedConnection, handleRemovedConnection, peers, - showConnections, } from "../src/topology"; describe("Network Monitor App", () => { @@ -20,6 +19,7 @@ describe("Network Monitor App", () => { test("should update peers table when receiving added connection", () => { const mockAddedConnection = new AddedConnectionT( + "tx1", "peer1", 0.6254, "peer2", @@ -32,17 +32,20 @@ describe("Network Monitor App", () => { id: "peer1", locationHistory: [], currentLocation: 0.6254, - connections: [{ id: "peer2", location: 0.2875 }], + connectionTimestamp: expect.any(Number), + connections: [{ id: "peer2", location: 0.2875, transaction: "tx1" }], history: [ { type: "Added", from: { id: "peer1", location: 0.6254, + transaction: "tx1", }, to: { id: "peer2", location: 0.2875, + transaction: "tx1", }, timestamp: expect.any(Number), }, @@ -52,17 +55,20 @@ describe("Network Monitor App", () => { id: "peer2", locationHistory: [], currentLocation: 0.2875, - connections: [{ id: "peer1", location: 0.6254 }], + connectionTimestamp: expect.any(Number), + connections: [{ id: "peer1", location: 0.6254, transaction: "tx1" }], history: [ { type: "Added", from: { id: "peer1", location: 0.6254, + transaction: "tx1", }, to: { id: "peer2", location: 0.2875, + transaction: "tx1", }, timestamp: expect.any(Number), }, diff --git a/schemas/flatbuffers/topology.fbs b/schemas/flatbuffers/topology.fbs index dfdf2eea2..aaa676a2e 100644 --- a/schemas/flatbuffers/topology.fbs +++ b/schemas/flatbuffers/topology.fbs @@ -1,6 +1,7 @@ namespace topology; table AddedConnection { + transaction: string; from: string(required); // encoded PeerId from_location: float64; to: string(required); // encoded PeerId From ba6ecce1f3459d2ec579f4c5e6916ff17d2a295e Mon Sep 17 00:00:00 2001 From: Ignacio Duart Date: Thu, 30 Nov 2023 18:19:55 +0100 Subject: [PATCH 10/12] Add modal for showing all the info on click --- network-monitor/dist/index.html | 2 +- network-monitor/src/topology.ts | 170 +++++++++++++++++-------- network-monitor/tests/topology.test.ts | 19 +-- 3 files changed, 132 insertions(+), 59 deletions(-) diff --git a/network-monitor/dist/index.html b/network-monitor/dist/index.html index a987a8911..2e490f60d 100644 --- a/network-monitor/dist/index.html +++ b/network-monitor/dist/index.html @@ -19,7 +19,7 @@

Freenet Network Monitor

-
+
conn.id === toAdded)) { - peers[fromAdded].connections.push(toConnection); + if (!peers[fromFullId].connections.some((conn) => conn.id === to)) { + peers[fromFullId].connections.push(toConnection); } } else { - peers[fromAdded] = { - id: fromAdded, + peers[fromFullId] = { + id: from, currentLocation: added.fromLocation, connectionTimestamp: Date.now(), connections: [toConnection], @@ -115,21 +136,22 @@ export function handleAddedConnection( }; } - if (peers[toAdded]) { - if (peers[toAdded].currentLocation !== added.toLocation) { - peers[toAdded].locationHistory.push({ - location: peers[toAdded].currentLocation, + const toFullId = to.full; + if (peers[toFullId]) { + if (peers[toFullId].currentLocation !== added.toLocation) { + peers[toFullId].locationHistory.push({ + location: peers[toFullId].currentLocation, timestamp: Date.now(), }); - peers[toAdded].currentLocation = added.toLocation; + peers[toFullId].currentLocation = added.toLocation; } - if (!peers[toAdded].connections.some((conn) => conn.id === fromAdded)) { - peers[toAdded].connections.push(toConnection); + if (!peers[toFullId].connections.some((conn) => conn.id === from)) { + peers[toFullId].connections.push(toConnection); } } else { - peers[toAdded] = { - id: toAdded, + peers[toFullId] = { + id: to, currentLocation: added.toLocation, connectionTimestamp: Date.now(), connections: [fromConnection], @@ -146,62 +168,62 @@ export function handleAddedConnection( }; // Check if the (to, from) pair or its reverse is already present in the history - const isPresent = peers[fromAdded].history.some( + const isPresent = peers[fromFullId].history.some( (item) => - (item.from.id === changeInfo.from.id && - item.to.id === changeInfo.to.id && + (item.from.id.full === changeInfo.from.id.full && + item.to.id.full === changeInfo.to.id.full && item.from.transaction === changeInfo.from.transaction) || - (item.from.id === changeInfo.to.id && - item.to.id === changeInfo.from.id && + (item.from.id.full === changeInfo.to.id.full && + item.to.id.full === changeInfo.from.id.full && item.from.transaction === changeInfo.from.transaction) ); // Only push changeInfo if the pair is not already present if (!isPresent) { - peers[fromAdded].history.push(changeInfo); - peers[toAdded].history.push(changeInfo); + peers[fromFullId].history.push(changeInfo); + peers[toFullId].history.push(changeInfo); } } export function handleRemovedConnection( peerChange: fbTopology.RemovedConnectionT ) { - const removed = peerChange; - const fromRemoved = removed.from!.toString(); - const atRemoved = removed.at!.toString(); - const index = peers[fromRemoved].connections.findIndex( - (connection) => connection.id === atRemoved + const from = new PeerId(peerChange.from!); + const at = new PeerId(peerChange.at!); + + const index = peers[from.full].connections.findIndex( + (connection) => connection.id.full === at.full ); if (index > -1) { - peers[fromRemoved].connections.splice(index, 1); + peers[from.full].connections.splice(index, 1); } - const reverseIndex = peers[atRemoved].connections.findIndex( - (connection: Connection) => connection.id === fromRemoved + const reverseIndex = peers[at.full].connections.findIndex( + (connection: Connection) => connection.id.full === from.full ); if (reverseIndex > -1) { - peers[atRemoved].connections.splice(reverseIndex, 1); + peers[at.full].connections.splice(reverseIndex, 1); } const changeInfo: ChangeInfo = { type: "Removed", from: { transaction: null, - id: fromRemoved, - location: peers[fromRemoved].currentLocation, + id: from, + location: peers[from.full].currentLocation, }, to: { transaction: null, - id: atRemoved, - location: peers[atRemoved].currentLocation, + id: at, + location: peers[at.full].currentLocation, }, timestamp: Date.now(), }; - peers[fromRemoved].history.push(changeInfo); - peers[atRemoved].history.push(changeInfo); + peers[from.full].history.push(changeInfo); + peers[at.full].history.push(changeInfo); } function updateTable() { @@ -245,9 +267,57 @@ function updateTable() { "mouseout", () => (peerConnectionsDiv.style.display = "none") ); + row.addEventListener("click", () => { + const modal = document.createElement("div"); + modal.classList.add("modal", "is-active"); + const modalBackground = document.createElement("div"); + modalBackground.classList.add("modal-background"); + modal.appendChild(modalBackground); + + const modalContent = document.createElement("div"); + modalContent.classList.add("modal-content"); + // Set the width of the modal content based on the viewport width + if (window.innerWidth >= 1200) { + modalContent.style.width = "80%"; + } else if (window.innerWidth >= 769) { + modalContent.style.width = "640px"; + } else { + modalContent.style.width = "100%"; + } + + const modalBox = document.createElement("div"); + modalBox.classList.add("box"); + modalBox.style.overflowWrap = "break-word"; + modalBox.style.whiteSpace = "normal"; + + const peerData = document.getElementById("peer-connections")!.innerHTML; + modalBox.innerHTML = peerData; + + // Make the table width 100% to ensure it fits within the modal box + modalBox.querySelectorAll("table").forEach((table) => { + if (table) { + table.style.width = "100%"; + table.style.tableLayout = "fixed"; + } + }); + + modalContent.appendChild(modalBox); + modal.appendChild(modalContent); + + const closeModal = document.createElement("button"); + closeModal.classList.add("modal-close", "is-large"); + modal.appendChild(closeModal); + + const containerDiv = document.querySelector(".container.main")!; + containerDiv.appendChild(modal); + modal.addEventListener("click", () => { + modal.style.display = "none"; + modal.remove(); + }); + }); const id = document.createElement("td"); - id.textContent = peerData.id; + id.textContent = "..." + peerData.id.short; const location = document.createElement("td"); location.textContent = peerData.currentLocation.toString(); const connectionTimestamp = document.createElement("td"); @@ -370,7 +440,7 @@ export function showPeerData(peer: Peer) { connections.forEach((connection) => { const row = document.createElement("tr"); const idCell = document.createElement("td"); - idCell.textContent = connection.id.toString() ?? ""; + idCell.textContent = connection.id.short; const locationCell = document.createElement("td"); locationCell.textContent = connection.location.toString() ?? ""; row.appendChild(idCell); @@ -424,9 +494,9 @@ function displayHistory(peer: Peer) { const typeCell = document.createElement("td"); typeCell.textContent = change.type; const fromCell = document.createElement("td"); - fromCell.textContent = change.from.id.slice(-8); // Show last 8 characters + fromCell.textContent = "..." + change.from.id.short; // Show last 8 characters const toCell = document.createElement("td"); - toCell.textContent = change.to.id.slice(-8); // Show last 8 characters + toCell.textContent = "..." + change.to.id.short; // Show last 8 characters const dateColumn = document.createElement("td"); const date = new Date(change.timestamp); dateColumn.textContent = `${date.toUTCString()} (${date.getMilliseconds()}ms)`; diff --git a/network-monitor/tests/topology.test.ts b/network-monitor/tests/topology.test.ts index 354b42366..41ec74900 100644 --- a/network-monitor/tests/topology.test.ts +++ b/network-monitor/tests/topology.test.ts @@ -10,6 +10,7 @@ import { handleAddedConnection, handleRemovedConnection, peers, + PeerId, } from "../src/topology"; describe("Network Monitor App", () => { @@ -27,23 +28,25 @@ describe("Network Monitor App", () => { ); handleAddedConnection(mockAddedConnection); + const peer1 = new PeerId("peer1"); + const peer2 = new PeerId("peer2"); expect(peers).toEqual({ peer1: { - id: "peer1", + id: peer1, locationHistory: [], currentLocation: 0.6254, connectionTimestamp: expect.any(Number), - connections: [{ id: "peer2", location: 0.2875, transaction: "tx1" }], + connections: [{ id: peer2, location: 0.2875, transaction: "tx1" }], history: [ { type: "Added", from: { - id: "peer1", + id: peer1, location: 0.6254, transaction: "tx1", }, to: { - id: "peer2", + id: peer2, location: 0.2875, transaction: "tx1", }, @@ -52,21 +55,21 @@ describe("Network Monitor App", () => { ], }, peer2: { - id: "peer2", + id: peer2, locationHistory: [], currentLocation: 0.2875, connectionTimestamp: expect.any(Number), - connections: [{ id: "peer1", location: 0.6254, transaction: "tx1" }], + connections: [{ id: peer1, location: 0.6254, transaction: "tx1" }], history: [ { type: "Added", from: { - id: "peer1", + id: peer1, location: 0.6254, transaction: "tx1", }, to: { - id: "peer2", + id: peer2, location: 0.2875, transaction: "tx1", }, From d683821a326177493595320e6953129eec72db13 Mon Sep 17 00:00:00 2001 From: Ignacio Duart Date: Thu, 30 Nov 2023 19:38:58 +0100 Subject: [PATCH 11/12] Show connections in a ring graph --- network-monitor/dist/index.html | 6 +- network-monitor/package.json | 2 + network-monitor/src/topology.ts | 225 +++++++++++++++++++++++++++--- network-monitor/webpack.config.js | 2 +- 4 files changed, 212 insertions(+), 23 deletions(-) diff --git a/network-monitor/dist/index.html b/network-monitor/dist/index.html index 2e490f60d..3ac30651c 100644 --- a/network-monitor/dist/index.html +++ b/network-monitor/dist/index.html @@ -22,11 +22,11 @@

Freenet Network Monitor

-
+
@@ -54,6 +54,7 @@

Connections history

+
diff --git a/network-monitor/src/topology.ts b/network-monitor/src/topology.ts index b9640ffec..2961ee5bf 100644 --- a/network-monitor/src/topology.ts +++ b/network-monitor/src/topology.ts @@ -1,5 +1,6 @@ import * as fbTopology from "./generated/topology"; import * as d3 from "d3"; +import { BaseType } from "d3-selection"; export let peers: PeerList = {}; @@ -73,6 +74,7 @@ export function handleChange(peerChange: fbTopology.PeerChange) { unpacked.currentState.forEach((connection) => { handleAddedConnection(connection, false); }); + ringHistogram(Object.values(peers)); } catch (e) { console.error(e); } finally { @@ -312,8 +314,8 @@ function updateTable() { closeModal.classList.add("modal-close", "is-large"); modal.appendChild(closeModal); - const containerDiv = document.querySelector(".container.main")!; - containerDiv.appendChild(modal); + const mainDiv = document.querySelector(".main")!; + mainDiv.appendChild(modal); modal.addEventListener("click", () => { modal.style.display = "none"; const graphContainer = d3.select("#peer-conns-graph"); @@ -392,6 +394,7 @@ function updateTable() { const sortDirections: number[] = []; document.addEventListener("DOMContentLoaded", () => { + ringHistogram(Object.values(peers)); document .querySelector("#peers-table-h")! .querySelectorAll("th")! @@ -510,19 +513,15 @@ function ringVisualization( e: MouseEvent, d: { value: number; legend: string } ) { - const circleRect = this.getBoundingClientRect(); - const mouseX = e.clientX - circleRect.left; // Get the mouse's x position within the circle - const mouseY = e.clientY - circleRect.top; // Get the mouse's y position within the circle tooltip .style("visibility", "visible") .style("position", "absolute") .style("stroke", "black") - .style("z-index", "2") .html(`${d.legend}: ${d.value}`); const tooltipRect = tooltip.node()!.getBoundingClientRect(); tooltip - .style("left", circleRect.left + mouseX - tooltipRect.width / 1.5 + "px") - .style("top", circleRect.top + mouseY - tooltipRect.height / 1.5 + "px"); + .style("left", e.clientX - tooltipRect.width / 2 + "px") + .style("top", e.clientY - tooltipRect.height / 2 + "px"); d3.select(this).style("stroke", "black"); } @@ -558,9 +557,6 @@ function ringVisualization( d: { value: number; legend: string } ) { const distance = getDistance(referencePoint.value, d.value).toFixed(5); - const lineRect = this.getBoundingClientRect(); - const mouseX = e.clientX - lineRect.left; // Get the mouse's x position within the line - const mouseY = e.clientY - lineRect.top; // Get the mouse's y position within the line tooltip .style("visibility", "visible") .style("position", "absolute") @@ -568,14 +564,8 @@ function ringVisualization( .html(`Distance: ${distance}`); const tooltipRect = tooltip.node()!.getBoundingClientRect(); tooltip - .style( - "left", - lineRect.left + window.scrollX + mouseX - tooltipRect.width + "px" - ) - .style( - "top", - lineRect.top + window.scrollY + mouseY - tooltipRect.height / 1.5 + "px" - ); + .style("left", e.clientX - tooltipRect.width / 2 + "px") + .style("top", e.clientY - tooltipRect.height / 2 + "px"); d3.select(this).style("stroke", "black"); } @@ -706,3 +696,124 @@ function displayHistory(peer: Peer) { // Append the new table to the peerConnections element peerDetails.appendChild(table); } + +function ringHistogram(peerLocations: Peer[]) { + const width = 500; + const height = 300; + const margin = { top: 10, right: 30, bottom: 50, left: 60 }; + const innerWidth = width - margin.left - margin.right; + + const container = d3.select("#peers-histogram"); + container.selectAll("*").remove(); + + const svg = container + .append("svg") + .attr("width", width) + .attr("height", height) + .append("g") + .attr("transform", `translate(${margin.left}, ${margin.top})`); + + const bucketSize = 0.05; + + const binsData: number[] = Array(Math.ceil(1 / bucketSize)).fill(0); + peerLocations.forEach((peer) => { + const binIndex = Math.floor(peer.currentLocation / bucketSize); + binsData[binIndex]++; + }); + + const histogram = binsData.map((count, i) => ({ + x0: i * bucketSize, + x1: (i + 1) * bucketSize, + count, + })); + + const xScale = d3.scaleLinear().domain([0, 1]).range([0, innerWidth]); + const legendSpace = 50; + const adjustedHeight = height - legendSpace; + const padding = 10; + const yScale = d3 + .scaleLinear() + .domain([0, d3.max(histogram, (d) => d.count)! + padding]) + .range([adjustedHeight, 0]); + + const colorScale = d3 + .scaleQuantile() + .domain(binsData) + .range(d3.schemeYlGn[9]); + + const bins = svg + .selectAll("rect") + .data(histogram) + .enter() + .append("rect") + .attr("x", (d) => xScale(d.x0!)) + .attr("y", (d) => yScale(d.count)) + .attr("width", innerWidth / histogram.length) + .attr("height", (d) => adjustedHeight - yScale(d.count)) + .attr("fill", (d) => colorScale(d.count)) + .attr("stroke", "black") + .attr("stroke-width", 1); + + const tooltip = container + .append("div") + .attr("class", "bin-tooltip") + .style("background-color", "white") + .style("border", "solid") + .style("border-width", "2px") + .style("border-radius", "5px") + .style("padding", "5px") + .style("opacity", 0) + .style("position", "absolute"); + + bins + .on("mouseover", (event: MouseEvent, d) => { + tooltip.transition().duration(200).style("opacity", 0.9); + tooltip + .html( + `Number of peers: ${d.count}, Subrange: [${d.x0.toFixed( + 2 + )}, ${d.x1.toFixed(2)})` + ) + .style("left", event.clientX + window.scrollX + "px") + .style("top", event.clientY + window.scrollY - 150 + "px"); + }) + .on("mousemove", (event, _) => { + tooltip + .style("left", event.clientX + window.scrollX + "px") + .style("top", event.clientY + window.scrollY - 150 + "px"); + }) + .on("mouseout", (_) => { + tooltip.transition().duration(500).style("opacity", 0); + }); + + const xAxisTicks = Math.floor(innerWidth / 50); // 50 is the desired space between ticks + const xAxis = d3.axisBottom(xScale).ticks(xAxisTicks); + svg + .append("g") + .attr("transform", `translate(0, ${adjustedHeight})`) + .call(xAxis); + + const yAxisTicks = Math.floor(adjustedHeight / 50); // 50 is the desired space between ticks + const yAxis = d3.axisLeft(yScale).ticks(yAxisTicks); + svg.append("g").call(yAxis); + + // Position the legend within the SVG area + svg + .append("text") + .attr("fill", "#000") + .attr("x", innerWidth / 2) + .attr("y", height - margin.bottom / 4) + .attr("text-anchor", "middle") + .attr("font-size", "14px") + .text("Peer Locations"); + + svg + .append("text") + .attr("fill", "#000") + .attr("y", margin.left - 100) + .attr("x", -adjustedHeight / 2) + .attr("transform", "rotate(-90)") + .attr("text-anchor", "middle") + .attr("font-size", "14px") + .text("Amount of Peers"); +}