transport
Source: crates/lvcs-net/src/transport/mod.rs.
/// A peer device as the net actors name it (design §2).
///
/// It is its Ed25519 public key, from which its libp2p `PeerId` derives.
/// Every LVCS device key is Ed25519, so the transport refuses a connection
/// whose identity is another key type (chosen at the M3 freeze: a
/// fixed-size, journaled form, where a `PeerId` would need libp2p's serde
/// feature). Journaled as a byte string. Owner: `M3.transport`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Peer(#[serde(with = "crate::wire::cbor::fixed")] pub [u8; 32]);
/// How the transport found a peer (design §2).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Discovery {
/// mDNS on the local network.
Mdns,
/// The Kademlia DHT.
Kademlia,
/// A `--bootstrap` address.
Bootstrap,
}
/// What `AutoNAT` concluded about this device's reachability (design §2):
/// a `Private` device is reached through a circuit relay v2 reservation,
/// upgraded by `DCUtR` when hole punching succeeds.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum NatStatus {
/// Not concluded yet.
Unknown,
/// Publicly reachable.
Public,
/// Not publicly reachable.
Private,
}
/// How the transport runs, from `lvcs daemon`'s flags (design §2).
///
/// It holds the multiaddrs to listen on (`--listen`) and to dial at start
/// (`--bootstrap`), the wire major it speaks, and seed mode (`--seed`): the same binary with no
/// special authority, which pins every document it learns of, serves as a
/// circuit relay v2 server, and is a bootstrap address for others. Owner:
/// `M3.transport`; the daemon (`M3.daemon`) builds it.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TransportConfig {
/// The multiaddrs to listen on (`--listen`).
pub listen: Vec<String>,
/// The multiaddrs to dial at start (`--bootstrap`).
pub bootstrap: Vec<String>,
/// Seed mode (`--seed`).
pub seed: bool,
/// Whether the swarm runs mDNS (off with `--no-mdns`, decision 307),
/// which `build_swarm` composes.
pub mdns: bool,
/// The project id (`ID_ENCODING`), which names the sync protocol, so a
/// peer of another project is never synced with (decision 320).
pub project: String,
/// The wire major version this daemon speaks (production-readiness
/// U4): `WIRE_MAJOR`, except where a debug build's
/// `WIRE_MAJOR_TEST_ENV` sets another. It names the sync protocol id
/// and is advertised in identify; a peer that advertises another, or
/// lists no sync protocol of this one, is incompatible.
pub wire: u16,
}
impl Default for TransportConfig {
/// No listen or bootstrap address, no seed mode, mDNS on, no project,
/// and `WIRE_MAJOR`.
fn default() -> Self {
Self {
listen: Vec::new(),
bootstrap: Vec::new(),
seed: false,
mdns: true,
project: String::new(),
wire: lvcs_vocab::ids::WIRE_MAJOR,
}
}
}
/// What the swarm reports (design §2), in the order it saw it.
///
/// The daemon shell turns each into the events of the actors that need it:
/// peers and the sync stream go to the sync actor (`SyncingEvents`), gossip
/// messages to the gossip actor (`GossipingEvents`). Addresses are multiaddrs
/// in text form. Owner: `M3.transport`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransportEvent {
/// The swarm listens on `addr`.
Listening {
/// The listen address.
addr: String,
},
/// A connection to `peer` is up (the first one, if there are several).
Connected {
/// The connected peer.
peer: Peer,
},
/// The last connection to `peer` closed.
Gone {
/// The peer whose last connection closed.
peer: Peer,
},
/// `peer` was discovered at `addrs` through `via`.
Discovered {
/// The discovered peer.
peer: Peer,
/// Its addresses.
addrs: Vec<String>,
/// How it was found.
via: Discovery,
},
/// `AutoNAT`'s conclusion changed.
Nat(NatStatus),
/// The one sync stream with `peer` is open (decision 68): whichever
/// side's device key bytes are smaller opens it, and the transport
/// closes a second one, so each peer pair has exactly one.
SyncOpened {
/// The peer the stream is with.
peer: Peer,
},
/// One length-delimited frame read from the sync stream: an encoded
/// envelope of kind `sync`, at most `WIRE_MAX_BYTES` (a longer frame
/// closes the stream and is reported as [`TransportEvent::SyncClosed`]).
SyncFrame {
/// The peer the stream is with.
peer: Peer,
/// The frame's bytes.
frame: Vec<u8>,
},
/// The sync stream with `peer` closed.
SyncClosed {
/// The peer the stream was with.
peer: Peer,
},
/// A gossipsub message waiting for validation (design §9).
///
/// `id` is gossipsub's message id, `propagation` the peer that
/// forwarded it (whose score a repeat offense lowers, §15), and `data`
/// the encoded envelope, which its participant signed. Gossipsub
/// forwards it only after [`TransportCommand::Validate`] accepts it.
/// The publishing device is not reported: nothing downstream needs it,
/// since the envelope names its signer and the yield order reads only
/// the payload (chosen at the M3 freeze).
Gossip {
/// Gossipsub's message id.
id: Vec<u8>,
/// The peer that forwarded it.
propagation: Peer,
/// The topic it came on.
topic: String,
/// The encoded envelope.
data: Vec<u8>,
},
/// `peer` runs a release this daemon can't sync with
/// (production-readiness U4): its identify advertises another wire
/// major, or no agent version this daemon parses, or lists no sync
/// protocol of this daemon's major. Reported once per peer and daemon
/// run, until [`TransportEvent::Cleared`]; no sync stream opens with
/// it meanwhile.
Incompatible {
/// The incompatible peer.
peer: Peer,
/// Its identify agent version, printable ASCII, at most
/// `AGENT_MAX_BYTES`.
agent: String,
/// The wire major it advertises, `None` when its agent version
/// names none.
wire: Option<u16>,
/// The wire major this daemon speaks.
here: u16,
},
/// This daemon refused `peer`'s sync and disconnected it, though it
/// speaks the same wire major: its build and this one disagree
/// (decision 320). Reported on its first refusal; the transport then
/// lets it connect only after a doubling backoff, until
/// [`TransportEvent::Cleared`].
BuildRefused {
/// The refused peer.
peer: Peer,
/// Its identify agent version, empty if none arrived.
agent: String,
},
/// A peer reported [`TransportEvent::Incompatible`] or
/// [`TransportEvent::BuildRefused`] is no longer: it identified again
/// as compatible (it upgraded, or downgraded), or the transport forgot
/// it to remember a newer one past its bound.
Cleared {
/// The peer.
peer: Peer,
},
/// `peer` told this side it joined `topic`: on their first connection,
/// gossipsub's greeting lists every topic the peer is subscribed to,
/// and a later join is told on its own. A publish on `topic` reaches
/// the peer from now on.
Subscribed {
/// The subscribed peer.
peer: Peer,
/// The topic it joined.
topic: String,
},
}
/// Gossipsub's validation verdict for a received message (design §9):
/// only an accepted message is forwarded; a rejected one also counts
/// against the propagating peer's score; an ignored one does neither.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Acceptance {
/// Forward the message.
Accept,
/// Drop it and count it against the propagating peer's score.
Reject,
/// Drop it.
Ignore,
}
/// What the daemon shell asks the swarm to do, built from the net actors'
/// outputs. Owner: `M3.transport`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransportCommand {
/// Dial the multiaddr `addr`.
Dial {
/// The multiaddr to dial.
addr: String,
},
/// Write one frame (an encoded envelope) to the sync stream with
/// `peer`, length-delimited.
SyncFrame {
/// The peer the stream is with.
peer: Peer,
/// The frame's bytes.
frame: Vec<u8>,
},
/// Close every connection to `peer`.
Disconnect {
/// The peer to disconnect.
peer: Peer,
},
/// Join a gossipsub topic.
Subscribe {
/// The topic to join.
topic: String,
},
/// Leave a gossipsub topic.
Unsubscribe {
/// The topic to leave.
topic: String,
},
/// Publish `data` (an encoded envelope) on `topic`.
Publish {
/// The topic to publish on.
topic: String,
/// The encoded envelope.
data: Vec<u8>,
},
/// Report the validation of the gossipsub message `id` received from
/// `propagation`.
Validate {
/// Gossipsub's message id.
id: Vec<u8>,
/// The peer that forwarded it.
propagation: Peer,
/// The verdict.
acceptance: Acceptance,
},
/// Lower `peer`'s score for a protocol violation (design §11, §15).
Penalize {
/// The peer to penalize.
peer: Peer,
/// The violation.
reason: Rejection,
},
/// Stop the transport, as its last command: every connection is closed
/// before the swarm is dropped, so each peer sees it end at once rather
/// than when its pings fail, and a restarted daemon's next connection is
/// its peers' first again.
Stop,
}