1use std::{any::Any, str::FromStr, sync::Arc};
4
5use rustc_hash::FxHashMap;
6use tokio::sync::RwLock;
7use tycho_simulation::tycho_common::models::Address;
8
9use super::{
10 computation::{ComputationId, DerivedComputation, FailedItem, FailedItemError},
11 computations::{ComponentDepthComputation, SpotPriceComputation, TokenGasPriceComputation},
12 types::{
13 ComponentDepthKey, ComponentDepths, SpotPriceKey, SpotPrices, TokenGasPriceKey,
14 TokenGasPrices, TokenPricesWithDeps,
15 },
16};
17use crate::derived::SharedDerivedDataRef;
18
19#[derive(Debug)]
21struct ComputedValue<T> {
22 data: T,
23 block: u64,
24}
25
26struct ComputedSlot {
28 data: Box<dyn Any + Send + Sync>,
29 block: u64,
30}
31
32impl std::fmt::Debug for ComputedSlot {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 f.debug_struct("ComputedSlot")
35 .field("block", &self.block)
36 .finish_non_exhaustive()
37 }
38}
39
40#[derive(Debug, Default)]
46pub struct DerivedData {
47 slots: FxHashMap<ComputationId, ComputedSlot>,
49 token_prices_failed: FxHashMap<TokenGasPriceKey, (u64, FailedItemError)>,
51 token_prices_deps: Option<ComputedValue<TokenPricesWithDeps>>,
53 component_depths_failed: FxHashMap<ComponentDepthKey, (u64, FailedItemError)>,
55 spot_prices_failed: FxHashMap<SpotPriceKey, (u64, FailedItemError)>,
57}
58
59fn parse_pair_key(s: &str) -> Option<(String, Address, Address)> {
61 let mut parts = s.rsplitn(3, '/');
62 let token_out_str = parts.next()?;
63 let token_in_str = parts.next()?;
64 let component_id = parts.next()?;
65 let token_in = Address::from_str(token_in_str).ok()?;
66 let token_out = Address::from_str(token_out_str).ok()?;
67 Some((component_id.to_string(), token_in, token_out))
68}
69
70impl DerivedData {
71 pub fn new() -> Self {
73 Self::default()
74 }
75
76 pub fn new_shared() -> SharedDerivedDataRef {
79 Arc::new(RwLock::new(Self::new()))
80 }
81
82 pub(crate) fn set_output<T: Any + Send + Sync>(
84 &mut self,
85 id: ComputationId,
86 data: T,
87 block: u64,
88 ) {
89 self.slots
90 .insert(id, ComputedSlot { data: Box::new(data), block });
91 }
92
93 pub(crate) fn output<T: Any>(&self, id: ComputationId) -> Option<&T> {
98 let slot = self.slots.get(id)?;
99 debug_assert!(slot.data.is::<T>(), "derived output {id} read as the wrong type");
100 slot.data.downcast_ref::<T>()
101 }
102
103 pub(crate) fn output_block(&self, id: ComputationId) -> Option<u64> {
105 self.slots
106 .get(id)
107 .map(|slot| slot.block)
108 }
109
110 fn clear_output(&mut self, id: ComputationId) {
112 self.slots.remove(id);
113 }
114
115 pub fn derived_data_ready(&self) -> bool {
117 self.token_prices_block().is_some() &&
118 self.token_prices_deps_block().is_some() &&
119 self.component_depths_block().is_some() &&
120 self.spot_prices_block().is_some()
121 }
122
123 pub fn token_prices(&self) -> Option<&TokenGasPrices> {
129 self.token_prices_slot()
130 .map(Arc::as_ref)
131 }
132
133 pub fn token_prices_shared(&self) -> Option<Arc<TokenGasPrices>> {
139 self.token_prices_slot().cloned()
140 }
141
142 fn token_prices_slot(&self) -> Option<&Arc<TokenGasPrices>> {
143 self.output(TokenGasPriceComputation::ID)
144 }
145
146 pub fn token_prices_block(&self) -> Option<u64> {
148 self.output_block(TokenGasPriceComputation::ID)
149 }
150
151 pub fn set_token_prices(
157 &mut self,
158 prices: TokenGasPrices,
159 failed_items: Vec<FailedItem>,
160 block: u64,
161 is_full_recompute: bool,
162 ) {
163 let new_failures: FxHashMap<TokenGasPriceKey, (u64, FailedItemError)> = failed_items
164 .into_iter()
165 .filter_map(|f| {
166 Address::from_str(&f.key)
167 .ok()
168 .map(|k| (k, (block, f.error)))
169 })
170 .collect();
171
172 if is_full_recompute {
173 self.token_prices_failed = new_failures;
174 } else {
175 self.token_prices_failed
176 .retain(|k, _| !prices.contains_key(k));
177 self.token_prices_failed
178 .extend(new_failures);
179 }
180
181 self.set_output(TokenGasPriceComputation::ID, Arc::new(prices), block);
182 }
183
184 pub fn token_price_failure(&self, key: &TokenGasPriceKey) -> Option<(u64, &FailedItemError)> {
187 self.token_prices_failed
188 .get(key)
189 .map(|(block, error)| (*block, error))
190 }
191
192 pub fn clear_token_prices(&mut self) {
194 self.clear_output(TokenGasPriceComputation::ID);
195 self.token_prices_failed.clear();
196 }
197
198 pub fn token_prices_deps(&self) -> Option<&TokenPricesWithDeps> {
204 self.token_prices_deps
205 .as_ref()
206 .map(|v| &v.data)
207 }
208
209 pub fn token_prices_deps_block(&self) -> Option<u64> {
211 self.token_prices_deps
212 .as_ref()
213 .map(|v| v.block)
214 }
215
216 pub fn set_token_prices_deps(&mut self, prices: TokenPricesWithDeps, block: u64) {
218 self.token_prices_deps = Some(ComputedValue { data: prices, block });
219 }
220
221 pub fn clear_token_prices_deps(&mut self) {
223 self.token_prices_deps = None;
224 }
225
226 pub fn component_depths(&self) -> Option<&ComponentDepths> {
232 self.output(ComponentDepthComputation::ID)
233 }
234
235 pub fn component_depths_block(&self) -> Option<u64> {
237 self.output_block(ComponentDepthComputation::ID)
238 }
239
240 pub fn set_component_depths(
246 &mut self,
247 depths: ComponentDepths,
248 failed_items: Vec<FailedItem>,
249 block: u64,
250 is_full_recompute: bool,
251 ) {
252 let new_failures: FxHashMap<ComponentDepthKey, (u64, FailedItemError)> = failed_items
253 .into_iter()
254 .filter_map(|f| parse_pair_key(&f.key).map(|k| (k, (block, f.error))))
255 .collect();
256
257 if is_full_recompute {
258 self.component_depths_failed = new_failures;
259 } else {
260 self.component_depths_failed
261 .retain(|k, _| !depths.contains_key(k));
262 self.component_depths_failed
263 .extend(new_failures);
264 }
265
266 self.set_output(ComponentDepthComputation::ID, depths, block);
267 }
268
269 pub fn component_depth_failure(
274 &self,
275 key: &ComponentDepthKey,
276 ) -> Option<(u64, &FailedItemError)> {
277 self.component_depths_failed
278 .get(key)
279 .map(|(block, error)| (*block, error))
280 }
281
282 pub fn clear_component_depths(&mut self) {
284 self.clear_output(ComponentDepthComputation::ID);
285 self.component_depths_failed.clear();
286 }
287
288 pub fn spot_prices(&self) -> Option<&SpotPrices> {
294 self.output(SpotPriceComputation::ID)
295 }
296
297 pub fn spot_prices_block(&self) -> Option<u64> {
299 self.output_block(SpotPriceComputation::ID)
300 }
301
302 pub fn set_spot_prices(
308 &mut self,
309 prices: SpotPrices,
310 failed_items: Vec<FailedItem>,
311 block: u64,
312 is_full_recompute: bool,
313 ) {
314 let new_failures: FxHashMap<SpotPriceKey, (u64, FailedItemError)> = failed_items
315 .into_iter()
316 .filter_map(|f| parse_pair_key(&f.key).map(|k| (k, (block, f.error))))
317 .collect();
318
319 if is_full_recompute {
320 self.spot_prices_failed = new_failures;
321 } else {
322 self.spot_prices_failed
323 .retain(|k, _| !prices.contains_key(k));
324 self.spot_prices_failed
325 .extend(new_failures);
326 }
327
328 self.set_output(SpotPriceComputation::ID, prices, block);
329 }
330
331 pub fn spot_price_failure(&self, key: &SpotPriceKey) -> Option<(u64, &FailedItemError)> {
336 self.spot_prices_failed
337 .get(key)
338 .map(|(block, error)| (*block, error))
339 }
340
341 pub fn clear_spot_prices(&mut self) {
343 self.clear_output(SpotPriceComputation::ID);
344 self.spot_prices_failed.clear();
345 }
346
347 pub fn clear_all(&mut self) {
353 self.slots.clear();
354 self.token_prices_failed.clear();
355 self.token_prices_deps = None;
356 self.component_depths_failed.clear();
357 self.spot_prices_failed.clear();
358 }
359}
360
361#[cfg(test)]
362mod tests {
363 use super::*;
364 use crate::{algorithm::test_utils::addr, derived::types::SpotPrices};
365
366 fn failed(key: &str, error: FailedItemError) -> FailedItem {
367 FailedItem { key: key.to_string(), error }
368 }
369
370 fn pair_key(comp: &str, b_in: u8, b_out: u8) -> SpotPriceKey {
371 (comp.to_string(), addr(b_in), addr(b_out))
372 }
373
374 #[test]
375 fn test_token_prices_block_tracks_independently() {
376 let mut store = DerivedData::new();
377 assert_eq!(store.token_prices_block(), None);
378
379 store.set_token_prices(Default::default(), vec![], 42, true);
380 assert_eq!(store.token_prices_block(), Some(42));
381
382 assert_eq!(store.spot_prices_block(), None);
384 assert_eq!(store.component_depths_block(), None);
385 }
386
387 #[test]
388 fn test_spot_prices_block_tracks_independently() {
389 let mut store = DerivedData::new();
390 store.set_spot_prices(Default::default(), vec![], 10, true);
391 assert_eq!(store.spot_prices_block(), Some(10));
392 assert_eq!(store.token_prices_block(), None);
393 }
394
395 #[test]
396 fn test_component_depths_block_tracks_independently() {
397 let mut store = DerivedData::new();
398 store.set_component_depths(Default::default(), vec![], 7, true);
399 assert_eq!(store.component_depths_block(), Some(7));
400 assert_eq!(store.token_prices_block(), None);
401 }
402
403 #[test]
404 fn test_derived_data_ready() {
405 let mut store = DerivedData::new();
406 assert!(!store.derived_data_ready());
407
408 store.set_spot_prices(Default::default(), vec![], 5, true);
409 assert!(!store.derived_data_ready());
410
411 store.set_token_prices(Default::default(), vec![], 10, true);
412 assert!(!store.derived_data_ready());
413
414 store.set_token_prices_deps(Default::default(), 10);
415 assert!(!store.derived_data_ready());
416
417 store.set_component_depths(Default::default(), vec![], 9, true);
418 assert!(store.derived_data_ready());
419 }
420
421 #[test]
422 fn test_clear_all_resets_all_fields() {
423 let mut store = DerivedData::new();
424 store.set_token_prices(Default::default(), vec![], 1, true);
425 store.set_spot_prices(Default::default(), vec![], 1, true);
426 store.set_component_depths(Default::default(), vec![], 1, true);
427
428 store.clear_all();
429
430 assert!(store.token_prices().is_none());
431 assert!(store.spot_prices().is_none());
432 assert!(store.component_depths().is_none());
433 assert!(!store.derived_data_ready());
434 }
435
436 #[test]
437 fn test_token_price_failure_stored_with_block() {
438 let token_addr = addr(0xab);
439 let key_str = format!("{token_addr}");
440 let mut store = DerivedData::new();
441 store.set_token_prices(
442 Default::default(),
443 vec![failed(&key_str, FailedItemError::SimulationFailed("sim error".into()))],
444 42,
445 true,
446 );
447 assert_eq!(
448 store.token_price_failure(&token_addr),
449 Some((42, &FailedItemError::SimulationFailed("sim error".into())))
450 );
451 assert_eq!(store.token_price_failure(&addr(0xcd)), None);
452 }
453
454 #[test]
455 fn test_spot_price_failure_stored_with_block() {
456 let key = pair_key("component1", 0x01, 0x02);
457 let key_str = format!("component1/{}/{}", addr(0x01), addr(0x02));
458 let mut store = DerivedData::new();
459 store.set_spot_prices(
460 Default::default(),
461 vec![failed(&key_str, FailedItemError::SimulationFailed("sim error".into()))],
462 10,
463 true,
464 );
465 assert_eq!(
466 store.spot_price_failure(&key),
467 Some((10, &FailedItemError::SimulationFailed("sim error".into())))
468 );
469 assert_eq!(store.spot_price_failure(&pair_key("component1", 0x01, 0x03)), None);
470 }
471
472 #[test]
473 fn test_component_depth_failure_stored_with_block() {
474 let key: ComponentDepthKey = pair_key("component1", 0x01, 0x02);
475 let key_str = format!("component1/{}/{}", addr(0x01), addr(0x02));
476 let mut store = DerivedData::new();
477 store.set_component_depths(
478 Default::default(),
479 vec![failed(&key_str, FailedItemError::SimulationFailed("depth error".into()))],
480 7,
481 true,
482 );
483 assert_eq!(
484 store.component_depth_failure(&key),
485 Some((7, &FailedItemError::SimulationFailed("depth error".into())))
486 );
487 assert_eq!(store.component_depth_failure(&pair_key("component2", 0x01, 0x02)), None);
488 }
489
490 #[test]
491 fn test_rerunning_with_empty_failures_clears_old_reasons() {
492 let key = pair_key("component1", 0x01, 0x02);
493 let key_str = format!("component1/{}/{}", addr(0x01), addr(0x02));
494 let mut store = DerivedData::new();
495 store.set_spot_prices(
496 Default::default(),
497 vec![failed(&key_str, FailedItemError::MissingSimulationState)],
498 1,
499 true,
500 );
501 assert!(store.spot_price_failure(&key).is_some());
502
503 store.set_spot_prices(Default::default(), vec![], 2, true);
505 assert_eq!(store.spot_price_failure(&key), None);
506 }
507
508 #[test]
509 fn test_clear_token_prices_clears_failure_map() {
510 let token_addr = addr(0xab);
511 let key_str = format!("{token_addr}");
512 let mut store = DerivedData::new();
513 store.set_token_prices(
514 Default::default(),
515 vec![failed(&key_str, FailedItemError::AllSimulationPathsFailed)],
516 1,
517 true,
518 );
519 store.clear_token_prices();
520 assert_eq!(store.token_price_failure(&token_addr), None);
521 }
522
523 #[test]
524 fn test_clear_spot_prices_clears_failure_map() {
525 let key = pair_key("component1", 0x01, 0x02);
526 let key_str = format!("component1/{}/{}", addr(0x01), addr(0x02));
527 let mut store = DerivedData::new();
528 store.set_spot_prices(
529 Default::default(),
530 vec![failed(&key_str, FailedItemError::MissingSimulationState)],
531 1,
532 true,
533 );
534 store.clear_spot_prices();
535 assert_eq!(store.spot_price_failure(&key), None);
536 }
537
538 #[test]
539 fn test_clear_component_depths_clears_failure_map() {
540 let key: ComponentDepthKey = pair_key("component1", 0x01, 0x02);
541 let key_str = format!("component1/{}/{}", addr(0x01), addr(0x02));
542 let mut store = DerivedData::new();
543 store.set_component_depths(
544 Default::default(),
545 vec![failed(&key_str, FailedItemError::MissingSpotPrice)],
546 1,
547 true,
548 );
549 store.clear_component_depths();
550 assert_eq!(store.component_depth_failure(&key), None);
551 }
552
553 #[test]
554 fn test_incremental_run_preserves_failures_for_unattempted_items() {
555 let key_a = pair_key("component_a", 0x01, 0x02);
556 let key_a_str = format!("component_a/{}/{}", addr(0x01), addr(0x02));
557 let key_b = pair_key("component_b", 0x03, 0x04);
558 let key_b_str = format!("component_b/{}/{}", addr(0x03), addr(0x04));
559
560 let mut store = DerivedData::new();
561
562 store.set_spot_prices(
564 Default::default(),
565 vec![
566 failed(&key_a_str, FailedItemError::MissingSimulationState),
567 failed(&key_b_str, FailedItemError::MissingTokenMetadata),
568 ],
569 10,
570 true,
571 );
572 assert_eq!(
573 store.spot_price_failure(&key_a),
574 Some((10, &FailedItemError::MissingSimulationState))
575 );
576 assert_eq!(
577 store.spot_price_failure(&key_b),
578 Some((10, &FailedItemError::MissingTokenMetadata))
579 );
580
581 let mut prices = SpotPrices::default();
583 prices.insert(key_b.clone(), 1.0);
584 store.set_spot_prices(prices, vec![], 11, false);
585
586 assert_eq!(
588 store.spot_price_failure(&key_a),
589 Some((10, &FailedItemError::MissingSimulationState))
590 );
591 assert_eq!(store.spot_price_failure(&key_b), None);
593 }
594
595 #[test]
596 fn test_incremental_run_updates_block_on_repeated_failure() {
597 let key = pair_key("component_a", 0x01, 0x02);
598 let key_str = format!("component_a/{}/{}", addr(0x01), addr(0x02));
599
600 let mut store = DerivedData::new();
601
602 store.set_spot_prices(
603 Default::default(),
604 vec![failed(&key_str, FailedItemError::MissingSimulationState)],
605 10,
606 true,
607 );
608 assert_eq!(
609 store.spot_price_failure(&key),
610 Some((10, &FailedItemError::MissingSimulationState))
611 );
612
613 store.set_spot_prices(
615 Default::default(),
616 vec![failed(&key_str, FailedItemError::MissingTokenMetadata)],
617 11,
618 false,
619 );
620 assert_eq!(
621 store.spot_price_failure(&key),
622 Some((11, &FailedItemError::MissingTokenMetadata))
623 );
624 }
625
626 #[test]
627 fn test_clear_all_clears_all_failure_maps() {
628 let token_addr = addr(0xab);
629 let token_str = format!("{token_addr}");
630 let pair = pair_key("component1", 0x01, 0x02);
631 let pair_str = format!("component1/{}/{}", addr(0x01), addr(0x02));
632
633 let mut store = DerivedData::new();
634 store.set_token_prices(
635 Default::default(),
636 vec![failed(&token_str, FailedItemError::AllSimulationPathsFailed)],
637 1,
638 true,
639 );
640 store.set_spot_prices(
641 Default::default(),
642 vec![failed(&pair_str, FailedItemError::MissingSimulationState)],
643 1,
644 true,
645 );
646 store.set_component_depths(
647 Default::default(),
648 vec![failed(&pair_str, FailedItemError::MissingSpotPrice)],
649 1,
650 true,
651 );
652
653 store.clear_all();
654
655 assert_eq!(store.token_price_failure(&token_addr), None);
656 assert_eq!(store.spot_price_failure(&pair), None);
657 assert_eq!(store.component_depth_failure(&pair), None);
658 }
659}