Skip to content

Commit 6cb0be1

Browse files
committed
feat: implement artifact builder for CardanoDatabase signed entity type
1 parent 92ac8ca commit 6cb0be1

File tree

2 files changed

+209
-0
lines changed

2 files changed

+209
-0
lines changed
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
use std::path::{Path, PathBuf};
2+
3+
use anyhow::{anyhow, Context};
4+
use async_trait::async_trait;
5+
use semver::Version;
6+
7+
use mithril_common::{
8+
entities::{
9+
ArtifactsLocations, CardanoDatabase, CardanoDbBeacon, Certificate, CompressionAlgorithm,
10+
ProtocolMessagePartKey, SignedEntityType,
11+
},
12+
StdResult,
13+
};
14+
15+
use crate::artifact_builder::ArtifactBuilder;
16+
17+
pub struct CardanoDatabaseArtifactBuilder {
18+
db_directory: PathBuf, // TODO: temporary, will be accessed through another dependency instead of direct path.
19+
cardano_node_version: Version,
20+
compression_algorithm: CompressionAlgorithm,
21+
}
22+
23+
impl CardanoDatabaseArtifactBuilder {
24+
pub fn new(
25+
db_directory: PathBuf,
26+
cardano_node_version: &Version,
27+
compression_algorithm: CompressionAlgorithm,
28+
) -> Self {
29+
Self {
30+
db_directory,
31+
cardano_node_version: cardano_node_version.clone(),
32+
compression_algorithm,
33+
}
34+
}
35+
}
36+
37+
#[async_trait]
38+
impl ArtifactBuilder<CardanoDbBeacon, CardanoDatabase> for CardanoDatabaseArtifactBuilder {
39+
async fn compute_artifact(
40+
&self,
41+
beacon: CardanoDbBeacon,
42+
certificate: &Certificate,
43+
) -> StdResult<CardanoDatabase> {
44+
let merkle_root = certificate
45+
.protocol_message
46+
.get_message_part(&ProtocolMessagePartKey::CardanoDatabaseMerkleRoot)
47+
.ok_or(anyhow!(
48+
"Can not find CardanoDatabaseMerkleRoot protocol message part in certificate"
49+
))
50+
.with_context(|| {
51+
format!(
52+
"Can not compute CardanoDatabase artifact for signed_entity: {:?}",
53+
SignedEntityType::CardanoDatabase(beacon.clone())
54+
)
55+
})?;
56+
let total_db_size_uncompressed = compute_uncompressed_database_size(&self.db_directory)?;
57+
58+
let cardano_database = CardanoDatabase::new(
59+
merkle_root.to_string(),
60+
beacon,
61+
total_db_size_uncompressed,
62+
ArtifactsLocations::default(), // TODO: temporary default locations, will be injected in next PR.
63+
self.compression_algorithm,
64+
&self.cardano_node_version,
65+
);
66+
67+
Ok(cardano_database)
68+
}
69+
}
70+
71+
// Return the sum of the files size contained in the subdirectories 'immutable', 'ledger' and 'volatile'.
72+
fn compute_uncompressed_database_size(db_directory: &Path) -> StdResult<u64> {
73+
let subdirs = ["immutable", "ledger", "volatile"];
74+
75+
let mut total_db_size_uncompressed = 0;
76+
for subdir in subdirs {
77+
let dir_path = db_directory.join(subdir);
78+
79+
total_db_size_uncompressed += get_directory_size(&dir_path)
80+
.with_context(|| format!("Failed to read metadata for directory: {:?}", dir_path))?;
81+
}
82+
83+
Ok(total_db_size_uncompressed)
84+
}
85+
86+
fn get_directory_size(path: &Path) -> StdResult<u64> {
87+
let entries =
88+
std::fs::read_dir(path).with_context(|| format!("Failed to read directory: {:?}", path))?;
89+
90+
let mut directory_size = 0;
91+
for entry in entries {
92+
let path = entry
93+
.with_context(|| format!("Failed to read directory entry in {:?}", path))?
94+
.path();
95+
96+
if path.is_file() {
97+
let metadata = std::fs::metadata(&path)
98+
.with_context(|| format!("Failed to read metadata for file: {:?}", path))?;
99+
directory_size += metadata.len();
100+
} else if path.is_dir() {
101+
directory_size += get_directory_size(&path)?;
102+
}
103+
}
104+
105+
Ok(directory_size)
106+
}
107+
108+
#[cfg(test)]
109+
mod tests {
110+
use std::path::PathBuf;
111+
112+
use mithril_common::{
113+
digesters::DummyImmutablesDbBuilder,
114+
entities::{ProtocolMessage, ProtocolMessagePartKey},
115+
test_utils::{fake_data, TempDir},
116+
};
117+
118+
use super::*;
119+
120+
fn get_test_directory(dir_name: &str) -> PathBuf {
121+
TempDir::create("cardano_database", dir_name)
122+
}
123+
124+
#[test]
125+
fn should_compute_the_size_of_the_uncompressed_database_only_immutable_ledger_and_volatile() {
126+
let test_dir = get_test_directory("should_compute_the_size_of_the_uncompressed_database_only_immutable_ledger_and_volatile");
127+
128+
DummyImmutablesDbBuilder::new(test_dir.as_os_str().to_str().unwrap())
129+
.with_immutables(&[1, 2])
130+
.set_immutable_file_size(1000)
131+
.with_ledger_files(vec!["blocks-0.dat".to_string()])
132+
.set_ledger_file_size(5000)
133+
.with_volatile_files(vec!["437".to_string(), "537".to_string()])
134+
.set_volatile_file_size(2000)
135+
.build();
136+
// Number of immutable files = 2 × 3 ('chunk', 'primary' and 'secondary').
137+
let expected_total_size = 2 * 3 * 1000 + 5000 + 2000 * 2;
138+
139+
std::fs::write(test_dir.join("non_computed_file.txt"), "file inside root").unwrap();
140+
let non_computed_dir = test_dir.join("non_computed_dir");
141+
std::fs::create_dir(&non_computed_dir).unwrap();
142+
std::fs::write(
143+
non_computed_dir.join("another_non_computed_file.txt"),
144+
"file inside a non computed directory",
145+
)
146+
.unwrap();
147+
148+
let total_size = compute_uncompressed_database_size(&test_dir).unwrap();
149+
150+
assert_eq!(expected_total_size, total_size);
151+
}
152+
153+
#[tokio::test]
154+
async fn should_compute_valid_artifact() {
155+
let test_dir = get_test_directory("should_compute_valid_artifact");
156+
157+
DummyImmutablesDbBuilder::new(test_dir.as_os_str().to_str().unwrap())
158+
.with_immutables(&[1, 2])
159+
.set_immutable_file_size(1000)
160+
.with_ledger_files(vec!["blocks-0.dat".to_string()])
161+
.set_ledger_file_size(5000)
162+
.with_volatile_files(vec!["437".to_string(), "537".to_string()])
163+
.set_volatile_file_size(2000)
164+
.build();
165+
// Number of immutable files = 2 × 3 ('chunk', 'primary' and 'secondary').
166+
let expected_total_size = 2 * 3 * 1000 + 5000 + 2000 * 2;
167+
168+
let cardano_database_artifact_builder = CardanoDatabaseArtifactBuilder::new(
169+
test_dir,
170+
&Version::parse("1.0.0").unwrap(),
171+
CompressionAlgorithm::Zstandard,
172+
);
173+
174+
let beacon = fake_data::beacon();
175+
let certificate_with_merkle_root = {
176+
let mut protocol_message = ProtocolMessage::new();
177+
protocol_message.set_message_part(
178+
ProtocolMessagePartKey::CardanoDatabaseMerkleRoot,
179+
"merkleroot".to_string(),
180+
);
181+
Certificate {
182+
protocol_message,
183+
..fake_data::certificate("certificate-123".to_string())
184+
}
185+
};
186+
187+
let artifact = cardano_database_artifact_builder
188+
.compute_artifact(beacon.clone(), &certificate_with_merkle_root)
189+
.await
190+
.unwrap();
191+
192+
let artifact_expected = CardanoDatabase::new(
193+
"merkleroot".to_string(),
194+
beacon,
195+
expected_total_size,
196+
ArtifactsLocations {
197+
digests: vec![],
198+
immutables: vec![],
199+
ancillary: vec![],
200+
},
201+
CompressionAlgorithm::Zstandard,
202+
&Version::parse("1.0.0").unwrap(),
203+
);
204+
205+
assert_eq!(artifact_expected, artifact);
206+
}
207+
}

mithril-aggregator/src/artifact_builder/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
//! The module used for building artifact
2+
mod cardano_database;
23
mod cardano_immutable_files_full;
34
mod cardano_stake_distribution;
45
mod cardano_transactions;
56
mod interface;
67
mod mithril_stake_distribution;
78

9+
pub use cardano_database::*;
810
pub use cardano_immutable_files_full::*;
911
pub use cardano_stake_distribution::*;
1012
pub use cardano_transactions::*;

0 commit comments

Comments
 (0)