mithril_signed_entity_preloader/
cardano_transactions_preloader.rs1use std::sync::Arc;
7
8use anyhow::Context;
9use async_trait::async_trait;
10use mithril_signed_entity_lock::SignedEntityTypeLock;
11use slog::{debug, info, Logger};
12
13use mithril_cardano_node_chain::chain_observer::ChainObserver;
14use mithril_common::{
15 entities::{BlockNumber, SignedEntityTypeDiscriminants},
16 logging::LoggerExtensions,
17 signable_builder::TransactionsImporter,
18 StdResult,
19};
20
21#[cfg(test)]
22use mockall::automock;
23
24#[cfg_attr(test, automock)]
27#[async_trait]
28pub trait CardanoTransactionsPreloaderChecker: Send + Sync {
29 async fn is_activated(&self) -> StdResult<bool>;
31}
32
33pub struct CardanoTransactionsPreloaderActivation {
35 activation: bool,
36}
37
38impl CardanoTransactionsPreloaderActivation {
39 pub fn new(activation: bool) -> Self {
41 Self { activation }
42 }
43}
44
45#[async_trait]
46impl CardanoTransactionsPreloaderChecker for CardanoTransactionsPreloaderActivation {
47 async fn is_activated(&self) -> StdResult<bool> {
48 Ok(self.activation)
49 }
50}
51
52pub struct CardanoTransactionsPreloader {
55 signed_entity_type_lock: Arc<SignedEntityTypeLock>,
56 importer: Arc<dyn TransactionsImporter>,
57 security_parameter: BlockNumber,
58 chain_observer: Arc<dyn ChainObserver>,
59 logger: Logger,
60 activation_state: Arc<dyn CardanoTransactionsPreloaderChecker>,
61}
62
63impl CardanoTransactionsPreloader {
64 pub fn new(
66 signed_entity_type_lock: Arc<SignedEntityTypeLock>,
67 importer: Arc<dyn TransactionsImporter>,
68 security_parameter: BlockNumber,
69 chain_observer: Arc<dyn ChainObserver>,
70 logger: Logger,
71 activation_state: Arc<dyn CardanoTransactionsPreloaderChecker>,
72 ) -> Self {
73 Self {
74 signed_entity_type_lock,
75 importer,
76 security_parameter,
77 chain_observer,
78 logger: logger.new_with_component_name::<Self>(),
79 activation_state,
80 }
81 }
82
83 pub async fn preload(&self) -> StdResult<()> {
85 if !self.is_activated().await? {
86 debug!(self.logger, "Not running, conditions not met");
87 return Ok(());
88 }
89
90 info!(self.logger, "Started");
91 debug!(self.logger, "Locking signed entity type"; "entity_type" => "CardanoTransactions");
92 self.signed_entity_type_lock
93 .lock(SignedEntityTypeDiscriminants::CardanoTransactions)
94 .await;
95
96 let preload_result = self.do_preload().await;
97
98 debug!(self.logger, "Releasing signed entity type"; "entity_type" => "CardanoTransactions");
99 self.signed_entity_type_lock
100 .release(SignedEntityTypeDiscriminants::CardanoTransactions)
101 .await;
102 info!(self.logger, "Finished");
103
104 preload_result
105 }
106
107 pub async fn is_activated(&self) -> StdResult<bool> {
109 self.activation_state.is_activated().await
110 }
111
112 async fn do_preload(&self) -> StdResult<()> {
113 let chain_point = self
114 .chain_observer
115 .get_current_chain_point()
116 .await?
117 .with_context(|| {
118 "No chain point yielded by the chain observer, is your cardano node ready?"
119 })?;
120 let up_to_block_number = chain_point.block_number - self.security_parameter;
121 self.importer.import(up_to_block_number).await?;
122
123 Ok(())
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use async_trait::async_trait;
130 use mockall::mock;
131 use mockall::predicate::eq;
132
133 use mithril_cardano_node_chain::test::double::FakeChainObserver;
134 use mithril_common::entities::{BlockNumber, ChainPoint, TimePoint};
135
136 use crate::test_tools::TestLogger;
137
138 use super::*;
139
140 mock! {
141 pub TransactionsImporterImpl { }
142
143 #[async_trait]
144 impl TransactionsImporter for TransactionsImporterImpl {
145 async fn import(&self, up_to_beacon: BlockNumber) -> StdResult<()>;
146 }
147 }
148
149 struct ImporterWithSignedEntityTypeLockCheck {
150 signed_entity_type_lock: Arc<SignedEntityTypeLock>,
151 }
152 #[async_trait]
153 impl TransactionsImporter for ImporterWithSignedEntityTypeLockCheck {
154 async fn import(&self, _up_to_beacon: BlockNumber) -> StdResult<()> {
155 assert!(
156 self.signed_entity_type_lock
157 .is_locked(SignedEntityTypeDiscriminants::CardanoTransactions)
158 .await
159 );
160 Ok(())
161 }
162 }
163
164 fn build_chain_observer() -> (FakeChainObserver, BlockNumber, BlockNumber) {
165 let chain_block_number = BlockNumber(5000);
166 let security_parameter = BlockNumber(542);
167 let chain_observer = FakeChainObserver::new(Some(TimePoint {
168 chain_point: ChainPoint {
169 block_number: chain_block_number,
170 ..ChainPoint::dummy()
171 },
172 ..TimePoint::dummy()
173 }));
174 (chain_observer, chain_block_number, security_parameter)
175 }
176
177 #[tokio::test]
178 async fn call_its_inner_importer_when_is_activated() {
179 let (chain_observer, chain_block_number, security_parameter) = build_chain_observer();
180 let expected_parsed_block_number = chain_block_number - security_parameter;
181
182 let mut importer = MockTransactionsImporterImpl::new();
183 importer
184 .expect_import()
185 .times(1)
186 .with(eq(expected_parsed_block_number))
187 .returning(|_| Ok(()));
188
189 let preloader = CardanoTransactionsPreloader::new(
190 Arc::new(SignedEntityTypeLock::default()),
191 Arc::new(importer),
192 security_parameter,
193 Arc::new(chain_observer),
194 TestLogger::stdout(),
195 Arc::new(CardanoTransactionsPreloaderActivation::new(true)),
196 );
197
198 preloader.preload().await.unwrap();
199 }
200
201 #[tokio::test]
202 async fn do_not_call_its_inner_importer_when_is_not_activated() {
203 let (chain_observer, _, _) = build_chain_observer();
204 let mut importer = MockTransactionsImporterImpl::new();
205 importer.expect_import().never();
206
207 let preloader = CardanoTransactionsPreloader::new(
208 Arc::new(SignedEntityTypeLock::default()),
209 Arc::new(importer),
210 BlockNumber(542),
211 Arc::new(chain_observer),
212 TestLogger::stdout(),
213 Arc::new(CardanoTransactionsPreloaderActivation::new(false)),
214 );
215
216 preloader.preload().await.unwrap();
217 }
218
219 #[tokio::test]
220 async fn return_error_when_is_activated_return_error() {
221 let (chain_observer, _, _) = build_chain_observer();
222 let mut importer = MockTransactionsImporterImpl::new();
223 importer.expect_import().never();
224
225 let mut preloader_checker = MockCardanoTransactionsPreloaderChecker::new();
226 preloader_checker
227 .expect_is_activated()
228 .returning(|| Err(anyhow::anyhow!("error")));
229
230 let preloader = CardanoTransactionsPreloader::new(
231 Arc::new(SignedEntityTypeLock::default()),
232 Arc::new(importer),
233 BlockNumber(542),
234 Arc::new(chain_observer),
235 TestLogger::stdout(),
236 Arc::new(preloader_checker),
237 );
238
239 preloader
240 .preload()
241 .await
242 .expect_err("should raise an error with error from the activation");
243 }
244
245 #[tokio::test]
246 async fn fail_if_chain_point_is_not_available() {
247 let chain_observer = FakeChainObserver::new(None);
248 let mut importer = MockTransactionsImporterImpl::new();
249 importer.expect_import().never();
250
251 let preloader = CardanoTransactionsPreloader::new(
252 Arc::new(SignedEntityTypeLock::default()),
253 Arc::new(importer),
254 BlockNumber(0),
255 Arc::new(chain_observer),
256 TestLogger::stdout(),
257 Arc::new(CardanoTransactionsPreloaderActivation::new(true)),
258 );
259
260 preloader
261 .preload()
262 .await
263 .expect_err("should raise an error when chain point is not available");
264 }
265
266 #[tokio::test]
267 async fn should_lock_entity_type_while_preloading() {
268 let signed_entity_type_lock = Arc::new(SignedEntityTypeLock::default());
269
270 let preloader = CardanoTransactionsPreloader::new(
271 signed_entity_type_lock.clone(),
272 Arc::new(ImporterWithSignedEntityTypeLockCheck {
273 signed_entity_type_lock: signed_entity_type_lock.clone(),
274 }),
275 BlockNumber(0),
276 Arc::new(FakeChainObserver::new(Some(TimePoint::dummy()))),
277 TestLogger::stdout(),
278 Arc::new(CardanoTransactionsPreloaderActivation::new(true)),
279 );
280
281 assert!(
282 !signed_entity_type_lock
283 .is_locked(SignedEntityTypeDiscriminants::CardanoTransactions)
284 .await
285 );
286
287 preloader.preload().await.unwrap();
288
289 assert!(
290 !signed_entity_type_lock
291 .is_locked(SignedEntityTypeDiscriminants::CardanoTransactions)
292 .await
293 );
294 }
295
296 #[tokio::test]
297 async fn should_release_locked_entity_type_when_preloading_fail() {
298 let signed_entity_type_lock = Arc::new(SignedEntityTypeLock::default());
299 let chain_observer = FakeChainObserver::new(None);
300
301 let preloader = CardanoTransactionsPreloader::new(
302 signed_entity_type_lock.clone(),
303 Arc::new(ImporterWithSignedEntityTypeLockCheck {
304 signed_entity_type_lock: signed_entity_type_lock.clone(),
305 }),
306 BlockNumber(0),
307 Arc::new(chain_observer),
308 TestLogger::stdout(),
309 Arc::new(CardanoTransactionsPreloaderActivation::new(true)),
310 );
311
312 preloader.preload().await.unwrap_err();
313
314 assert!(
315 !signed_entity_type_lock
316 .is_locked(SignedEntityTypeDiscriminants::CardanoTransactions)
317 .await
318 );
319 }
320}