bootstrap

This commit is contained in:
John Smith 2023-02-13 16:12:46 -05:00
parent 5fd0684ae7
commit 1d8e2d3fda
19 changed files with 428 additions and 471 deletions

7
Cargo.lock generated
View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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<CryptoKind> {
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<TypedKey> {
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;
}

View File

@ -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);
}

View File

@ -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<TypedKey, Arc<BucketEntry>>,
entries: BTreeMap<PublicKey, Arc<BucketEntry>>,
/// The most recent entry in this bucket
newest_entry: Option<TypedKey>,
newest_entry: Option<PublicKey>,
/// 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<BucketEntry>>;
alloc::collections::btree_map::Iter<'a, PublicKey, Arc<BucketEntry>>;
#[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<SerializedBucketEntryData>,
newest_entry: Option<TypedKey>,
newest_entry: Option<PublicKey>,
}
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<BucketEntry>) {
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<BucketEntry>) {
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<Arc<BucketEntry>> {
pub(super) fn entry(&self, key: &PublicKey) -> Option<Arc<BucketEntry>> {
self.entries.get(key).cloned()
}
@ -145,7 +141,7 @@ impl Bucket {
self.entries.iter()
}
pub(super) fn kick(&mut self, bucket_depth: usize) -> Option<BTreeSet<TypedKey>> {
pub(super) fn kick(&mut self, bucket_depth: usize) -> Option<BTreeSet<PublicKey>> {
// 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<TypedKey> = BTreeSet::new();
let mut dead_node_ids: BTreeSet<PublicKey> = 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<BucketEntry>)> = self
let mut sorted_entries: Vec<(PublicKey, Arc<BucketEntry>)> = self
.entries
.iter()
.map(|(k, v)| (k.clone(), v.clone()))

View File

@ -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::<Vec<String>>()
.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 {

View File

@ -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<RoutingTableEntryFilter>,
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<NetworkResult<Vec<NodeRef>>> {
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<NetworkResult<Vec<NodeRef>>> {
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<BucketEntry>)> = None;
let mut best_inbound_relay: Option<Arc<BucketEntry>> = 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))
}
}

View File

@ -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<RoutingTableUnlockedInner>,
/// Routing table buckets that hold references to entries, per crypto kind
pub(super) buckets: BTreeMap<CryptoKind, Vec<Bucket>>,
/// 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<Weak<BucketEntry>>,
/// 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<BucketEntry>) -> Option<T>,
>(
pub fn with_entries<T, F: FnMut(&RoutingTableInner, Arc<BucketEntry>) -> Option<T>>(
&self,
cur_ts: Timestamp,
min_state: BucketEntryState,
mut f: F,
) -> Option<T> {
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<BucketEntry>) -> Option<T>,
>(
pub fn with_entries_mut<T, F: FnMut(&mut RoutingTableInner, Arc<BucketEntry>) -> Option<T>>(
&mut self,
cur_ts: Timestamp,
min_state: BucketEntryState,
mut f: F,
) -> Option<T> {
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<Arc<BucketEntry>>| {
|_rti: &RoutingTableInner, v: Option<Arc<BucketEntry>>| {
NodeRef::new(outer_self.clone(), v.unwrap().clone(), None)
},
)
@ -846,30 +839,30 @@ impl RoutingTableInner {
&'b Option<Arc<BucketEntry>>,
&'b Option<Arc<BucketEntry>>,
) -> core::cmp::Ordering,
T: for<'r> FnMut(&'r RoutingTableInner, TypedKey, Option<Arc<BucketEntry>>) -> O,
T: for<'r> FnMut(&'r RoutingTableInner, Option<Arc<BucketEntry>>) -> O,
{
// collect all the nodes for sorting
let mut nodes =
Vec::<(TypedKey, Option<Arc<BucketEntry>>)>::with_capacity(self.bucket_entry_count + 1);
Vec::<Option<Arc<BucketEntry>>>::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::<O>::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<Arc<BucketEntry>>),
(b_key, b_entry): &(TypedKey, Option<Arc<BucketEntry>>)| {
a_entry: &Option<Arc<BucketEntry>>,
b_entry: &Option<Arc<BucketEntry>>| {
// 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<T, O>(
&self,
node_count: usize,
node_id: TypedKey,
filters: VecDeque<RoutingTableEntryFilter>,
transform: T,
) -> Vec<O>
where
T: for<'r> FnMut(&'r RoutingTableInner, TypedKey, Option<Arc<BucketEntry>>) -> O,
T: for<'r> FnMut(&'r RoutingTableInner, Option<Arc<BucketEntry>>) -> 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<Arc<BucketEntry>>),
(b_key, b_entry): &(TypedKey, Option<Arc<BucketEntry>>)| {
a_entry: &Option<Arc<BucketEntry>>,
b_entry: &Option<Arc<BucketEntry>>| {
// 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;
}

View File

@ -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<u8>,
dial_info_details: Vec<DialInfoDetail>,
}
pub type BootstrapRecordMap = BTreeMap<PublicKey, BootstrapRecord>;
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<String>,
) -> EyreResult<Option<BootstrapRecord>> {
// 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::<u8>() {
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<String>,
) -> EyreResult<BootstrapRecordMap> {
) -> EyreResult<Vec<BootstrapRecord>> {
// Resolve from bootstrap root to bootstrap hostnames
let mut bsnames = Vec::<String>::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<BootstrapRecord> = 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<String> = 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::<u8>() {
@ -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::<u8>() {
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::<u8>() {
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<BootstrapRecord> = 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::<DialInfo>::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::<DialInfo>::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(

View File

@ -10,12 +10,13 @@ impl RoutingTable {
_last_ts: Timestamp,
cur_ts: Timestamp,
) -> EyreResult<()> {
let kick_queue: Vec<usize> = 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(())
}

View File

@ -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

View File

@ -195,7 +195,6 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
"network.node_id" => Ok(Box::new(Option::<TypedKey>::None)),
"network.node_id_secret" => Ok(Box::new(Option::<SecretKey>::None)),
"network.bootstrap" => Ok(Box::new(Vec::<String>::new())),
"network.bootstrap_nodes" => Ok(Box::new(Vec::<String>::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::<String>::new());
assert_eq!(inner.network.bootstrap_nodes, Vec::<String>::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);

View File

@ -352,7 +352,6 @@ pub struct VeilidConfigNodeId {
pub struct VeilidConfigRoutingTable {
pub node_ids: BTreeMap<CryptoKind, VeilidConfigNodeId>,
pub bootstrap: Vec<String>,
pub bootstrap_nodes: Vec<String>,
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);

View File

@ -51,7 +51,6 @@ Future<VeilidConfig> getDefaultVeilidConfig(String programName) async {
bootstrap: kIsWeb
? ["ws://bootstrap.dev.veilid.net:5150/ws"]
: ["bootstrap.dev.veilid.net"],
bootstrapNodes: [],
routingTable: VeilidConfigRoutingTable(
limitOverAttached: 64,
limitFullyAttached: 32,

View File

@ -744,7 +744,6 @@ class VeilidConfigNetwork {
String? nodeId;
String? nodeIdSecret;
List<String> bootstrap;
List<String> 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']),

View File

@ -122,14 +122,6 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap
.value_name("BOOTSTRAP_LIST")
.help("Specify a list of bootstrap hostnames to use")
)
.arg(
Arg::new("bootstrap-nodes")
.conflicts_with("bootstrap")
.long("bootstrap-nodes")
.takes_value(true)
.value_name("BOOTSTRAP_NODE_LIST")
.help("Specify a list of bootstrap node dialinfos to use"),
)
.arg(
Arg::new("panic")
.long("panic")
@ -280,28 +272,6 @@ pub fn process_command_line() -> 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<ParsedNodeDialInfo> = 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;

View File

@ -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<ParsedNodeDialInfo, veilid_core::VeilidAPIError> {
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<D>(deserializer: D) -> Result<Self, D::Error>
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<veilid_core::TypedKey>,
pub node_id_secret: Option<veilid_core::SecretKey>,
pub bootstrap: Vec<String>,
pub bootstrap_nodes: Vec<ParsedNodeDialInfo>,
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::<Vec<String>>(),
)),
"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);