Skip to content

Commit 66b7a2e

Browse files
authored
Merge pull request #860 from input-output-hk/greg/849/tick_service
Add ticker service
2 parents e2eca8b + 8b829ff commit 66b7a2e

File tree

5 files changed

+184
-2
lines changed

5 files changed

+184
-2
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mithril-aggregator/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mithril-aggregator"
3-
version = "0.2.49"
3+
version = "0.2.50"
44
description = "A Mithril Aggregator server"
55
authors = { workspace = true }
66
edition = { workspace = true }

mithril-aggregator/src/dependency_injection/builder.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use crate::{
4343
event_store::{EventMessage, EventStore, TransmitterService},
4444
http_server::routes::router,
4545
stake_distribution_service::{MithrilStakeDistributionService, StakeDistributionService},
46+
ticker_service::{MithrilTickerService, TickerService},
4647
tools::{GcpFileUploader, GenesisToolsDependency},
4748
AggregatorConfig, AggregatorRunner, AggregatorRuntime, CertificatePendingStore,
4849
CertificateStore, Configuration, DependencyManager, DumbSnapshotUploader, DumbSnapshotter,
@@ -148,6 +149,9 @@ pub struct DependenciesBuilder {
148149

149150
/// Stake Distribution Service
150151
pub stake_distribution_service: Option<Arc<dyn StakeDistributionService>>,
152+
153+
/// Ticker Service (TODO: remove BeaconProvider)
154+
pub ticker_service: Option<Arc<dyn TickerService>>,
151155
}
152156

153157
impl DependenciesBuilder {
@@ -183,6 +187,7 @@ impl DependenciesBuilder {
183187
event_transmitter_channel: (None, None),
184188
api_version_provider: None,
185189
stake_distribution_service: None,
190+
ticker_service: None,
186191
}
187192
}
188193

@@ -1077,4 +1082,26 @@ impl DependenciesBuilder {
10771082

10781083
Ok(dependencies)
10791084
}
1085+
1086+
/// Create [TickerService] instance.
1087+
pub async fn build_ticker_service(&mut self) -> Result<Arc<dyn TickerService>> {
1088+
let network = self.configuration.get_network()?;
1089+
let chain_observer = self.get_chain_observer().await?;
1090+
let immutable_observer = self.get_immutable_file_observer().await?;
1091+
1092+
Ok(Arc::new(MithrilTickerService::new(
1093+
chain_observer,
1094+
immutable_observer,
1095+
network,
1096+
)))
1097+
}
1098+
1099+
/// [StakeDistributionService] service
1100+
pub async fn get_ticker_service(&mut self) -> Result<Arc<dyn TickerService>> {
1101+
if self.ticker_service.is_none() {
1102+
self.ticker_service = Some(self.build_ticker_service().await?);
1103+
}
1104+
1105+
Ok(self.ticker_service.as_ref().cloned().unwrap())
1106+
}
10801107
}

mithril-aggregator/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ mod snapshot_uploaders;
2828
mod snapshotter;
2929
pub mod stake_distribution_service;
3030
mod store;
31+
pub mod ticker_service;
3132
mod tools;
3233

3334
pub use crate::configuration::{
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//! ## Ticker Service
2+
//!
3+
//! This service read time information from the chain and helps create beacons
4+
//! for every message types.
5+
6+
use std::sync::Arc;
7+
8+
use async_trait::async_trait;
9+
use mithril_common::{
10+
chain_observer::ChainObserver,
11+
digesters::ImmutableFileObserver,
12+
entities::{Beacon, Epoch},
13+
CardanoNetwork, StdError,
14+
};
15+
use thiserror::Error;
16+
17+
type StdResult<T> = Result<T, StdError>;
18+
19+
#[derive(Debug, Error)]
20+
enum MithrilTickerError {
21+
#[error("No Epoch information was returned by the ChainObserver.")]
22+
NoEpoch,
23+
}
24+
25+
/// Service trait with consistent business oriented API.
26+
#[async_trait]
27+
pub trait TickerService {
28+
/// Return the current Epoch as read from the chain.
29+
async fn get_current_epoch(&self) -> StdResult<Epoch>;
30+
31+
/// Return the current Beacon used for CardanoImmutableFileDigest message type.
32+
async fn get_current_immutable_beacon(&self) -> StdResult<Beacon>;
33+
}
34+
35+
/// ## MithrilTickerService
36+
///
37+
/// This service is responsible of giving the right time information to other
38+
/// services. It reads data either from the Chain or the filesystem to create
39+
/// beacons for each message type.
40+
pub struct MithrilTickerService {
41+
chain_observer: Arc<dyn ChainObserver>,
42+
immutable_observer: Arc<dyn ImmutableFileObserver>,
43+
network: CardanoNetwork,
44+
}
45+
46+
impl MithrilTickerService {
47+
/// Instantiate a new service
48+
pub fn new(
49+
chain_observer: Arc<dyn ChainObserver>,
50+
immutable_observer: Arc<dyn ImmutableFileObserver>,
51+
network: CardanoNetwork,
52+
) -> Self {
53+
Self {
54+
chain_observer,
55+
immutable_observer,
56+
network,
57+
}
58+
}
59+
}
60+
61+
#[async_trait]
62+
impl TickerService for MithrilTickerService {
63+
async fn get_current_epoch(&self) -> StdResult<Epoch> {
64+
let epoch = self
65+
.chain_observer
66+
.get_current_epoch()
67+
.await?
68+
.ok_or(MithrilTickerError::NoEpoch)?;
69+
70+
Ok(epoch)
71+
}
72+
73+
async fn get_current_immutable_beacon(&self) -> StdResult<Beacon> {
74+
let epoch = self.get_current_epoch().await?;
75+
let immutable_file_number = self.immutable_observer.get_last_immutable_number().await?;
76+
77+
Ok(Beacon::new(
78+
self.network.to_string(),
79+
epoch.0,
80+
immutable_file_number,
81+
))
82+
}
83+
}
84+
85+
#[cfg(test)]
86+
mod tests {
87+
use mithril_common::{chain_observer::MockChainObserver, digesters::DumbImmutableFileObserver};
88+
89+
use super::*;
90+
91+
async fn get_ticker() -> MithrilTickerService {
92+
let mut chain_observer = MockChainObserver::new();
93+
chain_observer
94+
.expect_get_current_epoch()
95+
.returning(|| Ok(Some(Epoch(10))))
96+
.times(1);
97+
let immutable_observer = DumbImmutableFileObserver::new();
98+
immutable_observer.shall_return(Some(99)).await;
99+
let network = CardanoNetwork::DevNet(42);
100+
101+
MithrilTickerService::new(
102+
Arc::new(chain_observer),
103+
Arc::new(immutable_observer),
104+
network,
105+
)
106+
}
107+
108+
#[tokio::test]
109+
async fn get_epoch() {
110+
let ticker_service = get_ticker().await;
111+
let epoch = ticker_service.get_current_epoch().await.unwrap();
112+
113+
assert_eq!(Epoch(10), epoch);
114+
}
115+
116+
#[tokio::test]
117+
async fn get_immutable_beacon() {
118+
let ticker_service = get_ticker().await;
119+
let beacon = ticker_service.get_current_immutable_beacon().await.unwrap();
120+
121+
assert_eq!(
122+
Beacon {
123+
epoch: Epoch(10),
124+
immutable_file_number: 99,
125+
network: "devnet".to_string()
126+
},
127+
beacon
128+
)
129+
}
130+
131+
#[tokio::test]
132+
async fn no_beacon_error() {
133+
let mut chain_observer = MockChainObserver::new();
134+
chain_observer
135+
.expect_get_current_epoch()
136+
.returning(|| Ok(None))
137+
.times(1);
138+
let immutable_observer = DumbImmutableFileObserver::new();
139+
immutable_observer.shall_return(Some(99)).await;
140+
let network = CardanoNetwork::DevNet(42);
141+
142+
let ticker_service = MithrilTickerService::new(
143+
Arc::new(chain_observer),
144+
Arc::new(immutable_observer),
145+
network,
146+
);
147+
let error = ticker_service.get_current_epoch().await.unwrap_err();
148+
let error = error
149+
.downcast_ref::<MithrilTickerError>()
150+
.expect("Expected error was a `MithrilTickerError`.");
151+
152+
assert!(matches!(error, MithrilTickerError::NoEpoch));
153+
}
154+
}

0 commit comments

Comments
 (0)