Skip to content

Commit ae10e2c

Browse files
committed
Add Digester dependency & tests
1 parent 5c2cd19 commit ae10e2c

File tree

1 file changed

+79
-68
lines changed

1 file changed

+79
-68
lines changed

mithril-client/src/runtime.rs

Lines changed: 79 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@ use crate::aggregator::{AggregatorHandler, AggregatorHandlerError};
77
use crate::entities::*;
88
use crate::verifier::{ProtocolError, Verifier};
99

10+
use mithril_common::digesters::{Digester, DigesterError, ImmutableDigester};
1011
use mithril_common::entities::{Certificate, Snapshot};
11-
use mithril_common::immutable_digester::{ImmutableDigester, ImmutableDigesterError};
1212

1313
/// AggregatorHandlerWrapper wraps an AggregatorHandler
1414
pub type AggregatorHandlerWrapper = Box<dyn AggregatorHandler>;
1515

1616
/// VerifierWrapper wraps a Verifier
1717
pub type VerifierWrapper = Box<dyn Verifier>;
1818

19+
/// DigesterWrapper wraps a Digester
20+
pub type DigesterWrapper = Box<dyn Digester>;
21+
1922
#[derive(Error, Debug)]
2023
pub enum RuntimeError {
2124
#[error("a dependency is missing: '{0}'")]
@@ -31,7 +34,7 @@ pub enum RuntimeError {
3134
Verifier(#[from] ProtocolError),
3235

3336
#[error("immutale digester error: '{0}'")]
34-
ImmutableDigester(#[from] ImmutableDigesterError),
37+
ImmutableDigester(#[from] DigesterError),
3538

3639
#[error("digest unmatch error: '{0}'")]
3740
DigestUnmatch(String),
@@ -50,6 +53,9 @@ pub struct Runtime {
5053

5154
/// Verifier dependency that verifies certificates and their multi signatures
5255
verifier: Option<VerifierWrapper>,
56+
57+
/// Digester dependency that computes the digest used as the message ot be signed and embedded in the multisignature
58+
digester: Option<DigesterWrapper>,
5359
}
5460

5561
impl Runtime {
@@ -59,6 +65,7 @@ impl Runtime {
5965
network,
6066
aggregator_handler: None,
6167
verifier: None,
68+
digester: None,
6269
}
6370
}
6471

@@ -77,6 +84,12 @@ impl Runtime {
7784
self
7885
}
7986

87+
/// With Digester
88+
pub fn with_digester(&mut self, digester: DigesterWrapper) -> &mut Self {
89+
self.digester = Some(digester);
90+
self
91+
}
92+
8093
/// Get AggregatorHandler
8194
fn get_aggregator_handler(&self) -> Result<&AggregatorHandlerWrapper, RuntimeError> {
8295
self.aggregator_handler
@@ -91,6 +104,13 @@ impl Runtime {
91104
.ok_or_else(|| RuntimeError::MissingDependency("verifier".to_string()))
92105
}
93106

107+
/// Get Digester
108+
fn get_digester(&self) -> Result<&DigesterWrapper, RuntimeError> {
109+
self.digester
110+
.as_ref()
111+
.ok_or_else(|| RuntimeError::MissingDependency("digester".to_string()))
112+
}
113+
94114
/// List snapshots
95115
pub async fn list_snapshots(&self) -> Result<Vec<SnapshotListItem>, RuntimeError> {
96116
debug!("List snapshots");
@@ -148,7 +168,7 @@ impl Runtime {
148168
}
149169

150170
/// Restore a snapshot by digest
151-
pub async fn restore_snapshot(&self, digest: &str) -> Result<String, RuntimeError> {
171+
pub async fn restore_snapshot(&mut self, digest: &str) -> Result<String, RuntimeError> {
152172
debug!("Restore snapshot {}", digest);
153173
let snapshot = &self
154174
.get_aggregator_handler()?
@@ -178,11 +198,17 @@ impl Runtime {
178198
.unpack_snapshot(digest)
179199
.await
180200
.map_err(RuntimeError::AggregatorHandler)?;
181-
let unpacked_digest =
182-
ImmutableDigester::new(Path::new(unpacked_path).into(), slog_scope::logger())
183-
.compute_digest()
184-
.map_err(RuntimeError::ImmutableDigester)?
185-
.digest;
201+
if self.get_digester().is_err() {
202+
self.with_digester(Box::new(ImmutableDigester::new(
203+
Path::new(unpacked_path).into(),
204+
slog_scope::logger(),
205+
)));
206+
}
207+
let unpacked_digest = self
208+
.get_digester()?
209+
.compute_digest()
210+
.map_err(RuntimeError::ImmutableDigester)?
211+
.digest;
186212
match unpacked_digest == digest {
187213
true => Ok(unpacked_path.to_owned()),
188214
false => Err(RuntimeError::DigestUnmatch(unpacked_digest)),
@@ -227,41 +253,20 @@ pub(crate) fn convert_to_field_items(
227253
#[cfg(test)]
228254
mod tests {
229255
use super::*;
256+
use mockall::mock;
230257

231258
use crate::aggregator::{AggregatorHandlerError, MockAggregatorHandler};
232259
use crate::verifier::{MockVerifier, ProtocolError};
260+
use mithril_common::digesters::{Digester, DigesterError, DigesterResult};
233261
use mithril_common::fake_data;
234262

235-
// TODO: Dulicated test setup code from 'mithril-common/src/immutable_digester.rs' that maybe should be set in a `tests_setup' module in the 'immutable_digester'. A mock would be even better!
236-
// Start duplicated code
237-
use std::fs;
238-
use std::fs::File;
239-
use std::io::prelude::*;
240-
use std::path::PathBuf;
241-
const ROOT_FOLDER: &str = "tests-work-folder/";
242-
243-
fn get_test_dir(subdir_name: &str) -> PathBuf {
244-
let parent_dir = format!("{}{}", ROOT_FOLDER, subdir_name);
245-
let parent_dir = Path::new(&parent_dir).to_path_buf();
246-
247-
if parent_dir.exists() {
248-
fs::remove_dir_all(&parent_dir)
249-
.expect(&*format!("Could not remove dir {:?}", parent_dir));
263+
mock! {
264+
pub DigesterImpl { }
265+
impl Digester for DigesterImpl {
266+
fn compute_digest(&self) -> Result<DigesterResult, DigesterError>;
250267
}
251-
fs::create_dir_all(&parent_dir).expect(&*format!("Could not create dir {:?}", parent_dir));
252-
253-
parent_dir
254268
}
255269

256-
fn create_fake_files(parent_dir: &Path, child_filenames: &[&str]) {
257-
for filename in child_filenames {
258-
let file = parent_dir.join(Path::new(filename));
259-
let mut source_file = File::create(&file).unwrap();
260-
write!(source_file, "This is a test file named '{}'", filename).unwrap();
261-
}
262-
}
263-
// End duplicated code
264-
265270
#[tokio::test]
266271
async fn test_list_snapshots_ok() {
267272
let network = "testnet".to_string();
@@ -338,26 +343,14 @@ mod tests {
338343

339344
#[tokio::test]
340345
async fn test_restore_snapshot_ok() {
341-
let target_dir = get_test_dir("test_restore_snapshot_ok/immutable");
342-
let entries = vec![
343-
"001.chunk",
344-
"001.primary",
345-
"001.secondary",
346-
"002.chunk",
347-
"002.primary",
348-
"002.secondary",
349-
];
350-
create_fake_files(&target_dir, &entries);
351-
let digest = ImmutableDigester::new(target_dir.clone().into(), slog_scope::logger())
352-
.compute_digest()
353-
.unwrap()
354-
.digest;
346+
let digest = "digest123";
355347
let certificate_hash = "certhash123";
356348
let mut fake_certificate = fake_data::certificate(certificate_hash.to_string());
357349
fake_certificate.hash = Certificate::compute_hash(&fake_certificate);
358350
let fake_snapshot = fake_data::snapshots(1).first().unwrap().to_owned();
359351
let mut mock_aggregator_handler = MockAggregatorHandler::new();
360352
let mut mock_verifier = MockVerifier::new();
353+
let mut mock_digester = MockDigesterImpl::new();
361354
mock_aggregator_handler
362355
.expect_get_snapshot_details()
363356
.return_once(move |_| Ok(fake_snapshot));
@@ -366,14 +359,21 @@ mod tests {
366359
.return_once(move |_| Ok(fake_certificate));
367360
mock_aggregator_handler
368361
.expect_unpack_snapshot()
369-
.return_once(move |_| Ok(target_dir.to_str().unwrap().to_owned()));
362+
.return_once(move |_| Ok("./target-dir".to_string()));
370363
mock_verifier
371364
.expect_verify_multi_signature()
372365
.return_once(|_, _, _, _| Ok(()));
366+
mock_digester.expect_compute_digest().return_once(|| {
367+
Ok(DigesterResult {
368+
digest: digest.to_string(),
369+
last_immutable_file_number: 0,
370+
})
371+
});
373372
let mut client = Runtime::new("testnet".to_string());
374373
client
375374
.with_aggregator_handler(Box::new(mock_aggregator_handler))
376-
.with_verifier(Box::new(mock_verifier));
375+
.with_verifier(Box::new(mock_verifier))
376+
.with_digester(Box::new(mock_digester));
377377
let restore = client.restore_snapshot(&digest).await;
378378
restore.expect("unexpected error");
379379
}
@@ -386,38 +386,38 @@ mod tests {
386386
let fake_snapshot = fake_data::snapshots(1).first().unwrap().to_owned();
387387
let mut mock_aggregator_handler = MockAggregatorHandler::new();
388388
let mock_verifier = MockVerifier::new();
389+
let mut mock_digester = MockDigesterImpl::new();
389390
mock_aggregator_handler
390391
.expect_get_snapshot_details()
391392
.return_once(move |_| Ok(fake_snapshot));
392393
mock_aggregator_handler
393394
.expect_get_certificate_details()
394395
.return_once(move |_| Ok(fake_certificate));
396+
mock_digester.expect_compute_digest().return_once(|| {
397+
Ok(DigesterResult {
398+
digest: digest.to_string(),
399+
last_immutable_file_number: 0,
400+
})
401+
});
395402
let mut client = Runtime::new("testnet".to_string());
396403
client
397404
.with_aggregator_handler(Box::new(mock_aggregator_handler))
398-
.with_verifier(Box::new(mock_verifier));
405+
.with_verifier(Box::new(mock_verifier))
406+
.with_digester(Box::new(mock_digester));
399407
let restore = client.restore_snapshot(&digest).await;
400408
assert!(restore.is_err(), "an error should have occurred");
401409
}
402410

403411
#[tokio::test]
404412
async fn test_restore_snapshot_ko_digests_not_matching() {
405-
let target_dir = get_test_dir("test_restore_snapshot_ko_digests_not_matching/immutable");
406-
let entries = vec![
407-
"001.chunk",
408-
"001.primary",
409-
"001.secondary",
410-
"002.chunk",
411-
"002.primary",
412-
"002.secondary",
413-
];
414-
create_fake_files(&target_dir, &entries);
415-
let digest = "digest-not-matching";
413+
let digest = "digest123";
414+
let digest_tampered = "digest-not-matching";
416415
let certificate_hash = "certhash123";
417416
let fake_certificate = fake_data::certificate(certificate_hash.to_string());
418417
let fake_snapshot = fake_data::snapshots(1).first().unwrap().to_owned();
419418
let mut mock_aggregator_handler = MockAggregatorHandler::new();
420419
let mut mock_verifier = MockVerifier::new();
420+
let mut mock_digester = MockDigesterImpl::new();
421421
mock_aggregator_handler
422422
.expect_get_snapshot_details()
423423
.return_once(move |_| Ok(fake_snapshot));
@@ -426,27 +426,34 @@ mod tests {
426426
.return_once(move |_| Ok(fake_certificate));
427427
mock_aggregator_handler
428428
.expect_unpack_snapshot()
429-
.return_once(move |_| Ok(target_dir.to_str().unwrap().to_owned()));
429+
.return_once(move |_| Ok("./target-dir".to_string()));
430430
mock_verifier
431431
.expect_verify_multi_signature()
432432
.return_once(|_, _, _, _| Ok(()));
433+
mock_digester.expect_compute_digest().return_once(|| {
434+
Ok(DigesterResult {
435+
digest: digest_tampered.to_string(),
436+
last_immutable_file_number: 0,
437+
})
438+
});
433439
let mut client = Runtime::new("testnet".to_string());
434440
client
435441
.with_aggregator_handler(Box::new(mock_aggregator_handler))
436-
.with_verifier(Box::new(mock_verifier));
442+
.with_verifier(Box::new(mock_verifier))
443+
.with_digester(Box::new(mock_digester));
437444
let restore = client.restore_snapshot(digest).await;
438445
assert!(restore.is_err(), "an error should have occurred");
439446
}
440447

441448
#[tokio::test]
442449
async fn test_restore_snapshot_ko_digester_error() {
443-
let target_dir = get_test_dir("test_restore_snapshot_ko_digester_error/immutable");
444450
let digest = "digest123";
445451
let certificate_hash = "certhash123";
446452
let fake_certificate = fake_data::certificate(certificate_hash.to_string());
447453
let fake_snapshot = fake_data::snapshots(1).first().unwrap().to_owned();
448454
let mut mock_aggregator_handler = MockAggregatorHandler::new();
449455
let mut mock_verifier = MockVerifier::new();
456+
let mut mock_digester = MockDigesterImpl::new();
450457
mock_aggregator_handler
451458
.expect_get_snapshot_details()
452459
.return_once(move |_| Ok(fake_snapshot));
@@ -455,14 +462,18 @@ mod tests {
455462
.return_once(move |_| Ok(fake_certificate));
456463
mock_aggregator_handler
457464
.expect_unpack_snapshot()
458-
.return_once(move |_| Ok(target_dir.to_str().unwrap().to_owned()));
465+
.return_once(move |_| Ok("./target-dir".to_string()));
459466
mock_verifier
460467
.expect_verify_multi_signature()
461468
.return_once(|_, _, _, _| Ok(()));
469+
mock_digester
470+
.expect_compute_digest()
471+
.return_once(|| Err(DigesterError::NotEnoughImmutable()));
462472
let mut client = Runtime::new("testnet".to_string());
463473
client
464474
.with_aggregator_handler(Box::new(mock_aggregator_handler))
465-
.with_verifier(Box::new(mock_verifier));
475+
.with_verifier(Box::new(mock_verifier))
476+
.with_digester(Box::new(mock_digester));
466477
let restore = client.restore_snapshot(digest).await;
467478
assert!(restore.is_err(), "an error should have occurred");
468479
}

0 commit comments

Comments
 (0)