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