signed node info
This commit is contained in:
@@ -37,8 +37,8 @@ pub struct BucketEntry {
|
||||
min_max_version: Option<(u8, u8)>,
|
||||
seen_our_node_info: bool,
|
||||
last_connection: Option<(ConnectionDescriptor, u64)>,
|
||||
node_info: NodeInfo,
|
||||
local_node_info: LocalNodeInfo,
|
||||
opt_signed_node_info: Option<SignedNodeInfo>,
|
||||
opt_local_node_info: Option<LocalNodeInfo>,
|
||||
peer_stats: PeerStats,
|
||||
latency_stats_accounting: LatencyStatsAccounting,
|
||||
transfer_stats_accounting: TransferStatsAccounting,
|
||||
@@ -52,8 +52,8 @@ impl BucketEntry {
|
||||
min_max_version: None,
|
||||
seen_our_node_info: false,
|
||||
last_connection: None,
|
||||
node_info: NodeInfo::default(),
|
||||
local_node_info: LocalNodeInfo::default(),
|
||||
opt_signed_node_info: None,
|
||||
opt_local_node_info: None,
|
||||
latency_stats_accounting: LatencyStatsAccounting::new(),
|
||||
transfer_stats_accounting: TransferStatsAccounting::new(),
|
||||
peer_stats: PeerStats {
|
||||
@@ -107,26 +107,44 @@ impl BucketEntry {
|
||||
move |e1, e2| Self::cmp_fastest_reliable(cur_ts, e1, e2)
|
||||
}
|
||||
|
||||
pub fn update_node_info(&mut self, node_info: NodeInfo) {
|
||||
self.node_info = node_info
|
||||
pub fn update_node_info(&mut self, signed_node_info: SignedNodeInfo) {
|
||||
self.opt_signed_node_info = Some(signed_node_info);
|
||||
}
|
||||
pub fn update_local_node_info(&mut self, local_node_info: LocalNodeInfo) {
|
||||
self.local_node_info = local_node_info
|
||||
self.opt_local_node_info = Some(local_node_info)
|
||||
}
|
||||
|
||||
pub fn node_info(&self) -> &NodeInfo {
|
||||
&self.node_info
|
||||
pub fn has_node_info(&self) -> bool {
|
||||
self.opt_signed_node_info.is_some()
|
||||
}
|
||||
pub fn local_node_info(&self) -> &LocalNodeInfo {
|
||||
&self.local_node_info
|
||||
}
|
||||
pub fn peer_info(&self, key: DHTKey) -> PeerInfo {
|
||||
PeerInfo {
|
||||
node_id: NodeId::new(key),
|
||||
node_info: self.node_info.clone(),
|
||||
|
||||
pub fn has_valid_signed_node_info(&self) -> bool {
|
||||
if let Some(sni) = &self.opt_signed_node_info {
|
||||
sni.signature.valid
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_local_node_info(&self) -> bool {
|
||||
self.opt_local_node_info.is_some()
|
||||
}
|
||||
|
||||
pub fn node_info(&self) -> Option<NodeInfo> {
|
||||
self.opt_signed_node_info
|
||||
.as_ref()
|
||||
.map(|s| s.node_info.clone())
|
||||
}
|
||||
pub fn local_node_info(&self) -> Option<LocalNodeInfo> {
|
||||
self.opt_local_node_info.clone()
|
||||
}
|
||||
pub fn peer_info(&self, key: DHTKey) -> Option<PeerInfo> {
|
||||
self.opt_signed_node_info.as_ref().map(|s| PeerInfo {
|
||||
node_id: NodeId::new(key),
|
||||
signed_node_info: s.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_last_connection(&mut self, last_connection: ConnectionDescriptor, timestamp: u64) {
|
||||
self.last_connection = Some((last_connection, timestamp));
|
||||
}
|
||||
|
@@ -22,17 +22,20 @@ impl RoutingTable {
|
||||
let entry = params.1.as_ref().unwrap();
|
||||
|
||||
// skip nodes on our local network here
|
||||
if entry.local_node_info().has_dial_info() {
|
||||
if entry.local_node_info().is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// does it have matching public dial info?
|
||||
entry
|
||||
.node_info()
|
||||
.first_filtered_dial_info_detail(|did| {
|
||||
did.matches_filter(&dial_info_filter1)
|
||||
.map(|n| {
|
||||
n.first_filtered_dial_info_detail(|did| {
|
||||
did.matches_filter(&dial_info_filter1)
|
||||
})
|
||||
.is_some()
|
||||
})
|
||||
.is_some()
|
||||
.unwrap_or(false)
|
||||
},
|
||||
)),
|
||||
// transform
|
||||
@@ -49,16 +52,30 @@ impl RoutingTable {
|
||||
|
||||
// Get our own node's peer info (public node info) so we can share it with other nodes
|
||||
pub fn get_own_peer_info(&self) -> PeerInfo {
|
||||
PeerInfo::new(NodeId::new(self.node_id()), self.get_own_signed_node_info())
|
||||
}
|
||||
|
||||
pub fn get_own_signed_node_info(&self) -> SignedNodeInfo {
|
||||
let node_id = NodeId::new(self.node_id());
|
||||
let secret = self.node_id_secret();
|
||||
SignedNodeInfo::with_secret(self.get_own_node_info(), node_id, &secret).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_own_node_info(&self) -> NodeInfo {
|
||||
let netman = self.network_manager();
|
||||
let relay_node = netman.relay_node();
|
||||
PeerInfo {
|
||||
node_id: NodeId::new(self.node_id()),
|
||||
node_info: NodeInfo {
|
||||
network_class: netman.get_network_class().unwrap_or(NetworkClass::Invalid),
|
||||
outbound_protocols: netman.get_protocol_config().unwrap_or_default().outbound,
|
||||
dial_info_detail_list: self.dial_info_details(RoutingDomain::PublicInternet),
|
||||
relay_peer_info: relay_node.map(|rn| Box::new(rn.peer_info())),
|
||||
},
|
||||
NodeInfo {
|
||||
network_class: netman.get_network_class().unwrap_or(NetworkClass::Invalid),
|
||||
outbound_protocols: netman.get_protocol_config().unwrap_or_default().outbound,
|
||||
dial_info_detail_list: self.dial_info_details(RoutingDomain::PublicInternet),
|
||||
relay_peer_info: relay_node.and_then(|rn| rn.peer_info().map(Box::new)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filter_has_valid_signed_node_info(kv: &(&DHTKey, Option<&mut BucketEntry>)) -> bool {
|
||||
match &kv.1 {
|
||||
None => true,
|
||||
Some(b) => b.has_node_info(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +85,7 @@ impl RoutingTable {
|
||||
) -> PeerInfo {
|
||||
match &kv.1 {
|
||||
None => own_peer_info.clone(),
|
||||
Some(entry) => entry.peer_info(*kv.0),
|
||||
Some(entry) => entry.peer_info(*kv.0).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -482,13 +482,13 @@ impl RoutingTable {
|
||||
|
||||
// Shortcut function to add a node to our routing table if it doesn't exist
|
||||
// and add the dial info we have for it, since that's pretty common
|
||||
pub fn register_node_with_node_info(
|
||||
pub fn register_node_with_signed_node_info(
|
||||
&self,
|
||||
node_id: DHTKey,
|
||||
node_info: NodeInfo,
|
||||
signed_node_info: SignedNodeInfo,
|
||||
) -> Result<NodeRef, String> {
|
||||
let nr = self.create_node_ref(node_id, |e| {
|
||||
e.update_node_info(node_info);
|
||||
e.update_node_info(signed_node_info);
|
||||
})?;
|
||||
|
||||
Ok(nr)
|
||||
@@ -542,7 +542,11 @@ impl RoutingTable {
|
||||
// Ensure it's not dead
|
||||
if !matches!(entry.state(cur_ts), BucketEntryState::Dead) {
|
||||
// Ensure this node is not on our local network
|
||||
if !entry.local_node_info().has_dial_info() {
|
||||
if !entry
|
||||
.local_node_info()
|
||||
.map(|l| l.has_dial_info())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// Ensure we have the node's status
|
||||
if let Some(node_status) = &entry.peer_stats().status {
|
||||
// Ensure the node will relay
|
||||
@@ -569,8 +573,36 @@ impl RoutingTable {
|
||||
best_inbound_relay
|
||||
}
|
||||
|
||||
pub async fn find_self(&self, node_ref: NodeRef) -> Result<Vec<NodeRef>, String> {
|
||||
pub fn register_find_node_answer(&self, fna: FindNodeAnswer) -> Result<Vec<NodeRef>, String> {
|
||||
let node_id = self.node_id();
|
||||
|
||||
// register nodes we'd found
|
||||
let mut out = Vec::<NodeRef>::with_capacity(fna.peers.len());
|
||||
for p in fna.peers {
|
||||
// if our own node if is in the list then ignore it, as we don't add ourselves to our own routing table
|
||||
if p.node_id.key == node_id {
|
||||
continue;
|
||||
}
|
||||
|
||||
// register the node if it's new
|
||||
let nr = self
|
||||
.register_node_with_signed_node_info(p.node_id.key, p.signed_node_info.clone())
|
||||
.map_err(map_to_string)
|
||||
.map_err(logthru_rtab!(
|
||||
"couldn't register node {} at {:?}",
|
||||
p.node_id.key,
|
||||
&p.signed_node_info
|
||||
))?;
|
||||
out.push(nr);
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub async fn find_node(
|
||||
&self,
|
||||
node_ref: NodeRef,
|
||||
node_id: DHTKey,
|
||||
) -> Result<Vec<NodeRef>, String> {
|
||||
let rpc_processor = self.rpc_processor();
|
||||
|
||||
let res = rpc_processor
|
||||
@@ -594,29 +626,14 @@ impl RoutingTable {
|
||||
self.register_find_node_answer(res)
|
||||
}
|
||||
|
||||
pub fn register_find_node_answer(&self, fna: FindNodeAnswer) -> Result<Vec<NodeRef>, String> {
|
||||
pub async fn find_self(&self, node_ref: NodeRef) -> Result<Vec<NodeRef>, String> {
|
||||
let node_id = self.node_id();
|
||||
self.find_node(node_ref, node_id).await
|
||||
}
|
||||
|
||||
// register nodes we'd found
|
||||
let mut out = Vec::<NodeRef>::with_capacity(fna.peers.len());
|
||||
for p in fna.peers {
|
||||
// if our own node if is in the list then ignore it, as we don't add ourselves to our own routing table
|
||||
if p.node_id.key == node_id {
|
||||
continue;
|
||||
}
|
||||
|
||||
// register the node if it's new
|
||||
let nr = self
|
||||
.register_node_with_node_info(p.node_id.key, p.node_info.clone())
|
||||
.map_err(map_to_string)
|
||||
.map_err(logthru_rtab!(
|
||||
"couldn't register node {} at {:?}",
|
||||
p.node_id.key,
|
||||
&p.node_info
|
||||
))?;
|
||||
out.push(nr);
|
||||
}
|
||||
Ok(out)
|
||||
pub async fn find_target(&self, node_ref: NodeRef) -> Result<Vec<NodeRef>, String> {
|
||||
let node_id = node_ref.node_id();
|
||||
self.find_node(node_ref, node_id).await
|
||||
}
|
||||
|
||||
pub async fn reverse_find_node(&self, node_ref: NodeRef, wide: bool) {
|
||||
@@ -681,18 +698,39 @@ impl RoutingTable {
|
||||
let mut unord = FuturesUnordered::new();
|
||||
for (k, v) in bsmap {
|
||||
log_rtab!(" bootstrapping {} with {:?}", k.encode(), &v);
|
||||
|
||||
// Make invalid signed node info (no signature)
|
||||
let nr = self
|
||||
.register_node_with_node_info(
|
||||
.register_node_with_signed_node_info(
|
||||
k,
|
||||
NodeInfo {
|
||||
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
|
||||
},
|
||||
}),
|
||||
)
|
||||
.map_err(logthru_rtab!("Couldn't add bootstrap node: {}", k))?;
|
||||
unord.push(self.reverse_find_node(nr, true));
|
||||
|
||||
// Add this our futures to process in parallel
|
||||
let this = self.clone();
|
||||
unord.push(async move {
|
||||
// Need VALID signed peer info, so ask bootstrap to find_node of itself
|
||||
// which will ensure it has the bootstrap's signed peer info as part of the response
|
||||
let _ = this.find_target(nr.clone()).await;
|
||||
|
||||
// Ensure we got the signed peer info
|
||||
if !nr.operate(|e| e.has_valid_signed_node_info()) {
|
||||
warn!(
|
||||
"bootstrap at {:?} did not return valid signed node info",
|
||||
nr
|
||||
);
|
||||
// xxx: delete the node?
|
||||
} else {
|
||||
// otherwise this bootstrap is valid, lets ask it to find ourselves now
|
||||
this.reverse_find_node(nr, true).await
|
||||
}
|
||||
});
|
||||
}
|
||||
while unord.next().await.is_some() {}
|
||||
Ok(())
|
||||
@@ -749,7 +787,7 @@ impl RoutingTable {
|
||||
nr,
|
||||
entry.state_debug_info(cur_ts)
|
||||
);
|
||||
intf::spawn_local(rpc.clone().rpc_call_info(nr)).detach();
|
||||
intf::spawn_local(rpc.clone().rpc_call_status(nr)).detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -63,7 +63,7 @@ impl NodeRef {
|
||||
self.routing_table.operate_on_bucket_entry(self.node_id, f)
|
||||
}
|
||||
|
||||
pub fn peer_info(&self) -> PeerInfo {
|
||||
pub fn peer_info(&self) -> Option<PeerInfo> {
|
||||
self.operate(|e| e.peer_info(self.node_id()))
|
||||
}
|
||||
pub fn has_seen_our_node_info(&self) -> bool {
|
||||
@@ -72,23 +72,24 @@ impl NodeRef {
|
||||
pub fn set_seen_our_node_info(&self) {
|
||||
self.operate(|e| e.set_seen_our_node_info(true));
|
||||
}
|
||||
|
||||
pub fn network_class(&self) -> NetworkClass {
|
||||
self.operate(|e| e.node_info().network_class)
|
||||
pub fn network_class(&self) -> Option<NetworkClass> {
|
||||
self.operate(|e| e.node_info().map(|n| n.network_class))
|
||||
}
|
||||
pub fn outbound_protocols(&self) -> ProtocolSet {
|
||||
self.operate(|e| e.node_info().outbound_protocols)
|
||||
pub fn outbound_protocols(&self) -> Option<ProtocolSet> {
|
||||
self.operate(|e| e.node_info().map(|n| n.outbound_protocols))
|
||||
}
|
||||
pub fn relay(&self) -> Option<NodeRef> {
|
||||
let target_rpi = self.operate(|e| e.node_info().relay_peer_info.clone())?;
|
||||
self.routing_table
|
||||
.register_node_with_node_info(target_rpi.node_id.key, target_rpi.node_info)
|
||||
.map_err(logthru_rtab!(error))
|
||||
.ok()
|
||||
.map(|mut nr| {
|
||||
nr.set_filter(self.filter_ref().cloned());
|
||||
nr
|
||||
})
|
||||
let target_rpi = self.operate(|e| e.node_info().map(|n| n.relay_peer_info))?;
|
||||
target_rpi.and_then(|t| {
|
||||
self.routing_table
|
||||
.register_node_with_signed_node_info(t.node_id.key, t.signed_node_info)
|
||||
.map_err(logthru_rtab!(error))
|
||||
.ok()
|
||||
.map(|mut nr| {
|
||||
nr.set_filter(self.filter_ref().cloned());
|
||||
nr
|
||||
})
|
||||
})
|
||||
}
|
||||
pub fn first_filtered_dial_info_detail(
|
||||
&self,
|
||||
@@ -105,8 +106,8 @@ impl NodeRef {
|
||||
PeerScope::All | PeerScope::Local
|
||||
)
|
||||
{
|
||||
e.local_node_info()
|
||||
.first_filtered_dial_info(|di| {
|
||||
e.local_node_info().and_then(|l| {
|
||||
l.first_filtered_dial_info(|di| {
|
||||
if let Some(filter) = self.filter.as_ref() {
|
||||
di.matches_filter(filter)
|
||||
} else {
|
||||
@@ -117,6 +118,7 @@ impl NodeRef {
|
||||
class: DialInfoClass::Direct,
|
||||
dial_info: di,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -130,12 +132,14 @@ impl NodeRef {
|
||||
PeerScope::All | PeerScope::Global
|
||||
)
|
||||
{
|
||||
e.node_info().first_filtered_dial_info_detail(|did| {
|
||||
if let Some(filter) = self.filter.as_ref() {
|
||||
did.matches_filter(filter)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
e.node_info().and_then(|n| {
|
||||
n.first_filtered_dial_info_detail(|did| {
|
||||
if let Some(filter) = self.filter.as_ref() {
|
||||
did.matches_filter(filter)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
None
|
||||
@@ -160,17 +164,19 @@ impl NodeRef {
|
||||
PeerScope::All | PeerScope::Local
|
||||
)
|
||||
{
|
||||
for di in e.local_node_info().all_filtered_dial_info(|di| {
|
||||
if let Some(filter) = self.filter.as_ref() {
|
||||
di.matches_filter(filter)
|
||||
} else {
|
||||
true
|
||||
if let Some(lni) = e.local_node_info() {
|
||||
for di in lni.all_filtered_dial_info(|di| {
|
||||
if let Some(filter) = self.filter.as_ref() {
|
||||
di.matches_filter(filter)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}) {
|
||||
out.push(DialInfoDetail {
|
||||
class: DialInfoClass::Direct,
|
||||
dial_info: di,
|
||||
});
|
||||
}
|
||||
}) {
|
||||
out.push(DialInfoDetail {
|
||||
class: DialInfoClass::Direct,
|
||||
dial_info: di,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (routing_domain == None || routing_domain == Some(RoutingDomain::PublicInternet))
|
||||
@@ -182,13 +188,15 @@ impl NodeRef {
|
||||
PeerScope::All | PeerScope::Global
|
||||
)
|
||||
{
|
||||
out.append(&mut e.node_info().all_filtered_dial_info_details(|did| {
|
||||
if let Some(filter) = self.filter.as_ref() {
|
||||
did.matches_filter(filter)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}))
|
||||
if let Some(ni) = e.node_info() {
|
||||
out.append(&mut ni.all_filtered_dial_info_details(|did| {
|
||||
if let Some(filter) = self.filter.as_ref() {
|
||||
did.matches_filter(filter)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
});
|
||||
out.remove_duplicates();
|
||||
@@ -225,7 +233,14 @@ impl NodeRef {
|
||||
}
|
||||
|
||||
pub fn has_any_dial_info(&self) -> bool {
|
||||
self.operate(|e| e.node_info().has_any_dial_info() || e.local_node_info().has_dial_info())
|
||||
self.operate(|e| {
|
||||
e.node_info()
|
||||
.map(|n| n.has_any_dial_info())
|
||||
.unwrap_or(false)
|
||||
|| e.local_node_info()
|
||||
.map(|l| l.has_dial_info())
|
||||
.unwrap_or(false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user