pragma circom 2.2.0; include "field_params.circom"; include "goldilocks_func.circom"; include "misc.circom"; //------------------------------------------------------------------------------ // a type for bit-decomposed numbers // the invariant to respect is that the value is in the range [0..2^n-1] // bus Binary(n) { signal val; signal bits[n]; } // the type of Goldilocks field elements. // the invariant to respect is that the value is in the range [0..P-1] // bus Goldilocks() { signal val; } //------------------------------------------------------------------------------ // n-bit binary decomposition template ToBinary(n) { signal input inp; output Binary(n) out; component tobits = ToBits(n); tobits.inp <== inp; tobits.out ==> out.bits; inp ==> out.val; } //------------------------------------------------------------------------------ // // do a range check for [0,P-1] where `P = 2^64 - 2^32 + 1` // template ToGoldilocks() { signal input inp; output Goldilocks() out; // first we do a range check and bit decomposition to 64 bits var nbits = SolinasExpoBig(); Binary(nbits) bin <== ToBinary(nbits)( inp ); // compute the low and high 32 bit words var sum_lo = 0; for(var i=0; i < SolinasExpoSmall(); i++) { sum_lo += (2**i) * bin.bits[i]; } var expo_jump = SolinasExpoBig() - SolinasExpoSmall(); var sum_hi = 0; for(var i=0; i < expo_jump; i++) { sum_hi += (2**i) * bin.bits[ SolinasExpoSmall() + i ]; } // now, since we have p-1 = 2^64 - 2^32 = 0xff...ff00...00 // // we need to check that IF hi == 2^32-1 THEN lo == 0 signal iseq <== IsEqual()( sum_hi , 2**expo_jump - 1 ); iseq * sum_lo === 0; // either (hi < 2^32-1) OR (lo == 0) out.val <== bin.val; } //------------------------------------------------------------------------------ // reduce numbers in the range `0 <= x < 2^k * P` modulo P template ReduceModP(k) { signal input inp; output Goldilocks() out; var P = FieldPrime(); signal quot <-- inp \ P; signal rem <-- inp % P; // check that `quot` is in a `k` bit range! component bits = ToBits( k ); bits.inp <== quot; inp === rem + P * quot; // check the multiplication equation out <== ToGoldilocks()(rem); // range check on the output } //------------------------------------------------------------------------------ // // negation in the Goldilocks field // template Neg() { input Goldilocks() A; output Goldilocks() C; signal isz <== IsZero()( A.val ); C.val <== (1 - isz) * (FieldPrime() - A.val); } //-------------------------------------- // // addition in the Goldilocks field // template Add() { input Goldilocks() A,B; output Goldilocks() C; var P = FieldPrime(); // A + B = C + P * bit var overflow = (A.val + B.val >= P); signal quot <-- (overflow ? 1 : 0); signal rem <-- A.val + B.val - quot * P; quot*(1-quot) === 0; // `quot` must be either zero or one A.val + B.val === rem + P * quot; // check the addition C <== ToGoldilocks()(rem); // range check on C } // // subtraction in the Goldilocks field // template Sub() { input Goldilocks() A,B; output Goldilocks() C; var P = FieldPrime(); // A - B = C - P * bit var overflow = (A.val - B.val < 0); signal aquot <-- (overflow ? 1 : 0); // absolute value of the quotient signal rem <-- A.val - B.val + aquot * P; aquot*(1-aquot) === 0; // `aquot` must be either zero or one A.val - B.val === rem - P * aquot; // check the subtraction C <== ToGoldilocks()(rem); // range check on C } //------------------------------------------------------------------------------ // // summation in the Goldilocks field // template Sum(k) { input Goldilocks() A[k]; output Goldilocks() C; // sum A[i] = C + P * Q // since all A[i] < 2^64, Q will have at most as many bits as `k` have // so we can do a simple binary range-check on Q var sum = 0; for(var i=0; i