commonware_sync/databases/
any.rs

1//! Any database types and helpers for the sync example.
2
3use crate::{Hasher, Key, Translator, Value};
4use commonware_cryptography::Hasher as CryptoHasher;
5use commonware_runtime::{buffer, Clock, Metrics, Storage};
6use commonware_storage::{
7    mmr::{Location, Proof},
8    qmdb::{
9        self,
10        any::{
11            unordered::{
12                fixed::{Db, Operation as FixedOperation},
13                Update,
14            },
15            FixedConfig as Config,
16        },
17        store::CleanStore,
18    },
19};
20use commonware_utils::{NZUsize, NZU64};
21use std::{future::Future, num::NonZeroU64};
22
23/// Database type alias.
24pub type Database<E> = Db<E, Key, Value, Hasher, Translator>;
25
26/// Operation type alias.
27pub type Operation = FixedOperation<Key, Value>;
28
29/// Create a database configuration for use in tests.
30pub fn create_config() -> Config<Translator> {
31    Config {
32        mmr_journal_partition: "mmr_journal".into(),
33        mmr_metadata_partition: "mmr_metadata".into(),
34        mmr_items_per_blob: NZU64!(4096),
35        mmr_write_buffer: NZUsize!(1024),
36        log_journal_partition: "log_journal".into(),
37        log_items_per_blob: NZU64!(4096),
38        log_write_buffer: NZUsize!(1024),
39        translator: Translator::default(),
40        thread_pool: None,
41        buffer_pool: buffer::PoolRef::new(NZUsize!(1024), NZUsize!(10)),
42    }
43}
44
45impl<E> crate::databases::Syncable for Database<E>
46where
47    E: Storage + Clock + Metrics,
48{
49    type Operation = Operation;
50
51    fn create_test_operations(count: usize, seed: u64) -> Vec<Self::Operation> {
52        let mut hasher = <Hasher as CryptoHasher>::new();
53        let mut operations = Vec::new();
54        for i in 0..count {
55            let key = {
56                hasher.update(&i.to_be_bytes());
57                hasher.update(&seed.to_be_bytes());
58                hasher.finalize()
59            };
60
61            let value = {
62                hasher.update(&key);
63                hasher.update(b"value");
64                hasher.finalize()
65            };
66
67            operations.push(Operation::Update(Update(key, value)));
68
69            if (i + 1) % 10 == 0 {
70                operations.push(Operation::CommitFloor(None, Location::from(i + 1)));
71            }
72        }
73
74        // Always end with a commit
75        operations.push(Operation::CommitFloor(None, Location::from(count)));
76        operations
77    }
78
79    async fn add_operations(
80        database: &mut Self,
81        operations: Vec<Self::Operation>,
82    ) -> Result<(), commonware_storage::qmdb::Error> {
83        for operation in operations {
84            match operation {
85                Operation::Update(Update(key, value)) => {
86                    database.update(key, value).await?;
87                }
88                Operation::Delete(key) => {
89                    database.delete(key).await?;
90                }
91                Operation::CommitFloor(metadata, _) => {
92                    database.commit(metadata).await?;
93                }
94            }
95        }
96        Ok(())
97    }
98
99    async fn commit(&mut self) -> Result<(), commonware_storage::qmdb::Error> {
100        self.commit(None).await?;
101        Ok(())
102    }
103
104    fn root(&self) -> Key {
105        CleanStore::root(self)
106    }
107
108    fn op_count(&self) -> Location {
109        self.op_count()
110    }
111
112    fn lower_bound(&self) -> Location {
113        self.inactivity_floor_loc()
114    }
115
116    fn historical_proof(
117        &self,
118        op_count: Location,
119        start_loc: Location,
120        max_ops: NonZeroU64,
121    ) -> impl Future<Output = Result<(Proof<Key>, Vec<Self::Operation>), qmdb::Error>> + Send {
122        CleanStore::historical_proof(self, op_count, start_loc, max_ops)
123    }
124
125    fn name() -> &'static str {
126        "any"
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133    use crate::databases::Syncable;
134    use commonware_runtime::deterministic;
135
136    type AnyDb = Database<deterministic::Context>;
137
138    #[test]
139    fn test_create_test_operations() {
140        let ops = <AnyDb as Syncable>::create_test_operations(5, 12345);
141        assert_eq!(ops.len(), 6); // 5 operations + 1 commit
142
143        if let Operation::CommitFloor(_, loc) = &ops[5] {
144            assert_eq!(*loc, 5);
145        } else {
146            panic!("Last operation should be a commit");
147        }
148    }
149
150    #[test]
151    fn test_deterministic_operations() {
152        // Operations should be deterministic based on seed
153        let ops1 = <AnyDb as Syncable>::create_test_operations(3, 12345);
154        let ops2 = <AnyDb as Syncable>::create_test_operations(3, 12345);
155        assert_eq!(ops1, ops2);
156
157        // Different seeds should produce different operations
158        let ops3 = <AnyDb as Syncable>::create_test_operations(3, 54321);
159        assert_ne!(ops1, ops3);
160    }
161}