prikk_object/payload/
block.rs1use prikk_error::{PrikkError, Result};
4
5use crate::canonical::is_strictly_sorted;
6use crate::payload::common::MerkleRoot;
7use crate::{CanonicalEncode, CanonicalWriter, ObjectId};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[repr(u16)]
12pub enum BlockKind {
13 Root = 1,
15 Normal = 2,
17 Merge = 3,
19 Repair = 4,
21 Import = 5,
23}
24
25impl BlockKind {
26 #[must_use]
28 pub const fn code(self) -> u16 {
29 self as u16
30 }
31
32 pub fn from_code(code: u32) -> Result<Self> {
34 match code {
35 1 => Ok(Self::Root),
36 2 => Ok(Self::Normal),
37 3 => Ok(Self::Merge),
38 4 => Ok(Self::Repair),
39 5 => Ok(Self::Import),
40 other => Err(PrikkError::MalformedData(format!(
41 "unknown block kind code: {other}"
42 ))),
43 }
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct BlockPayload {
50 pub parent_block_ids: Vec<ObjectId>,
52 pub kind: BlockKind,
54 pub patch_ids: Vec<ObjectId>,
56 pub state_merkle_root: MerkleRoot,
58 pub snapshot_blob_ref: Option<ObjectId>,
60 pub mainline_parent_id: Option<ObjectId>,
64 pub merge_baseline_block_id: Option<ObjectId>,
68}
69
70impl BlockPayload {
71 pub fn decode_canonical(bytes: &[u8]) -> Result<Self> {
73 let mut cursor = BlockCanonicalCursor::new(bytes);
74 let mut parent_block_ids = Vec::new();
75 let mut kind = None;
76 let mut patch_ids = Vec::new();
77 let mut state_merkle_root = None;
78 let mut snapshot_blob_ref = None;
79 let mut mainline_parent_id = None;
80 let mut merge_baseline_block_id = None;
81 while let Some(field) = cursor.next_field()? {
82 match field.tag {
83 1 => parent_block_ids.push(field.read_object_id()?),
84 2 => kind = Some(BlockKind::from_code(u32::from(field.read_enum_u16()?))?),
85 3 => patch_ids.push(field.read_object_id()?),
86 4 => state_merkle_root = Some(MerkleRoot(field.read_array::<32>()?)),
87 5 => snapshot_blob_ref = Some(field.read_object_id()?),
88 6 => mainline_parent_id = Some(field.read_object_id()?),
89 7 => merge_baseline_block_id = Some(field.read_object_id()?),
90 other => {
91 return Err(PrikkError::MalformedData(format!(
92 "unknown Block field tag: {other}"
93 )));
94 }
95 }
96 }
97 let payload = Self {
98 parent_block_ids,
99 kind: kind
100 .ok_or_else(|| PrikkError::MalformedData("Block missing kind".to_string()))?,
101 patch_ids,
102 state_merkle_root: state_merkle_root.ok_or_else(|| {
103 PrikkError::MalformedData("Block missing state_merkle_root".to_string())
104 })?,
105 snapshot_blob_ref,
106 mainline_parent_id,
107 merge_baseline_block_id,
108 };
109 if !is_strictly_sorted(&payload.parent_block_ids) {
110 return Err(PrikkError::MalformedData(
111 "Block parent IDs are not sorted and unique".to_string(),
112 ));
113 }
114 Ok(payload)
115 }
116}
117
118struct BlockCanonicalCursor<'a> {
119 bytes: &'a [u8],
120 pos: usize,
121 last_tag: Option<u16>,
122}
123
124impl<'a> BlockCanonicalCursor<'a> {
125 const fn new(bytes: &'a [u8]) -> Self {
126 Self {
127 bytes,
128 pos: 0,
129 last_tag: None,
130 }
131 }
132
133 fn next_field(&mut self) -> Result<Option<BlockCanonicalField<'a>>> {
134 if self.pos == self.bytes.len() {
135 return Ok(None);
136 }
137 let tag = u16::from_be_bytes(self.read_array::<2>()?);
138 if tag == 0 {
139 return Err(PrikkError::MalformedData(
140 "field tag 0 is reserved".to_string(),
141 ));
142 }
143 if let Some(last) = self.last_tag {
144 if tag < last {
145 return Err(PrikkError::MalformedData(format!(
146 "field tag order violation: {tag} after {last}"
147 )));
148 }
149 }
150 self.last_tag = Some(tag);
151 let wire_type = self.read_u8()?;
152 let len = usize::try_from(u64::from_be_bytes(self.read_array::<8>()?)).map_err(|_| {
153 PrikkError::MalformedData("canonical field length does not fit usize".to_string())
154 })?;
155 let value = self.read_exact(len)?;
156 Ok(Some(BlockCanonicalField {
157 tag,
158 wire_type,
159 value,
160 }))
161 }
162
163 fn read_u8(&mut self) -> Result<u8> {
164 let value = self.read_exact(1)?;
165 let Some(byte) = value.first() else {
166 return Err(PrikkError::MalformedData(
167 "unexpected empty byte".to_string(),
168 ));
169 };
170 Ok(*byte)
171 }
172
173 fn read_array<const N: usize>(&mut self) -> Result<[u8; N]> {
174 let bytes = self.read_exact(N)?;
175 let mut out = [0_u8; N];
176 out.copy_from_slice(bytes);
177 Ok(out)
178 }
179
180 fn read_exact(&mut self, len: usize) -> Result<&'a [u8]> {
181 let end = self
182 .pos
183 .checked_add(len)
184 .ok_or_else(|| PrikkError::MalformedData("canonical range overflow".to_string()))?;
185 let Some(slice) = self.bytes.get(self.pos..end) else {
186 return Err(PrikkError::MalformedData(
187 "unexpected end of canonical payload".to_string(),
188 ));
189 };
190 self.pos = end;
191 Ok(slice)
192 }
193}
194
195struct BlockCanonicalField<'a> {
196 tag: u16,
197 wire_type: u8,
198 value: &'a [u8],
199}
200
201impl<'a> BlockCanonicalField<'a> {
202 fn read_object_id(&self) -> Result<ObjectId> {
203 self.require_wire(crate::canonical::WireType::ObjectId)?;
204 Ok(ObjectId::from_bytes(self.read_array::<32>()?))
205 }
206
207 fn read_enum_u16(&self) -> Result<u16> {
208 self.require_wire(crate::canonical::WireType::EnumU16)?;
209 Ok(u16::from_be_bytes(self.read_array::<2>()?))
210 }
211
212 fn require_wire(&self, expected: crate::canonical::WireType) -> Result<()> {
213 if self.wire_type == expected as u8 {
214 return Ok(());
215 }
216 Err(PrikkError::MalformedData(format!(
217 "field {} has wrong wire type: expected {}, got {}",
218 self.tag, expected as u8, self.wire_type
219 )))
220 }
221
222 fn read_array<const N: usize>(&self) -> Result<[u8; N]> {
223 if self.value.len() != N {
224 return Err(PrikkError::MalformedData(format!(
225 "field {} expected {N} bytes, got {}",
226 self.tag,
227 self.value.len()
228 )));
229 }
230 let mut out = [0_u8; N];
231 out.copy_from_slice(self.value);
232 Ok(out)
233 }
234}
235
236impl CanonicalEncode for BlockPayload {
237 fn encode_canonical(&self, writer: &mut CanonicalWriter) -> Result<()> {
238 if !is_strictly_sorted(&self.parent_block_ids) {
239 return Err(PrikkError::CanonicalEncoding(
240 "parent_block_ids must be sorted and unique".to_string(),
241 ));
242 }
243 writer.repeated_object_id(1, &self.parent_block_ids)?;
244 writer.field_enum_u16(2, self.kind.code())?;
245 writer.repeated_object_id(3, &self.patch_ids)?;
246 writer.field_bytes(4, &self.state_merkle_root.0)?;
247 if let Some(snapshot) = self.snapshot_blob_ref {
248 writer.field_object_id(5, &snapshot)?;
249 }
250 if let Some(mainline) = self.mainline_parent_id {
251 writer.field_object_id(6, &mainline)?;
252 }
253 if let Some(baseline) = self.merge_baseline_block_id {
254 writer.field_object_id(7, &baseline)?;
255 }
256 Ok(())
257 }
258}