refactor attachment

This commit is contained in:
John Smith 2022-12-26 16:33:48 -05:00
parent 525baea32c
commit f49e4f0892
11 changed files with 402 additions and 239 deletions

View File

@ -388,7 +388,11 @@ reply - reply to an AppCall not handled directly by the server
//////////////////////////////////////////// ////////////////////////////////////////////
pub fn update_attachment(&mut self, attachment: veilid_core::VeilidStateAttachment) { pub fn update_attachment(&mut self, attachment: veilid_core::VeilidStateAttachment) {
self.inner_mut().ui.set_attachment_state(attachment.state); self.inner_mut().ui.set_attachment_state(
attachment.state,
attachment.public_internet_ready,
attachment.local_network_ready,
);
} }
pub fn update_network_status(&mut self, network: veilid_core::VeilidStateNetwork) { pub fn update_network_status(&mut self, network: veilid_core::VeilidStateNetwork) {

View File

@ -51,6 +51,8 @@ pub type UICallback = Box<dyn Fn(&mut Cursive) + Send>;
struct UIState { struct UIState {
attachment_state: Dirty<AttachmentState>, attachment_state: Dirty<AttachmentState>,
public_internet_ready: Dirty<bool>,
local_network_ready: Dirty<bool>,
network_started: Dirty<bool>, network_started: Dirty<bool>,
network_down_up: Dirty<(f32, f32)>, network_down_up: Dirty<(f32, f32)>,
connection_state: Dirty<ConnectionState>, connection_state: Dirty<ConnectionState>,
@ -62,6 +64,8 @@ impl UIState {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
attachment_state: Dirty::new(AttachmentState::Detached), attachment_state: Dirty::new(AttachmentState::Detached),
public_internet_ready: Dirty::new(false),
local_network_ready: Dirty::new(false),
network_started: Dirty::new(false), network_started: Dirty::new(false),
network_down_up: Dirty::new((0.0, 0.0)), network_down_up: Dirty::new((0.0, 0.0)),
connection_state: Dirty::new(ConnectionState::Disconnected), connection_state: Dirty::new(ConnectionState::Disconnected),
@ -234,17 +238,28 @@ impl UI {
fn peers(s: &mut Cursive) -> ViewRef<PeersTableView> { fn peers(s: &mut Cursive) -> ViewRef<PeersTableView> {
s.find_name("peers").unwrap() s.find_name("peers").unwrap()
} }
fn render_attachment_state<'a>(inner: &mut UIInner) -> &'a str { fn render_attachment_state(inner: &mut UIInner) -> String {
match inner.ui_state.attachment_state.get() { let att = match inner.ui_state.attachment_state.get() {
AttachmentState::Detached => " Detached [----]", AttachmentState::Detached => "[----]",
AttachmentState::Attaching => "Attaching [/ ]", AttachmentState::Attaching => "[/ ]",
AttachmentState::AttachedWeak => " Attached [| ]", AttachmentState::AttachedWeak => "[| ]",
AttachmentState::AttachedGood => " Attached [|| ]", AttachmentState::AttachedGood => "[|| ]",
AttachmentState::AttachedStrong => " Attached [||| ]", AttachmentState::AttachedStrong => "[||| ]",
AttachmentState::FullyAttached => " Attached [||||]", AttachmentState::FullyAttached => "[||||]",
AttachmentState::OverAttached => " Attached [++++]", AttachmentState::OverAttached => "[++++]",
AttachmentState::Detaching => "Detaching [////]", AttachmentState::Detaching => "[////]",
} };
let pi = if *inner.ui_state.public_internet_ready.get() {
"+P"
} else {
"-p"
};
let ln = if *inner.ui_state.local_network_ready.get() {
"+L"
} else {
"-l"
};
format!("{}{}{}", att, pi, ln)
} }
fn render_network_status(inner: &mut UIInner) -> String { fn render_network_status(inner: &mut UIInner) -> String {
match inner.ui_state.network_started.get() { match inner.ui_state.network_started.get() {
@ -832,9 +847,20 @@ impl UI {
inner.cmdproc = Some(cmdproc); inner.cmdproc = Some(cmdproc);
let _ = inner.cb_sink.send(Box::new(UI::update_cb)); let _ = inner.cb_sink.send(Box::new(UI::update_cb));
} }
pub fn set_attachment_state(&mut self, state: AttachmentState) { pub fn set_attachment_state(
&mut self,
state: AttachmentState,
public_internet_ready: bool,
local_network_ready: bool,
) {
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.borrow_mut();
inner.ui_state.attachment_state.set(state); inner.ui_state.attachment_state.set(state);
inner
.ui_state
.public_internet_ready
.set(public_internet_ready);
inner.ui_state.local_network_ready.set(local_network_ready);
let _ = inner.cb_sink.send(Box::new(UI::update_cb)); let _ = inner.cb_sink.send(Box::new(UI::update_cb));
} }
pub fn set_network_status( pub fn set_network_status(

View File

@ -2,106 +2,10 @@ use crate::crypto::Crypto;
use crate::network_manager::*; use crate::network_manager::*;
use crate::routing_table::*; use crate::routing_table::*;
use crate::*; use crate::*;
use core::convert::TryFrom;
use core::fmt;
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
use serde::*;
state_machine! {
derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,)
pub Attachment(Detached)
//---
Detached(AttachRequested) => Attaching [StartAttachment],
Attaching => {
AttachmentStopped => Detached,
WeakPeers => AttachedWeak,
GoodPeers => AttachedGood,
StrongPeers => AttachedStrong,
FullPeers => FullyAttached,
TooManyPeers => OverAttached,
DetachRequested => Detaching [StopAttachment]
},
AttachedWeak => {
NoPeers => Attaching,
GoodPeers => AttachedGood,
StrongPeers => AttachedStrong,
FullPeers => FullyAttached,
TooManyPeers => OverAttached,
DetachRequested => Detaching [StopAttachment]
},
AttachedGood => {
NoPeers => Attaching,
WeakPeers => AttachedWeak,
StrongPeers => AttachedStrong,
FullPeers => FullyAttached,
TooManyPeers => OverAttached,
DetachRequested => Detaching [StopAttachment]
},
AttachedStrong => {
NoPeers => Attaching,
WeakPeers => AttachedWeak,
GoodPeers => AttachedGood,
FullPeers => FullyAttached,
TooManyPeers => OverAttached,
DetachRequested => Detaching [StopAttachment]
},
FullyAttached => {
NoPeers => Attaching,
WeakPeers => AttachedWeak,
GoodPeers => AttachedGood,
StrongPeers => AttachedStrong,
TooManyPeers => OverAttached,
DetachRequested => Detaching [StopAttachment]
},
OverAttached => {
NoPeers => Attaching,
WeakPeers => AttachedWeak,
GoodPeers => AttachedGood,
StrongPeers => AttachedStrong,
FullPeers => FullyAttached,
DetachRequested => Detaching [StopAttachment]
},
Detaching => {
AttachmentStopped => Detached,
},
}
impl fmt::Display for AttachmentState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let out = match self {
AttachmentState::Attaching => "attaching".to_owned(),
AttachmentState::AttachedWeak => "attached_weak".to_owned(),
AttachmentState::AttachedGood => "attached_good".to_owned(),
AttachmentState::AttachedStrong => "attached_strong".to_owned(),
AttachmentState::FullyAttached => "fully_attached".to_owned(),
AttachmentState::OverAttached => "over_attached".to_owned(),
AttachmentState::Detaching => "detaching".to_owned(),
AttachmentState::Detached => "detached".to_owned(),
};
write!(f, "{}", out)
}
}
impl TryFrom<String> for AttachmentState {
type Error = ();
fn try_from(s: String) -> Result<Self, Self::Error> {
Ok(match s.as_str() {
"attaching" => AttachmentState::Attaching,
"attached_weak" => AttachmentState::AttachedWeak,
"attached_good" => AttachmentState::AttachedGood,
"attached_strong" => AttachmentState::AttachedStrong,
"fully_attached" => AttachmentState::FullyAttached,
"over_attached" => AttachmentState::OverAttached,
"detaching" => AttachmentState::Detaching,
"detached" => AttachmentState::Detached,
_ => return Err(()),
})
}
}
pub struct AttachmentManagerInner { pub struct AttachmentManagerInner {
attachment_machine: CallbackStateMachine<Attachment>, last_attachment_state: AttachmentState,
last_routing_table_health: Option<RoutingTableHealth>,
maintain_peers: bool, maintain_peers: bool,
attach_ts: Option<Timestamp>, attach_ts: Option<Timestamp>,
update_callback: Option<UpdateCallback>, update_callback: Option<UpdateCallback>,
@ -140,7 +44,8 @@ impl AttachmentManager {
} }
fn new_inner() -> AttachmentManagerInner { fn new_inner() -> AttachmentManagerInner {
AttachmentManagerInner { AttachmentManagerInner {
attachment_machine: CallbackStateMachine::new(), last_attachment_state: AttachmentState::Detached,
last_routing_table_health: None,
maintain_peers: false, maintain_peers: false,
attach_ts: None, attach_ts: None,
update_callback: None, update_callback: None,
@ -175,11 +80,11 @@ impl AttachmentManager {
} }
pub fn is_attached(&self) -> bool { pub fn is_attached(&self) -> bool {
let s = self.inner.lock().attachment_machine.state(); let s = self.inner.lock().last_attachment_state;
!matches!(s, AttachmentState::Detached | AttachmentState::Detaching) !matches!(s, AttachmentState::Detached | AttachmentState::Detaching)
} }
pub fn is_detached(&self) -> bool { pub fn is_detached(&self) -> bool {
let s = self.inner.lock().attachment_machine.state(); let s = self.inner.lock().last_attachment_state;
matches!(s, AttachmentState::Detached) matches!(s, AttachmentState::Detached)
} }
@ -188,71 +93,94 @@ impl AttachmentManager {
} }
fn translate_routing_table_health( fn translate_routing_table_health(
health: RoutingTableHealth, health: &RoutingTableHealth,
config: &VeilidConfigRoutingTable, config: &VeilidConfigRoutingTable,
) -> AttachmentInput { ) -> AttachmentState {
if health.reliable_entry_count >= config.limit_over_attached.try_into().unwrap() { if health.reliable_entry_count >= config.limit_over_attached.try_into().unwrap() {
return AttachmentInput::TooManyPeers; return AttachmentState::OverAttached;
} }
if health.reliable_entry_count >= config.limit_fully_attached.try_into().unwrap() { if health.reliable_entry_count >= config.limit_fully_attached.try_into().unwrap() {
return AttachmentInput::FullPeers; return AttachmentState::FullyAttached;
} }
if health.reliable_entry_count >= config.limit_attached_strong.try_into().unwrap() { if health.reliable_entry_count >= config.limit_attached_strong.try_into().unwrap() {
return AttachmentInput::StrongPeers; return AttachmentState::AttachedStrong;
} }
if health.reliable_entry_count >= config.limit_attached_good.try_into().unwrap() { if health.reliable_entry_count >= config.limit_attached_good.try_into().unwrap() {
return AttachmentInput::GoodPeers; return AttachmentState::AttachedGood;
} }
if health.reliable_entry_count >= config.limit_attached_weak.try_into().unwrap() if health.reliable_entry_count >= config.limit_attached_weak.try_into().unwrap()
|| health.unreliable_entry_count >= config.limit_attached_weak.try_into().unwrap() || health.unreliable_entry_count >= config.limit_attached_weak.try_into().unwrap()
{ {
return AttachmentInput::WeakPeers; return AttachmentState::AttachedWeak;
}
AttachmentInput::NoPeers
}
fn translate_attachment_state(state: &AttachmentState) -> AttachmentInput {
match state {
AttachmentState::OverAttached => AttachmentInput::TooManyPeers,
AttachmentState::FullyAttached => AttachmentInput::FullPeers,
AttachmentState::AttachedStrong => AttachmentInput::StrongPeers,
AttachmentState::AttachedGood => AttachmentInput::GoodPeers,
AttachmentState::AttachedWeak => AttachmentInput::WeakPeers,
AttachmentState::Attaching => AttachmentInput::NoPeers,
_ => panic!("Invalid state"),
} }
AttachmentState::Attaching
} }
async fn update_attachment(&self) { /// Update attachment and network readiness state
let new_peer_state_input = { /// and possibly send a VeilidUpdate::Attachment
let inner = self.inner.lock(); fn update_attachment(&self) {
// update the routing table health
let routing_table = self.network_manager().routing_table();
let health = routing_table.get_routing_table_health();
let opt_update = {
let mut inner = self.inner.lock();
let old_peer_state_input = // Check if the routing table health is different
AttachmentManager::translate_attachment_state(&inner.attachment_machine.state()); if let Some(last_routing_table_health) = &inner.last_routing_table_health {
// If things are the same, just return
if last_routing_table_health == &health {
return;
}
}
// get reliable peer count from routing table // Swap in new health numbers
let routing_table = self.network_manager().routing_table(); let opt_previous_health = inner.last_routing_table_health.take();
let health = routing_table.get_routing_table_health(); inner.last_routing_table_health = Some(health.clone());
// Calculate new attachment state
let config = self.config(); let config = self.config();
let routing_table_config = &config.get().network.routing_table; let routing_table_config = &config.get().network.routing_table;
let previous_attachment_state = inner.last_attachment_state;
inner.last_attachment_state =
AttachmentManager::translate_routing_table_health(&health, routing_table_config);
let new_peer_state_input = // If we don't have an update callback yet for some reason, just return now
AttachmentManager::translate_routing_table_health(health, routing_table_config); let Some(update_callback) = inner.update_callback.clone() else {
return;
};
if old_peer_state_input == new_peer_state_input { // Send update if one of:
None // * the attachment state has changed
// * routing domain readiness has changed
// * this is our first routing table health check
let send_update = previous_attachment_state != inner.last_attachment_state
|| opt_previous_health
.map(|x| {
x.public_internet_ready != health.public_internet_ready
|| x.local_network_ready != health.local_network_ready
})
.unwrap_or(true);
if send_update {
Some((update_callback, Self::get_veilid_state_inner(&*inner)))
} else { } else {
Some(new_peer_state_input) None
} }
}; };
if let Some(next_input) = new_peer_state_input {
let _ = self.process_input(&next_input).await; // Send the update outside of the lock
if let Some(update) = opt_update {
(update.0)(VeilidUpdate::Attachment(update.1));
} }
} }
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
async fn attachment_maintainer(self) { async fn attachment_maintainer(self) {
debug!("attachment starting"); {
self.inner.lock().attach_ts = Some(get_aligned_timestamp()); let mut inner = self.inner.lock();
inner.last_attachment_state = AttachmentState::Attaching;
self.inner.lock().attach_ts = Some(get_aligned_timestamp());
debug!("attachment starting");
}
let netman = self.network_manager(); let netman = self.network_manager();
let mut restart; let mut restart;
@ -281,13 +209,21 @@ impl AttachmentManager {
break; break;
} }
self.update_attachment().await; // Update attachment and network readiness state
// and possibly send a VeilidUpdate::Attachment
self.update_attachment();
// sleep should be at the end in case maintain_peers changes state // sleep should be at the end in case maintain_peers changes state
sleep(1000).await; sleep(1000).await;
} }
debug!("stopped maintaining peers"); debug!("stopped maintaining peers");
if !restart {
let mut inner = self.inner.lock();
inner.last_attachment_state = AttachmentState::Detaching;
debug!("attachment stopping");
}
debug!("stopping network"); debug!("stopping network");
netman.shutdown().await; netman.shutdown().await;
@ -300,13 +236,12 @@ impl AttachmentManager {
sleep(1000).await; sleep(1000).await;
} }
trace!("stopping attachment"); {
let attachment_machine = self.inner.lock().attachment_machine.clone(); let mut inner = self.inner.lock();
let _output = attachment_machine inner.last_attachment_state = AttachmentState::Detached;
.consume(&AttachmentInput::AttachmentStopped) inner.attach_ts = None;
.await; debug!("attachment stopped");
debug!("attachment stopped"); }
self.inner.lock().attach_ts = None;
} }
#[instrument(level = "debug", skip_all, err)] #[instrument(level = "debug", skip_all, err)]
@ -315,15 +250,7 @@ impl AttachmentManager {
{ {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
inner.update_callback = Some(update_callback.clone()); inner.update_callback = Some(update_callback.clone());
let update_callback2 = update_callback.clone(); }
inner.attachment_machine.set_state_change_callback(Arc::new(
move |_old_state: AttachmentState, new_state: AttachmentState| {
update_callback2(VeilidUpdate::Attachment(VeilidStateAttachment {
state: new_state,
}))
},
));
};
self.network_manager().init(update_callback).await?; self.network_manager().init(update_callback).await?;
@ -339,18 +266,20 @@ impl AttachmentManager {
} }
#[instrument(level = "trace", skip(self))] #[instrument(level = "trace", skip(self))]
fn attach(&self) { pub async fn attach(&self) -> bool {
// Create long-running connection maintenance routine // Create long-running connection maintenance routine
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
if inner.attachment_maintainer_jh.is_some() { if inner.attachment_maintainer_jh.is_some() {
return; return false;
} }
inner.maintain_peers = true; inner.maintain_peers = true;
inner.attachment_maintainer_jh = Some(spawn(self.clone().attachment_maintainer())); inner.attachment_maintainer_jh = Some(spawn(self.clone().attachment_maintainer()));
true
} }
#[instrument(level = "trace", skip(self))] #[instrument(level = "trace", skip(self))]
async fn detach(&self) { pub async fn detach(&self) -> bool {
let attachment_maintainer_jh = { let attachment_maintainer_jh = {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
let attachment_maintainer_jh = inner.attachment_maintainer_jh.take(); let attachment_maintainer_jh = inner.attachment_maintainer_jh.take();
@ -362,57 +291,34 @@ impl AttachmentManager {
}; };
if let Some(jh) = attachment_maintainer_jh { if let Some(jh) = attachment_maintainer_jh {
jh.await; jh.await;
true
} else {
false
} }
} }
async fn handle_output(&self, output: &AttachmentOutput) { pub fn get_attachment_state(&self) -> AttachmentState {
match output { self.inner.lock().last_attachment_state
AttachmentOutput::StartAttachment => self.attach(), }
AttachmentOutput::StopAttachment => self.detach().await,
fn get_veilid_state_inner(inner: &AttachmentManagerInner) -> VeilidStateAttachment {
VeilidStateAttachment {
state: inner.last_attachment_state,
public_internet_ready: inner
.last_routing_table_health
.as_ref()
.map(|x| x.public_internet_ready)
.unwrap_or(false),
local_network_ready: inner
.last_routing_table_health
.as_ref()
.map(|x| x.local_network_ready)
.unwrap_or(false),
} }
} }
async fn process_input(&self, input: &AttachmentInput) -> EyreResult<()> {
let attachment_machine = self.inner.lock().attachment_machine.clone();
let output = attachment_machine.consume(input).await;
match output {
Err(e) => Err(eyre!(
"invalid input '{:?}' for state machine in state '{:?}': {:?}",
input,
attachment_machine.state(),
e
)),
Ok(v) => {
if let Some(o) = v {
self.handle_output(&o).await;
}
Ok(())
}
}
}
#[instrument(level = "trace", skip(self), err)]
pub async fn request_attach(&self) -> EyreResult<()> {
self.process_input(&AttachmentInput::AttachRequested)
.await
.map_err(|e| eyre!("Attach request failed: {}", e))
}
#[instrument(level = "trace", skip(self), err)]
pub async fn request_detach(&self) -> EyreResult<()> {
self.process_input(&AttachmentInput::DetachRequested)
.await
.map_err(|e| eyre!("Detach request failed: {}", e))
}
pub fn get_state(&self) -> AttachmentState {
let attachment_machine = self.inner.lock().attachment_machine.clone();
attachment_machine.state()
}
pub fn get_veilid_state(&self) -> VeilidStateAttachment { pub fn get_veilid_state(&self) -> VeilidStateAttachment {
VeilidStateAttachment { let inner = self.inner.lock();
state: self.get_state(), Self::get_veilid_state_inner(&*inner)
}
} }
} }

View File

@ -34,7 +34,6 @@ mod veilid_config;
mod veilid_layer_filter; mod veilid_layer_filter;
pub use self::api_tracing_layer::ApiTracingLayer; pub use self::api_tracing_layer::ApiTracingLayer;
pub use self::attachment_manager::AttachmentState;
pub use self::core_context::{api_startup, api_startup_json, UpdateCallback}; pub use self::core_context::{api_startup, api_startup_json, UpdateCallback};
pub use self::veilid_api::*; pub use self::veilid_api::*;
pub use self::veilid_config::*; pub use self::veilid_config::*;

View File

@ -51,7 +51,7 @@ pub struct LowLevelPortInfo {
pub type RoutingTableEntryFilter<'t> = pub type RoutingTableEntryFilter<'t> =
Box<dyn FnMut(&RoutingTableInner, DHTKey, Option<Arc<BucketEntry>>) -> bool + Send + 't>; Box<dyn FnMut(&RoutingTableInner, DHTKey, Option<Arc<BucketEntry>>) -> bool + Send + 't>;
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct RoutingTableHealth { pub struct RoutingTableHealth {
/// Number of reliable (responsive) entries in the routing table /// Number of reliable (responsive) entries in the routing table
pub reliable_entry_count: usize, pub reliable_entry_count: usize,
@ -60,9 +60,9 @@ pub struct RoutingTableHealth {
/// Number of dead (always unresponsive) entries in the routing table /// Number of dead (always unresponsive) entries in the routing table
pub dead_entry_count: usize, pub dead_entry_count: usize,
/// If PublicInternet network class is valid yet /// If PublicInternet network class is valid yet
pub public_internet_network_class_valid: bool, xxx do this and add to attachment calculation pub public_internet_ready: bool,
/// If LocalNetwork network class is valid yet /// If LocalNetwork network class is valid yet
pub local_network_network_class_valid: bool, pub local_network_ready: bool,
} }
pub(super) struct RoutingTableUnlockedInner { pub(super) struct RoutingTableUnlockedInner {
@ -78,7 +78,7 @@ pub(super) struct RoutingTableUnlockedInner {
kick_queue: Mutex<BTreeSet<usize>>, kick_queue: Mutex<BTreeSet<usize>>,
/// Background process for computing statistics /// Background process for computing statistics
rolling_transfers_task: TickTask<EyreReport>, rolling_transfers_task: TickTask<EyreReport>,
/// Backgroup process to purge dead routing table entries when necessary /// Background process to purge dead routing table entries when necessary
kick_buckets_task: TickTask<EyreReport>, kick_buckets_task: TickTask<EyreReport>,
/// Background process to get our initial routing table /// Background process to get our initial routing table
bootstrap_task: TickTask<EyreReport>, bootstrap_task: TickTask<EyreReport>,

View File

@ -318,10 +318,8 @@ impl RoutingTableInner {
4 => 16, 4 => 16,
5 => 8, 5 => 8,
6 => 4, 6 => 4,
7 => 4, 7 => 2,
8 => 4, _ => 1,
9 => 4,
_ => 4,
} }
} }
@ -718,24 +716,45 @@ impl RoutingTableInner {
// Routing Table Health Metrics // Routing Table Health Metrics
pub fn get_routing_table_health(&self) -> RoutingTableHealth { pub fn get_routing_table_health(&self) -> RoutingTableHealth {
let mut health = RoutingTableHealth::default(); let mut reliable_entry_count: usize = 0;
let mut unreliable_entry_count: usize = 0;
let mut dead_entry_count: usize = 0;
let cur_ts = get_aligned_timestamp(); let cur_ts = get_aligned_timestamp();
for bucket in &self.buckets { for bucket in &self.buckets {
for (_, v) in bucket.entries() { for (_, v) in bucket.entries() {
match v.with(self, |_rti, e| e.state(cur_ts)) { match v.with(self, |_rti, e| e.state(cur_ts)) {
BucketEntryState::Reliable => { BucketEntryState::Reliable => {
health.reliable_entry_count += 1; reliable_entry_count += 1;
} }
BucketEntryState::Unreliable => { BucketEntryState::Unreliable => {
health.unreliable_entry_count += 1; unreliable_entry_count += 1;
} }
BucketEntryState::Dead => { BucketEntryState::Dead => {
health.dead_entry_count += 1; dead_entry_count += 1;
} }
} }
} }
} }
health
let public_internet_ready = matches!(
self.get_network_class(RoutingDomain::PublicInternet)
.unwrap_or_default(),
NetworkClass::Invalid
);
let local_network_ready = matches!(
self.get_network_class(RoutingDomain::LocalNetwork)
.unwrap_or_default(),
NetworkClass::Invalid
);
RoutingTableHealth {
reliable_entry_count,
unreliable_entry_count,
dead_entry_count,
public_internet_ready,
local_network_ready,
}
} }
pub fn touch_recent_peer(&mut self, node_id: DHTKey, last_connection: ConnectionDescriptor) { pub fn touch_recent_peer(&mut self, node_id: DHTKey, last_connection: ConnectionDescriptor) {

View File

@ -139,20 +139,20 @@ impl VeilidAPI {
#[instrument(level = "debug", err, skip_all)] #[instrument(level = "debug", err, skip_all)]
pub async fn attach(&self) -> Result<(), VeilidAPIError> { pub async fn attach(&self) -> Result<(), VeilidAPIError> {
let attachment_manager = self.attachment_manager()?; let attachment_manager = self.attachment_manager()?;
attachment_manager if !attachment_manager.attach().await {
.request_attach() apibail_generic!("Already attached");
.await }
.map_err(|e| VeilidAPIError::internal(e)) Ok(())
} }
// disconnect from the network // disconnect from the network
#[instrument(level = "debug", err, skip_all)] #[instrument(level = "debug", err, skip_all)]
pub async fn detach(&self) -> Result<(), VeilidAPIError> { pub async fn detach(&self) -> Result<(), VeilidAPIError> {
let attachment_manager = self.attachment_manager()?; let attachment_manager = self.attachment_manager()?;
attachment_manager if !attachment_manager.detach().await {
.request_detach() apibail_generic!("Already detached");
.await }
.map_err(|e| VeilidAPIError::internal(e)) Ok(())
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////

View File

@ -130,12 +130,72 @@ pub struct VeilidAppCall {
pub id: OperationId, pub id: OperationId,
} }
#[derive(
Debug,
PartialEq,
Eq,
Clone,
Copy,
Serialize,
Deserialize,
RkyvArchive,
RkyvSerialize,
RkyvDeserialize,
)]
#[archive_attr(repr(u8), derive(CheckBytes))]
pub enum AttachmentState {
Detached,
Attaching,
AttachedWeak,
AttachedGood,
AttachedStrong,
FullyAttached,
OverAttached,
Detaching,
}
impl fmt::Display for AttachmentState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let out = match self {
AttachmentState::Attaching => "attaching".to_owned(),
AttachmentState::AttachedWeak => "attached_weak".to_owned(),
AttachmentState::AttachedGood => "attached_good".to_owned(),
AttachmentState::AttachedStrong => "attached_strong".to_owned(),
AttachmentState::FullyAttached => "fully_attached".to_owned(),
AttachmentState::OverAttached => "over_attached".to_owned(),
AttachmentState::Detaching => "detaching".to_owned(),
AttachmentState::Detached => "detached".to_owned(),
};
write!(f, "{}", out)
}
}
impl TryFrom<String> for AttachmentState {
type Error = ();
fn try_from(s: String) -> Result<Self, Self::Error> {
Ok(match s.as_str() {
"attaching" => AttachmentState::Attaching,
"attached_weak" => AttachmentState::AttachedWeak,
"attached_good" => AttachmentState::AttachedGood,
"attached_strong" => AttachmentState::AttachedStrong,
"fully_attached" => AttachmentState::FullyAttached,
"over_attached" => AttachmentState::OverAttached,
"detaching" => AttachmentState::Detaching,
"detached" => AttachmentState::Detached,
_ => return Err(()),
})
}
}
#[derive( #[derive(
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
)] )]
#[archive_attr(repr(C), derive(CheckBytes))] #[archive_attr(repr(C), derive(CheckBytes))]
pub struct VeilidStateAttachment { pub struct VeilidStateAttachment {
pub state: AttachmentState, pub state: AttachmentState,
pub public_internet_ready: bool,
pub local_network_ready: bool,
} }
#[derive( #[derive(

View File

@ -336,6 +336,8 @@ pub struct VeilidConfigRoutingTable {
pub limit_attached_strong: u32, pub limit_attached_strong: u32,
pub limit_attached_good: u32, pub limit_attached_good: u32,
pub limit_attached_weak: u32, pub limit_attached_weak: u32,
// xxx pub enable_public_internet: bool,
// xxx pub enable_local_network: bool,
} }
#[derive( #[derive(

View File

@ -0,0 +1,140 @@
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
import 'veilid.dart';
Future<VeilidConfig> getDefaultVeilidConfig(String programName) async {
return VeilidConfig(
programName: programName,
namespace: "",
capabilities: VeilidConfigCapabilities(
protocolUDP: !kIsWeb,
protocolConnectTCP: !kIsWeb,
protocolAcceptTCP: !kIsWeb,
protocolConnectWS: true,
protocolAcceptWS: !kIsWeb,
protocolConnectWSS: true,
protocolAcceptWSS: false,
),
protectedStore: VeilidConfigProtectedStore(
allowInsecureFallback: false,
alwaysUseInsecureStorage: false,
insecureFallbackDirectory: "",
delete: false,
),
tableStore: VeilidConfigTableStore(
directory: kIsWeb
? ""
: p.join((await getApplicationSupportDirectory()).absolute.path,
"table_store"),
delete: false,
),
blockStore: VeilidConfigBlockStore(
directory: kIsWeb
? ""
: p.join((await getApplicationSupportDirectory()).absolute.path,
"block_store"),
delete: false,
),
network: VeilidConfigNetwork(
connectionInitialTimeoutMs: 2000,
connectionInactivityTimeoutMs: 60000,
maxConnectionsPerIp4: 32,
maxConnectionsPerIp6Prefix: 32,
maxConnectionsPerIp6PrefixSize: 56,
maxConnectionFrequencyPerMin: 128,
clientWhitelistTimeoutMs: 300000,
reverseConnectionReceiptTimeMs: 5000,
holePunchReceiptTimeMs: 5000,
nodeId: null,
nodeIdSecret: null,
bootstrap: kIsWeb
? ["ws://bootstrap.dev.veilid.net:5150/ws"]
: ["bootstrap.dev.veilid.net"],
bootstrapNodes: [],
routingTable: VeilidConfigRoutingTable(
limitOverAttached: 64,
limitFullyAttached: 32,
limitAttachedStrong: 16,
limitAttachedGood: 8,
limitAttachedWeak: 4,
),
rpc: VeilidConfigRPC(
concurrency: 0,
queueSize: 1024,
maxTimestampBehindMs: 10000,
maxTimestampAheadMs: 10000,
timeoutMs: 10000,
maxRouteHopCount: 4,
defaultRouteHopCount: 1,
),
dht: VeilidConfigDHT(
resolveNodeTimeoutMs: null,
resolveNodeCount: 20,
resolveNodeFanout: 3,
maxFindNodeCount: 20,
getValueTimeoutMs: null,
getValueCount: 20,
getValueFanout: 3,
setValueTimeoutMs: null,
setValueCount: 20,
setValueFanout: 5,
minPeerCount: 20,
minPeerRefreshTimeMs: 2000,
validateDialInfoReceiptTimeMs: 2000,
),
upnp: true,
detectAddressChanges: true,
restrictedNatRetries: 0,
tls: VeilidConfigTLS(
certificatePath: "",
privateKeyPath: "",
connectionInitialTimeoutMs: 2000,
),
application: VeilidConfigApplication(
https: VeilidConfigHTTPS(
enabled: false,
listenAddress: "",
path: "",
url: null,
),
http: VeilidConfigHTTP(
enabled: false,
listenAddress: "",
path: "",
url: null,
)),
protocol: VeilidConfigProtocol(
udp: VeilidConfigUDP(
enabled: !kIsWeb,
socketPoolSize: 0,
listenAddress: "",
publicAddress: null,
),
tcp: VeilidConfigTCP(
connect: !kIsWeb,
listen: !kIsWeb,
maxConnections: 32,
listenAddress: "",
publicAddress: null,
),
ws: VeilidConfigWS(
connect: true,
listen: !kIsWeb,
maxConnections: 16,
listenAddress: "",
path: "ws",
url: null,
),
wss: VeilidConfigWSS(
connect: true,
listen: false,
maxConnections: 16,
listenAddress: "",
path: "ws",
url: null,
),
),
),
);
}

View File

@ -1400,15 +1400,22 @@ class VeilidUpdateRoute implements VeilidUpdate {
class VeilidStateAttachment { class VeilidStateAttachment {
final AttachmentState state; final AttachmentState state;
final bool publicInternetReady;
final bool localNetworkReady;
VeilidStateAttachment(this.state); VeilidStateAttachment(
this.state, this.publicInternetReady, this.localNetworkReady);
VeilidStateAttachment.fromJson(Map<String, dynamic> json) VeilidStateAttachment.fromJson(Map<String, dynamic> json)
: state = attachmentStateFromJson(json['state']); : state = attachmentStateFromJson(json['state']),
publicInternetReady = json['public_internet_ready'],
localNetworkReady = json['local_network_ready'];
Map<String, dynamic> get json { Map<String, dynamic> get json {
return { return {
'state': state.json, 'state': state.json,
'public_internet_ready': publicInternetReady,
'local_network_ready': localNetworkReady,
}; };
} }
} }