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