receipt rework and discovery rework

This commit is contained in:
John Smith
2022-05-28 10:07:57 -04:00
parent d80a81e460
commit b6e568f664
23 changed files with 817 additions and 431 deletions

View File

@@ -143,6 +143,10 @@ impl BucketEntry {
return;
}
}
self.min_max_version = Some((
signed_node_info.node_info.min_version,
signed_node_info.node_info.max_version,
));
self.opt_signed_node_info = Some(signed_node_info);
}
pub fn update_local_node_info(&mut self, local_node_info: LocalNodeInfo) {

View File

@@ -30,18 +30,41 @@ impl RoutingTable {
if gdis.is_empty() {
out += "No TXT Record\n";
} else {
out += "TXT Record:\n";
out += &self.node_id().encode();
let mut urls = Vec::new();
let mut short_urls = Vec::new();
let mut some_hostname = Option::<String>::None;
for gdi in gdis {
urls.push(gdi.dial_info.to_url().await);
}
urls.sort();
urls.dedup();
let (short_url, hostname) = gdi.dial_info.to_short().await;
if let Some(h) = &some_hostname {
if h != &hostname {
return format!(
"Inconsistent hostnames for dial info: {} vs {}",
some_hostname.unwrap(),
hostname
);
}
} else {
some_hostname = Some(hostname);
}
for url in urls {
out += &format!(",{}", url);
short_urls.push(short_url);
}
if some_hostname.is_none() || short_urls.is_empty() {
return "No dial info for bootstrap host".to_owned();
}
short_urls.sort();
short_urls.dedup();
out += "TXT Record:\n";
out += &format!(
"{},{},{},{},{}",
BOOTSTRAP_TXT_VERSION,
MIN_VERSION,
MAX_VERSION,
self.node_id().encode(),
some_hostname.unwrap()
);
for short_url in short_urls {
out += &format!(",{}", short_url);
}
out += "\n";
}

View File

@@ -67,6 +67,8 @@ impl RoutingTable {
NodeInfo {
network_class: netman.get_network_class().unwrap_or(NetworkClass::Invalid),
outbound_protocols: netman.get_protocol_config().unwrap_or_default().outbound,
min_version: MIN_VERSION,
max_version: MAX_VERSION,
dial_info_detail_list: self.dial_info_details(RoutingDomain::PublicInternet),
relay_peer_info: relay_node.and_then(|rn| rn.peer_info().map(Box::new)),
}

View File

@@ -22,6 +22,16 @@ pub use stats_accounting::*;
//////////////////////////////////////////////////////////////////////////
pub const BOOTSTRAP_TXT_VERSION: u8 = 0;
#[derive(Clone, Debug)]
pub struct BootstrapRecord {
min_version: u8,
max_version: u8,
dial_info_details: Vec<DialInfoDetail>,
}
pub type BootstrapRecordMap = BTreeMap<DHTKey, BootstrapRecord>;
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
pub enum RoutingDomain {
PublicInternet,
@@ -817,9 +827,10 @@ impl RoutingTable {
}
// Bootstrap lookup process
async fn resolve_bootstrap(&self, bootstrap: Vec<String>) -> Result<Vec<NodeDialInfo>, String> {
let mut out = Vec::<NodeDialInfo>::new();
async fn resolve_bootstrap(
&self,
bootstrap: Vec<String>,
) -> Result<BootstrapRecordMap, String> {
// Resolve from bootstrap root to bootstrap hostnames
let mut bsnames = Vec::<String>::new();
for bh in bootstrap {
@@ -856,20 +867,64 @@ impl RoutingTable {
}
Ok(v) => v,
};
// for each record resolve into node dial info strings
let mut nodedialinfos: Vec<NodeDialInfo> = Vec::new();
// for each record resolve into key/bootstraprecord pairs
let mut bootstrap_records: Vec<(DHTKey, BootstrapRecord)> = Vec::new();
for bsnirecord in bsnirecords {
// split bootstrap node record by commas. example:
// 7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ,tcp://bootstrap-dev-alpha.veilid.net:5150,udp://bootstrap-dev-alpha.veilid.net:5150,ws://bootstrap-dev-alpha.veilid.net:5150/ws
let mut records = bsnirecord.split(',').map(|x| x.trim());
let node_id_str = match records.next() {
Some(v) => v,
None => {
warn!("no node id specified in bootstrap node txt record");
// 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-dev-alpha.veilid.net,T5150,U5150,W5150/ws
let records: Vec<String> = bsnirecord
.trim()
.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>() {
Ok(v) => v,
Err(e) => {
warn!(
"invalid txt_version specified in bootstrap node txt record: {}",
e
);
continue;
}
};
// Decode the node id
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 DHTKey::try_decode(node_id_str) {
Ok(v) => v,
Err(e) => {
@@ -881,16 +936,23 @@ impl RoutingTable {
}
};
// 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 node_id = NodeId::new(node_id_key);
for rec in records {
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_url(rec) {
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);
@@ -898,24 +960,34 @@ impl RoutingTable {
}
};
for dial_info in dial_infos {
nodedialinfos.push(NodeDialInfo {
node_id: node_id.clone(),
dial_info,
})
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));
}
Some(nodedialinfos)
Some(bootstrap_records)
});
}
while let Some(ndis) = unord.next().await {
if let Some(mut ndis) = ndis {
out.append(&mut ndis);
let mut bsmap = BootstrapRecordMap::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);
}
}
}
Ok(out)
Ok(bsmap)
}
async fn bootstrap_task_routine(self) -> Result<(), String> {
@@ -930,8 +1002,10 @@ impl RoutingTable {
log_rtab!(debug "--- bootstrap_task");
// If we aren't specifying a bootstrap node list explicitly, then pull from the bootstrap server(s)
let bootstrap_node_dial_infos = if !bootstrap_nodes.is_empty() {
let mut bsnvec = Vec::new();
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 ndis = NodeDialInfo::from_str(b.as_str())
.map_err(map_to_string)
@@ -939,26 +1013,30 @@ impl RoutingTable {
"Invalid node dial info in bootstrap entry: {}",
b
))?;
bsnvec.push(ndis);
bootstrap_node_dial_infos.push(ndis);
}
bsnvec
for ndi in bootstrap_node_dial_infos {
let node_id = ndi.node_id.key;
bsmap
.entry(node_id)
.or_insert_with(|| BootstrapRecord {
min_version: MIN_VERSION,
max_version: MAX_VERSION,
dial_info_details: Vec::new(),
})
.dial_info_details
.push(DialInfoDetail {
dial_info: ndi.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
let mut bsmap: BTreeMap<DHTKey, Vec<DialInfoDetail>> = BTreeMap::new();
for ndi in bootstrap_node_dial_infos {
let node_id = ndi.node_id.key;
bsmap
.entry(node_id)
.or_insert_with(Vec::new)
.push(DialInfoDetail {
dial_info: ndi.dial_info,
class: DialInfoClass::Direct, // Bootstraps are always directly reachable
});
}
// Run all bootstrap operations concurrently
let mut unord = FuturesUnordered::new();
@@ -972,8 +1050,10 @@ impl RoutingTable {
SignedNodeInfo::with_no_signature(NodeInfo {
network_class: NetworkClass::InboundCapable, // Bootstraps are always inbound capable
outbound_protocols: ProtocolSet::empty(), // Bootstraps do not participate in relaying and will not make outbound requests
dial_info_detail_list: v, // Dial info is as specified in the bootstrap list
relay_peer_info: None, // Bootstraps never require a relay themselves
min_version: v.min_version, // Minimum protocol version specified in txt record
max_version: v.max_version, // Maximum protocol version specified in txt record
dial_info_detail_list: v.dial_info_details, // Dial info is as specified in the bootstrap list
relay_peer_info: None, // Bootstraps never require a relay themselves
}),
)
.map_err(logthru_rtab!(error "Couldn't add bootstrap node: {}", k))?;

View File

@@ -228,6 +228,14 @@ impl NodeRef {
None
}
})?;
// Verify this connection matches the noderef filter
if let Some(filter) = &self.filter {
if !last_connection.matches_filter(filter) {
return None;
}
}
// Should we check the connection table?
if last_connection.protocol_type().is_connection_oriented() {
// Look the connection up in the connection manager and see if it's still there