1use std::{
4 cmp::min,
5 collections::HashMap,
6 io::{self, Cursor, Read, Write},
7 time::{Duration, Instant},
8};
9
10use blake2b_simd::Params as Blake2bParams;
11use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
12use thiserror::Error;
13
14use zakura_chain::{parameters::Network, serialization::ZcashSerialize};
15
16use crate::{protocol::external::types::Nonce, VersionMessage};
17
18pub const P2P_V2_UPGRADE_COMMAND: &str = "p2pv2up";
20
21pub const P2P_V2_UPGRADE_COMMAND_BYTES: &[u8; 12] = b"p2pv2up\0\0\0\0\0";
23
24pub const P2P_V2_ALPN: &[u8] = b"p2p-v2/1";
26
27pub const PRELUDE_MAGIC: [u8; 8] = *b"ZAKURA1\0";
29
30pub const CONTROL_HELLO_MAGIC: [u8; 8] = *b"ZAKCTRL\0";
32
33pub const CONTROL_ACK_MAGIC: [u8; 8] = *b"ZAKACK\0\0";
35
36pub const STREAM_PRELUDE_MAGIC: [u8; 4] = *b"ZKST";
38
39pub const PRELUDE_VERSION: u16 = 1;
41
42pub const CONTROL_VERSION: u16 = 1;
44
45pub const ZAKURA_PROTOCOL_VERSION_1: u16 = 1;
47
48pub const MAX_PRELUDE_PAYLOAD_BYTES: usize = 4 * 1024;
50
51pub const MAX_CONTROL_PAYLOAD_BYTES: usize = 16 * 1024;
53
54pub const MAX_IROH_NODE_ID_BYTES: usize = 128;
56
57pub const MAX_IROH_DIRECT_ADDRESSES: usize = 8;
59
60pub const MAX_IROH_DIRECT_ADDRESS_BYTES: usize = 512;
62
63pub const MAX_IROH_RELAY_HINT_BYTES: usize = 512;
65
66pub const LOCAL_MAX_CONTROL_FRAME_BYTES: u32 = 1024 * 1024;
68
69pub const LOCAL_MAX_MESSAGE_BYTES: u32 = 4 * 1024 * 1024;
71
72pub const LOCAL_MAX_OPEN_STREAMS: u16 = 1024;
74
75pub const LOCAL_MAX_INBOUND_QUEUE_DEPTH: u16 = 4096;
77
78pub const LOCAL_MAX_IDLE_TIMEOUT_MILLIS: u32 = 10 * 60 * 1000;
80
81pub const TRANSCRIPT_HASH_BYTES: usize = 32;
83
84const INIT_DISCRIMINATOR: u8 = 1;
85const ACCEPT_DISCRIMINATOR: u8 = 2;
86const REJECT_DISCRIMINATOR: u8 = 3;
87pub(crate) const FRAME_HEADER_BYTES: usize = 2 + 2 + 4;
88
89#[derive(Clone, Debug, Eq, Hash, PartialEq)]
91#[cfg_attr(
92 any(test, feature = "proptest-impl"),
93 derive(proptest_derive::Arbitrary)
94)]
95pub struct ZakuraPeerId(Vec<u8>);
96
97impl ZakuraPeerId {
98 pub fn new(bytes: impl Into<Vec<u8>>) -> Result<Self, ZakuraProtocolError> {
100 let bytes = bytes.into();
101 validate_bounded_bytes(&bytes, MAX_IROH_NODE_ID_BYTES, "iroh node id")?;
102 Ok(Self(bytes))
103 }
104
105 pub fn as_bytes(&self) -> &[u8] {
107 &self.0
108 }
109
110 pub fn digest(&self) -> [u8; 32] {
112 if let Ok(digest) = <[u8; 32]>::try_from(self.0.as_slice()) {
113 return digest;
114 }
115 let hash = Blake2bParams::new()
116 .hash_length(32)
117 .personal(b"ZakuraPeerId-v1")
118 .hash(&self.0);
119 hash.as_bytes()
120 .try_into()
121 .expect("the peer identity digest has the configured 32-byte length")
122 }
123}
124
125#[derive(Copy, Clone, Debug, Eq, PartialEq)]
127#[repr(u32)]
128pub enum ZakuraNetworkId {
129 Mainnet = 1,
131 Testnet = 2,
133 Regtest = 3,
135 Configured = 4,
137}
138
139impl ZakuraNetworkId {
140 pub fn from_network(network: &Network) -> Self {
142 match network {
143 Network::Mainnet => Self::Mainnet,
144 Network::Testnet(params) if params.is_regtest() => Self::Regtest,
145 Network::Testnet(params) if params.is_default_testnet() => Self::Testnet,
146 Network::Testnet(_) => Self::Configured,
147 }
148 }
149
150 fn from_u32(value: u32) -> Result<Self, ZakuraProtocolError> {
151 match value {
152 1 => Ok(Self::Mainnet),
153 2 => Ok(Self::Testnet),
154 3 => Ok(Self::Regtest),
155 4 => Ok(Self::Configured),
156 _ => Err(ZakuraProtocolError::InvalidNetworkId(value)),
157 }
158 }
159
160 pub fn code(self) -> u32 {
162 self as u32
164 }
165}
166
167#[derive(Copy, Clone, Debug, Eq, PartialEq)]
169pub struct ZakuraHandshakeConfig {
170 pub prelude_version: u16,
172 pub zakura_protocol_min: u16,
174 pub zakura_protocol_max: u16,
176 pub network_id: ZakuraNetworkId,
178 pub chain_id: [u8; 32],
186 pub required_capabilities: u64,
188 pub supported_capabilities: u64,
190 pub supported_channels: u64,
192 pub max_control_frame_bytes: u32,
194 pub max_message_bytes: u32,
196 pub max_open_streams: u16,
198 pub max_inbound_queue_depth: u16,
200 pub max_idle_timeout_millis: u32,
202}
203
204impl ZakuraHandshakeConfig {
205 pub fn for_network(network: &Network) -> Self {
207 Self {
208 prelude_version: PRELUDE_VERSION,
209 zakura_protocol_min: ZAKURA_PROTOCOL_VERSION_1,
210 zakura_protocol_max: ZAKURA_PROTOCOL_VERSION_1,
211 network_id: ZakuraNetworkId::from_network(network),
212 chain_id: network.genesis_hash().0,
213 required_capabilities: 0,
214 supported_capabilities: 0,
215 supported_channels: 0,
216 max_control_frame_bytes: LOCAL_MAX_CONTROL_FRAME_BYTES,
217 max_message_bytes: LOCAL_MAX_MESSAGE_BYTES,
218 max_open_streams: LOCAL_MAX_OPEN_STREAMS,
219 max_inbound_queue_depth: LOCAL_MAX_INBOUND_QUEUE_DEPTH,
220 max_idle_timeout_millis: LOCAL_MAX_IDLE_TIMEOUT_MILLIS,
221 }
222 }
223
224 pub fn for_network_with_dev_cohort(network: &Network, dev_network: Option<&str>) -> Self {
234 let mut config = Self::for_network(network);
235
236 if let Some(tag) = dev_network.filter(|tag| !tag.is_empty()) {
237 config.network_id = ZakuraNetworkId::Configured;
238 config.chain_id = derive_dev_chain_id(network.genesis_hash().0, tag);
239 }
240
241 config
242 }
243
244 pub fn network_label(&self) -> &'static str {
246 match self.network_id {
247 ZakuraNetworkId::Mainnet => "mainnet",
248 ZakuraNetworkId::Testnet => "testnet",
249 ZakuraNetworkId::Regtest => "regtest",
250 ZakuraNetworkId::Configured => "configured",
251 }
252 }
253}
254
255pub fn derive_dev_chain_id(genesis_hash: [u8; 32], tag: &str) -> [u8; 32] {
268 let hash = Blake2bParams::new()
269 .hash_length(32)
270 .personal(b"zebra-zk-cohort1")
271 .to_state()
272 .update(&genesis_hash)
273 .update(tag.as_bytes())
274 .finalize();
275
276 let mut out = [0; 32];
277 out.copy_from_slice(hash.as_bytes());
278 out
279}
280
281#[derive(Copy, Clone, Debug, Eq, PartialEq)]
283pub struct ZakuraLegacyNonces {
284 pub local_zebra_nonce: Nonce,
286 pub remote_zebra_nonce: Nonce,
288}
289
290#[derive(Copy, Clone, Debug, Eq, PartialEq)]
292#[repr(u16)]
293pub enum ZakuraRejectReason {
294 UnsupportedPreludeVersion = 1,
296 IncompatibleZakuraProtocol = 2,
298 WrongNetwork = 3,
300 WrongChain = 4,
302 MissingRequiredCapability = 5,
304 ResourceLimit = 6,
306 AlreadyConnected = 7,
308 TemporaryUnavailable = 8,
310}
311
312impl ZakuraRejectReason {
313 fn from_u16(value: u16) -> Result<Self, ZakuraProtocolError> {
314 match value {
315 1 => Ok(Self::UnsupportedPreludeVersion),
316 2 => Ok(Self::IncompatibleZakuraProtocol),
317 3 => Ok(Self::WrongNetwork),
318 4 => Ok(Self::WrongChain),
319 5 => Ok(Self::MissingRequiredCapability),
320 6 => Ok(Self::ResourceLimit),
321 7 => Ok(Self::AlreadyConnected),
322 8 => Ok(Self::TemporaryUnavailable),
323 _ => Err(ZakuraProtocolError::InvalidRejectReason(value)),
324 }
325 }
326
327 fn code(self) -> u16 {
328 self as u16
330 }
331}
332
333#[derive(Copy, Clone, Debug, Eq, PartialEq)]
335pub enum ZakuraFailureClass {
336 Neutral,
338 PotentiallyPunitive,
340}
341
342#[derive(Copy, Clone, Debug, Eq, Error, PartialEq)]
347pub enum ZakuraValidationError {
348 #[error("unsupported Zakura {0} version")]
350 UnsupportedVersion(&'static str),
351
352 #[error("incompatible Zakura protocol")]
354 IncompatibleZakuraProtocol,
355
356 #[error("wrong Zakura network")]
358 WrongNetwork,
359
360 #[error("wrong Zakura chain")]
362 WrongChain,
363
364 #[error("missing required Zakura capability or channel")]
366 MissingRequiredCapability,
367
368 #[error("invalid Zakura resource limit")]
370 ResourceLimit,
371
372 #[error("authenticated Zakura identity mismatch")]
374 IdentityMismatch,
375
376 #[error("Zakura legacy upgrade transcript mismatch")]
378 TranscriptMismatch,
379
380 #[error("Zakura upgrade nonce mismatch")]
382 UpgradeNonceMismatch,
383
384 #[error("malformed Zakura control handshake")]
386 MalformedControl,
387
388 #[error("Zakura control nonce mismatch")]
390 ControlNonceMismatch,
391}
392
393impl ZakuraValidationError {
394 pub fn failure_class(self) -> ZakuraFailureClass {
396 match self {
397 Self::MalformedControl
398 | Self::IdentityMismatch
399 | Self::TranscriptMismatch
400 | Self::UpgradeNonceMismatch
401 | Self::ControlNonceMismatch => ZakuraFailureClass::PotentiallyPunitive,
402
403 Self::UnsupportedVersion(_)
404 | Self::IncompatibleZakuraProtocol
405 | Self::WrongNetwork
406 | Self::WrongChain
407 | Self::MissingRequiredCapability
408 | Self::ResourceLimit => ZakuraFailureClass::Neutral,
409 }
410 }
411
412 fn from_wire_reject(reason: ZakuraRejectReason) -> Self {
413 match reason {
414 ZakuraRejectReason::UnsupportedPreludeVersion => Self::UnsupportedVersion("prelude"),
415 ZakuraRejectReason::IncompatibleZakuraProtocol => Self::IncompatibleZakuraProtocol,
416 ZakuraRejectReason::WrongNetwork => Self::WrongNetwork,
417 ZakuraRejectReason::WrongChain => Self::WrongChain,
418 ZakuraRejectReason::MissingRequiredCapability => Self::MissingRequiredCapability,
419 ZakuraRejectReason::ResourceLimit => Self::ResourceLimit,
420 ZakuraRejectReason::AlreadyConnected | ZakuraRejectReason::TemporaryUnavailable => {
421 Self::MalformedControl
422 }
423 }
424 }
425}
426
427#[derive(Clone, Debug, Eq, PartialEq)]
429pub enum P2pV2Upgrade {
430 Init(P2pV2UpgradeInit),
432 Accept(P2pV2UpgradeAccept),
434 Reject(P2pV2UpgradeReject),
436}
437
438impl P2pV2Upgrade {
439 pub fn encode(&self) -> Result<Vec<u8>, ZakuraProtocolError> {
441 let mut bytes = Vec::new();
442 match self {
443 Self::Init(init) => {
444 bytes.write_u8(INIT_DISCRIMINATOR)?;
445 init.encode_to(&mut bytes)?;
446 }
447 Self::Accept(accept) => {
448 bytes.write_u8(ACCEPT_DISCRIMINATOR)?;
449 accept.encode_to(&mut bytes)?;
450 }
451 Self::Reject(reject) => {
452 bytes.write_u8(REJECT_DISCRIMINATOR)?;
453 reject.encode_to(&mut bytes)?;
454 }
455 }
456 if bytes.len() > MAX_PRELUDE_PAYLOAD_BYTES {
457 return Err(ZakuraProtocolError::OversizedPayload {
458 actual: bytes.len(),
459 max: MAX_PRELUDE_PAYLOAD_BYTES,
460 });
461 }
462 Ok(bytes)
463 }
464
465 pub fn decode(bytes: &[u8]) -> Result<Self, ZakuraProtocolError> {
467 if bytes.len() > MAX_PRELUDE_PAYLOAD_BYTES {
468 return Err(ZakuraProtocolError::OversizedPayload {
469 actual: bytes.len(),
470 max: MAX_PRELUDE_PAYLOAD_BYTES,
471 });
472 }
473
474 let mut reader = Cursor::new(bytes);
475 let message = match reader.read_u8()? {
476 INIT_DISCRIMINATOR => Self::Init(P2pV2UpgradeInit::decode_from(&mut reader)?),
477 ACCEPT_DISCRIMINATOR => Self::Accept(P2pV2UpgradeAccept::decode_from(&mut reader)?),
478 REJECT_DISCRIMINATOR => Self::Reject(P2pV2UpgradeReject::decode_from(&mut reader)?),
479 value => return Err(ZakuraProtocolError::InvalidDiscriminator(value)),
480 };
481 reject_trailing(bytes, &reader)?;
482 Ok(message)
483 }
484}
485
486#[derive(Clone, Debug, Eq, PartialEq)]
488pub struct P2pV2UpgradeInit {
489 pub magic: [u8; 8],
491 pub prelude_version: u16,
493 pub zakura_protocol_min: u16,
495 pub zakura_protocol_max: u16,
497 pub network_id: ZakuraNetworkId,
499 pub chain_id: [u8; 32],
501 pub capabilities: u64,
503 pub local_zebra_nonce: Nonce,
505 pub remote_zebra_nonce: Nonce,
507 pub upgrade_nonce: [u8; 32],
509 pub iroh_node_id: Vec<u8>,
511 pub iroh_direct_addresses: Vec<Vec<u8>>,
513 pub iroh_relay_hint: Option<Vec<u8>>,
515 pub max_control_frame_bytes: u32,
517 pub max_open_streams: u16,
519}
520
521impl P2pV2UpgradeInit {
522 pub fn validate(
524 &self,
525 local: &ZakuraHandshakeConfig,
526 nonces: ZakuraLegacyNonces,
527 ) -> Result<u16, ZakuraRejectReason> {
528 validate_prelude_static_fields(
529 PreludeStaticFields {
530 magic: self.magic,
531 prelude_version: self.prelude_version,
532 network_id: self.network_id,
533 chain_id: self.chain_id,
534 capabilities: self.capabilities,
535 iroh_node_id: &self.iroh_node_id,
536 iroh_direct_addresses: &self.iroh_direct_addresses,
537 iroh_relay_hint: self.iroh_relay_hint.as_deref(),
538 max_control_frame_bytes: self.max_control_frame_bytes,
539 max_open_streams: self.max_open_streams,
540 },
541 local,
542 )?;
543
544 validate_peer_nonce_labels(self.local_zebra_nonce, self.remote_zebra_nonce, nonces)?;
545
546 select_zakura_protocol(
547 local.zakura_protocol_min,
548 local.zakura_protocol_max,
549 self.zakura_protocol_min,
550 self.zakura_protocol_max,
551 )
552 }
553
554 fn encode_to<W: Write>(&self, writer: &mut W) -> Result<(), ZakuraProtocolError> {
555 writer.write_all(&self.magic)?;
556 writer.write_u16::<LittleEndian>(self.prelude_version)?;
557 writer.write_u16::<LittleEndian>(self.zakura_protocol_min)?;
558 writer.write_u16::<LittleEndian>(self.zakura_protocol_max)?;
559 writer.write_u32::<LittleEndian>(self.network_id.code())?;
560 writer.write_all(&self.chain_id)?;
561 writer.write_u64::<LittleEndian>(self.capabilities)?;
562 writer.write_u64::<LittleEndian>(self.local_zebra_nonce.0)?;
563 writer.write_u64::<LittleEndian>(self.remote_zebra_nonce.0)?;
564 writer.write_all(&self.upgrade_nonce)?;
565 write_bounded_bytes(writer, &self.iroh_node_id, MAX_IROH_NODE_ID_BYTES)?;
566 write_bounded_bytes_list(
567 writer,
568 &self.iroh_direct_addresses,
569 MAX_IROH_DIRECT_ADDRESSES,
570 MAX_IROH_DIRECT_ADDRESS_BYTES,
571 )?;
572 write_optional_bounded_bytes(
573 writer,
574 self.iroh_relay_hint.as_deref(),
575 MAX_IROH_RELAY_HINT_BYTES,
576 )?;
577 writer.write_u32::<LittleEndian>(self.max_control_frame_bytes)?;
578 writer.write_u16::<LittleEndian>(self.max_open_streams)?;
579 Ok(())
580 }
581
582 fn decode_from<R: Read>(reader: &mut R) -> Result<Self, ZakuraProtocolError> {
583 let mut magic = [0; 8];
584 reader.read_exact(&mut magic)?;
585 let prelude_version = reader.read_u16::<LittleEndian>()?;
586 let zakura_protocol_min = reader.read_u16::<LittleEndian>()?;
587 let zakura_protocol_max = reader.read_u16::<LittleEndian>()?;
588 let network_id = ZakuraNetworkId::from_u32(reader.read_u32::<LittleEndian>()?)?;
589 let mut chain_id = [0; 32];
590 reader.read_exact(&mut chain_id)?;
591 let capabilities = reader.read_u64::<LittleEndian>()?;
592 let local_zebra_nonce = Nonce(reader.read_u64::<LittleEndian>()?);
593 let remote_zebra_nonce = Nonce(reader.read_u64::<LittleEndian>()?);
594 let mut upgrade_nonce = [0; 32];
595 reader.read_exact(&mut upgrade_nonce)?;
596 let iroh_node_id = read_bounded_bytes(reader, MAX_IROH_NODE_ID_BYTES)?;
597 let iroh_direct_addresses = read_bounded_bytes_list(
598 reader,
599 MAX_IROH_DIRECT_ADDRESSES,
600 MAX_IROH_DIRECT_ADDRESS_BYTES,
601 )?;
602 let iroh_relay_hint = read_optional_bounded_bytes(reader, MAX_IROH_RELAY_HINT_BYTES)?;
603 let max_control_frame_bytes = reader.read_u32::<LittleEndian>()?;
604 let max_open_streams = reader.read_u16::<LittleEndian>()?;
605
606 Ok(Self {
607 magic,
608 prelude_version,
609 zakura_protocol_min,
610 zakura_protocol_max,
611 network_id,
612 chain_id,
613 capabilities,
614 local_zebra_nonce,
615 remote_zebra_nonce,
616 upgrade_nonce,
617 iroh_node_id,
618 iroh_direct_addresses,
619 iroh_relay_hint,
620 max_control_frame_bytes,
621 max_open_streams,
622 })
623 }
624}
625
626#[derive(Clone, Debug, Eq, PartialEq)]
628pub struct P2pV2UpgradeAccept {
629 pub magic: [u8; 8],
631 pub prelude_version: u16,
633 pub selected_zakura_protocol: u16,
635 pub network_id: ZakuraNetworkId,
637 pub chain_id: [u8; 32],
639 pub capabilities: u64,
641 pub initiator_upgrade_nonce: [u8; 32],
643 pub responder_upgrade_nonce: [u8; 32],
645 pub local_zebra_nonce: Nonce,
647 pub remote_zebra_nonce: Nonce,
649 pub iroh_node_id: Vec<u8>,
651 pub iroh_direct_addresses: Vec<Vec<u8>>,
653 pub iroh_relay_hint: Option<Vec<u8>>,
655 pub max_control_frame_bytes: u32,
657 pub max_open_streams: u16,
659}
660
661impl P2pV2UpgradeAccept {
662 pub fn validate(
664 &self,
665 local: &ZakuraHandshakeConfig,
666 nonces: ZakuraLegacyNonces,
667 init: &P2pV2UpgradeInit,
668 ) -> Result<(), ZakuraValidationError> {
669 validate_prelude_static_fields(
670 PreludeStaticFields {
671 magic: self.magic,
672 prelude_version: self.prelude_version,
673 network_id: self.network_id,
674 chain_id: self.chain_id,
675 capabilities: self.capabilities,
676 iroh_node_id: &self.iroh_node_id,
677 iroh_direct_addresses: &self.iroh_direct_addresses,
678 iroh_relay_hint: self.iroh_relay_hint.as_deref(),
679 max_control_frame_bytes: self.max_control_frame_bytes,
680 max_open_streams: self.max_open_streams,
681 },
682 local,
683 )
684 .map_err(ZakuraValidationError::from_wire_reject)?;
685 validate_peer_nonce_labels(self.local_zebra_nonce, self.remote_zebra_nonce, nonces)
686 .map_err(|_| ZakuraValidationError::UpgradeNonceMismatch)?;
687 if self.initiator_upgrade_nonce != init.upgrade_nonce {
688 return Err(ZakuraValidationError::UpgradeNonceMismatch);
689 }
690
691 let selected = select_zakura_protocol(
692 local.zakura_protocol_min,
693 local.zakura_protocol_max,
694 init.zakura_protocol_min,
695 init.zakura_protocol_max,
696 )
697 .map_err(ZakuraValidationError::from_wire_reject)?;
698 if self.selected_zakura_protocol != selected {
699 return Err(ZakuraValidationError::IncompatibleZakuraProtocol);
700 }
701
702 Ok(())
703 }
704
705 fn encode_to<W: Write>(&self, writer: &mut W) -> Result<(), ZakuraProtocolError> {
706 writer.write_all(&self.magic)?;
707 writer.write_u16::<LittleEndian>(self.prelude_version)?;
708 writer.write_u16::<LittleEndian>(self.selected_zakura_protocol)?;
709 writer.write_u32::<LittleEndian>(self.network_id.code())?;
710 writer.write_all(&self.chain_id)?;
711 writer.write_u64::<LittleEndian>(self.capabilities)?;
712 writer.write_all(&self.initiator_upgrade_nonce)?;
713 writer.write_all(&self.responder_upgrade_nonce)?;
714 writer.write_u64::<LittleEndian>(self.local_zebra_nonce.0)?;
715 writer.write_u64::<LittleEndian>(self.remote_zebra_nonce.0)?;
716 write_bounded_bytes(writer, &self.iroh_node_id, MAX_IROH_NODE_ID_BYTES)?;
717 write_bounded_bytes_list(
718 writer,
719 &self.iroh_direct_addresses,
720 MAX_IROH_DIRECT_ADDRESSES,
721 MAX_IROH_DIRECT_ADDRESS_BYTES,
722 )?;
723 write_optional_bounded_bytes(
724 writer,
725 self.iroh_relay_hint.as_deref(),
726 MAX_IROH_RELAY_HINT_BYTES,
727 )?;
728 writer.write_u32::<LittleEndian>(self.max_control_frame_bytes)?;
729 writer.write_u16::<LittleEndian>(self.max_open_streams)?;
730 Ok(())
731 }
732
733 fn decode_from<R: Read>(reader: &mut R) -> Result<Self, ZakuraProtocolError> {
734 let mut magic = [0; 8];
735 reader.read_exact(&mut magic)?;
736 let prelude_version = reader.read_u16::<LittleEndian>()?;
737 let selected_zakura_protocol = reader.read_u16::<LittleEndian>()?;
738 let network_id = ZakuraNetworkId::from_u32(reader.read_u32::<LittleEndian>()?)?;
739 let mut chain_id = [0; 32];
740 reader.read_exact(&mut chain_id)?;
741 let capabilities = reader.read_u64::<LittleEndian>()?;
742 let mut initiator_upgrade_nonce = [0; 32];
743 reader.read_exact(&mut initiator_upgrade_nonce)?;
744 let mut responder_upgrade_nonce = [0; 32];
745 reader.read_exact(&mut responder_upgrade_nonce)?;
746 let local_zebra_nonce = Nonce(reader.read_u64::<LittleEndian>()?);
747 let remote_zebra_nonce = Nonce(reader.read_u64::<LittleEndian>()?);
748 let iroh_node_id = read_bounded_bytes(reader, MAX_IROH_NODE_ID_BYTES)?;
749 let iroh_direct_addresses = read_bounded_bytes_list(
750 reader,
751 MAX_IROH_DIRECT_ADDRESSES,
752 MAX_IROH_DIRECT_ADDRESS_BYTES,
753 )?;
754 let iroh_relay_hint = read_optional_bounded_bytes(reader, MAX_IROH_RELAY_HINT_BYTES)?;
755 let max_control_frame_bytes = reader.read_u32::<LittleEndian>()?;
756 let max_open_streams = reader.read_u16::<LittleEndian>()?;
757
758 Ok(Self {
759 magic,
760 prelude_version,
761 selected_zakura_protocol,
762 network_id,
763 chain_id,
764 capabilities,
765 initiator_upgrade_nonce,
766 responder_upgrade_nonce,
767 local_zebra_nonce,
768 remote_zebra_nonce,
769 iroh_node_id,
770 iroh_direct_addresses,
771 iroh_relay_hint,
772 max_control_frame_bytes,
773 max_open_streams,
774 })
775 }
776}
777
778#[derive(Clone, Debug, Eq, PartialEq)]
780pub struct P2pV2UpgradeReject {
781 pub magic: [u8; 8],
783 pub prelude_version: u16,
785 pub reason: ZakuraRejectReason,
787}
788
789impl P2pV2UpgradeReject {
790 fn encode_to<W: Write>(&self, writer: &mut W) -> Result<(), ZakuraProtocolError> {
791 writer.write_all(&self.magic)?;
792 writer.write_u16::<LittleEndian>(self.prelude_version)?;
793 writer.write_u16::<LittleEndian>(self.reason.code())?;
794 Ok(())
795 }
796
797 fn decode_from<R: Read>(reader: &mut R) -> Result<Self, ZakuraProtocolError> {
798 let mut magic = [0; 8];
799 reader.read_exact(&mut magic)?;
800 let prelude_version = reader.read_u16::<LittleEndian>()?;
801 let reason = ZakuraRejectReason::from_u16(reader.read_u16::<LittleEndian>()?)?;
802 Ok(Self {
803 magic,
804 prelude_version,
805 reason,
806 })
807 }
808}
809
810#[derive(Copy, Clone, Debug, Eq, PartialEq)]
812#[repr(u8)]
813pub enum ZakuraControlRole {
814 Initiator = 1,
816 Responder = 2,
818}
819
820impl ZakuraControlRole {
821 fn from_u8(value: u8) -> Result<Self, ZakuraProtocolError> {
822 match value {
823 1 => Ok(Self::Initiator),
824 2 => Ok(Self::Responder),
825 _ => Err(ZakuraProtocolError::InvalidRole(value)),
826 }
827 }
828
829 fn code(self) -> u8 {
830 self as u8
832 }
833}
834
835#[derive(Copy, Clone, Debug, Eq, PartialEq)]
837#[repr(u8)]
838pub enum ZakuraHandshakePath {
839 Upgraded = 0,
841 Native = 1,
843}
844
845impl ZakuraHandshakePath {
846 fn from_u8(value: u8) -> Result<Self, ZakuraProtocolError> {
847 match value {
848 0 => Ok(Self::Upgraded),
849 1 => Ok(Self::Native),
850 _ => Err(ZakuraProtocolError::InvalidHandshakePath(value)),
851 }
852 }
853
854 fn code(self) -> u8 {
855 self as u8
857 }
858}
859
860#[derive(Copy, Clone, Debug, Eq, PartialEq)]
862pub struct ZakuraLimits {
863 pub max_frame_bytes: u32,
865 pub max_message_bytes: u32,
867 pub max_open_streams: u16,
869 pub max_inbound_queue_depth: u16,
871 pub idle_timeout_millis: u32,
873}
874
875pub type ZakuraInitialLimits = ZakuraLimits;
877
878pub type ZakuraAcceptedLimits = ZakuraLimits;
880
881#[derive(Clone, Debug, Eq, PartialEq)]
883pub struct ZakuraControlHello {
884 pub magic: [u8; 8],
886 pub control_version: u16,
888 pub selected_zakura_protocol: u16,
890 pub handshake_path: ZakuraHandshakePath,
892 pub role: ZakuraControlRole,
894 pub network_id: ZakuraNetworkId,
896 pub chain_id: [u8; 32],
898 pub iroh_node_id: Vec<u8>,
900 pub peer_nonce: [u8; 32],
902 pub initiator_upgrade_nonce: [u8; 32],
904 pub responder_upgrade_nonce: [u8; 32],
906 pub legacy_upgrade_transcript: [u8; 32],
908 pub capabilities: u64,
910 pub required_channels: u64,
912 pub initial_limits: ZakuraInitialLimits,
914}
915
916impl ZakuraControlHello {
917 pub fn encode(&self) -> Result<Vec<u8>, ZakuraProtocolError> {
919 let mut bytes = Vec::new();
920 bytes.write_all(&self.magic)?;
921 bytes.write_u16::<LittleEndian>(self.control_version)?;
922 bytes.write_u16::<LittleEndian>(self.selected_zakura_protocol)?;
923 bytes.write_u8(self.handshake_path.code())?;
924 bytes.write_u8(self.role.code())?;
925 bytes.write_u32::<LittleEndian>(self.network_id.code())?;
926 bytes.write_all(&self.chain_id)?;
927 write_bounded_bytes(&mut bytes, &self.iroh_node_id, MAX_IROH_NODE_ID_BYTES)?;
928 bytes.write_all(&self.peer_nonce)?;
929 bytes.write_all(&self.initiator_upgrade_nonce)?;
930 bytes.write_all(&self.responder_upgrade_nonce)?;
931 bytes.write_all(&self.legacy_upgrade_transcript)?;
932 bytes.write_u64::<LittleEndian>(self.capabilities)?;
933 bytes.write_u64::<LittleEndian>(self.required_channels)?;
934 self.initial_limits.encode_to(&mut bytes)?;
935 if bytes.len() > MAX_CONTROL_PAYLOAD_BYTES {
936 return Err(ZakuraProtocolError::OversizedPayload {
937 actual: bytes.len(),
938 max: MAX_CONTROL_PAYLOAD_BYTES,
939 });
940 }
941 Ok(bytes)
942 }
943
944 pub fn decode(bytes: &[u8]) -> Result<Self, ZakuraProtocolError> {
946 if bytes.len() > MAX_CONTROL_PAYLOAD_BYTES {
947 return Err(ZakuraProtocolError::OversizedPayload {
948 actual: bytes.len(),
949 max: MAX_CONTROL_PAYLOAD_BYTES,
950 });
951 }
952 let mut reader = Cursor::new(bytes);
953 let mut magic = [0; 8];
954 reader.read_exact(&mut magic)?;
955 let control_version = reader.read_u16::<LittleEndian>()?;
956 let selected_zakura_protocol = reader.read_u16::<LittleEndian>()?;
957 let handshake_path = ZakuraHandshakePath::from_u8(reader.read_u8()?)?;
958 let role = ZakuraControlRole::from_u8(reader.read_u8()?)?;
959 let network_id = ZakuraNetworkId::from_u32(reader.read_u32::<LittleEndian>()?)?;
960 let mut chain_id = [0; 32];
961 reader.read_exact(&mut chain_id)?;
962 let iroh_node_id = read_bounded_bytes(&mut reader, MAX_IROH_NODE_ID_BYTES)?;
963 let mut peer_nonce = [0; 32];
964 reader.read_exact(&mut peer_nonce)?;
965 let mut initiator_upgrade_nonce = [0; 32];
966 reader.read_exact(&mut initiator_upgrade_nonce)?;
967 let mut responder_upgrade_nonce = [0; 32];
968 reader.read_exact(&mut responder_upgrade_nonce)?;
969 let mut legacy_upgrade_transcript = [0; 32];
970 reader.read_exact(&mut legacy_upgrade_transcript)?;
971 let capabilities = reader.read_u64::<LittleEndian>()?;
972 let required_channels = reader.read_u64::<LittleEndian>()?;
973 let initial_limits = ZakuraInitialLimits::decode_from(&mut reader)?;
974 reject_trailing(bytes, &reader)?;
975
976 Ok(Self {
977 magic,
978 control_version,
979 selected_zakura_protocol,
980 handshake_path,
981 role,
982 network_id,
983 chain_id,
984 iroh_node_id,
985 peer_nonce,
986 initiator_upgrade_nonce,
987 responder_upgrade_nonce,
988 legacy_upgrade_transcript,
989 capabilities,
990 required_channels,
991 initial_limits,
992 })
993 }
994
995 pub fn validate(
997 &self,
998 expected: &ZakuraControlValidation<'_>,
999 ) -> Result<(), ZakuraValidationError> {
1000 if self.magic != CONTROL_HELLO_MAGIC {
1001 return Err(ZakuraValidationError::MalformedControl);
1002 }
1003 if self.control_version != CONTROL_VERSION {
1004 return Err(ZakuraValidationError::UnsupportedVersion("control"));
1005 }
1006 if self.selected_zakura_protocol != expected.selected_zakura_protocol {
1007 return Err(ZakuraValidationError::IncompatibleZakuraProtocol);
1008 }
1009 if self.handshake_path != expected.handshake_path {
1010 return Err(ZakuraValidationError::MalformedControl);
1011 }
1012 if self.role != expected.remote_role {
1013 return Err(ZakuraValidationError::MalformedControl);
1014 }
1015 if self.network_id != expected.local.network_id {
1016 return Err(ZakuraValidationError::WrongNetwork);
1017 }
1018 if self.chain_id != expected.local.chain_id {
1019 return Err(ZakuraValidationError::WrongChain);
1020 }
1021 if self.iroh_node_id.as_slice() != expected.authenticated_remote_id {
1022 return Err(ZakuraValidationError::IdentityMismatch);
1023 }
1024 if self.capabilities & expected.local.required_capabilities
1025 != expected.local.required_capabilities
1026 {
1027 return Err(ZakuraValidationError::MissingRequiredCapability);
1028 }
1029 if self.required_channels & !expected.local.supported_channels != 0 {
1030 return Err(ZakuraValidationError::MissingRequiredCapability);
1031 }
1032 validate_initial_limits(self.initial_limits, expected.local)
1033 .map_err(ZakuraValidationError::from_wire_reject)?;
1034
1035 match expected.handshake_path {
1036 ZakuraHandshakePath::Upgraded => {
1037 if self.initiator_upgrade_nonce != expected.initiator_upgrade_nonce
1038 || self.responder_upgrade_nonce != expected.responder_upgrade_nonce
1039 {
1040 return Err(ZakuraValidationError::UpgradeNonceMismatch);
1041 }
1042 if self.legacy_upgrade_transcript != expected.legacy_upgrade_transcript {
1043 return Err(ZakuraValidationError::TranscriptMismatch);
1044 }
1045 }
1046 ZakuraHandshakePath::Native => {
1047 if self.initiator_upgrade_nonce != [0; 32]
1048 || self.responder_upgrade_nonce != [0; 32]
1049 {
1050 return Err(ZakuraValidationError::UpgradeNonceMismatch);
1051 }
1052 if self.legacy_upgrade_transcript != [0; 32] {
1053 return Err(ZakuraValidationError::TranscriptMismatch);
1054 }
1055 }
1056 }
1057
1058 Ok(())
1059 }
1060}
1061
1062#[derive(Copy, Clone, Debug)]
1064pub struct ZakuraControlValidation<'a> {
1065 pub local: &'a ZakuraHandshakeConfig,
1067 pub authenticated_remote_id: &'a [u8],
1069 pub selected_zakura_protocol: u16,
1071 pub handshake_path: ZakuraHandshakePath,
1073 pub remote_role: ZakuraControlRole,
1075 pub initiator_upgrade_nonce: [u8; 32],
1077 pub responder_upgrade_nonce: [u8; 32],
1079 pub legacy_upgrade_transcript: [u8; 32],
1081}
1082
1083#[derive(Clone, Debug, Eq, PartialEq)]
1085pub struct ZakuraControlAck {
1086 pub magic: [u8; 8],
1088 pub control_version: u16,
1090 pub selected_zakura_protocol: u16,
1092 pub peer_nonce: [u8; 32],
1094 pub remote_peer_nonce: [u8; 32],
1096 pub accepted_capabilities: u64,
1098 pub accepted_channels: u64,
1100 pub accepted_limits: ZakuraAcceptedLimits,
1102}
1103
1104impl ZakuraControlAck {
1105 pub fn encode(&self) -> Result<Vec<u8>, ZakuraProtocolError> {
1107 let mut bytes = Vec::new();
1108 bytes.write_all(&self.magic)?;
1109 bytes.write_u16::<LittleEndian>(self.control_version)?;
1110 bytes.write_u16::<LittleEndian>(self.selected_zakura_protocol)?;
1111 bytes.write_all(&self.peer_nonce)?;
1112 bytes.write_all(&self.remote_peer_nonce)?;
1113 bytes.write_u64::<LittleEndian>(self.accepted_capabilities)?;
1114 bytes.write_u64::<LittleEndian>(self.accepted_channels)?;
1115 self.accepted_limits.encode_to(&mut bytes)?;
1116 if bytes.len() > MAX_CONTROL_PAYLOAD_BYTES {
1117 return Err(ZakuraProtocolError::OversizedPayload {
1118 actual: bytes.len(),
1119 max: MAX_CONTROL_PAYLOAD_BYTES,
1120 });
1121 }
1122 Ok(bytes)
1123 }
1124
1125 pub fn decode(bytes: &[u8]) -> Result<Self, ZakuraProtocolError> {
1127 if bytes.len() > MAX_CONTROL_PAYLOAD_BYTES {
1128 return Err(ZakuraProtocolError::OversizedPayload {
1129 actual: bytes.len(),
1130 max: MAX_CONTROL_PAYLOAD_BYTES,
1131 });
1132 }
1133 let mut reader = Cursor::new(bytes);
1134 let mut magic = [0; 8];
1135 reader.read_exact(&mut magic)?;
1136 let control_version = reader.read_u16::<LittleEndian>()?;
1137 let selected_zakura_protocol = reader.read_u16::<LittleEndian>()?;
1138 let mut peer_nonce = [0; 32];
1139 reader.read_exact(&mut peer_nonce)?;
1140 let mut remote_peer_nonce = [0; 32];
1141 reader.read_exact(&mut remote_peer_nonce)?;
1142 let accepted_capabilities = reader.read_u64::<LittleEndian>()?;
1143 let accepted_channels = reader.read_u64::<LittleEndian>()?;
1144 let accepted_limits = ZakuraAcceptedLimits::decode_from(&mut reader)?;
1145 reject_trailing(bytes, &reader)?;
1146
1147 Ok(Self {
1148 magic,
1149 control_version,
1150 selected_zakura_protocol,
1151 peer_nonce,
1152 remote_peer_nonce,
1153 accepted_capabilities,
1154 accepted_channels,
1155 accepted_limits,
1156 })
1157 }
1158
1159 pub fn validate(
1161 &self,
1162 selected_zakura_protocol: u16,
1163 local_peer_nonce: [u8; 32],
1164 remote_peer_nonce: [u8; 32],
1165 requested_limits: &ZakuraInitialLimits,
1166 local: &ZakuraHandshakeConfig,
1167 ) -> Result<(), ZakuraValidationError> {
1168 if self.magic != CONTROL_ACK_MAGIC || self.control_version != CONTROL_VERSION {
1169 return Err(ZakuraValidationError::MalformedControl);
1170 }
1171 if self.selected_zakura_protocol != selected_zakura_protocol {
1172 return Err(ZakuraValidationError::IncompatibleZakuraProtocol);
1173 }
1174 if self.remote_peer_nonce != local_peer_nonce || self.peer_nonce != remote_peer_nonce {
1175 return Err(ZakuraValidationError::ControlNonceMismatch);
1176 }
1177 validate_initial_limits(self.accepted_limits, local)
1178 .map_err(ZakuraValidationError::from_wire_reject)?;
1179 if self.accepted_limits.max_frame_bytes > requested_limits.max_frame_bytes
1180 || self.accepted_limits.max_message_bytes > requested_limits.max_message_bytes
1181 || self.accepted_limits.max_open_streams > requested_limits.max_open_streams
1182 || self.accepted_limits.max_inbound_queue_depth
1183 > requested_limits.max_inbound_queue_depth
1184 || self.accepted_limits.idle_timeout_millis > requested_limits.idle_timeout_millis
1185 {
1186 return Err(ZakuraValidationError::ResourceLimit);
1187 }
1188 Ok(())
1189 }
1190}
1191
1192#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1194pub struct StreamPrelude {
1195 pub magic: [u8; 4],
1197 pub stream_kind: u16,
1199 pub stream_version: u16,
1201 pub request_id: Option<u64>,
1203 pub max_frame_bytes: u32,
1205}
1206
1207impl StreamPrelude {
1208 pub fn encode(&self) -> Result<Vec<u8>, ZakuraProtocolError> {
1210 let mut bytes = Vec::new();
1211 bytes.write_all(&self.magic)?;
1212 bytes.write_u16::<LittleEndian>(self.stream_kind)?;
1213 bytes.write_u16::<LittleEndian>(self.stream_version)?;
1214 match self.request_id {
1215 Some(request_id) => {
1216 bytes.write_u8(1)?;
1217 bytes.write_u64::<LittleEndian>(request_id)?;
1218 }
1219 None => bytes.write_u8(0)?,
1220 }
1221 bytes.write_u32::<LittleEndian>(self.max_frame_bytes)?;
1222 Ok(bytes)
1223 }
1224
1225 pub fn decode(bytes: &[u8]) -> Result<Self, ZakuraProtocolError> {
1227 let mut reader = Cursor::new(bytes);
1228 let mut magic = [0; 4];
1229 reader.read_exact(&mut magic)?;
1230 if magic != STREAM_PRELUDE_MAGIC {
1231 return Err(ZakuraProtocolError::InvalidMagic);
1232 }
1233 let stream_kind = reader.read_u16::<LittleEndian>()?;
1234 let stream_version = reader.read_u16::<LittleEndian>()?;
1235 let request_id = match reader.read_u8()? {
1236 0 => None,
1237 1 => Some(reader.read_u64::<LittleEndian>()?),
1238 value => return Err(ZakuraProtocolError::InvalidFlag(value)),
1239 };
1240 let max_frame_bytes = reader.read_u32::<LittleEndian>()?;
1241 reject_trailing(bytes, &reader)?;
1242 Ok(Self {
1243 magic,
1244 stream_kind,
1245 stream_version,
1246 request_id,
1247 max_frame_bytes,
1248 })
1249 }
1250}
1251
1252#[derive(Clone, Debug, Eq, PartialEq)]
1254pub struct Frame {
1255 pub message_type: u16,
1257 pub flags: u16,
1259 pub payload: Vec<u8>,
1261}
1262
1263impl Frame {
1264 pub fn encode(&self, max_frame_bytes: u32) -> Result<Vec<u8>, ZakuraProtocolError> {
1266 let max_frame_bytes = usize_from_u32(max_frame_bytes, "frame cap")?;
1267 if self.payload.len() > max_frame_bytes.saturating_sub(FRAME_HEADER_BYTES) {
1268 return Err(ZakuraProtocolError::OversizedPayload {
1269 actual: self.payload.len(),
1270 max: max_frame_bytes.saturating_sub(FRAME_HEADER_BYTES),
1271 });
1272 }
1273 let mut bytes = Vec::new();
1274 bytes.write_u16::<LittleEndian>(self.message_type)?;
1275 bytes.write_u16::<LittleEndian>(self.flags)?;
1276 bytes.write_u32::<LittleEndian>(u32_from_usize(self.payload.len(), "payload length")?)?;
1277 bytes.write_all(&self.payload)?;
1278 Ok(bytes)
1279 }
1280
1281 pub fn decode(bytes: &[u8], max_frame_bytes: u32) -> Result<Self, ZakuraProtocolError> {
1283 let max_frame_bytes = usize_from_u32(max_frame_bytes, "frame cap")?;
1284 if bytes.len() > max_frame_bytes {
1285 return Err(ZakuraProtocolError::OversizedPayload {
1286 actual: bytes.len(),
1287 max: max_frame_bytes,
1288 });
1289 }
1290 let mut reader = Cursor::new(bytes);
1291 let message_type = reader.read_u16::<LittleEndian>()?;
1292 let flags = reader.read_u16::<LittleEndian>()?;
1293 let payload_len = usize_from_u32(reader.read_u32::<LittleEndian>()?, "payload length")?;
1294 if payload_len > max_frame_bytes.saturating_sub(FRAME_HEADER_BYTES) {
1295 return Err(ZakuraProtocolError::OversizedPayload {
1296 actual: payload_len,
1297 max: max_frame_bytes.saturating_sub(FRAME_HEADER_BYTES),
1298 });
1299 }
1300 let payload = read_exact_vec(&mut reader, payload_len)?;
1301 reject_trailing(bytes, &reader)?;
1302 Ok(Self {
1303 message_type,
1304 flags,
1305 payload,
1306 })
1307 }
1308}
1309
1310#[derive(Clone, Debug, Eq, PartialEq)]
1312pub struct PendingUpgrade {
1313 pub expected_peer_id: ZakuraPeerId,
1315 pub selected_zakura_protocol: u16,
1317 pub initiator_upgrade_nonce: [u8; 32],
1319 pub responder_upgrade_nonce: [u8; 32],
1321 pub legacy_upgrade_transcript: [u8; 32],
1323}
1324
1325#[derive(Clone, Debug, Eq, PartialEq)]
1326struct PendingUpgradeEntry {
1327 pending: PendingUpgrade,
1328 expires_at: Instant,
1329}
1330
1331#[derive(Debug)]
1333pub struct PendingUpgradeRegistry {
1334 entries: HashMap<ZakuraPeerId, PendingUpgradeEntry>,
1335 max_entries: usize,
1336 ttl: Duration,
1337}
1338
1339impl PendingUpgradeRegistry {
1340 pub fn new(max_entries: usize, ttl: Duration) -> Self {
1342 Self {
1343 entries: HashMap::new(),
1344 max_entries,
1345 ttl,
1346 }
1347 }
1348
1349 pub fn insert(
1351 &mut self,
1352 now: Instant,
1353 pending: PendingUpgrade,
1354 ) -> Result<(), ZakuraRejectReason> {
1355 self.prune_expired(now);
1356 if self.entries.len() >= self.max_entries
1357 && !self.entries.contains_key(&pending.expected_peer_id)
1358 {
1359 return Err(ZakuraRejectReason::ResourceLimit);
1360 }
1361 self.entries.insert(
1362 pending.expected_peer_id.clone(),
1363 PendingUpgradeEntry {
1364 pending,
1365 expires_at: now + self.ttl,
1366 },
1367 );
1368 Ok(())
1369 }
1370
1371 pub fn take(&mut self, now: Instant, peer_id: &ZakuraPeerId) -> Option<PendingUpgrade> {
1373 self.prune_expired(now);
1374 self.entries.remove(peer_id).map(|entry| entry.pending)
1375 }
1376
1377 pub fn len(&self) -> usize {
1379 self.entries.len()
1380 }
1381
1382 pub fn is_empty(&self) -> bool {
1384 self.entries.is_empty()
1385 }
1386
1387 fn prune_expired(&mut self, now: Instant) {
1388 self.entries.retain(|_, entry| entry.expires_at > now);
1389 }
1390}
1391
1392#[derive(Debug, Default)]
1394pub struct ZakuraPeerSupervisor {
1395 peers: HashMap<ZakuraPeerId, [u8; TRANSCRIPT_HASH_BYTES]>,
1396}
1397
1398#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1400pub enum AuthenticatedPeerRegistration {
1401 Registered,
1403 Duplicate,
1405}
1406
1407impl ZakuraPeerSupervisor {
1408 pub fn register_authenticated(
1410 &mut self,
1411 peer_id: ZakuraPeerId,
1412 transcript_hash: [u8; TRANSCRIPT_HASH_BYTES],
1413 ) -> AuthenticatedPeerRegistration {
1414 if let Some(existing_hash) = self.peers.get(&peer_id) {
1415 if existing_hash <= &transcript_hash {
1417 metrics::counter!("zakura.p2p.handshake.duplicate").increment(1);
1418 return AuthenticatedPeerRegistration::Duplicate;
1419 }
1420 }
1421
1422 self.peers.insert(peer_id, transcript_hash);
1423 metrics::counter!("zakura.p2p.handshake.upgraded").increment(1);
1424 AuthenticatedPeerRegistration::Registered
1425 }
1426
1427 pub fn deregister_authenticated(&mut self, peer_id: &ZakuraPeerId) {
1429 self.peers.remove(peer_id);
1430 }
1431}
1432
1433impl PendingUpgrade {
1434 pub fn new(
1436 expected_peer_id: ZakuraPeerId,
1437 selected_zakura_protocol: u16,
1438 initiator_upgrade_nonce: [u8; 32],
1439 responder_upgrade_nonce: [u8; 32],
1440 legacy_upgrade_transcript: [u8; 32],
1441 ) -> Self {
1442 Self {
1443 expected_peer_id,
1444 selected_zakura_protocol,
1445 initiator_upgrade_nonce,
1446 responder_upgrade_nonce,
1447 legacy_upgrade_transcript,
1448 }
1449 }
1450}
1451
1452pub fn legacy_upgrade_transcript(
1454 initiator_version_message: &VersionMessage,
1455 responder_version_message: &VersionMessage,
1456 p2p_v2_upgrade_init: &P2pV2UpgradeInit,
1457 p2p_v2_upgrade_accept: &P2pV2UpgradeAccept,
1458) -> Result<[u8; TRANSCRIPT_HASH_BYTES], ZakuraProtocolError> {
1459 let mut state = Blake2bParams::new()
1460 .hash_length(TRANSCRIPT_HASH_BYTES)
1461 .personal(b"zakura-upgrade1")
1462 .to_state();
1463
1464 write_version_message_to_hash(&mut state, initiator_version_message)?;
1465 write_version_message_to_hash(&mut state, responder_version_message)?;
1466 state.update(&P2pV2Upgrade::Init(p2p_v2_upgrade_init.clone()).encode()?);
1467 state.update(&P2pV2Upgrade::Accept(p2p_v2_upgrade_accept.clone()).encode()?);
1468
1469 let hash = state.finalize();
1470 let mut out = [0; TRANSCRIPT_HASH_BYTES];
1471 out.copy_from_slice(hash.as_bytes());
1472 Ok(out)
1473}
1474
1475pub fn select_zakura_protocol(
1477 local_min: u16,
1478 local_max: u16,
1479 remote_min: u16,
1480 remote_max: u16,
1481) -> Result<u16, ZakuraRejectReason> {
1482 if local_min > local_max || remote_min > remote_max {
1483 return Err(ZakuraRejectReason::IncompatibleZakuraProtocol);
1484 }
1485 let selected = min(local_max, remote_max);
1486 if selected < local_min || selected < remote_min {
1487 return Err(ZakuraRejectReason::IncompatibleZakuraProtocol);
1488 }
1489 Ok(selected)
1490}
1491
1492#[derive(Error, Debug)]
1494pub enum ZakuraProtocolError {
1495 #[error("Zakura payload length {actual} exceeds hard cap {max}")]
1497 OversizedPayload {
1498 actual: usize,
1500 max: usize,
1502 },
1503
1504 #[error("Zakura wire I/O error: {0}")]
1506 Io(#[from] io::Error),
1507
1508 #[error("invalid Zakura message discriminator {0}")]
1510 InvalidDiscriminator(u8),
1511
1512 #[error("invalid Zakura network id {0}")]
1514 InvalidNetworkId(u32),
1515
1516 #[error("invalid Zakura reject reason {0}")]
1518 InvalidRejectReason(u16),
1519
1520 #[error("invalid Zakura control role {0}")]
1522 InvalidRole(u8),
1523
1524 #[error("invalid Zakura handshake path {0}")]
1526 InvalidHandshakePath(u8),
1527
1528 #[error("invalid Zakura flag {0}")]
1530 InvalidFlag(u8),
1531
1532 #[error("invalid Zakura magic")]
1534 InvalidMagic,
1535
1536 #[error("trailing bytes in Zakura payload")]
1538 TrailingBytes,
1539
1540 #[error("empty Zakura {0}")]
1542 Empty(&'static str),
1543
1544 #[error("numeric overflow while encoding Zakura {0}")]
1546 NumericOverflow(&'static str),
1547}
1548
1549struct PreludeStaticFields<'a> {
1550 magic: [u8; 8],
1551 prelude_version: u16,
1552 network_id: ZakuraNetworkId,
1553 chain_id: [u8; 32],
1554 capabilities: u64,
1555 iroh_node_id: &'a [u8],
1556 iroh_direct_addresses: &'a [Vec<u8>],
1557 iroh_relay_hint: Option<&'a [u8]>,
1558 max_control_frame_bytes: u32,
1559 max_open_streams: u16,
1560}
1561
1562fn validate_prelude_static_fields(
1563 prelude: PreludeStaticFields<'_>,
1564 local: &ZakuraHandshakeConfig,
1565) -> Result<(), ZakuraRejectReason> {
1566 if prelude.magic != PRELUDE_MAGIC || prelude.prelude_version != local.prelude_version {
1567 return Err(ZakuraRejectReason::UnsupportedPreludeVersion);
1568 }
1569 if prelude.network_id != local.network_id {
1570 return Err(ZakuraRejectReason::WrongNetwork);
1571 }
1572 if prelude.chain_id != local.chain_id {
1573 return Err(ZakuraRejectReason::WrongChain);
1574 }
1575 if prelude.capabilities & local.required_capabilities != local.required_capabilities {
1576 return Err(ZakuraRejectReason::MissingRequiredCapability);
1577 }
1578 validate_peer_hints(
1579 prelude.iroh_node_id,
1580 prelude.iroh_direct_addresses,
1581 prelude.iroh_relay_hint,
1582 )
1583 .map_err(|_| ZakuraRejectReason::ResourceLimit)?;
1584 validate_resource_limits(
1585 prelude.max_control_frame_bytes,
1586 prelude.max_open_streams,
1587 local,
1588 )?;
1589 Ok(())
1590}
1591
1592fn validate_peer_nonce_labels(
1593 peer_local_zebra_nonce: Nonce,
1594 peer_remote_zebra_nonce: Nonce,
1595 local_observed: ZakuraLegacyNonces,
1596) -> Result<(), ZakuraRejectReason> {
1597 if peer_local_zebra_nonce != local_observed.remote_zebra_nonce
1598 || peer_remote_zebra_nonce != local_observed.local_zebra_nonce
1599 {
1600 return Err(ZakuraRejectReason::TemporaryUnavailable);
1601 }
1602 Ok(())
1603}
1604
1605fn validate_peer_hints(
1606 iroh_node_id: &[u8],
1607 iroh_direct_addresses: &[Vec<u8>],
1608 iroh_relay_hint: Option<&[u8]>,
1609) -> Result<(), ZakuraProtocolError> {
1610 validate_bounded_bytes(iroh_node_id, MAX_IROH_NODE_ID_BYTES, "iroh node id")?;
1611 if iroh_direct_addresses.len() > MAX_IROH_DIRECT_ADDRESSES {
1612 return Err(ZakuraProtocolError::OversizedPayload {
1613 actual: iroh_direct_addresses.len(),
1614 max: MAX_IROH_DIRECT_ADDRESSES,
1615 });
1616 }
1617 for address in iroh_direct_addresses {
1618 validate_bounded_bytes(address, MAX_IROH_DIRECT_ADDRESS_BYTES, "direct address")?;
1619 }
1620 if let Some(relay_hint) = iroh_relay_hint {
1621 validate_bounded_bytes(relay_hint, MAX_IROH_RELAY_HINT_BYTES, "relay hint")?;
1622 }
1623 Ok(())
1624}
1625
1626fn validate_resource_limits(
1627 max_control_frame_bytes: u32,
1628 max_open_streams: u16,
1629 local: &ZakuraHandshakeConfig,
1630) -> Result<(), ZakuraRejectReason> {
1631 if max_control_frame_bytes == 0
1632 || max_control_frame_bytes > local.max_control_frame_bytes
1633 || max_open_streams == 0
1634 || max_open_streams > local.max_open_streams
1635 {
1636 return Err(ZakuraRejectReason::ResourceLimit);
1637 }
1638 Ok(())
1639}
1640
1641fn validate_initial_limits(
1642 limits: ZakuraInitialLimits,
1643 local: &ZakuraHandshakeConfig,
1644) -> Result<(), ZakuraRejectReason> {
1645 if limits.max_frame_bytes == 0
1648 || limits.max_frame_bytes > local.max_message_bytes
1649 || limits.max_message_bytes == 0
1650 || limits.max_message_bytes > local.max_message_bytes
1651 || limits.max_open_streams == 0
1652 || limits.max_open_streams > local.max_open_streams
1653 || limits.max_inbound_queue_depth == 0
1654 || limits.max_inbound_queue_depth > local.max_inbound_queue_depth
1655 || limits.idle_timeout_millis == 0
1656 || limits.idle_timeout_millis > local.max_idle_timeout_millis
1657 {
1658 return Err(ZakuraRejectReason::ResourceLimit);
1659 }
1660 Ok(())
1661}
1662
1663impl ZakuraLimits {
1664 fn encode_to<W: Write>(&self, writer: &mut W) -> Result<(), ZakuraProtocolError> {
1665 writer.write_u32::<LittleEndian>(self.max_frame_bytes)?;
1666 writer.write_u32::<LittleEndian>(self.max_message_bytes)?;
1667 writer.write_u16::<LittleEndian>(self.max_open_streams)?;
1668 writer.write_u16::<LittleEndian>(self.max_inbound_queue_depth)?;
1669 writer.write_u32::<LittleEndian>(self.idle_timeout_millis)?;
1670 Ok(())
1671 }
1672
1673 fn decode_from<R: Read>(reader: &mut R) -> Result<Self, ZakuraProtocolError> {
1674 Ok(Self {
1675 max_frame_bytes: reader.read_u32::<LittleEndian>()?,
1676 max_message_bytes: reader.read_u32::<LittleEndian>()?,
1677 max_open_streams: reader.read_u16::<LittleEndian>()?,
1678 max_inbound_queue_depth: reader.read_u16::<LittleEndian>()?,
1679 idle_timeout_millis: reader.read_u32::<LittleEndian>()?,
1680 })
1681 }
1682}
1683
1684fn write_bounded_bytes<W: Write>(
1685 writer: &mut W,
1686 bytes: &[u8],
1687 max_len: usize,
1688) -> Result<(), ZakuraProtocolError> {
1689 validate_bounded_bytes(bytes, max_len, "byte string")?;
1690 writer.write_u16::<LittleEndian>(
1691 u16::try_from(bytes.len())
1692 .map_err(|_| ZakuraProtocolError::NumericOverflow("byte string length"))?,
1693 )?;
1694 writer.write_all(bytes)?;
1695 Ok(())
1696}
1697
1698fn write_optional_bounded_bytes<W: Write>(
1699 writer: &mut W,
1700 bytes: Option<&[u8]>,
1701 max_len: usize,
1702) -> Result<(), ZakuraProtocolError> {
1703 match bytes {
1704 Some(bytes) => {
1705 writer.write_u8(1)?;
1706 write_bounded_bytes(writer, bytes, max_len)?;
1707 }
1708 None => writer.write_u8(0)?,
1709 }
1710 Ok(())
1711}
1712
1713fn write_bounded_bytes_list<W: Write>(
1714 writer: &mut W,
1715 items: &[Vec<u8>],
1716 max_count: usize,
1717 max_item_len: usize,
1718) -> Result<(), ZakuraProtocolError> {
1719 if items.len() > max_count {
1720 return Err(ZakuraProtocolError::OversizedPayload {
1721 actual: items.len(),
1722 max: max_count,
1723 });
1724 }
1725 writer.write_u16::<LittleEndian>(
1726 u16::try_from(items.len())
1727 .map_err(|_| ZakuraProtocolError::NumericOverflow("list count"))?,
1728 )?;
1729 for item in items {
1730 write_bounded_bytes(writer, item, max_item_len)?;
1731 }
1732 Ok(())
1733}
1734
1735fn read_bounded_bytes<R: Read>(
1736 reader: &mut R,
1737 max_len: usize,
1738) -> Result<Vec<u8>, ZakuraProtocolError> {
1739 let len = usize::from(reader.read_u16::<LittleEndian>()?);
1740 if len > max_len {
1741 return Err(ZakuraProtocolError::OversizedPayload {
1742 actual: len,
1743 max: max_len,
1744 });
1745 }
1746 read_exact_vec(reader, len)
1747}
1748
1749fn read_optional_bounded_bytes<R: Read>(
1750 reader: &mut R,
1751 max_len: usize,
1752) -> Result<Option<Vec<u8>>, ZakuraProtocolError> {
1753 match reader.read_u8()? {
1754 0 => Ok(None),
1755 1 => Ok(Some(read_bounded_bytes(reader, max_len)?)),
1756 value => Err(ZakuraProtocolError::InvalidFlag(value)),
1757 }
1758}
1759
1760fn read_bounded_bytes_list<R: Read>(
1761 reader: &mut R,
1762 max_count: usize,
1763 max_item_len: usize,
1764) -> Result<Vec<Vec<u8>>, ZakuraProtocolError> {
1765 let count = usize::from(reader.read_u16::<LittleEndian>()?);
1766 if count > max_count {
1767 return Err(ZakuraProtocolError::OversizedPayload {
1768 actual: count,
1769 max: max_count,
1770 });
1771 }
1772 let mut items = Vec::with_capacity(count);
1773 for _ in 0..count {
1774 items.push(read_bounded_bytes(reader, max_item_len)?);
1775 }
1776 Ok(items)
1777}
1778
1779fn read_exact_vec<R: Read>(reader: &mut R, len: usize) -> Result<Vec<u8>, ZakuraProtocolError> {
1780 let mut bytes = vec![0; len];
1781 reader.read_exact(&mut bytes)?;
1782 Ok(bytes)
1783}
1784
1785fn validate_bounded_bytes(
1786 bytes: &[u8],
1787 max_len: usize,
1788 field: &'static str,
1789) -> Result<(), ZakuraProtocolError> {
1790 if bytes.is_empty() {
1791 return Err(ZakuraProtocolError::Empty(field));
1792 }
1793 if bytes.len() > max_len {
1794 return Err(ZakuraProtocolError::OversizedPayload {
1795 actual: bytes.len(),
1796 max: max_len,
1797 });
1798 }
1799 Ok(())
1800}
1801
1802fn reject_trailing(bytes: &[u8], reader: &Cursor<&[u8]>) -> Result<(), ZakuraProtocolError> {
1803 let consumed = usize::try_from(reader.position())
1804 .map_err(|_| ZakuraProtocolError::NumericOverflow("cursor position"))?;
1805 if consumed != bytes.len() {
1806 return Err(ZakuraProtocolError::TrailingBytes);
1807 }
1808 Ok(())
1809}
1810
1811fn usize_from_u32(value: u32, field: &'static str) -> Result<usize, ZakuraProtocolError> {
1812 usize::try_from(value).map_err(|_| ZakuraProtocolError::NumericOverflow(field))
1813}
1814
1815fn u32_from_usize(value: usize, field: &'static str) -> Result<u32, ZakuraProtocolError> {
1816 u32::try_from(value).map_err(|_| ZakuraProtocolError::NumericOverflow(field))
1817}
1818
1819fn write_version_message_to_hash(
1820 state: &mut blake2b_simd::State,
1821 version: &VersionMessage,
1822) -> Result<(), ZakuraProtocolError> {
1823 let mut bytes = Vec::new();
1824 bytes.write_u32::<LittleEndian>(version.version.0)?;
1825 bytes.write_u64::<LittleEndian>(version.services.bits())?;
1826 bytes.write_i64::<LittleEndian>(version.timestamp.timestamp())?;
1827 version.address_recv.zcash_serialize(&mut bytes)?;
1828 version.address_from.zcash_serialize(&mut bytes)?;
1829 bytes.write_u64::<LittleEndian>(version.nonce.0)?;
1830 version.user_agent.zcash_serialize(&mut bytes)?;
1831 bytes.write_u32::<LittleEndian>(version.start_height.0)?;
1832 bytes.write_u8(u8::from(version.relay))?;
1833 state.update(&bytes);
1834 Ok(())
1835}
1836
1837#[cfg(test)]
1838mod tests {
1839 use super::*;
1840
1841 use std::net::SocketAddr;
1842
1843 use chrono::{TimeZone, Utc};
1844 use futures::{SinkExt, StreamExt};
1845 use proptest::prelude::*;
1846 use tokio_util::codec::{FramedRead, FramedWrite};
1847
1848 use crate::protocol::external::{types::*, AddrInVersion, Codec, Message};
1849
1850 fn local_config() -> ZakuraHandshakeConfig {
1851 ZakuraHandshakeConfig::for_network(&Network::Mainnet)
1852 }
1853
1854 fn nonces() -> ZakuraLegacyNonces {
1855 ZakuraLegacyNonces {
1856 local_zebra_nonce: Nonce(10),
1857 remote_zebra_nonce: Nonce(20),
1858 }
1859 }
1860
1861 #[test]
1862 fn peer_id_digest_preserves_native_ids_and_canonicalizes_bounded_legacy_ids() {
1863 let native = ZakuraPeerId::new(vec![7; 32]).expect("the native node ID is valid");
1864 assert_eq!(native.digest(), [7; 32]);
1865
1866 let legacy = ZakuraPeerId::new(vec![1, 2, 3]).expect("the bounded legacy ID is valid");
1867 assert_eq!(legacy.digest(), legacy.digest());
1868 assert_ne!(legacy.digest(), native.digest());
1869 }
1870
1871 fn init() -> P2pV2UpgradeInit {
1872 let local = local_config();
1873 P2pV2UpgradeInit {
1874 magic: PRELUDE_MAGIC,
1875 prelude_version: PRELUDE_VERSION,
1876 zakura_protocol_min: 1,
1877 zakura_protocol_max: 1,
1878 network_id: local.network_id,
1879 chain_id: local.chain_id,
1880 capabilities: 0,
1881 local_zebra_nonce: nonces().remote_zebra_nonce,
1882 remote_zebra_nonce: nonces().local_zebra_nonce,
1883 upgrade_nonce: [1; 32],
1884 iroh_node_id: vec![7; 32],
1885 iroh_direct_addresses: vec![b"127.0.0.1:0".to_vec()],
1886 iroh_relay_hint: None,
1887 max_control_frame_bytes: 1024,
1888 max_open_streams: 8,
1889 }
1890 }
1891
1892 fn accept(init: &P2pV2UpgradeInit) -> P2pV2UpgradeAccept {
1893 let local = local_config();
1894 P2pV2UpgradeAccept {
1895 magic: PRELUDE_MAGIC,
1896 prelude_version: PRELUDE_VERSION,
1897 selected_zakura_protocol: 1,
1898 network_id: local.network_id,
1899 chain_id: local.chain_id,
1900 capabilities: 0,
1901 initiator_upgrade_nonce: init.upgrade_nonce,
1902 responder_upgrade_nonce: [2; 32],
1903 local_zebra_nonce: nonces().remote_zebra_nonce,
1904 remote_zebra_nonce: nonces().local_zebra_nonce,
1905 iroh_node_id: vec![8; 32],
1906 iroh_direct_addresses: vec![b"127.0.0.1:1".to_vec()],
1907 iroh_relay_hint: None,
1908 max_control_frame_bytes: 1024,
1909 max_open_streams: 8,
1910 }
1911 }
1912
1913 #[test]
1914 fn prelude_roundtrip_and_rejects_trailing() {
1915 let upgrade = P2pV2Upgrade::Init(init());
1916 let encoded = upgrade.encode().expect("valid init encodes");
1917 assert_eq!(
1918 P2pV2Upgrade::decode(&encoded).expect("valid init decodes"),
1919 upgrade
1920 );
1921
1922 let mut with_trailing = encoded;
1923 with_trailing.push(0);
1924 assert!(matches!(
1925 P2pV2Upgrade::decode(&with_trailing),
1926 Err(ZakuraProtocolError::TrailingBytes)
1927 ));
1928 }
1929
1930 #[test]
1931 fn prelude_rejects_oversized_inputs_before_decoding() {
1932 let oversized = vec![0; MAX_PRELUDE_PAYLOAD_BYTES + 1];
1933 assert!(matches!(
1934 P2pV2Upgrade::decode(&oversized),
1935 Err(ZakuraProtocolError::OversizedPayload { .. })
1936 ));
1937 }
1938
1939 #[test]
1940 fn init_validation_selects_overlap_and_rejects_bad_ranges() {
1941 let local = local_config();
1942 assert_eq!(init().validate(&local, nonces()), Ok(1));
1943
1944 let mut bad = init();
1945 bad.zakura_protocol_min = 2;
1946 bad.zakura_protocol_max = 3;
1947 assert_eq!(
1948 bad.validate(&local, nonces()),
1949 Err(ZakuraRejectReason::IncompatibleZakuraProtocol)
1950 );
1951 }
1952
1953 #[test]
1954 fn prelude_validation_rejects_network_chain_and_nonce_mismatch() {
1955 let local = local_config();
1956
1957 let mut wrong_network = init();
1958 wrong_network.network_id = ZakuraNetworkId::Testnet;
1959 assert_eq!(
1960 wrong_network.validate(&local, nonces()),
1961 Err(ZakuraRejectReason::WrongNetwork)
1962 );
1963
1964 let mut wrong_chain = init();
1965 wrong_chain.chain_id = [9; 32];
1966 assert_eq!(
1967 wrong_chain.validate(&local, nonces()),
1968 Err(ZakuraRejectReason::WrongChain)
1969 );
1970
1971 let mut wrong_nonce = init();
1972 wrong_nonce.local_zebra_nonce = Nonce(99);
1973 assert_eq!(
1974 wrong_nonce.validate(&local, nonces()),
1975 Err(ZakuraRejectReason::TemporaryUnavailable)
1976 );
1977 }
1978
1979 fn cohort_local(tag: &str) -> ZakuraHandshakeConfig {
1980 ZakuraHandshakeConfig::for_network_with_dev_cohort(&Network::Mainnet, Some(tag))
1981 }
1982
1983 fn cohort_init(tag: &str) -> P2pV2UpgradeInit {
1984 let local = cohort_local(tag);
1985 let mut init = init();
1986 init.network_id = local.network_id;
1987 init.chain_id = local.chain_id;
1988 init
1989 }
1990
1991 #[test]
1992 fn dev_cohort_overlay_scopes_network_and_chain_id() {
1993 let plain = ZakuraHandshakeConfig::for_network(&Network::Mainnet);
1994
1995 assert_eq!(
1997 ZakuraHandshakeConfig::for_network_with_dev_cohort(&Network::Mainnet, None),
1998 plain
1999 );
2000 assert_eq!(
2001 ZakuraHandshakeConfig::for_network_with_dev_cohort(&Network::Mainnet, Some("")),
2002 plain
2003 );
2004
2005 let cohort = cohort_local("evan-breaking-change");
2007 assert_eq!(cohort.network_id, ZakuraNetworkId::Configured);
2008 assert_ne!(cohort.chain_id, plain.chain_id);
2009
2010 assert_eq!(
2012 cohort.chain_id,
2013 cohort_local("evan-breaking-change").chain_id
2014 );
2015 assert_ne!(cohort.chain_id, cohort_local("roman-test").chain_id);
2016 }
2017
2018 #[test]
2019 fn derive_dev_chain_id_never_collides_with_genesis() {
2020 let genesis = Network::Mainnet.genesis_hash().0;
2021 for tag in ["", "a", "evan-breaking-change", "roman-test"] {
2022 assert_ne!(derive_dev_chain_id(genesis, tag), genesis);
2023 }
2024 let testnet_genesis = Network::new_default_testnet().genesis_hash().0;
2026 assert_ne!(
2027 derive_dev_chain_id(genesis, "shared"),
2028 derive_dev_chain_id(testnet_genesis, "shared"),
2029 );
2030 }
2031
2032 #[test]
2033 fn dev_cohort_prelude_matches_same_tag_and_rejects_others() {
2034 assert_eq!(
2036 cohort_init("alpha").validate(&cohort_local("alpha"), nonces()),
2037 Ok(1)
2038 );
2039
2040 assert_eq!(
2042 cohort_init("beta").validate(&cohort_local("alpha"), nonces()),
2043 Err(ZakuraRejectReason::WrongChain)
2044 );
2045
2046 assert_eq!(
2048 init().validate(&cohort_local("alpha"), nonces()),
2049 Err(ZakuraRejectReason::WrongNetwork)
2050 );
2051
2052 assert_eq!(
2054 cohort_init("alpha").validate(&local_config(), nonces()),
2055 Err(ZakuraRejectReason::WrongNetwork)
2056 );
2057 }
2058
2059 #[test]
2060 fn accept_validation_distinguishes_upgrade_nonce_mismatch() {
2061 let local = local_config();
2062 let init = init();
2063 let mut accept = accept(&init);
2064 accept.initiator_upgrade_nonce = [9; 32];
2065
2066 assert_eq!(
2067 accept.validate(&local, nonces(), &init),
2068 Err(ZakuraValidationError::UpgradeNonceMismatch)
2069 );
2070 assert_eq!(
2071 accept
2072 .validate(&local, nonces(), &init)
2073 .unwrap_err()
2074 .failure_class(),
2075 ZakuraFailureClass::PotentiallyPunitive
2076 );
2077 }
2078
2079 #[test]
2080 fn bounded_lists_enforce_maximum_lengths() {
2081 let mut too_many = init();
2082 too_many.iroh_direct_addresses = vec![b"x".to_vec(); MAX_IROH_DIRECT_ADDRESSES + 1];
2083 assert!(P2pV2Upgrade::Init(too_many).encode().is_err());
2084
2085 let mut too_long = init();
2086 too_long.iroh_direct_addresses = vec![vec![1; MAX_IROH_DIRECT_ADDRESS_BYTES + 1]];
2087 assert!(P2pV2Upgrade::Init(too_long).encode().is_err());
2088 }
2089
2090 proptest! {
2091 #[test]
2092 fn arbitrary_prelude_decode_never_panics(
2093 bytes in prop::collection::vec(any::<u8>(), 0..(MAX_PRELUDE_PAYLOAD_BYTES + 8))
2094 ) {
2095 let _ = P2pV2Upgrade::decode(&bytes);
2096 }
2097
2098 #[test]
2099 fn arbitrary_control_hello_decode_never_panics(
2100 bytes in prop::collection::vec(any::<u8>(), 0..2048usize)
2101 ) {
2102 let _ = ZakuraControlHello::decode(&bytes);
2103 }
2104
2105 #[test]
2106 fn arbitrary_control_ack_decode_never_panics(
2107 bytes in prop::collection::vec(any::<u8>(), 0..512usize)
2108 ) {
2109 let _ = ZakuraControlAck::decode(&bytes);
2110 }
2111
2112 #[test]
2113 fn arbitrary_stream_prelude_decode_never_panics(
2114 bytes in prop::collection::vec(any::<u8>(), 0..64usize)
2115 ) {
2116 let _ = StreamPrelude::decode(&bytes);
2117 }
2118
2119 #[test]
2120 fn arbitrary_frame_decode_never_panics(
2121 bytes in prop::collection::vec(any::<u8>(), 0..512usize),
2122 max_frame_bytes in 0u32..512
2123 ) {
2124 let _ = Frame::decode(&bytes, max_frame_bytes);
2125 }
2126 }
2127
2128 #[test]
2129 fn control_hello_validates_identity_transcript_and_native_zeroes() {
2130 let local = local_config();
2131 let hello = ZakuraControlHello {
2132 magic: CONTROL_HELLO_MAGIC,
2133 control_version: CONTROL_VERSION,
2134 selected_zakura_protocol: 1,
2135 handshake_path: ZakuraHandshakePath::Upgraded,
2136 role: ZakuraControlRole::Initiator,
2137 network_id: local.network_id,
2138 chain_id: local.chain_id,
2139 iroh_node_id: vec![7; 32],
2140 peer_nonce: [3; 32],
2141 initiator_upgrade_nonce: [1; 32],
2142 responder_upgrade_nonce: [2; 32],
2143 legacy_upgrade_transcript: [4; 32],
2144 capabilities: 0,
2145 required_channels: 0,
2146 initial_limits: ZakuraInitialLimits {
2147 max_frame_bytes: 1024,
2148 max_message_bytes: 2048,
2149 max_open_streams: 8,
2150 max_inbound_queue_depth: 8,
2151 idle_timeout_millis: 1000,
2152 },
2153 };
2154 let expected = ZakuraControlValidation {
2155 local: &local,
2156 authenticated_remote_id: &[7; 32],
2157 selected_zakura_protocol: 1,
2158 handshake_path: ZakuraHandshakePath::Upgraded,
2159 remote_role: ZakuraControlRole::Initiator,
2160 initiator_upgrade_nonce: [1; 32],
2161 responder_upgrade_nonce: [2; 32],
2162 legacy_upgrade_transcript: [4; 32],
2163 };
2164
2165 let encoded = hello.encode().expect("valid hello encodes");
2166 let decoded = ZakuraControlHello::decode(&encoded).expect("valid hello decodes");
2167 assert_eq!(decoded.validate(&expected), Ok(()));
2168
2169 let mut wrong_identity = decoded.clone();
2170 wrong_identity.iroh_node_id = vec![8; 32];
2171 assert_eq!(
2172 wrong_identity.validate(&expected),
2173 Err(ZakuraValidationError::IdentityMismatch)
2174 );
2175 assert_eq!(
2176 wrong_identity
2177 .validate(&expected)
2178 .unwrap_err()
2179 .failure_class(),
2180 ZakuraFailureClass::PotentiallyPunitive
2181 );
2182
2183 let mut wrong_transcript = decoded;
2184 wrong_transcript.legacy_upgrade_transcript = [5; 32];
2185 assert_eq!(
2186 wrong_transcript.validate(&expected),
2187 Err(ZakuraValidationError::TranscriptMismatch)
2188 );
2189 assert_eq!(
2190 wrong_transcript
2191 .validate(&expected)
2192 .unwrap_err()
2193 .failure_class(),
2194 ZakuraFailureClass::PotentiallyPunitive
2195 );
2196
2197 let native = ZakuraControlHello {
2198 handshake_path: ZakuraHandshakePath::Native,
2199 initiator_upgrade_nonce: [0; 32],
2200 responder_upgrade_nonce: [0; 32],
2201 legacy_upgrade_transcript: [0; 32],
2202 ..hello
2203 };
2204 let native_expected = ZakuraControlValidation {
2205 handshake_path: ZakuraHandshakePath::Native,
2206 initiator_upgrade_nonce: [0; 32],
2207 responder_upgrade_nonce: [0; 32],
2208 legacy_upgrade_transcript: [0; 32],
2209 ..expected
2210 };
2211 assert_eq!(native.validate(&native_expected), Ok(()));
2212 }
2213
2214 #[test]
2215 fn control_ack_echoes_exact_peer_nonces() {
2216 let ack = ZakuraControlAck {
2217 magic: CONTROL_ACK_MAGIC,
2218 control_version: CONTROL_VERSION,
2219 selected_zakura_protocol: 1,
2220 peer_nonce: [2; 32],
2221 remote_peer_nonce: [1; 32],
2222 accepted_capabilities: 0,
2223 accepted_channels: 0,
2224 accepted_limits: ZakuraAcceptedLimits {
2225 max_frame_bytes: 1024,
2226 max_message_bytes: 2048,
2227 max_open_streams: 8,
2228 max_inbound_queue_depth: 8,
2229 idle_timeout_millis: 1000,
2230 },
2231 };
2232 let requested_limits = ack.accepted_limits;
2233 let local = local_config();
2234 assert_eq!(
2235 ack.validate(1, [1; 32], [2; 32], &requested_limits, &local),
2236 Ok(())
2237 );
2238 assert_eq!(
2239 ack.validate(1, [9; 32], [2; 32], &requested_limits, &local),
2240 Err(ZakuraValidationError::ControlNonceMismatch)
2241 );
2242
2243 let malicious_ack = ZakuraControlAck {
2244 accepted_limits: ZakuraAcceptedLimits {
2245 max_frame_bytes: requested_limits.max_frame_bytes + 1,
2246 ..requested_limits
2247 },
2248 ..ack
2249 };
2250 assert_eq!(
2251 malicious_ack.validate(1, [1; 32], [2; 32], &requested_limits, &local),
2252 Err(ZakuraValidationError::ResourceLimit)
2253 );
2254
2255 let over_cap_acks = [
2257 ZakuraAcceptedLimits {
2258 max_message_bytes: local.max_message_bytes + 1,
2259 ..requested_limits
2260 },
2261 ZakuraAcceptedLimits {
2262 max_open_streams: local.max_open_streams + 1,
2263 ..requested_limits
2264 },
2265 ZakuraAcceptedLimits {
2266 max_inbound_queue_depth: local.max_inbound_queue_depth + 1,
2267 ..requested_limits
2268 },
2269 ZakuraAcceptedLimits {
2270 idle_timeout_millis: local.max_idle_timeout_millis + 1,
2271 ..requested_limits
2272 },
2273 ];
2274 for accepted_limits in over_cap_acks {
2275 let over_cap_ack = ZakuraControlAck {
2276 accepted_limits,
2277 ..ack.clone()
2278 };
2279 assert_eq!(
2280 over_cap_ack.validate(1, [1; 32], [2; 32], &requested_limits, &local),
2281 Err(ZakuraValidationError::ResourceLimit)
2282 );
2283 }
2284
2285 let zero_acks = [
2287 ZakuraAcceptedLimits {
2288 max_frame_bytes: 0,
2289 ..requested_limits
2290 },
2291 ZakuraAcceptedLimits {
2292 max_message_bytes: 0,
2293 ..requested_limits
2294 },
2295 ZakuraAcceptedLimits {
2296 max_open_streams: 0,
2297 ..requested_limits
2298 },
2299 ZakuraAcceptedLimits {
2300 max_inbound_queue_depth: 0,
2301 ..requested_limits
2302 },
2303 ZakuraAcceptedLimits {
2304 idle_timeout_millis: 0,
2305 ..requested_limits
2306 },
2307 ];
2308 for accepted_limits in zero_acks {
2309 let requested = ZakuraInitialLimits {
2311 max_frame_bytes: local.max_control_frame_bytes,
2312 max_message_bytes: local.max_message_bytes,
2313 max_open_streams: local.max_open_streams,
2314 max_inbound_queue_depth: local.max_inbound_queue_depth,
2315 idle_timeout_millis: local.max_idle_timeout_millis,
2316 };
2317 let zero_ack = ZakuraControlAck {
2318 accepted_limits,
2319 ..ack.clone()
2320 };
2321 assert_eq!(
2322 zero_ack.validate(1, [1; 32], [2; 32], &requested, &local),
2323 Err(ZakuraValidationError::ResourceLimit)
2324 );
2325 }
2326 }
2327
2328 #[test]
2329 fn initial_limits_allow_application_frames_above_control_cap() {
2330 let local = local_config();
2331 let limits = ZakuraInitialLimits {
2332 max_frame_bytes: local.max_message_bytes,
2333 max_message_bytes: local.max_message_bytes,
2334 max_open_streams: local.max_open_streams,
2335 max_inbound_queue_depth: local.max_inbound_queue_depth,
2336 idle_timeout_millis: local.max_idle_timeout_millis,
2337 };
2338
2339 assert_eq!(validate_initial_limits(limits, &local), Ok(()));
2340 assert!(limits.max_frame_bytes > local.max_control_frame_bytes);
2341 }
2342
2343 #[test]
2344 fn stream_prelude_and_frame_are_bounded() {
2345 let prelude = StreamPrelude {
2346 magic: STREAM_PRELUDE_MAGIC,
2347 stream_kind: 1,
2348 stream_version: 1,
2349 request_id: Some(10),
2350 max_frame_bytes: 16,
2351 };
2352 let encoded = prelude.encode().expect("stream prelude encodes");
2353 assert_eq!(StreamPrelude::decode(&encoded).unwrap(), prelude);
2354
2355 let frame = Frame {
2356 message_type: 1,
2357 flags: 0,
2358 payload: vec![1; 8],
2359 };
2360 let encoded = frame.encode(16).expect("frame fits");
2361 assert_eq!(Frame::decode(&encoded, 16).unwrap(), frame);
2362
2363 let oversized = Frame {
2364 payload: vec![1; 9],
2365 ..frame
2366 };
2367 assert!(oversized.encode(16).is_err());
2368 }
2369
2370 #[test]
2371 fn pending_upgrade_registry_matches_and_expires_by_peer_id() {
2372 let now = Instant::now();
2373 let peer_id = ZakuraPeerId::new(vec![7; 32]).unwrap();
2374 let pending = PendingUpgrade::new(peer_id.clone(), 1, [1; 32], [2; 32], [3; 32]);
2375 let mut registry = PendingUpgradeRegistry::new(1, Duration::from_secs(1));
2376
2377 registry.insert(now, pending).expect("under cap");
2378 assert_eq!(registry.len(), 1);
2379 assert!(registry
2380 .take(now + Duration::from_millis(500), &peer_id)
2381 .is_some());
2382 assert!(registry.is_empty());
2383
2384 let pending = PendingUpgrade::new(peer_id.clone(), 1, [1; 32], [2; 32], [3; 32]);
2385 registry.insert(now, pending).expect("under cap");
2386 assert!(registry
2387 .take(now + Duration::from_secs(2), &peer_id)
2388 .is_none());
2389 }
2390
2391 #[test]
2392 fn duplicate_supervisor_returns_duplicate_without_replacing_winner() {
2393 let peer_id = ZakuraPeerId::new(vec![7; 32]).unwrap();
2394 let mut supervisor = ZakuraPeerSupervisor::default();
2395
2396 assert!(matches!(
2397 supervisor.register_authenticated(peer_id.clone(), [1; 32]),
2398 AuthenticatedPeerRegistration::Registered
2399 ));
2400 assert!(matches!(
2401 supervisor.register_authenticated(peer_id.clone(), [2; 32]),
2402 AuthenticatedPeerRegistration::Duplicate
2403 ));
2404 assert!(matches!(
2405 supervisor.register_authenticated(peer_id, [0; 32]),
2406 AuthenticatedPeerRegistration::Registered
2407 ));
2408 }
2409
2410 fn version(nonce: Nonce) -> VersionMessage {
2411 let addr: SocketAddr = "127.0.0.1:8233".parse().unwrap();
2412 VersionMessage {
2413 version: Version(1),
2414 services: PeerServices::NODE_NETWORK,
2415 timestamp: Utc::now(),
2416 address_recv: AddrInVersion::new(addr, PeerServices::NODE_NETWORK),
2417 address_from: AddrInVersion::new(addr, PeerServices::NODE_NETWORK),
2418 nonce,
2419 user_agent: "/Zebra:test/".to_string(),
2420 start_height: zakura_chain::block::Height(0),
2421 relay: true,
2422 }
2423 }
2424
2425 fn frozen_version(services: PeerServices, user_agent: &str) -> VersionMessage {
2426 let addr: SocketAddr = "127.0.0.1:8233".parse().unwrap();
2427
2428 VersionMessage {
2429 version: crate::constants::CURRENT_NETWORK_PROTOCOL_VERSION,
2430 services,
2431 timestamp: Utc
2432 .timestamp_opt(1_700_000_000, 0)
2433 .single()
2434 .expect("fixed timestamp is in range"),
2435 address_recv: AddrInVersion::new(addr, PeerServices::NODE_NETWORK),
2436 address_from: AddrInVersion::new(addr, services),
2437 nonce: Nonce(0x0102_0304_0506_0708),
2438 user_agent: user_agent.to_string(),
2439 start_height: zakura_chain::block::Height(1),
2440 relay: true,
2441 }
2442 }
2443
2444 fn frozen_plain_zebra_version() -> VersionMessage {
2445 frozen_version(PeerServices::NODE_NETWORK, "/Zebra:compat/")
2446 }
2447
2448 fn frozen_zakura_version() -> VersionMessage {
2449 frozen_version(
2450 PeerServices::NODE_NETWORK | PeerServices::NODE_P2P_V2,
2451 "/Zakura:7.0.0/Zebra:compat/",
2452 )
2453 }
2454
2455 fn control_hello() -> ZakuraControlHello {
2456 let local = local_config();
2457
2458 ZakuraControlHello {
2459 magic: CONTROL_HELLO_MAGIC,
2460 control_version: CONTROL_VERSION,
2461 selected_zakura_protocol: 1,
2462 handshake_path: ZakuraHandshakePath::Upgraded,
2463 role: ZakuraControlRole::Initiator,
2464 network_id: local.network_id,
2465 chain_id: local.chain_id,
2466 iroh_node_id: vec![7; 32],
2467 peer_nonce: [3; 32],
2468 initiator_upgrade_nonce: [1; 32],
2469 responder_upgrade_nonce: [2; 32],
2470 legacy_upgrade_transcript: [4; 32],
2471 capabilities: 0,
2472 required_channels: 0,
2473 initial_limits: ZakuraInitialLimits {
2474 max_frame_bytes: 1024,
2475 max_message_bytes: 2048,
2476 max_open_streams: 8,
2477 max_inbound_queue_depth: 8,
2478 idle_timeout_millis: 1000,
2479 },
2480 }
2481 }
2482
2483 fn control_ack() -> ZakuraControlAck {
2484 ZakuraControlAck {
2485 magic: CONTROL_ACK_MAGIC,
2486 control_version: CONTROL_VERSION,
2487 selected_zakura_protocol: 1,
2488 peer_nonce: [2; 32],
2489 remote_peer_nonce: [1; 32],
2490 accepted_capabilities: 0,
2491 accepted_channels: 0,
2492 accepted_limits: ZakuraAcceptedLimits {
2493 max_frame_bytes: 1024,
2494 max_message_bytes: 2048,
2495 max_open_streams: 8,
2496 max_inbound_queue_depth: 8,
2497 idle_timeout_millis: 1000,
2498 },
2499 }
2500 }
2501
2502 fn stream_prelude() -> StreamPrelude {
2503 StreamPrelude {
2504 magic: STREAM_PRELUDE_MAGIC,
2505 stream_kind: 1,
2506 stream_version: 1,
2507 request_id: Some(10),
2508 max_frame_bytes: 1024,
2509 }
2510 }
2511
2512 fn frame_sample() -> Frame {
2513 Frame {
2514 message_type: 1,
2515 flags: 0,
2516 payload: b"zakura-compat-v1".to_vec(),
2517 }
2518 }
2519
2520 fn encode_version_message(version: VersionMessage) -> Vec<u8> {
2521 let (rt, _init_guard) = zakura_test::init_async();
2522
2523 rt.block_on(async {
2524 let mut bytes = Vec::new();
2525 {
2526 let mut writer = FramedWrite::new(&mut bytes, Codec::builder().finish());
2527 writer
2528 .send(Message::Version(version))
2529 .await
2530 .expect("frozen version message serializes");
2531 }
2532 bytes
2533 })
2534 }
2535
2536 fn decode_version_message(bytes: &[u8]) -> VersionMessage {
2537 decode_version_message_result(bytes).expect("frozen version vector decodes")
2538 }
2539
2540 #[derive(Clone, Debug)]
2541 enum WireMessage {
2542 Version(VersionMessage),
2543 P2pV2Upgrade(P2pV2Upgrade),
2544 ControlHello(ZakuraControlHello),
2545 ControlAck(ZakuraControlAck),
2546 StreamPrelude(StreamPrelude),
2547 Frame { value: Frame, max_frame_bytes: u32 },
2548 }
2549
2550 impl WireMessage {
2551 fn encode(&self) -> Vec<u8> {
2552 match self {
2553 Self::Version(value) => encode_version_message(value.clone()),
2554 Self::P2pV2Upgrade(value) => value.encode().expect("valid message encodes"),
2555 Self::ControlHello(value) => value.encode().expect("valid message encodes"),
2556 Self::ControlAck(value) => value.encode().expect("valid message encodes"),
2557 Self::StreamPrelude(value) => value.encode().expect("valid message encodes"),
2558 Self::Frame {
2559 value,
2560 max_frame_bytes,
2561 } => value
2562 .encode(*max_frame_bytes)
2563 .expect("valid message encodes"),
2564 }
2565 }
2566
2567 fn assert_decodes(&self, bytes: &[u8]) {
2568 match self {
2569 Self::Version(value) => assert_eq!(decode_version_message(bytes), *value),
2570 Self::P2pV2Upgrade(value) => {
2571 assert_eq!(P2pV2Upgrade::decode(bytes).unwrap(), *value)
2572 }
2573 Self::ControlHello(value) => {
2574 assert_eq!(ZakuraControlHello::decode(bytes).unwrap(), *value)
2575 }
2576 Self::ControlAck(value) => {
2577 assert_eq!(ZakuraControlAck::decode(bytes).unwrap(), *value)
2578 }
2579 Self::StreamPrelude(value) => {
2580 assert_eq!(StreamPrelude::decode(bytes).unwrap(), *value)
2581 }
2582 Self::Frame {
2583 value,
2584 max_frame_bytes,
2585 } => assert_eq!(Frame::decode(bytes, *max_frame_bytes).unwrap(), *value),
2586 }
2587 }
2588
2589 fn assert_rejects_trailing_bytes(&self) {
2590 let mut bytes = self.encode();
2591 bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc]);
2592
2593 match self {
2594 Self::Version(_) => {}
2595 Self::P2pV2Upgrade(_) => assert!(
2596 P2pV2Upgrade::decode(&bytes).is_err(),
2597 "p2pv2up accepted trailing bytes",
2598 ),
2599 Self::ControlHello(_) => assert!(
2600 ZakuraControlHello::decode(&bytes).is_err(),
2601 "control hello accepted trailing bytes",
2602 ),
2603 Self::ControlAck(_) => assert!(
2604 ZakuraControlAck::decode(&bytes).is_err(),
2605 "control ack accepted trailing bytes",
2606 ),
2607 Self::StreamPrelude(_) => assert!(
2608 StreamPrelude::decode(&bytes).is_err(),
2609 "stream prelude accepted trailing bytes",
2610 ),
2611 Self::Frame {
2612 max_frame_bytes, ..
2613 } => assert!(
2614 Frame::decode(&bytes, *max_frame_bytes).is_err(),
2615 "frame accepted trailing bytes",
2616 ),
2617 }
2618 }
2619 }
2620
2621 fn decode_version_message_result(bytes: &[u8]) -> Result<VersionMessage, crate::BoxError> {
2622 let (rt, _init_guard) = zakura_test::init_async();
2623
2624 rt.block_on(async {
2625 let mut reader = FramedRead::new(Cursor::new(bytes), Codec::builder().finish());
2626 match reader
2627 .next()
2628 .await
2629 .ok_or_else(|| -> crate::BoxError { "no message decoded".into() })??
2630 {
2631 Message::Version(version) => Ok(version),
2632 message => Err(format!("unexpected wire message: {message:?}").into()),
2633 }
2634 })
2635 }
2636
2637 fn wire_messages() -> Vec<WireMessage> {
2638 let init = init();
2639 vec![
2640 WireMessage::Version(frozen_plain_zebra_version()),
2641 WireMessage::Version(frozen_zakura_version()),
2642 WireMessage::P2pV2Upgrade(P2pV2Upgrade::Init(init.clone())),
2643 WireMessage::P2pV2Upgrade(P2pV2Upgrade::Accept(accept(&init))),
2644 WireMessage::P2pV2Upgrade(P2pV2Upgrade::Reject(P2pV2UpgradeReject {
2645 magic: PRELUDE_MAGIC,
2646 prelude_version: PRELUDE_VERSION,
2647 reason: ZakuraRejectReason::IncompatibleZakuraProtocol,
2648 })),
2649 WireMessage::ControlHello(control_hello()),
2650 WireMessage::ControlAck(control_ack()),
2651 WireMessage::StreamPrelude(stream_prelude()),
2652 WireMessage::Frame {
2653 value: frame_sample(),
2654 max_frame_bytes: 1024,
2655 },
2656 ]
2657 }
2658
2659 #[test]
2660 fn transcript_hash_binds_preludes() {
2661 let init = init();
2662 let accept = accept(&init);
2663 let hash =
2664 legacy_upgrade_transcript(&version(Nonce(1)), &version(Nonce(2)), &init, &accept)
2665 .expect("valid transcript hashes");
2666
2667 let mut tampered_accept = accept;
2668 tampered_accept.capabilities = 1;
2669 let tampered_hash = legacy_upgrade_transcript(
2670 &version(Nonce(1)),
2671 &version(Nonce(2)),
2672 &init,
2673 &tampered_accept,
2674 )
2675 .expect("valid tampered transcript hashes");
2676
2677 assert_ne!(hash, tampered_hash);
2678 }
2679
2680 #[test]
2681 fn compat_i1_i2_legacy_version_messages_roundtrip() {
2682 let expected = frozen_plain_zebra_version();
2683 let bytes = encode_version_message(expected.clone());
2684
2685 assert_eq!(decode_version_message(&bytes), expected);
2686 assert!(!expected.services.contains(PeerServices::NODE_P2P_V2));
2687 assert!(!expected
2688 .address_from
2689 .untrusted_services()
2690 .contains(PeerServices::NODE_P2P_V2));
2691
2692 let expected = frozen_zakura_version();
2693 let bytes = encode_version_message(expected.clone());
2694
2695 assert_eq!(decode_version_message(&bytes), expected);
2696 assert!(expected.services.contains(PeerServices::NODE_P2P_V2));
2697 assert!(expected
2698 .address_from
2699 .untrusted_services()
2700 .contains(PeerServices::NODE_P2P_V2));
2701 assert!(!expected
2702 .address_recv
2703 .untrusted_services()
2704 .contains(PeerServices::NODE_P2P_V2));
2705 }
2706
2707 #[test]
2708 fn compat_i5_p2pv2up_wire_messages_roundtrip() {
2709 for message in wire_messages()
2710 .into_iter()
2711 .filter(|message| matches!(message, WireMessage::P2pV2Upgrade(_)))
2712 {
2713 let bytes = message.encode();
2714 message.assert_decodes(&bytes);
2715 }
2716 }
2717
2718 #[test]
2719 fn compat_i5_control_and_stream_messages_roundtrip() {
2720 for message in wire_messages().into_iter().filter(|message| {
2721 matches!(
2722 message,
2723 WireMessage::ControlHello(_)
2724 | WireMessage::ControlAck(_)
2725 | WireMessage::StreamPrelude(_)
2726 | WireMessage::Frame { .. }
2727 )
2728 }) {
2729 let bytes = message.encode();
2730 message.assert_decodes(&bytes);
2731 }
2732 }
2733
2734 #[test]
2735 fn compat_i3_v1_zakura_decoders_reject_unknown_trailing_data() {
2736 for message in wire_messages()
2737 .into_iter()
2738 .filter(|message| !matches!(message, WireMessage::Version(_)))
2739 {
2740 message.assert_rejects_trailing_bytes();
2741 }
2742 }
2743
2744 #[test]
2745 fn compat_i3_unknown_service_bits_are_truncated_without_selecting_zakura() {
2746 let unknown_high_bit = 1 << 63;
2747 let services =
2748 PeerServices::from_bits_truncate(PeerServices::NODE_NETWORK.bits() | unknown_high_bit);
2749
2750 assert_eq!(services, PeerServices::NODE_NETWORK);
2751 assert!(!services.contains(PeerServices::NODE_P2P_V2));
2752 }
2753}