mithril_common/digesters/
dummy_cardano_db.rs

1use crate::test_utils::TempDir;
2use crate::{
3    digesters::{ImmutableFile, IMMUTABLE_DIR, LEDGER_DIR, VOLATILE_DIR},
4    entities::ImmutableFileNumber,
5};
6use std::{
7    fs::File,
8    io::prelude::Write,
9    path::{Path, PathBuf},
10};
11
12/// A dummy cardano immutable db.
13struct DummyImmutableDb {
14    /// The dummy cardano db directory path.
15    dir: PathBuf,
16    /// The [immutables files][ImmutableFile] in the dummy cardano db.
17    immutables_files: Vec<ImmutableFile>,
18    /// Files that doesn't follow the immutable file name scheme in the dummy cardano db.
19    non_immutables_files: Vec<PathBuf>,
20}
21
22impl DummyImmutableDb {
23    /// Add an immutable chunk file and its primary & secondary to the dummy DB.
24    pub fn add_immutable_file(&mut self) -> ImmutableFileNumber {
25        let new_file_number = self.last_immutable_number().unwrap_or(0) + 1;
26        let mut new_files = write_immutable_trio(None, &self.dir, new_file_number);
27
28        self.immutables_files.append(&mut new_files);
29
30        new_file_number
31    }
32
33    /// Return the file number of the last immutable
34    pub fn last_immutable_number(&self) -> Option<ImmutableFileNumber> {
35        self.immutables_files.last().map(|f| f.number)
36    }
37}
38
39/// A dummy cardano db.
40pub struct DummyCardanoDb {
41    /// The dummy cardano db directory path.
42    dir: PathBuf,
43
44    /// Dummy immutable db
45    immutable_db: DummyImmutableDb,
46
47    /// Ledger files in the dummy cardano db.
48    ledger_files: Vec<PathBuf>,
49}
50
51impl DummyCardanoDb {
52    /// Return the cardano db directory path.
53    pub fn get_dir(&self) -> &PathBuf {
54        &self.dir
55    }
56
57    /// Return the immutable db directory path.
58    pub fn get_immutable_dir(&self) -> &PathBuf {
59        &self.immutable_db.dir
60    }
61
62    /// Return the ledger directory path.
63    pub fn get_ledger_dir(&self) -> PathBuf {
64        self.dir.join(LEDGER_DIR)
65    }
66
67    /// Return the volatile directory path.
68    pub fn get_volatile_dir(&self) -> PathBuf {
69        self.dir.join(VOLATILE_DIR)
70    }
71
72    /// Return the file number of the last immutable
73    pub fn get_immutable_files(&self) -> &Vec<ImmutableFile> {
74        &self.immutable_db.immutables_files
75    }
76
77    /// Return the path of the files in the ledger directory.
78    pub fn get_ledger_files(&self) -> &Vec<PathBuf> {
79        &self.ledger_files
80    }
81
82    /// Add an immutable chunk file and its primary & secondary to the dummy DB.
83    pub fn add_immutable_file(&mut self) -> ImmutableFileNumber {
84        self.immutable_db.add_immutable_file()
85    }
86
87    /// Return the file number of the last immutable
88    pub fn last_immutable_number(&self) -> Option<ImmutableFileNumber> {
89        self.immutable_db.last_immutable_number()
90    }
91
92    /// Return the non immutables files in the immutables directory
93    pub fn get_non_immutables_files(&self) -> &Vec<PathBuf> {
94        &self.immutable_db.non_immutables_files
95    }
96}
97
98/// A [DummyCardanoDbBuilder] builder.
99pub struct DummyCardanoDbBuilder {
100    sub_dir: String,
101    immutables_to_write: Vec<ImmutableFileNumber>,
102    non_immutables_to_write: Vec<String>,
103    append_uncompleted_trio: bool,
104    immutable_file_size: Option<u64>,
105    ledger_files_to_write: Vec<String>,
106    ledger_file_size: Option<u64>,
107    volatile_files_to_write: Vec<String>,
108    volatile_file_size: Option<u64>,
109}
110
111impl DummyCardanoDbBuilder {
112    /// [DummyCardanoDbBuilder] factory, will create a folder with the given `dirname` in the
113    /// system temp directory, if it exists already it will be cleaned.
114    pub fn new(dir_name: &str) -> Self {
115        Self {
116            sub_dir: dir_name.to_string(),
117            immutables_to_write: vec![],
118            non_immutables_to_write: vec![],
119            append_uncompleted_trio: false,
120            immutable_file_size: None,
121            ledger_files_to_write: vec![],
122            ledger_file_size: None,
123            volatile_files_to_write: vec![],
124            volatile_file_size: None,
125        }
126    }
127
128    /// Set the immutables file number that will be used to generate the immutable files, for each
129    /// number three files will be generated (a 'chunk', a 'primary' and a 'secondary' file).
130    pub fn with_immutables(&mut self, immutables: &[ImmutableFileNumber]) -> &mut Self {
131        self.immutables_to_write = immutables.to_vec();
132        self
133    }
134
135    /// Set filenames to write to the db that doesn't follow the immutable file name scheme.
136    pub fn with_non_immutables(&mut self, non_immutables: &[&str]) -> &mut Self {
137        self.non_immutables_to_write = non_immutables.iter().map(|f| f.to_string()).collect();
138        self
139    }
140
141    /// Set ledger files to write to the db in the 'ledger' subdirectory.
142    pub fn with_ledger_files(&mut self, files: &[&str]) -> &mut Self {
143        self.ledger_files_to_write = files.iter().map(|name| name.to_string()).collect();
144        self
145    }
146
147    /// Set the size of all ledger files written by [build][Self::build] to the given `file_size` in bytes.
148    pub fn set_ledger_file_size(&mut self, file_size: u64) -> &mut Self {
149        self.ledger_file_size = Some(file_size);
150        self
151    }
152
153    /// Set volatile files to write to the db in the 'volatile' subdirectory.
154    pub fn with_volatile_files(&mut self, files: &[&str]) -> &mut Self {
155        self.volatile_files_to_write = files.iter().map(|f| f.to_string()).collect();
156        self
157    }
158
159    /// Set the size of all volatile files written by [build][Self::build] to the given `file_size` in bytes.
160    pub fn set_volatile_file_size(&mut self, file_size: u64) -> &mut Self {
161        self.volatile_file_size = Some(file_size);
162        self
163    }
164
165    /// Makes [build][Self::build] add another trio of immutables file, that won't be included
166    /// in its returned vec, to simulate the last 3 'uncompleted / wip' files that can be found in
167    /// a cardano immutable db.
168    pub fn append_immutable_trio(&mut self) -> &mut Self {
169        self.append_uncompleted_trio = true;
170        self
171    }
172
173    /// Set the size of all immutable files written by [build][Self::build] to the given `file_size` in bytes.
174    ///
175    /// Note: by default the size of the produced files is less than a 1kb.
176    pub fn set_immutable_trio_file_size(&mut self, trio_file_size: u64) -> &mut Self {
177        assert!(
178            trio_file_size % 3 == 0,
179            "'trio_file_size' must be a multiple of 3"
180        );
181
182        self.immutable_file_size = Some(trio_file_size / 3);
183        self
184    }
185
186    /// Build a [DummyCardanoDb].
187    pub fn build(&self) -> DummyCardanoDb {
188        let dir = get_test_dir(&self.sub_dir);
189
190        let mut ledger_files = vec![];
191        let mut non_immutables_files = vec![];
192        let mut immutable_numbers = self.immutables_to_write.clone();
193        immutable_numbers.sort();
194
195        if self.append_uncompleted_trio {
196            write_immutable_trio(
197                self.immutable_file_size,
198                &dir.join(IMMUTABLE_DIR),
199                match immutable_numbers.last() {
200                    None => 0,
201                    Some(last) => last + 1,
202                },
203            );
204        }
205
206        for non_immutable in &self.non_immutables_to_write {
207            non_immutables_files.push(write_dummy_file(
208                self.immutable_file_size,
209                &dir.join(IMMUTABLE_DIR),
210                non_immutable,
211            ));
212        }
213
214        for filename in &self.ledger_files_to_write {
215            let ledger_file_path =
216                write_dummy_file(self.ledger_file_size, &dir.join(LEDGER_DIR), filename);
217            ledger_files.push(ledger_file_path);
218        }
219
220        for filename in &self.volatile_files_to_write {
221            write_dummy_file(self.volatile_file_size, &dir.join(VOLATILE_DIR), filename);
222        }
223
224        let immutable_db = DummyImmutableDb {
225            dir: dir.join(IMMUTABLE_DIR),
226            immutables_files: immutable_numbers
227                .into_iter()
228                .flat_map(|ifn| {
229                    write_immutable_trio(self.immutable_file_size, &dir.join(IMMUTABLE_DIR), ifn)
230                })
231                .collect::<Vec<_>>(),
232            non_immutables_files,
233        };
234
235        DummyCardanoDb {
236            dir,
237            immutable_db,
238            ledger_files,
239        }
240    }
241}
242
243fn write_immutable_trio(
244    optional_size: Option<u64>,
245    dir: &Path,
246    immutable: ImmutableFileNumber,
247) -> Vec<ImmutableFile> {
248    let mut result = vec![];
249    for filename in [
250        format!("{immutable:05}.chunk"),
251        format!("{immutable:05}.primary"),
252        format!("{immutable:05}.secondary"),
253    ] {
254        let file = write_dummy_file(optional_size, dir, &filename);
255        result.push(ImmutableFile {
256            number: immutable.to_owned(),
257            path: file,
258            filename: filename.to_string(),
259        });
260    }
261    result
262}
263
264/// Create a file with the given name in the given dir, write some text to it, and then
265/// return its path.
266fn write_dummy_file(optional_size: Option<u64>, dir: &Path, filename: &str) -> PathBuf {
267    let file = dir.join(Path::new(filename));
268    let mut source_file = File::create(&file).unwrap();
269
270    write!(source_file, "This is a test file named '{filename}'").unwrap();
271
272    if let Some(file_size) = optional_size {
273        writeln!(source_file).unwrap();
274        source_file.set_len(file_size).unwrap();
275    }
276
277    file
278}
279
280fn get_test_dir(subdir_name: &str) -> PathBuf {
281    let db_dir = TempDir::create("test_cardano_db", subdir_name);
282    for subdir_name in [LEDGER_DIR, IMMUTABLE_DIR, VOLATILE_DIR] {
283        std::fs::create_dir(db_dir.join(subdir_name)).unwrap();
284    }
285
286    db_dir
287}