1use crate::Context;
2use crate::consensus::consensus::{self, Consensus};
3use crate::consensus::doms::attestation::EventAttestation;
4use crate::consensus::doms::entry::{Entry, EventEntry};
5use crate::consensus::doms::tx::TxU;
6use crate::consensus::doms::{Attestation, EntrySummary};
7use crate::consensus::fabric::Fabric;
8use crate::node::anr::Anr;
9use crate::node::peers::HandshakeStatus;
10use crate::node::{anr, peers};
11use crate::utils::Hash;
12use crate::utils::bls12_381;
13use crate::utils::misc::get_unix_millis_now;
14#[cfg(test)]
15use crate::utils::{PublicKey, Signature};
16use amadeus_utils::B3f4;
17use amadeus_utils::vecpak;
18use ambassador::{Delegate, delegatable_trait};
19use serde::{Deserialize, Serialize};
20use std::fmt::Debug;
21use std::io::Error as IoError;
22use std::net::{Ipv4Addr, SocketAddr};
23use tracing::instrument;
24
25#[derive(Delegate, Debug, Serialize, Deserialize)]
26#[delegate(Handle)]
27pub enum Protocol {
28 Ping(Ping),
29 PingReply(PingReply),
30 EventEntry(EventEntry),
31 EventTip(EventTip),
32 EventAttestation(EventAttestation),
33 EventTx(EventTx),
34 GetPeerAnrs(GetPeerAnrs),
35 GetPeerAnrsReply(GetPeerAnrsReply),
36 NewPhoneWhoDis(NewPhoneWhoDis),
37 NewPhoneWhoDisReply(NewPhoneWhoDisReply),
38 Catchup(Catchup),
39 CatchupReply(CatchupReply),
40 SpecialBusiness(SpecialBusiness),
41 SpecialBusinessReply(SpecialBusinessReply),
42}
43
44impl Typename for Protocol {
45 fn typename(&self) -> &'static str {
46 match self {
47 Protocol::Ping(_) => Ping::TYPENAME,
48 Protocol::PingReply(_) => PingReply::TYPENAME,
49 Protocol::EventEntry(_) => EventEntry::TYPENAME,
50 Protocol::EventTip(_) => EventTip::TYPENAME,
51 Protocol::EventAttestation(_) => EventAttestation::TYPENAME,
52 Protocol::EventTx(_) => EventTx::TYPENAME,
53 Protocol::GetPeerAnrs(_) => GetPeerAnrs::TYPENAME,
54 Protocol::GetPeerAnrsReply(_) => GetPeerAnrsReply::TYPENAME,
55 Protocol::NewPhoneWhoDis(_) => NewPhoneWhoDis::TYPENAME,
56 Protocol::NewPhoneWhoDisReply(_) => NewPhoneWhoDisReply::TYPENAME,
57 Protocol::Catchup(_) => Catchup::TYPENAME,
58 Protocol::CatchupReply(_) => CatchupReply::TYPENAME,
59 Protocol::SpecialBusiness(_) => SpecialBusiness::TYPENAME,
60 Protocol::SpecialBusinessReply(_) => SpecialBusinessReply::TYPENAME,
61 }
62 }
63}
64
65impl Protocol {
66 pub async fn send_to_with_metrics(&self, ctx: &Context, dst: Ipv4Addr) -> Result<(), Error> {
68 let dst_addr = SocketAddr::new(std::net::IpAddr::V4(dst), ctx.config.udp_port);
69 let dst_anr = ctx.anrs.get_by_ip4(dst).await.ok_or(Error::NoAnrForDestination(dst))?;
70
71 let shards = ctx.reassembler.build_shards(&ctx.config, &vecpak::to_vec(&self)?, &dst_anr.pk).await?;
72 for shard in &shards {
73 ctx.socket.send_to_with_metrics(shard, dst_addr, &ctx.metrics).await?;
74 }
75
76 ctx.metrics.add_outgoing_proto(self.typename());
77 Ok(())
78 }
79}
80
81#[delegatable_trait]
83pub trait Typename {
84 fn typename(&self) -> &'static str;
87}
88
89#[async_trait::async_trait]
92#[delegatable_trait]
93pub trait Handle: Typename + Debug + Send + Sync + Serialize {
94 async fn handle(&self, ctx: &Context, src: Ipv4Addr) -> Result<Vec<Instruction>, Error>;
96}
97
98#[derive(Debug, thiserror::Error, strum_macros::IntoStaticStr)]
99pub enum Error {
100 #[error(transparent)]
101 Io(#[from] IoError),
102 #[error(transparent)]
103 Bls(#[from] bls12_381::Error),
104 #[error(transparent)]
105 Tx(#[from] crate::consensus::doms::tx::Error),
106 #[error(transparent)]
107 Entry(#[from] crate::consensus::doms::entry::Error),
108 #[error(transparent)]
109 Archiver(#[from] crate::utils::archiver::Error),
110 #[error(transparent)]
111 Peers(#[from] peers::Error),
112 #[error(transparent)]
113 Consensus(#[from] consensus::Error),
114 #[error(transparent)]
115 Fabric(#[from] crate::consensus::fabric::Error),
116 #[error(transparent)]
117 Att(#[from] crate::consensus::doms::attestation::Error),
118 #[error(transparent)]
119 Reassembler(#[from] crate::node::reassembler::Error),
120 #[error(transparent)]
121 Anr(#[from] anr::Error),
122 #[error("parse error: {0}")]
123 ParseError(&'static str),
124 #[error("No ANR found for destination IP: {0}")]
125 NoAnrForDestination(Ipv4Addr),
126 #[error(transparent)]
127 Vecpak(#[from] vecpak::Error),
128 #[error("other error: {0}")]
129 Other(String),
130}
131
132impl Typename for Error {
133 fn typename(&self) -> &'static str {
134 self.into()
135 }
136}
137
138#[derive(Debug, strum_macros::IntoStaticStr)]
140pub enum Instruction {
141 Noop { why: String },
142 SendNewPhoneWhoDisReply { dst: Ipv4Addr },
143 SendGetPeerAnrsReply { anrs: Vec<Anr>, dst: Ipv4Addr },
144 SendPingReply { ts_m: u64, dst: Ipv4Addr },
145
146 ValidTxs { txs: Vec<Vec<u8>> },
147 ReceivedEntry { entry: Entry },
148 ReceivedAttestation { attestation: Attestation },
149 ReceivedConsensus { consensus: Consensus },
150 SpecialBusiness { business: Vec<u8> },
151 SpecialBusinessReply { business: Vec<u8> },
152 SolicitEntry { hash: Vec<u8> },
153 SolicitEntry2,
154}
155
156impl Typename for Instruction {
157 fn typename(&self) -> &'static str {
158 self.into()
159 }
160}
161
162#[derive(Debug, serde::Serialize, serde::Deserialize)]
163pub struct EventTip {
164 pub temporal: EntrySummary,
165 pub rooted: EntrySummary,
166}
167
168impl Typename for EventTip {
169 fn typename(&self) -> &'static str {
170 Self::TYPENAME
171 }
172}
173
174#[async_trait::async_trait]
175impl Handle for EventTip {
176 #[instrument(skip(self, ctx), fields(src = %src), name = "EventTip::handle")]
177 async fn handle(&self, ctx: &Context, src: Ipv4Addr) -> Result<Vec<Instruction>, Error> {
178 let signer = self.temporal.header.signer.to_vec();
181 let is_trainer =
182 match ctx.fabric.trainers_for_height(ctx.fabric.get_temporal_height().ok().flatten().unwrap_or_default()) {
183 Some(trainers) => trainers.iter().any(|pk| pk.as_slice() == signer),
184 None => false,
185 };
186
187 if is_trainer || ctx.is_peer_handshaked(src).await {
188 ctx.peers.update_peer_from_tip(ctx, src, self).await;
189 }
190
191 Ok(vec![Instruction::Noop { why: "event_tip handling not implemented".to_string() }])
192 }
193}
194
195impl EventTip {
196 pub const TYPENAME: &'static str = "event_tip";
197
198 pub fn new(temporal: EntrySummary, rooted: EntrySummary) -> Self {
199 Self { temporal, rooted }
200 }
201
202 pub fn from_current_tips() -> Result<Self, Error> {
203 Err(Error::Consensus(consensus::Error::NotImplemented("from_current_tips requires DB context")))
205 }
206
207 pub fn from_current_tips_db(fab: &Fabric) -> Result<Self, Error> {
209 fn entry_summary_by_hash(fab: &Fabric, hash: &Hash) -> EntrySummary {
211 if let Some(entry) = fab.get_entry_by_hash(hash) { entry.into() } else { EntrySummary::empty() }
212 }
213
214 let temporal_summary = match fab.get_temporal_hash()? {
215 Some(h) => entry_summary_by_hash(fab, &Hash::from(h)),
216 None => EntrySummary::empty(),
217 };
218
219 let rooted_summary = match fab.get_rooted_hash()? {
220 Some(h) => entry_summary_by_hash(fab, &Hash::from(h)),
221 None => EntrySummary::empty(),
222 };
223
224 Ok(Self { temporal: temporal_summary, rooted: rooted_summary })
225 }
226}
227
228#[derive(Debug, serde::Serialize, serde::Deserialize)]
229pub struct Ping {
230 pub ts_m: u64,
231}
232
233#[derive(Debug, serde::Serialize, serde::Deserialize)]
234pub struct PingReply {
235 pub ts_m: u64,
236 #[serde(skip)]
237 pub seen_time: u64,
238}
239
240#[derive(Debug, serde::Serialize, serde::Deserialize)]
241pub struct Catchup {
242 pub height_flags: Vec<CatchupHeight>,
243}
244
245#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
246pub struct CatchupHeight {
247 pub height: u64,
248 #[serde(default, skip_serializing_if = "Option::is_none")]
249 pub c: Option<bool>,
250 #[serde(default, skip_serializing_if = "Option::is_none")]
251 pub e: Option<bool>,
252 #[serde(default, skip_serializing_if = "Option::is_none")]
253 pub a: Option<bool>,
254 #[serde(default, skip_serializing_if = "Option::is_none")]
255 pub hashes: Option<Vec<Vec<u8>>>,
256}
257
258impl Catchup {
259 pub const TYPENAME: &'static str = "catchup";
260}
261
262impl Typename for Catchup {
263 fn typename(&self) -> &'static str {
264 Self::TYPENAME
265 }
266}
267
268#[async_trait::async_trait]
269impl Handle for Catchup {
270 async fn handle(&self, _ctx: &Context, _src: Ipv4Addr) -> Result<Vec<Instruction>, Error> {
271 Ok(vec![Instruction::Noop { why: "catchup received".to_string() }])
272 }
273}
274
275#[derive(Debug, serde::Serialize, serde::Deserialize)]
276pub struct CatchupReply {
277 pub tries: Vec<CatchupHeightReply>,
278}
279
280#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
281pub struct CatchupHeightReply {
282 pub height: u64,
283 #[serde(default, skip_serializing_if = "Option::is_none")]
284 pub entries: Option<Vec<Entry>>,
285 #[serde(default, skip_serializing_if = "Option::is_none")]
286 pub attestations: Option<Vec<Attestation>>,
287 #[serde(default, skip_serializing_if = "Option::is_none")]
288 pub consensuses: Option<Vec<Consensus>>,
289}
290
291impl CatchupReply {
292 pub const TYPENAME: &'static str = "catchup_reply";
293}
294
295impl Typename for CatchupReply {
296 fn typename(&self) -> &'static str {
297 Self::TYPENAME
298 }
299}
300
301#[async_trait::async_trait]
302impl Handle for CatchupReply {
303 async fn handle(&self, ctx: &Context, _src: Ipv4Addr) -> Result<Vec<Instruction>, Error> {
304 let instructions = Vec::new();
305 let rooted_tip_height = ctx.fabric.get_rooted_height()?.unwrap_or(0);
306
307 for trie in &self.tries {
308 if let Some(ref entries) = trie.entries {
309 for entry in entries {
310 if entry.header.height >= rooted_tip_height {
311 let seen_time_ms = get_unix_millis_now();
312 let entry_bin = entry.to_vecpak_bin();
313 {
314 let _ = ctx.fabric.insert_entry(
315 &entry.hash,
316 entry.header.height,
317 entry.header.slot,
318 &entry_bin,
319 seen_time_ms,
320 );
321 }
322 }
323 }
324 }
325
326 if let Some(ref attestations) = trie.attestations {
327 for _attestation in attestations {}
328 }
329
330 if let Some(ref consensuses) = trie.consensuses {
331 for consensus in consensuses {
332 let _ = ctx.fabric.insert_consensus(&consensus);
333 }
334 }
335 }
336
337 Ok(instructions)
338 }
339}
340
341#[derive(Debug)]
342pub struct SolicitEntry {
343 pub hash: Vec<u8>,
344}
345
346#[derive(Debug)]
347pub struct SolicitEntry2;
348
349impl Typename for Ping {
350 fn typename(&self) -> &'static str {
351 Self::TYPENAME
352 }
353}
354
355#[async_trait::async_trait]
356impl Handle for Ping {
357 #[instrument(skip(self, ctx), fields(src = %src), name = "Ping::handle")]
358 async fn handle(&self, ctx: &Context, src: Ipv4Addr) -> Result<Vec<Instruction>, Error> {
359 ctx.peers.update_peer_ping_timestamp(src, self.ts_m).await;
360 Ok(vec![Instruction::SendPingReply { ts_m: self.ts_m, dst: src }])
361 }
362}
363
364impl Ping {
365 pub const TYPENAME: &'static str = "ping";
366
367 pub fn new() -> Self {
369 Self { ts_m: get_unix_millis_now() }
370 }
371
372 pub fn with_timestamp(ts_m: u64) -> Self {
374 Self { ts_m }
375 }
376}
377
378impl Typename for PingReply {
379 fn typename(&self) -> &'static str {
380 Self::TYPENAME
381 }
382}
383
384#[async_trait::async_trait]
385impl Handle for PingReply {
386 #[instrument(skip(self, ctx), fields(src = %src), name = "PingReply::handle")]
387 async fn handle(&self, ctx: &Context, src: Ipv4Addr) -> Result<Vec<Instruction>, Error> {
388 ctx.peers.update_peer_from_pong(src, self).await;
389 Ok(vec![Instruction::Noop { why: "pong processed".to_string() }])
390 }
391}
392
393impl PingReply {
394 pub const TYPENAME: &'static str = "ping_reply";
395
396 pub fn new(ts_m: u64) -> Self {
397 Self { ts_m, seen_time: get_unix_millis_now() }
398 }
399}
400
401#[derive(Debug, serde::Serialize, serde::Deserialize)]
402pub struct EventTx {
403 #[serde(rename = "txus")]
404 pub txs: Vec<TxU>,
405}
406
407impl Typename for EventTx {
408 fn typename(&self) -> &'static str {
409 Self::TYPENAME
410 }
411}
412
413#[async_trait::async_trait]
414impl Handle for EventTx {
415 async fn handle(&self, _ctx: &Context, _src: Ipv4Addr) -> Result<Vec<Instruction>, Error> {
416 Ok(vec![Instruction::Noop { why: "event_tx handling not implemented".to_string() }])
417 }
418}
419
420impl EventTx {
421 pub const TYPENAME: &'static str = "event_tx";
422
423 pub fn new(txs: Vec<TxU>) -> Self {
424 Self { txs }
425 }
426}
427
428#[derive(Debug, serde::Serialize, serde::Deserialize)]
429#[allow(non_snake_case)]
430pub struct GetPeerAnrs {
431 pub hasPeersb3f4: Vec<B3f4>,
432}
433
434impl Typename for GetPeerAnrs {
435 fn typename(&self) -> &'static str {
436 Self::TYPENAME
437 }
438}
439
440#[async_trait::async_trait]
441impl Handle for GetPeerAnrs {
442 async fn handle(&self, ctx: &Context, src: Ipv4Addr) -> Result<Vec<Instruction>, Error> {
443 let anrs = ctx.anrs.get_all_excluding_b3f4(&self.hasPeersb3f4).await;
444
445 Ok(vec![Instruction::SendGetPeerAnrsReply { anrs, dst: src }])
446 }
447}
448
449impl GetPeerAnrs {
450 pub const TYPENAME: &'static str = "get_peer_anrs";
451
452 pub fn new(has_peers_b3f4: Vec<B3f4>) -> Self {
453 Self { hasPeersb3f4: has_peers_b3f4 }
454 }
455}
456
457#[derive(Debug, serde::Serialize, serde::Deserialize)]
458pub struct GetPeerAnrsReply {
459 pub anrs: Vec<Anr>,
460}
461
462impl Typename for GetPeerAnrsReply {
463 fn typename(&self) -> &'static str {
464 Self::TYPENAME
465 }
466}
467
468#[async_trait::async_trait]
469impl Handle for GetPeerAnrsReply {
470 async fn handle(&self, ctx: &Context, _src: Ipv4Addr) -> Result<Vec<Instruction>, Error> {
471 for anr in &self.anrs {
472 ctx.anrs.insert(anr.clone()).await;
473 }
474 Ok(vec![Instruction::Noop { why: format!("inserted {} anrs", self.anrs.len()) }])
475 }
476}
477
478impl GetPeerAnrsReply {
479 pub const TYPENAME: &'static str = "get_peer_anrs_reply";
480
481 pub fn new(anrs: Vec<Anr>) -> Self {
482 Self { anrs }
483 }
484}
485
486#[derive(Debug, serde::Serialize, serde::Deserialize)]
487pub struct NewPhoneWhoDis {}
488
489impl Typename for NewPhoneWhoDis {
490 fn typename(&self) -> &'static str {
491 Self::TYPENAME
492 }
493}
494
495#[async_trait::async_trait]
496impl Handle for NewPhoneWhoDis {
497 #[instrument(skip_all)]
498 async fn handle(&self, _ctx: &Context, src: Ipv4Addr) -> Result<Vec<Instruction>, Error> {
499 Ok(vec![Instruction::SendNewPhoneWhoDisReply { dst: src }])
501 }
502}
503
504impl NewPhoneWhoDis {
505 pub const TYPENAME: &'static str = "new_phone_who_dis";
506
507 pub fn new() -> Self {
509 Self {}
510 }
511}
512
513#[derive(Debug, serde::Serialize, serde::Deserialize)]
514pub struct NewPhoneWhoDisReply {
515 pub anr: Anr,
516}
517
518impl Typename for NewPhoneWhoDisReply {
519 fn typename(&self) -> &'static str {
520 Self::TYPENAME
521 }
522}
523
524#[async_trait::async_trait]
525impl Handle for NewPhoneWhoDisReply {
526 #[instrument(skip(self, ctx), fields(src = %src), name = "NewPhoneWhoDisReply::handle")]
527 async fn handle(&self, ctx: &Context, src: Ipv4Addr) -> Result<Vec<Instruction>, Error> {
528 if src != self.anr.ip4 {
530 return Err(Error::ParseError("anr_ip_mismatch"));
531 }
532
533 let now_s = crate::utils::misc::get_unix_secs_now();
534 let age_secs = now_s.saturating_sub(self.anr.ts);
535 if age_secs > 60 {
536 return Err(Error::ParseError("anr_too_old"));
537 }
538
539 ctx.anrs.insert(self.anr.clone()).await;
540 ctx.anrs.set_handshaked(self.anr.pk.as_ref()).await;
541 ctx.update_peer_from_anr(src, &self.anr.pk, &self.anr.version, Some(HandshakeStatus::Completed)).await;
542
543 Ok(vec![Instruction::Noop { why: "handshake completed".to_string() }])
544 }
545}
546
547impl NewPhoneWhoDisReply {
548 pub const TYPENAME: &'static str = "new_phone_who_dis_reply";
549
550 pub fn new(anr: Anr) -> Self {
551 Self { anr }
552 }
553}
554
555#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
558pub struct SpecialBusiness {
559 #[serde(with = "serde_bytes")]
560 pub business: Vec<u8>,
561}
562
563impl SpecialBusiness {
564 pub const TYPENAME: &'static str = "special_business";
565
566 pub fn new(business: Vec<u8>) -> Self {
567 Self { business }
568 }
569}
570
571impl Typename for SpecialBusiness {
572 fn typename(&self) -> &'static str {
573 Self::TYPENAME
574 }
575}
576
577#[async_trait::async_trait]
578impl Handle for SpecialBusiness {
579 async fn handle(&self, _ctx: &Context, _src: Ipv4Addr) -> Result<Vec<Instruction>, Error> {
580 Ok(vec![Instruction::SpecialBusiness { business: self.business.clone() }])
581 }
582}
583
584#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
586pub struct SpecialBusinessReply {
587 #[serde(with = "serde_bytes")]
588 pub business: Vec<u8>,
589}
590
591impl SpecialBusinessReply {
592 pub const TYPENAME: &'static str = "special_business_reply";
593
594 pub fn new(business: Vec<u8>) -> Self {
595 Self { business }
596 }
597}
598
599impl Typename for SpecialBusinessReply {
600 fn typename(&self) -> &'static str {
601 Self::TYPENAME
602 }
603}
604
605#[async_trait::async_trait]
606impl Handle for SpecialBusinessReply {
607 async fn handle(&self, _ctx: &Context, _src: Ipv4Addr) -> Result<Vec<Instruction>, Error> {
608 Ok(vec![Instruction::SpecialBusinessReply { business: self.business.clone() }])
609 }
610}
611
612#[cfg(test)]
613mod tests {
614 use super::*;
615 use crate::consensus::doms::entry::{Entry, EntryHeader, EntrySummary};
616 use crate::consensus::doms::tx::{EntryTx, EntryTxAction, EntryTxInner, Tx, TxAction, TxU};
617 use crate::node::anr::Anr;
618 use crate::utils::bls12_381;
619 use amadeus_utils::version::Ver;
620 use std::net::Ipv4Addr;
621
622 fn dummy_anr() -> Anr {
623 let sk = bls12_381::generate_sk();
624 let pk = bls12_381::get_public_key(&sk).unwrap();
625 let pop = bls12_381::sign(&sk, pk.as_ref(), crate::consensus::DST_POP).unwrap();
626 Anr::build(&sk, &pk, pop.as_ref(), Ipv4Addr::new(127, 0, 0, 1), Ver::new(1, 0, 0)).unwrap()
627 }
628
629 fn dummy_entry_summary() -> EntrySummary {
630 EntrySummary {
631 header: EntryHeader {
632 height: 1,
633 slot: 1,
634 prev_slot: 0,
635 prev_hash: Hash::new([0; 32]),
636 dr: Hash::new([1; 32]),
637 vr: Signature::new([2; 96]),
638 signer: PublicKey::new([3; 48]),
639 root_tx: Hash::new([4; 32]),
640 root_validator: Hash::new([5; 32]),
641 },
642 signature: Signature::new([6; 96]),
643 mask: None,
644 }
645 }
646
647 fn dummy_entry() -> Entry {
648 Entry {
649 hash: Hash::new([10; 32]),
650 header: EntryHeader {
651 height: 1,
652 slot: 1,
653 prev_slot: 0,
654 prev_hash: Hash::new([0; 32]),
655 dr: Hash::new([1; 32]),
656 vr: Signature::new([2; 96]),
657 signer: PublicKey::new([3; 48]),
658 root_tx: Hash::new([4; 32]),
659 root_validator: Hash::new([5; 32]),
660 },
661 signature: Signature::new([6; 96]),
662 mask: None,
663 txs: vec![EntryTx {
664 hash: Hash::new([20; 32]),
665 signature: Signature::new([21; 96]),
666 tx: EntryTxInner {
667 action: EntryTxAction {
668 args: vec![],
669 contract: b"C".to_vec(),
670 function: b"f".to_vec(),
671 op: "call".into(),
672 attached_symbol: None,
673 attached_amount: None,
674 },
675 nonce: 1,
676 signer: PublicKey::new([22; 48]),
677 },
678 }],
679 }
680 }
681
682 fn dummy_txu() -> TxU {
683 TxU {
684 hash: Hash::new([30; 32]),
685 signature: Signature::new([31; 96]),
686 tx: Tx {
687 action: TxAction {
688 args: vec![],
689 contract: b"C".to_vec(),
690 function: b"f".to_vec(),
691 op: "call".into(),
692 attached_symbol: None,
693 attached_amount: None,
694 },
695 nonce: 1,
696 signer: PublicKey::new([32; 48]),
697 },
698 }
699 }
700
701 fn dummy_attestation() -> Attestation {
702 Attestation {
703 entry_hash: Hash::new([40; 32]),
704 mutations_hash: Hash::new([41; 32]),
705 signer: PublicKey::new([42; 48]),
706 signature: Signature::new([43; 96]),
707 }
708 }
709
710 fn roundtrip<T: serde::Serialize + for<'de> serde::Deserialize<'de>>(msg: &T) -> T {
711 let bin = vecpak::to_vec(msg).expect("serialize");
712 vecpak::from_slice(&bin).expect("deserialize")
713 }
714
715 #[test]
716 fn ping_roundtrip() {
717 let msg = Protocol::Ping(Ping::with_timestamp(123456));
718 let rt = roundtrip(&msg);
719 assert_eq!(rt.typename(), "ping");
720 }
721
722 #[test]
723 fn ping_reply_roundtrip() {
724 let msg = Protocol::PingReply(PingReply { ts_m: 123456, seen_time: 0 });
725 let rt = roundtrip(&msg);
726 assert_eq!(rt.typename(), "ping_reply");
727 }
728
729 #[test]
730 fn event_entry_roundtrip() {
731 let msg = Protocol::EventEntry(EventEntry { entry_packed: dummy_entry() });
732 let rt = roundtrip(&msg);
733 assert_eq!(rt.typename(), "event_entry");
734 }
735
736 #[test]
737 fn event_tip_roundtrip() {
738 let msg = Protocol::EventTip(EventTip::new(dummy_entry_summary(), dummy_entry_summary()));
739 let rt = roundtrip(&msg);
740 assert_eq!(rt.typename(), "event_tip");
741 }
742
743 #[test]
744 fn event_attestation_roundtrip() {
745 let msg = Protocol::EventAttestation(EventAttestation::new(vec![dummy_attestation()]));
746 let rt = roundtrip(&msg);
747 assert_eq!(rt.typename(), "event_attestation");
748 }
749
750 #[test]
751 fn event_tx_roundtrip() {
752 let msg = Protocol::EventTx(EventTx::new(vec![dummy_txu()]));
753 let rt = roundtrip(&msg);
754 assert_eq!(rt.typename(), "event_tx");
755 }
756
757 #[test]
758 fn get_peer_anrs_roundtrip() {
759 let msg = Protocol::GetPeerAnrs(GetPeerAnrs::new(vec![[1, 2, 3, 4].into()]));
760 let rt = roundtrip(&msg);
761 assert_eq!(rt.typename(), "get_peer_anrs");
762 }
763
764 #[test]
765 fn get_peer_anrs_reply_roundtrip() {
766 let msg = Protocol::GetPeerAnrsReply(GetPeerAnrsReply::new(vec![dummy_anr()]));
767 let rt = roundtrip(&msg);
768 assert_eq!(rt.typename(), "get_peer_anrs_reply");
769 }
770
771 #[test]
772 fn new_phone_who_dis_roundtrip() {
773 let msg = Protocol::NewPhoneWhoDis(NewPhoneWhoDis::new());
774 let rt = roundtrip(&msg);
775 assert_eq!(rt.typename(), "new_phone_who_dis");
776 }
777
778 #[test]
779 fn new_phone_who_dis_reply_roundtrip() {
780 let msg = Protocol::NewPhoneWhoDisReply(NewPhoneWhoDisReply::new(dummy_anr()));
781 let rt = roundtrip(&msg);
782 assert_eq!(rt.typename(), "new_phone_who_dis_reply");
783 }
784
785 #[test]
786 fn special_business_roundtrip() {
787 let msg = Protocol::SpecialBusiness(SpecialBusiness::new(vec![1, 2, 3, 4, 5]));
788 let rt = roundtrip(&msg);
789 assert_eq!(rt.typename(), "special_business");
790 if let Protocol::SpecialBusiness(sb) = rt {
791 assert_eq!(sb.business, vec![1, 2, 3, 4, 5]);
792 }
793 }
794
795 #[test]
796 fn special_business_reply_roundtrip() {
797 let msg = Protocol::SpecialBusinessReply(SpecialBusinessReply::new(vec![6, 7, 8, 9]));
798 let rt = roundtrip(&msg);
799 assert_eq!(rt.typename(), "special_business_reply");
800 if let Protocol::SpecialBusinessReply(sbr) = rt {
801 assert_eq!(sbr.business, vec![6, 7, 8, 9]);
802 }
803 }
804
805 #[test]
806 fn parse_real_elixir_event_tx() {
807 let packet = hex::decode("0701020501026f700501086576656e745f74780501047478757306010107010305010274780701030501056e6f6e63650308187c02e8527c7cbf050106616374696f6e0701040501026f7005010463616c6c05010461726773060101050204f09e01000060f7e2fb1fbd6e6425d1fdb53a172e548a3369d041508952e0c27ee7bd548963904796524edd0037f341613e24a7bf2c1992044ea66cbc8d8f1ff91b3d61705de2780321cc74bb12c693913edf938ca4b6348ad0d56c1fb45c5d0c573ccc09771dcf89434cba91a977f5d8e7bcddd60220ca849cbf3babb55d515b14e8d248a40f6a515f91174b41fe9296f0cf027bc1d3cb1d8b2e4c5bfee88c3f0c914678ad45ac8fbf59b9cb0f535861ef54c474ad904796524edd0037f341613e24a7bf2c1992044ea66cbc8d8f1ff91b3d61705de2780321cc74bb12c693913edf938ca48566b77b83ee09c66333f2a34deba8ffa9f1aeff6205baffd891e5ffdb1fa8ff4190a6ffe96ef1ffd9cfc9ffb1bdb6ffe72ee5ff41e3eeffad23daff3c69b4ffb042cbff3d3e0d005e3696ffa7119dffd030bcffaaceaeff6f25caff76d5a3ffe40c9bff7291e4ff14fcd0ff2fe5cfffc2b7f5ffc869d2ffce69d6ffa7a0dbff24cfc7ff71ec09008862a3ff4b237dffe554d9ff9e84baff0adcd7ff8d9f9fff48e1b7ffce29daff0236a7ffad19bbffb065fbffe7cac0ffd941f2ff9e1d96ffad5ec7ffd4111300c0078bffd82db8ff8353bcff01dfd8fff139acff79c5afffa4bcb2ff617aeaffbaa4caff3a2ed4ff04730b00e66dc6ff6946cdff29e4abffac5ba3fff9571a008b19aeffde77aaff0987bcffe226b1ffb11cbbff88d1a6ff8ab1d6ff9b3ed8ff8460cdff6841e7ff9d16dbffebc1d3ffb76cfdffffd9c3ff9329c5fffa46f5ff649c99ff78fab1ffcf64bdffc65fc1ff7316c6ffed4eb2fff3a8a1ff8ec9cfff95d5d4ff8b66c0ffc1aff5ff889ac2ff87f3f5ff49fab0ff2a3db6ffebaf130008d88bffc1ab91ff5d00cdffd864a0ff06edceff66decbff8181c6ffda9dbcff2826ccffbcb2c0ff27c5e0ff294edaff5b8bd9ff07c998ff4571b6ff9fc8160069eeb5ffad93b2ff80a7bbffbae9c2ff65ecbdffa591a4ff2683c8ffe9c5d8ffc969b6ff1ba6c5ff19400e00830bc9ff5857bcff0cb89bffe8a4b4ffdeefffff547badfffd76b1ffeed4b0ffbc44cbff0c3ebfff0cb9b5ff08c9a8ff97b4caffc11de3ff2173a7fff744eaffdbd1f1ff3ab3dfffaefc96ffeca3abff8aba2100f8e084ff90cabeffdb0bbaffca18d1ffaff6c0ff45a38dffe05aaeff3db7cfff87c9bcffbabdc0ffb81be9ffeb1bcdff7741d5ff6ef9b2ffba33b6ff5b1a02006593a9ff4398b4ff666bb3ffe42fcbff7fceb9ffa179c3ffaaadd4ffa48efbff0578c6ffe541c9ff43dff8ff257498ff0d88dbff49758affb3c7c1ff8f7df9ff1ff587ff0842d6ffe954b9fff39fc8ffbd55a3ff2ea1a2ffa28bc5ff874bdcffa06ddcff9b74b6ff1976e6ffa2a8b8ff1d27d9ff31ffc3ff8603bbff760b270029349dff40c8a4ff27b8c5ff97faa1ff7087c9ff66cfa3ff46bdb2ffd404e8ff387dc5ff0512d7ffe955f5ffcd6bc6ffc777d9fff0439aff0034b0ffc1ad5100f9b4adff79379eff7e5eb9ff444096ffb8f0d2ff6f4cc5ffe4cbd7ff2abdf1ff8ccec0ff152cc7ffa7ccf4fff186ceff7a99d3ff608e94ffc9dfd4ff1a9b1c00a5f4bfff21dda5ff21b5ceff0d1ba6ff6388edff336fa7ffdd29b8ff41a8d5ffaccdc5ffbfdcc3ff64cff1ffdad7c2ff79afdfff699c9dfff1b2d1ff0014460022f399ffc803a5ff1a1396ff7df5ceff95eec6ff182eadff834ab1ff8de1d6ff8af2c1ffee38c9ff4a90eaff1b29c0ff6390cbff724aa8ffa417adffae762900ca90a2ff050108636f6e747261637405010545706f636805010866756e6374696f6e05010a7375626d69745f736f6c0501067369676e657205013098b033f3c88d92c3ed617e06b87ff452bebc505fcdcc094f5f673433817825801ba0eaa8319f05804ed4728c9809d3380501046861736805012092a51d6e44c6e8b662d2870c4a8a9efd99f026de98c6a3a67f1652a79fa2d6b20501097369676e6174757265050160ad01c361a5845bf5772540c3719bd7088a742c4905271bb89edbd3808accf0e2a2186d300370d42f7da17cddab2ddb310517521e67a74438d25cf549b46e51d385f32cafe2b8e8a4f3c71dd384f7456f41c0d83556205890ae5522cd2a18c45f").unwrap();
808 let parsed: Protocol = vecpak::from_slice(&packet).expect("should parse real elixir event_tx");
809 assert_eq!(parsed.typename(), "event_tx");
810 assert_eq!(packet, vecpak::to_vec(&parsed).expect("should encode real elixir event_tx"));
811 }
812
813 #[test]
814 fn handshake_compatibility() {
815 let p_hex = "0701010501026f700501116e65775f70686f6e655f77686f5f646973";
816 let p_bytes = hex::decode(p_hex).expect("valid hex");
817 let npwd: Protocol = amadeus_utils::vecpak::from_slice(&p_bytes).unwrap();
818 let rt_bytes = amadeus_utils::vecpak::to_vec(&npwd).unwrap();
819 assert_eq!(rt_bytes, p_bytes);
820
821 let p_hex = "0701020501026f700501176e65775f70686f6e655f77686f5f6469735f7265706c79050103616e72070107050102706b050130a9e81ed8c8eaaebd8dd53a889d8c5a8612ab7330275a5d39043e95200e7c1b66f0dc00c5307e867a55a9ad9e7ae4b9f005010274730304692634f205010369703405010c37322e392e3134342e313130050103706f70050160b62a96d62af0d2d7006ab560c64bde562df13ae642380a31d935276412c59f9944dceaa4060903e4ead197e97ad1654910be87ac556a5063e1d68df542aab1a3f75df3eab891a7cab572ba7170716c5487183ef28ef89f7c7555be2bb1d41218050104706f72740302906905010776657273696f6e050105312e332e300501097369676e6174757265050160b62d43994fa7614138d205ecefeb1677d4998574aac1db8fdd5673de4e1d2ae8391c4cf703007ce37778e20624650143068c59596b5838536ecfd05a0d0805b0baa04dcae97caf9f199232fbfff462ebb35bfc653576af43007ba9666a2952a7";
822 let p_bytes = hex::decode(p_hex).expect("valid hex");
823 let npwdr: Protocol = amadeus_utils::vecpak::from_slice(&p_bytes).unwrap();
824 let rt_bytes = amadeus_utils::vecpak::to_vec(&npwdr).unwrap();
825 assert_eq!(rt_bytes, p_bytes);
826 }
827}