1use super::{
4 COMMON_BYTES, ControlEncoding, Incoming, PeerBinding, ReadFields, WireError, WireLimits,
5 WriteFields, Writer,
6 transfer::{read_source, validate_source, write_source},
7 validate_scope,
8};
9use crate::{LogSource, Scope};
10use ozzy_proto::{Envelope, LinkSessionId, NodeId, Opcode, RequestId};
11
12const METADATA_BYTES: usize = COMMON_BYTES + 16 + 72 + 8 + 4;
13
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub struct CheckpointRequest {
17 pub scope: Scope,
19 pub request_id: RequestId,
21 pub nonce: RequestId,
23 pub source: LogSource,
25 pub offset: u64,
27 pub max_bytes: u32,
29}
30
31impl CheckpointRequest {
32 fn validate(self, limits: WireLimits) -> Result<(), WireError> {
33 validate_scope(self.scope)?;
34 validate_source(self.source)?;
35 if self.request_id.as_bytes() == &[0; 16] || self.nonce.as_bytes() == &[0; 16] {
36 return Err(WireError::Correlation);
37 }
38 if self.max_bytes == 0 || self.max_bytes as usize > limits.envelope.max_payload_bytes {
39 return Err(WireError::Limit);
40 }
41 Ok(())
42 }
43}
44
45#[derive(Clone, Copy, Debug, Eq, PartialEq)]
47pub enum CheckpointMessage<'a> {
48 Request(CheckpointRequest),
50 Chunk {
52 request: CheckpointRequest,
54 bytes: &'a [u8],
56 },
57}
58
59pub fn encode_checkpoint(
61 sender: NodeId,
62 session: LinkSessionId,
63 message: CheckpointMessage<'_>,
64 output: &mut [u8],
65 limits: WireLimits,
66) -> Result<ControlEncoding, WireError> {
67 let (request, payload_bytes, response) = match message {
68 CheckpointMessage::Request(request) => (request, 0, false),
69 CheckpointMessage::Chunk { request, bytes } => {
70 if bytes.is_empty()
71 || bytes.len() > request.max_bytes as usize
72 || request.source.voter != sender
73 {
74 return Err(WireError::History);
75 }
76 (request, bytes.len(), true)
77 }
78 };
79 request.validate(limits)?;
80 let header = Envelope {
81 opcode: if response {
82 Opcode::SnapshotChunk
83 } else {
84 Opcode::SnapshotBegin
85 },
86 response,
87 request_id: Some(request.request_id),
88 sender,
89 session: Some(session),
90 }
91 .encode_header(METADATA_BYTES, payload_bytes, limits.envelope)?;
92 if output.len() < METADATA_BYTES {
93 return Err(WireError::Capacity);
94 }
95 let mut writer = Writer::with_len(output, METADATA_BYTES)?;
96 writer.scope(request.scope, sender)?;
97 writer.bytes(request.nonce.as_bytes())?;
98 write_source(&mut writer, request.source)?;
99 writer.bytes(&request.offset.to_be_bytes())?;
100 writer.bytes(&request.max_bytes.to_be_bytes())?;
101 Ok(ControlEncoding {
102 header,
103 metadata_bytes: METADATA_BYTES,
104 })
105}
106
107pub(super) fn decode(
108 Incoming {
109 scope,
110 envelope,
111 mut reader,
112 payload,
113 }: Incoming<'_>,
114 binding: PeerBinding,
115 limits: WireLimits,
116) -> Result<CheckpointMessage<'_>, WireError> {
117 let request = CheckpointRequest {
118 scope,
119 request_id: envelope.request_id.ok_or(WireError::Correlation)?,
120 nonce: RequestId::from_bytes(reader.array()?),
121 source: read_source(&mut reader)?,
122 offset: u64::from_be_bytes(reader.array()?),
123 max_bytes: reader.u32()?,
124 };
125 reader.finish()?;
126 request.validate(limits)?;
127 binding.configuration.voter_index(request.source.voter)?;
128 match envelope.opcode {
129 Opcode::SnapshotBegin if !envelope.response && payload.is_empty() => {
130 Ok(CheckpointMessage::Request(request))
131 }
132 Opcode::SnapshotChunk
133 if envelope.response
134 && request.source.voter == binding.peer
135 && !payload.is_empty()
136 && payload.len() <= request.max_bytes as usize =>
137 {
138 Ok(CheckpointMessage::Chunk {
139 request,
140 bytes: payload,
141 })
142 }
143 _ => Err(WireError::Correlation),
144 }
145}
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq)]
149pub enum HistoryFence {
150 Receive(crate::flow::ReceiveEpoch),
152 Fetch(RequestId),
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub struct HistoryRetired {
160 pub scope: Scope,
162 pub fence: HistoryFence,
164 pub before: crate::Prefix,
169}
170
171pub fn encode_history_retired(
173 sender: NodeId,
174 session: LinkSessionId,
175 notice: HistoryRetired,
176 output: &mut [u8],
177 limits: WireLimits,
178) -> Result<ControlEncoding, WireError> {
179 validate_scope(notice.scope)?;
180 super::validate_prefix(notice.before)?;
181 if (notice.before.op.0 == 0 && matches!(notice.fence, HistoryFence::Receive(_)))
182 || matches!(notice.fence, HistoryFence::Fetch(id) if id.as_bytes() == &[0; 16])
183 {
184 return Err(WireError::History);
185 }
186 let length = COMMON_BYTES + 1 + 16 + 40;
187 let header = Envelope {
188 opcode: Opcode::HistoryRetired,
189 response: false,
190 request_id: None,
191 sender,
192 session: Some(session),
193 }
194 .encode_header(length, 0, limits.envelope)?;
195 if output.len() < length {
196 return Err(WireError::Capacity);
197 }
198 let mut writer = Writer::with_len(output, length)?;
199 writer.scope(notice.scope, sender)?;
200 match notice.fence {
201 HistoryFence::Receive(epoch) => {
202 writer.bytes(&[0])?;
203 writer.bytes(&epoch.get().to_be_bytes())?;
204 }
205 HistoryFence::Fetch(id) => {
206 writer.bytes(&[1])?;
207 writer.bytes(id.as_bytes())?;
208 }
209 }
210 writer.prefix(notice.before)?;
211 Ok(ControlEncoding {
212 header,
213 metadata_bytes: length,
214 })
215}
216
217pub(super) fn decode_retired(
218 Incoming {
219 scope,
220 envelope,
221 mut reader,
222 payload,
223 }: Incoming<'_>,
224) -> Result<HistoryRetired, WireError> {
225 if envelope.response || envelope.request_id.is_some() || !payload.is_empty() {
226 return Err(WireError::Correlation);
227 }
228 let tag: [u8; 1] = reader.array()?;
229 let id: [u8; 16] = reader.array()?;
230 let fence = match tag[0] {
231 0 => HistoryFence::Receive(crate::flow::ReceiveEpoch::new(u128::from_be_bytes(id))?),
232 1 if id != [0; 16] => HistoryFence::Fetch(RequestId::from_bytes(id)),
233 _ => return Err(WireError::Correlation),
234 };
235 let before = reader.prefix()?;
236 reader.finish()?;
237 super::validate_prefix(before)?;
238 if before.op.0 == 0 && matches!(fence, HistoryFence::Receive(_)) {
239 return Err(WireError::History);
240 }
241 Ok(HistoryRetired {
242 scope,
243 fence,
244 before,
245 })
246}