commonware_sync/databases/
any.rs1use crate::{Hasher, Key, Translator, Value};
4use commonware_cryptography::Hasher as CryptoHasher;
5use commonware_parallel::Sequential;
6use commonware_runtime::{buffer, BufferPooler};
7use commonware_storage::{
8 journal::contiguous::fixed::Config as FConfig,
9 mmr::{self, full::Config as MmrConfig, Location, Proof},
10 qmdb::{
11 self,
12 any::{
13 unordered::{
14 fixed::{Db, Operation as FixedOperation},
15 Update,
16 },
17 FixedConfig as Config,
18 },
19 operation::Committable,
20 },
21 Context,
22};
23use commonware_utils::{NZUsize, NZU16, NZU64};
24use std::{future::Future, num::NonZeroU64};
25use tracing::error;
26
27pub type Database<E> = Db<mmr::Family, E, Key, Value, Hasher, Translator, Sequential>;
29
30pub type Operation = FixedOperation<mmr::Family, Key, Value>;
32
33pub fn create_config(context: &impl BufferPooler) -> Config<Translator, Sequential> {
35 let page_cache = buffer::paged::CacheRef::from_pooler(context, NZU16!(2048), NZUsize!(10));
36 Config {
37 merkle_config: MmrConfig {
38 journal_partition: "mmr-journal".into(),
39 metadata_partition: "mmr-metadata".into(),
40 items_per_blob: NZU64!(4096),
41 write_buffer: NZUsize!(4096),
42 strategy: Sequential,
43 page_cache: page_cache.clone(),
44 },
45 journal_config: FConfig {
46 partition: "log-journal".into(),
47 items_per_blob: NZU64!(4096),
48 write_buffer: NZUsize!(4096),
49 page_cache,
50 },
51 translator: Translator::default(),
52 init_cache_size: Some(NZUsize!(1 << 16)),
53 }
54}
55
56impl<E> crate::databases::ExampleDatabase for Database<E>
57where
58 E: Context,
59{
60 type Family = mmr::Family;
61 type Operation = Operation;
62
63 fn create_test_operations(count: usize, seed: u64, _starting_loc: u64) -> Vec<Self::Operation> {
64 let mut hasher = <Hasher as CryptoHasher>::new();
65 let mut operations = Vec::new();
66 for i in 0..count {
67 let key = {
68 hasher.update(&i.to_be_bytes());
69 hasher.update(&seed.to_be_bytes());
70 hasher.finalize()
71 };
72
73 let value = {
74 hasher.update(&key);
75 hasher.update(b"value");
76 hasher.finalize()
77 };
78
79 operations.push(Operation::Update(Update(key, value)));
80
81 if (i + 1) % 10 == 0 {
82 operations.push(Operation::CommitFloor(None, Location::from(i + 1)));
83 }
84 }
85
86 operations.push(Operation::CommitFloor(None, Location::from(count)));
88 operations
89 }
90
91 async fn add_operations(
92 &mut self,
93 operations: Vec<Self::Operation>,
94 ) -> Result<(), qmdb::Error<mmr::Family>> {
95 if operations.last().is_none() || !operations.last().unwrap().is_commit() {
96 error!("operations must end with a commit");
98 return Ok(());
99 }
100
101 let mut batch = self.new_batch();
102 for operation in operations {
103 match operation {
104 Operation::Update(Update(key, value)) => {
105 batch = batch.write(key, Some(value));
106 }
107 Operation::Delete(key) => {
108 batch = batch.write(key, None);
109 }
110 Operation::CommitFloor(metadata, _) => {
111 let merkleized = batch.merkleize(self, metadata).await?;
112 self.apply_batch(merkleized).await?;
113 self.commit().await?;
114 batch = self.new_batch();
115 }
116 }
117 }
118 Ok(())
119 }
120
121 fn current_floor(&self) -> u64 {
122 0
125 }
126
127 fn root(&self) -> Key {
128 self.root()
129 }
130
131 fn name() -> &'static str {
132 "any"
133 }
134}
135
136impl<E> crate::databases::Syncable for Database<E>
137where
138 E: Context,
139{
140 fn size(&self) -> Location {
141 self.bounds().end
142 }
143
144 fn sync_boundary(&self) -> Location {
145 self.sync_boundary()
146 }
147
148 fn historical_proof(
149 &self,
150 op_count: Location,
151 start_loc: Location,
152 max_ops: NonZeroU64,
153 ) -> impl Future<Output = Result<(Proof<Key>, Vec<Self::Operation>), qmdb::Error<mmr::Family>>> + Send
154 {
155 self.historical_proof(op_count, start_loc, max_ops)
156 }
157
158 fn pinned_nodes_at(
159 &self,
160 loc: Location,
161 ) -> impl Future<Output = Result<Vec<Key>, qmdb::Error<mmr::Family>>> + Send {
162 self.pinned_nodes_at(loc)
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use crate::databases::ExampleDatabase;
170 use commonware_runtime::deterministic;
171
172 type AnyDb = Database<deterministic::Context>;
173
174 #[test]
175 fn test_create_test_operations() {
176 let ops = <AnyDb as ExampleDatabase>::create_test_operations(5, 12345, 0);
177 assert_eq!(ops.len(), 6); if let Operation::CommitFloor(_, loc) = &ops[5] {
180 assert_eq!(*loc, 5);
181 } else {
182 panic!("Last operation should be a commit");
183 }
184 }
185
186 #[test]
187 fn test_deterministic_operations() {
188 let ops1 = <AnyDb as ExampleDatabase>::create_test_operations(3, 12345, 0);
190 let ops2 = <AnyDb as ExampleDatabase>::create_test_operations(3, 12345, 0);
191 assert_eq!(ops1, ops2);
192
193 let ops3 = <AnyDb as ExampleDatabase>::create_test_operations(3, 54321, 0);
195 assert_ne!(ops1, ops3);
196 }
197}