sc_proof_of_time/source/
state.rs

1use crate::verifier::PotVerifier;
2use parking_lot::Mutex;
3use sp_consensus_slots::Slot;
4use sp_consensus_subspace::{PotNextSlotInput, PotParametersChange};
5use subspace_core_primitives::pot::PotOutput;
6
7#[derive(Debug, Copy, Clone, Eq, PartialEq)]
8struct InnerState {
9    next_slot_input: PotNextSlotInput,
10    parameters_change: Option<PotParametersChange>,
11}
12
13impl InnerState {
14    pub(super) fn update(
15        mut self,
16        mut best_slot: Slot,
17        mut best_output: PotOutput,
18        maybe_updated_parameters_change: Option<Option<PotParametersChange>>,
19        pot_verifier: &PotVerifier,
20    ) -> Self {
21        if let Some(updated_parameters_change) = maybe_updated_parameters_change {
22            self.parameters_change = updated_parameters_change;
23        }
24
25        loop {
26            self.next_slot_input = PotNextSlotInput::derive(
27                self.next_slot_input.slot_iterations,
28                best_slot,
29                best_output,
30                &self.parameters_change,
31            );
32
33            // Advance further as far as possible using previously verified proofs/checkpoints
34            if let Some(checkpoints) = pot_verifier.try_get_checkpoints(
35                self.next_slot_input.slot_iterations,
36                self.next_slot_input.seed,
37            ) {
38                best_slot = self.next_slot_input.slot;
39                best_output = checkpoints.output();
40            } else {
41                break;
42            }
43        }
44
45        self
46    }
47}
48
49#[derive(Debug)]
50pub(super) enum PotStateUpdateOutcome {
51    NoChange,
52    Extension {
53        from: PotNextSlotInput,
54        to: PotNextSlotInput,
55    },
56    Reorg {
57        from: PotNextSlotInput,
58        to: PotNextSlotInput,
59    },
60}
61
62#[derive(Debug)]
63pub(super) struct PotState {
64    inner_state: Mutex<InnerState>,
65    verifier: PotVerifier,
66}
67
68impl PotState {
69    pub(super) fn new(
70        next_slot_input: PotNextSlotInput,
71        parameters_change: Option<PotParametersChange>,
72        verifier: PotVerifier,
73    ) -> Self {
74        let inner = InnerState {
75            next_slot_input,
76            parameters_change,
77        };
78
79        Self {
80            inner_state: Mutex::new(inner),
81            verifier,
82        }
83    }
84
85    pub(super) fn next_slot_input(&self) -> PotNextSlotInput {
86        self.inner_state.lock().next_slot_input
87    }
88
89    /// Extend state if it matches provided expected next slot input.
90    ///
91    /// Returns `Ok(new_next_slot_input)` if state was extended successfully and
92    /// `Err(existing_next_slot_input)` in case state was changed in the meantime.
93    pub(super) fn try_extend(
94        &self,
95        expected_existing_next_slot_input: PotNextSlotInput,
96        best_slot: Slot,
97        best_output: PotOutput,
98        maybe_updated_parameters_change: Option<Option<PotParametersChange>>,
99    ) -> Result<PotNextSlotInput, PotNextSlotInput> {
100        let mut existing_inner_state = self.inner_state.lock();
101        if expected_existing_next_slot_input != existing_inner_state.next_slot_input {
102            return Err(existing_inner_state.next_slot_input);
103        }
104
105        *existing_inner_state = existing_inner_state.update(
106            best_slot,
107            best_output,
108            maybe_updated_parameters_change,
109            &self.verifier,
110        );
111
112        Ok(existing_inner_state.next_slot_input)
113    }
114
115    /// Update state, overriding PoT chain if it doesn't match provided values.
116    ///
117    /// Returns `Some(next_slot_input)` if reorg happened.
118    pub(super) fn update(
119        &self,
120        best_slot: Slot,
121        best_output: PotOutput,
122        maybe_updated_parameters_change: Option<Option<PotParametersChange>>,
123    ) -> PotStateUpdateOutcome {
124        let previous_best_state;
125        let new_best_state;
126        {
127            let mut inner_state = self.inner_state.lock();
128            previous_best_state = *inner_state;
129            new_best_state = previous_best_state.update(
130                best_slot,
131                best_output,
132                maybe_updated_parameters_change,
133                &self.verifier,
134            );
135            *inner_state = new_best_state;
136        }
137
138        if previous_best_state.next_slot_input == new_best_state.next_slot_input {
139            return PotStateUpdateOutcome::NoChange;
140        }
141
142        if previous_best_state.next_slot_input.slot < new_best_state.next_slot_input.slot {
143            let mut slot_iterations = previous_best_state.next_slot_input.slot_iterations;
144            let mut seed = previous_best_state.next_slot_input.seed;
145
146            for slot in u64::from(previous_best_state.next_slot_input.slot)
147                ..u64::from(new_best_state.next_slot_input.slot)
148            {
149                let slot = Slot::from(slot);
150
151                let Some(checkpoints) = self.verifier.try_get_checkpoints(slot_iterations, seed)
152                else {
153                    break;
154                };
155
156                let pot_input = PotNextSlotInput::derive(
157                    slot_iterations,
158                    slot,
159                    checkpoints.output(),
160                    &maybe_updated_parameters_change.flatten(),
161                );
162
163                // TODO: Consider carrying of the whole `PotNextSlotInput` rather than individual
164                //  variables
165                let next_slot = slot + Slot::from(1);
166                slot_iterations = pot_input.slot_iterations;
167                seed = pot_input.seed;
168
169                if next_slot == new_best_state.next_slot_input.slot
170                    && slot_iterations == new_best_state.next_slot_input.slot_iterations
171                    && seed == new_best_state.next_slot_input.seed
172                {
173                    return PotStateUpdateOutcome::Extension {
174                        from: previous_best_state.next_slot_input,
175                        to: new_best_state.next_slot_input,
176                    };
177                }
178            }
179        }
180
181        PotStateUpdateOutcome::Reorg {
182            from: previous_best_state.next_slot_input,
183            to: new_best_state.next_slot_input,
184        }
185    }
186}