From 1d8e2d3fdae303f512aa63b7d09bcc795aec809a Mon Sep 17 00:00:00 2001 From: John Smith Date: Mon, 13 Feb 2023 16:12:46 -0500 Subject: [PATCH] bootstrap --- Cargo.lock | 7 + doc/config/sample.config | 1 - doc/config/veilid-server-config.md | 65 ++- veilid-core/Cargo.toml | 9 +- veilid-core/src/crypto/types.rs | 36 +- veilid-core/src/network_manager/mod.rs | 2 +- veilid-core/src/routing_table/bucket.rs | 48 +-- veilid-core/src/routing_table/debug.rs | 17 +- veilid-core/src/routing_table/mod.rs | 37 +- .../src/routing_table/routing_table_inner.rs | 170 ++++---- .../src/routing_table/tasks/bootstrap.rs | 376 +++++++++--------- .../src/routing_table/tasks/kick_buckets.rs | 11 +- .../src/rpc_processor/rpc_find_node.rs | 7 + .../src/tests/common/test_veilid_config.rs | 2 - veilid-core/src/veilid_config.rs | 2 - veilid-flutter/lib/default_config.dart | 1 - veilid-flutter/lib/veilid.dart | 4 - veilid-server/src/cmdline.rs | 30 -- veilid-server/src/settings.rs | 74 ---- 19 files changed, 428 insertions(+), 471 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e8f6c9d..0d769411 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6003,6 +6003,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-bindgen-test", "wasm-logger", + "weak-table", "web-sys", "webpki 0.22.0", "webpki-roots 0.22.6", @@ -6327,6 +6328,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "weak-table" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549" + [[package]] name = "web-sys" version = "0.3.60" diff --git a/doc/config/sample.config b/doc/config/sample.config index 8c939d13..a7dc1033 100644 --- a/doc/config/sample.config +++ b/doc/config/sample.config @@ -50,7 +50,6 @@ core: node_id: '' node_id_secret: '' bootstrap: ['bootstrap.dev.veilid.net'] - bootstrap_nodes: [] routing_table: limit_over_attached: 64 limit_fully_attached: 32 diff --git a/doc/config/veilid-server-config.md b/doc/config/veilid-server-config.md index 8fbe74fe..525ce32d 100644 --- a/doc/config/veilid-server-config.md +++ b/doc/config/veilid-server-config.md @@ -14,14 +14,14 @@ and the `veilid-server.conf` file. ## Global Directives -| Directive | Description | -|-------------------------------|-----------------------------------------| -| [daemon](#daemon) | Run `veilid-server` in the background | -| [client\_api](#client_api) || -| [auto\_attach](#auto_attach) || -| [logging](#logging) || -| [testing](#testing) || -| [core](#core) || +| Directive | Description | +| ---------------------------- | ------------------------------------- | +| [daemon](#daemon) | Run `veilid-server` in the background | +| [client\_api](#client_api) | | +| [auto\_attach](#auto_attach) | | +| [logging](#logging) | | +| [testing](#testing) | | +| [core](#core) | | ### daemon @@ -39,10 +39,10 @@ client_api: listen_address: 'localhost:5959' ``` -| Parameter | Description | -|-----------------------------------------------|-------------| -| [enabled](#client_apienabled) || -| [listen\_address](#client_apilisten_address) || +| Parameter | Description | +| -------------------------------------------- | ----------- | +| [enabled](#client_apienabled) | | +| [listen\_address](#client_apilisten_address) | | #### client\_api:enabled @@ -82,13 +82,13 @@ logging: grpc_endpoint: 'localhost:4317' ``` -| Parameter | Description | -|-------------------------------|-------------| -| [system](#loggingsystem) || -| [terminal](#loggingterminal) || -| [file](#loggingfile) || -| [api](#loggingapi) || -| [otlp](#loggingotlp) || +| Parameter | Description | +| ---------------------------- | ----------- | +| [system](#loggingsystem) | | +| [terminal](#loggingterminal) | | +| [file](#loggingfile) | | +| [api](#loggingapi) | | +| [otlp](#loggingotlp) | | #### logging:system @@ -142,12 +142,12 @@ testing: ### core -| Parameter | Description | -|-------------------------------------------|-------------| -| [protected\_store](#coreprotected_store) || -| [table\_store](#coretable_store) || -| [block\_store](#block_store) || -| [network](#corenetwork) || +| Parameter | Description | +| ---------------------------------------- | ----------- | +| [protected\_store](#coreprotected_store) | | +| [table\_store](#coretable_store) | | +| [block\_store](#block_store) | | +| [network](#corenetwork) | | #### core:protected\_store @@ -191,7 +191,6 @@ network: node_id: '' node_id_secret: '' bootstrap: ['bootstrap.dev.veilid.net'] - bootstrap_nodes: [] upnp: true detect_address_changes: true enable_local_peer_scope: false @@ -199,13 +198,13 @@ network: ``` | Parameter | Description | -|---------------------------------------------|-------------| -| [routing\_table](#corenetworkrouting_table) || -| [rpc](#corenetworkrpc) || -| [dht](#corenetworkdht) || -| [tls](#corenetworktls) || -| [application](#corenetworkapplication) || -| [protocol](#corenetworkprotocol) || +| ------------------------------------------- | ----------- | +| [routing\_table](#corenetworkrouting_table) | | +| [rpc](#corenetworkrpc) | | +| [dht](#corenetworkdht) | | +| [tls](#corenetworktls) | | +| [application](#corenetworkapplication) | | +| [protocol](#corenetworkprotocol) | | #### core:network:routing\_table diff --git a/veilid-core/Cargo.toml b/veilid-core/Cargo.toml index ddb2d3f4..938c90b1 100644 --- a/veilid-core/Cargo.toml +++ b/veilid-core/Cargo.toml @@ -11,11 +11,11 @@ crate-type = ["cdylib", "staticlib", "rlib"] [features] default = [] -rt-async-std = [ "async-std", "async-std-resolver", "async_executors/async_std", "rtnetlink?/smol_socket", "veilid-tools/rt-async-std" ] -rt-tokio = [ "tokio", "tokio-util", "tokio-stream", "trust-dns-resolver/tokio-runtime", "async_executors/tokio_tp", "async_executors/tokio_io", "async_executors/tokio_timer", "rtnetlink?/tokio_socket", "veilid-tools/rt-tokio" ] +rt-async-std = ["async-std", "async-std-resolver", "async_executors/async_std", "rtnetlink?/smol_socket", "veilid-tools/rt-async-std"] +rt-tokio = ["tokio", "tokio-util", "tokio-stream", "trust-dns-resolver/tokio-runtime", "async_executors/tokio_tp", "async_executors/tokio_io", "async_executors/tokio_timer", "rtnetlink?/tokio_socket", "veilid-tools/rt-tokio"] -veilid_core_android_tests = [ "dep:paranoid-android" ] -veilid_core_ios_tests = [ "dep:tracing-oslog" ] +veilid_core_android_tests = ["dep:paranoid-android"] +veilid_core_ios_tests = ["dep:tracing-oslog"] tracking = [] [dependencies] @@ -65,6 +65,7 @@ keyvaluedb = { path = "../external/keyvaluedb/keyvaluedb" } rkyv = { git = "https://github.com/rkyv/rkyv.git", rev = "57e2a8d", default_features = false, features = ["std", "alloc", "strict", "size_32", "validation"] } bytecheck = "^0" data-encoding = { version = "^2" } +weak-table = "0.3.2" # Dependencies for native builds only # Linux, Windows, Mac, iOS, Android diff --git a/veilid-core/src/crypto/types.rs b/veilid-core/src/crypto/types.rs index c002e473..8548e171 100644 --- a/veilid-core/src/crypto/types.rs +++ b/veilid-core/src/crypto/types.rs @@ -11,9 +11,9 @@ use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as pub type CryptoKind = FourCC; /// Sort best crypto kinds first -pub fn compare_crypto_kind(a: CryptoKind, b: CryptoKind) -> cmp::Ordering { - let a_idx = VALID_CRYPTO_KINDS.iter().position(|&k| k == a); - let b_idx = VALID_CRYPTO_KINDS.iter().position(|&k| k == b); +pub fn compare_crypto_kind(a: &CryptoKind, b: &CryptoKind) -> cmp::Ordering { + let a_idx = VALID_CRYPTO_KINDS.iter().position(|k| k == a); + let b_idx = VALID_CRYPTO_KINDS.iter().position(|k| k == b); if let Some(a_idx) = a_idx { if let Some(b_idx) = b_idx { a_idx.cmp(&b_idx) @@ -23,7 +23,7 @@ pub fn compare_crypto_kind(a: CryptoKind, b: CryptoKind) -> cmp::Ordering { } else if let Some(b_idx) = b_idx { cmp::Ordering::Greater } else { - a.cmp(&b) + a.cmp(b) } } @@ -86,7 +86,7 @@ impl PartialOrd for TypedKey { impl Ord for TypedKey { fn cmp(&self, other: &Self) -> cmp::Ordering { - let x = compare_crypto_kind(self.kind, other.kind); + let x = compare_crypto_kind(&self.kind, &other.kind); if x != cmp::Ordering::Equal { return x; } @@ -140,6 +140,14 @@ impl TypedKeySet { items: Vec::with_capacity(cap), } } + pub fn kinds(&self) -> Vec { + let mut out = Vec::new(); + for tk in &self.items { + out.push(tk.kind); + } + out.sort_by(compare_crypto_kind); + out + } pub fn get(&self, kind: CryptoKind) -> Option { self.items.iter().find(|x| x.kind == kind).copied() } @@ -153,6 +161,18 @@ impl TypedKeySet { self.items.push(typed_key); self.items.sort() } + pub fn add_all(&mut self, typed_keys: &[TypedKey]) { + 'outer: for typed_key in typed_keys { + for x in &mut self.items { + if x.kind == typed_key.kind { + *x = *typed_key; + continue 'outer; + } + } + self.items.push(*typed_key); + } + self.items.sort() + } pub fn remove(&self, kind: CryptoKind) { if let Some(idx) = self.items.iter().position(|x| x.kind == kind) { self.items.remove(idx); @@ -256,7 +276,7 @@ impl PartialOrd for TypedKeyPair { impl Ord for TypedKeyPair { fn cmp(&self, other: &Self) -> cmp::Ordering { - let x = compare_crypto_kind(self.kind, other.kind); + let x = compare_crypto_kind(&self.kind, &other.kind); if x != cmp::Ordering::Equal { return x; } @@ -340,7 +360,7 @@ impl PartialOrd for TypedSignature { impl Ord for TypedSignature { fn cmp(&self, other: &Self) -> cmp::Ordering { - let x = compare_crypto_kind(self.kind, other.kind); + let x = compare_crypto_kind(&self.kind, &other.kind); if x != cmp::Ordering::Equal { return x; } @@ -410,7 +430,7 @@ impl PartialOrd for TypedKeySignature { impl Ord for TypedKeySignature { fn cmp(&self, other: &Self) -> cmp::Ordering { - let x = compare_crypto_kind(self.kind, other.kind); + let x = compare_crypto_kind(&self.kind, &other.kind); if x != cmp::Ordering::Equal { return x; } diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index d3948b68..a1afd869 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -1327,7 +1327,7 @@ impl NetworkManager { } // Is this an out-of-band receipt instead of an envelope? - if data[0..4] == *RECEIPT_MAGIC { + if data[0..3] == *RECEIPT_MAGIC { network_result_value_or_log!(self.handle_out_of_band_receipt(data).await => {}); return Ok(true); } diff --git a/veilid-core/src/routing_table/bucket.rs b/veilid-core/src/routing_table/bucket.rs index ca99e64d..0b0bbb96 100644 --- a/veilid-core/src/routing_table/bucket.rs +++ b/veilid-core/src/routing_table/bucket.rs @@ -11,19 +11,19 @@ pub struct Bucket { /// handle to the routing table routing_table: RoutingTable, /// Map of keys to entries for this bucket - entries: BTreeMap>, + entries: BTreeMap>, /// The most recent entry in this bucket - newest_entry: Option, + newest_entry: Option, /// The crypto kind in use for the public keys in this bucket kind: CryptoKind, } pub(super) type EntriesIter<'a> = - alloc::collections::btree_map::Iter<'a, TypedKey, Arc>; + alloc::collections::btree_map::Iter<'a, PublicKey, Arc>; #[derive(Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)] #[archive_attr(repr(C), derive(CheckBytes))] struct SerializedBucketEntryData { - key: TypedKey, + key: PublicKey, value: u32, // index into serialized entries list } @@ -31,7 +31,7 @@ struct SerializedBucketEntryData { #[archive_attr(repr(C), derive(CheckBytes))] struct SerializedBucketData { entries: Vec, - newest_entry: Option, + newest_entry: Option, } fn state_ordering(state: BucketEntryState) -> usize { @@ -95,49 +95,45 @@ impl Bucket { } /// Create a new entry with a node_id of this crypto kind and return it - pub(super) fn add_entry(&mut self, node_id: TypedKey) -> NodeRef { - assert_eq!(node_id.kind, self.kind); - - log_rtab!("Node added: {}", node_id); + pub(super) fn add_entry(&mut self, node_id_key: PublicKey) -> NodeRef { + log_rtab!("Node added: {}:{}", self.kind, node_id_key); // Add new entry - let entry = Arc::new(BucketEntry::new(node_id)); - self.entries.insert(node_id.key, entry.clone()); + let entry = Arc::new(BucketEntry::new(TypedKey::new(self.kind, node_id_key))); + self.entries.insert(node_id_key, entry.clone()); // This is now the newest bucket entry - self.newest_entry = Some(node_id.key); + self.newest_entry = Some(node_id_key); // Get a node ref to return since this is new NodeRef::new(self.routing_table.clone(), entry, None) } /// Add an existing entry with a new node_id for this crypto kind - pub(super) fn add_existing_entry(&mut self, node_id: TypedKey, entry: Arc) { - assert_eq!(node_id.kind, self.kind); - - log_rtab!("Existing node added: {}", node_id); + pub(super) fn add_existing_entry(&mut self, node_id_key: PublicKey, entry: Arc) { + log_rtab!("Existing node added: {}:{}", self.kind, node_id_key); // Add existing entry - entry.with_mut_inner(|e| e.add_node_id(node_id)); - self.entries.insert(node_id.key, entry); + entry.with_mut_inner(|e| e.add_node_id(TypedKey::new(self.kind, node_id_key))); + self.entries.insert(node_id_key, entry); // This is now the newest bucket entry - self.newest_entry = Some(node_id.key); + self.newest_entry = Some(node_id_key); // No need to return a noderef here because the noderef will already exist in the caller } /// Remove an entry with a node_id for this crypto kind from the bucket - fn remove_entry(&mut self, node_id: &TypedKey) { - log_rtab!("Node removed: {}:{}", self.kind, node_id); + fn remove_entry(&mut self, node_id_key: &PublicKey) { + log_rtab!("Node removed: {}:{}", self.kind, node_id_key); // Remove the entry - self.entries.remove(node_id); + self.entries.remove(node_id_key); // newest_entry is updated by kick_bucket() } - pub(super) fn entry(&self, key: &TypedKey) -> Option> { + pub(super) fn entry(&self, key: &PublicKey) -> Option> { self.entries.get(key).cloned() } @@ -145,7 +141,7 @@ impl Bucket { self.entries.iter() } - pub(super) fn kick(&mut self, bucket_depth: usize) -> Option> { + pub(super) fn kick(&mut self, bucket_depth: usize) -> Option> { // Get number of entries to attempt to purge from bucket let bucket_len = self.entries.len(); @@ -155,11 +151,11 @@ impl Bucket { } // Try to purge the newest entries that overflow the bucket - let mut dead_node_ids: BTreeSet = BTreeSet::new(); + let mut dead_node_ids: BTreeSet = BTreeSet::new(); let mut extra_entries = bucket_len - bucket_depth; // Get the sorted list of entries by their kick order - let mut sorted_entries: Vec<(TypedKey, Arc)> = self + let mut sorted_entries: Vec<(PublicKey, Arc)> = self .entries .iter() .map(|(k, v)| (k.clone(), v.clone())) diff --git a/veilid-core/src/routing_table/debug.rs b/veilid-core/src/routing_table/debug.rs index 862cff7f..f4721ec3 100644 --- a/veilid-core/src/routing_table/debug.rs +++ b/veilid-core/src/routing_table/debug.rs @@ -7,7 +7,7 @@ impl RoutingTable { let inner = self.inner.read(); out += "Routing Table Info:\n"; - out += &format!(" Node Id: {}\n", self.unlocked_inner.node_id.encode()); + out += &format!(" Node Ids: {}\n", self.unlocked_inner.node_ids()); out += &format!( " Self Latency Stats Accounting: {:#?}\n\n", inner.self_latency_stats_accounting @@ -55,13 +55,20 @@ impl RoutingTable { short_urls.sort(); short_urls.dedup(); + let valid_envelope_versions = VALID_ENVELOPE_VERSIONS.map(|x| x.to_string()).join(","); + let node_ids = self + .unlocked_inner + .node_ids() + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","); out += "TXT Record:\n"; out += &format!( - "{},{},{},{},{}", + "{}|{}|{}|{}|", BOOTSTRAP_TXT_VERSION, - MIN_CRYPTO_VERSION, - MAX_CRYPTO_VERSION, - self.node_id().encode(), + valid_envelope_versions, + node_ids, some_hostname.unwrap() ); for short_url in short_urls { diff --git a/veilid-core/src/routing_table/mod.rs b/veilid-core/src/routing_table/mod.rs index 616a31b4..af953db0 100644 --- a/veilid-core/src/routing_table/mod.rs +++ b/veilid-core/src/routing_table/mod.rs @@ -111,7 +111,15 @@ impl RoutingTableUnlockedInner { } pub fn node_id(&self, kind: CryptoKind) -> TypedKey { - self.node_id_keypairs.get(&kind).unwrap().key + TypedKey::new(kind, self.node_id_keypairs.get(&kind).unwrap().key) + } + + pub fn node_ids(&self) -> TypedKeySet { + let mut tks = TypedKeySet::new(); + for x in &self.node_id_keypairs { + tks.add(TypedKey::new(*x.0, x.1.key)); + } + tks } pub fn node_id_secret(&self, kind: CryptoKind) -> SecretKey { @@ -890,6 +898,7 @@ impl RoutingTable { pub fn find_closest_nodes<'a, T, O>( &self, + node_count: usize, node_id: TypedKey, filters: VecDeque, transform: T, @@ -899,7 +908,7 @@ impl RoutingTable { { self.inner .read() - .find_closest_nodes(node_id, filters, transform) + .find_closest_nodes(node_count, node_id, filters, transform) } #[instrument(level = "trace", skip(self), ret)] @@ -940,14 +949,14 @@ impl RoutingTable { #[instrument(level = "trace", skip(self), ret, err)] pub async fn find_self(&self, node_ref: NodeRef) -> EyreResult>> { - let node_id = self.node_id(); - self.find_node(node_ref, node_id).await + let self_node_id = self.node_id(); + self.find_node(node_ref, self_node_id).await } #[instrument(level = "trace", skip(self), ret, err)] pub async fn find_target(&self, node_ref: NodeRef) -> EyreResult>> { - let node_id = node_ref.node_id(); - self.find_node(node_ref, node_id).await + let target_node_id = node_ref.node_id(); + self.find_node(node_ref, target_node_id).await } #[instrument(level = "trace", skip(self))] @@ -1047,12 +1056,12 @@ impl RoutingTable { // Go through all entries and find fastest entry that matches filter function let inner = self.inner.read(); let inner = &*inner; - let mut best_inbound_relay: Option<(TypedKey, Arc)> = None; + let mut best_inbound_relay: Option> = None; // Iterate all known nodes for candidates - inner.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, k, v| { - let v2 = v.clone(); - v.with(rti, |rti, e| { + inner.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, entry| { + let entry2 = entry.clone(); + entry.with(rti, |rti, e| { // Ensure we have the node's status if let Some(node_status) = e.node_status(routing_domain) { // Ensure the node will relay @@ -1060,18 +1069,18 @@ impl RoutingTable { // Compare against previous candidate if let Some(best_inbound_relay) = best_inbound_relay.as_mut() { // Less is faster - let better = best_inbound_relay.1.with(rti, |_rti, best| { + let better = best_inbound_relay.with(rti, |_rti, best| { // choose low latency stability for relays BucketEntryInner::cmp_fastest_reliable(cur_ts, e, best) == std::cmp::Ordering::Less }); // Now apply filter function and see if this node should be included if better && relay_node_filter(e) { - *best_inbound_relay = (k, v2); + *best_inbound_relay = entry2; } } else if relay_node_filter(e) { // Always store the first candidate - best_inbound_relay = Some((k, v2)); + best_inbound_relay = Some(entry2); } } } @@ -1080,6 +1089,6 @@ impl RoutingTable { Option::<()>::None }); // Return the best inbound relay noderef - best_inbound_relay.map(|(k, e)| NodeRef::new(self.clone(), e, None)) + best_inbound_relay.map(|e| NodeRef::new(self.clone(), e, None)) } } diff --git a/veilid-core/src/routing_table/routing_table_inner.rs b/veilid-core/src/routing_table/routing_table_inner.rs index 1334d541..69c64f55 100644 --- a/veilid-core/src/routing_table/routing_table_inner.rs +++ b/veilid-core/src/routing_table/routing_table_inner.rs @@ -1,4 +1,5 @@ use super::*; +use weak_table::PtrWeakHashSet; const RECENT_PEERS_TABLE_SIZE: usize = 64; @@ -15,8 +16,8 @@ pub struct RoutingTableInner { pub(super) unlocked_inner: Arc, /// Routing table buckets that hold references to entries, per crypto kind pub(super) buckets: BTreeMap>, - /// A fast counter for the number of entries in the table, total - pub(super) bucket_entry_count: usize, + /// A weak set of all the entries we have in the buckets for faster iteration + pub(super) all_entries: PtrWeakHashSet>, /// The public internet routing domain pub(super) public_internet_routing_domain: PublicInternetRoutingDomainDetail, /// The dial info we use on the local network @@ -40,7 +41,7 @@ impl RoutingTableInner { buckets: BTreeMap::new(), public_internet_routing_domain: PublicInternetRoutingDomainDetail::default(), local_network_routing_domain: LocalNetworkRoutingDomainDetail::default(), - bucket_entry_count: 0, + all_entries: PtrWeakHashSet::new(), self_latency_stats_accounting: LatencyStatsAccounting::new(), self_transfer_stats_accounting: TransferStatsAccounting::new(), self_transfer_stats: TransferStatsDownUp::default(), @@ -49,6 +50,10 @@ impl RoutingTableInner { } } + pub fn bucket_entry_count(&self) -> usize { + self.all_entries.len() + } + pub fn transfer_stats_accounting(&mut self) -> &mut TransferStatsAccounting { &mut self.self_transfer_stats_accounting } @@ -209,7 +214,7 @@ impl RoutingTableInner { pub fn reset_all_updated_since_last_network_change(&mut self) { let cur_ts = get_aligned_timestamp(); - self.with_entries_mut(cur_ts, BucketEntryState::Dead, |rti, _, v| { + self.with_entries_mut(cur_ts, BucketEntryState::Dead, |rti, v| { v.with_mut(rti, |_rti, e| { e.set_updated_since_last_network_change(false) }); @@ -310,7 +315,7 @@ impl RoutingTableInner { for ck in VALID_CRYPTO_KINDS { let ckbuckets = Vec::with_capacity(PUBLIC_KEY_LENGTH * 8); for _ in 0..PUBLIC_KEY_LENGTH * 8 { - let bucket = Bucket::new(routing_table.clone()); + let bucket = Bucket::new(routing_table.clone(), ck); ckbuckets.push(bucket); } self.buckets.insert(ck, ckbuckets); @@ -345,14 +350,18 @@ impl RoutingTableInner { pub fn purge_buckets(&mut self) { log_rtab!( "Starting routing table buckets purge. Table currently has {} nodes", - self.bucket_entry_count + self.bucket_entry_count() ); - for bucket in &mut self.buckets { - bucket.kick(0); + for ck in VALID_CRYPTO_KINDS { + for bucket in &mut self.buckets[&ck] { + bucket.kick(0); + } } + self.all_entries.remove_expired(); + log_rtab!(debug "Routing table buckets purge complete. Routing table now has {} nodes", - self.bucket_entry_count + self.bucket_entry_count() ); } @@ -360,32 +369,36 @@ impl RoutingTableInner { pub fn purge_last_connections(&mut self) { log_rtab!( "Starting routing table last_connections purge. Table currently has {} nodes", - self.bucket_entry_count + self.bucket_entry_count() ); - for bucket in &self.buckets { - for entry in bucket.entries() { - entry.1.with_mut_inner(|e| { - e.clear_last_connections(); - }); + for ck in VALID_CRYPTO_KINDS { + for bucket in &self.buckets[&ck] { + for entry in bucket.entries() { + entry.1.with_mut_inner(|e| { + e.clear_last_connections(); + }); + } } } + self.all_entries.remove_expired(); log_rtab!(debug "Routing table last_connections purge complete. Routing table now has {} nodes", - self.bucket_entry_count + self.bucket_entry_count() ); } /// Attempt to settle buckets and remove entries down to the desired number /// which may not be possible due extant NodeRefs - pub fn kick_bucket(&mut self, idx: usize) { - let bucket = &mut self.buckets[idx]; + pub fn kick_bucket(&mut self, kind: CryptoKind, idx: usize) { + let bucket = &mut self.buckets[&kind][idx]; let bucket_depth = Self::bucket_depth(idx); if let Some(dead_node_ids) = bucket.kick(bucket_depth) { - // Remove counts - self.bucket_entry_count -= dead_node_ids.len(); - log_rtab!(debug "Routing table now has {} nodes", self.bucket_entry_count); + // Remove expired entries + self.all_entries.remove_expired(); + + log_rtab!(debug "Bucket {}:{} kicked Routing table now has {} nodes", kind, idx, self.bucket_entry_count()); // Now purge the routing table inner vectors //let filter = |k: &DHTKey| dead_node_ids.contains(k); @@ -403,7 +416,7 @@ impl RoutingTableInner { ) -> usize { let mut count = 0usize; let cur_ts = get_aligned_timestamp(); - self.with_entries(cur_ts, min_state, |rti, _, e| { + self.with_entries(cur_ts, min_state, |rti, e| { if e.with(rti, |rti, e| e.best_routing_domain(rti, routing_domain_set)) .is_some() { @@ -414,54 +427,36 @@ impl RoutingTableInner { count } - pub fn with_entries< - T, - F: FnMut(&RoutingTableInner, TypedKey, Arc) -> Option, - >( + pub fn with_entries) -> Option>( &self, cur_ts: Timestamp, min_state: BucketEntryState, mut f: F, ) -> Option { - let mut entryvec = Vec::with_capacity(self.bucket_entry_count); - for bucket in &self.buckets { - for entry in bucket.entries() { - if entry.1.with(self, |_rti, e| e.state(cur_ts) >= min_state) { - entryvec.push((*entry.0, entry.1.clone())); + for entry in self.all_entries { + if entry.with(self, |_rti, e| e.state(cur_ts) >= min_state) { + if let Some(out) = f(self, entry) { + return Some(out); } } } - for entry in entryvec { - if let Some(out) = f(self, entry.0, entry.1) { - return Some(out); - } - } None } - pub fn with_entries_mut< - T, - F: FnMut(&mut RoutingTableInner, TypedKey, Arc) -> Option, - >( + pub fn with_entries_mut) -> Option>( &mut self, cur_ts: Timestamp, min_state: BucketEntryState, mut f: F, ) -> Option { - let mut entryvec = Vec::with_capacity(self.bucket_entry_count); - for bucket in &self.buckets { - for entry in bucket.entries() { - if entry.1.with(self, |_rti, e| e.state(cur_ts) >= min_state) { - entryvec.push((*entry.0, entry.1.clone())); + for entry in self.all_entries { + if entry.with(self, |_rti, e| e.state(cur_ts) >= min_state) { + if let Some(out) = f(self, entry) { + return Some(out); } } } - for entry in entryvec { - if let Some(out) = f(self, entry.0, entry.1) { - return Some(out); - } - } None } @@ -728,18 +723,16 @@ impl RoutingTableInner { let mut dead_entry_count: usize = 0; let cur_ts = get_aligned_timestamp(); - for bucket in &self.buckets { - for (_, v) in bucket.entries() { - match v.with(self, |_rti, e| e.state(cur_ts)) { - BucketEntryState::Reliable => { - reliable_entry_count += 1; - } - BucketEntryState::Unreliable => { - unreliable_entry_count += 1; - } - BucketEntryState::Dead => { - dead_entry_count += 1; - } + for entry in self.all_entries { + match entry.with(self, |_rti, e| e.state(cur_ts)) { + BucketEntryState::Reliable => { + reliable_entry_count += 1; + } + BucketEntryState::Unreliable => { + unreliable_entry_count += 1; + } + BucketEntryState::Dead => { + dead_entry_count += 1; } } } @@ -798,7 +791,7 @@ impl RoutingTableInner { self.find_fastest_nodes( node_count, filters, - |_rti: &RoutingTableInner, k: TypedKey, v: Option>| { + |_rti: &RoutingTableInner, v: Option>| { NodeRef::new(outer_self.clone(), v.unwrap().clone(), None) }, ) @@ -846,30 +839,30 @@ impl RoutingTableInner { &'b Option>, &'b Option>, ) -> core::cmp::Ordering, - T: for<'r> FnMut(&'r RoutingTableInner, TypedKey, Option>) -> O, + T: for<'r> FnMut(&'r RoutingTableInner, Option>) -> O, { // collect all the nodes for sorting let mut nodes = - Vec::<(TypedKey, Option>)>::with_capacity(self.bucket_entry_count + 1); + Vec::>>::with_capacity(self.bucket_entry_count() + 1); // add our own node (only one of there with the None entry) let mut filtered = false; for filter in &mut filters { - if !filter(self, self.unlocked_inner.node_id, None) { + if !filter(self, None) { filtered = true; break; } } if !filtered { - nodes.push((self.unlocked_inner.node_id, None)); + nodes.push(None); } - // add all nodes from buckets - self.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, k, v| { + // add all nodes that match filter + self.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, v| { // Apply filter for filter in &mut filters { - if filter(rti, k, Some(v.clone())) { - nodes.push((k, Some(v.clone()))); + if filter(rti, Some(v.clone())) { + nodes.push(Some(v.clone())); break; } } @@ -883,7 +876,7 @@ impl RoutingTableInner { let cnt = usize::min(node_count, nodes.len()); let mut out = Vec::::with_capacity(cnt); for node in nodes { - let val = transform(self, node.0, node.1); + let val = transform(self, node); out.push(val); } @@ -921,12 +914,19 @@ impl RoutingTableInner { // Fastest sort let sort = |rti: &RoutingTableInner, - (a_key, a_entry): &(TypedKey, Option>), - (b_key, b_entry): &(TypedKey, Option>)| { + a_entry: &Option>, + b_entry: &Option>| { // same nodes are always the same - if a_key == b_key { + if let Some(a_entry) = a_entry { + if let Some(b_entry) = b_entry { + if Arc::ptr_eq(&a_entry, &b_entry) { + return core::cmp::Ordering::Equal; + } + } + } else if b_entry.is_none() { return core::cmp::Ordering::Equal; } + // our own node always comes last (should not happen, here for completeness) if a_entry.is_none() { return core::cmp::Ordering::Greater; @@ -977,26 +977,28 @@ impl RoutingTableInner { pub fn find_closest_nodes( &self, + node_count: usize, node_id: TypedKey, filters: VecDeque, transform: T, ) -> Vec where - T: for<'r> FnMut(&'r RoutingTableInner, TypedKey, Option>) -> O, + T: for<'r> FnMut(&'r RoutingTableInner, Option>) -> O, { let cur_ts = get_aligned_timestamp(); - let node_count = { - let config = self.config(); - let c = config.get(); - c.network.dht.max_find_node_count as usize - }; // closest sort let sort = |rti: &RoutingTableInner, - (a_key, a_entry): &(TypedKey, Option>), - (b_key, b_entry): &(TypedKey, Option>)| { + a_entry: &Option>, + b_entry: &Option>| { // same nodes are always the same - if a_key == b_key { + if let Some(a_entry) = a_entry { + if let Some(b_entry) = b_entry { + if Arc::ptr_eq(&a_entry, &b_entry) { + return core::cmp::Ordering::Equal; + } + } + } else if b_entry.is_none() { return core::cmp::Ordering::Equal; } diff --git a/veilid-core/src/routing_table/tasks/bootstrap.rs b/veilid-core/src/routing_table/tasks/bootstrap.rs index f2834125..5f1f4d4c 100644 --- a/veilid-core/src/routing_table/tasks/bootstrap.rs +++ b/veilid-core/src/routing_table/tasks/bootstrap.rs @@ -3,23 +3,122 @@ use super::*; use futures_util::stream::{FuturesUnordered, StreamExt}; use stop_token::future::FutureExt as StopFutureExt; -pub const BOOTSTRAP_TXT_VERSION: u8 = 0; +pub const BOOTSTRAP_TXT_VERSION_0: u8 = 0; #[derive(Clone, Debug)] pub struct BootstrapRecord { - min_version: u8, - max_version: u8, + node_ids: TypedKeySet, + envelope_support: Vec, dial_info_details: Vec, } -pub type BootstrapRecordMap = BTreeMap; +impl BootstrapRecord { + pub fn merge(&mut self, other: &BootstrapRecord) { + self.node_ids.add_all(&other.node_ids); + for x in other.envelope_support { + if !self.envelope_support.contains(&x) { + self.envelope_support.push(x); + self.envelope_support.sort(); + } + } + for did in &other.dial_info_details { + if !self.dial_info_details.contains(did) { + self.dial_info_details.push(did.clone()); + } + } + } +} impl RoutingTable { + /// Process bootstrap version 0 + async fn process_bootstrap_records_v0( + &self, + records: Vec, + ) -> EyreResult> { + // Bootstrap TXT Record Format Version 0: + // txt_version|envelope_support|node_ids|hostname|dialinfoshort* + // + // Split bootstrap node record by '|' and then lists by ','. Example: + // 0|0|VLD0:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ|bootstrap-1.dev.veilid.net|T5150,U5150,W5150/ws + + if records.len() != 5 { + bail!("invalid number of fields in bootstrap v0 txt record"); + } + + // Envelope support + let mut envelope_support = Vec::new(); + for ess in records[1].split(",") { + let ess = ess.trim(); + let es = match records[1].parse::() { + Ok(v) => v, + Err(e) => { + bail!( + "invalid envelope version specified in bootstrap node txt record: {}", + e + ); + } + }; + envelope_support.push(es); + } + envelope_support.dedup(); + envelope_support.sort(); + + // Node Id + let node_ids = TypedKeySet::new(); + for node_id_str in records[2].split(",") { + let node_id_str = node_id_str.trim(); + let node_id = match TypedKey::from_str(&node_id_str) { + Ok(v) => v, + Err(e) => { + bail!( + "Invalid node id in bootstrap node record {}: {}", + node_id_str, + e + ); + } + }; + } + + // If this is our own node id, then we skip it for bootstrap, in case we are a bootstrap node + if self.unlocked_inner.matches_own_node_id(&node_ids) { + return Ok(None); + } + + // Hostname + let hostname_str = records[3].trim(); + + // Resolve each record and store in node dial infos list + let mut dial_info_details = Vec::new(); + for rec in records[4].split(",") { + let rec = rec.trim(); + let dial_infos = match DialInfo::try_vec_from_short(rec, hostname_str) { + Ok(dis) => dis, + Err(e) => { + warn!("Couldn't resolve bootstrap node dial info {}: {}", rec, e); + continue; + } + }; + + for di in dial_infos { + dial_info_details.push(DialInfoDetail { + dial_info: di, + class: DialInfoClass::Direct, + }); + } + } + + Ok(Some(BootstrapRecord { + node_ids, + envelope_support, + dial_info_details, + })) + } + // Bootstrap lookup process #[instrument(level = "trace", skip(self), ret, err)] pub(crate) async fn resolve_bootstrap( &self, bootstrap: Vec, - ) -> EyreResult { + ) -> EyreResult> { // Resolve from bootstrap root to bootstrap hostnames let mut bsnames = Vec::::new(); for bh in bootstrap { @@ -58,22 +157,14 @@ impl RoutingTable { Ok(v) => v, }; // for each record resolve into key/bootstraprecord pairs - let mut bootstrap_records: Vec<(PublicKey, BootstrapRecord)> = Vec::new(); + let mut bootstrap_records: Vec = Vec::new(); for bsnirecord in bsnirecords { - // Bootstrap TXT Record Format Version 0: - // txt_version,min_version,max_version,nodeid,hostname,dialinfoshort* - // - // Split bootstrap node record by commas. Example: - // 0,0,0,7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ,bootstrap-1.dev.veilid.net,T5150,U5150,W5150/ws + // All formats split on '|' character let records: Vec = bsnirecord .trim() - .split(',') + .split('|') .map(|x| x.trim().to_owned()) .collect(); - if records.len() < 6 { - warn!("invalid number of fields in bootstrap txt record"); - continue; - } // Bootstrap TXT record version let txt_version: u8 = match records[0].parse::() { @@ -86,81 +177,30 @@ impl RoutingTable { continue; } }; - if txt_version != BOOTSTRAP_TXT_VERSION { - warn!("unsupported bootstrap txt record version"); - continue; - } - - // Min/Max wire protocol version - let min_version: u8 = match records[1].parse::() { - Ok(v) => v, - Err(e) => { - warn!( - "invalid min_version specified in bootstrap node txt record: {}", - e - ); - continue; - } - }; - let max_version: u8 = match records[2].parse::() { - Ok(v) => v, - Err(e) => { - warn!( - "invalid max_version specified in bootstrap node txt record: {}", - e - ); - continue; - } - }; - - // Node Id - let node_id_str = &records[3]; - let node_id_key = match PublicKey::try_decode(node_id_str) { - Ok(v) => v, - Err(e) => { - warn!( - "Invalid node id in bootstrap node record {}: {}", - node_id_str, e - ); - continue; - } - }; - - // Hostname - let hostname_str = &records[4]; - - // If this is our own node id, then we skip it for bootstrap, in case we are a bootstrap node - if self.node_id() == node_id_key { - continue; - } - - // Resolve each record and store in node dial infos list - let mut bootstrap_record = BootstrapRecord { - min_version, - max_version, - dial_info_details: Vec::new(), - }; - for rec in &records[5..] { - let rec = rec.trim(); - let dial_infos = match DialInfo::try_vec_from_short(rec, hostname_str) { - Ok(dis) => dis, - Err(e) => { - warn!( - "Couldn't resolve bootstrap node dial info {}: {}", - rec, e - ); - continue; + let bootstrap_record = match txt_version { + BOOTSTRAP_TXT_VERSION_0 => { + match self.process_bootstrap_records_v0(records).await { + Err(e) => { + warn!( + "couldn't process v0 bootstrap records from {}: {}", + bsname, e + ); + continue; + } + Ok(Some(v)) => v, + Ok(None) => { + // skipping + continue; + } } - }; - - for di in dial_infos { - bootstrap_record.dial_info_details.push(DialInfoDetail { - dial_info: di, - class: DialInfoClass::Direct, - }); } - } - bootstrap_records.push((node_id_key, bootstrap_record)); + _ => { + warn!("unsupported bootstrap txt record version"); + continue; + } + }; + + bootstrap_records.push(bootstrap_record); } Some(bootstrap_records) } @@ -168,21 +208,35 @@ impl RoutingTable { ); } - let mut bsmap = BootstrapRecordMap::new(); + let mut merged_bootstrap_records: Vec = Vec::new(); while let Some(bootstrap_records) = unord.next().await { - if let Some(bootstrap_records) = bootstrap_records { - for (bskey, mut bsrec) in bootstrap_records { - let rec = bsmap.entry(bskey).or_insert_with(|| BootstrapRecord { - min_version: bsrec.min_version, - max_version: bsrec.max_version, - dial_info_details: Vec::new(), - }); - rec.dial_info_details.append(&mut bsrec.dial_info_details); + let Some(bootstrap_records) = bootstrap_records else { + continue; + }; + for mut bsrec in bootstrap_records { + let mut mbi = 0; + while mbi < merged_bootstrap_records.len() { + let mbr = &mut merged_bootstrap_records[mbi]; + if mbr.node_ids.contains_any(&bsrec.node_ids) { + // Merge record, pop this one out + let mbr = merged_bootstrap_records.remove(mbi); + bsrec.merge(&mbr); + } else { + // No overlap, go to next record + mbi += 1; + } } + // Append merged record + merged_bootstrap_records.push(bsrec); } } - Ok(bsmap) + // ensure dial infos are sorted + for mbr in &mut merged_bootstrap_records { + mbr.dial_info_details.sort(); + } + + Ok(merged_bootstrap_records) } // 'direct' bootstrap task routine for systems incapable of resolving TXT records, such as browser WASM @@ -203,14 +257,10 @@ impl RoutingTable { // Got peer info, let's add it to the routing table for pi in peer_info { - let k = pi.node_id.key; // Register the node - if let Some(nr) = self.register_node_with_signed_node_info( - RoutingDomain::PublicInternet, - k, - pi.signed_node_info, - false, - ) { + if let Some(nr) = + self.register_node_with_peer_info(RoutingDomain::PublicInternet, pi, false) + { // Add this our futures to process in parallel let routing_table = self.clone(); unord.push( @@ -230,90 +280,62 @@ impl RoutingTable { #[instrument(level = "trace", skip(self), err)] pub(crate) async fn bootstrap_task_routine(self, stop_token: StopToken) -> EyreResult<()> { - let (bootstrap, bootstrap_nodes) = self.with_config(|c| { - ( - c.network.bootstrap.clone(), - c.network.bootstrap_nodes.clone(), - ) - }); + let bootstrap = self + .unlocked_inner + .with_config(|c| c.network.routing_table.bootstrap.clone()); + + // Don't bother if bootstraps aren't configured + if bootstrap.is_empty() { + return Ok(()); + } log_rtab!(debug "--- bootstrap_task"); // See if we are specifying a direct dialinfo for bootstrap, if so use the direct mechanism - if !bootstrap.is_empty() && bootstrap_nodes.is_empty() { - let mut bootstrap_dialinfos = Vec::::new(); - for b in &bootstrap { - if let Ok(bootstrap_di_vec) = DialInfo::try_vec_from_url(&b) { - for bootstrap_di in bootstrap_di_vec { - bootstrap_dialinfos.push(bootstrap_di); - } + let mut bootstrap_dialinfos = Vec::::new(); + for b in &bootstrap { + if let Ok(bootstrap_di_vec) = DialInfo::try_vec_from_url(&b) { + for bootstrap_di in bootstrap_di_vec { + bootstrap_dialinfos.push(bootstrap_di); } } - if bootstrap_dialinfos.len() > 0 { - return self - .direct_bootstrap_task_routine(stop_token, bootstrap_dialinfos) - .await; - } + } + if bootstrap_dialinfos.len() > 0 { + return self + .direct_bootstrap_task_routine(stop_token, bootstrap_dialinfos) + .await; } - // If we aren't specifying a bootstrap node list explicitly, then pull from the bootstrap server(s) - let bsmap: BootstrapRecordMap = if !bootstrap_nodes.is_empty() { - let mut bsmap = BootstrapRecordMap::new(); - let mut bootstrap_node_dial_infos = Vec::new(); - for b in bootstrap_nodes { - let (id_str, di_str) = b - .split_once('@') - .ok_or_else(|| eyre!("Invalid node dial info in bootstrap entry"))?; - let node_id = - NodeId::from_str(id_str).wrap_err("Invalid node id in bootstrap entry")?; - let dial_info = - DialInfo::from_str(di_str).wrap_err("Invalid dial info in bootstrap entry")?; - bootstrap_node_dial_infos.push((node_id, dial_info)); - } - for (node_id, dial_info) in bootstrap_node_dial_infos { - bsmap - .entry(node_id.key) - .or_insert_with(|| BootstrapRecord { - min_version: MIN_CRYPTO_VERSION, - max_version: MAX_CRYPTO_VERSION, - dial_info_details: Vec::new(), - }) - .dial_info_details - .push(DialInfoDetail { - dial_info, - class: DialInfoClass::Direct, // Bootstraps are always directly reachable - }); - } - bsmap - } else { - // Resolve bootstrap servers and recurse their TXT entries - self.resolve_bootstrap(bootstrap).await? - }; - - // Map all bootstrap entries to a single key with multiple dialinfo + // If not direct, resolve bootstrap servers and recurse their TXT entries + let bsrecs = self.resolve_bootstrap(bootstrap).await?; // Run all bootstrap operations concurrently let mut unord = FuturesUnordered::new(); - for (k, mut v) in bsmap { - // Sort dial info so we get the preferred order correct - v.dial_info_details.sort(); + for bsrec in bsrecs { + log_rtab!( + "--- bootstrapping {} with {:?}", + &bsrec.node_ids, + &bsrec.dial_info_details + ); - log_rtab!("--- bootstrapping {} with {:?}", k.encode(), &v); + // Get crypto support from list of node ids + let crypto_support = bsrec.node_ids.kinds(); - // Make invalid signed node info (no signature) - if let Some(nr) = self.register_node_with_signed_node_info( - RoutingDomain::PublicInternet, - k, - SignedNodeInfo::Direct(SignedDirectNodeInfo::with_no_signature(NodeInfo { - network_class: NetworkClass::InboundCapable, // Bootstraps are always inbound capable - outbound_protocols: ProtocolTypeSet::only(ProtocolType::UDP), // Bootstraps do not participate in relaying and will not make outbound requests, but will have UDP enabled - address_types: AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable - min_version: v.min_version, // Minimum crypto version specified in txt record - max_version: v.max_version, // Maximum crypto version specified in txt record - dial_info_detail_list: v.dial_info_details, // Dial info is as specified in the bootstrap list - })), - true, - ) { + // Make unsigned SignedNodeInfo + let sni = SignedNodeInfo::Direct(SignedDirectNodeInfo::with_no_signature(NodeInfo { + network_class: NetworkClass::InboundCapable, // Bootstraps are always inbound capable + outbound_protocols: ProtocolTypeSet::only(ProtocolType::UDP), // Bootstraps do not participate in relaying and will not make outbound requests, but will have UDP enabled + address_types: AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable + envelope_support: bsrec.envelope_support, // Envelope support is as specified in the bootstrap list + crypto_support, // Crypto support is derived from list of node ids + dial_info_detail_list: bsrec.dial_info_details, // Dial info is as specified in the bootstrap list + })); + + let pi = PeerInfo::new(bsrec.node_ids, sni); + + if let Some(nr) = + self.register_node_with_peer_info(RoutingDomain::PublicInternet, pi, true) + { // Add this our futures to process in parallel let routing_table = self.clone(); unord.push( diff --git a/veilid-core/src/routing_table/tasks/kick_buckets.rs b/veilid-core/src/routing_table/tasks/kick_buckets.rs index 318f1915..f3ebcc98 100644 --- a/veilid-core/src/routing_table/tasks/kick_buckets.rs +++ b/veilid-core/src/routing_table/tasks/kick_buckets.rs @@ -10,12 +10,13 @@ impl RoutingTable { _last_ts: Timestamp, cur_ts: Timestamp, ) -> EyreResult<()> { - let kick_queue: Vec = core::mem::take(&mut *self.unlocked_inner.kick_queue.lock()) - .into_iter() - .collect(); + let kick_queue: Vec<(CryptoKind, usize)> = + core::mem::take(&mut *self.unlocked_inner.kick_queue.lock()) + .into_iter() + .collect(); let mut inner = self.inner.write(); - for idx in kick_queue { - inner.kick_bucket(idx) + for (ck, idx) in kick_queue { + inner.kick_bucket(ck, idx) } Ok(()) } diff --git a/veilid-core/src/rpc_processor/rpc_find_node.rs b/veilid-core/src/rpc_processor/rpc_find_node.rs index 87e7c495..13b95c95 100644 --- a/veilid-core/src/rpc_processor/rpc_find_node.rs +++ b/veilid-core/src/rpc_processor/rpc_find_node.rs @@ -106,7 +106,14 @@ impl RPCProcessor { ) as RoutingTableEntryFilter; let filters = VecDeque::from([filter]); + let node_count = { + let config = self.config(); + let c = config.get(); + c.network.dht.max_find_node_count as usize + }; + let closest_nodes = routing_table.find_closest_nodes( + node_count, find_node_q.node_id, filters, // transform diff --git a/veilid-core/src/tests/common/test_veilid_config.rs b/veilid-core/src/tests/common/test_veilid_config.rs index b023171a..6b175b8b 100644 --- a/veilid-core/src/tests/common/test_veilid_config.rs +++ b/veilid-core/src/tests/common/test_veilid_config.rs @@ -195,7 +195,6 @@ fn config_callback(key: String) -> ConfigCallbackReturn { "network.node_id" => Ok(Box::new(Option::::None)), "network.node_id_secret" => Ok(Box::new(Option::::None)), "network.bootstrap" => Ok(Box::new(Vec::::new())), - "network.bootstrap_nodes" => Ok(Box::new(Vec::::new())), "network.routing_table.limit_over_attached" => Ok(Box::new(64u32)), "network.routing_table.limit_fully_attached" => Ok(Box::new(32u32)), "network.routing_table.limit_attached_strong" => Ok(Box::new(16u32)), @@ -319,7 +318,6 @@ pub async fn test_config() { assert!(inner.network.node_id.is_none()); assert!(inner.network.node_id_secret.is_none()); assert_eq!(inner.network.bootstrap, Vec::::new()); - assert_eq!(inner.network.bootstrap_nodes, Vec::::new()); assert_eq!(inner.network.rpc.concurrency, 2u32); assert_eq!(inner.network.rpc.queue_size, 1024u32); assert_eq!(inner.network.rpc.timeout_ms, 10_000u32); diff --git a/veilid-core/src/veilid_config.rs b/veilid-core/src/veilid_config.rs index 065340ee..a307e527 100644 --- a/veilid-core/src/veilid_config.rs +++ b/veilid-core/src/veilid_config.rs @@ -352,7 +352,6 @@ pub struct VeilidConfigNodeId { pub struct VeilidConfigRoutingTable { pub node_ids: BTreeMap, pub bootstrap: Vec, - pub bootstrap_nodes: Vec, pub limit_over_attached: u32, pub limit_fully_attached: u32, pub limit_attached_strong: u32, @@ -677,7 +676,6 @@ impl VeilidConfig { get_config_indexed!(inner.network.routing_table.node_ids, ck, node_id_secret); } get_config!(inner.network.routing_table.bootstrap); - get_config!(inner.network.routing_table.bootstrap_nodes); get_config!(inner.network.routing_table.limit_over_attached); get_config!(inner.network.routing_table.limit_fully_attached); get_config!(inner.network.routing_table.limit_attached_strong); diff --git a/veilid-flutter/lib/default_config.dart b/veilid-flutter/lib/default_config.dart index 51800a03..d2844be6 100644 --- a/veilid-flutter/lib/default_config.dart +++ b/veilid-flutter/lib/default_config.dart @@ -51,7 +51,6 @@ Future getDefaultVeilidConfig(String programName) async { bootstrap: kIsWeb ? ["ws://bootstrap.dev.veilid.net:5150/ws"] : ["bootstrap.dev.veilid.net"], - bootstrapNodes: [], routingTable: VeilidConfigRoutingTable( limitOverAttached: 64, limitFullyAttached: 32, diff --git a/veilid-flutter/lib/veilid.dart b/veilid-flutter/lib/veilid.dart index 3a8baea4..919103d5 100644 --- a/veilid-flutter/lib/veilid.dart +++ b/veilid-flutter/lib/veilid.dart @@ -744,7 +744,6 @@ class VeilidConfigNetwork { String? nodeId; String? nodeIdSecret; List bootstrap; - List bootstrapNodes; VeilidConfigRoutingTable routingTable; VeilidConfigRPC rpc; VeilidConfigDHT dht; @@ -768,7 +767,6 @@ class VeilidConfigNetwork { required this.nodeId, required this.nodeIdSecret, required this.bootstrap, - required this.bootstrapNodes, required this.routingTable, required this.rpc, required this.dht, @@ -794,7 +792,6 @@ class VeilidConfigNetwork { 'node_id': nodeId, 'node_id_secret': nodeIdSecret, 'bootstrap': bootstrap, - 'bootstrap_nodes': bootstrapNodes, 'routing_table': routingTable.json, 'rpc': rpc.json, 'dht': dht.json, @@ -823,7 +820,6 @@ class VeilidConfigNetwork { nodeId = json['node_id'], nodeIdSecret = json['node_id_secret'], bootstrap = json['bootstrap'], - bootstrapNodes = json['bootstrap_nodes'], routingTable = VeilidConfigRoutingTable.fromJson(json['routing_table']), rpc = VeilidConfigRPC.fromJson(json['rpc']), dht = VeilidConfigDHT.fromJson(json['dht']), diff --git a/veilid-server/src/cmdline.rs b/veilid-server/src/cmdline.rs index 7eaf84b4..a3602eb0 100644 --- a/veilid-server/src/cmdline.rs +++ b/veilid-server/src/cmdline.rs @@ -122,14 +122,6 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result EyreResult<(Settings, ArgMatches)> { settingsrw.core.network.bootstrap = bootstrap_list; } - if matches.occurrences_of("bootstrap-nodes") != 0 { - let bootstrap_list = match matches.value_of("bootstrap-nodes") { - Some(x) => { - println!("Overriding bootstrap node list with: "); - let mut out: Vec = Vec::new(); - for x in x.split(',') { - let x = x.trim(); - println!(" {}", x); - out.push( - ParsedNodeDialInfo::from_str(x) - .wrap_err("unable to parse dial info in bootstrap node list")?, - ); - } - out - } - None => { - bail!("value not specified for bootstrap node list"); - } - }; - settingsrw.core.network.bootstrap_nodes = bootstrap_list; - } - #[cfg(feature = "rt-tokio")] if matches.occurrences_of("console") != 0 { settingsrw.logging.console.enabled = true; diff --git a/veilid-server/src/settings.rs b/veilid-server/src/settings.rs index d0fbd29c..94b182d9 100644 --- a/veilid-server/src/settings.rs +++ b/veilid-server/src/settings.rs @@ -67,7 +67,6 @@ core: node_id: null node_id_secret: null bootstrap: ['bootstrap.dev.veilid.net'] - bootstrap_nodes: [] routing_table: limit_over_attached: 64 limit_fully_attached: 32 @@ -300,66 +299,6 @@ impl serde::Serialize for ParsedUrl { } } -#[derive(Debug, Clone, PartialEq)] -pub struct ParsedNodeDialInfo { - pub node_dial_info_string: String, - pub node_id: NodeId, - pub dial_info: DialInfo, -} - -// impl ParsedNodeDialInfo { -// pub fn offset_port(&mut self, offset: u16) -> Result<(), ()> { -// // Bump port on dial_info -// self.dial_info -// .set_port(self.dial_info.port() + 1); -// self.node_dial_info_string = format!("{}@{}",self.node_id, self.dial_info); -// Ok(()) -// } -// } - -impl FromStr for ParsedNodeDialInfo { - type Err = veilid_core::VeilidAPIError; - fn from_str( - node_dial_info_string: &str, - ) -> Result { - let (id_str, di_str) = node_dial_info_string.split_once('@').ok_or_else(|| { - VeilidAPIError::invalid_argument( - "Invalid node dial info in bootstrap entry", - "node_dial_info_string", - node_dial_info_string, - ) - })?; - let node_id = NodeId::from_str(id_str) - .map_err(|e| VeilidAPIError::invalid_argument(e, "node_id", id_str))?; - let dial_info = DialInfo::from_str(di_str) - .map_err(|e| VeilidAPIError::invalid_argument(e, "dial_info", id_str))?; - Ok(Self { - node_dial_info_string: node_dial_info_string.to_owned(), - node_id, - dial_info, - }) - } -} - -impl<'de> serde::Deserialize<'de> for ParsedNodeDialInfo { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - ParsedNodeDialInfo::from_str(s.as_str()).map_err(serde::de::Error::custom) - } -} - -impl serde::Serialize for ParsedNodeDialInfo { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.node_dial_info_string.serialize(serializer) - } -} - #[derive(Debug, PartialEq)] pub struct NamedSocketAddrs { pub name: String, @@ -598,7 +537,6 @@ pub struct Network { pub node_id: Option, pub node_id_secret: Option, pub bootstrap: Vec, - pub bootstrap_nodes: Vec, pub routing_table: RoutingTable, pub rpc: Rpc, pub dht: Dht, @@ -967,7 +905,6 @@ impl Settings { set_config_value!(inner.core.network.node_id, value); set_config_value!(inner.core.network.node_id_secret, value); set_config_value!(inner.core.network.bootstrap, value); - set_config_value!(inner.core.network.bootstrap_nodes, value); set_config_value!(inner.core.network.routing_table.limit_over_attached, value); set_config_value!(inner.core.network.routing_table.limit_fully_attached, value); set_config_value!( @@ -1122,16 +1059,6 @@ impl Settings { "network.node_id" => Ok(Box::new(inner.core.network.node_id)), "network.node_id_secret" => Ok(Box::new(inner.core.network.node_id_secret)), "network.bootstrap" => Ok(Box::new(inner.core.network.bootstrap.clone())), - "network.bootstrap_nodes" => Ok(Box::new( - inner - .core - .network - .bootstrap_nodes - .clone() - .into_iter() - .map(|e| e.node_dial_info_string) - .collect::>(), - )), "network.routing_table.limit_over_attached" => Ok(Box::new( inner.core.network.routing_table.limit_over_attached, )), @@ -1495,7 +1422,6 @@ mod tests { s.core.network.bootstrap, vec!["bootstrap.dev.veilid.net".to_owned()] ); - assert_eq!(s.core.network.bootstrap_nodes, vec![]); // assert_eq!(s.core.network.rpc.concurrency, 0); assert_eq!(s.core.network.rpc.queue_size, 1024);