commonware_storage/qmdb/sync/
target.rs1use crate::{
2 merkle::{Family, Location},
3 qmdb::sync::error::EngineError,
4};
5use commonware_codec::{EncodeSize, Error as CodecError, Read, ReadExt as _, Write};
6use commonware_cryptography::Digest;
7use commonware_runtime::{Buf, BufMut};
8use commonware_utils::{non_empty_range, range::NonEmptyRange};
9
10#[derive(Debug)]
15pub struct Target<F: Family, D: Digest> {
16 pub root: D,
18 pub range: NonEmptyRange<Location<F>>,
20}
21
22impl<F: Family, D: Digest> Target<F, D> {
23 pub const fn new(root: D, range: NonEmptyRange<Location<F>>) -> Self {
25 Self { root, range }
26 }
27
28 pub fn advances(&self, from: &Self) -> bool {
34 self.range.end().is_valid()
35 && self.range.end() > from.range.end()
36 && self.range.start() >= from.range.start()
37 }
38}
39
40impl<F: Family, D: Digest> Clone for Target<F, D> {
41 fn clone(&self) -> Self {
42 Self {
43 root: self.root,
44 range: self.range.clone(),
45 }
46 }
47}
48
49impl<F: Family, D: Digest> PartialEq for Target<F, D> {
50 fn eq(&self, other: &Self) -> bool {
51 self.root == other.root && self.range == other.range
52 }
53}
54
55impl<F: Family, D: Digest> Eq for Target<F, D> {}
56
57impl<F: Family, D: Digest> Write for Target<F, D> {
58 fn write(&self, buf: &mut impl BufMut) {
59 self.root.write(buf);
60 self.range.write(buf);
61 }
62}
63
64impl<F: Family, D: Digest> EncodeSize for Target<F, D> {
65 fn encode_size(&self) -> usize {
66 self.root.encode_size() + self.range.encode_size()
67 }
68}
69
70impl<F: Family, D: Digest> Read for Target<F, D> {
71 type Cfg = ();
72
73 fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
74 let root = D::read(buf)?;
75 let range = NonEmptyRange::<Location<F>>::read(buf)?;
76 if !range.start().is_valid() || !range.end().is_valid() {
77 return Err(CodecError::Invalid(
78 "storage::qmdb::sync::Target",
79 "range bounds out of valid range",
80 ));
81 }
82 Ok(Self { root, range })
83 }
84}
85
86#[cfg(feature = "arbitrary")]
87impl<F: Family, D: Digest> arbitrary::Arbitrary<'_> for Target<F, D>
88where
89 D: for<'a> arbitrary::Arbitrary<'a>,
90{
91 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
92 let root = u.arbitrary()?;
93 let max_loc = F::MAX_LEAVES;
94 let lower = u.int_in_range(0..=*max_loc - 1)?;
95 let upper = u.int_in_range(lower + 1..=*max_loc)?;
96 Ok(Self {
97 root,
98 range: commonware_utils::non_empty_range!(Location::new(lower), Location::new(upper)),
99 })
100 }
101}
102
103#[derive(Debug)]
105pub struct CompactTarget<F: Family, D: Digest> {
106 pub root: D,
108 pub size: Location<F>,
110}
111
112impl<F: Family, D: Digest> TryFrom<&CompactTarget<F, D>> for Target<F, D> {
113 type Error = EngineError<F, D>;
114
115 fn try_from(target: &CompactTarget<F, D>) -> Result<Self, Self::Error> {
116 let end = target.size;
117 let start =
118 end.checked_sub(1)
119 .filter(|_| end.is_valid())
120 .ok_or(EngineError::InvalidTarget {
121 lower_bound_pos: Location::new(0),
122 upper_bound_pos: end,
123 })?;
124 Ok(Self {
125 root: target.root,
126 range: non_empty_range!(start, end),
127 })
128 }
129}
130
131impl<F: Family, D: Digest> Clone for CompactTarget<F, D> {
132 fn clone(&self) -> Self {
133 Self {
134 root: self.root,
135 size: self.size,
136 }
137 }
138}
139
140impl<F: Family, D: Digest> PartialEq for CompactTarget<F, D> {
141 fn eq(&self, other: &Self) -> bool {
142 self.root == other.root && self.size == other.size
143 }
144}
145
146impl<F: Family, D: Digest> Eq for CompactTarget<F, D> {}
147
148impl<F: Family, D: Digest> Write for CompactTarget<F, D> {
149 fn write(&self, buf: &mut impl BufMut) {
150 self.root.write(buf);
151 self.size.write(buf);
152 }
153}
154
155impl<F: Family, D: Digest> EncodeSize for CompactTarget<F, D> {
156 fn encode_size(&self) -> usize {
157 self.root.encode_size() + self.size.encode_size()
158 }
159}
160
161impl<F: Family, D: Digest> Read for CompactTarget<F, D> {
162 type Cfg = ();
163
164 fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
165 let root = D::read(buf)?;
166 let size = Location::<F>::read(buf)?;
167 if !size.is_valid() || size == 0 {
168 return Err(CodecError::Invalid(
169 "storage::qmdb::sync::CompactTarget",
170 "size must be in 1..=MAX_LEAVES",
171 ));
172 }
173 Ok(Self { root, size })
174 }
175}
176
177#[cfg(feature = "arbitrary")]
178impl<F: Family, D: Digest> arbitrary::Arbitrary<'_> for CompactTarget<F, D>
179where
180 D: for<'a> arbitrary::Arbitrary<'a>,
181{
182 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
183 let root = u.arbitrary()?;
184 let size = Location::new(u.int_in_range(1..=*F::MAX_LEAVES)?);
185 Ok(Self { root, size })
186 }
187}
188
189#[cfg(test)]
190mod tests {
194 use super::*;
195 use crate::merkle::mmr::Family as MmrFamily;
196 use commonware_codec::{DecodeExt as _, Encode as _};
197 use commonware_cryptography::sha256;
198 use commonware_utils::non_empty_range;
199 use std::io::Cursor;
200
201 fn target(root: sha256::Digest, start: u64, end: u64) -> Target<MmrFamily, sha256::Digest> {
202 Target::new(
203 root,
204 non_empty_range!(Location::new(start), Location::new(end)),
205 )
206 }
207
208 #[test]
209 fn test_sync_target_serialization() {
210 let target = target(sha256::Digest::from([42; 32]), 100, 500);
211
212 let mut buffer = Vec::new();
214 target.write(&mut buffer);
215
216 assert_eq!(buffer.len(), target.encode_size());
218
219 let mut cursor = Cursor::new(buffer);
221 let deserialized = Target::read(&mut cursor).unwrap();
222
223 assert_eq!(target, deserialized);
225 assert_eq!(target.root, deserialized.root);
226 assert_eq!(target.range, deserialized.range);
227 }
228
229 #[test]
230 fn test_sync_target_read_invalid_bounds() {
231 let mut buffer = Vec::new();
233 sha256::Digest::from([42; 32]).write(&mut buffer);
234 Location::<MmrFamily>::new(100).write(&mut buffer); Location::<MmrFamily>::new(50).write(&mut buffer); let mut cursor = Cursor::new(buffer);
238 assert!(matches!(
239 Target::<MmrFamily, sha256::Digest>::read(&mut cursor),
240 Err(CodecError::Invalid("NonEmptyRange", "start must be < end"))
241 ));
242
243 let root = sha256::Digest::from([42; 32]);
245 let mut buffer = Vec::new();
246 root.write(&mut buffer);
247 Location::<MmrFamily>::new(100).write(&mut buffer);
248 Location::<MmrFamily>::new(100).write(&mut buffer);
249
250 let mut cursor = Cursor::new(buffer);
251 assert!(matches!(
252 Target::<MmrFamily, sha256::Digest>::read(&mut cursor),
253 Err(CodecError::Invalid("NonEmptyRange", "start must be < end"))
254 ));
255 }
256
257 #[test]
258 fn test_compact_target_decode_rejects_zero_size() {
259 let unused_root = sha256::Digest::from([42; 32]);
260 let encoded = CompactTarget::<MmrFamily, sha256::Digest> {
261 root: unused_root,
262 size: Location::new(0),
263 }
264 .encode();
265
266 assert!(CompactTarget::<MmrFamily, sha256::Digest>::decode(encoded).is_err());
267 }
268
269 #[test]
270 fn test_advances() {
271 let current_root = sha256::Digest::from([0; 32]);
272 let advanced_root = sha256::Digest::from([1; 32]);
273 let current = target(current_root, 10, 100);
274
275 assert!(target(advanced_root, 10, 101).advances(¤t));
277 assert!(target(advanced_root, 50, 200).advances(¤t));
278
279 assert!(!target(current_root, 10, 100).advances(¤t));
281 assert!(!target(current_root, 10, 50).advances(¤t));
282
283 assert!(!target(advanced_root, 5, 200).advances(¤t));
285
286 let beyond = target(advanced_root, 10, *MmrFamily::MAX_LEAVES + 1);
288 assert!(!beyond.advances(¤t));
289 }
290
291 #[test]
292 fn test_compact_target_serialization() {
293 let target = CompactTarget::<MmrFamily, sha256::Digest> {
294 root: sha256::Digest::from([42; 32]),
295 size: Location::new(100),
296 };
297
298 let mut buffer = Vec::new();
299 target.write(&mut buffer);
300 assert_eq!(buffer.len(), target.encode_size());
301
302 let mut cursor = Cursor::new(buffer);
303 let deserialized = CompactTarget::read(&mut cursor).unwrap();
304 assert_eq!(target, deserialized);
305 assert_eq!(target.root, deserialized.root);
306 assert_eq!(target.size, deserialized.size);
307 }
308
309 #[test]
310 fn test_compact_target_decode_rejects_size_beyond_domain() {
311 let mut buffer = Vec::new();
312 sha256::Digest::from([42; 32]).write(&mut buffer);
313 Location::<MmrFamily>::new(*MmrFamily::MAX_LEAVES + 1).write(&mut buffer);
314
315 let mut cursor = Cursor::new(buffer);
316 assert!(matches!(
317 CompactTarget::<MmrFamily, sha256::Digest>::read(&mut cursor),
318 Err(CodecError::Invalid(_, _))
319 ));
320 }
321
322 #[test]
323 fn test_compact_target_to_ranged() {
324 let root = sha256::Digest::from([42; 32]);
325
326 let compact = CompactTarget::<MmrFamily, _> {
328 root,
329 size: Location::new(100),
330 };
331 let ranged = Target::try_from(&compact).unwrap();
332 assert_eq!(ranged.root, root);
333 assert_eq!(ranged.range.start(), Location::new(99));
334 assert_eq!(ranged.range.end(), Location::new(100));
335
336 let genesis = CompactTarget::<MmrFamily, _> {
338 root,
339 size: Location::new(1),
340 };
341 let ranged = Target::try_from(&genesis).unwrap();
342 assert_eq!(ranged.range.start(), Location::new(0));
343 assert_eq!(ranged.range.end(), Location::new(1));
344
345 let empty = CompactTarget::<MmrFamily, _> {
347 root,
348 size: Location::new(0),
349 };
350 assert!(matches!(
351 Target::try_from(&empty),
352 Err(EngineError::InvalidTarget { .. })
353 ));
354
355 let beyond = CompactTarget::<MmrFamily, _> {
357 root,
358 size: MmrFamily::MAX_LEAVES + 1,
359 };
360 assert!(matches!(
361 Target::try_from(&beyond),
362 Err(EngineError::InvalidTarget { .. })
363 ));
364 }
365
366 #[cfg(feature = "arbitrary")]
367 mod conformance {
368 use super::*;
369 use crate::merkle::mmb;
370 use commonware_codec::conformance::CodecConformance;
371
372 commonware_conformance::conformance_tests! {
373 CodecConformance<Target<MmrFamily, sha256::Digest>>,
374 CodecConformance<Target<mmb::Family, sha256::Digest>>,
375 CodecConformance<CompactTarget<MmrFamily, sha256::Digest>>,
376 CodecConformance<CompactTarget<mmb::Family, sha256::Digest>>,
377 }
378 }
379}