#[cfg(feature = "fs")]
use std::path::Path;
use std::sync::Arc;
#[cfg(feature = "fs")]
use slog::Logger;
#[cfg(feature = "fs")]
use mithril_common::{
crypto_helper::MKProof,
messages::{CardanoDatabaseSnapshotMessage, CertificateMessage},
};
#[cfg(feature = "fs")]
use mithril_cardano_node_internal_database::entities::ImmutableFile;
#[cfg(feature = "fs")]
use crate::cardano_database_client::{VerifiedDigests, proving::CardanoDatabaseVerificationError};
use crate::common::{Epoch, EpochSpecifier};
#[cfg(feature = "fs")]
use crate::feedback::FeedbackSender;
#[cfg(feature = "fs")]
use crate::file_downloader::FileDownloader;
#[cfg(feature = "fs")]
use crate::utils::{AncillaryVerifier, TempDirectoryProvider};
use crate::{CardanoDatabaseSnapshot, CardanoDatabaseSnapshotListItem, MithrilResult};
use super::fetch::InternalArtifactRetriever;
use super::statistics::InternalStatisticsSender;
#[cfg(feature = "fs")]
use super::{
DownloadUnpackOptions, ImmutableFileRange, download_unpack::InternalArtifactDownloader,
proving::InternalArtifactProver,
};
pub struct CardanoDatabaseClient {
pub(super) artifact_retriever: InternalArtifactRetriever,
#[cfg(feature = "fs")]
pub(super) artifact_downloader: InternalArtifactDownloader,
#[cfg(feature = "fs")]
pub(super) artifact_prover: InternalArtifactProver,
pub(super) statistics_sender: InternalStatisticsSender,
}
#[cfg_attr(test, mockall::automock)]
#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
pub trait CardanoDatabaseAggregatorRequest: Send + Sync {
async fn list_latest(&self) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>>;
async fn list_by_epoch(
&self,
specifier: EpochSpecifier,
) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>>;
async fn get_by_hash(&self, hash: &str) -> MithrilResult<Option<CardanoDatabaseSnapshot>>;
async fn increment_cardano_database_complete_restoration_statistic(&self) -> MithrilResult<()>;
async fn increment_cardano_database_partial_restoration_statistic(&self) -> MithrilResult<()>;
async fn increment_immutables_snapshot_restored_statistic(
&self,
number_of_immutable_files_restored: u32,
) -> MithrilResult<()>;
async fn increment_ancillary_downloaded_statistic(&self) -> MithrilResult<()>;
}
impl CardanoDatabaseClient {
pub fn new(
aggregator_requester: Arc<dyn CardanoDatabaseAggregatorRequest>,
#[cfg(feature = "fs")] http_file_downloader: Arc<dyn FileDownloader>,
#[cfg(feature = "fs")] ancillary_verifier: Option<Arc<AncillaryVerifier>>,
#[cfg(feature = "fs")] feedback_sender: FeedbackSender,
#[cfg(feature = "fs")] temp_directory_provider: Arc<dyn TempDirectoryProvider>,
#[cfg(feature = "fs")] logger: Logger,
) -> Self {
#[cfg(feature = "fs")]
let logger =
mithril_common::logging::LoggerExtensions::new_with_component_name::<Self>(&logger);
Self {
artifact_retriever: InternalArtifactRetriever::new(aggregator_requester.clone()),
#[cfg(feature = "fs")]
artifact_downloader: InternalArtifactDownloader::new(
http_file_downloader.clone(),
ancillary_verifier,
feedback_sender.clone(),
logger.clone(),
),
#[cfg(feature = "fs")]
artifact_prover: InternalArtifactProver::new(
http_file_downloader.clone(),
temp_directory_provider.clone(),
logger.clone(),
),
statistics_sender: InternalStatisticsSender::new(aggregator_requester.clone()),
}
}
pub async fn list(&self) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>> {
self.artifact_retriever.list().await
}
pub async fn list_by_epoch(
&self,
epoch: Epoch,
) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>> {
self.artifact_retriever.list_by_epoch(epoch).await
}
pub async fn list_for_latest_epoch(
&self,
) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>> {
self.artifact_retriever.list_for_latest_epoch().await
}
pub async fn list_for_latest_epoch_with_offset(
&self,
offset: u64,
) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>> {
self.artifact_retriever
.list_for_latest_epoch_with_offset(offset)
.await
}
pub async fn get(&self, hash: &str) -> MithrilResult<Option<CardanoDatabaseSnapshot>> {
self.artifact_retriever.get(hash).await
}
#[cfg(feature = "fs")]
pub async fn download_unpack(
&self,
cardano_database_snapshot: &CardanoDatabaseSnapshotMessage,
immutable_file_range: &ImmutableFileRange,
target_dir: &Path,
download_unpack_options: DownloadUnpackOptions,
) -> MithrilResult<()> {
self.artifact_downloader
.download_unpack(
cardano_database_snapshot,
immutable_file_range,
target_dir,
download_unpack_options,
)
.await
}
#[cfg(feature = "fs")]
pub async fn download_and_verify_digests(
&self,
certificate: &CertificateMessage,
cardano_database_snapshot: &CardanoDatabaseSnapshotMessage,
) -> MithrilResult<VerifiedDigests> {
self.artifact_prover
.download_and_verify_digests(certificate, cardano_database_snapshot)
.await
}
#[cfg(feature = "fs")]
pub async fn verify_cardano_database(
&self,
certificate: &CertificateMessage,
cardano_database_snapshot: &CardanoDatabaseSnapshotMessage,
immutable_file_range: &ImmutableFileRange,
allow_missing: bool,
database_dir: &Path,
verified_digests: &VerifiedDigests,
) -> Result<MKProof, CardanoDatabaseVerificationError> {
self.artifact_prover
.verify_cardano_database(
certificate,
cardano_database_snapshot,
immutable_file_range,
allow_missing,
database_dir,
verified_digests,
)
.await
}
#[cfg(feature = "fs")]
pub fn check_has_immutables(&self, database_dir: &Path) -> MithrilResult<()> {
ImmutableFile::at_least_one_immutable_files_exist_in_dir(database_dir)?;
Ok(())
}
pub async fn add_statistics(
&self,
full_restoration: bool,
include_ancillary: bool,
number_of_immutable_files_restored: u64,
) -> MithrilResult<()> {
self.statistics_sender
.add_statistics(
full_restoration,
include_ancillary,
number_of_immutable_files_restored,
)
.await
}
}
#[cfg(test)]
pub(crate) mod test_dependency_injector {
use super::*;
#[cfg(feature = "fs")]
use mithril_common::crypto_helper::ManifestVerifierVerificationKey;
#[cfg(feature = "fs")]
use crate::file_downloader::{FileDownloader, MockFileDownloaderBuilder};
#[cfg(feature = "fs")]
use crate::utils::TimestampTempDirectoryProvider;
#[cfg(feature = "fs")]
use crate::{feedback::FeedbackReceiver, test_utils::TestLogger};
pub(crate) struct CardanoDatabaseClientDependencyInjector {
aggregator_requester: MockCardanoDatabaseAggregatorRequest,
#[cfg(feature = "fs")]
http_file_downloader: Arc<dyn FileDownloader>,
#[cfg(feature = "fs")]
ancillary_verifier: Option<Arc<AncillaryVerifier>>,
#[cfg(feature = "fs")]
feedback_receivers: Vec<Arc<dyn FeedbackReceiver>>,
#[cfg(feature = "fs")]
temp_directory_provider: Arc<dyn TempDirectoryProvider>,
#[cfg(feature = "fs")]
logger: Logger,
}
impl CardanoDatabaseClientDependencyInjector {
pub(crate) fn new() -> Self {
Self {
aggregator_requester: MockCardanoDatabaseAggregatorRequest::new(),
#[cfg(feature = "fs")]
http_file_downloader: Arc::new(
MockFileDownloaderBuilder::default()
.with_compression(None)
.with_success()
.with_times(0)
.build(),
),
#[cfg(feature = "fs")]
ancillary_verifier: None,
#[cfg(feature = "fs")]
feedback_receivers: vec![],
#[cfg(feature = "fs")]
temp_directory_provider: Arc::new(TimestampTempDirectoryProvider::new(
"cardano_database_client_test",
)),
#[cfg(feature = "fs")]
logger: TestLogger::stdout(),
}
}
#[cfg(feature = "fs")]
pub(crate) fn with_logger(self, logger: Logger) -> Self {
#[cfg(feature = "fs")]
Self { logger, ..self }
}
pub(crate) fn with_aggregator_requester_mock_config<F>(mut self, config: F) -> Self
where
F: FnOnce(&mut MockCardanoDatabaseAggregatorRequest),
{
config(&mut self.aggregator_requester);
self
}
#[cfg(feature = "fs")]
pub(crate) fn with_http_file_downloader(
self,
http_file_downloader: Arc<dyn FileDownloader>,
) -> Self {
Self {
http_file_downloader,
..self
}
}
#[cfg(feature = "fs")]
pub(crate) fn with_ancillary_verifier<T>(self, ancillary_verification_key: T) -> Self
where
T: TryInto<ManifestVerifierVerificationKey>,
T::Error: std::fmt::Debug,
{
Self {
ancillary_verifier: Some(Arc::new(AncillaryVerifier::new(
ancillary_verification_key.try_into().unwrap(),
))),
..self
}
}
#[cfg(feature = "fs")]
pub(crate) fn with_feedback_receivers(
self,
feedback_receivers: &[Arc<dyn FeedbackReceiver>],
) -> Self {
Self {
feedback_receivers: feedback_receivers.to_vec(),
..self
}
}
#[cfg(feature = "fs")]
pub(crate) fn with_temp_directory_provider(
self,
temp_directory_provider: Arc<dyn TempDirectoryProvider>,
) -> Self {
Self {
temp_directory_provider,
..self
}
}
#[cfg(feature = "fs")]
pub(crate) fn build_cardano_database_client(self) -> CardanoDatabaseClient {
CardanoDatabaseClient::new(
Arc::new(self.aggregator_requester),
self.http_file_downloader,
self.ancillary_verifier,
FeedbackSender::new(&self.feedback_receivers),
self.temp_directory_provider,
self.logger,
)
}
#[cfg(not(feature = "fs"))]
pub(crate) fn build_cardano_database_client(self) -> CardanoDatabaseClient {
CardanoDatabaseClient::new(Arc::new(self.aggregator_requester))
}
}
mod tests {
#[cfg(feature = "fs")]
use crate::feedback::StackFeedbackReceiver;
use crate::common::test::Dummy;
use super::*;
#[cfg(feature = "fs")]
#[test]
fn test_cardano_database_client_dependency_injector_builds() {
let _ = CardanoDatabaseClientDependencyInjector::new()
.with_aggregator_requester_mock_config(|requester| {
let message = vec![CardanoDatabaseSnapshotListItem {
hash: "hash-123".to_string(),
..CardanoDatabaseSnapshotListItem::dummy()
}];
requester.expect_list_latest().return_once(move || Ok(message));
})
.with_http_file_downloader(Arc::new(
MockFileDownloaderBuilder::default()
.with_success()
.with_times(0)
.build(),
))
.with_feedback_receivers(&[Arc::new(StackFeedbackReceiver::new())])
.build_cardano_database_client();
}
#[cfg(not(feature = "fs"))]
#[test]
fn test_cardano_database_client_dependency_injector_builds() {
let _ = CardanoDatabaseClientDependencyInjector::new()
.with_aggregator_requester_mock_config(|requester| {
let message = vec![CardanoDatabaseSnapshotListItem {
hash: "hash-123".to_string(),
..CardanoDatabaseSnapshotListItem::dummy()
}];
requester.expect_list_latest().return_once(move || Ok(message));
})
.build_cardano_database_client();
}
}
}