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