mina_tree/scan_state/
currency.rs

1use std::cmp::Ordering::{Equal, Greater, Less};
2
3use ark_ff::{fields::arithmetic::InvalidBigInt, BigInteger256, Field};
4use mina_p2p_messages::v2::BlockTimeTimeStableV1;
5use rand::Rng;
6
7use crate::proofs::{
8    field::FieldWitness, to_field_elements::ToFieldElements, transaction::Check, witness::Witness,
9};
10
11#[derive(Copy, Clone, Debug, PartialEq, Eq)]
12pub enum Sgn {
13    Pos,
14    Neg,
15}
16
17impl Sgn {
18    pub fn is_pos(&self) -> bool {
19        match self {
20            Sgn::Pos => true,
21            Sgn::Neg => false,
22        }
23    }
24
25    pub fn negate(&self) -> Self {
26        match self {
27            Sgn::Pos => Sgn::Neg,
28            Sgn::Neg => Sgn::Pos,
29        }
30    }
31
32    pub fn to_field<F: FieldWitness>(&self) -> F {
33        match self {
34            Sgn::Pos => F::one(),
35            Sgn::Neg => F::one().neg(),
36        }
37    }
38}
39
40pub trait Magnitude
41where
42    Self: Sized + PartialOrd + Copy,
43{
44    const NBITS: usize;
45
46    fn abs_diff(&self, rhs: &Self) -> Self;
47    fn wrapping_add(&self, rhs: &Self) -> Self;
48    fn wrapping_mul(&self, rhs: &Self) -> Self;
49    fn wrapping_sub(&self, rhs: &Self) -> Self;
50    fn checked_add(&self, rhs: &Self) -> Option<Self>;
51    fn checked_mul(&self, rhs: &Self) -> Option<Self>;
52    fn checked_sub(&self, rhs: &Self) -> Option<Self>;
53    fn checked_div(&self, rhs: &Self) -> Option<Self>;
54    fn checked_rem(&self, rhs: &Self) -> Option<Self>;
55
56    fn is_zero(&self) -> bool;
57    fn zero() -> Self;
58
59    fn add_flagged(&self, rhs: &Self) -> (Self, bool) {
60        let z = self.wrapping_add(rhs);
61        (z, z < *self)
62    }
63
64    fn sub_flagged(&self, rhs: &Self) -> (Self, bool) {
65        (self.wrapping_sub(rhs), self < rhs)
66    }
67
68    fn to_field<F: FieldWitness>(&self) -> F;
69    fn of_field<F: FieldWitness>(field: F) -> Self;
70}
71
72/// Trait used for default values with `ClosedInterval`
73pub trait MinMax {
74    fn min() -> Self;
75    fn max() -> Self;
76}
77
78#[derive(Copy, Clone, Debug, PartialEq, Eq)]
79pub struct Signed<T: Magnitude> {
80    pub magnitude: T,
81    pub sgn: Sgn,
82}
83
84impl<T> Signed<T>
85where
86    T: Magnitude + PartialOrd + Ord + Clone,
87{
88    const NBITS: usize = T::NBITS;
89
90    pub fn create(magnitude: T, sgn: Sgn) -> Self {
91        Self {
92            magnitude,
93            sgn: if magnitude.is_zero() { Sgn::Pos } else { sgn },
94        }
95    }
96
97    pub fn of_unsigned(magnitude: T) -> Self {
98        Self::create(magnitude, Sgn::Pos)
99    }
100
101    pub fn negate(&self) -> Self {
102        if self.magnitude.is_zero() {
103            Self::zero()
104        } else {
105            Self {
106                magnitude: self.magnitude,
107                sgn: self.sgn.negate(),
108            }
109        }
110    }
111
112    pub fn is_pos(&self) -> bool {
113        matches!(self.sgn, Sgn::Pos)
114    }
115
116    /// <https://github.com/MinaProtocol/mina/blob/42d2005d04b59d14aacf4eef5ccee353e9a531b7/src/lib/transaction_logic/mina_transaction_logic.ml#L1615>
117    pub fn is_non_neg(&self) -> bool {
118        matches!(self.sgn, Sgn::Pos)
119    }
120
121    pub fn is_neg(&self) -> bool {
122        matches!(self.sgn, Sgn::Neg)
123    }
124
125    /// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/currency/currency.ml#L441>
126    pub fn zero() -> Self {
127        Self {
128            magnitude: T::zero(),
129            sgn: Sgn::Pos,
130        }
131    }
132
133    pub fn is_zero(&self) -> bool {
134        self.magnitude.is_zero() //&& matches!(self.sgn, Sgn::Pos)
135    }
136
137    /// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/currency/currency.ml#L460>
138    pub fn add(&self, rhs: &Self) -> Option<Self> {
139        let (magnitude, sgn) = if self.sgn == rhs.sgn {
140            let magnitude = self.magnitude.checked_add(&rhs.magnitude)?;
141            let sgn = self.sgn;
142
143            (magnitude, sgn)
144        } else {
145            let sgn = match self.magnitude.cmp(&rhs.magnitude) {
146                Less => rhs.sgn,
147                Greater => self.sgn,
148                Equal => return Some(Self::zero()),
149            };
150            let magnitude = self.magnitude.abs_diff(&rhs.magnitude);
151
152            (magnitude, sgn)
153        };
154
155        Some(Self::create(magnitude, sgn))
156    }
157
158    pub fn add_flagged(&self, rhs: Self) -> (Self, bool) {
159        match (self.sgn, rhs.sgn) {
160            (Sgn::Neg, sgn @ Sgn::Neg) | (Sgn::Pos, sgn @ Sgn::Pos) => {
161                let (magnitude, overflow) = self.magnitude.add_flagged(&rhs.magnitude);
162                (Self { magnitude, sgn }, overflow)
163            }
164            (Sgn::Pos, Sgn::Neg) | (Sgn::Neg, Sgn::Pos) => {
165                let sgn = match self.magnitude.cmp(&rhs.magnitude) {
166                    Less => rhs.sgn,
167                    Greater => self.sgn,
168                    Equal => Sgn::Pos,
169                };
170                let magnitude = self.magnitude.abs_diff(&rhs.magnitude);
171                (Self { magnitude, sgn }, false)
172            }
173        }
174    }
175}
176
177impl Signed<Amount> {
178    pub fn to_fee(self) -> Signed<Fee> {
179        let Self { magnitude, sgn } = self;
180
181        Signed {
182            magnitude: Fee(magnitude.0),
183            sgn,
184        }
185    }
186}
187
188impl<T> Signed<T>
189where
190    T: Magnitude + PartialOrd + Ord + Clone,
191    rand::distributions::Standard: rand::distributions::Distribution<T>,
192{
193    pub fn gen() -> Self {
194        let mut rng = rand::thread_rng();
195
196        let magnitude: T = rng.gen();
197        let sgn = if rng.gen::<bool>() {
198            Sgn::Pos
199        } else {
200            Sgn::Neg
201        };
202
203        Self::create(magnitude, sgn)
204    }
205}
206
207impl Amount {
208    /// The number of nanounits in a unit. User for unit transformations.
209    const UNIT_TO_NANO: u64 = 1_000_000_000;
210
211    pub fn of_fee(fee: &Fee) -> Self {
212        Self(fee.0)
213    }
214
215    pub fn add_signed_flagged(&self, rhs: Signed<Self>) -> (Self, bool) {
216        if let Sgn::Pos = rhs.sgn {
217            self.add_flagged(&rhs.magnitude)
218        } else {
219            self.sub_flagged(&rhs.magnitude)
220        }
221    }
222
223    pub fn to_nanomina_int(&self) -> Self {
224        *self
225    }
226
227    pub fn to_mina_int(&self) -> Self {
228        Self(self.0.checked_div(Self::UNIT_TO_NANO).unwrap())
229    }
230
231    pub fn of_mina_int_exn(int: u64) -> Self {
232        Self::from_u64(int).scale(Self::UNIT_TO_NANO).unwrap()
233    }
234
235    pub fn of_nanomina_int_exn(int: u64) -> Self {
236        Self::from_u64(int)
237    }
238}
239
240impl Balance {
241    pub fn sub_amount(&self, amount: Amount) -> Option<Self> {
242        self.0.checked_sub(amount.0).map(Self)
243    }
244
245    pub fn add_amount(&self, amount: Amount) -> Option<Self> {
246        self.0.checked_add(amount.0).map(Self)
247    }
248
249    pub fn add_signed_flagged(&self, rhs: Signed<Self>) -> (Self, bool) {
250        if let Sgn::Pos = rhs.sgn {
251            self.add_flagged(&rhs.magnitude)
252        } else {
253            self.sub_flagged(&rhs.magnitude)
254        }
255    }
256
257    pub fn add_signed_amount_flagged(&self, rhs: Signed<Amount>) -> (Self, bool) {
258        let rhs = Signed {
259            magnitude: Balance::from_u64(rhs.magnitude.0),
260            sgn: rhs.sgn,
261        };
262
263        if let Sgn::Pos = rhs.sgn {
264            self.add_flagged(&rhs.magnitude)
265        } else {
266            self.sub_flagged(&rhs.magnitude)
267        }
268    }
269
270    pub fn to_amount(self) -> Amount {
271        Amount(self.0)
272    }
273
274    /// Passed amount gets multiplied by 1 billion to convert to nanomina.
275    pub fn from_mina(amount: u64) -> Option<Self> {
276        amount.checked_mul(1_000_000_000).map(Self::from_u64)
277    }
278
279    pub fn of_nanomina_int_exn(int: u64) -> Self {
280        Self::from_u64(int)
281    }
282}
283
284impl Fee {
285    pub const fn of_nanomina_int_exn(int: u64) -> Self {
286        Self::from_u64(int)
287    }
288}
289
290impl Index {
291    // TODO: Not sure if OCaml wraps around here
292    pub fn incr(&self) -> Self {
293        Self(self.0.wrapping_add(1))
294    }
295}
296
297impl Nonce {
298    // TODO: Not sure if OCaml wraps around here
299    pub fn incr(&self) -> Self {
300        Self(self.0.wrapping_add(1))
301    }
302
303    pub fn succ(&self) -> Self {
304        self.incr()
305    }
306
307    pub fn add_signed_flagged(&self, rhs: Signed<Self>) -> (Self, bool) {
308        if let Sgn::Pos = rhs.sgn {
309            self.add_flagged(&rhs.magnitude)
310        } else {
311            self.sub_flagged(&rhs.magnitude)
312        }
313    }
314
315    /// low <= self <= high
316    pub fn between(&self, low: &Self, high: &Self) -> bool {
317        low <= self && self <= high
318    }
319}
320
321impl BlockTime {
322    pub fn add(&self, span: BlockTimeSpan) -> Self {
323        Self(self.0.checked_add(span.0).unwrap())
324    }
325
326    pub fn sub(&self, span: BlockTimeSpan) -> Self {
327        Self(self.0.checked_sub(span.0).unwrap())
328    }
329
330    pub fn diff(&self, other: Self) -> BlockTimeSpan {
331        BlockTimeSpan(self.0 - other.0)
332    }
333
334    pub fn to_span_since_epoch(&self) -> BlockTimeSpan {
335        let Self(ms) = self;
336        BlockTimeSpan(*ms)
337    }
338
339    pub fn of_span_since_epoch(span: BlockTimeSpan) -> Self {
340        let BlockTimeSpan(ms) = span;
341        Self(ms)
342    }
343}
344
345impl From<BlockTimeTimeStableV1> for BlockTime {
346    fn from(bt: BlockTimeTimeStableV1) -> Self {
347        Self(bt.0 .0 .0)
348    }
349}
350
351impl BlockTimeSpan {
352    pub fn of_ms(ms: u64) -> Self {
353        Self(ms)
354    }
355    pub fn to_ms(&self) -> u64 {
356        let Self(ms) = self;
357        *ms
358    }
359}
360
361impl Slot {
362    // TODO: Not sure if OCaml wraps around here
363    pub fn incr(&self) -> Self {
364        Self(self.0.wrapping_add(1))
365    }
366
367    pub fn add(&self, other: SlotSpan) -> Self {
368        let SlotSpan(other) = other;
369        Self(self.0.checked_add(other).unwrap())
370    }
371
372    pub fn succ(&self) -> Self {
373        self.incr()
374    }
375
376    pub fn gen_small() -> Self {
377        let mut rng = rand::thread_rng();
378        Self(rng.gen::<u32>() % 10_000)
379    }
380}
381
382macro_rules! impl_number {
383    (32: { $($name32:ident,)* }, 64: { $($name64:ident,)* },) => {
384        $(impl_number!({$name32, u32, as_u32, from_u32, next_u32, append_u32},);)+
385        $(impl_number!({$name64, u64, as_u64, from_u64, next_u64, append_u64},);)+
386    };
387    ($({ $name:ident, $inner:ty, $as_name:ident, $from_name:ident, $next_name:ident, $append_name:ident },)*) => ($(
388        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Deserialize, serde::Serialize)]
389        pub struct $name(pub(super) $inner);
390
391        impl std::fmt::Debug for $name {
392            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
393                f.write_fmt(format_args!("{}({:?})", stringify!($name), self.0))
394            }
395        }
396
397        impl Magnitude for $name {
398            const NBITS: usize = Self::NBITS;
399
400            fn zero() -> Self {
401                Self(0)
402            }
403
404            fn is_zero(&self) -> bool {
405                self.0 == 0
406            }
407
408            fn wrapping_add(&self, rhs: &Self) -> Self {
409                Self(self.0.wrapping_add(rhs.0))
410            }
411
412            fn wrapping_mul(&self, rhs: &Self) -> Self {
413                Self(self.0.wrapping_mul(rhs.0))
414            }
415
416            fn wrapping_sub(&self, rhs: &Self) -> Self {
417                Self(self.0.wrapping_sub(rhs.0))
418            }
419
420            fn checked_add(&self, rhs: &Self) -> Option<Self> {
421                self.0.checked_add(rhs.0).map(Self)
422            }
423
424            fn checked_mul(&self, rhs: &Self) -> Option<Self> {
425                self.0.checked_mul(rhs.0).map(Self)
426            }
427
428            fn checked_sub(&self, rhs: &Self) -> Option<Self> {
429                self.0.checked_sub(rhs.0).map(Self)
430            }
431
432            fn checked_div(&self, rhs: &Self) -> Option<Self> {
433                self.0.checked_div(rhs.0).map(Self)
434            }
435
436            fn checked_rem(&self, rhs: &Self) -> Option<Self> {
437                self.0.checked_rem(rhs.0).map(Self)
438            }
439
440            fn abs_diff(&self, rhs: &Self) -> Self {
441                Self(self.0.abs_diff(rhs.0))
442            }
443
444            fn to_field<F: FieldWitness>(&self) -> F {
445                self.to_field()
446            }
447
448            fn of_field<F: FieldWitness>(field: F) -> Self {
449                let amount: BigInteger256 = field.into();
450                let amount: $inner = amount.to_64x4()[0].try_into().unwrap();
451
452                Self::$from_name(amount)
453            }
454        }
455
456        impl MinMax for $name {
457            fn min() -> Self { Self(0) }
458            fn max() -> Self { Self(<$inner>::MAX) }
459        }
460
461        impl $name {
462            pub const NBITS: usize = <$inner>::BITS as usize;
463
464            pub fn $as_name(&self) -> $inner {
465                self.0
466            }
467
468            pub const fn $from_name(value: $inner) -> Self {
469                Self(value)
470            }
471
472            /// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/currency/currency.ml#L379>
473            pub const fn scale(&self, n: $inner) -> Option<Self> {
474                match self.0.checked_mul(n) {
475                    Some(n) => Some(Self(n)),
476                    None => None
477                }
478            }
479
480            pub fn min() -> Self {
481                <Self as MinMax>::min()
482            }
483
484            pub fn max() -> Self {
485                <Self as MinMax>::max()
486            }
487
488            /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/currency/currency.ml#L124>
489            pub fn of_mina_string_exn(input: &str) -> Self {
490                const PRECISION: usize = 9;
491
492                let mut s = String::with_capacity(input.len() + 9);
493
494                if !input.contains('.') {
495                    let append = "000000000";
496                    assert_eq!(append.len(), PRECISION);
497
498                    s.push_str(input);
499                    s.push_str(append);
500                } else {
501                    let (whole, decimal) = {
502                        let mut splitted = input.split('.');
503                        let whole = splitted.next().unwrap();
504                        let decimal = splitted.next().unwrap();
505                        assert!(splitted.next().is_none(), "Currency.of_mina_string_exn: Invalid currency input");
506                        (whole, decimal)
507                    };
508
509                    let decimal_length = decimal.len();
510
511                    if decimal_length > PRECISION {
512                        s.push_str(whole);
513                        s.push_str(&decimal[0..PRECISION]);
514                    } else {
515                        s.push_str(whole);
516                        s.push_str(decimal);
517                        for _ in 0..PRECISION - decimal_length {
518                            s.push('0');
519                        }
520                    }
521                }
522
523                let n = s.parse::<$inner>().unwrap();
524                Self(n)
525            }
526
527            pub fn to_bits(&self) -> [bool; <$inner>::BITS as usize] {
528                use crate::proofs::transaction::legacy_input::bits_iter;
529
530                let mut iter = bits_iter::<$inner, { <$inner>::BITS as usize }>(self.0);
531                std::array::from_fn(|_| iter.next().unwrap())
532            }
533
534            pub fn to_field<F: Field + TryFrom<BigInteger256, Error = InvalidBigInt>>(&self) -> F {
535                let int = self.0 as u64;
536                F::from(int)
537            }
538        }
539
540        impl rand::distributions::Distribution<$name> for rand::distributions::Standard {
541            fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $name {
542                $name(rng.$next_name())
543            }
544        }
545
546        impl crate::ToInputs for $name {
547            fn to_inputs(&self, inputs: &mut poseidon::hash::Inputs) {
548                inputs.$append_name(self.0);
549            }
550        }
551
552        impl<F: FieldWitness> ToFieldElements<F> for $name {
553            fn to_field_elements(&self, fields: &mut Vec<F>) {
554                fields.push(self.to_field());
555            }
556        }
557
558        impl<F: FieldWitness> Check<F> for $name {
559            fn check(&self, witnesses: &mut Witness<F>) {
560                use crate::proofs::transaction::scalar_challenge::to_field_checked_prime;
561
562                const NBITS: usize = <$inner>::BITS as usize;
563
564                let number: $inner = self.$as_name();
565                assert_eq!(NBITS, std::mem::size_of_val(&number) * 8);
566
567                let number: F = number.into();
568                to_field_checked_prime::<F, NBITS>(number, witnesses);
569            }
570        }
571
572    )+)
573}
574
575impl_number!(
576    32: { Length, Slot, Nonce, Index, SlotSpan, TxnVersion, Epoch, },
577    64: { Amount, Balance, Fee, BlockTime, BlockTimeSpan, N, },
578);