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