mina_tree/staged_ledger/
diff.rs

1use openmina_core::constants::ConstraintConstants;
2
3use crate::{
4    scan_state::{
5        currency::{Amount, Magnitude},
6        scan_state::transaction_snark::work,
7        transaction_logic::{
8            valid, CoinbaseFeeTransfer, TransactionStatus, UserCommand, WithStatus,
9        },
10    },
11    split_at_vec,
12    verifier::VerifierError,
13};
14
15use super::{pre_diff_info::PreDiffError, staged_ledger::StagedLedger};
16
17/// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff_intf.ml#L5>
18#[derive(Clone, PartialEq)]
19pub enum AtMostTwo<T> {
20    Zero,
21    One(Option<T>),
22    Two(Option<(T, Option<T>)>),
23}
24
25impl<T> std::fmt::Debug for AtMostTwo<T> {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        match self {
28            Self::Zero => write!(f, "Zero"),
29            Self::One(_) => f.debug_tuple("One(_)").finish(),
30            Self::Two(_) => f.debug_tuple("Two(_, _)").finish(),
31        }
32    }
33}
34
35/// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff_intf.ml#L20>
36#[derive(Clone, PartialEq)]
37pub enum AtMostOne<T> {
38    Zero,
39    One(Option<T>),
40}
41
42impl<T> std::fmt::Debug for AtMostOne<T> {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        match self {
45            Self::Zero => write!(f, "Zero"),
46            Self::One(_) => f.debug_tuple("One(_)").finish(),
47        }
48    }
49}
50
51/// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff_intf.ml#L37>
52#[derive(Clone, PartialEq)]
53pub struct PreDiffTwo<A, B> {
54    pub completed_works: Vec<A>,
55    pub commands: Vec<B>,
56    pub coinbase: AtMostTwo<CoinbaseFeeTransfer>,
57    pub internal_command_statuses: Vec<TransactionStatus>,
58}
59
60impl<A, B> std::fmt::Debug for PreDiffTwo<A, B> {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        let Self {
63            completed_works,
64            commands,
65            coinbase,
66            internal_command_statuses,
67        } = self;
68
69        f.debug_struct("PreDiffTwo")
70            .field("completed_works", &completed_works.len())
71            .field("commands", &commands.len())
72            .field("coinbase", coinbase)
73            .field("internal_command_statuses", internal_command_statuses)
74            .finish()
75    }
76}
77
78/// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff_intf.ml#L54>
79#[derive(Clone, PartialEq)]
80pub struct PreDiffOne<A, B> {
81    pub completed_works: Vec<A>,
82    pub commands: Vec<B>,
83    pub coinbase: AtMostOne<CoinbaseFeeTransfer>,
84    pub internal_command_statuses: Vec<TransactionStatus>,
85}
86
87impl<A, B> std::fmt::Debug for PreDiffOne<A, B> {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        let Self {
90            completed_works,
91            commands,
92            coinbase,
93            internal_command_statuses,
94        } = self;
95
96        f.debug_struct("PreDiffOne")
97            .field("completed_works", &completed_works.len())
98            .field("commands", &commands.len())
99            .field("coinbase", coinbase)
100            .field("internal_command_statuses", internal_command_statuses)
101            .finish()
102    }
103}
104
105/// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff_intf.ml#L68>
106pub type PreDiffWithAtMostTwoCoinbase = PreDiffTwo<work::Work, WithStatus<UserCommand>>;
107
108/// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff_intf.ml#L82>
109pub type PreDiffWithAtMostOneCoinbase = PreDiffOne<work::Work, WithStatus<UserCommand>>;
110
111/// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff_intf.ml#L107>
112#[derive(Debug, Clone, PartialEq)]
113pub struct Diff {
114    pub diff: (
115        PreDiffWithAtMostTwoCoinbase,
116        Option<PreDiffWithAtMostOneCoinbase>,
117    ),
118}
119
120impl Diff {
121    /// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff.ml#L429>
122    pub fn completed_works(&self) -> Vec<work::Work> {
123        let first = self.diff.0.completed_works.as_slice();
124
125        let second = match self.diff.1.as_ref() {
126            Some(second) => second.completed_works.as_slice(),
127            None => &[],
128        };
129
130        first.iter().chain(second).cloned().collect()
131    }
132
133    /// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff.ml#L425>
134    pub fn commands(&self) -> Vec<WithStatus<UserCommand>> {
135        let first = self.diff.0.commands.as_slice();
136
137        let second = match self.diff.1.as_ref() {
138            Some(second) => second.commands.as_slice(),
139            None => &[],
140        };
141
142        first.iter().chain(second).cloned().collect()
143    }
144
145    /// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff.ml#L333>
146    pub fn validate_commands<F>(self, check: F) -> Result<with_valid_signatures::Diff, PreDiffError>
147    where
148        F: Fn(Vec<WithStatus<UserCommand>>) -> Result<Vec<valid::UserCommand>, VerifierError>,
149    {
150        let validate = |cmds: Vec<WithStatus<UserCommand>>| -> Result<Vec<WithStatus<valid::UserCommand>>, VerifierError> {
151            let valids = check(cmds.clone())?;
152            Ok(valids.into_iter().zip(cmds).map(|(data, c)| {
153                WithStatus { data, status: c.status  }
154            }).collect())
155        };
156
157        let commands = self.commands();
158
159        let (d1, d2) = self.diff;
160
161        let commands_all = validate(commands)?;
162
163        let (commands1, commands2) = split_at_vec(commands_all, d1.commands.len());
164
165        let p1 = with_valid_signatures::PreDiffWithAtMostTwoCoinbase {
166            completed_works: d1.completed_works,
167            commands: commands1,
168            coinbase: d1.coinbase,
169            internal_command_statuses: d1.internal_command_statuses,
170        };
171
172        let p2 = d2.map(|d2| with_valid_signatures::PreDiffWithAtMostOneCoinbase {
173            completed_works: d2.completed_works,
174            commands: commands2,
175            coinbase: d2.coinbase,
176            internal_command_statuses: d2.internal_command_statuses,
177        });
178
179        Ok(with_valid_signatures::Diff { diff: (p1, p2) })
180    }
181
182    pub fn empty() -> Self {
183        Self {
184            diff: (
185                PreDiffWithAtMostTwoCoinbase {
186                    completed_works: Vec::new(),
187                    commands: Vec::new(),
188                    coinbase: AtMostTwo::Zero,
189                    internal_command_statuses: Vec::new(),
190                },
191                None,
192            ),
193        }
194    }
195}
196
197pub mod with_valid_signatures_and_proofs {
198    use super::*;
199
200    /// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff_intf.ml#L123>
201    pub type PreDiffWithAtMostTwoCoinbase =
202        PreDiffTwo<work::Checked, WithStatus<valid::UserCommand>>;
203
204    /// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff_intf.ml#L129>
205    pub type PreDiffWithAtMostOneCoinbase =
206        PreDiffOne<work::Checked, WithStatus<valid::UserCommand>>;
207
208    /// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff_intf.ml#L140>
209    #[derive(Clone, Debug)]
210    pub struct Diff {
211        pub diff: (
212            PreDiffWithAtMostTwoCoinbase,
213            Option<PreDiffWithAtMostOneCoinbase>,
214        ),
215    }
216
217    /// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff.ml#L268>
218    fn forget_cw(list: Vec<work::Checked>) -> Vec<work::Unchecked> {
219        list.into_iter().map(work::Checked::forget).collect()
220    }
221
222    impl Diff {
223        pub fn commands(&self) -> Vec<WithStatus<valid::UserCommand>> {
224            let first = self.diff.0.commands.as_slice();
225
226            let second = match self.diff.1.as_ref() {
227                Some(second) => second.commands.as_slice(),
228                None => &[],
229            };
230
231            first.iter().chain(second).cloned().collect()
232        }
233
234        /// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff.ml#L373>
235        pub fn forget_proof_checks(self) -> super::with_valid_signatures::Diff {
236            let d1 = self.diff.0;
237
238            let p1 = with_valid_signatures::PreDiffWithAtMostTwoCoinbase {
239                completed_works: forget_cw(d1.completed_works),
240                commands: d1.commands,
241                coinbase: d1.coinbase,
242                internal_command_statuses: d1.internal_command_statuses,
243            };
244
245            let p2 = self
246                .diff
247                .1
248                .map(|d2| with_valid_signatures::PreDiffWithAtMostOneCoinbase {
249                    completed_works: forget_cw(d2.completed_works),
250                    commands: d2.commands,
251                    coinbase: d2.coinbase,
252                    internal_command_statuses: d2.internal_command_statuses,
253                });
254
255            super::with_valid_signatures::Diff { diff: (p1, p2) }
256        }
257
258        /// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff.ml#L419>
259        pub fn forget(self) -> super::Diff {
260            let d1 = self.diff.0;
261            let p1 = super::PreDiffWithAtMostTwoCoinbase {
262                completed_works: forget_cw(d1.completed_works),
263                commands: d1
264                    .commands
265                    .into_iter()
266                    .map(|c| c.map(|c| c.forget_check()))
267                    .collect(),
268                coinbase: d1.coinbase,
269                internal_command_statuses: d1.internal_command_statuses,
270            };
271
272            let d2 = self.diff.1;
273            let p2 = d2.map(|d2| super::PreDiffWithAtMostOneCoinbase {
274                completed_works: forget_cw(d2.completed_works),
275                commands: d2
276                    .commands
277                    .into_iter()
278                    .map(|c| c.map(|c| c.forget_check()))
279                    .collect(),
280                coinbase: d2.coinbase,
281                internal_command_statuses: d2.internal_command_statuses,
282            });
283
284            super::Diff { diff: (p1, p2) }
285        }
286
287        pub fn empty() -> Self {
288            Self {
289                diff: (
290                    PreDiffWithAtMostTwoCoinbase {
291                        completed_works: Vec::new(),
292                        commands: Vec::new(),
293                        coinbase: AtMostTwo::Zero,
294                        internal_command_statuses: Vec::new(),
295                    },
296                    None,
297                ),
298            }
299        }
300    }
301}
302
303pub mod with_valid_signatures {
304    use super::*;
305
306    pub type PreDiffWithAtMostTwoCoinbase = PreDiffTwo<work::Work, WithStatus<valid::UserCommand>>;
307
308    pub type PreDiffWithAtMostOneCoinbase = PreDiffOne<work::Work, WithStatus<valid::UserCommand>>;
309
310    pub struct Diff {
311        pub diff: (
312            PreDiffWithAtMostTwoCoinbase,
313            Option<PreDiffWithAtMostOneCoinbase>,
314        ),
315    }
316
317    impl Diff {
318        pub fn empty() -> Self {
319            Self {
320                diff: (
321                    PreDiffWithAtMostTwoCoinbase {
322                        completed_works: Vec::new(),
323                        commands: Vec::new(),
324                        coinbase: AtMostTwo::Zero,
325                        internal_command_statuses: Vec::new(),
326                    },
327                    None,
328                ),
329            }
330        }
331    }
332}
333
334/// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/staged_ledger_diff/diff.ml#L278>
335pub fn coinbase<A, B>(
336    diff: &(PreDiffTwo<A, B>, Option<PreDiffOne<A, B>>),
337    constraint_constants: &ConstraintConstants,
338    supercharge_coinbase: bool,
339) -> Option<Amount> {
340    let (first_pre_diff, second_pre_diff_opt) = &diff;
341    let coinbase_amount = StagedLedger::coinbase_amount(supercharge_coinbase, constraint_constants);
342
343    match (
344        &first_pre_diff.coinbase,
345        second_pre_diff_opt
346            .as_ref()
347            .map(|s| &s.coinbase)
348            .unwrap_or(&AtMostOne::Zero),
349    ) {
350        (AtMostTwo::Zero, AtMostOne::Zero) => Some(Amount::zero()),
351        _ => coinbase_amount,
352    }
353}