evm_fork_cache/cold_start/config.rs
1//! Cold-start run configuration and the accumulated run report.
2
3use alloy_primitives::B256;
4
5use crate::cold_start::plan::ColdStartPlan;
6use crate::cold_start::results::ColdStartResults;
7use crate::freshness::SlotFetch;
8
9/// Configuration for a cold-start run.
10///
11/// `Default` is hand-written (`max_rounds: 8`, `pin: ColdStartPin::CachePinned`)
12/// rather than derived, because a derived `max_rounds: 0` would trip
13/// [`RoundBudgetExceeded`](crate::cold_start::ColdStartError::RoundBudgetExceeded)
14/// before any round ran. `max_rounds >= 1` is a precondition.
15#[derive(Clone, Debug)]
16pub struct ColdStartConfig {
17 /// Hard upper bound on **executed** rounds. A planner that returns `Continue`
18 /// past this bound yields
19 /// [`RoundBudgetExceeded`](crate::cold_start::ColdStartError::RoundBudgetExceeded).
20 /// Must be `>= 1`.
21 pub max_rounds: usize,
22 /// Block-pinning policy for the run's reads.
23 pub pin: ColdStartPin,
24}
25
26impl Default for ColdStartConfig {
27 fn default() -> Self {
28 Self {
29 max_rounds: 8,
30 pin: ColdStartPin::CachePinned,
31 }
32 }
33}
34
35/// How the driver pins the block for the run's reads.
36#[derive(Clone, Debug)]
37pub enum ColdStartPin {
38 /// Use the cache's currently-pinned block (`self.block`) for every round.
39 CachePinned,
40 /// Pin every round to an explicit hash for the whole run (reorg-stable cold
41 /// start), restoring the prior block on completion. With
42 /// `require_canonical: true`, the provider rejects a reorged hash so the run
43 /// fails fast.
44 Hash {
45 /// Block number associated with the hash (advisory).
46 number: u64,
47 /// Block hash to pin all reads to.
48 hash: B256,
49 /// Whether the provider must reject a non-canonical hash.
50 require_canonical: bool,
51 },
52}
53
54/// Summary of a completed cold-start run, returned by
55/// [`run_cold_start`](crate::cache::EvmCache::run_cold_start) on success.
56///
57/// Accumulated round-by-round as the run progresses. Note `run_cold_start`
58/// returns this report **only on the `Ok` path**: on a hard error the partial
59/// round is folded in before the error is returned, but the report itself is
60/// then dropped (the `Err` carries only the cause). To observe partial progress
61/// on failure, drive rounds yourself via
62/// [`execute_cold_start_round`](crate::cache::EvmCache::execute_cold_start_round),
63/// which always returns a [`RoundOutcome`](crate::cold_start::RoundOutcome).
64#[derive(Clone, Debug, Default)]
65pub struct ColdStartRunReport {
66 /// Number of rounds executed.
67 pub rounds: usize,
68 /// Total verify slots requested across all rounds.
69 pub verified_slots: usize,
70 /// Total slots that changed and were injected.
71 pub changed_slots: usize,
72 /// Total accounts touched by discover calls, summed across calls and rounds
73 /// (not de-duplicated — the same account touched twice counts twice).
74 pub discovered_accounts: usize,
75 /// Total slots touched by discover calls, summed across calls and rounds
76 /// (not de-duplicated — the same slot touched twice counts twice).
77 pub discovered_slots: usize,
78 /// Total verify + probe slots whose fetch failed.
79 pub failed_slots: usize,
80 /// One summary per round, in execution order.
81 pub per_round: Vec<ColdStartRoundSummary>,
82}
83
84/// Per-round breakdown recorded in [`ColdStartRunReport::per_round`].
85#[derive(Clone, Debug, Default)]
86pub struct ColdStartRoundSummary {
87 /// Verify slots requested this round.
88 pub verify_requested: usize,
89 /// Verify slots that changed and were injected.
90 pub verify_changed: usize,
91 /// Verify slots whose fetch failed.
92 pub verify_failed: usize,
93 /// Probe slots requested this round.
94 pub probe_requested: usize,
95 /// Probe slots whose fetch failed.
96 pub probe_failed: usize,
97 /// Probe-roots addresses requested this round.
98 pub probe_roots_requested: usize,
99 /// Probe-roots addresses whose root could not be observed
100 /// (`RootProbeOutcome::root` is `None`).
101 pub probe_roots_failed: usize,
102 /// Discover calls issued this round.
103 pub discover_calls: usize,
104 /// Slots touched by this round's discover calls.
105 pub discovered_slots: usize,
106}
107
108impl ColdStartRunReport {
109 /// Fold one round's plan and results into the report.
110 ///
111 /// Plain field accumulation, no IO. `failed_slots` counts
112 /// [`FetchFailed`](crate::freshness::SlotFetch::FetchFailed) outcomes across
113 /// both the verify (`fetched`) and probe (`probed`) phases. Root probes are
114 /// not slots, so probe-roots counts are recorded per round
115 /// (`probe_roots_requested` / `probe_roots_failed`) and never fold into
116 /// `failed_slots` — mirroring how `probe_requested` / `probe_failed` carry
117 /// no dedicated run-level totals.
118 pub(crate) fn absorb_round(&mut self, plan: &ColdStartPlan, results: &ColdStartResults) {
119 self.rounds += 1;
120 let verify_failed = results
121 .fetched
122 .iter()
123 .filter(|o| matches!(o.fetch, SlotFetch::FetchFailed { .. }))
124 .count();
125 let probe_failed = results
126 .probed
127 .iter()
128 .filter(|o| matches!(o.fetch, SlotFetch::FetchFailed { .. }))
129 .count();
130 let probe_roots_failed = results
131 .probed_roots
132 .iter()
133 .filter(|o| o.root.is_none())
134 .count();
135 let discovered_slots: usize = results
136 .discovered
137 .iter()
138 .map(|d| d.access.slots.len())
139 .sum();
140 let discovered_accounts: usize = results
141 .discovered
142 .iter()
143 .map(|d| d.access.accounts.len())
144 .sum();
145
146 self.verified_slots += plan.verify.len();
147 self.changed_slots += results.verified.len();
148 self.failed_slots += verify_failed + probe_failed;
149 self.discovered_slots += discovered_slots;
150 self.discovered_accounts += discovered_accounts;
151
152 self.per_round.push(ColdStartRoundSummary {
153 verify_requested: plan.verify.len(),
154 verify_changed: results.verified.len(),
155 verify_failed,
156 probe_requested: plan.probe.len(),
157 probe_failed,
158 probe_roots_requested: plan.probe_roots.len(),
159 probe_roots_failed,
160 discover_calls: plan.discover.len(),
161 discovered_slots,
162 });
163 }
164}