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