1#![no_std]
2#![doc = "Wire versioning and bounded-message primitives for BCX."]
3
4use bcx_core::ValidationError;
5
6#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub struct ProtocolVersion {
9 pub major: u16,
11 pub minor: u16,
13}
14
15impl ProtocolVersion {
16 pub const CURRENT: Self = Self::new(1, 0);
18
19 #[must_use]
21 pub const fn new(major: u16, minor: u16) -> Self {
22 Self { major, minor }
23 }
24}
25
26#[derive(Clone, Copy, Debug, Eq, PartialEq)]
28pub struct WireLimits {
29 maximum_message_len: usize,
31 maximum_parent_events: usize,
33 maximum_why_depth: usize,
35 maximum_explanation_events: usize,
37}
38
39impl WireLimits {
40 pub const MAXIMUM_MESSAGE_LEN: usize = 16 * 1024 * 1024;
42 pub const MAXIMUM_PARENT_EVENTS: usize = 1_024;
44 pub const MAXIMUM_WHY_DEPTH: usize = 32;
46 pub const MAXIMUM_EXPLANATION_EVENTS: usize = 10_000;
48
49 pub const DEVELOPMENT: Self = Self {
51 maximum_message_len: 1_048_576,
52 maximum_parent_events: 16,
53 maximum_why_depth: 5,
54 maximum_explanation_events: 100,
55 };
56
57 pub const fn new(
59 maximum_message_len: usize,
60 maximum_parent_events: usize,
61 maximum_why_depth: usize,
62 maximum_explanation_events: usize,
63 ) -> Result<Self, ValidationError> {
64 if maximum_message_len == 0
65 || maximum_parent_events == 0
66 || maximum_why_depth == 0
67 || maximum_explanation_events == 0
68 {
69 return Err(ValidationError::Empty);
70 }
71 if maximum_message_len > Self::MAXIMUM_MESSAGE_LEN
72 || maximum_parent_events > Self::MAXIMUM_PARENT_EVENTS
73 || maximum_why_depth > Self::MAXIMUM_WHY_DEPTH
74 || maximum_explanation_events > Self::MAXIMUM_EXPLANATION_EVENTS
75 {
76 return Err(ValidationError::TooLarge);
77 }
78 Ok(Self {
79 maximum_message_len,
80 maximum_parent_events,
81 maximum_why_depth,
82 maximum_explanation_events,
83 })
84 }
85
86 #[must_use]
88 pub const fn maximum_message_len(self) -> usize {
89 self.maximum_message_len
90 }
91
92 #[must_use]
94 pub const fn maximum_parent_events(self) -> usize {
95 self.maximum_parent_events
96 }
97
98 #[must_use]
100 pub const fn maximum_why_depth(self) -> usize {
101 self.maximum_why_depth
102 }
103
104 #[must_use]
106 pub const fn maximum_explanation_events(self) -> usize {
107 self.maximum_explanation_events
108 }
109}
110
111#[derive(Clone, Copy, Debug, Eq, PartialEq)]
113pub struct WireHeader {
114 pub version: ProtocolVersion,
116 pub payload_len: usize,
118}
119
120impl WireHeader {
121 pub const fn validate(&self, limits: WireLimits) -> Result<(), ValidationError> {
123 if self.version.major != ProtocolVersion::CURRENT.major {
124 return Err(ValidationError::NotPermitted);
125 }
126 if self.version.minor != ProtocolVersion::CURRENT.minor {
127 return Err(ValidationError::NotPermitted);
128 }
129 if self.payload_len == 0 {
130 return Err(ValidationError::Empty);
131 }
132 if self.payload_len > limits.maximum_message_len() {
133 return Err(ValidationError::TooLarge);
134 }
135 Ok(())
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn header_rejects_empty_payload() {
145 let header = WireHeader {
146 version: ProtocolVersion::CURRENT,
147 payload_len: 0,
148 };
149
150 assert_eq!(
151 header.validate(WireLimits::DEVELOPMENT),
152 Err(ValidationError::Empty)
153 );
154 }
155
156 #[test]
157 fn header_rejects_future_minor_version() {
158 let header = WireHeader {
159 version: ProtocolVersion::new(1, 1),
160 payload_len: 1,
161 };
162
163 assert_eq!(
164 header.validate(WireLimits::DEVELOPMENT),
165 Err(ValidationError::NotPermitted)
166 );
167 }
168
169 #[test]
170 fn limits_reject_unbounded_values() {
171 assert_eq!(
172 WireLimits::new(usize::MAX, 1, 1, 1),
173 Err(ValidationError::TooLarge)
174 );
175 assert_eq!(WireLimits::new(1, 0, 1, 1), Err(ValidationError::Empty));
176 }
177}