Skip to main content

commonware_storage/qmdb/
operation.rs

1//! Shared traits and codecs for QMDB operations.
2
3use crate::merkle::{Family, Location};
4use commonware_codec::{
5    CodecShared, EncodeSize, Error as CodecError, FixedSize, Read, ReadExt as _, Write,
6    util::ensure_zeros,
7};
8use commonware_runtime::{Buf, BufMut};
9use core::{fmt::Debug, hash::Hash, ops::Deref};
10
11/// Trait bound for key types used in QMDB operations. Satisfied by both fixed-size keys
12/// (`Array` types) and variable-length keys (`Vec<u8>`).
13pub trait Key:
14    CodecShared + Clone + 'static + Eq + Ord + Hash + AsRef<[u8]> + Deref<Target = [u8]> + Debug
15{
16}
17
18impl<T> Key for T where
19    T: CodecShared + Clone + 'static + Eq + Ord + Hash + AsRef<[u8]> + Deref<Target = [u8]> + Debug
20{
21}
22
23/// An operation from which an inactivity floor can be read.
24pub trait Floored<F: Family> {
25    /// The inactivity floor location if this operation is a commit operation with a floor value,
26    /// None otherwise.
27    fn has_floor(&self) -> Option<Location<F>>;
28}
29
30/// An operation that can be applied to a database.
31pub trait Operation<F: Family>: Floored<F> {
32    /// The key type for this operation.
33    type Key: Key;
34
35    /// Returns the key if this operation involves a key, None otherwise.
36    fn key(&self) -> Option<&Self::Key>;
37
38    /// Consumes the operation and returns its owned key, if any.
39    fn into_key(self) -> Option<Self::Key>;
40
41    /// If this operation updates its key's value.
42    fn is_update(&self) -> bool;
43
44    /// If this operation deletes its key's value.
45    fn is_delete(&self) -> bool;
46}
47
48/// A trait for operations used by database variants that support commit operations.
49pub trait Committable {
50    /// If this operation is a commit operation.
51    fn is_commit(&self) -> bool;
52}
53
54/// Unpadded size of a fixed-encoded commit operation, context byte included.
55pub(crate) const fn commit_fixed_operation_size<V: FixedSize>() -> usize {
56    1 + 1 + V::SIZE + u64::SIZE
57}
58
59/// Writes a commit's optional metadata and inactivity floor in the fixed encoding.
60pub(crate) fn write_commit_fixed<F: Family, V: Write + FixedSize>(
61    metadata: &Option<V>,
62    floor: Location<F>,
63    buf: &mut impl BufMut,
64) {
65    if let Some(value) = metadata {
66        true.write(buf);
67        value.write(buf);
68    } else {
69        buf.put_bytes(0, 1 + V::SIZE);
70    }
71    buf.put_slice(&floor.as_u64().to_be_bytes());
72}
73
74/// Reads a commit's optional metadata and inactivity floor from the fixed encoding.
75pub(crate) fn read_commit_fixed<F: Family, V: Read<Cfg = ()> + FixedSize>(
76    buf: &mut impl Buf,
77) -> Result<(Option<V>, Location<F>), CodecError> {
78    let metadata = if bool::read(buf)? {
79        Some(V::read(buf)?)
80    } else {
81        ensure_zeros(buf, V::SIZE)?;
82        None
83    };
84    let floor = Location::new(u64::read(buf)?);
85    if !floor.is_valid() {
86        return Err(CodecError::Invalid(
87            "storage::qmdb::operation::commit",
88            "commit floor location overflow",
89        ));
90    }
91    Ok((metadata, floor))
92}
93
94/// Encoded size of a variable-encoded commit payload.
95pub(crate) fn commit_variable_payload_size<F: Family, V: EncodeSize>(
96    metadata: &Option<V>,
97    floor: Location<F>,
98) -> usize {
99    metadata.encode_size() + floor.encode_size()
100}
101
102/// Writes a commit's optional metadata and inactivity floor in the variable encoding.
103pub(crate) fn write_commit_variable<F: Family, V: Write>(
104    metadata: &Option<V>,
105    floor: Location<F>,
106    buf: &mut impl BufMut,
107) {
108    metadata.write(buf);
109    floor.write(buf);
110}
111
112/// Reads a commit's optional metadata and inactivity floor from the variable encoding.
113pub(crate) fn read_commit_variable<F: Family, V: Read>(
114    buf: &mut impl Buf,
115    value_cfg: &V::Cfg,
116) -> Result<(Option<V>, Location<F>), CodecError> {
117    let metadata = Option::<V>::read_cfg(buf, value_cfg)?;
118    let floor = Location::read(buf)?;
119    Ok((metadata, floor))
120}