1use crate::{
2 proofs::field::CircuitVar,
3 scan_state::currency::{self, Amount, Balance, Fee, Magnitude, MinMax, Sgn, Signed},
4};
5use std::{cell::Cell, cmp::Ordering::Less};
6
7use crate::{
8 proofs::{
9 field::{field, Boolean, FieldWitness},
10 to_field_elements::ToFieldElements,
11 transaction::Check,
12 witness::Witness,
13 },
14 ToInputs,
15};
16
17use super::common::{range_check, ForZkappCheck};
18
19#[derive(Debug)]
20pub enum RangeCheckFlaggedKind {
21 Add,
22 Sub,
23 AddOrSub,
24}
25
26fn modulus_as_field_64_bits<F: FieldWitness>() -> F {
27 fn modulus_as_field_impl<F: FieldWitness>() -> F {
28 let mut one = F::one();
29 let two: F = 2u64.into();
30
31 for _ in 0..64 {
32 one *= two;
33 }
34
35 one
36 }
37 cache!(F, modulus_as_field_impl::<F>())
38}
39
40fn double_modulus_as_field_64_bits<F: FieldWitness>() -> F {
41 cache!(F, modulus_as_field_64_bits::<F>().double())
42}
43
44#[derive(Clone)]
45pub struct CheckedSigned<F, T>
46where
47 F: FieldWitness,
48 T: CheckedCurrency<F>,
49{
50 pub magnitude: T,
51 pub sgn: CircuitVar<Sgn>,
52 pub value: Cell<Option<F>>,
53}
54
55impl<F: std::fmt::Debug, T: std::fmt::Debug> std::fmt::Debug for CheckedSigned<F, T>
56where
57 F: FieldWitness,
58 T: CheckedCurrency<F>,
59{
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 f.debug_struct("CheckedSigned")
62 .field("magnitude", &self.magnitude)
63 .field("sgn", &self.sgn)
64 .field("value", &self.value)
65 .finish()
66 }
67}
68
69impl<F, T> CheckedSigned<F, T>
70where
71 F: FieldWitness + std::fmt::Debug,
72 T: CheckedCurrency<F> + std::fmt::Debug,
73{
74 pub fn create(magnitude: T, sgn: CircuitVar<Sgn>, value: Option<F>) -> Self {
75 Self {
76 magnitude,
77 sgn,
78 value: Cell::new(value),
79 }
80 }
81
82 pub fn of_unsigned(magnitude: T) -> Self {
83 let value = magnitude.to_field();
84 Self {
85 magnitude,
86 sgn: CircuitVar::Constant(Sgn::Pos),
87 value: Cell::new(Some(value)),
88 }
89 }
90
91 pub fn zero() -> Self {
92 Self {
93 magnitude: T::zero(),
94 sgn: CircuitVar::Constant(Sgn::Pos),
95 value: Cell::new(None),
96 }
97 }
98
99 pub fn constant_zero() -> Self {
102 Self {
103 magnitude: T::zero(),
104 sgn: CircuitVar::Constant(Sgn::Pos),
105 value: Cell::new(Some(T::zero().to_field())),
106 }
107 }
108
109 pub fn negate(self) -> Self {
110 Self {
111 magnitude: self.magnitude,
112 sgn: self.sgn.map(|sgn| sgn.negate()),
113 value: Cell::new(self.value.get().map(|f| f.neg())),
114 }
115 }
116
117 pub fn is_neg(&self) -> Boolean {
118 match self.sgn.value() {
119 Sgn::Pos => Boolean::False,
120 Sgn::Neg => Boolean::True,
121 }
122 }
123
124 pub fn is_pos(&self) -> Boolean {
125 match self.sgn.value() {
126 Sgn::Pos => Boolean::True,
127 Sgn::Neg => Boolean::False,
128 }
129 }
130
131 pub fn value(&self, w: &mut Witness<F>) -> F {
132 match self.value.get() {
133 Some(x) => x,
134 None => {
135 let sgn: F = self.sgn.value().to_field();
136 let magnitude: F = self.magnitude.to_field();
137 let value = w.exists_no_check(magnitude * sgn);
138 self.value.replace(Some(value));
139 value
140 }
141 }
142 }
143
144 pub fn force_value(&self) -> F {
145 match self.value.get() {
146 Some(x) => x,
147 None => {
148 let sgn: F = self.sgn.value().to_field();
149 let magnitude: F = self.magnitude.to_field();
150 magnitude * sgn
151 }
152 }
153 }
154
155 pub fn set_value(&self) {
156 match self.value.get() {
157 Some(_) => {}
158 None => {
159 let sgn: F = self.sgn.value().to_field();
160 let magnitude: F = self.magnitude.to_field();
161 self.value.replace(Some(magnitude * sgn));
162 }
163 }
164 }
165
166 pub fn try_get_value(&self) -> Option<F> {
167 self.value.get()
168 }
169
170 fn unchecked(&self) -> currency::Signed<T::Inner> {
171 currency::Signed {
172 magnitude: self.magnitude.to_inner(),
173 sgn: *self.sgn.value(),
174 }
175 }
176
177 pub fn add_flagged(&self, y: &Self, w: &mut Witness<F>) -> (Self, Boolean)
178 where
179 T::Inner: Ord,
180 {
181 let x = self;
182
183 let xv = x.value(w);
184 let yv = y.value(w);
185
186 let sgn = w.exists({
187 let x = x.unchecked();
188 let y = y.unchecked();
189 match x.add(&y) {
190 Some(r) => r.sgn,
191 None => match (x.sgn, y.sgn) {
192 (Sgn::Neg, Sgn::Neg) => Sgn::Neg,
193 _ => Sgn::Pos,
194 },
195 }
196 });
197
198 let value = xv + yv;
199 let magnitude = field::mul(sgn.to_field(), value, w);
200
201 let (res_magnitude, overflow) =
202 T::range_check_flagged(RangeCheckFlaggedKind::AddOrSub, magnitude, w);
203
204 let res_value = field::mul(sgn.to_field(), magnitude, w);
205
206 let res = Self {
207 magnitude: res_magnitude,
208 sgn: CircuitVar::Var(sgn),
209 value: Cell::new(Some(res_value)),
210 };
211 (res, overflow)
212 }
213
214 pub fn add(&self, y: &Self, w: &mut Witness<F>) -> Self
215 where
216 T::Inner: Ord,
217 {
218 let x = self;
219
220 let xv: F = x.value(w);
221 let yv: F = y.value(w);
222
223 let sgn = w.exists({
224 let x = x.unchecked();
225 let y = y.unchecked();
226 x.add(&y).map(|r| r.sgn).unwrap_or(Sgn::Pos)
227 });
228
229 let res_value = w.exists(xv + yv);
230 let magnitude = w.exists(sgn.to_field::<F>() * res_value);
231
232 range_check::<F, CURRENCY_NBITS>(magnitude, w);
233
234 Self::create(
235 T::from_field(magnitude),
236 CircuitVar::Var(sgn),
237 Some(res_value),
238 )
239 }
240
241 pub fn equal(&self, other: &Self, w: &mut Witness<F>) -> Boolean {
242 let t1 = self.value(w);
243 let t2 = other.value(w);
244 field::equal(t1, t2, w)
245 }
246
247 pub fn const_equal(&self, other: &Self, w: &mut Witness<F>) -> Boolean {
248 let t1 = self.value(w);
249 let t2 = other.value(w);
250 field::equal(t1, t2, w)
251 }
252}
253
254const CURRENCY_NBITS: usize = 64;
255
256pub trait CheckedCurrency<F: FieldWitness>:
257 Sized + ToFieldElements<F> + Check<F> + std::fmt::Debug
258{
259 type Inner: MinMax + Magnitude + std::fmt::Debug;
260
261 fn to_field(&self) -> F;
262 fn from_field(field: F) -> Self;
263
264 fn zero() -> Self {
265 Self::from_field(F::zero())
266 }
267
268 fn to_inner(&self) -> Self::Inner {
269 Self::Inner::of_field(self.to_field())
270 }
271
272 fn from_inner(inner: Self::Inner) -> Self {
273 Self::from_field(inner.to_field())
274 }
275
276 fn min() -> Self {
277 Self::from_inner(Self::Inner::min())
278 }
279 fn max() -> Self {
280 Self::from_inner(Self::Inner::max())
281 }
282
283 fn modulus_as_field() -> F {
284 modulus_as_field_64_bits::<F>()
285 }
286
287 fn double_modulus_as_field() -> F {
288 double_modulus_as_field_64_bits::<F>()
289 }
290
291 fn equal(&self, other: &Self, w: &mut Witness<F>) -> Boolean {
292 field::equal(self.to_field(), other.to_field(), w)
293 }
294
295 fn sub_flagged(&self, y: &Self, w: &mut Witness<F>) -> (Self, Boolean) {
296 let (x, y) = (self.to_field(), y.to_field());
297 let z = w.exists(x - y);
298 Self::range_check_flagged(RangeCheckFlaggedKind::Sub, z, w)
299 }
300
301 fn sub_or_zero(&self, y: &Self, w: &mut Witness<F>) -> Self {
302 let (res, underflow) = self.sub_flagged(y, w);
303 w.exists_no_check(match underflow {
304 Boolean::True => Self::zero(),
305 Boolean::False => res,
306 })
307 }
308
309 fn range_check_flagged(
310 kind: RangeCheckFlaggedKind,
311 t: F,
312 w: &mut Witness<F>,
313 ) -> (Self, Boolean) {
314 use RangeCheckFlaggedKind::{Add, AddOrSub, Sub};
315
316 let adjustment_factor = w.exists(match &kind {
317 Add => {
318 if let Less = t.cmp(&Self::modulus_as_field()) {
319 F::zero()
320 } else {
321 F::one().neg()
322 }
323 }
324 Sub => {
325 if let Less = t.cmp(&Self::modulus_as_field()) {
326 F::zero()
327 } else {
328 F::one()
329 }
330 }
331 AddOrSub => {
332 if let Less = t.cmp(&Self::modulus_as_field()) {
333 F::zero()
334 } else if let Less = t.cmp(&Self::double_modulus_as_field()) {
335 F::one().neg()
336 } else {
337 F::one()
338 }
339 }
340 });
341
342 let out_of_range = match kind {
343 Add => Boolean::of_field(adjustment_factor.neg()),
344 Sub => Boolean::of_field(adjustment_factor),
345 AddOrSub => Boolean::of_field(field::mul(adjustment_factor, adjustment_factor, w)),
346 };
347 let t_ajusted: F = t + (adjustment_factor * Self::modulus_as_field());
348 w.exists(t_ajusted);
349 range_check::<F, CURRENCY_NBITS>(t_ajusted, w);
350 (Self::from_field(t_ajusted), out_of_range)
351 }
352
353 fn lt(&self, y: &Self, w: &mut Witness<F>) -> Boolean {
356 let diff: F = w.exists(self.to_field() - y.to_field());
357 let (_res, lt) = Self::range_check_flagged(RangeCheckFlaggedKind::Sub, diff, w);
358 lt
359 }
360
361 fn lte(&self, y: &Self, w: &mut Witness<F>) -> Boolean {
364 let y_lt_x = y.lt(self, w);
365 y_lt_x.neg()
366 }
367
368 fn gte(&self, y: &Self, w: &mut Witness<F>) -> Boolean {
371 y.lte(self, w)
372 }
373
374 fn gt(&self, y: &Self, w: &mut Witness<F>) -> Boolean {
377 y.lt(self, w)
378 }
379
380 fn add_signed(&self, d: CheckedSigned<F, Self>, w: &mut Witness<F>) -> Self {
381 let t = self.to_field();
382 let d = d.value(w);
383 let res = w.exists(t + d);
384 range_check::<F, CURRENCY_NBITS>(res, w);
385 Self::from_field(res)
386 }
387
388 fn add_signed_flagged(&self, d: CheckedSigned<F, Self>, w: &mut Witness<F>) -> (Self, Boolean) {
389 let t = self.to_field();
390 let d = d.value(w);
391 let res = w.exists(t + d);
392 let (res, overflow) = Self::range_check_flagged(RangeCheckFlaggedKind::AddOrSub, res, w);
393 (res, overflow)
394 }
395
396 fn const_add_flagged(&self, y: &Self, w: &mut Witness<F>) -> (Self, Boolean) {
398 let x = self;
399 let z: F = x.to_field() + y.to_field();
400 Self::range_check_flagged(RangeCheckFlaggedKind::Add, z, w)
401 }
402
403 fn add_flagged(&self, y: &Self, w: &mut Witness<F>) -> (Self, Boolean) {
405 let x = self;
406 let z: F = w.exists(x.to_field() + y.to_field());
407 Self::range_check_flagged(RangeCheckFlaggedKind::Add, z, w)
408 }
409
410 fn sub(&self, y: &Self, w: &mut Witness<F>) -> Self {
411 let x = self.to_field();
412 let y = y.to_field();
413
414 let res = w.exists(x - y);
415 range_check::<F, { CURRENCY_NBITS }>(res, w);
416
417 Self::from_field(res)
418 }
419}
420
421#[derive(Copy, Clone, Debug)]
422pub struct CheckedAmount<F: FieldWitness>(F);
423#[derive(Copy, Clone, Debug)]
424pub struct CheckedFee<F: FieldWitness>(F);
425#[derive(Copy, Clone, Debug)]
426pub struct CheckedBalance<F: FieldWitness>(F);
427
428impl<F: FieldWitness> CheckedBalance<F> {
429 pub fn add_signed_amount(
430 &self,
431 d: CheckedSigned<F, CheckedAmount<F>>,
432 w: &mut Witness<F>,
433 ) -> Self {
434 let d = CheckedSigned::<F, Self>::create(Self(d.magnitude.0), d.sgn, d.value.get());
435 self.add_signed(d, w)
436 }
437
438 pub fn add_amount_flagged(&self, y: &CheckedAmount<F>, w: &mut Witness<F>) -> (Self, Boolean) {
439 let y = Self(y.0);
440 self.add_flagged(&y, w)
441 }
442
443 pub fn sub_amount_or_zero(&self, y: &CheckedAmount<F>, w: &mut Witness<F>) -> Self {
444 let y = Self(y.0);
445 self.sub_or_zero(&y, w)
446 }
447
448 pub fn sub_amount_flagged(&self, y: &CheckedAmount<F>, w: &mut Witness<F>) -> (Self, Boolean) {
449 let y = Self(y.0);
450 self.sub_flagged(&y, w)
451 }
452
453 pub fn add_signed_amount_flagged(
454 &self,
455 amount: CheckedSigned<F, CheckedAmount<F>>,
456 w: &mut Witness<F>,
457 ) -> (Self, Boolean) {
458 let amount = CheckedSigned::<F, Self>::create(
459 Self(amount.magnitude.0),
460 amount.sgn,
461 amount.value.get(),
462 );
463 self.add_signed_flagged(amount, w)
464 }
465}
466
467impl<F: FieldWitness> CheckedAmount<F> {
468 pub fn of_fee(fee: &CheckedFee<F>) -> Self {
469 Self(fee.0)
470 }
471}
472
473impl<F: FieldWitness> CheckedSigned<F, CheckedAmount<F>> {
474 pub fn to_fee(&self) -> CheckedSigned<F, CheckedFee<F>> {
475 CheckedSigned {
476 magnitude: CheckedFee(self.magnitude.0),
477 sgn: self.sgn,
478 value: self.value.clone(),
479 }
480 }
481}
482
483macro_rules! impl_currency {
484 ($({$name:tt, $unchecked:tt}),*) => ($(
485 impl<F: FieldWitness> CheckedCurrency<F> for $name::<F> {
486 type Inner = $unchecked;
487 fn to_field(&self) -> F {
488 self.0
489 }
490 fn from_field(field: F) -> Self {
491 Self(field)
492 }
493 }
494
495 impl<F: FieldWitness> ToFieldElements<F> for $name::<F> {
496 fn to_field_elements(&self, fields: &mut Vec<F>) {
497 self.0.to_field_elements(fields)
498 }
499 }
500
501 impl<F: FieldWitness> Check<F> for $name::<F> {
502 fn check(&self, w: &mut Witness<F>) {
503 range_check::<F, { CURRENCY_NBITS }>(self.0, w);
504 }
505 }
506
507 impl<F: FieldWitness> ToInputs for $name<F> {
508 fn to_inputs(&self, inputs: &mut poseidon::hash::Inputs) {
509 self.to_inner().to_inputs(inputs)
510 }
511 }
512
513 impl $unchecked {
514 pub fn to_checked<F: FieldWitness>(&self) -> $name<F> {
515 $name::from_inner(*self)
516 }
517 }
518
519 impl Signed<$unchecked> {
520 pub fn to_checked<F: FieldWitness>(&self) -> CheckedSigned<F, $name<F>> {
521 CheckedSigned {
522 magnitude: self.magnitude.to_checked(),
523 sgn: CircuitVar::Var(self.sgn),
524 value: Cell::new(None),
525 }
526 }
527 }
528
529 impl<F: FieldWitness> ForZkappCheck<F> for $unchecked {
530 type CheckedType = $name<F>;
531 fn checked_from_field(field: F) -> Self::CheckedType {
532 Self::CheckedType::from_field(field)
533 }
534 fn lte(this: &Self::CheckedType, other: &Self::CheckedType, w: &mut Witness<F>) -> Boolean {
535 Self::CheckedType::lte(this, other, w)
536 }
537 }
538 )*)
539}
540
541impl_currency!(
542 {CheckedAmount, Amount},
543 {CheckedFee, Fee},
544 {CheckedBalance, Balance}
545);