bootstrap
This commit is contained in:
parent
5fd0684ae7
commit
1d8e2d3fda
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -6003,6 +6003,7 @@ dependencies = [
|
|||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"wasm-bindgen-test",
|
"wasm-bindgen-test",
|
||||||
"wasm-logger",
|
"wasm-logger",
|
||||||
|
"weak-table",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"webpki 0.22.0",
|
"webpki 0.22.0",
|
||||||
"webpki-roots 0.22.6",
|
"webpki-roots 0.22.6",
|
||||||
@ -6327,6 +6328,12 @@ dependencies = [
|
|||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "weak-table"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.60"
|
version = "0.3.60"
|
||||||
|
@ -50,7 +50,6 @@ core:
|
|||||||
node_id: ''
|
node_id: ''
|
||||||
node_id_secret: ''
|
node_id_secret: ''
|
||||||
bootstrap: ['bootstrap.dev.veilid.net']
|
bootstrap: ['bootstrap.dev.veilid.net']
|
||||||
bootstrap_nodes: []
|
|
||||||
routing_table:
|
routing_table:
|
||||||
limit_over_attached: 64
|
limit_over_attached: 64
|
||||||
limit_fully_attached: 32
|
limit_fully_attached: 32
|
||||||
|
@ -14,14 +14,14 @@ and the `veilid-server.conf` file.
|
|||||||
|
|
||||||
## Global Directives
|
## Global Directives
|
||||||
|
|
||||||
| Directive | Description |
|
| Directive | Description |
|
||||||
|-------------------------------|-----------------------------------------|
|
| ---------------------------- | ------------------------------------- |
|
||||||
| [daemon](#daemon) | Run `veilid-server` in the background |
|
| [daemon](#daemon) | Run `veilid-server` in the background |
|
||||||
| [client\_api](#client_api) ||
|
| [client\_api](#client_api) | |
|
||||||
| [auto\_attach](#auto_attach) ||
|
| [auto\_attach](#auto_attach) | |
|
||||||
| [logging](#logging) ||
|
| [logging](#logging) | |
|
||||||
| [testing](#testing) ||
|
| [testing](#testing) | |
|
||||||
| [core](#core) ||
|
| [core](#core) | |
|
||||||
|
|
||||||
|
|
||||||
### daemon
|
### daemon
|
||||||
@ -39,10 +39,10 @@ client_api:
|
|||||||
listen_address: 'localhost:5959'
|
listen_address: 'localhost:5959'
|
||||||
```
|
```
|
||||||
|
|
||||||
| Parameter | Description |
|
| Parameter | Description |
|
||||||
|-----------------------------------------------|-------------|
|
| -------------------------------------------- | ----------- |
|
||||||
| [enabled](#client_apienabled) ||
|
| [enabled](#client_apienabled) | |
|
||||||
| [listen\_address](#client_apilisten_address) ||
|
| [listen\_address](#client_apilisten_address) | |
|
||||||
|
|
||||||
#### client\_api:enabled
|
#### client\_api:enabled
|
||||||
|
|
||||||
@ -82,13 +82,13 @@ logging:
|
|||||||
grpc_endpoint: 'localhost:4317'
|
grpc_endpoint: 'localhost:4317'
|
||||||
```
|
```
|
||||||
|
|
||||||
| Parameter | Description |
|
| Parameter | Description |
|
||||||
|-------------------------------|-------------|
|
| ---------------------------- | ----------- |
|
||||||
| [system](#loggingsystem) ||
|
| [system](#loggingsystem) | |
|
||||||
| [terminal](#loggingterminal) ||
|
| [terminal](#loggingterminal) | |
|
||||||
| [file](#loggingfile) ||
|
| [file](#loggingfile) | |
|
||||||
| [api](#loggingapi) ||
|
| [api](#loggingapi) | |
|
||||||
| [otlp](#loggingotlp) ||
|
| [otlp](#loggingotlp) | |
|
||||||
|
|
||||||
#### logging:system
|
#### logging:system
|
||||||
|
|
||||||
@ -142,12 +142,12 @@ testing:
|
|||||||
|
|
||||||
### core
|
### core
|
||||||
|
|
||||||
| Parameter | Description |
|
| Parameter | Description |
|
||||||
|-------------------------------------------|-------------|
|
| ---------------------------------------- | ----------- |
|
||||||
| [protected\_store](#coreprotected_store) ||
|
| [protected\_store](#coreprotected_store) | |
|
||||||
| [table\_store](#coretable_store) ||
|
| [table\_store](#coretable_store) | |
|
||||||
| [block\_store](#block_store) ||
|
| [block\_store](#block_store) | |
|
||||||
| [network](#corenetwork) ||
|
| [network](#corenetwork) | |
|
||||||
|
|
||||||
#### core:protected\_store
|
#### core:protected\_store
|
||||||
|
|
||||||
@ -191,7 +191,6 @@ network:
|
|||||||
node_id: ''
|
node_id: ''
|
||||||
node_id_secret: ''
|
node_id_secret: ''
|
||||||
bootstrap: ['bootstrap.dev.veilid.net']
|
bootstrap: ['bootstrap.dev.veilid.net']
|
||||||
bootstrap_nodes: []
|
|
||||||
upnp: true
|
upnp: true
|
||||||
detect_address_changes: true
|
detect_address_changes: true
|
||||||
enable_local_peer_scope: false
|
enable_local_peer_scope: false
|
||||||
@ -199,13 +198,13 @@ network:
|
|||||||
```
|
```
|
||||||
|
|
||||||
| Parameter | Description |
|
| Parameter | Description |
|
||||||
|---------------------------------------------|-------------|
|
| ------------------------------------------- | ----------- |
|
||||||
| [routing\_table](#corenetworkrouting_table) ||
|
| [routing\_table](#corenetworkrouting_table) | |
|
||||||
| [rpc](#corenetworkrpc) ||
|
| [rpc](#corenetworkrpc) | |
|
||||||
| [dht](#corenetworkdht) ||
|
| [dht](#corenetworkdht) | |
|
||||||
| [tls](#corenetworktls) ||
|
| [tls](#corenetworktls) | |
|
||||||
| [application](#corenetworkapplication) ||
|
| [application](#corenetworkapplication) | |
|
||||||
| [protocol](#corenetworkprotocol) ||
|
| [protocol](#corenetworkprotocol) | |
|
||||||
|
|
||||||
#### core:network:routing\_table
|
#### core:network:routing\_table
|
||||||
|
|
||||||
|
@ -11,11 +11,11 @@ crate-type = ["cdylib", "staticlib", "rlib"]
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
rt-async-std = [ "async-std", "async-std-resolver", "async_executors/async_std", "rtnetlink?/smol_socket", "veilid-tools/rt-async-std" ]
|
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-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_android_tests = ["dep:paranoid-android"]
|
||||||
veilid_core_ios_tests = [ "dep:tracing-oslog" ]
|
veilid_core_ios_tests = ["dep:tracing-oslog"]
|
||||||
tracking = []
|
tracking = []
|
||||||
|
|
||||||
[dependencies]
|
[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"] }
|
rkyv = { git = "https://github.com/rkyv/rkyv.git", rev = "57e2a8d", default_features = false, features = ["std", "alloc", "strict", "size_32", "validation"] }
|
||||||
bytecheck = "^0"
|
bytecheck = "^0"
|
||||||
data-encoding = { version = "^2" }
|
data-encoding = { version = "^2" }
|
||||||
|
weak-table = "0.3.2"
|
||||||
|
|
||||||
# Dependencies for native builds only
|
# Dependencies for native builds only
|
||||||
# Linux, Windows, Mac, iOS, Android
|
# Linux, Windows, Mac, iOS, Android
|
||||||
|
@ -11,9 +11,9 @@ use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as
|
|||||||
pub type CryptoKind = FourCC;
|
pub type CryptoKind = FourCC;
|
||||||
|
|
||||||
/// Sort best crypto kinds first
|
/// Sort best crypto kinds first
|
||||||
pub fn compare_crypto_kind(a: CryptoKind, b: CryptoKind) -> cmp::Ordering {
|
pub fn compare_crypto_kind(a: &CryptoKind, b: &CryptoKind) -> cmp::Ordering {
|
||||||
let a_idx = VALID_CRYPTO_KINDS.iter().position(|&k| k == a);
|
let a_idx = VALID_CRYPTO_KINDS.iter().position(|k| k == a);
|
||||||
let b_idx = VALID_CRYPTO_KINDS.iter().position(|&k| k == b);
|
let b_idx = VALID_CRYPTO_KINDS.iter().position(|k| k == b);
|
||||||
if let Some(a_idx) = a_idx {
|
if let Some(a_idx) = a_idx {
|
||||||
if let Some(b_idx) = b_idx {
|
if let Some(b_idx) = b_idx {
|
||||||
a_idx.cmp(&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 {
|
} else if let Some(b_idx) = b_idx {
|
||||||
cmp::Ordering::Greater
|
cmp::Ordering::Greater
|
||||||
} else {
|
} else {
|
||||||
a.cmp(&b)
|
a.cmp(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ impl PartialOrd for TypedKey {
|
|||||||
|
|
||||||
impl Ord for TypedKey {
|
impl Ord for TypedKey {
|
||||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
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 {
|
if x != cmp::Ordering::Equal {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
@ -140,6 +140,14 @@ impl TypedKeySet {
|
|||||||
items: Vec::with_capacity(cap),
|
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> {
|
pub fn get(&self, kind: CryptoKind) -> Option<TypedKey> {
|
||||||
self.items.iter().find(|x| x.kind == kind).copied()
|
self.items.iter().find(|x| x.kind == kind).copied()
|
||||||
}
|
}
|
||||||
@ -153,6 +161,18 @@ impl TypedKeySet {
|
|||||||
self.items.push(typed_key);
|
self.items.push(typed_key);
|
||||||
self.items.sort()
|
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) {
|
pub fn remove(&self, kind: CryptoKind) {
|
||||||
if let Some(idx) = self.items.iter().position(|x| x.kind == kind) {
|
if let Some(idx) = self.items.iter().position(|x| x.kind == kind) {
|
||||||
self.items.remove(idx);
|
self.items.remove(idx);
|
||||||
@ -256,7 +276,7 @@ impl PartialOrd for TypedKeyPair {
|
|||||||
|
|
||||||
impl Ord for TypedKeyPair {
|
impl Ord for TypedKeyPair {
|
||||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
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 {
|
if x != cmp::Ordering::Equal {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
@ -340,7 +360,7 @@ impl PartialOrd for TypedSignature {
|
|||||||
|
|
||||||
impl Ord for TypedSignature {
|
impl Ord for TypedSignature {
|
||||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
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 {
|
if x != cmp::Ordering::Equal {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
@ -410,7 +430,7 @@ impl PartialOrd for TypedKeySignature {
|
|||||||
|
|
||||||
impl Ord for TypedKeySignature {
|
impl Ord for TypedKeySignature {
|
||||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
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 {
|
if x != cmp::Ordering::Equal {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
@ -1327,7 +1327,7 @@ impl NetworkManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Is this an out-of-band receipt instead of an envelope?
|
// 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 => {});
|
network_result_value_or_log!(self.handle_out_of_band_receipt(data).await => {});
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
@ -11,19 +11,19 @@ pub struct Bucket {
|
|||||||
/// handle to the routing table
|
/// handle to the routing table
|
||||||
routing_table: RoutingTable,
|
routing_table: RoutingTable,
|
||||||
/// Map of keys to entries for this bucket
|
/// 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
|
/// 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
|
/// The crypto kind in use for the public keys in this bucket
|
||||||
kind: CryptoKind,
|
kind: CryptoKind,
|
||||||
}
|
}
|
||||||
pub(super) type EntriesIter<'a> =
|
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)]
|
#[derive(Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
|
||||||
#[archive_attr(repr(C), derive(CheckBytes))]
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
struct SerializedBucketEntryData {
|
struct SerializedBucketEntryData {
|
||||||
key: TypedKey,
|
key: PublicKey,
|
||||||
value: u32, // index into serialized entries list
|
value: u32, // index into serialized entries list
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ struct SerializedBucketEntryData {
|
|||||||
#[archive_attr(repr(C), derive(CheckBytes))]
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
struct SerializedBucketData {
|
struct SerializedBucketData {
|
||||||
entries: Vec<SerializedBucketEntryData>,
|
entries: Vec<SerializedBucketEntryData>,
|
||||||
newest_entry: Option<TypedKey>,
|
newest_entry: Option<PublicKey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state_ordering(state: BucketEntryState) -> usize {
|
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
|
/// 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 {
|
pub(super) fn add_entry(&mut self, node_id_key: PublicKey) -> NodeRef {
|
||||||
assert_eq!(node_id.kind, self.kind);
|
log_rtab!("Node added: {}:{}", self.kind, node_id_key);
|
||||||
|
|
||||||
log_rtab!("Node added: {}", node_id);
|
|
||||||
|
|
||||||
// Add new entry
|
// Add new entry
|
||||||
let entry = Arc::new(BucketEntry::new(node_id));
|
let entry = Arc::new(BucketEntry::new(TypedKey::new(self.kind, node_id_key)));
|
||||||
self.entries.insert(node_id.key, entry.clone());
|
self.entries.insert(node_id_key, entry.clone());
|
||||||
|
|
||||||
// This is now the newest bucket entry
|
// 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
|
// Get a node ref to return since this is new
|
||||||
NodeRef::new(self.routing_table.clone(), entry, None)
|
NodeRef::new(self.routing_table.clone(), entry, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an existing entry with a new node_id for this crypto kind
|
/// 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>) {
|
pub(super) fn add_existing_entry(&mut self, node_id_key: PublicKey, entry: Arc<BucketEntry>) {
|
||||||
assert_eq!(node_id.kind, self.kind);
|
log_rtab!("Existing node added: {}:{}", self.kind, node_id_key);
|
||||||
|
|
||||||
log_rtab!("Existing node added: {}", node_id);
|
|
||||||
|
|
||||||
// Add existing entry
|
// Add existing entry
|
||||||
entry.with_mut_inner(|e| e.add_node_id(node_id));
|
entry.with_mut_inner(|e| e.add_node_id(TypedKey::new(self.kind, node_id_key)));
|
||||||
self.entries.insert(node_id.key, entry);
|
self.entries.insert(node_id_key, entry);
|
||||||
|
|
||||||
// This is now the newest bucket 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
|
// 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
|
/// Remove an entry with a node_id for this crypto kind from the bucket
|
||||||
fn remove_entry(&mut self, node_id: &TypedKey) {
|
fn remove_entry(&mut self, node_id_key: &PublicKey) {
|
||||||
log_rtab!("Node removed: {}:{}", self.kind, node_id);
|
log_rtab!("Node removed: {}:{}", self.kind, node_id_key);
|
||||||
|
|
||||||
// Remove the entry
|
// Remove the entry
|
||||||
self.entries.remove(node_id);
|
self.entries.remove(node_id_key);
|
||||||
|
|
||||||
// newest_entry is updated by kick_bucket()
|
// 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()
|
self.entries.get(key).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +141,7 @@ impl Bucket {
|
|||||||
self.entries.iter()
|
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
|
// Get number of entries to attempt to purge from bucket
|
||||||
let bucket_len = self.entries.len();
|
let bucket_len = self.entries.len();
|
||||||
|
|
||||||
@ -155,11 +151,11 @@ impl Bucket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to purge the newest entries that overflow the 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;
|
let mut extra_entries = bucket_len - bucket_depth;
|
||||||
|
|
||||||
// Get the sorted list of entries by their kick order
|
// 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
|
.entries
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v)| (k.clone(), v.clone()))
|
.map(|(k, v)| (k.clone(), v.clone()))
|
||||||
|
@ -7,7 +7,7 @@ impl RoutingTable {
|
|||||||
let inner = self.inner.read();
|
let inner = self.inner.read();
|
||||||
out += "Routing Table Info:\n";
|
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!(
|
out += &format!(
|
||||||
" Self Latency Stats Accounting: {:#?}\n\n",
|
" Self Latency Stats Accounting: {:#?}\n\n",
|
||||||
inner.self_latency_stats_accounting
|
inner.self_latency_stats_accounting
|
||||||
@ -55,13 +55,20 @@ impl RoutingTable {
|
|||||||
short_urls.sort();
|
short_urls.sort();
|
||||||
short_urls.dedup();
|
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 += "TXT Record:\n";
|
||||||
out += &format!(
|
out += &format!(
|
||||||
"{},{},{},{},{}",
|
"{}|{}|{}|{}|",
|
||||||
BOOTSTRAP_TXT_VERSION,
|
BOOTSTRAP_TXT_VERSION,
|
||||||
MIN_CRYPTO_VERSION,
|
valid_envelope_versions,
|
||||||
MAX_CRYPTO_VERSION,
|
node_ids,
|
||||||
self.node_id().encode(),
|
|
||||||
some_hostname.unwrap()
|
some_hostname.unwrap()
|
||||||
);
|
);
|
||||||
for short_url in short_urls {
|
for short_url in short_urls {
|
||||||
|
@ -111,7 +111,15 @@ impl RoutingTableUnlockedInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn node_id(&self, kind: CryptoKind) -> TypedKey {
|
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 {
|
pub fn node_id_secret(&self, kind: CryptoKind) -> SecretKey {
|
||||||
@ -890,6 +898,7 @@ impl RoutingTable {
|
|||||||
|
|
||||||
pub fn find_closest_nodes<'a, T, O>(
|
pub fn find_closest_nodes<'a, T, O>(
|
||||||
&self,
|
&self,
|
||||||
|
node_count: usize,
|
||||||
node_id: TypedKey,
|
node_id: TypedKey,
|
||||||
filters: VecDeque<RoutingTableEntryFilter>,
|
filters: VecDeque<RoutingTableEntryFilter>,
|
||||||
transform: T,
|
transform: T,
|
||||||
@ -899,7 +908,7 @@ impl RoutingTable {
|
|||||||
{
|
{
|
||||||
self.inner
|
self.inner
|
||||||
.read()
|
.read()
|
||||||
.find_closest_nodes(node_id, filters, transform)
|
.find_closest_nodes(node_count, node_id, filters, transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "trace", skip(self), ret)]
|
#[instrument(level = "trace", skip(self), ret)]
|
||||||
@ -940,14 +949,14 @@ impl RoutingTable {
|
|||||||
|
|
||||||
#[instrument(level = "trace", skip(self), ret, err)]
|
#[instrument(level = "trace", skip(self), ret, err)]
|
||||||
pub async fn find_self(&self, node_ref: NodeRef) -> EyreResult<NetworkResult<Vec<NodeRef>>> {
|
pub async fn find_self(&self, node_ref: NodeRef) -> EyreResult<NetworkResult<Vec<NodeRef>>> {
|
||||||
let node_id = self.node_id();
|
let self_node_id = self.node_id();
|
||||||
self.find_node(node_ref, node_id).await
|
self.find_node(node_ref, self_node_id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "trace", skip(self), ret, err)]
|
#[instrument(level = "trace", skip(self), ret, err)]
|
||||||
pub async fn find_target(&self, node_ref: NodeRef) -> EyreResult<NetworkResult<Vec<NodeRef>>> {
|
pub async fn find_target(&self, node_ref: NodeRef) -> EyreResult<NetworkResult<Vec<NodeRef>>> {
|
||||||
let node_id = node_ref.node_id();
|
let target_node_id = node_ref.node_id();
|
||||||
self.find_node(node_ref, node_id).await
|
self.find_node(node_ref, target_node_id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "trace", skip(self))]
|
#[instrument(level = "trace", skip(self))]
|
||||||
@ -1047,12 +1056,12 @@ impl RoutingTable {
|
|||||||
// Go through all entries and find fastest entry that matches filter function
|
// Go through all entries and find fastest entry that matches filter function
|
||||||
let inner = self.inner.read();
|
let inner = self.inner.read();
|
||||||
let inner = &*inner;
|
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
|
// Iterate all known nodes for candidates
|
||||||
inner.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, k, v| {
|
inner.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, entry| {
|
||||||
let v2 = v.clone();
|
let entry2 = entry.clone();
|
||||||
v.with(rti, |rti, e| {
|
entry.with(rti, |rti, e| {
|
||||||
// Ensure we have the node's status
|
// Ensure we have the node's status
|
||||||
if let Some(node_status) = e.node_status(routing_domain) {
|
if let Some(node_status) = e.node_status(routing_domain) {
|
||||||
// Ensure the node will relay
|
// Ensure the node will relay
|
||||||
@ -1060,18 +1069,18 @@ impl RoutingTable {
|
|||||||
// Compare against previous candidate
|
// Compare against previous candidate
|
||||||
if let Some(best_inbound_relay) = best_inbound_relay.as_mut() {
|
if let Some(best_inbound_relay) = best_inbound_relay.as_mut() {
|
||||||
// Less is faster
|
// 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
|
// choose low latency stability for relays
|
||||||
BucketEntryInner::cmp_fastest_reliable(cur_ts, e, best)
|
BucketEntryInner::cmp_fastest_reliable(cur_ts, e, best)
|
||||||
== std::cmp::Ordering::Less
|
== std::cmp::Ordering::Less
|
||||||
});
|
});
|
||||||
// Now apply filter function and see if this node should be included
|
// Now apply filter function and see if this node should be included
|
||||||
if better && relay_node_filter(e) {
|
if better && relay_node_filter(e) {
|
||||||
*best_inbound_relay = (k, v2);
|
*best_inbound_relay = entry2;
|
||||||
}
|
}
|
||||||
} else if relay_node_filter(e) {
|
} else if relay_node_filter(e) {
|
||||||
// Always store the first candidate
|
// Always store the first candidate
|
||||||
best_inbound_relay = Some((k, v2));
|
best_inbound_relay = Some(entry2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1080,6 +1089,6 @@ impl RoutingTable {
|
|||||||
Option::<()>::None
|
Option::<()>::None
|
||||||
});
|
});
|
||||||
// Return the best inbound relay noderef
|
// 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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
use weak_table::PtrWeakHashSet;
|
||||||
|
|
||||||
const RECENT_PEERS_TABLE_SIZE: usize = 64;
|
const RECENT_PEERS_TABLE_SIZE: usize = 64;
|
||||||
|
|
||||||
@ -15,8 +16,8 @@ pub struct RoutingTableInner {
|
|||||||
pub(super) unlocked_inner: Arc<RoutingTableUnlockedInner>,
|
pub(super) unlocked_inner: Arc<RoutingTableUnlockedInner>,
|
||||||
/// Routing table buckets that hold references to entries, per crypto kind
|
/// Routing table buckets that hold references to entries, per crypto kind
|
||||||
pub(super) buckets: BTreeMap<CryptoKind, Vec<Bucket>>,
|
pub(super) buckets: BTreeMap<CryptoKind, Vec<Bucket>>,
|
||||||
/// A fast counter for the number of entries in the table, total
|
/// A weak set of all the entries we have in the buckets for faster iteration
|
||||||
pub(super) bucket_entry_count: usize,
|
pub(super) all_entries: PtrWeakHashSet<Weak<BucketEntry>>,
|
||||||
/// The public internet routing domain
|
/// The public internet routing domain
|
||||||
pub(super) public_internet_routing_domain: PublicInternetRoutingDomainDetail,
|
pub(super) public_internet_routing_domain: PublicInternetRoutingDomainDetail,
|
||||||
/// The dial info we use on the local network
|
/// The dial info we use on the local network
|
||||||
@ -40,7 +41,7 @@ impl RoutingTableInner {
|
|||||||
buckets: BTreeMap::new(),
|
buckets: BTreeMap::new(),
|
||||||
public_internet_routing_domain: PublicInternetRoutingDomainDetail::default(),
|
public_internet_routing_domain: PublicInternetRoutingDomainDetail::default(),
|
||||||
local_network_routing_domain: LocalNetworkRoutingDomainDetail::default(),
|
local_network_routing_domain: LocalNetworkRoutingDomainDetail::default(),
|
||||||
bucket_entry_count: 0,
|
all_entries: PtrWeakHashSet::new(),
|
||||||
self_latency_stats_accounting: LatencyStatsAccounting::new(),
|
self_latency_stats_accounting: LatencyStatsAccounting::new(),
|
||||||
self_transfer_stats_accounting: TransferStatsAccounting::new(),
|
self_transfer_stats_accounting: TransferStatsAccounting::new(),
|
||||||
self_transfer_stats: TransferStatsDownUp::default(),
|
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 {
|
pub fn transfer_stats_accounting(&mut self) -> &mut TransferStatsAccounting {
|
||||||
&mut self.self_transfer_stats_accounting
|
&mut self.self_transfer_stats_accounting
|
||||||
}
|
}
|
||||||
@ -209,7 +214,7 @@ impl RoutingTableInner {
|
|||||||
|
|
||||||
pub fn reset_all_updated_since_last_network_change(&mut self) {
|
pub fn reset_all_updated_since_last_network_change(&mut self) {
|
||||||
let cur_ts = get_aligned_timestamp();
|
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| {
|
v.with_mut(rti, |_rti, e| {
|
||||||
e.set_updated_since_last_network_change(false)
|
e.set_updated_since_last_network_change(false)
|
||||||
});
|
});
|
||||||
@ -310,7 +315,7 @@ impl RoutingTableInner {
|
|||||||
for ck in VALID_CRYPTO_KINDS {
|
for ck in VALID_CRYPTO_KINDS {
|
||||||
let ckbuckets = Vec::with_capacity(PUBLIC_KEY_LENGTH * 8);
|
let ckbuckets = Vec::with_capacity(PUBLIC_KEY_LENGTH * 8);
|
||||||
for _ in 0..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);
|
ckbuckets.push(bucket);
|
||||||
}
|
}
|
||||||
self.buckets.insert(ck, ckbuckets);
|
self.buckets.insert(ck, ckbuckets);
|
||||||
@ -345,14 +350,18 @@ impl RoutingTableInner {
|
|||||||
pub fn purge_buckets(&mut self) {
|
pub fn purge_buckets(&mut self) {
|
||||||
log_rtab!(
|
log_rtab!(
|
||||||
"Starting routing table buckets purge. Table currently has {} nodes",
|
"Starting routing table buckets purge. Table currently has {} nodes",
|
||||||
self.bucket_entry_count
|
self.bucket_entry_count()
|
||||||
);
|
);
|
||||||
for bucket in &mut self.buckets {
|
for ck in VALID_CRYPTO_KINDS {
|
||||||
bucket.kick(0);
|
for bucket in &mut self.buckets[&ck] {
|
||||||
|
bucket.kick(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
self.all_entries.remove_expired();
|
||||||
|
|
||||||
log_rtab!(debug
|
log_rtab!(debug
|
||||||
"Routing table buckets purge complete. Routing table now has {} nodes",
|
"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) {
|
pub fn purge_last_connections(&mut self) {
|
||||||
log_rtab!(
|
log_rtab!(
|
||||||
"Starting routing table last_connections purge. Table currently has {} nodes",
|
"Starting routing table last_connections purge. Table currently has {} nodes",
|
||||||
self.bucket_entry_count
|
self.bucket_entry_count()
|
||||||
);
|
);
|
||||||
for bucket in &self.buckets {
|
for ck in VALID_CRYPTO_KINDS {
|
||||||
for entry in bucket.entries() {
|
for bucket in &self.buckets[&ck] {
|
||||||
entry.1.with_mut_inner(|e| {
|
for entry in bucket.entries() {
|
||||||
e.clear_last_connections();
|
entry.1.with_mut_inner(|e| {
|
||||||
});
|
e.clear_last_connections();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.all_entries.remove_expired();
|
||||||
|
|
||||||
log_rtab!(debug
|
log_rtab!(debug
|
||||||
"Routing table last_connections purge complete. Routing table now has {} nodes",
|
"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
|
/// Attempt to settle buckets and remove entries down to the desired number
|
||||||
/// which may not be possible due extant NodeRefs
|
/// which may not be possible due extant NodeRefs
|
||||||
pub fn kick_bucket(&mut self, idx: usize) {
|
pub fn kick_bucket(&mut self, kind: CryptoKind, idx: usize) {
|
||||||
let bucket = &mut self.buckets[idx];
|
let bucket = &mut self.buckets[&kind][idx];
|
||||||
let bucket_depth = Self::bucket_depth(idx);
|
let bucket_depth = Self::bucket_depth(idx);
|
||||||
|
|
||||||
if let Some(dead_node_ids) = bucket.kick(bucket_depth) {
|
if let Some(dead_node_ids) = bucket.kick(bucket_depth) {
|
||||||
// Remove counts
|
// Remove expired entries
|
||||||
self.bucket_entry_count -= dead_node_ids.len();
|
self.all_entries.remove_expired();
|
||||||
log_rtab!(debug "Routing table now has {} nodes", self.bucket_entry_count);
|
|
||||||
|
log_rtab!(debug "Bucket {}:{} kicked Routing table now has {} nodes", kind, idx, self.bucket_entry_count());
|
||||||
|
|
||||||
// Now purge the routing table inner vectors
|
// Now purge the routing table inner vectors
|
||||||
//let filter = |k: &DHTKey| dead_node_ids.contains(k);
|
//let filter = |k: &DHTKey| dead_node_ids.contains(k);
|
||||||
@ -403,7 +416,7 @@ impl RoutingTableInner {
|
|||||||
) -> usize {
|
) -> usize {
|
||||||
let mut count = 0usize;
|
let mut count = 0usize;
|
||||||
let cur_ts = get_aligned_timestamp();
|
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))
|
if e.with(rti, |rti, e| e.best_routing_domain(rti, routing_domain_set))
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
@ -414,54 +427,36 @@ impl RoutingTableInner {
|
|||||||
count
|
count
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_entries<
|
pub fn with_entries<T, F: FnMut(&RoutingTableInner, Arc<BucketEntry>) -> Option<T>>(
|
||||||
T,
|
|
||||||
F: FnMut(&RoutingTableInner, TypedKey, Arc<BucketEntry>) -> Option<T>,
|
|
||||||
>(
|
|
||||||
&self,
|
&self,
|
||||||
cur_ts: Timestamp,
|
cur_ts: Timestamp,
|
||||||
min_state: BucketEntryState,
|
min_state: BucketEntryState,
|
||||||
mut f: F,
|
mut f: F,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
let mut entryvec = Vec::with_capacity(self.bucket_entry_count);
|
for entry in self.all_entries {
|
||||||
for bucket in &self.buckets {
|
if entry.with(self, |_rti, e| e.state(cur_ts) >= min_state) {
|
||||||
for entry in bucket.entries() {
|
if let Some(out) = f(self, entry) {
|
||||||
if entry.1.with(self, |_rti, e| e.state(cur_ts) >= min_state) {
|
return Some(out);
|
||||||
entryvec.push((*entry.0, entry.1.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for entry in entryvec {
|
|
||||||
if let Some(out) = f(self, entry.0, entry.1) {
|
|
||||||
return Some(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_entries_mut<
|
pub fn with_entries_mut<T, F: FnMut(&mut RoutingTableInner, Arc<BucketEntry>) -> Option<T>>(
|
||||||
T,
|
|
||||||
F: FnMut(&mut RoutingTableInner, TypedKey, Arc<BucketEntry>) -> Option<T>,
|
|
||||||
>(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
cur_ts: Timestamp,
|
cur_ts: Timestamp,
|
||||||
min_state: BucketEntryState,
|
min_state: BucketEntryState,
|
||||||
mut f: F,
|
mut f: F,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
let mut entryvec = Vec::with_capacity(self.bucket_entry_count);
|
for entry in self.all_entries {
|
||||||
for bucket in &self.buckets {
|
if entry.with(self, |_rti, e| e.state(cur_ts) >= min_state) {
|
||||||
for entry in bucket.entries() {
|
if let Some(out) = f(self, entry) {
|
||||||
if entry.1.with(self, |_rti, e| e.state(cur_ts) >= min_state) {
|
return Some(out);
|
||||||
entryvec.push((*entry.0, entry.1.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for entry in entryvec {
|
|
||||||
if let Some(out) = f(self, entry.0, entry.1) {
|
|
||||||
return Some(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -728,18 +723,16 @@ impl RoutingTableInner {
|
|||||||
let mut dead_entry_count: usize = 0;
|
let mut dead_entry_count: usize = 0;
|
||||||
|
|
||||||
let cur_ts = get_aligned_timestamp();
|
let cur_ts = get_aligned_timestamp();
|
||||||
for bucket in &self.buckets {
|
for entry in self.all_entries {
|
||||||
for (_, v) in bucket.entries() {
|
match entry.with(self, |_rti, e| e.state(cur_ts)) {
|
||||||
match v.with(self, |_rti, e| e.state(cur_ts)) {
|
BucketEntryState::Reliable => {
|
||||||
BucketEntryState::Reliable => {
|
reliable_entry_count += 1;
|
||||||
reliable_entry_count += 1;
|
}
|
||||||
}
|
BucketEntryState::Unreliable => {
|
||||||
BucketEntryState::Unreliable => {
|
unreliable_entry_count += 1;
|
||||||
unreliable_entry_count += 1;
|
}
|
||||||
}
|
BucketEntryState::Dead => {
|
||||||
BucketEntryState::Dead => {
|
dead_entry_count += 1;
|
||||||
dead_entry_count += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -798,7 +791,7 @@ impl RoutingTableInner {
|
|||||||
self.find_fastest_nodes(
|
self.find_fastest_nodes(
|
||||||
node_count,
|
node_count,
|
||||||
filters,
|
filters,
|
||||||
|_rti: &RoutingTableInner, k: TypedKey, v: Option<Arc<BucketEntry>>| {
|
|_rti: &RoutingTableInner, v: Option<Arc<BucketEntry>>| {
|
||||||
NodeRef::new(outer_self.clone(), v.unwrap().clone(), None)
|
NodeRef::new(outer_self.clone(), v.unwrap().clone(), None)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -846,30 +839,30 @@ impl RoutingTableInner {
|
|||||||
&'b Option<Arc<BucketEntry>>,
|
&'b Option<Arc<BucketEntry>>,
|
||||||
&'b Option<Arc<BucketEntry>>,
|
&'b Option<Arc<BucketEntry>>,
|
||||||
) -> core::cmp::Ordering,
|
) -> 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
|
// collect all the nodes for sorting
|
||||||
let mut nodes =
|
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)
|
// add our own node (only one of there with the None entry)
|
||||||
let mut filtered = false;
|
let mut filtered = false;
|
||||||
for filter in &mut filters {
|
for filter in &mut filters {
|
||||||
if !filter(self, self.unlocked_inner.node_id, None) {
|
if !filter(self, None) {
|
||||||
filtered = true;
|
filtered = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !filtered {
|
if !filtered {
|
||||||
nodes.push((self.unlocked_inner.node_id, None));
|
nodes.push(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add all nodes from buckets
|
// add all nodes that match filter
|
||||||
self.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, k, v| {
|
self.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, v| {
|
||||||
// Apply filter
|
// Apply filter
|
||||||
for filter in &mut filters {
|
for filter in &mut filters {
|
||||||
if filter(rti, k, Some(v.clone())) {
|
if filter(rti, Some(v.clone())) {
|
||||||
nodes.push((k, Some(v.clone())));
|
nodes.push(Some(v.clone()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -883,7 +876,7 @@ impl RoutingTableInner {
|
|||||||
let cnt = usize::min(node_count, nodes.len());
|
let cnt = usize::min(node_count, nodes.len());
|
||||||
let mut out = Vec::<O>::with_capacity(cnt);
|
let mut out = Vec::<O>::with_capacity(cnt);
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
let val = transform(self, node.0, node.1);
|
let val = transform(self, node);
|
||||||
out.push(val);
|
out.push(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -921,12 +914,19 @@ impl RoutingTableInner {
|
|||||||
|
|
||||||
// Fastest sort
|
// Fastest sort
|
||||||
let sort = |rti: &RoutingTableInner,
|
let sort = |rti: &RoutingTableInner,
|
||||||
(a_key, a_entry): &(TypedKey, Option<Arc<BucketEntry>>),
|
a_entry: &Option<Arc<BucketEntry>>,
|
||||||
(b_key, b_entry): &(TypedKey, Option<Arc<BucketEntry>>)| {
|
b_entry: &Option<Arc<BucketEntry>>| {
|
||||||
// same nodes are always the same
|
// 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;
|
return core::cmp::Ordering::Equal;
|
||||||
}
|
}
|
||||||
|
|
||||||
// our own node always comes last (should not happen, here for completeness)
|
// our own node always comes last (should not happen, here for completeness)
|
||||||
if a_entry.is_none() {
|
if a_entry.is_none() {
|
||||||
return core::cmp::Ordering::Greater;
|
return core::cmp::Ordering::Greater;
|
||||||
@ -977,26 +977,28 @@ impl RoutingTableInner {
|
|||||||
|
|
||||||
pub fn find_closest_nodes<T, O>(
|
pub fn find_closest_nodes<T, O>(
|
||||||
&self,
|
&self,
|
||||||
|
node_count: usize,
|
||||||
node_id: TypedKey,
|
node_id: TypedKey,
|
||||||
filters: VecDeque<RoutingTableEntryFilter>,
|
filters: VecDeque<RoutingTableEntryFilter>,
|
||||||
transform: T,
|
transform: T,
|
||||||
) -> Vec<O>
|
) -> Vec<O>
|
||||||
where
|
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 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
|
// closest sort
|
||||||
let sort = |rti: &RoutingTableInner,
|
let sort = |rti: &RoutingTableInner,
|
||||||
(a_key, a_entry): &(TypedKey, Option<Arc<BucketEntry>>),
|
a_entry: &Option<Arc<BucketEntry>>,
|
||||||
(b_key, b_entry): &(TypedKey, Option<Arc<BucketEntry>>)| {
|
b_entry: &Option<Arc<BucketEntry>>| {
|
||||||
// same nodes are always the same
|
// 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;
|
return core::cmp::Ordering::Equal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,23 +3,122 @@ use super::*;
|
|||||||
use futures_util::stream::{FuturesUnordered, StreamExt};
|
use futures_util::stream::{FuturesUnordered, StreamExt};
|
||||||
use stop_token::future::FutureExt as StopFutureExt;
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct BootstrapRecord {
|
pub struct BootstrapRecord {
|
||||||
min_version: u8,
|
node_ids: TypedKeySet,
|
||||||
max_version: u8,
|
envelope_support: Vec<u8>,
|
||||||
dial_info_details: Vec<DialInfoDetail>,
|
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 {
|
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
|
// Bootstrap lookup process
|
||||||
#[instrument(level = "trace", skip(self), ret, err)]
|
#[instrument(level = "trace", skip(self), ret, err)]
|
||||||
pub(crate) async fn resolve_bootstrap(
|
pub(crate) async fn resolve_bootstrap(
|
||||||
&self,
|
&self,
|
||||||
bootstrap: Vec<String>,
|
bootstrap: Vec<String>,
|
||||||
) -> EyreResult<BootstrapRecordMap> {
|
) -> EyreResult<Vec<BootstrapRecord>> {
|
||||||
// Resolve from bootstrap root to bootstrap hostnames
|
// Resolve from bootstrap root to bootstrap hostnames
|
||||||
let mut bsnames = Vec::<String>::new();
|
let mut bsnames = Vec::<String>::new();
|
||||||
for bh in bootstrap {
|
for bh in bootstrap {
|
||||||
@ -58,22 +157,14 @@ impl RoutingTable {
|
|||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
};
|
};
|
||||||
// for each record resolve into key/bootstraprecord pairs
|
// 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 {
|
for bsnirecord in bsnirecords {
|
||||||
// Bootstrap TXT Record Format Version 0:
|
// All formats split on '|' character
|
||||||
// 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
|
|
||||||
let records: Vec<String> = bsnirecord
|
let records: Vec<String> = bsnirecord
|
||||||
.trim()
|
.trim()
|
||||||
.split(',')
|
.split('|')
|
||||||
.map(|x| x.trim().to_owned())
|
.map(|x| x.trim().to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
if records.len() < 6 {
|
|
||||||
warn!("invalid number of fields in bootstrap txt record");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bootstrap TXT record version
|
// Bootstrap TXT record version
|
||||||
let txt_version: u8 = match records[0].parse::<u8>() {
|
let txt_version: u8 = match records[0].parse::<u8>() {
|
||||||
@ -86,81 +177,30 @@ impl RoutingTable {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if txt_version != BOOTSTRAP_TXT_VERSION {
|
let bootstrap_record = match txt_version {
|
||||||
warn!("unsupported bootstrap txt record version");
|
BOOTSTRAP_TXT_VERSION_0 => {
|
||||||
continue;
|
match self.process_bootstrap_records_v0(records).await {
|
||||||
}
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
// Min/Max wire protocol version
|
"couldn't process v0 bootstrap records from {}: {}",
|
||||||
let min_version: u8 = match records[1].parse::<u8>() {
|
bsname, e
|
||||||
Ok(v) => v,
|
);
|
||||||
Err(e) => {
|
continue;
|
||||||
warn!(
|
}
|
||||||
"invalid min_version specified in bootstrap node txt record: {}",
|
Ok(Some(v)) => v,
|
||||||
e
|
Ok(None) => {
|
||||||
);
|
// skipping
|
||||||
continue;
|
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;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
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)
|
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 {
|
while let Some(bootstrap_records) = unord.next().await {
|
||||||
if let Some(bootstrap_records) = bootstrap_records {
|
let Some(bootstrap_records) = bootstrap_records else {
|
||||||
for (bskey, mut bsrec) in bootstrap_records {
|
continue;
|
||||||
let rec = bsmap.entry(bskey).or_insert_with(|| BootstrapRecord {
|
};
|
||||||
min_version: bsrec.min_version,
|
for mut bsrec in bootstrap_records {
|
||||||
max_version: bsrec.max_version,
|
let mut mbi = 0;
|
||||||
dial_info_details: Vec::new(),
|
while mbi < merged_bootstrap_records.len() {
|
||||||
});
|
let mbr = &mut merged_bootstrap_records[mbi];
|
||||||
rec.dial_info_details.append(&mut bsrec.dial_info_details);
|
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
|
// '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
|
// Got peer info, let's add it to the routing table
|
||||||
for pi in peer_info {
|
for pi in peer_info {
|
||||||
let k = pi.node_id.key;
|
|
||||||
// Register the node
|
// Register the node
|
||||||
if let Some(nr) = self.register_node_with_signed_node_info(
|
if let Some(nr) =
|
||||||
RoutingDomain::PublicInternet,
|
self.register_node_with_peer_info(RoutingDomain::PublicInternet, pi, false)
|
||||||
k,
|
{
|
||||||
pi.signed_node_info,
|
|
||||||
false,
|
|
||||||
) {
|
|
||||||
// Add this our futures to process in parallel
|
// Add this our futures to process in parallel
|
||||||
let routing_table = self.clone();
|
let routing_table = self.clone();
|
||||||
unord.push(
|
unord.push(
|
||||||
@ -230,90 +280,62 @@ impl RoutingTable {
|
|||||||
|
|
||||||
#[instrument(level = "trace", skip(self), err)]
|
#[instrument(level = "trace", skip(self), err)]
|
||||||
pub(crate) async fn bootstrap_task_routine(self, stop_token: StopToken) -> EyreResult<()> {
|
pub(crate) async fn bootstrap_task_routine(self, stop_token: StopToken) -> EyreResult<()> {
|
||||||
let (bootstrap, bootstrap_nodes) = self.with_config(|c| {
|
let bootstrap = self
|
||||||
(
|
.unlocked_inner
|
||||||
c.network.bootstrap.clone(),
|
.with_config(|c| c.network.routing_table.bootstrap.clone());
|
||||||
c.network.bootstrap_nodes.clone(),
|
|
||||||
)
|
// Don't bother if bootstraps aren't configured
|
||||||
});
|
if bootstrap.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
log_rtab!(debug "--- bootstrap_task");
|
log_rtab!(debug "--- bootstrap_task");
|
||||||
|
|
||||||
// See if we are specifying a direct dialinfo for bootstrap, if so use the direct mechanism
|
// 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();
|
||||||
let mut bootstrap_dialinfos = Vec::<DialInfo>::new();
|
for b in &bootstrap {
|
||||||
for b in &bootstrap {
|
if let Ok(bootstrap_di_vec) = DialInfo::try_vec_from_url(&b) {
|
||||||
if let Ok(bootstrap_di_vec) = DialInfo::try_vec_from_url(&b) {
|
for bootstrap_di in bootstrap_di_vec {
|
||||||
for bootstrap_di in bootstrap_di_vec {
|
bootstrap_dialinfos.push(bootstrap_di);
|
||||||
bootstrap_dialinfos.push(bootstrap_di);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if bootstrap_dialinfos.len() > 0 {
|
}
|
||||||
return self
|
if bootstrap_dialinfos.len() > 0 {
|
||||||
.direct_bootstrap_task_routine(stop_token, bootstrap_dialinfos)
|
return self
|
||||||
.await;
|
.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)
|
// If not direct, resolve bootstrap servers and recurse their TXT entries
|
||||||
let bsmap: BootstrapRecordMap = if !bootstrap_nodes.is_empty() {
|
let bsrecs = self.resolve_bootstrap(bootstrap).await?;
|
||||||
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
|
|
||||||
|
|
||||||
// Run all bootstrap operations concurrently
|
// Run all bootstrap operations concurrently
|
||||||
let mut unord = FuturesUnordered::new();
|
let mut unord = FuturesUnordered::new();
|
||||||
for (k, mut v) in bsmap {
|
for bsrec in bsrecs {
|
||||||
// Sort dial info so we get the preferred order correct
|
log_rtab!(
|
||||||
v.dial_info_details.sort();
|
"--- 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)
|
// Make unsigned SignedNodeInfo
|
||||||
if let Some(nr) = self.register_node_with_signed_node_info(
|
let sni = SignedNodeInfo::Direct(SignedDirectNodeInfo::with_no_signature(NodeInfo {
|
||||||
RoutingDomain::PublicInternet,
|
network_class: NetworkClass::InboundCapable, // Bootstraps are always inbound capable
|
||||||
k,
|
outbound_protocols: ProtocolTypeSet::only(ProtocolType::UDP), // Bootstraps do not participate in relaying and will not make outbound requests, but will have UDP enabled
|
||||||
SignedNodeInfo::Direct(SignedDirectNodeInfo::with_no_signature(NodeInfo {
|
address_types: AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable
|
||||||
network_class: NetworkClass::InboundCapable, // Bootstraps are always inbound capable
|
envelope_support: bsrec.envelope_support, // Envelope support is as specified in the bootstrap list
|
||||||
outbound_protocols: ProtocolTypeSet::only(ProtocolType::UDP), // Bootstraps do not participate in relaying and will not make outbound requests, but will have UDP enabled
|
crypto_support, // Crypto support is derived from list of node ids
|
||||||
address_types: AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable
|
dial_info_detail_list: bsrec.dial_info_details, // Dial info is as specified in the bootstrap list
|
||||||
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
|
let pi = PeerInfo::new(bsrec.node_ids, sni);
|
||||||
})),
|
|
||||||
true,
|
if let Some(nr) =
|
||||||
) {
|
self.register_node_with_peer_info(RoutingDomain::PublicInternet, pi, true)
|
||||||
|
{
|
||||||
// Add this our futures to process in parallel
|
// Add this our futures to process in parallel
|
||||||
let routing_table = self.clone();
|
let routing_table = self.clone();
|
||||||
unord.push(
|
unord.push(
|
||||||
|
@ -10,12 +10,13 @@ impl RoutingTable {
|
|||||||
_last_ts: Timestamp,
|
_last_ts: Timestamp,
|
||||||
cur_ts: Timestamp,
|
cur_ts: Timestamp,
|
||||||
) -> EyreResult<()> {
|
) -> EyreResult<()> {
|
||||||
let kick_queue: Vec<usize> = core::mem::take(&mut *self.unlocked_inner.kick_queue.lock())
|
let kick_queue: Vec<(CryptoKind, usize)> =
|
||||||
.into_iter()
|
core::mem::take(&mut *self.unlocked_inner.kick_queue.lock())
|
||||||
.collect();
|
.into_iter()
|
||||||
|
.collect();
|
||||||
let mut inner = self.inner.write();
|
let mut inner = self.inner.write();
|
||||||
for idx in kick_queue {
|
for (ck, idx) in kick_queue {
|
||||||
inner.kick_bucket(idx)
|
inner.kick_bucket(ck, idx)
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,14 @@ impl RPCProcessor {
|
|||||||
) as RoutingTableEntryFilter;
|
) as RoutingTableEntryFilter;
|
||||||
let filters = VecDeque::from([filter]);
|
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(
|
let closest_nodes = routing_table.find_closest_nodes(
|
||||||
|
node_count,
|
||||||
find_node_q.node_id,
|
find_node_q.node_id,
|
||||||
filters,
|
filters,
|
||||||
// transform
|
// transform
|
||||||
|
@ -195,7 +195,6 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
|
|||||||
"network.node_id" => Ok(Box::new(Option::<TypedKey>::None)),
|
"network.node_id" => Ok(Box::new(Option::<TypedKey>::None)),
|
||||||
"network.node_id_secret" => Ok(Box::new(Option::<SecretKey>::None)),
|
"network.node_id_secret" => Ok(Box::new(Option::<SecretKey>::None)),
|
||||||
"network.bootstrap" => Ok(Box::new(Vec::<String>::new())),
|
"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_over_attached" => Ok(Box::new(64u32)),
|
||||||
"network.routing_table.limit_fully_attached" => Ok(Box::new(32u32)),
|
"network.routing_table.limit_fully_attached" => Ok(Box::new(32u32)),
|
||||||
"network.routing_table.limit_attached_strong" => Ok(Box::new(16u32)),
|
"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.is_none());
|
||||||
assert!(inner.network.node_id_secret.is_none());
|
assert!(inner.network.node_id_secret.is_none());
|
||||||
assert_eq!(inner.network.bootstrap, Vec::<String>::new());
|
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.concurrency, 2u32);
|
||||||
assert_eq!(inner.network.rpc.queue_size, 1024u32);
|
assert_eq!(inner.network.rpc.queue_size, 1024u32);
|
||||||
assert_eq!(inner.network.rpc.timeout_ms, 10_000u32);
|
assert_eq!(inner.network.rpc.timeout_ms, 10_000u32);
|
||||||
|
@ -352,7 +352,6 @@ pub struct VeilidConfigNodeId {
|
|||||||
pub struct VeilidConfigRoutingTable {
|
pub struct VeilidConfigRoutingTable {
|
||||||
pub node_ids: BTreeMap<CryptoKind, VeilidConfigNodeId>,
|
pub node_ids: BTreeMap<CryptoKind, VeilidConfigNodeId>,
|
||||||
pub bootstrap: Vec<String>,
|
pub bootstrap: Vec<String>,
|
||||||
pub bootstrap_nodes: Vec<String>,
|
|
||||||
pub limit_over_attached: u32,
|
pub limit_over_attached: u32,
|
||||||
pub limit_fully_attached: u32,
|
pub limit_fully_attached: u32,
|
||||||
pub limit_attached_strong: 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_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);
|
||||||
get_config!(inner.network.routing_table.bootstrap_nodes);
|
|
||||||
get_config!(inner.network.routing_table.limit_over_attached);
|
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_fully_attached);
|
||||||
get_config!(inner.network.routing_table.limit_attached_strong);
|
get_config!(inner.network.routing_table.limit_attached_strong);
|
||||||
|
@ -51,7 +51,6 @@ Future<VeilidConfig> getDefaultVeilidConfig(String programName) async {
|
|||||||
bootstrap: kIsWeb
|
bootstrap: kIsWeb
|
||||||
? ["ws://bootstrap.dev.veilid.net:5150/ws"]
|
? ["ws://bootstrap.dev.veilid.net:5150/ws"]
|
||||||
: ["bootstrap.dev.veilid.net"],
|
: ["bootstrap.dev.veilid.net"],
|
||||||
bootstrapNodes: [],
|
|
||||||
routingTable: VeilidConfigRoutingTable(
|
routingTable: VeilidConfigRoutingTable(
|
||||||
limitOverAttached: 64,
|
limitOverAttached: 64,
|
||||||
limitFullyAttached: 32,
|
limitFullyAttached: 32,
|
||||||
|
@ -744,7 +744,6 @@ class VeilidConfigNetwork {
|
|||||||
String? nodeId;
|
String? nodeId;
|
||||||
String? nodeIdSecret;
|
String? nodeIdSecret;
|
||||||
List<String> bootstrap;
|
List<String> bootstrap;
|
||||||
List<String> bootstrapNodes;
|
|
||||||
VeilidConfigRoutingTable routingTable;
|
VeilidConfigRoutingTable routingTable;
|
||||||
VeilidConfigRPC rpc;
|
VeilidConfigRPC rpc;
|
||||||
VeilidConfigDHT dht;
|
VeilidConfigDHT dht;
|
||||||
@ -768,7 +767,6 @@ class VeilidConfigNetwork {
|
|||||||
required this.nodeId,
|
required this.nodeId,
|
||||||
required this.nodeIdSecret,
|
required this.nodeIdSecret,
|
||||||
required this.bootstrap,
|
required this.bootstrap,
|
||||||
required this.bootstrapNodes,
|
|
||||||
required this.routingTable,
|
required this.routingTable,
|
||||||
required this.rpc,
|
required this.rpc,
|
||||||
required this.dht,
|
required this.dht,
|
||||||
@ -794,7 +792,6 @@ class VeilidConfigNetwork {
|
|||||||
'node_id': nodeId,
|
'node_id': nodeId,
|
||||||
'node_id_secret': nodeIdSecret,
|
'node_id_secret': nodeIdSecret,
|
||||||
'bootstrap': bootstrap,
|
'bootstrap': bootstrap,
|
||||||
'bootstrap_nodes': bootstrapNodes,
|
|
||||||
'routing_table': routingTable.json,
|
'routing_table': routingTable.json,
|
||||||
'rpc': rpc.json,
|
'rpc': rpc.json,
|
||||||
'dht': dht.json,
|
'dht': dht.json,
|
||||||
@ -823,7 +820,6 @@ class VeilidConfigNetwork {
|
|||||||
nodeId = json['node_id'],
|
nodeId = json['node_id'],
|
||||||
nodeIdSecret = json['node_id_secret'],
|
nodeIdSecret = json['node_id_secret'],
|
||||||
bootstrap = json['bootstrap'],
|
bootstrap = json['bootstrap'],
|
||||||
bootstrapNodes = json['bootstrap_nodes'],
|
|
||||||
routingTable = VeilidConfigRoutingTable.fromJson(json['routing_table']),
|
routingTable = VeilidConfigRoutingTable.fromJson(json['routing_table']),
|
||||||
rpc = VeilidConfigRPC.fromJson(json['rpc']),
|
rpc = VeilidConfigRPC.fromJson(json['rpc']),
|
||||||
dht = VeilidConfigDHT.fromJson(json['dht']),
|
dht = VeilidConfigDHT.fromJson(json['dht']),
|
||||||
|
@ -122,14 +122,6 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap
|
|||||||
.value_name("BOOTSTRAP_LIST")
|
.value_name("BOOTSTRAP_LIST")
|
||||||
.help("Specify a list of bootstrap hostnames to use")
|
.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(
|
||||||
Arg::new("panic")
|
Arg::new("panic")
|
||||||
.long("panic")
|
.long("panic")
|
||||||
@ -280,28 +272,6 @@ pub fn process_command_line() -> EyreResult<(Settings, ArgMatches)> {
|
|||||||
settingsrw.core.network.bootstrap = bootstrap_list;
|
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")]
|
#[cfg(feature = "rt-tokio")]
|
||||||
if matches.occurrences_of("console") != 0 {
|
if matches.occurrences_of("console") != 0 {
|
||||||
settingsrw.logging.console.enabled = true;
|
settingsrw.logging.console.enabled = true;
|
||||||
|
@ -67,7 +67,6 @@ core:
|
|||||||
node_id: null
|
node_id: null
|
||||||
node_id_secret: null
|
node_id_secret: null
|
||||||
bootstrap: ['bootstrap.dev.veilid.net']
|
bootstrap: ['bootstrap.dev.veilid.net']
|
||||||
bootstrap_nodes: []
|
|
||||||
routing_table:
|
routing_table:
|
||||||
limit_over_attached: 64
|
limit_over_attached: 64
|
||||||
limit_fully_attached: 32
|
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)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct NamedSocketAddrs {
|
pub struct NamedSocketAddrs {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -598,7 +537,6 @@ pub struct Network {
|
|||||||
pub node_id: Option<veilid_core::TypedKey>,
|
pub node_id: Option<veilid_core::TypedKey>,
|
||||||
pub node_id_secret: Option<veilid_core::SecretKey>,
|
pub node_id_secret: Option<veilid_core::SecretKey>,
|
||||||
pub bootstrap: Vec<String>,
|
pub bootstrap: Vec<String>,
|
||||||
pub bootstrap_nodes: Vec<ParsedNodeDialInfo>,
|
|
||||||
pub routing_table: RoutingTable,
|
pub routing_table: RoutingTable,
|
||||||
pub rpc: Rpc,
|
pub rpc: Rpc,
|
||||||
pub dht: Dht,
|
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, value);
|
||||||
set_config_value!(inner.core.network.node_id_secret, 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, 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_over_attached, value);
|
||||||
set_config_value!(inner.core.network.routing_table.limit_fully_attached, value);
|
set_config_value!(inner.core.network.routing_table.limit_fully_attached, value);
|
||||||
set_config_value!(
|
set_config_value!(
|
||||||
@ -1122,16 +1059,6 @@ impl Settings {
|
|||||||
"network.node_id" => Ok(Box::new(inner.core.network.node_id)),
|
"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.node_id_secret" => Ok(Box::new(inner.core.network.node_id_secret)),
|
||||||
"network.bootstrap" => Ok(Box::new(inner.core.network.bootstrap.clone())),
|
"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(
|
"network.routing_table.limit_over_attached" => Ok(Box::new(
|
||||||
inner.core.network.routing_table.limit_over_attached,
|
inner.core.network.routing_table.limit_over_attached,
|
||||||
)),
|
)),
|
||||||
@ -1495,7 +1422,6 @@ mod tests {
|
|||||||
s.core.network.bootstrap,
|
s.core.network.bootstrap,
|
||||||
vec!["bootstrap.dev.veilid.net".to_owned()]
|
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.concurrency, 0);
|
||||||
assert_eq!(s.core.network.rpc.queue_size, 1024);
|
assert_eq!(s.core.network.rpc.queue_size, 1024);
|
||||||
|
Loading…
Reference in New Issue
Block a user