summaryrefslogtreecommitdiffstats
path: root/src/transport
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-27 02:07:04 +0100
committertv <tv@krebsco.de>2026-10-27 02:07:04 +0100
commitec862a35e867c118dbab237e72057a433bc968d7 (patch)
treec39c05cc098bba2a586f660b6c3f319a43568b1c /src/transport
parentb56535ba5d236a96598d8156e3534932271acf54 (diff)
introduce locations
This prepares the identity layer for transports where an identity alone doesn't say where a node is. The identity layer keeps identities in its books and publishes locations. For Mycelium and the simulator both methods default to the identity, so nothing changes there.
Diffstat (limited to 'src/transport')
-rw-r--r--src/transport/identity.rs93
-rw-r--r--src/transport/mod.rs16
2 files changed, 85 insertions, 24 deletions
diff --git a/src/transport/identity.rs b/src/transport/identity.rs
index 2b9bf5a..abedd35 100644
--- a/src/transport/identity.rs
+++ b/src/transport/identity.rs
@@ -22,7 +22,9 @@
//! with only hints, those are asked WHO, and the send is refused until one answers
//! (the node retries refused sends).
//!
-//! Addresses are `<scheme>:<transport identity>`, e.g. `mycelium:<public key>`.
+//! Addresses are `<scheme>:<transport identity>`, e.g. `mycelium:<public key>`, or
+//! `<scheme>:<location>` where a transport needs more than an identity to reach a node
+//! (`Transport::locate`, `Transport::locations`); the books keep the identities.
use std::collections::HashMap;
use std::sync::{Arc, Mutex, MutexGuard};
@@ -231,12 +233,33 @@ impl Shared {
self.book.lock_or_recover()
}
- /// The transport `address` is on, and the transport identity in it; None if no
- /// transport of ours has its scheme.
- fn split<'a>(&self, address: &'a str) -> Option<(&Link, &'a str)> {
+ /// The transport `address` is on, and the transport identity at it
+ /// (`Transport::locate`, which notes where that is if the address says); None if no
+ /// transport of ours has its scheme, or it isn't one of that transport's.
+ fn split(&self, address: &str) -> Option<(&Link, String)> {
let (scheme, target) = address.split_once(':')?;
let link = self.links.iter().find(|link| link.scheme == scheme)?;
- link.inner.is_peer_id(target).then_some((link, target))
+ let target = link.inner.locate(target)?;
+ Some((link, target))
+ }
+
+ /// `address` as the books keep it: `<scheme>:<transport identity>`.
+ fn canonical(&self, address: &str) -> Option<String> {
+ self.split(address)
+ .map(|(link, target)| link.address(&target))
+ }
+
+ /// Where the node at `address` (as the books keep it) is, as others can use it
+ /// (`Transport::locations`).
+ fn locations(&self, address: &str) -> Vec<String> {
+ let Some((link, target)) = self.split(address) else {
+ return Vec::new();
+ };
+ link.inner
+ .locations(&target)
+ .iter()
+ .map(|location| link.address(location))
+ .collect()
}
/// Where `address`'s transport is in the order of preference (none of ours: last).
@@ -301,11 +324,11 @@ impl Shared {
return;
};
let message = if kind == MessageKind::Who {
- ParsedMessage::Who(self.proof(WHO_LABEL, link, target))
+ ParsedMessage::Who(self.proof(WHO_LABEL, link, &target))
} else {
- ParsedMessage::Me(self.proof(ME_LABEL, link, target))
+ ParsedMessage::Me(self.proof(ME_LABEL, link, &target))
};
- let encoded = match link.inner.encode(target, &message) {
+ let encoded = match link.inner.encode(&target, &message) {
Ok(encoded) => encoded,
Err(error) => {
debug!(to = %address, error = %format!("{error:#}"), "Couldn't encode an identity proof");
@@ -419,17 +442,39 @@ impl Transport for IdentityTransport {
.unwrap_or_default()
}
+ /// Each transport's (`Transport::local_addresses`), with its scheme.
fn local_addresses(&self) -> Vec<String> {
- self.shared.links.iter().map(Link::own_address).collect()
+ self.shared
+ .links
+ .iter()
+ .flat_map(|link| {
+ link.inner
+ .local_addresses()
+ .into_iter()
+ .map(|target| link.address(&target))
+ })
+ .collect()
}
+ /// Where `peer` proved itself, else where it is said to be, as others can use it.
fn addresses_of(&self, peer: &str) -> Vec<String> {
- let book = self.shared.lock();
- book.proven
- .get(peer)
- .or_else(|| book.hints.get(peer))
- .cloned()
- .unwrap_or_default()
+ let addresses = {
+ let book = self.shared.lock();
+ book.proven
+ .get(peer)
+ .or_else(|| book.hints.get(peer))
+ .cloned()
+ .unwrap_or_default()
+ };
+ let mut locations = Vec::new();
+ for address in addresses {
+ for location in self.shared.locations(&address) {
+ if !locations.contains(&location) {
+ locations.push(location);
+ }
+ }
+ }
+ locations
}
fn learn_addresses(&self, peer: &str, addresses: &[String]) {
@@ -438,11 +483,11 @@ impl Transport for IdentityTransport {
}
let mut known: Vec<String> = Vec::new();
for address in addresses {
- if self.shared.split(address).is_some()
- && !self.shared.is_own(address)
- && !known.contains(address)
+ if let Some(address) = self.shared.canonical(address)
+ && !self.shared.is_own(&address)
+ && !known.contains(&address)
{
- known.push(address.clone());
+ known.push(address);
}
}
known.truncate(MAX_HINTS);
@@ -469,8 +514,8 @@ impl Transport for IdentityTransport {
}
bail!("a node ID alone doesn't say where that node is; give its address");
}
- let address = match self.shared.split(seed) {
- Some(_) => seed.to_owned(),
+ let address = match self.shared.canonical(seed) {
+ Some(address) => address,
None => {
let mut failures = Vec::new();
let mut resolved = None;
@@ -521,7 +566,7 @@ impl Transport for IdentityTransport {
let Some((link, target)) = self.shared.split(&address) else {
bail!("no transport for {address}");
};
- let mut encoded = link.inner.encode(target, message)?;
+ let mut encoded = link.inner.encode(&target, message)?;
encoded.peer = peer.to_owned();
encoded.address = address;
Ok(encoded)
@@ -550,8 +595,8 @@ impl Transport for IdentityTransport {
bail!("no transport for {}", message.address);
};
let mut inner = message.clone();
- inner.peer = target.to_owned();
- inner.address = target.to_owned();
+ inner.peer = target.clone();
+ inner.address = target;
link.inner.send_encoded(&inner).await
}
diff --git a/src/transport/mod.rs b/src/transport/mod.rs
index cbbef45..58e71d8 100644
--- a/src/transport/mod.rs
+++ b/src/transport/mod.rs
@@ -531,6 +531,22 @@ pub(crate) trait Transport: Send + Sync {
vec![self.local_id().to_owned()]
}
+ /// For a transport under the identity layer: the transport identity at `target`, an
+ /// address of this transport without its scheme, noting where it is if `target` says
+ /// more than who (as a libp2p multiaddr ending in the peer ID does). None if
+ /// `target` isn't one of this transport's. By default `target` itself, for a
+ /// transport whose identities are addresses.
+ fn locate(&self, target: &str) -> Option<String> {
+ self.is_peer_id(target).then(|| target.to_owned())
+ }
+
+ /// For a transport under the identity layer: where the node with transport identity
+ /// `target` is, as addresses others can use (`locate` reads them). By default
+ /// `target` itself, as for `locate`.
+ fn locations(&self, target: &str) -> Vec<String> {
+ vec![target.to_owned()]
+ }
+
/// Where `peer` can be reached, as far as this node knows: passed on to others (in
/// a HELLO_REPLY). By default its identity, as for `local_addresses`.
fn addresses_of(&self, peer: &str) -> Vec<String> {