aboutsummaryrefslogtreecommitdiffstats
path: root/src/transport
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-06 16:47:32 +0100
committertv <tv@krebsco.de>2026-11-06 16:47:32 +0100
commitab8f5447680da56ce658abbc33c222219c3204ef (patch)
tree93ed572d120cf29b7e440c31194bafd021e8a5a6 /src/transport
parent7c0f90b327ad567c4e5e6a96cda433d774182dac (diff)
plumtree
Diffstat (limited to 'src/transport')
-rw-r--r--src/transport/mod.rs23
-rw-r--r--src/transport/traffic.rs7
-rw-r--r--src/transport/wire.rs23
3 files changed, 51 insertions, 2 deletions
diff --git a/src/transport/mod.rs b/src/transport/mod.rs
index 743e713..52b544a 100644
--- a/src/transport/mod.rs
+++ b/src/transport/mod.rs
@@ -247,6 +247,10 @@ pub(crate) enum ParsedMessage {
Probe(ProbeRequest),
/// Answers a PROBE.
ProbeReply(ProbeReply),
+ /// Asks the recipient to leave a writer's changes to announcements (`swarm::plumtree`).
+ Prune(TreeLink),
+ /// Asks the recipient to send a writer's changes at once (`swarm::plumtree`).
+ Graft(TreeLink),
}
/// A node's proof that it holds the key of its node ID (`identity`), sending from its
@@ -391,6 +395,8 @@ pub(crate) enum MessageKind {
LinksReply,
Probe,
ProbeReply,
+ Prune,
+ Graft,
}
impl ParsedMessage {
@@ -410,6 +416,8 @@ impl ParsedMessage {
ParsedMessage::LinksReply(_) => MessageKind::LinksReply,
ParsedMessage::Probe(_) => MessageKind::Probe,
ParsedMessage::ProbeReply(_) => MessageKind::ProbeReply,
+ ParsedMessage::Prune(_) => MessageKind::Prune,
+ ParsedMessage::Graft(_) => MessageKind::Graft,
}
}
@@ -430,6 +438,9 @@ impl ParsedMessage {
ParsedMessage::LinksReply(reply) => Some((reply.epoch, reply.epoch_seq)),
ParsedMessage::Probe(request) => Some((request.epoch, request.epoch_seq)),
ParsedMessage::ProbeReply(reply) => Some((reply.epoch, reply.epoch_seq)),
+ ParsedMessage::Prune(link) | ParsedMessage::Graft(link) => {
+ Some((link.epoch, link.epoch_seq))
+ }
ParsedMessage::Who(_)
| ParsedMessage::Me(_)
| ParsedMessage::Ping(_)
@@ -438,6 +449,18 @@ impl ParsedMessage {
}
}
+/// PRUNE and GRAFT: a writer's changes left to announcements, or sent at once, between
+/// the sender and the recipient (`swarm::plumtree`).
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub(crate) struct TreeLink {
+ #[serde(with = "crate::transport::wire::codec::epoch")]
+ pub(crate) epoch: Epoch,
+ /// See `SyncEnvelope::epoch_seq`.
+ pub(crate) epoch_seq: u32,
+ /// The writer, by its pair key (`topology::pair_key`).
+ pub(crate) writer: u64,
+}
+
/// PROBE: whether the recipient reaches `member` (`swarm::working_links`), as SWIM
/// asks others before it suspects a member. Answered from what the recipient knows,
/// without contacting the member.
diff --git a/src/transport/traffic.rs b/src/transport/traffic.rs
index 2e4266c..501c77b 100644
--- a/src/transport/traffic.rs
+++ b/src/transport/traffic.rs
@@ -55,6 +55,8 @@ pub(crate) struct ByKind {
pub(crate) links: Counter,
/// PROBE and PROBE_REPLY (`swarm::working_links`).
pub(crate) probe: Counter,
+ /// PRUNE and GRAFT (`swarm::plumtree`).
+ pub(crate) plumtree: Counter,
}
impl ByKind {
@@ -70,6 +72,7 @@ impl ByKind {
MessageKind::Ping,
MessageKind::Links,
MessageKind::Probe,
+ MessageKind::Prune,
] {
let theirs = *other.get(kind);
self.get_mut(kind).absorb(theirs);
@@ -88,6 +91,7 @@ impl ByKind {
MessageKind::Ping | MessageKind::Pong => &self.paths,
MessageKind::Links | MessageKind::LinksReply => &self.links,
MessageKind::Probe | MessageKind::ProbeReply => &self.probe,
+ MessageKind::Prune | MessageKind::Graft => &self.plumtree,
}
}
@@ -103,6 +107,7 @@ impl ByKind {
MessageKind::Ping | MessageKind::Pong => &mut self.paths,
MessageKind::Links | MessageKind::LinksReply => &mut self.links,
MessageKind::Probe | MessageKind::ProbeReply => &mut self.probe,
+ MessageKind::Prune | MessageKind::Graft => &mut self.plumtree,
}
}
}
@@ -295,6 +300,8 @@ fn names(kind: MessageKind) -> (&'static str, &'static str) {
MessageKind::LinksReply => ("LINKS_REPLY", "links"),
MessageKind::Probe => ("PROBE", "probe"),
MessageKind::ProbeReply => ("PROBE_REPLY", "probe"),
+ MessageKind::Prune => ("PRUNE", "plumtree"),
+ MessageKind::Graft => ("GRAFT", "plumtree"),
}
}
diff --git a/src/transport/wire.rs b/src/transport/wire.rs
index bf46fc9..729acd3 100644
--- a/src/transport/wire.rs
+++ b/src/transport/wire.rs
@@ -14,7 +14,7 @@ use super::{EncodedMessage, MessageKind, ParsedMessage};
/// The wire format's version, the first byte of every payload. Bump it with every change
/// to a message type or a codec (`golden_bytes` fails then).
-pub(crate) const VERSION: u8 = 36;
+pub(crate) const VERSION: u8 = 37;
/// What the second byte of a payload says.
fn tag(kind: MessageKind) -> u8 {
@@ -33,6 +33,8 @@ fn tag(kind: MessageKind) -> u8 {
MessageKind::LinksReply => 11,
MessageKind::Probe => 12,
MessageKind::ProbeReply => 13,
+ MessageKind::Prune => 14,
+ MessageKind::Graft => 15,
}
}
@@ -89,6 +91,9 @@ pub(crate) fn encode(message: &ParsedMessage) -> Result<Vec<u8>, WireError> {
ParsedMessage::LinksReply(reply) => rmp_serde::encode::write(&mut payload, reply)?,
ParsedMessage::Probe(request) => rmp_serde::encode::write(&mut payload, request)?,
ParsedMessage::ProbeReply(reply) => rmp_serde::encode::write(&mut payload, reply)?,
+ ParsedMessage::Prune(link) | ParsedMessage::Graft(link) => {
+ rmp_serde::encode::write(&mut payload, link)?
+ }
}
Ok(payload)
}
@@ -162,6 +167,10 @@ pub(crate) fn decode(payload: &[u8]) -> Result<ParsedMessage, WireError> {
ParsedMessage::Probe(rmp_serde::from_slice(body)?)
} else if is(MessageKind::ProbeReply) {
ParsedMessage::ProbeReply(rmp_serde::from_slice(body)?)
+ } else if is(MessageKind::Prune) {
+ ParsedMessage::Prune(rmp_serde::from_slice(body)?)
+ } else if is(MessageKind::Graft) {
+ ParsedMessage::Graft(rmp_serde::from_slice(body)?)
} else {
return Err(WireError::UnknownKind(kind));
})
@@ -532,6 +541,16 @@ pub(crate) fn samples() -> Vec<ParsedMessage> {
member: "q".into(),
reached: None,
}),
+ ParsedMessage::Prune(crate::transport::TreeLink {
+ epoch,
+ epoch_seq: 1,
+ writer: 5,
+ }),
+ ParsedMessage::Graft(crate::transport::TreeLink {
+ epoch,
+ epoch_seq: 1,
+ writer: 5,
+ }),
]
}
@@ -563,7 +582,7 @@ mod tests {
#[test]
fn every_kind_round_trips() {
let samples = samples();
- assert_eq!(samples.len(), 14, "a kind is missing from `samples`");
+ assert_eq!(samples.len(), 16, "a kind is missing from `samples`");
for message in samples {
let bytes = encode(&message).unwrap();
let decoded = decode(&bytes).unwrap();