1use ozzy_proto::{Envelope, EnvelopeLimits, Opcode};
4use ozzy_proto::{LinkSessionId, NodeId, RequestId};
5
6use super::prepare::{DecodedBatch, decode_batch, metadata_length, validate_batch, write_batch};
7use super::{
8 COMMON_BYTES, ControlEncoding, Operation, Operations, PeerBinding, PrepareEncoding, Reader,
9 WireError, WireLimits, Writer, validate_prefix, validate_scope,
10};
11use crate::{JournalGeneration, LogSource, Prefix, Scope};
12
13const SOURCE_BYTES: usize = 16 + 16 + 40;
14pub(super) const FETCH_BYTES: usize = COMMON_BYTES + SOURCE_BYTES + 40 + 4 + 4;
15pub(super) const OPS_FIXED_BYTES: usize = COMMON_BYTES + SOURCE_BYTES + 40 + 4;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct FetchOps {
23 pub scope: Scope,
25 pub request_id: RequestId,
27 pub source: LogSource,
29 pub predecessor: Prefix,
31 pub max_operations: u32,
33 pub max_body_bytes: u32,
35}
36
37impl FetchOps {
38 fn validate(self) -> Result<(), WireError> {
39 validate_scope(self.scope)?;
40 validate_source(self.source)?;
41 validate_prefix(self.predecessor)?;
42 if self.predecessor.op >= self.source.accepted.op {
43 return Err(WireError::History);
44 }
45 if self.max_operations == 0 || self.max_body_bytes == 0 {
46 return Err(WireError::Limit);
47 }
48 Ok(())
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub struct OpsBatch<'a> {
59 request_id: RequestId,
60 source: LogSource,
61 batch: DecodedBatch<'a>,
62}
63
64impl<'a> OpsBatch<'a> {
65 pub const fn scope(self) -> Scope {
67 self.batch.scope
68 }
69 pub const fn request_id(self) -> RequestId {
71 self.request_id
72 }
73 pub const fn source(self) -> LogSource {
75 self.source
76 }
77 pub const fn predecessor(self) -> Prefix {
79 self.batch.predecessor
80 }
81 pub const fn end(self) -> Prefix {
83 self.batch.accepted
84 }
85 pub fn operations(self) -> Operations<'a> {
87 self.batch.operations()
88 }
89
90 pub fn validate_response(self, request: FetchOps) -> Result<(), WireError> {
95 request.validate()?;
96 if self.request_id != request.request_id
97 || self.scope() != request.scope
98 || self.source != request.source
99 || self.predecessor() != request.predecessor
100 || self.operations().len() > request.max_operations as usize
101 || self.batch.payload.len() > request.max_body_bytes as usize
102 {
103 return Err(WireError::Transfer);
104 }
105 Ok(())
106 }
107}
108
109pub fn encode_ops(
115 sender: NodeId,
116 session: LinkSessionId,
117 request: FetchOps,
118 operations: &[Operation<'_>],
119 metadata: &mut [u8],
120 payload: &mut [u8],
121 limits: WireLimits,
122) -> Result<PrepareEncoding, WireError> {
123 request.validate()?;
124 if sender != request.source.voter {
125 return Err(WireError::Peer);
126 }
127 let limits = WireLimits {
128 max_operations: limits.max_operations.min(request.max_operations as usize),
129 envelope: EnvelopeLimits {
130 max_metadata_bytes: limits.envelope.max_metadata_bytes,
131 max_payload_bytes: limits
132 .envelope
133 .max_payload_bytes
134 .min(request.max_body_bytes as usize),
135 },
136 };
137 let metadata_bytes = metadata_length(operations.len(), OPS_FIXED_BYTES, limits)?;
138 let payload_bytes =
139 validate_batch(request.scope, request.predecessor, None, operations, limits)?;
140 validate_tail(
141 request.source,
142 operations.last().expect("nonempty validated").prefix(),
143 )?;
144 let header = Envelope {
145 opcode: Opcode::Ops,
146 response: true,
147 request_id: Some(request.request_id),
148 sender,
149 session: Some(session),
150 }
151 .encode_header(metadata_bytes, payload_bytes, limits.envelope)?;
152 if metadata.len() < metadata_bytes || payload.len() < payload_bytes {
153 return Err(WireError::Capacity);
154 }
155 let mut writer = Writer::new(&mut metadata[..metadata_bytes]);
156 writer.scope(request.scope, sender);
157 write_source(&mut writer, request.source);
158 writer.prefix(request.predecessor);
159 write_batch(&mut writer, Some(payload), operations);
160 Ok(PrepareEncoding {
161 header,
162 metadata_bytes,
163 payload_bytes,
164 })
165}
166
167pub(super) fn decode_ops<'a>(
168 scope: Scope,
169 envelope: Envelope,
170 mut reader: Reader<'a>,
171 payload: &'a [u8],
172 limits: WireLimits,
173) -> Result<OpsBatch<'a>, WireError> {
174 let request_id = envelope.request_id.ok_or(WireError::Correlation)?;
175 if !envelope.response {
176 return Err(WireError::Correlation);
177 }
178 let source = read_source(&mut reader)?;
179 if source.voter != envelope.sender {
180 return Err(WireError::Peer);
181 }
182 let predecessor = reader.prefix()?;
183 let batch = decode_batch(scope, predecessor, None, reader, payload, limits)?;
184 validate_tail(source, batch.accepted)?;
185 Ok(OpsBatch {
186 request_id,
187 source,
188 batch,
189 })
190}
191
192pub(super) fn validate_routing(
193 request: FetchOps,
194 envelope: Envelope,
195 mut reader: Reader<'_>,
196 payload_bytes: usize,
197) -> Result<(), WireError> {
198 request.validate()?;
199 if !envelope.response || envelope.request_id != Some(request.request_id) {
200 return Err(WireError::Correlation);
201 }
202 if envelope.sender != request.source.voter
203 || read_source(&mut reader)? != request.source
204 || reader.prefix()? != request.predecessor
205 {
206 return Err(WireError::Transfer);
207 }
208 let operations = reader.u32()?;
209 if operations == 0
210 || operations > request.max_operations
211 || payload_bytes > request.max_body_bytes as usize
212 {
213 return Err(WireError::Limit);
214 }
215 Ok(())
216}
217
218fn validate_tail(source: LogSource, end: Prefix) -> Result<(), WireError> {
219 if end.op > source.accepted.op || (end.op == source.accepted.op && end != source.accepted) {
220 return Err(WireError::History);
221 }
222 Ok(())
223}
224
225pub fn encode_fetch(
230 sender: NodeId,
231 session: LinkSessionId,
232 request: FetchOps,
233 metadata: &mut [u8],
234 limits: WireLimits,
235) -> Result<ControlEncoding, WireError> {
236 request.validate()?;
237 let header = Envelope {
238 opcode: Opcode::FetchOps,
239 response: false,
240 request_id: Some(request.request_id),
241 sender,
242 session: Some(session),
243 }
244 .encode_header(FETCH_BYTES, 0, limits.envelope)?;
245 if metadata.len() < FETCH_BYTES {
246 return Err(WireError::Capacity);
247 }
248 let mut writer = Writer::new(&mut metadata[..FETCH_BYTES]);
249 writer.scope(request.scope, sender);
250 write_source(&mut writer, request.source);
251 writer.prefix(request.predecessor);
252 writer.bytes(&request.max_operations.to_be_bytes());
253 writer.bytes(&request.max_body_bytes.to_be_bytes());
254 Ok(ControlEncoding {
255 header,
256 metadata_bytes: FETCH_BYTES,
257 })
258}
259
260pub(super) fn decode_fetch(
261 scope: Scope,
262 envelope: Envelope,
263 mut reader: Reader<'_>,
264 payload: &[u8],
265 binding: PeerBinding,
266) -> Result<FetchOps, WireError> {
267 let request_id = envelope.request_id.ok_or(WireError::Correlation)?;
268 if envelope.response {
269 return Err(WireError::Correlation);
270 }
271 if !payload.is_empty() {
272 return Err(WireError::Payload);
273 }
274 let source = read_source(&mut reader)?;
275 binding.configuration.voter_index(source.voter)?;
276 let request = FetchOps {
277 scope,
278 request_id,
279 source,
280 predecessor: reader.prefix()?,
281 max_operations: reader.u32()?,
282 max_body_bytes: reader.u32()?,
283 };
284 reader.finish()?;
285 request.validate()?;
286 Ok(request)
287}
288
289pub(super) fn validate_source(source: LogSource) -> Result<(), WireError> {
290 validate_prefix(source.accepted)?;
291 if source.generation.0 == 0 || source.voter.as_bytes() == &[0; 16] {
292 return Err(WireError::History);
293 }
294 Ok(())
295}
296
297pub(super) fn write_source(writer: &mut Writer<'_>, source: LogSource) {
298 writer.bytes(source.voter.as_bytes());
299 writer.bytes(&source.generation.0.to_be_bytes());
300 writer.prefix(source.accepted);
301}
302
303pub(super) fn read_source(reader: &mut Reader<'_>) -> Result<LogSource, WireError> {
304 let source = LogSource {
305 voter: NodeId::from_bytes(reader.bytes()?),
306 generation: JournalGeneration(u128::from_be_bytes(reader.bytes()?)),
307 accepted: reader.prefix()?,
308 };
309 validate_source(source)?;
310 Ok(source)
311}