1use mina_curves::pasta::Fp;
2
3use crate::{
4 proofs::{
5 block::consensus::ConsensusConstantsChecked,
6 field::{field, Boolean, FieldWitness},
7 to_field_elements::ToFieldElements,
8 transaction::Check,
9 witness::Witness,
10 },
11 scan_state::currency::{
12 BlockTime, BlockTimeSpan, Index, Length, Magnitude, MinMax, Nonce, Slot, SlotSpan,
13 TxnVersion,
14 },
15 ToInputs,
16};
17
18use super::common::{range_check, range_check_flag, ForZkappCheck};
19
20pub trait CheckedNat<F: FieldWitness, const NBITS: usize>:
21 Sized + ToFieldElements<F> + Check<F> + Clone
22{
23 type Inner: MinMax + Magnitude;
24
25 fn to_field(&self) -> F;
26 fn from_field(field: F) -> Self;
27
28 fn zero() -> Self {
29 Self::from_field(F::zero())
30 }
31
32 fn one() -> Self {
33 Self::from_field(F::one())
34 }
35
36 fn to_inner(&self) -> Self::Inner {
37 Self::Inner::of_field(self.to_field())
38 }
39
40 fn from_inner(inner: Self::Inner) -> Self {
41 Self::from_field(inner.to_field())
42 }
43
44 fn gte(&self, other: &Self, w: &mut Witness<F>) -> Boolean {
47 let (x, y) = (self.to_field(), other.to_field());
48
49 let xy = w.exists(x - y);
50 let yx = w.exists(xy.neg());
51
52 let x_gte_y = range_check_flag::<F, NBITS>(xy, w);
53 let y_gte_x = range_check_flag::<F, NBITS>(yx, w);
54
55 Boolean::assert_any(&[x_gte_y, y_gte_x], w);
56 x_gte_y
57 }
58
59 fn lte(&self, other: &Self, w: &mut Witness<F>) -> Boolean {
62 other.gte(self, w)
63 }
64
65 fn less_than(&self, other: &Self, w: &mut Witness<F>) -> Boolean {
68 let is_equal = field::equal(other.to_field(), self.to_field(), w);
69 other.gte(self, w).and(&is_equal.neg(), w)
70 }
71
72 fn const_gte(&self, other: &Self, w: &mut Witness<F>) -> Boolean {
74 let (x, y) = (self.to_field(), other.to_field());
75
76 let xy = x - y;
77 let yx = w.exists(xy.neg());
78
79 let x_gte_y = range_check_flag::<F, NBITS>(xy, w);
80 let y_gte_x = range_check_flag::<F, NBITS>(yx, w);
81
82 Boolean::assert_any(&[x_gte_y, y_gte_x], w);
83 x_gte_y
84 }
85
86 fn const_less_than(&self, other: &Self, w: &mut Witness<F>) -> Boolean {
87 let is_equal = field::equal(other.to_field(), self.to_field(), w);
88 other.const_gte(self, w).and(&is_equal.neg(), w)
89 }
90
91 fn greater_than(&self, other: &Self, w: &mut Witness<F>) -> Boolean {
94 other.less_than(self, w)
95 }
96
97 fn const_greater_than(&self, other: &Self, w: &mut Witness<F>) -> Boolean {
98 other.const_less_than(self, w)
99 }
100
101 fn equal(&self, other: &Self, w: &mut Witness<F>) -> Boolean {
102 field::equal(self.to_field(), other.to_field(), w)
103 }
104
105 fn subtract_unpacking_or_zero(&self, y: &Self, w: &mut Witness<F>) -> (Boolean, Self) {
106 let x = self.to_field();
107 let y = y.to_field();
108
109 let res = w.exists(x - y);
110 let neg_res = w.exists(res.neg());
111
112 let x_gte_y = range_check_flag::<F, NBITS>(res, w);
113 let y_gte_x = range_check_flag::<F, NBITS>(neg_res, w);
114
115 Boolean::assert_any(&[x_gte_y, y_gte_x], w);
116
117 let is_equal = field::equal(x, y, w);
118 let underflow = y_gte_x.and(&is_equal.neg(), w);
119
120 let value = w.exists(match underflow {
121 Boolean::True => F::zero(),
122 Boolean::False => res,
123 });
124
125 (underflow, Self::from_field(value))
126 }
127
128 fn sub_or_zero(&self, y: &Self, w: &mut Witness<F>) -> (Boolean, Self) {
130 self.subtract_unpacking_or_zero(y, w)
131 }
132
133 fn div_mod(&self, y: &Self, w: &mut Witness<F>) -> (Self, Self) {
135 let x = self.to_inner();
136 let y = y.to_inner();
137
138 let div = x.checked_div(&y).unwrap();
139 let rem = x.checked_rem(&y).unwrap();
140
141 w.exists((Self::from_inner(div), Self::from_inner(rem)))
142 }
143
144 fn add(&self, y: &Self, w: &mut Witness<F>) -> Self {
145 let res = field::add(self.to_field(), y.to_field(), w);
146 range_check::<F, NBITS>(res, w);
147 Self::from_field(res)
148 }
149
150 fn const_add(&self, y: &Self, w: &mut Witness<F>) -> Self {
151 let res = self.to_field() + y.to_field();
152 range_check::<F, NBITS>(res, w);
153 Self::from_field(res)
154 }
155
156 fn mul(&self, y: &Self, w: &mut Witness<F>) -> Self {
157 let res = field::mul(self.to_field(), y.to_field(), w);
158 range_check::<F, NBITS>(res, w);
159 Self::from_field(res)
160 }
161
162 fn const_mul(&self, y: &Self, w: &mut Witness<F>) -> Self {
163 let res = self.to_field() * y.to_field();
164 range_check::<F, NBITS>(res, w);
165 Self::from_field(res)
166 }
167
168 fn min(&self, b: &Self, w: &mut Witness<F>) -> Self {
169 let a_lte_b = self.lte(b, w);
170 w.exists_no_check(match a_lte_b {
171 Boolean::True => self.clone(),
172 Boolean::False => b.clone(),
173 })
174 }
175
176 fn succ(&self) -> Self {
177 let this = self.to_field();
178 Self::from_field(this + F::one())
179 }
180}
181
182impl<F: FieldWitness> CheckedSlot<F> {
183 pub fn diff_or_zero(&self, t2: &Self, w: &mut Witness<F>) -> (Boolean, CheckedSlotSpan<F>) {
184 let t1 = self;
185 let (is_underflow, diff) = Self::sub_or_zero(t1, t2, w);
186 (is_underflow, CheckedSlotSpan(diff.0))
187 }
188
189 pub fn diff(&self, t2: &Self, w: &mut Witness<F>) -> CheckedSlotSpan<F> {
190 let diff = field::sub(self.to_field(), t2.to_field(), w);
191 range_check::<F, 32>(diff, w);
192 CheckedSlotSpan::from_field(diff)
193 }
194
195 pub fn to_length(&self) -> CheckedLength<F> {
196 CheckedLength::from_field(self.to_field())
197 }
198}
199
200impl CheckedSlot<Fp> {
201 pub fn in_seed_update_range(
202 &self,
203 constants: &ConsensusConstantsChecked,
204 w: &mut Witness<Fp>,
205 ) -> Boolean {
206 let c = |n: u32| Length::from_u32(n).to_checked();
208 let third_epoch = {
209 let (q, _r) = constants.slots_per_epoch.div_mod(&c(3), w);
210 q
211 };
212 let ck_times_2 = third_epoch.const_mul(&c(2), w);
213 self.to_length().less_than(&ck_times_2, w)
215 }
216}
217
218impl<F: FieldWitness> CheckedLength<F> {
219 pub fn to_slot(&self) -> CheckedSlot<F> {
220 CheckedSlot::from_field(self.to_field())
221 }
222
223 pub fn const_succ(&self) -> Self {
224 Self(self.0 + F::one())
225 }
226}
227
228macro_rules! impl_nat {
229 ($({$name:tt, $unchecked:tt}),*) => ($(
230
231 #[derive(Copy, Clone, Debug)]
232 pub struct $name<F: FieldWitness>(F);
233
234 impl<F: FieldWitness> CheckedNat<F, 32> for $name::<F> {
235 type Inner = $unchecked;
236 fn to_field(&self) -> F {
237 self.0
238 }
239 fn from_field(field: F) -> Self {
240 Self(field)
241 }
242 }
243
244 impl<F: FieldWitness> ToFieldElements<F> for $name::<F> {
245 fn to_field_elements(&self, fields: &mut Vec<F>) {
246 let Self(this) = self;
247 this.to_field_elements(fields)
248 }
249 }
250
251 impl<F: FieldWitness> Check<F> for $name::<F> {
252 fn check(&self, w: &mut Witness<F>) {
253 range_check::<F, { 32 }>(self.0, w);
254 }
255 }
256
257 impl<F: FieldWitness> ToInputs for $name<F> {
258 fn to_inputs(&self, inputs: &mut ::poseidon::hash::Inputs) {
259 self.to_inner().to_inputs(inputs)
260 }
261 }
262
263 impl $unchecked {
264 pub fn to_checked<F: FieldWitness>(&self) -> $name<F> {
265 $name::from_inner(*self)
266 }
267 }
268
269 impl<F: FieldWitness> ForZkappCheck<F> for $unchecked {
270 type CheckedType = $name<F>;
271 fn checked_from_field(field: F) -> Self::CheckedType {
272 Self::CheckedType::from_field(field)
273 }
274 fn lte(this: &Self::CheckedType, other: &Self::CheckedType, w: &mut Witness<F>) -> Boolean {
275 Self::CheckedType::lte(this, other, w)
276 }
277 }
278 )*)
279}
280
281impl_nat!(
282 {CheckedTxnVersion, TxnVersion},
283 {CheckedSlot, Slot},
284 {CheckedSlotSpan, SlotSpan},
285 {CheckedLength, Length},
286 {CheckedNonce, Nonce},
287 {CheckedIndex, Index},
288 {CheckedBlockTime, BlockTime},
289 {CheckedBlockTimeSpan, BlockTimeSpan}
290);
291
292#[derive(Clone, Debug)]
294pub struct CheckedN<F: FieldWitness>(F);
295
296impl<F: FieldWitness> CheckedNat<F, 64> for CheckedN<F> {
297 type Inner = crate::scan_state::currency::N;
298 fn to_field(&self) -> F {
299 self.0
300 }
301 fn from_field(field: F) -> Self {
302 Self(field)
303 }
304}
305
306impl<F: FieldWitness> ToFieldElements<F> for CheckedN<F> {
307 fn to_field_elements(&self, fields: &mut Vec<F>) {
308 let Self(this) = self;
309 this.to_field_elements(fields)
310 }
311}
312
313impl<F: FieldWitness> Check<F> for CheckedN<F> {
314 fn check(&self, w: &mut Witness<F>) {
315 range_check::<F, 64>(self.0, w);
316 }
317}
318
319#[derive(Clone, Debug)]
321pub struct CheckedN32<F: FieldWitness>(F);
322
323impl<F: FieldWitness> CheckedNat<F, 32> for CheckedN32<F> {
324 type Inner = crate::scan_state::currency::N;
325 fn to_field(&self) -> F {
326 self.0
327 }
328 fn from_field(field: F) -> Self {
329 Self(field)
330 }
331}
332
333impl<F: FieldWitness> ToFieldElements<F> for CheckedN32<F> {
334 fn to_field_elements(&self, fields: &mut Vec<F>) {
335 let Self(this) = self;
336 this.to_field_elements(fields)
337 }
338}
339
340impl<F: FieldWitness> Check<F> for CheckedN32<F> {
341 fn check(&self, w: &mut Witness<F>) {
342 range_check::<F, 32>(self.0, w);
343 }
344}
345
346impl<F: FieldWitness> CheckedN32<F> {
347 pub fn constant(n: usize) -> Self {
348 let n: u32 = n.try_into().unwrap();
349 Self::from_field(n.into())
350 }
351}