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::{Logger, debug, info};
12
13use mithril_cardano_node_chain::chain_observer::ChainObserver;
14use mithril_common::{
15 StdResult,
16 entities::{BlockNumber, SignedEntityTypeDiscriminants},
17 logging::LoggerExtensions,
18 signable_builder::TransactionsImporter,
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.chain_observer.get_current_chain_point().await?.with_context(
114 || "No chain point yielded by the chain observer, is your cardano node ready?",
115 )?;
116 let up_to_block_number = chain_point.block_number - self.security_parameter;
117 self.importer.import(up_to_block_number).await?;
118
119 Ok(())
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use async_trait::async_trait;
126 use mockall::mock;
127 use mockall::predicate::eq;
128
129 use mithril_cardano_node_chain::test::double::FakeChainObserver;
130 use mithril_common::entities::{BlockNumber, ChainPoint, TimePoint};
131 use mithril_common::test::double::Dummy;
132
133 use crate::test_tools::TestLogger;
134
135 use super::*;
136
137 mock! {
138 pub TransactionsImporterImpl { }
139
140 #[async_trait]
141 impl TransactionsImporter for TransactionsImporterImpl {
142 async fn import(&self, up_to_beacon: BlockNumber) -> StdResult<()>;
143 }
144 }
145
146 struct ImporterWithSignedEntityTypeLockCheck {
147 signed_entity_type_lock: Arc<SignedEntityTypeLock>,
148 }
149 #[async_trait]
150 impl TransactionsImporter for ImporterWithSignedEntityTypeLockCheck {
151 async fn import(&self, _up_to_beacon: BlockNumber) -> StdResult<()> {
152 assert!(
153 self.signed_entity_type_lock
154 .is_locked(SignedEntityTypeDiscriminants::CardanoTransactions)
155 .await
156 );
157 Ok(())
158 }
159 }
160
161 fn build_chain_observer() -> (FakeChainObserver, BlockNumber, BlockNumber) {
162 let chain_block_number = BlockNumber(5000);
163 let security_parameter = BlockNumber(542);
164 let chain_observer = FakeChainObserver::new(Some(TimePoint {
165 chain_point: ChainPoint {
166 block_number: chain_block_number,
167 ..ChainPoint::dummy()
168 },
169 ..TimePoint::dummy()
170 }));
171 (chain_observer, chain_block_number, security_parameter)
172 }
173
174 #[tokio::test]
175 async fn call_its_inner_importer_when_is_activated() {
176 let (chain_observer, chain_block_number, security_parameter) = build_chain_observer();
177 let expected_parsed_block_number = chain_block_number - security_parameter;
178
179 let mut importer = MockTransactionsImporterImpl::new();
180 importer
181 .expect_import()
182 .times(1)
183 .with(eq(expected_parsed_block_number))
184 .returning(|_| Ok(()));
185
186 let preloader = CardanoTransactionsPreloader::new(
187 Arc::new(SignedEntityTypeLock::default()),
188 Arc::new(importer),
189 security_parameter,
190 Arc::new(chain_observer),
191 TestLogger::stdout(),
192 Arc::new(CardanoTransactionsPreloaderActivation::new(true)),
193 );
194
195 preloader.preload().await.unwrap();
196 }
197
198 #[tokio::test]
199 async fn do_not_call_its_inner_importer_when_is_not_activated() {
200 let (chain_observer, _, _) = build_chain_observer();
201 let mut importer = MockTransactionsImporterImpl::new();
202 importer.expect_import().never();
203
204 let preloader = CardanoTransactionsPreloader::new(
205 Arc::new(SignedEntityTypeLock::default()),
206 Arc::new(importer),
207 BlockNumber(542),
208 Arc::new(chain_observer),
209 TestLogger::stdout(),
210 Arc::new(CardanoTransactionsPreloaderActivation::new(false)),
211 );
212
213 preloader.preload().await.unwrap();
214 }
215
216 #[tokio::test]
217 async fn return_error_when_is_activated_return_error() {
218 let (chain_observer, _, _) = build_chain_observer();
219 let mut importer = MockTransactionsImporterImpl::new();
220 importer.expect_import().never();
221
222 let mut preloader_checker = MockCardanoTransactionsPreloaderChecker::new();
223 preloader_checker
224 .expect_is_activated()
225 .returning(|| Err(anyhow::anyhow!("error")));
226
227 let preloader = CardanoTransactionsPreloader::new(
228 Arc::new(SignedEntityTypeLock::default()),
229 Arc::new(importer),
230 BlockNumber(542),
231 Arc::new(chain_observer),
232 TestLogger::stdout(),
233 Arc::new(preloader_checker),
234 );
235
236 preloader
237 .preload()
238 .await
239 .expect_err("should raise an error with error from the activation");
240 }
241
242 #[tokio::test]
243 async fn fail_if_chain_point_is_not_available() {
244 let chain_observer = FakeChainObserver::new(None);
245 let mut importer = MockTransactionsImporterImpl::new();
246 importer.expect_import().never();
247
248 let preloader = CardanoTransactionsPreloader::new(
249 Arc::new(SignedEntityTypeLock::default()),
250 Arc::new(importer),
251 BlockNumber(0),
252 Arc::new(chain_observer),
253 TestLogger::stdout(),
254 Arc::new(CardanoTransactionsPreloaderActivation::new(true)),
255 );
256
257 preloader
258 .preload()
259 .await
260 .expect_err("should raise an error when chain point is not available");
261 }
262
263 #[tokio::test]
264 async fn should_lock_entity_type_while_preloading() {
265 let signed_entity_type_lock = Arc::new(SignedEntityTypeLock::default());
266
267 let preloader = CardanoTransactionsPreloader::new(
268 signed_entity_type_lock.clone(),
269 Arc::new(ImporterWithSignedEntityTypeLockCheck {
270 signed_entity_type_lock: signed_entity_type_lock.clone(),
271 }),
272 BlockNumber(0),
273 Arc::new(FakeChainObserver::new(Some(TimePoint::dummy()))),
274 TestLogger::stdout(),
275 Arc::new(CardanoTransactionsPreloaderActivation::new(true)),
276 );
277
278 assert!(
279 !signed_entity_type_lock
280 .is_locked(SignedEntityTypeDiscriminants::CardanoTransactions)
281 .await
282 );
283
284 preloader.preload().await.unwrap();
285
286 assert!(
287 !signed_entity_type_lock
288 .is_locked(SignedEntityTypeDiscriminants::CardanoTransactions)
289 .await
290 );
291 }
292
293 #[tokio::test]
294 async fn should_release_locked_entity_type_when_preloading_fail() {
295 let signed_entity_type_lock = Arc::new(SignedEntityTypeLock::default());
296 let chain_observer = FakeChainObserver::new(None);
297
298 let preloader = CardanoTransactionsPreloader::new(
299 signed_entity_type_lock.clone(),
300 Arc::new(ImporterWithSignedEntityTypeLockCheck {
301 signed_entity_type_lock: signed_entity_type_lock.clone(),
302 }),
303 BlockNumber(0),
304 Arc::new(chain_observer),
305 TestLogger::stdout(),
306 Arc::new(CardanoTransactionsPreloaderActivation::new(true)),
307 );
308
309 preloader.preload().await.unwrap_err();
310
311 assert!(
312 !signed_entity_type_lock
313 .is_locked(SignedEntityTypeDiscriminants::CardanoTransactions)
314 .await
315 );
316 }
317}