1use serde::{Deserialize, Serialize};
8use std::convert::TryFrom;
9
10use crate::{manifest::PublicShareSummary, peer::PeerAddr};
11
12mod int_cbor {
18 use ciborium::Value;
19
20 pub fn into_map(val: Value) -> Result<Vec<(Value, Value)>, String> {
22 match val {
23 Value::Map(m) => Ok(m),
24 other => Err(format!("expected CBOR map, got {:?}", other)),
25 }
26 }
27
28 pub fn find_field<'a>(
33 map: &'a [(Value, Value)],
34 int_key: i64,
35 _str_key: &str,
36 ) -> Option<&'a Value> {
37 map.iter()
38 .find(|(k, _)| {
39 k.as_integer()
40 .map(|i| i128::from(i) == int_key as i128)
41 .unwrap_or(false)
42 })
43 .map(|(_, v)| v)
44 }
45
46 pub fn extract_byte_array<const N: usize>(
48 map: &[(Value, Value)],
49 int_key: i64,
50 str_key: &str,
51 ) -> Result<[u8; N], String> {
52 let val =
53 find_field(map, int_key, str_key).ok_or_else(|| format!("missing field {str_key}"))?;
54 let bytes = val
55 .as_bytes()
56 .ok_or_else(|| format!("field {str_key}: expected bytes"))?;
57 if bytes.len() != N {
58 return Err(format!(
59 "field {str_key}: expected {N} bytes, got {}",
60 bytes.len()
61 ));
62 }
63 let mut out = [0u8; N];
64 out.copy_from_slice(bytes);
65 Ok(out)
66 }
67
68 pub fn extract_bytes(
70 map: &[(Value, Value)],
71 int_key: i64,
72 str_key: &str,
73 ) -> Result<Vec<u8>, String> {
74 let val =
75 find_field(map, int_key, str_key).ok_or_else(|| format!("missing field {str_key}"))?;
76 val.as_bytes()
77 .cloned()
78 .ok_or_else(|| format!("field {str_key}: expected bytes"))
79 }
80
81 pub fn extract_u64(map: &[(Value, Value)], int_key: i64, str_key: &str) -> Result<u64, String> {
83 let val =
84 find_field(map, int_key, str_key).ok_or_else(|| format!("missing field {str_key}"))?;
85 match val.as_integer() {
86 Some(i) => {
87 let n: i128 = i.into();
88 u64::try_from(n).map_err(|_| format!("field {str_key}: integer out of u64 range"))
89 }
90 None => Err(format!("field {str_key}: expected integer")),
91 }
92 }
93
94 pub fn extract_u32(map: &[(Value, Value)], int_key: i64, str_key: &str) -> Result<u32, String> {
96 extract_u64(map, int_key, str_key)?
97 .try_into()
98 .map_err(|_| format!("field {str_key}: integer out of u32 range"))
99 }
100
101 pub fn kv_bytes(key: i64, bytes: &[u8]) -> (Value, Value) {
103 (Value::Integer(key.into()), Value::Bytes(bytes.to_vec()))
104 }
105
106 pub fn kv_u64(key: i64, n: u64) -> (Value, Value) {
108 (
109 Value::Integer(key.into()),
110 Value::Integer((n as i64).into()),
111 )
112 }
113
114 pub fn kv_u32(key: i64, n: u32) -> (Value, Value) {
116 kv_u64(key, n as u64)
117 }
118
119 pub fn kv_serde<T: serde::Serialize>(key: i64, val: &T) -> Result<(Value, Value), String> {
121 let cbor_bytes = crate::cbor::to_vec(val).map_err(|e| format!("serialize nested: {e}"))?;
123 let v: Value =
124 crate::cbor::from_slice(&cbor_bytes).map_err(|e| format!("parse nested value: {e}"))?;
125 Ok((Value::Integer(key.into()), v))
126 }
127
128 pub fn deser_value<T: serde::de::DeserializeOwned>(
130 map: &[(Value, Value)],
131 int_key: i64,
132 str_key: &str,
133 ) -> Result<T, String> {
134 let val =
135 find_field(map, int_key, str_key).ok_or_else(|| format!("missing field {str_key}"))?;
136 let cbor_bytes = crate::cbor::to_vec(val)
138 .map_err(|e| format!("field {str_key}: serialize for deser: {e}"))?;
139 crate::cbor::from_slice(&cbor_bytes)
140 .map_err(|e| format!("field {str_key}: deserialize: {e}"))
141 }
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct Envelope {
146 pub r#type: u16,
147 pub req_id: u32,
148 pub flags: u16,
149 #[serde(with = "serde_bytes")]
150 pub payload: Vec<u8>,
151}
152
153pub const FLAG_RESPONSE: u16 = 0x0001;
154pub const FLAG_ERROR: u16 = 0x0002;
155
156pub const MAX_ENVELOPE_BYTES: usize = 2 * 1024 * 1024;
158pub const MAX_ENVELOPE_PAYLOAD_BYTES: usize = 1024 * 1024;
160
161impl Envelope {
162 pub fn encode(&self) -> anyhow::Result<Vec<u8>> {
163 Ok(crate::cbor::to_vec(self)?)
164 }
165
166 pub fn decode(bytes: &[u8]) -> anyhow::Result<Self> {
167 Self::decode_with_limits(bytes, MAX_ENVELOPE_BYTES, MAX_ENVELOPE_PAYLOAD_BYTES)
168 }
169
170 pub fn decode_with_limits(
171 bytes: &[u8],
172 max_envelope_bytes: usize,
173 max_payload_bytes: usize,
174 ) -> anyhow::Result<Self> {
175 if bytes.len() > max_envelope_bytes {
176 anyhow::bail!(
177 "envelope exceeds max size: {} > {}",
178 bytes.len(),
179 max_envelope_bytes
180 );
181 }
182
183 let envelope: Self = crate::cbor::from_slice(bytes)?;
184 if envelope.payload.len() > max_payload_bytes {
185 anyhow::bail!(
186 "envelope payload exceeds max size: {} > {}",
187 envelope.payload.len(),
188 max_payload_bytes
189 );
190 }
191 Ok(envelope)
192 }
193
194 pub fn decode_typed(&self) -> anyhow::Result<WirePayload> {
196 WirePayload::decode(self.r#type, &self.payload)
197 }
198
199 pub fn from_typed(req_id: u32, flags: u16, payload: &WirePayload) -> anyhow::Result<Self> {
201 Ok(Self {
202 r#type: u16::from(payload.msg_type()),
203 req_id,
204 flags,
205 payload: payload.encode()?,
206 })
207 }
208}
209
210#[repr(u16)]
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
212pub enum MsgType {
213 PexOffer = 100,
215 PexRequest = 101,
217 FindNode = 200,
219 FindValue = 201,
221 Store = 202,
223 GetManifest = 400,
225 ManifestData = 401,
227 ListPublicShares = 402,
229 PublicShareList = 403,
231 GetCommunityStatus = 404,
233 CommunityStatus = 405,
235 ListCommunityPublicShares = 406,
237 CommunityPublicShareList = 407,
239 RelayRegister = 450,
241 RelayRegistered = 451,
243 RelayConnect = 452,
245 RelayStream = 453,
247 Providers = 498,
249 HaveContent = 499,
251 GetChunk = 500,
253 ChunkData = 501,
255 GetChunkHashes = 502,
257 ChunkHashList = 503,
259 RelayListRequest = 460,
261 RelayListResponse = 461,
263}
264
265impl MsgType {
266 pub const ALL: [Self; 25] = [
268 Self::PexOffer,
269 Self::PexRequest,
270 Self::FindNode,
271 Self::FindValue,
272 Self::Store,
273 Self::GetManifest,
274 Self::ManifestData,
275 Self::ListPublicShares,
276 Self::PublicShareList,
277 Self::GetCommunityStatus,
278 Self::CommunityStatus,
279 Self::ListCommunityPublicShares,
280 Self::CommunityPublicShareList,
281 Self::RelayRegister,
282 Self::RelayRegistered,
283 Self::RelayConnect,
284 Self::RelayStream,
285 Self::RelayListRequest,
286 Self::RelayListResponse,
287 Self::Providers,
288 Self::HaveContent,
289 Self::GetChunk,
290 Self::ChunkData,
291 Self::GetChunkHashes,
292 Self::ChunkHashList,
293 ];
294}
295
296impl From<MsgType> for u16 {
297 fn from(value: MsgType) -> Self {
298 value as u16
299 }
300}
301
302impl TryFrom<u16> for MsgType {
303 type Error = anyhow::Error;
304
305 fn try_from(value: u16) -> Result<Self, Self::Error> {
306 match value {
307 100 => Ok(Self::PexOffer),
308 101 => Ok(Self::PexRequest),
309 200 => Ok(Self::FindNode),
310 201 => Ok(Self::FindValue),
311 202 => Ok(Self::Store),
312 400 => Ok(Self::GetManifest),
313 401 => Ok(Self::ManifestData),
314 402 => Ok(Self::ListPublicShares),
315 403 => Ok(Self::PublicShareList),
316 404 => Ok(Self::GetCommunityStatus),
317 405 => Ok(Self::CommunityStatus),
318 406 => Ok(Self::ListCommunityPublicShares),
319 407 => Ok(Self::CommunityPublicShareList),
320 450 => Ok(Self::RelayRegister),
321 451 => Ok(Self::RelayRegistered),
322 452 => Ok(Self::RelayConnect),
323 453 => Ok(Self::RelayStream),
324 460 => Ok(Self::RelayListRequest),
325 461 => Ok(Self::RelayListResponse),
326 498 => Ok(Self::Providers),
327 499 => Ok(Self::HaveContent),
328 500 => Ok(Self::GetChunk),
329 501 => Ok(Self::ChunkData),
330 502 => Ok(Self::GetChunkHashes),
331 503 => Ok(Self::ChunkHashList),
332 _ => anyhow::bail!("unknown message type {value}"),
333 }
334 }
335}
336
337#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
338pub struct PexOffer {
339 pub peers: Vec<PeerAddr>,
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
343pub struct PexRequest {
344 pub max_peers: u8,
345}
346
347#[derive(Debug, Clone, PartialEq, Eq)]
349pub struct FindNode {
350 pub target_node_id: [u8; 20],
351}
352
353impl Serialize for FindNode {
354 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
355 ciborium::Value::Map(vec![int_cbor::kv_bytes(0, &self.target_node_id)])
356 .serialize(serializer)
357 }
358}
359
360impl<'de> Deserialize<'de> for FindNode {
361 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
362 let val = ciborium::Value::deserialize(deserializer)?;
363 let map = int_cbor::into_map(val).map_err(serde::de::Error::custom)?;
364 Ok(FindNode {
365 target_node_id: int_cbor::extract_byte_array(&map, 0, "target_node_id")
366 .map_err(serde::de::Error::custom)?,
367 })
368 }
369}
370
371#[derive(Debug, Clone, PartialEq, Eq)]
373pub struct FindNodeResult {
374 pub peers: Vec<PeerAddr>,
375}
376
377impl Serialize for FindNodeResult {
378 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
379 ciborium::Value::Map(vec![
380 int_cbor::kv_serde(0, &self.peers).map_err(serde::ser::Error::custom)?,
381 ])
382 .serialize(serializer)
383 }
384}
385
386impl<'de> Deserialize<'de> for FindNodeResult {
387 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
388 let val = ciborium::Value::deserialize(deserializer)?;
389 let map = int_cbor::into_map(val).map_err(serde::de::Error::custom)?;
390 Ok(FindNodeResult {
391 peers: int_cbor::deser_value(&map, 0, "peers").map_err(serde::de::Error::custom)?,
392 })
393 }
394}
395
396#[derive(Debug, Clone, PartialEq, Eq)]
398pub struct FindValue {
399 pub key: [u8; 32],
400}
401
402impl Serialize for FindValue {
403 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
404 ciborium::Value::Map(vec![int_cbor::kv_bytes(0, &self.key)]).serialize(serializer)
405 }
406}
407
408impl<'de> Deserialize<'de> for FindValue {
409 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
410 let val = ciborium::Value::deserialize(deserializer)?;
411 let map = int_cbor::into_map(val).map_err(serde::de::Error::custom)?;
412 Ok(FindValue {
413 key: int_cbor::extract_byte_array(&map, 0, "key").map_err(serde::de::Error::custom)?,
414 })
415 }
416}
417
418#[derive(Debug, Clone, PartialEq, Eq)]
420pub struct Store {
421 pub key: [u8; 32],
422 pub value: Vec<u8>,
423 pub ttl_secs: u64,
424}
425
426impl Serialize for Store {
427 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
428 ciborium::Value::Map(vec![
429 int_cbor::kv_bytes(0, &self.key),
430 int_cbor::kv_bytes(1, &self.value),
431 int_cbor::kv_u64(2, self.ttl_secs),
432 ])
433 .serialize(serializer)
434 }
435}
436
437impl<'de> Deserialize<'de> for Store {
438 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
439 let val = ciborium::Value::deserialize(deserializer)?;
440 let map = int_cbor::into_map(val).map_err(serde::de::Error::custom)?;
441 Ok(Store {
442 key: int_cbor::extract_byte_array(&map, 0, "key").map_err(serde::de::Error::custom)?,
443 value: int_cbor::extract_bytes(&map, 1, "value").map_err(serde::de::Error::custom)?,
444 ttl_secs: int_cbor::extract_u64(&map, 2, "ttl_secs")
445 .map_err(serde::de::Error::custom)?,
446 })
447 }
448}
449
450#[derive(Debug, Clone, PartialEq, Eq)]
452pub struct FindValueResult {
453 pub value: Option<Store>,
454 pub closer_peers: Vec<PeerAddr>,
455}
456
457impl Serialize for FindValueResult {
458 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
459 let val_entry = match &self.value {
460 Some(s) => int_cbor::kv_serde(0, s).map_err(serde::ser::Error::custom)?,
461 None => (ciborium::Value::Integer(0.into()), ciborium::Value::Null),
462 };
463 ciborium::Value::Map(vec![
464 val_entry,
465 int_cbor::kv_serde(1, &self.closer_peers).map_err(serde::ser::Error::custom)?,
466 ])
467 .serialize(serializer)
468 }
469}
470
471impl<'de> Deserialize<'de> for FindValueResult {
472 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
473 let val = ciborium::Value::deserialize(deserializer)?;
474 let map = int_cbor::into_map(val).map_err(serde::de::Error::custom)?;
475 let value_field = int_cbor::find_field(&map, 0, "value");
476 let value = match value_field {
477 Some(ciborium::Value::Null) | None => None,
478 Some(_) => {
479 Some(int_cbor::deser_value(&map, 0, "value").map_err(serde::de::Error::custom)?)
480 }
481 };
482 Ok(FindValueResult {
483 value,
484 closer_peers: int_cbor::deser_value(&map, 1, "closer_peers")
485 .map_err(serde::de::Error::custom)?,
486 })
487 }
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
491pub struct GetManifest {
492 pub manifest_id: [u8; 32],
493}
494
495#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
496pub struct ManifestData {
497 pub manifest_id: [u8; 32],
498 #[serde(with = "serde_bytes")]
499 pub bytes: Vec<u8>,
500}
501
502#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
503pub struct ListPublicShares {
504 pub max_entries: u16,
505}
506
507#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
508pub struct PublicShareList {
509 pub shares: Vec<PublicShareSummary>,
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
513pub struct GetCommunityStatus {
514 pub community_share_id: [u8; 32],
515 pub community_share_pubkey: [u8; 32],
516}
517
518#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
519pub struct CommunityStatus {
520 pub community_share_id: [u8; 32],
521 pub joined: bool,
522 #[serde(default, with = "serde_bytes")]
526 pub membership_proof: Option<Vec<u8>>,
527 #[serde(default, skip_serializing_if = "Option::is_none")]
529 pub name: Option<String>,
530}
531
532#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
533pub struct ListCommunityPublicShares {
534 pub community_share_id: [u8; 32],
535 pub community_share_pubkey: [u8; 32],
536 pub max_entries: u16,
537 #[serde(default, skip_serializing_if = "Option::is_none")]
542 pub requester_node_pubkey: Option<[u8; 32]>,
543 #[serde(default, skip_serializing_if = "Option::is_none")]
548 pub requester_membership_proof: Option<Vec<u8>>,
549}
550
551#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
552pub struct CommunityPublicShareList {
553 pub community_share_id: [u8; 32],
554 pub shares: Vec<PublicShareSummary>,
555}
556
557#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
558pub struct RelayRegister {
559 #[serde(default)]
560 pub relay_slot_id: Option<u64>,
561 #[serde(default)]
566 pub tunnel: bool,
567}
568
569#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
570pub struct RelayRegistered {
571 pub relay_slot_id: u64,
572 pub expires_at: u64,
573}
574
575#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
576pub struct RelayConnect {
577 pub relay_slot_id: u64,
578}
579
580#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
581#[serde(rename_all = "lowercase")]
582pub enum RelayPayloadKind {
583 #[default]
584 Control,
585 Content,
586}
587
588#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
589pub struct RelayStream {
590 pub relay_slot_id: u64,
591 pub stream_id: u32,
592 #[serde(default)]
593 pub kind: RelayPayloadKind,
594 #[serde(with = "serde_bytes")]
595 pub payload: Vec<u8>,
596}
597
598#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
599pub struct HaveContent {
600 pub content_id: [u8; 32],
601}
602
603#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
604pub struct Providers {
605 pub content_id: [u8; 32],
606 pub providers: Vec<PeerAddr>,
607 pub updated_at: u64,
608}
609
610#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
616pub struct CommunityMembers {
617 pub community_share_id: [u8; 32],
618 pub members: Vec<PeerAddr>,
619 pub updated_at: u64,
620}
621
622#[derive(Debug, Clone, PartialEq, Eq)]
624pub struct GetChunk {
625 pub content_id: [u8; 32],
626 pub chunk_index: u32,
627}
628
629impl Serialize for GetChunk {
630 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
631 ciborium::Value::Map(vec![
632 int_cbor::kv_bytes(0, &self.content_id),
633 int_cbor::kv_u32(1, self.chunk_index),
634 ])
635 .serialize(serializer)
636 }
637}
638
639impl<'de> Deserialize<'de> for GetChunk {
640 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
641 let val = ciborium::Value::deserialize(deserializer)?;
642 let map = int_cbor::into_map(val).map_err(serde::de::Error::custom)?;
643 Ok(GetChunk {
644 content_id: int_cbor::extract_byte_array(&map, 0, "content_id")
645 .map_err(serde::de::Error::custom)?,
646 chunk_index: int_cbor::extract_u32(&map, 1, "chunk_index")
647 .map_err(serde::de::Error::custom)?,
648 })
649 }
650}
651
652#[derive(Debug, Clone, PartialEq, Eq)]
654pub struct ChunkData {
655 pub content_id: [u8; 32],
656 pub chunk_index: u32,
657 pub bytes: Vec<u8>,
658}
659
660impl Serialize for ChunkData {
661 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
662 ciborium::Value::Map(vec![
663 int_cbor::kv_bytes(0, &self.content_id),
664 int_cbor::kv_u32(1, self.chunk_index),
665 int_cbor::kv_bytes(2, &self.bytes),
666 ])
667 .serialize(serializer)
668 }
669}
670
671impl<'de> Deserialize<'de> for ChunkData {
672 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
673 let val = ciborium::Value::deserialize(deserializer)?;
674 let map = int_cbor::into_map(val).map_err(serde::de::Error::custom)?;
675 Ok(ChunkData {
676 content_id: int_cbor::extract_byte_array(&map, 0, "content_id")
677 .map_err(serde::de::Error::custom)?,
678 chunk_index: int_cbor::extract_u32(&map, 1, "chunk_index")
679 .map_err(serde::de::Error::custom)?,
680 bytes: int_cbor::extract_bytes(&map, 2, "bytes").map_err(serde::de::Error::custom)?,
681 })
682 }
683}
684
685#[derive(Debug, Clone, PartialEq, Eq)]
687pub struct GetChunkHashes {
688 pub content_id: [u8; 32],
689}
690
691impl Serialize for GetChunkHashes {
692 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
693 ciborium::Value::Map(vec![int_cbor::kv_bytes(0, &self.content_id)]).serialize(serializer)
694 }
695}
696
697impl<'de> Deserialize<'de> for GetChunkHashes {
698 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
699 let val = ciborium::Value::deserialize(deserializer)?;
700 let map = int_cbor::into_map(val).map_err(serde::de::Error::custom)?;
701 Ok(GetChunkHashes {
702 content_id: int_cbor::extract_byte_array(&map, 0, "content_id")
703 .map_err(serde::de::Error::custom)?,
704 })
705 }
706}
707
708#[derive(Debug, Clone, PartialEq, Eq)]
710pub struct ChunkHashList {
711 pub content_id: [u8; 32],
712 pub hashes: Vec<[u8; 32]>,
713}
714
715impl Serialize for ChunkHashList {
716 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
717 ciborium::Value::Map(vec![
718 int_cbor::kv_bytes(0, &self.content_id),
719 int_cbor::kv_serde(1, &self.hashes).map_err(serde::ser::Error::custom)?,
720 ])
721 .serialize(serializer)
722 }
723}
724
725impl<'de> Deserialize<'de> for ChunkHashList {
726 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
727 let val = ciborium::Value::deserialize(deserializer)?;
728 let map = int_cbor::into_map(val).map_err(serde::de::Error::custom)?;
729 Ok(ChunkHashList {
730 content_id: int_cbor::extract_byte_array(&map, 0, "content_id")
731 .map_err(serde::de::Error::custom)?,
732 hashes: int_cbor::deser_value(&map, 1, "hashes").map_err(serde::de::Error::custom)?,
733 })
734 }
735}
736
737#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
739pub struct RelayListRequest {
740 pub max_count: u16,
742}
743
744#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
749pub struct RelayListResponse {
750 pub announcements: Vec<crate::relay::RelayAnnouncement>,
751}
752
753#[derive(Debug, Clone, PartialEq, Eq)]
755pub enum WirePayload {
756 PexOffer(PexOffer),
757 PexRequest(PexRequest),
758 FindNode(FindNode),
759 FindValue(FindValue),
760 Store(Store),
761 GetManifest(GetManifest),
762 ManifestData(ManifestData),
763 ListPublicShares(ListPublicShares),
764 PublicShareList(PublicShareList),
765 GetCommunityStatus(GetCommunityStatus),
766 CommunityStatus(CommunityStatus),
767 ListCommunityPublicShares(ListCommunityPublicShares),
768 CommunityPublicShareList(CommunityPublicShareList),
769 RelayRegister(RelayRegister),
770 RelayRegistered(RelayRegistered),
771 RelayConnect(RelayConnect),
772 RelayStream(RelayStream),
773 RelayListRequest(RelayListRequest),
774 RelayListResponse(RelayListResponse),
775 Providers(Providers),
776 HaveContent(HaveContent),
777 GetChunk(GetChunk),
778 ChunkData(ChunkData),
779 GetChunkHashes(GetChunkHashes),
780 ChunkHashList(ChunkHashList),
781}
782
783impl WirePayload {
784 pub fn msg_type(&self) -> MsgType {
785 match self {
786 Self::PexOffer(_) => MsgType::PexOffer,
787 Self::PexRequest(_) => MsgType::PexRequest,
788 Self::FindNode(_) => MsgType::FindNode,
789 Self::FindValue(_) => MsgType::FindValue,
790 Self::Store(_) => MsgType::Store,
791 Self::GetManifest(_) => MsgType::GetManifest,
792 Self::ManifestData(_) => MsgType::ManifestData,
793 Self::ListPublicShares(_) => MsgType::ListPublicShares,
794 Self::PublicShareList(_) => MsgType::PublicShareList,
795 Self::GetCommunityStatus(_) => MsgType::GetCommunityStatus,
796 Self::CommunityStatus(_) => MsgType::CommunityStatus,
797 Self::ListCommunityPublicShares(_) => MsgType::ListCommunityPublicShares,
798 Self::CommunityPublicShareList(_) => MsgType::CommunityPublicShareList,
799 Self::RelayRegister(_) => MsgType::RelayRegister,
800 Self::RelayRegistered(_) => MsgType::RelayRegistered,
801 Self::RelayConnect(_) => MsgType::RelayConnect,
802 Self::RelayStream(_) => MsgType::RelayStream,
803 Self::RelayListRequest(_) => MsgType::RelayListRequest,
804 Self::RelayListResponse(_) => MsgType::RelayListResponse,
805 Self::Providers(_) => MsgType::Providers,
806 Self::HaveContent(_) => MsgType::HaveContent,
807 Self::GetChunk(_) => MsgType::GetChunk,
808 Self::ChunkData(_) => MsgType::ChunkData,
809 Self::GetChunkHashes(_) => MsgType::GetChunkHashes,
810 Self::ChunkHashList(_) => MsgType::ChunkHashList,
811 }
812 }
813
814 pub fn encode(&self) -> anyhow::Result<Vec<u8>> {
815 Ok(match self {
816 Self::PexOffer(msg) => crate::cbor::to_vec(msg)?,
817 Self::PexRequest(msg) => crate::cbor::to_vec(msg)?,
818 Self::FindNode(msg) => crate::cbor::to_vec(msg)?,
819 Self::FindValue(msg) => crate::cbor::to_vec(msg)?,
820 Self::Store(msg) => crate::cbor::to_vec(msg)?,
821 Self::GetManifest(msg) => crate::cbor::to_vec(msg)?,
822 Self::ManifestData(msg) => crate::cbor::to_vec(msg)?,
823 Self::ListPublicShares(msg) => crate::cbor::to_vec(msg)?,
824 Self::PublicShareList(msg) => crate::cbor::to_vec(msg)?,
825 Self::GetCommunityStatus(msg) => crate::cbor::to_vec(msg)?,
826 Self::CommunityStatus(msg) => crate::cbor::to_vec(msg)?,
827 Self::ListCommunityPublicShares(msg) => crate::cbor::to_vec(msg)?,
828 Self::CommunityPublicShareList(msg) => crate::cbor::to_vec(msg)?,
829 Self::RelayRegister(msg) => crate::cbor::to_vec(msg)?,
830 Self::RelayRegistered(msg) => crate::cbor::to_vec(msg)?,
831 Self::RelayConnect(msg) => crate::cbor::to_vec(msg)?,
832 Self::RelayStream(msg) => crate::cbor::to_vec(msg)?,
833 Self::RelayListRequest(msg) => crate::cbor::to_vec(msg)?,
834 Self::RelayListResponse(msg) => crate::cbor::to_vec(msg)?,
835 Self::Providers(msg) => crate::cbor::to_vec(msg)?,
836 Self::HaveContent(msg) => crate::cbor::to_vec(msg)?,
837 Self::GetChunk(msg) => crate::cbor::to_vec(msg)?,
838 Self::ChunkData(msg) => crate::cbor::to_vec(msg)?,
839 Self::GetChunkHashes(msg) => crate::cbor::to_vec(msg)?,
840 Self::ChunkHashList(msg) => crate::cbor::to_vec(msg)?,
841 })
842 }
843
844 pub fn decode(message_type: u16, payload: &[u8]) -> anyhow::Result<Self> {
845 let msg_type = MsgType::try_from(message_type)?;
846 Ok(match msg_type {
847 MsgType::PexOffer => Self::PexOffer(crate::cbor::from_slice(payload)?),
848 MsgType::PexRequest => Self::PexRequest(crate::cbor::from_slice(payload)?),
849 MsgType::FindNode => Self::FindNode(crate::cbor::from_slice(payload)?),
850 MsgType::FindValue => Self::FindValue(crate::cbor::from_slice(payload)?),
851 MsgType::Store => Self::Store(crate::cbor::from_slice(payload)?),
852 MsgType::GetManifest => Self::GetManifest(crate::cbor::from_slice(payload)?),
853 MsgType::ManifestData => Self::ManifestData(crate::cbor::from_slice(payload)?),
854 MsgType::ListPublicShares => Self::ListPublicShares(crate::cbor::from_slice(payload)?),
855 MsgType::PublicShareList => Self::PublicShareList(crate::cbor::from_slice(payload)?),
856 MsgType::GetCommunityStatus => {
857 Self::GetCommunityStatus(crate::cbor::from_slice(payload)?)
858 }
859 MsgType::CommunityStatus => Self::CommunityStatus(crate::cbor::from_slice(payload)?),
860 MsgType::ListCommunityPublicShares => {
861 Self::ListCommunityPublicShares(crate::cbor::from_slice(payload)?)
862 }
863 MsgType::CommunityPublicShareList => {
864 Self::CommunityPublicShareList(crate::cbor::from_slice(payload)?)
865 }
866 MsgType::RelayRegister => Self::RelayRegister(crate::cbor::from_slice(payload)?),
867 MsgType::RelayRegistered => Self::RelayRegistered(crate::cbor::from_slice(payload)?),
868 MsgType::RelayConnect => Self::RelayConnect(crate::cbor::from_slice(payload)?),
869 MsgType::RelayStream => Self::RelayStream(crate::cbor::from_slice(payload)?),
870 MsgType::RelayListRequest => Self::RelayListRequest(crate::cbor::from_slice(payload)?),
871 MsgType::RelayListResponse => {
872 Self::RelayListResponse(crate::cbor::from_slice(payload)?)
873 }
874 MsgType::Providers => Self::Providers(crate::cbor::from_slice(payload)?),
875 MsgType::HaveContent => Self::HaveContent(crate::cbor::from_slice(payload)?),
876 MsgType::GetChunk => Self::GetChunk(crate::cbor::from_slice(payload)?),
877 MsgType::ChunkData => Self::ChunkData(crate::cbor::from_slice(payload)?),
878 MsgType::GetChunkHashes => Self::GetChunkHashes(crate::cbor::from_slice(payload)?),
879 MsgType::ChunkHashList => Self::ChunkHashList(crate::cbor::from_slice(payload)?),
880 })
881 }
882}
883
884#[cfg(test)]
885mod tests {
886 use super::*;
887 use crate::{ids::NodeId, peer::TransportProtocol};
888
889 #[test]
893 fn int_cbor_find_node_uses_integer_keys() {
894 let msg = FindNode {
895 target_node_id: [0xAA; 20],
896 };
897 let bytes = crate::cbor::to_vec(&msg).expect("encode");
898 let val: ciborium::Value = crate::cbor::from_slice(&bytes).expect("parse value");
899 let map = val.as_map().expect("should be map");
900 assert!(
902 map[0].0.as_integer().is_some(),
903 "FindNode key should be integer, got {:?}",
904 map[0].0
905 );
906 assert_eq!(i128::from(map[0].0.as_integer().unwrap()), 0);
907 }
908
909 #[test]
911 fn int_cbor_store_uses_integer_keys() {
912 let msg = Store {
913 key: [0xBB; 32],
914 value: vec![1, 2, 3],
915 ttl_secs: 300,
916 };
917 let bytes = crate::cbor::to_vec(&msg).expect("encode");
918 let val: ciborium::Value = crate::cbor::from_slice(&bytes).expect("parse value");
919 let map = val.as_map().expect("should be map");
920 assert_eq!(map.len(), 3);
921 for (i, (k, _)) in map.iter().enumerate() {
922 let int_key = k.as_integer().expect("key should be integer");
923 assert_eq!(i128::from(int_key), i as i128);
924 }
925 }
926
927 #[test]
929 fn int_cbor_chunk_messages_integer_keys_and_roundtrip() {
930 let get = GetChunk {
931 content_id: [0xEE; 32],
932 chunk_index: 42,
933 };
934 let bytes = crate::cbor::to_vec(&get).expect("encode");
935 let val: ciborium::Value = crate::cbor::from_slice(&bytes).expect("parse");
936 let map = val.as_map().expect("should be map");
937 assert!(map[0].0.as_integer().is_some());
938 assert!(map[1].0.as_integer().is_some());
939 let rt: GetChunk = crate::cbor::from_slice(&bytes).expect("roundtrip");
940 assert_eq!(rt, get);
941
942 let data = ChunkData {
943 content_id: [0xFF; 32],
944 chunk_index: 7,
945 bytes: vec![10, 20, 30],
946 };
947 let data_bytes = crate::cbor::to_vec(&data).expect("encode");
948 let data_val: ciborium::Value = crate::cbor::from_slice(&data_bytes).expect("parse");
949 let data_map = data_val.as_map().expect("should be map");
950 assert_eq!(data_map.len(), 3);
951 for (k, _) in data_map {
952 assert!(k.as_integer().is_some(), "all keys should be integers");
953 }
954 let data_rt: ChunkData = crate::cbor::from_slice(&data_bytes).expect("roundtrip");
955 assert_eq!(data_rt, data);
956 }
957
958 #[test]
959 fn envelope_roundtrip() {
960 let payload = PexRequest { max_peers: 32 };
961 let envelope = Envelope {
962 r#type: MsgType::PexRequest as u16,
963 req_id: 7,
964 flags: 0,
965 payload: crate::cbor::to_vec(&payload).expect("encode payload"),
966 };
967
968 let encoded = envelope.encode().expect("encode envelope");
969 let decoded = Envelope::decode(&encoded).expect("decode envelope");
970 let decoded_payload: PexRequest =
971 crate::cbor::from_slice(&decoded.payload).expect("decode payload");
972
973 assert_eq!(decoded.r#type, MsgType::PexRequest as u16);
974 assert_eq!(decoded_payload, payload);
975 }
976
977 #[test]
978 fn pex_offer_roundtrip() {
979 let offer = PexOffer {
980 peers: vec![PeerAddr {
981 ip: "10.0.0.5".parse().expect("valid ip"),
982 port: 7001,
983 transport: TransportProtocol::Tcp,
984 pubkey_hint: Some([1u8; 32]),
985 relay_via: None,
986 }],
987 };
988
989 let encoded = crate::cbor::to_vec(&offer).expect("encode pex offer");
990 let decoded: PexOffer = crate::cbor::from_slice(&encoded).expect("decode pex offer");
991 assert_eq!(decoded, offer);
992 }
993
994 #[test]
995 fn dht_messages_roundtrip() {
996 let find_node = FindNode {
997 target_node_id: NodeId([1u8; 20]).0,
998 };
999 let find_node_roundtrip: FindNode =
1000 crate::cbor::from_slice(&crate::cbor::to_vec(&find_node).expect("encode find node"))
1001 .expect("decode find node");
1002 assert_eq!(find_node_roundtrip, find_node);
1003
1004 let store = Store {
1005 key: [9u8; 32],
1006 value: vec![1, 2, 3],
1007 ttl_secs: 60,
1008 };
1009 let find_value_result = FindValueResult {
1010 value: Some(store),
1011 closer_peers: vec![PeerAddr {
1012 ip: "192.168.1.2".parse().expect("valid ip"),
1013 port: 7000,
1014 transport: TransportProtocol::Quic,
1015 pubkey_hint: None,
1016 relay_via: None,
1017 }],
1018 };
1019
1020 let encoded = crate::cbor::to_vec(&find_value_result).expect("encode find value result");
1021 let decoded: FindValueResult =
1022 crate::cbor::from_slice(&encoded).expect("decode find value result");
1023 assert_eq!(decoded, find_value_result);
1024 }
1025
1026 #[test]
1027 fn relay_messages_roundtrip() {
1028 let reg = RelayRegister {
1029 relay_slot_id: Some(42),
1030 tunnel: false,
1031 };
1032 let reg_rt: RelayRegister =
1033 crate::cbor::from_slice(&crate::cbor::to_vec(®).expect("encode relay register"))
1034 .expect("decode relay register");
1035 assert_eq!(reg_rt, reg);
1036
1037 let reg_empty: RelayRegister = crate::cbor::from_slice(
1038 &crate::cbor::to_vec(&crate::cbor::Value::Map(Vec::new()))
1039 .expect("encode legacy empty register map"),
1040 )
1041 .expect("decode legacy empty register map");
1042 assert_eq!(reg_empty.relay_slot_id, None);
1043
1044 let registered = RelayRegistered {
1045 relay_slot_id: 7,
1046 expires_at: 99,
1047 };
1048 let registered_rt: RelayRegistered = crate::cbor::from_slice(
1049 &crate::cbor::to_vec(®istered).expect("encode relay registered"),
1050 )
1051 .expect("decode relay registered");
1052 assert_eq!(registered_rt, registered);
1053
1054 let stream = RelayStream {
1055 relay_slot_id: 7,
1056 stream_id: 1,
1057 kind: RelayPayloadKind::Control,
1058 payload: vec![1, 2, 3],
1059 };
1060 let stream_rt: RelayStream =
1061 crate::cbor::from_slice(&crate::cbor::to_vec(&stream).expect("encode relay stream"))
1062 .expect("decode relay stream");
1063 assert_eq!(stream_rt, stream);
1064
1065 assert!(
1066 crate::cbor::from_slice::<RelayStream>(
1067 &crate::cbor::to_vec(&(7u64, 9u32, serde_bytes::ByteBuf::from(vec![8u8, 7])))
1068 .expect("encode legacy stream tuple"),
1069 )
1070 .is_err(),
1071 "legacy tuple encoding should not decode as struct"
1072 );
1073 }
1074
1075 #[test]
1076 fn provider_messages_roundtrip() {
1077 let have = HaveContent {
1078 content_id: [4u8; 32],
1079 };
1080 let have_rt: HaveContent =
1081 crate::cbor::from_slice(&crate::cbor::to_vec(&have).expect("encode have"))
1082 .expect("decode have");
1083 assert_eq!(have_rt, have);
1084
1085 let prov = Providers {
1086 content_id: [4u8; 32],
1087 providers: vec![PeerAddr {
1088 ip: "10.1.0.2".parse().expect("valid ip"),
1089 port: 7777,
1090 transport: TransportProtocol::Quic,
1091 pubkey_hint: None,
1092 relay_via: None,
1093 }],
1094 updated_at: 123,
1095 };
1096 let prov_rt: Providers =
1097 crate::cbor::from_slice(&crate::cbor::to_vec(&prov).expect("encode providers"))
1098 .expect("decode providers");
1099 assert_eq!(prov_rt, prov);
1100 }
1101
1102 #[test]
1103 fn public_share_messages_roundtrip() {
1104 let request = ListPublicShares { max_entries: 25 };
1105 let request_rt: ListPublicShares = crate::cbor::from_slice(
1106 &crate::cbor::to_vec(&request).expect("encode public list req"),
1107 )
1108 .expect("decode public list req");
1109 assert_eq!(request_rt, request);
1110
1111 let response = PublicShareList {
1112 shares: vec![PublicShareSummary {
1113 share_id: [1u8; 32],
1114 share_pubkey: [2u8; 32],
1115 latest_seq: 7,
1116 latest_manifest_id: [3u8; 32],
1117 title: Some("Public Catalog".into()),
1118 description: Some("shared".into()),
1119 }],
1120 };
1121 let response_rt: PublicShareList = crate::cbor::from_slice(
1122 &crate::cbor::to_vec(&response).expect("encode public list response"),
1123 )
1124 .expect("decode public list response");
1125 assert_eq!(response_rt, response);
1126 }
1127
1128 #[test]
1129 fn community_status_messages_roundtrip() {
1130 let request = GetCommunityStatus {
1131 community_share_id: [4u8; 32],
1132 community_share_pubkey: [5u8; 32],
1133 };
1134 let request_rt: GetCommunityStatus =
1135 crate::cbor::from_slice(&crate::cbor::to_vec(&request).expect("encode"))
1136 .expect("decode");
1137 assert_eq!(request_rt, request);
1138
1139 let response = CommunityStatus {
1140 community_share_id: [4u8; 32],
1141 joined: true,
1142 membership_proof: None,
1143 name: None,
1144 };
1145 let response_rt: CommunityStatus =
1146 crate::cbor::from_slice(&crate::cbor::to_vec(&response).expect("encode"))
1147 .expect("decode");
1148 assert_eq!(response_rt, response);
1149
1150 let response_with_name = CommunityStatus {
1152 community_share_id: [4u8; 32],
1153 joined: true,
1154 membership_proof: None,
1155 name: Some("Pavlikeni".to_string()),
1156 };
1157 let rt2: CommunityStatus =
1158 crate::cbor::from_slice(&crate::cbor::to_vec(&response_with_name).expect("encode"))
1159 .expect("decode");
1160 assert_eq!(rt2.name, Some("Pavlikeni".to_string()));
1161 }
1162
1163 #[test]
1164 fn community_public_share_messages_roundtrip() {
1165 let request = ListCommunityPublicShares {
1166 community_share_id: [6u8; 32],
1167 community_share_pubkey: [7u8; 32],
1168 max_entries: 12,
1169 requester_node_pubkey: None,
1170 requester_membership_proof: None,
1171 };
1172 let request_rt: ListCommunityPublicShares =
1173 crate::cbor::from_slice(&crate::cbor::to_vec(&request).expect("encode"))
1174 .expect("decode");
1175 assert_eq!(request_rt, request);
1176
1177 let response = CommunityPublicShareList {
1178 community_share_id: [6u8; 32],
1179 shares: vec![PublicShareSummary {
1180 share_id: [8u8; 32],
1181 share_pubkey: [9u8; 32],
1182 latest_seq: 2,
1183 latest_manifest_id: [10u8; 32],
1184 title: Some("Community Public".into()),
1185 description: None,
1186 }],
1187 };
1188 let response_rt: CommunityPublicShareList =
1189 crate::cbor::from_slice(&crate::cbor::to_vec(&response).expect("encode"))
1190 .expect("decode");
1191 assert_eq!(response_rt, response);
1192 }
1193
1194 #[test]
1195 fn chunk_messages_roundtrip() {
1196 let get = GetChunk {
1197 content_id: [9u8; 32],
1198 chunk_index: 3,
1199 };
1200 let get_encoded = crate::cbor::to_vec(&get).expect("encode get chunk");
1201 let get_decoded: GetChunk =
1202 crate::cbor::from_slice(&get_encoded).expect("decode get chunk");
1203 assert_eq!(get_decoded, get);
1204
1205 let data = ChunkData {
1206 content_id: [9u8; 32],
1207 chunk_index: 3,
1208 bytes: vec![1, 2, 3],
1209 };
1210 let data_encoded = crate::cbor::to_vec(&data).expect("encode chunk data");
1211 let data_decoded: ChunkData =
1212 crate::cbor::from_slice(&data_encoded).expect("decode chunk data");
1213 assert_eq!(data_decoded, data);
1214 }
1215
1216 #[test]
1217 fn chunk_hash_messages_roundtrip() {
1218 let get = GetChunkHashes {
1219 content_id: [11u8; 32],
1220 };
1221 let get_rt: GetChunkHashes =
1222 crate::cbor::from_slice(&crate::cbor::to_vec(&get).expect("encode")).expect("decode");
1223 assert_eq!(get_rt, get);
1224
1225 let list = ChunkHashList {
1226 content_id: [11u8; 32],
1227 hashes: vec![[1u8; 32], [2u8; 32]],
1228 };
1229 let list_rt: ChunkHashList =
1230 crate::cbor::from_slice(&crate::cbor::to_vec(&list).expect("encode")).expect("decode");
1231 assert_eq!(list_rt, list);
1232 }
1233
1234 #[test]
1235 fn msg_type_registry_roundtrip_and_unique_values() {
1236 let mut sorted_values = MsgType::ALL
1237 .iter()
1238 .copied()
1239 .map(u16::from)
1240 .collect::<Vec<u16>>();
1241
1242 for msg_type in MsgType::ALL {
1243 let wire_value = u16::from(msg_type);
1244 let roundtrip = MsgType::try_from(wire_value).expect("registry roundtrip");
1245 assert_eq!(roundtrip, msg_type);
1246 }
1247
1248 let expected_len = sorted_values.len();
1249 sorted_values.sort_unstable();
1250 sorted_values.dedup();
1251 assert_eq!(sorted_values.len(), expected_len);
1252 }
1253
1254 #[test]
1255 fn envelope_decode_rejects_large_payload_limit() {
1256 let envelope = Envelope {
1257 r#type: MsgType::ChunkData as u16,
1258 req_id: 9,
1259 flags: 0,
1260 payload: vec![7u8; 32],
1261 };
1262 let encoded = envelope.encode().expect("encode envelope");
1263
1264 let err = Envelope::decode_with_limits(&encoded, 1024, 16)
1265 .expect_err("payload limit should reject envelope");
1266 assert!(err.to_string().contains("payload exceeds max size"));
1267 }
1268
1269 #[test]
1270 fn envelope_decode_rejects_large_serialized_limit() {
1271 let envelope = Envelope {
1272 r#type: MsgType::PexOffer as u16,
1273 req_id: 10,
1274 flags: 0,
1275 payload: vec![1u8; 8],
1276 };
1277 let encoded = envelope.encode().expect("encode envelope");
1278
1279 let err = Envelope::decode_with_limits(&encoded, 2, 1024)
1280 .expect_err("envelope bytes limit should reject envelope");
1281 assert!(err.to_string().contains("envelope exceeds max size"));
1282 }
1283
1284 #[test]
1285 fn typed_payload_dispatch_roundtrip_for_all_registered_types() {
1286 let cases = vec![
1287 WirePayload::PexOffer(PexOffer {
1288 peers: vec![PeerAddr {
1289 ip: "10.0.0.2".parse().expect("valid ip"),
1290 port: 1234,
1291 transport: TransportProtocol::Tcp,
1292 pubkey_hint: None,
1293 relay_via: None,
1294 }],
1295 }),
1296 WirePayload::PexRequest(PexRequest { max_peers: 8 }),
1297 WirePayload::FindNode(FindNode {
1298 target_node_id: NodeId([1u8; 20]).0,
1299 }),
1300 WirePayload::FindValue(FindValue { key: [2u8; 32] }),
1301 WirePayload::Store(Store {
1302 key: [3u8; 32],
1303 value: vec![1, 2, 3],
1304 ttl_secs: 15,
1305 }),
1306 WirePayload::GetManifest(GetManifest {
1307 manifest_id: [4u8; 32],
1308 }),
1309 WirePayload::ManifestData(ManifestData {
1310 manifest_id: [5u8; 32],
1311 bytes: vec![9, 8, 7],
1312 }),
1313 WirePayload::ListPublicShares(ListPublicShares { max_entries: 5 }),
1314 WirePayload::PublicShareList(PublicShareList {
1315 shares: vec![PublicShareSummary {
1316 share_id: [6u8; 32],
1317 share_pubkey: [7u8; 32],
1318 latest_seq: 8,
1319 latest_manifest_id: [9u8; 32],
1320 title: Some("pub".into()),
1321 description: None,
1322 }],
1323 }),
1324 WirePayload::GetCommunityStatus(GetCommunityStatus {
1325 community_share_id: [14u8; 32],
1326 community_share_pubkey: [15u8; 32],
1327 }),
1328 WirePayload::CommunityStatus(CommunityStatus {
1329 community_share_id: [14u8; 32],
1330 joined: true,
1331 membership_proof: None,
1332 name: None,
1333 }),
1334 WirePayload::ListCommunityPublicShares(ListCommunityPublicShares {
1335 community_share_id: [16u8; 32],
1336 community_share_pubkey: [17u8; 32],
1337 max_entries: 4,
1338 requester_node_pubkey: None,
1339 requester_membership_proof: None,
1340 }),
1341 WirePayload::CommunityPublicShareList(CommunityPublicShareList {
1342 community_share_id: [16u8; 32],
1343 shares: vec![PublicShareSummary {
1344 share_id: [18u8; 32],
1345 share_pubkey: [19u8; 32],
1346 latest_seq: 5,
1347 latest_manifest_id: [20u8; 32],
1348 title: Some("community".into()),
1349 description: Some("public".into()),
1350 }],
1351 }),
1352 WirePayload::RelayRegister(RelayRegister {
1353 relay_slot_id: Some(77),
1354 tunnel: false,
1355 }),
1356 WirePayload::RelayRegistered(RelayRegistered {
1357 relay_slot_id: 77,
1358 expires_at: 88,
1359 }),
1360 WirePayload::RelayConnect(RelayConnect { relay_slot_id: 11 }),
1361 WirePayload::RelayStream(RelayStream {
1362 relay_slot_id: 11,
1363 stream_id: 3,
1364 kind: RelayPayloadKind::Control,
1365 payload: vec![5, 4, 3],
1366 }),
1367 WirePayload::Providers(Providers {
1368 content_id: [10u8; 32],
1369 providers: vec![PeerAddr {
1370 ip: "10.0.0.3".parse().expect("valid ip"),
1371 port: 9999,
1372 transport: TransportProtocol::Quic,
1373 pubkey_hint: Some([1u8; 32]),
1374 relay_via: None,
1375 }],
1376 updated_at: 321,
1377 }),
1378 WirePayload::HaveContent(HaveContent {
1379 content_id: [11u8; 32],
1380 }),
1381 WirePayload::GetChunk(GetChunk {
1382 content_id: [12u8; 32],
1383 chunk_index: 2,
1384 }),
1385 WirePayload::ChunkData(ChunkData {
1386 content_id: [13u8; 32],
1387 chunk_index: 2,
1388 bytes: vec![6, 6, 6],
1389 }),
1390 WirePayload::GetChunkHashes(GetChunkHashes {
1391 content_id: [14u8; 32],
1392 }),
1393 WirePayload::ChunkHashList(ChunkHashList {
1394 content_id: [14u8; 32],
1395 hashes: vec![[1u8; 32], [2u8; 32]],
1396 }),
1397 ];
1398
1399 for (idx, message) in cases.iter().enumerate() {
1400 let envelope = Envelope::from_typed(idx as u32, 0, message).expect("build envelope");
1401 let decoded = envelope.decode_typed().expect("decode typed payload");
1402 assert_eq!(&decoded, message);
1403 }
1404 }
1405}