Skip to content

Commit d63b82b

Browse files
committed
feat(aggregator): ensure synced data is stored from genesis to latest
This is critical since rows are returned from the database by insertion order reversed.
1 parent b855ab3 commit d63b82b

File tree

1 file changed

+67
-13
lines changed

1 file changed

+67
-13
lines changed

mithril-aggregator/src/services/certificate_chain_synchroniser/synchroniser_service.rs

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
//! - if valid, store it in an in-memory FIFO list
1313
//! - if invalid, abort with an `Err`
1414
//! 3. Repeat step 2. with each parent of the certificate until the genesis certificate is reached
15-
//! 4. Store the fetched certificates in the database, for each certificate:
15+
//! 4. Store the fetched certificates in the database, from genesis to latest, for each certificate:
1616
//! - if it exists in the database, it is replaced
1717
//! - if it doesn't exist, it is inserted
1818
//! 5. End
1919
//!
2020
use anyhow::{Context, anyhow};
2121
use async_trait::async_trait;
2222
use slog::{Logger, debug, info};
23+
use std::collections::VecDeque;
2324
use std::sync::Arc;
2425

2526
use mithril_common::StdResult;
@@ -106,7 +107,9 @@ impl MithrilCertificateChainSynchronizer {
106107
&self,
107108
starting_point: Certificate,
108109
) -> StdResult<Vec<Certificate>> {
109-
let mut validated_certificates = Vec::new();
110+
// IMPORTANT: Order matters, certificates must be ordered from genesis to latest
111+
// (fetched database data is returned from last inserted to oldest)
112+
let mut validated_certificates = VecDeque::new();
110113
let mut certificate = starting_point;
111114

112115
loop {
@@ -117,7 +120,7 @@ impl MithrilCertificateChainSynchronizer {
117120
.with_context(
118121
|| format!("Failed to verify certificate: `{}`", certificate.hash,),
119122
)?;
120-
validated_certificates.push(certificate);
123+
validated_certificates.push_front(certificate);
121124

122125
match parent_certificate {
123126
None => break,
@@ -127,7 +130,7 @@ impl MithrilCertificateChainSynchronizer {
127130
}
128131
}
129132

130-
Ok(validated_certificates)
133+
Ok(validated_certificates.into())
131134
}
132135

133136
async fn store_certificate_chain(&self, certificate_chain: Vec<Certificate>) -> StdResult<()> {
@@ -445,6 +448,8 @@ mod tests {
445448
}
446449

447450
mod retrieve_validate_remote_certificate_chain {
451+
use mockall::predicate::{always, eq};
452+
448453
use super::*;
449454

450455
#[tokio::test]
@@ -494,9 +499,58 @@ mod tests {
494499
.await
495500
.unwrap();
496501

497-
let expected = chain.certificate_path_to_genesis(&starting_point.hash);
502+
let mut expected = chain.certificate_path_to_genesis(&starting_point.hash);
503+
expected.reverse();
498504
assert_eq!(remote_certificate_chain, expected);
499505
}
506+
507+
#[tokio::test]
508+
async fn return_chain_ordered_from_genesis_to_latest() {
509+
let base_certificate = fake_data::certificate("whatever");
510+
let chain = vec![
511+
fake_data::genesis_certificate("genesis"),
512+
Certificate {
513+
hash: "hash1".to_string(),
514+
previous_hash: "genesis".to_string(),
515+
..base_certificate.clone()
516+
},
517+
Certificate {
518+
hash: "hash2".to_string(),
519+
previous_hash: "hash1".to_string(),
520+
..base_certificate
521+
},
522+
];
523+
let synchroniser = MithrilCertificateChainSynchronizer {
524+
certificate_verifier: MockBuilder::<MockCertificateVerifier>::configure(|mock| {
525+
let cert_1 = chain[1].clone();
526+
mock.expect_verify_certificate()
527+
.with(eq(chain[2].clone()), always())
528+
.return_once(move |_, _| Ok(Some(cert_1)));
529+
let genesis = chain[0].clone();
530+
mock.expect_verify_certificate()
531+
.with(eq(chain[1].clone()), always())
532+
.return_once(move |_, _| Ok(Some(genesis)));
533+
mock.expect_verify_certificate()
534+
.with(eq(chain[0].clone()), always())
535+
.return_once(move |_, _| Ok(None));
536+
}),
537+
..MithrilCertificateChainSynchronizer::default_for_test()
538+
};
539+
540+
let starting_point = chain[2].clone();
541+
let remote_certificate_chain = synchroniser
542+
.retrieve_and_validate_remote_certificate_chain(starting_point.clone())
543+
.await
544+
.unwrap();
545+
546+
assert_eq!(
547+
remote_certificate_chain
548+
.into_iter()
549+
.map(|c| c.hash)
550+
.collect::<Vec<_>>(),
551+
vec!["genesis".to_string(), "hash1".to_string(), "hash2".to_string()]
552+
);
553+
}
500554
}
501555

502556
mod store_remote_certificate_chain {
@@ -579,10 +633,10 @@ mod tests {
579633
// Will sync even if force is false
580634
synchroniser.synchronize_certificate_chain(false).await.unwrap();
581635

582-
assert_eq!(
583-
remote_chain.certificate_path_to_genesis(&remote_chain.latest_certificate().hash),
584-
storer.stored_certificates()
585-
);
636+
let mut expected =
637+
remote_chain.certificate_path_to_genesis(&remote_chain.latest_certificate().hash);
638+
expected.reverse();
639+
assert_eq!(expected, storer.stored_certificates());
586640
}
587641

588642
#[tokio::test]
@@ -607,10 +661,10 @@ mod tests {
607661
// Force true - will sync
608662
synchroniser.synchronize_certificate_chain(true).await.unwrap();
609663

610-
assert_eq!(
611-
remote_chain.certificate_path_to_genesis(&remote_chain.latest_certificate().hash),
612-
storer.stored_certificates()
613-
);
664+
let mut expected =
665+
remote_chain.certificate_path_to_genesis(&remote_chain.latest_certificate().hash);
666+
expected.reverse();
667+
assert_eq!(expected, storer.stored_certificates());
614668
}
615669
}
616670
}

0 commit comments

Comments
 (0)