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