1use crate::{adb::operation, mmr::Location};
2use bytes::{Buf, BufMut};
3use commonware_codec::{
4 varint::UInt, Codec, EncodeSize, Error as CodecError, Read, ReadExt as _, Write,
5};
6use commonware_utils::{hex, Array};
7use core::fmt::Display;
8
9#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
11pub enum Operation<K: Array, V: Codec> {
12 Set(K, V),
14 Commit(Option<V>),
15 Delete(K),
17 Update(K, V),
18 CommitFloor(Option<V>, Location),
19}
20
21impl<K: Array, V: Codec> Operation<K, V> {
22 pub fn key(&self) -> Option<&K> {
24 match self {
25 Self::Set(key, _) => Some(key),
26 Self::Commit(_) => None,
27 Self::Delete(key) => Some(key),
28 Self::Update(key, _) => Some(key),
29 Self::CommitFloor(_, _) => None,
30 }
31 }
32
33 pub fn value(&self) -> Option<&V> {
35 match self {
36 Self::Set(_, value) => Some(value),
37 Self::Commit(value) => value.as_ref(),
38 Self::Delete(_) => None,
39 Self::Update(_, value) => Some(value),
40 Self::CommitFloor(value, _) => value.as_ref(),
41 }
42 }
43
44 pub fn into_value(self) -> Option<V> {
46 match self {
47 Self::Set(_, value) => Some(value),
48 Self::Commit(value) => value,
49 Self::Delete(_) => None,
50 Self::Update(_, value) => Some(value),
51 Self::CommitFloor(value, _) => value,
52 }
53 }
54}
55
56impl<K: Array, V: Codec> EncodeSize for Operation<K, V> {
57 fn encode_size(&self) -> usize {
58 1 + match self {
59 Self::Delete(_) => K::SIZE,
60 Self::Update(_, v) => K::SIZE + v.encode_size(),
61 Self::CommitFloor(v, floor_loc) => v.encode_size() + UInt(**floor_loc).encode_size(),
62 Self::Set(_, v) => K::SIZE + v.encode_size(),
63 Self::Commit(v) => v.encode_size(),
64 }
65 }
66}
67
68impl<K: Array, V: Codec> Write for Operation<K, V> {
69 fn write(&self, buf: &mut impl BufMut) {
70 match &self {
71 Self::Set(k, v) => {
72 operation::SET_CONTEXT.write(buf);
73 k.write(buf);
74 v.write(buf);
75 }
76 Self::Commit(v) => {
77 operation::COMMIT_CONTEXT.write(buf);
78 v.write(buf);
79 }
80 Self::Delete(k) => {
81 operation::DELETE_CONTEXT.write(buf);
82 k.write(buf);
83 }
84 Self::Update(k, v) => {
85 operation::UPDATE_CONTEXT.write(buf);
86 k.write(buf);
87 v.write(buf);
88 }
89 Self::CommitFloor(v, floor_loc) => {
90 operation::COMMIT_FLOOR_CONTEXT.write(buf);
91 v.write(buf);
92 UInt(**floor_loc).write(buf);
93 }
94 }
95 }
96}
97
98impl<K: Array, V: Codec> Read for Operation<K, V> {
99 type Cfg = <V as Read>::Cfg;
100
101 fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, CodecError> {
102 match u8::read(buf)? {
103 operation::SET_CONTEXT => {
104 let key = K::read(buf)?;
105 let value = V::read_cfg(buf, cfg)?;
106 Ok(Self::Set(key, value))
107 }
108 operation::COMMIT_CONTEXT => Ok(Self::Commit(Option::<V>::read_cfg(buf, cfg)?)),
109 operation::DELETE_CONTEXT => {
110 let key = K::read(buf)?;
111 Ok(Self::Delete(key))
112 }
113 operation::UPDATE_CONTEXT => {
114 let key = K::read(buf)?;
115 let value = V::read_cfg(buf, cfg)?;
116 Ok(Self::Update(key, value))
117 }
118 operation::COMMIT_FLOOR_CONTEXT => {
119 let metadata = Option::<V>::read_cfg(buf, cfg)?;
120 let floor_loc = UInt::read(buf)?;
121 let floor_loc = Location::new(floor_loc.into()).ok_or_else(|| {
122 CodecError::Invalid(
123 "storage::adb::operation::Operation",
124 "commit floor location overflow",
125 )
126 })?;
127 Ok(Self::CommitFloor(metadata, floor_loc))
128 }
129 e => Err(CodecError::InvalidEnum(e)),
130 }
131 }
132}
133
134impl<K: Array, V: Codec> Display for Operation<K, V> {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 match self {
137 Self::Set(key, value) => write!(f, "[key:{key} value:{}]", hex(&value.encode())),
138 Self::Commit(value) => {
139 if let Some(value) = value {
140 write!(f, "[commit {}]", hex(&value.encode()))
141 } else {
142 write!(f, "[commit]")
143 }
144 }
145 Self::Delete(key) => write!(f, "[key:{key} <deleted>]"),
146 Self::Update(key, value) => write!(f, "[key:{key} value:{}]", hex(&value.encode())),
147 Self::CommitFloor(value, loc) => {
148 if let Some(value) = value {
149 write!(
150 f,
151 "[commit {} with inactivity floor: {loc}]",
152 hex(&value.encode())
153 )
154 } else {
155 write!(f, "[commit with inactivity floor: {loc}]")
156 }
157 }
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165 use commonware_codec::{DecodeExt, Encode, EncodeSize, FixedSize as _};
166 use commonware_utils::sequence::U64;
167
168 #[test]
169 fn test_operation_key() {
170 let key = U64::new(1234);
171 let value = U64::new(56789);
172
173 let set_op = Operation::Set(key.clone(), value.clone());
174 assert_eq!(&key, set_op.key().unwrap());
175
176 let update_op = Operation::Update(key.clone(), value.clone());
177 assert_eq!(&key, update_op.key().unwrap());
178
179 let delete_op = Operation::<U64, U64>::Delete(key.clone());
180 assert_eq!(&key, delete_op.key().unwrap());
181
182 let commit_op = Operation::<U64, U64>::Commit(Some(value));
183 assert_eq!(None, commit_op.key());
184
185 let commit_floor_op = Operation::<U64, U64>::CommitFloor(None, Location::new_unchecked(42));
186 assert_eq!(None, commit_floor_op.key());
187 }
188
189 #[test]
190 fn test_operation_value() {
191 let key = U64::new(1234);
192 let value = U64::new(56789);
193
194 let set_op = Operation::Set(key.clone(), value.clone());
195 assert_eq!(&value, set_op.value().unwrap());
196
197 let update_op = Operation::Update(key.clone(), value.clone());
198 assert_eq!(&value, update_op.value().unwrap());
199
200 let delete_op = Operation::<U64, U64>::Delete(key.clone());
201 assert_eq!(None, delete_op.value());
202
203 let commit_op = Operation::<U64, U64>::Commit(Some(value.clone()));
204 assert_eq!(&value, commit_op.value().unwrap());
205
206 let commit_op_none = Operation::<U64, U64>::Commit(None);
207 assert_eq!(None, commit_op_none.value());
208
209 let commit_floor_op =
210 Operation::<U64, U64>::CommitFloor(Some(value.clone()), Location::new_unchecked(42));
211 assert_eq!(&value, commit_floor_op.value().unwrap());
212
213 let commit_floor_op_none =
214 Operation::<U64, U64>::CommitFloor(None, Location::new_unchecked(42));
215 assert_eq!(None, commit_floor_op_none.value());
216 }
217
218 #[test]
219 fn test_operation_into_value() {
220 let key = U64::new(1234);
221 let value = U64::new(56789);
222
223 let set_op = Operation::Set(key.clone(), value.clone());
224 assert_eq!(value.clone(), set_op.into_value().unwrap());
225
226 let update_op = Operation::Update(key.clone(), value.clone());
227 assert_eq!(value.clone(), update_op.into_value().unwrap());
228
229 let delete_op = Operation::<U64, U64>::Delete(key.clone());
230 assert_eq!(None, delete_op.into_value());
231
232 let commit_op = Operation::<U64, U64>::Commit(Some(value.clone()));
233 assert_eq!(value.clone(), commit_op.into_value().unwrap());
234
235 let commit_op_none = Operation::<U64, U64>::Commit(None);
236 assert_eq!(None, commit_op_none.into_value());
237
238 let commit_floor_op =
239 Operation::<U64, U64>::CommitFloor(Some(value.clone()), Location::new_unchecked(42));
240 assert_eq!(value, commit_floor_op.into_value().unwrap());
241
242 let commit_floor_op_none =
243 Operation::<U64, U64>::CommitFloor(None, Location::new_unchecked(42));
244 assert_eq!(None, commit_floor_op_none.into_value());
245 }
246
247 #[test]
248 fn test_operation_encode_decode() {
249 let key = U64::new(1234);
250 let value = U64::new(56789);
251
252 let set_op = Operation::Set(key.clone(), value.clone());
254 let encoded = set_op.encode();
255 let decoded = Operation::<U64, U64>::decode(encoded).unwrap();
256 assert_eq!(set_op, decoded);
257
258 let update_op = Operation::Update(key.clone(), value.clone());
260 let encoded = update_op.encode();
261 let decoded = Operation::<U64, U64>::decode(encoded).unwrap();
262 assert_eq!(update_op, decoded);
263
264 let delete_op = Operation::<U64, U64>::Delete(key.clone());
266 let encoded = delete_op.encode();
267 let decoded = Operation::<U64, U64>::decode(encoded).unwrap();
268 assert_eq!(delete_op, decoded);
269
270 let commit_op = Operation::<U64, U64>::Commit(Some(value.clone()));
272 let encoded = commit_op.encode();
273 let decoded = Operation::<U64, U64>::decode(encoded).unwrap();
274 assert_eq!(commit_op, decoded);
275
276 let commit_op = Operation::<U64, U64>::Commit(None);
278 let encoded = commit_op.encode();
279 let decoded = Operation::<U64, U64>::decode(encoded).unwrap();
280 assert_eq!(commit_op, decoded);
281
282 let commit_floor_op =
284 Operation::<U64, U64>::CommitFloor(Some(value.clone()), Location::new_unchecked(42));
285 let encoded = commit_floor_op.encode();
286 let decoded = Operation::<U64, U64>::decode(encoded).unwrap();
287 assert_eq!(commit_floor_op, decoded);
288
289 let commit_floor_op = Operation::<U64, U64>::CommitFloor(None, Location::new_unchecked(42));
291 let encoded = commit_floor_op.encode();
292 let decoded = Operation::<U64, U64>::decode(encoded).unwrap();
293 assert_eq!(commit_floor_op, decoded);
294 }
295
296 #[test]
297 fn test_operation_encode_size() {
298 let key = U64::new(1234);
299 let value = U64::new(56789);
300
301 let set_op = Operation::Set(key.clone(), value.clone());
303 assert_eq!(set_op.encode_size(), 1 + U64::SIZE + value.encode_size());
304 assert_eq!(set_op.encode().len(), set_op.encode_size());
305
306 let update_op = Operation::Update(key.clone(), value.clone());
308 assert_eq!(update_op.encode_size(), 1 + U64::SIZE + value.encode_size());
309 assert_eq!(update_op.encode().len(), update_op.encode_size());
310
311 let delete_op = Operation::<U64, U64>::Delete(key.clone());
313 assert_eq!(delete_op.encode_size(), 1 + U64::SIZE);
314 assert_eq!(delete_op.encode().len(), delete_op.encode_size());
315
316 let commit_op = Operation::<U64, U64>::Commit(Some(value.clone()));
318 assert_eq!(
319 commit_op.encode_size(),
320 1 + Some(value.clone()).encode_size()
321 );
322 assert_eq!(commit_op.encode().len(), commit_op.encode_size());
323
324 let commit_op = Operation::<U64, U64>::Commit(None);
326 assert_eq!(
327 commit_op.encode_size(),
328 1 + Option::<U64>::None.encode_size()
329 );
330 assert_eq!(commit_op.encode().len(), commit_op.encode_size());
331
332 let commit_floor_op =
334 Operation::<U64, U64>::CommitFloor(Some(value.clone()), Location::new_unchecked(42));
335 assert_eq!(
336 commit_floor_op.encode_size(),
337 1 + Some(value).encode_size() + UInt(42u64).encode_size()
338 );
339 assert_eq!(
340 commit_floor_op.encode().len(),
341 commit_floor_op.encode_size()
342 );
343 }
344
345 #[test]
346 fn test_operation_display() {
347 let key = U64::new(1234);
348 let value = U64::new(56789);
349
350 let set_op = Operation::Set(key.clone(), value.clone());
352 assert_eq!(
353 format!("{set_op}"),
354 format!("[key:{key} value:{}]", hex(&value.encode()))
355 );
356
357 let update_op = Operation::Update(key.clone(), value.clone());
359 assert_eq!(
360 format!("{update_op}"),
361 format!("[key:{key} value:{}]", hex(&value.encode()))
362 );
363
364 let delete_op = Operation::<U64, U64>::Delete(key.clone());
366 assert_eq!(format!("{delete_op}"), format!("[key:{key} <deleted>]"));
367
368 let commit_op = Operation::<U64, U64>::Commit(Some(value.clone()));
370 assert_eq!(
371 format!("{commit_op}"),
372 format!("[commit {}]", hex(&value.encode()))
373 );
374
375 let commit_op = Operation::<U64, U64>::Commit(None);
377 assert_eq!(format!("{commit_op}"), "[commit]");
378
379 let commit_floor_op =
381 Operation::<U64, U64>::CommitFloor(Some(value.clone()), Location::new_unchecked(42));
382 assert_eq!(
383 format!("{commit_floor_op}"),
384 format!(
385 "[commit {} with inactivity floor: {}]",
386 hex(&value.encode()),
387 Location::new_unchecked(42)
388 )
389 );
390
391 let commit_floor_op = Operation::<U64, U64>::CommitFloor(None, Location::new_unchecked(42));
393 assert_eq!(
394 format!("{commit_floor_op}"),
395 format!(
396 "[commit with inactivity floor: {}]",
397 Location::new_unchecked(42)
398 )
399 );
400 }
401
402 #[test]
403 fn test_operation_invalid_context() {
404 let invalid = vec![0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0];
405 let decoded = Operation::<U64, U64>::decode(invalid.as_ref());
406 assert!(matches!(
407 decoded.unwrap_err(),
408 CodecError::InvalidEnum(0xFF)
409 ));
410 }
411
412 #[test]
413 fn test_operation_commit_floor_location_overflow() {
414 use crate::mmr::MAX_LOCATION;
415
416 let valid_loc = MAX_LOCATION / 2;
418 let mut encoded = vec![operation::COMMIT_FLOOR_CONTEXT];
419 encoded.push(0); encoded.extend_from_slice(&UInt(valid_loc).encode());
423
424 let decoded = Operation::<U64, U64>::decode(encoded.as_ref());
425 assert!(decoded.is_ok());
426 if let Ok(Operation::CommitFloor(None, loc)) = decoded {
427 assert_eq!(*loc, valid_loc);
428 } else {
429 panic!("Expected CommitFloor operation with valid location");
430 }
431
432 let mut encoded = vec![operation::COMMIT_FLOOR_CONTEXT];
434 encoded.push(0); encoded.extend_from_slice(&UInt(MAX_LOCATION).encode());
436
437 let decoded = Operation::<U64, U64>::decode(encoded.as_ref());
438 assert!(decoded.is_ok());
439
440 let mut encoded = vec![operation::COMMIT_FLOOR_CONTEXT];
442 encoded.push(0); encoded.extend_from_slice(&UInt(MAX_LOCATION + 1).encode());
444
445 let decoded = Operation::<U64, U64>::decode(encoded.as_ref());
446 assert!(matches!(
447 decoded.unwrap_err(),
448 CodecError::Invalid(_, "commit floor location overflow")
449 ));
450 }
451
452 #[test]
453 fn test_operation_insufficient_buffer() {
454 let invalid = vec![operation::SET_CONTEXT];
456 let decoded = Operation::<U64, U64>::decode(invalid.as_ref());
457 assert!(matches!(decoded.unwrap_err(), CodecError::EndOfBuffer));
458
459 let invalid = vec![operation::DELETE_CONTEXT];
461 let decoded = Operation::<U64, U64>::decode(invalid.as_ref());
462 assert!(matches!(decoded.unwrap_err(), CodecError::EndOfBuffer));
463
464 let invalid = vec![operation::UPDATE_CONTEXT];
466 let decoded = Operation::<U64, U64>::decode(invalid.as_ref());
467 assert!(matches!(decoded.unwrap_err(), CodecError::EndOfBuffer));
468
469 let invalid = vec![operation::COMMIT_CONTEXT];
471 let decoded = Operation::<U64, U64>::decode(invalid.as_ref());
472 assert!(matches!(decoded.unwrap_err(), CodecError::EndOfBuffer));
473
474 let invalid = vec![operation::COMMIT_FLOOR_CONTEXT];
476 let decoded = Operation::<U64, U64>::decode(invalid.as_ref());
477 assert!(matches!(decoded.unwrap_err(), CodecError::EndOfBuffer));
478 }
479
480 #[test]
481 fn test_operation_roundtrip_all_variants() {
482 let key1 = U64::new(100);
483 let key2 = U64::new(200);
484 let value1 = U64::new(1000);
485 let value2 = U64::new(2000);
486 let location = Location::new_unchecked(999);
487
488 let operations: Vec<Operation<U64, U64>> = vec![
490 Operation::Set(key1.clone(), value1.clone()),
491 Operation::Update(key2.clone(), value2.clone()),
492 Operation::Delete(key1.clone()),
493 Operation::Commit(Some(value1.clone())),
494 Operation::Commit(None),
495 Operation::CommitFloor(Some(value2.clone()), location),
496 Operation::CommitFloor(None, location),
497 ];
498
499 for op in operations {
500 let encoded = op.encode();
501 let decoded = Operation::<U64, U64>::decode(encoded.clone()).unwrap();
502 assert_eq!(op, decoded, "Failed to roundtrip: {op:?}");
503 assert_eq!(encoded.len(), op.encode_size(), "Size mismatch for: {op:?}");
504 }
505 }
506
507 #[derive(Clone, Debug, PartialEq, Eq)]
508 struct VariableSizeValue(Vec<u8>);
509
510 impl Write for VariableSizeValue {
511 fn write(&self, buf: &mut impl BufMut) {
512 UInt(self.0.len() as u64).write(buf);
513 buf.put_slice(&self.0);
514 }
515 }
516
517 impl EncodeSize for VariableSizeValue {
518 fn encode_size(&self) -> usize {
519 UInt(self.0.len() as u64).encode_size() + self.0.len()
520 }
521 }
522
523 impl Read for VariableSizeValue {
524 type Cfg = ();
525
526 fn read_cfg(buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, CodecError> {
527 let len = UInt::read(buf)?;
528 let len: u64 = len.into();
529 let len = usize::try_from(len)
530 .map_err(|_| CodecError::Invalid("VariableSizeValue", "length overflow"))?;
531 if buf.remaining() < len {
532 return Err(CodecError::EndOfBuffer);
533 }
534 let mut data = vec![0u8; len];
535 buf.copy_to_slice(&mut data);
536 Ok(VariableSizeValue(data))
537 }
538 }
539
540 #[test]
541 fn test_operation_variable_size_values() {
542 let key = U64::new(42);
543
544 let small_value = VariableSizeValue(vec![1, 2, 3]);
546 let large_value = VariableSizeValue(vec![0xFF; 1000]);
547 let empty_value = VariableSizeValue(vec![]);
548
549 let set_small = Operation::Set(key.clone(), small_value.clone());
551 let encoded = set_small.encode();
552 let decoded = Operation::<U64, VariableSizeValue>::decode(encoded).unwrap();
553 assert_eq!(set_small, decoded);
554
555 let set_large = Operation::Set(key.clone(), large_value.clone());
556 let encoded = set_large.encode();
557 let decoded = Operation::<U64, VariableSizeValue>::decode(encoded).unwrap();
558 assert_eq!(set_large, decoded);
559
560 let set_empty = Operation::Set(key.clone(), empty_value.clone());
561 let encoded = set_empty.encode();
562 let decoded = Operation::<U64, VariableSizeValue>::decode(encoded).unwrap();
563 assert_eq!(set_empty, decoded);
564
565 let commit = Operation::<U64, VariableSizeValue>::Commit(Some(large_value.clone()));
567 let encoded = commit.encode();
568 let decoded = Operation::<U64, VariableSizeValue>::decode(encoded).unwrap();
569 assert_eq!(commit, decoded);
570
571 assert_eq!(set_small.encode().len(), set_small.encode_size());
573 assert_eq!(set_large.encode().len(), set_large.encode_size());
574 assert_eq!(set_empty.encode().len(), set_empty.encode_size());
575 }
576}