From bc2ad1620c2f01e8cbd0cb8b0dc2d8ac678e83f4 Mon Sep 17 00:00:00 2001 From: Khashayar Barooti Date: Tue, 24 Dec 2024 15:12:25 +0000 Subject: [PATCH 1/7] added from field method --- export/test_add_BN.json | 1 + src/bignum.nr | 17 ++++++++++- src/fns/constrained_ops.nr | 21 +++++++++++++- src/fns/unconstrained_helpers.nr | 19 +++++++++---- src/tests/bignum_test.nr | 9 ++++++ src/utils/split_bits.nr | 19 +++++++++++++ src/utils/u60_representation.nr | 49 ++++++++++++++++++++++++++++++++ 7 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 export/test_add_BN.json diff --git a/export/test_add_BN.json b/export/test_add_BN.json new file mode 100644 index 00000000..58b84a13 --- /dev/null +++ b/export/test_add_BN.json @@ -0,0 +1 @@ +{"noir_version":"0.36.0+801c71880ecf8386a26737a5d8bb5b4cb164b2ab","hash":1828426501882327147,"abi":{"parameters":[],"return_type":null,"error_types":{}},"bytecode":"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","debug_symbols":"zZJNCsMgFITv8tYuoml+r1JK0ESDIBrUFIp495o2bQPNAdzNvPkYZvECTJyt8yC1MA76awBlRuql0ckFKF4nt1C9Oeep9dC35IKA62lTXUQgpOJJNxH9oRhXpNvhpNvii2NSnPGkrqsPT+ruV9+U8YYAZ7aHZLanzGpPMsxKpeQ8HL8qne/USsoU361Y9XhI/WN5J6niCQ==","file_map":{"73":{"source":"use crate::params::BigNumParams as P;\n\nuse crate::fns::{\n expressions::evaluate_quadratic_expression,\n unconstrained_helpers::{\n __add_with_flags, __neg_with_flags, __sub_with_flags, __validate_gt_remainder,\n __validate_in_field_compute_borrow_flags,\n }, unconstrained_ops::{__div, __mul, __udiv_mod},\n};\n\n/**\n * In this file:\n *\n * conditional_select\n * assert_is_not_equal\n * eq\n * validate_in_field\n * validate_in_range\n * validate_quotient_in_range\n * validate_gt\n * neg\n * add\n * sub\n * mul\n * div\n * udiv_mod\n * udiv\n * umod\n */\n\n/**\n* @brief given an input seed, generate a pseudorandom BigNum value\n* @details we hash the input seed into `modulus_bits * 2` bits of entropy,\n* which is then reduced into a BigNum value\n* We use a hash function that can be modelled as a random oracle\n* This function *should* produce an output that is a uniformly randomly distributed value modulo BigNum::modulus()\n**/\npub(crate) fn derive_from_seed(\n params: P,\n seed: [u8; SeedBytes],\n) -> [Field; N] {\n let mut rolling_seed: [u8; SeedBytes + 1] = [0; SeedBytes + 1];\n for i in 0..SeedBytes {\n rolling_seed[i] = seed[i];\n assert_eq(rolling_seed[i], seed[i]);\n }\n\n let mut hash_buffer: [u8; N * 2 * 15] = [0; N * 2 * 15];\n\n let mut rolling_hash_fields: [Field; (SeedBytes / 31) + 1] = [0; (SeedBytes / 31) + 1];\n let mut seed_ptr = 0;\n for i in 0..(SeedBytes / 31) + 1 {\n let mut packed: Field = 0;\n for _ in 0..31 {\n if (seed_ptr < SeedBytes) {\n packed *= 256;\n packed += seed[seed_ptr] as Field;\n seed_ptr += 1;\n }\n }\n rolling_hash_fields[i] = packed;\n }\n\n let compressed =\n std::hash::poseidon2::Poseidon2::hash(rolling_hash_fields, (SeedBytes / 31) + 1);\n let mut rolling_hash: [Field; 2] = [compressed, 0];\n\n let num_hashes = (240 * N) / 254 + (((30 * N) % 32) != 0) as u32;\n for i in 0..num_hashes - 1 {\n let hash: Field = std::hash::poseidon2::Poseidon2::hash(rolling_hash, 2);\n let hash: [u8; 32] = hash.to_le_bytes();\n for j in 0..30 {\n hash_buffer[i * 30 + j] = hash[j];\n }\n rolling_hash[1] += 1;\n }\n\n {\n let hash: Field = std::hash::poseidon2::Poseidon2::hash(rolling_hash, 2);\n let hash: [u8; 32] = hash.to_le_bytes();\n let remaining_bytes = 30 * N - (num_hashes - 1) * 30;\n for j in 0..remaining_bytes {\n hash_buffer[(num_hashes - 1) * 30 + j] = hash[j];\n }\n }\n\n let num_bits = MOD_BITS * 2;\n let num_bytes = num_bits / 8 + ((num_bits % 8) != 0) as u32;\n\n let bits_in_last_byte = num_bits as u8 % 8;\n let last_byte_mask = (1 as u8 << bits_in_last_byte) - 1;\n hash_buffer[num_bytes - 1] = hash_buffer[num_bytes - 1] & last_byte_mask;\n\n let num_bigfield_chunks = (2 * N) / (N - 1) + (((2 * N) % (N - 1)) != 0) as u32;\n let mut byte_ptr = 0;\n\n // we want to convert our byte array into bigfield chunks\n // each chunk has at most N-1 limbs\n // to determine the exact number of chunks, we need the `!=` or `>` operator which is not avaiable when defining array sizes\n // so we overestimate at 4\n // e.g. if N = 20, then we have 40 limbs we want to reduce, but each bigfield chunk is 19 limbs, so we need 3\n // if N = 2, we have 4 limbs we want to reduce but each bigfield chunk is only 1 limb, so we need 4\n // max possible number of chunks is therefore 4\n let mut bigfield_chunks: [[Field; N]; 4] = [[0; N]; 4];\n for k in 0..num_bigfield_chunks {\n let mut bigfield_limbs: [Field; N] = [0; N];\n let mut num_filled_bytes = (k * 30);\n let mut num_remaining_bytes = num_bytes - num_filled_bytes;\n let mut num_remaining_limbs =\n (num_remaining_bytes / 15) + (num_remaining_bytes % 15 > 0) as u32;\n let mut more_than_N_minus_one_limbs = (num_remaining_limbs > (N - 1)) as u32;\n let mut num_limbs_in_bigfield = more_than_N_minus_one_limbs * (N - 1)\n + num_remaining_limbs * (1 - more_than_N_minus_one_limbs);\n\n for j in 0..num_limbs_in_bigfield {\n let mut limb: Field = 0;\n for _ in 0..15 {\n let need_more_bytes = (byte_ptr < num_bytes);\n let mut byte = hash_buffer[byte_ptr];\n limb *= (256 * need_more_bytes as Field + (1 - need_more_bytes as Field));\n limb += byte as Field * need_more_bytes as Field;\n byte_ptr += need_more_bytes as u32;\n }\n bigfield_limbs[num_limbs_in_bigfield - 1 - j] = limb;\n }\n bigfield_chunks[num_bigfield_chunks - 1 - k] = bigfield_limbs;\n }\n\n let mut bigfield_rhs_limbs: [Field; N] = [0; N];\n bigfield_rhs_limbs[N - 1] = 1;\n validate_in_range::<_, MOD_BITS>(bigfield_rhs_limbs);\n\n let mut result: [Field; N] = [0; N];\n\n for i in 0..num_bigfield_chunks {\n let bigfield_lhs_limbs = bigfield_chunks[i];\n\n result = mul(params, result, bigfield_rhs_limbs);\n result = add(params, result, bigfield_lhs_limbs);\n }\n result\n}\n\n/**\n* @brief conditional_select given the value of `predicate` return either `self` (if 0) or `other` (if 1)\n* @description should be cheaper than using an IF statement (TODO: check!)\n**/\npub(crate) fn conditional_select(\n lhs: [Field; N],\n rhs: [Field; N],\n predicate: bool,\n) -> [Field; N] {\n let mut result: [Field; N] = lhs;\n for i in 0..N {\n result[i] = (lhs[i] - rhs[i]) * predicate as Field + rhs[i];\n }\n result\n}\n\n/**\n * @brief Validate self != other\n * @details If A == B, then A == B mod N.\n * We can efficiently evaluate A == B mod N where N = circuit modulus\n * This method is *sound*, but not *complete* (i.e. A != B but A == B mod N)\n * However the probability of an honest Prover being unable to satisfy this check is tiny!\n * (todo: compute how tiny)\n **/\npub(crate) fn assert_is_not_equal(\n params: P,\n lhs: [Field; N],\n rhs: [Field; N],\n) {\n let mut l: Field = 0;\n let mut r: Field = 0;\n let mut modulus_mod_n: Field = 0;\n let two_pow_120: Field = 0x1000000000000000000000000000000;\n let modulus = params.modulus;\n for i in 0..N {\n l *= two_pow_120;\n r *= two_pow_120;\n modulus_mod_n *= two_pow_120;\n l += lhs[N - i - 1];\n r += rhs[N - i - 1];\n modulus_mod_n += modulus[N - i - 1];\n }\n\n // lhs can be either X mod N or P + X mod N\n // rhs can be either Y mod N or P + Y mod N\n // If lhs - rhs = 0 mod P then lhs - rhs = 0, P or -P mod N\n let mut diff = l - r;\n let mut target = diff * (diff + modulus_mod_n) * (diff - modulus_mod_n);\n assert(target != 0, \"asssert_is_not_equal fail\");\n}\n\npub(crate) fn eq(\n params: P,\n lhs: [Field; N],\n rhs: [Field; N],\n) -> bool {\n let diff = sub::<_, MOD_BITS>(params, lhs, rhs);\n // if self == other, possible values of `diff` will be `p` or `0`\n // (the subtract operator constrains diff to be < ceil(log(p)))\n // TODO: can do this more efficiently via witngen in unconstrained functions?\n let mut is_equal_modulus: bool = true;\n let mut is_equal_zero: bool = true;\n for i in 0..N {\n is_equal_modulus = is_equal_modulus & (diff[i] == params.modulus[i]);\n is_equal_zero = is_equal_zero & (diff[i] == 0);\n }\n is_equal_modulus | is_equal_zero\n}\n\npub(crate) fn validate_in_field(\n params: P,\n val: [Field; N],\n) {\n // N.B. need to combine with validate_in_range if `self` limbs have not been range constrained\n let mut p_minus_self: [Field; N] = [0; N];\n let modulus: [Field; N] = params.modulus;\n for i in 0..N {\n p_minus_self[i] = modulus[i] - val[i];\n }\n let borrow_flags = unsafe { __validate_in_field_compute_borrow_flags(params, val) };\n let two_pow_120: Field = 0x1000000000000000000000000000000;\n p_minus_self[0] += borrow_flags[0] as Field * two_pow_120;\n for i in 1..N - 1 {\n p_minus_self[i] += (borrow_flags[i] as Field * two_pow_120 - borrow_flags[i - 1] as Field);\n }\n p_minus_self[N - 1] -= borrow_flags[N - 2] as Field;\n let mut compare = val;\n compare = p_minus_self;\n validate_in_range::<_, MOD_BITS>(compare);\n}\n\n/**\n* @brief Validate a BigNum instance is correctly range constrained to contain no more than Params::modulus_bits()\n**/\npub(crate) fn validate_in_range(limbs: [Field; N]) {\n for i in 0..(N - 1) {\n limbs[i].assert_max_bit_size::<120>();\n }\n limbs[N - 1].assert_max_bit_size::();\n}\n\n/**\n* @brief validate quotient produced from `evaluate_quadratic_expression` is well-formed\n* @description because the inputs into evaluate_quadratic_expression may cause the quotient to extend beyond `Params::modulus_bits`.\n* We allow the quotient to extend 6 bits beyond Params::modulus_bits()\n* Why is this?\n* several factors: 1. quotient * modulus , limbs cannot overflow field boundary (254 bits)\n* 2. in `evaluate_quadratic_expression`, we require that for `expression - quotient * modulus`,\n* limbs cannot exceed 246 bits (246 magic number due to a higher number adding extra range check gates)\n* because of factor 2 and the fact that modulus limbs are 120 bits, quotient limbs cannot be >126 bits\n*\n* Note: doesn't this mean that final_limb_bits should be constrained to be 126 bits, not modulus_bits() - ((N - 1) * 120) + 6?\n* TODO: think about this more! we want the range constraint we apply to be as small as allowable as this is more efficient\n**/\npub(crate) fn validate_quotient_in_range(limbs: [Field; N]) {\n for i in 0..(N) {\n limbs[i].assert_max_bit_size::<120>();\n }\n // Note: replace magic number 6 with definition\n limbs[N - 1].assert_max_bit_size::();\n}\n\n// validate that lhs - rhs does not underflow i.e. lhs > rhs\npub(crate) fn validate_gt(lhs: [Field; N], rhs: [Field; N]) {\n // so we do... p - x - r = 0 and there might be borrow flags\n // a - b = r\n // p + a - b - r = 0\n let (result, carry_flags, borrow_flags) = unsafe { __validate_gt_remainder(lhs, rhs) };\n validate_in_range::<_, MOD_BITS>(result);\n\n let borrow_shift = 0x1000000000000000000000000000000;\n let carry_shift = 0x1000000000000000000000000000000;\n\n let mut addend: [Field; N] = [0; N];\n let result_limb = lhs[0] - rhs[0] + addend[0] - result[0] - 1\n + (borrow_flags[0] as Field * borrow_shift)\n - (carry_flags[0] as Field * carry_shift);\n assert(result_limb == 0);\n\n for i in 1..N - 1 {\n let result_limb = lhs[i] - rhs[i] + addend[i] - result[i] - borrow_flags[i - 1] as Field\n + carry_flags[i - 1] as Field\n + ((borrow_flags[i] as Field - carry_flags[i] as Field) * borrow_shift);\n assert(result_limb == 0);\n }\n\n let result_limb = lhs[N - 1] - rhs[N - 1] + addend[N - 1]\n - result[N - 1]\n - borrow_flags[N - 2] as Field\n + carry_flags[N - 2] as Field;\n assert(result_limb == 0);\n}\n\npub(crate) fn neg(\n params: P,\n val: [Field; N],\n) -> [Field; N] {\n // so we do... p - x - r = 0 and there might be borrow flags\n let (result, borrow_flags) = unsafe { __neg_with_flags(params, val) };\n validate_in_range::<_, MOD_BITS>(result);\n let modulus = params.modulus;\n let borrow_shift = 0x1000000000000000000000000000000;\n let result_limb = modulus[0] - val[0] - result[0] + (borrow_flags[0] as Field * borrow_shift);\n assert(result_limb == 0);\n for i in 1..N - 1 {\n let result_limb = modulus[i] - val[i] - result[i] - borrow_flags[i - 1] as Field\n + (borrow_flags[i] as Field * borrow_shift);\n assert(result_limb == 0);\n }\n let result_limb = modulus[N - 1] - val[N - 1] - result[N - 1] - borrow_flags[N - 2] as Field;\n assert(result_limb == 0);\n result\n}\n\npub(crate) fn add(\n params: P,\n lhs: [Field; N],\n rhs: [Field; N],\n) -> [Field; N] {\n // so we do... p - x - r = 0 and there might be borrow flags\n let (result, carry_flags, borrow_flags, overflow_modulus) =\n unsafe { __add_with_flags(params, lhs, rhs) };\n validate_in_range::<_, MOD_BITS>(result);\n let modulus = params.modulus;\n let borrow_shift = 0x1000000000000000000000000000000;\n let carry_shift = 0x1000000000000000000000000000000;\n\n let mut subtrahend: [Field; N] = [0; N];\n if (overflow_modulus) {\n subtrahend = modulus;\n }\n let result_limb = lhs[0] + rhs[0] - subtrahend[0] - result[0]\n + (borrow_flags[0] as Field * borrow_shift)\n - (carry_flags[0] as Field * carry_shift);\n assert(result_limb == 0);\n for i in 1..N - 1 {\n let result_limb = lhs[i] + rhs[i] - subtrahend[i] - result[i] - borrow_flags[i - 1] as Field\n + carry_flags[i - 1] as Field\n + ((borrow_flags[i] as Field - carry_flags[i] as Field) * borrow_shift);\n assert(result_limb == 0);\n }\n let result_limb = lhs[N - 1] + rhs[N - 1]\n - subtrahend[N - 1]\n - result[N - 1]\n - borrow_flags[N - 2] as Field\n + carry_flags[N - 2] as Field;\n assert(result_limb == 0);\n result\n}\n\npub(crate) fn sub(\n params: P,\n lhs: [Field; N],\n rhs: [Field; N],\n) -> [Field; N] {\n // so we do... p - x - r = 0 and there might be borrow flags\n // a - b = r\n // p + a - b - r = 0\n let (result, carry_flags, borrow_flags, underflow) =\n unsafe { __sub_with_flags(params, lhs, rhs) };\n validate_in_range::<_, MOD_BITS>(result);\n let modulus = params.modulus;\n let borrow_shift = 0x1000000000000000000000000000000;\n let carry_shift = 0x1000000000000000000000000000000;\n\n let mut addend: [Field; N] = [0; N];\n if (underflow) {\n addend = modulus;\n }\n let result_limb = lhs[0] - rhs[0] + addend[0] - result[0]\n + (borrow_flags[0] as Field * borrow_shift)\n - (carry_flags[0] as Field * carry_shift);\n assert(result_limb == 0);\n for i in 1..N - 1 {\n let result_limb = lhs[i] - rhs[i] + addend[i] - result[i] - borrow_flags[i - 1] as Field\n + carry_flags[i - 1] as Field\n + ((borrow_flags[i] as Field - carry_flags[i] as Field) * borrow_shift);\n assert(result_limb == 0);\n }\n let result_limb = lhs[N - 1] - rhs[N - 1] + addend[N - 1]\n - result[N - 1]\n - borrow_flags[N - 2] as Field\n + carry_flags[N - 2] as Field;\n assert(result_limb == 0);\n result\n}\n\n// Note: this method is expensive! Try to craft quadratic relations and directly evaluate them\n// via evaluate_quadratic_expression\n// e.g. performing a sum of multiple multiplications and additions via `evaluate_quadratic_expression`\n// will create much fewer constraints than calling `mul` and `add` directly\npub(crate) fn mul(\n params: P,\n lhs: [Field; N],\n rhs: [Field; N],\n) -> [Field; N] {\n let result = unsafe { __mul::<_, MOD_BITS>(params, lhs, rhs) };\n evaluate_quadratic_expression(\n params,\n [[lhs]],\n [[false]],\n [[rhs]],\n [[false]],\n [result],\n [true],\n );\n result\n}\n\n// Note: this method is expensive! Witness computation is extremely expensive as it requires modular exponentiation\npub(crate) fn div(\n params: P,\n lhs: [Field; N],\n rhs: [Field; N],\n) -> [Field; N] {\n assert(\n params.has_multiplicative_inverse,\n \"BigNum has no multiplicative inverse. Use udiv for unsigned integer division\",\n );\n let result = unsafe { __div::<_, MOD_BITS>(params, lhs, rhs) };\n evaluate_quadratic_expression(\n params,\n [[result]],\n [[false]],\n [[rhs]],\n [[false]],\n [lhs],\n [true],\n );\n result\n}\n\n/**\n* @brief udiv_mod performs integer division between numerator, divisor \n*\n* i.e. 1. floor(numerator / divisor) = quotient\n* 2. numerator % divisor = remainder\n* 3. divisor * quotient + remainder = numerator\n**/\npub(crate) fn udiv_mod(\n params: P,\n numerator: [Field; N],\n divisor: [Field; N],\n) -> ([Field; N], [Field; N]) {\n let (quotient, remainder) = unsafe { __udiv_mod(numerator, divisor) };\n\n // self / divisor = quotient rounded\n // quotient * divisor + remainder - self = 0\n evaluate_quadratic_expression(\n params,\n [[quotient]],\n [[false]],\n [[divisor]],\n [[false]],\n [numerator, remainder],\n [true, false],\n );\n // we need (remainder < divisor)\n // implies (divisor - remainder > 0)\n validate_gt::<_, MOD_BITS>(divisor, remainder);\n (quotient, remainder)\n}\n\n/**\n* @brief udiv_mod performs integer division between numerator, divisor \n*\n* i.e. return param is floor(numerator / divisor)\n**/\npub(crate) fn udiv(\n params: P,\n numerator: [Field; N],\n divisor: [Field; N],\n) -> [Field; N] {\n udiv_mod::<_, MOD_BITS>(params, numerator, divisor).0\n}\n\n/**\n* @brief udiv_mod performs integer modular reduction\n*\n* i.e. 1. numerator % divisor = return value\n**/\npub(crate) fn umod(\n params: P,\n numerator: [Field; N],\n divisor: [Field; N],\n) -> [Field; N] {\n udiv_mod::<_, MOD_BITS>(params, numerator, divisor).1\n}\n\n","path":"/Users/khashayarbarooti/aztec/noir-bignum-oracles/src/fns/constrained_ops.nr"},"87":{"source":"use crate::utils::u60_representation::U60Repr;\n\nuse crate::bignum::BigNum;\nuse crate::bignum::BigNumTrait;\n\nuse crate::params::BigNumParams;\nuse crate::params::BigNumParamsGetter;\n\nuse crate::fields::bls12_381Fq::BLS12_381_Fq_Params;\nuse crate::fields::bn254Fq::BN254_Fq_Params;\nuse crate::fields::U256::U256Params;\n\nstruct Test2048Params {}\n\n// See https://github.com/noir-lang/noir/issues/6172\n\ntype Fq = BigNum<3, 254, BN254_Fq_Params>;\ntype BN256 = BigNum<3, 257, U256Params>;\ntype BN381 = BigNum<4, 381, BLS12_381_Fq_Params>;\ntype BN2048 = BigNum<18, 2048, Test2048Params>;\n\n\n\n#[export]\nfn test_add_BN() {\n let mut a: Fq = BigNum::modulus();\n let mut b: Fq = BigNum::modulus();\n let mut expected: Fq = BigNum::modulus();\n\n a.limbs[0] -= 1;\n b.limbs[0] -= 1;\n expected.limbs[0] -= 2;\n\n let result = a + b;\n assert(result == expected);\n}\n","path":"/Users/khashayarbarooti/aztec/noir-bignum-oracles/src/benchmarks/bignum_bench.nr"},"120":{"source":"use crate::utils::map::map;\n\nuse crate::params::BigNumParamsGetter;\n\nuse crate::fns::{\n constrained_ops::{\n add, assert_is_not_equal, conditional_select, derive_from_seed, div, eq, mul, neg, sub,\n udiv, udiv_mod, umod, validate_in_field, validate_in_range,\n }, expressions::{__compute_quadratic_expression, evaluate_quadratic_expression},\n serialization::{from_be_bytes, to_le_bytes},\n unconstrained_ops::{\n __add, __batch_invert, __batch_invert_slice, __derive_from_seed, __div, __eq, __invmod,\n __is_zero, __mul, __neg, __pow, __sub, __tonelli_shanks_sqrt, __udiv_mod,\n },\n};\n\npub struct BigNum {\n pub limbs: [Field; N],\n}\n// We aim to avoid needing to add a generic parameter to this trait, for this reason we do not allow\n// accessing the limbs of the bignum except through slices.\npub trait BigNumTrait {\n // TODO: this crashes the compiler? v0.32\n // fn default() -> Self { std::default::Default::default () }\n pub fn new() -> Self;\n pub fn one() -> Self;\n pub fn derive_from_seed(seed: [u8; SeedBytes]) -> Self;\n pub unconstrained fn __derive_from_seed(seed: [u8; SeedBytes]) -> Self;\n pub fn from_slice(limbs: [Field]) -> Self;\n pub fn from_be_bytes(x: [u8; NBytes]) -> Self;\n pub fn to_le_bytes(self) -> [u8; NBytes];\n\n pub fn modulus() -> Self;\n pub fn modulus_bits(self) -> u32;\n pub fn num_limbs(self) -> u32;\n pub fn get_limbs_slice(self) -> [Field];\n pub fn get_limb(self, idx: u32) -> Field;\n pub fn set_limb(&mut self, idx: u32, value: Field);\n\n pub unconstrained fn __eq(self, other: Self) -> bool;\n pub unconstrained fn __is_zero(self) -> bool;\n\n pub unconstrained fn __neg(self) -> Self;\n pub unconstrained fn __add(self, other: Self) -> Self;\n pub unconstrained fn __sub(self, other: Self) -> Self;\n pub unconstrained fn __mul(self, other: Self) -> Self;\n pub unconstrained fn __div(self, other: Self) -> Self;\n pub unconstrained fn __udiv_mod(self, divisor: Self) -> (Self, Self);\n pub unconstrained fn __invmod(self) -> Self;\n pub unconstrained fn __pow(self, exponent: Self) -> Self;\n\n pub unconstrained fn __batch_invert(to_invert: [Self; M]) -> [Self; M];\n pub unconstrained fn __batch_invert_slice(to_invert: [Self]) -> [Self];\n\n pub unconstrained fn __tonelli_shanks_sqrt(self) -> std::option::Option;\n\n pub unconstrained fn __compute_quadratic_expression(\n lhs: [[Self; LHS_N]; NUM_PRODUCTS],\n lhs_flags: [[bool; LHS_N]; NUM_PRODUCTS],\n rhs: [[Self; RHS_N]; NUM_PRODUCTS],\n rhs_flags: [[bool; RHS_N]; NUM_PRODUCTS],\n add: [Self; ADD_N],\n add_flags: [bool; ADD_N],\n ) -> (Self, Self);\n\n pub fn evaluate_quadratic_expression(\n lhs: [[Self; LHS_N]; NUM_PRODUCTS],\n lhs_flags: [[bool; LHS_N]; NUM_PRODUCTS],\n rhs: [[Self; RHS_N]; NUM_PRODUCTS],\n rhs_flags: [[bool; RHS_N]; NUM_PRODUCTS],\n add: [Self; ADD_N],\n add_flags: [bool; ADD_N],\n );\n\n pub fn eq(self, other: Self) -> bool {\n self == other\n }\n pub fn assert_is_not_equal(self, other: Self);\n pub fn validate_in_range(self);\n pub fn validate_in_field(self);\n\n pub fn neg(self) -> Self;\n pub fn add(self, other: Self) -> Self {\n self + other\n }\n pub fn sub(self, other: Self) -> Self {\n self - other\n }\n pub fn mul(self, other: Self) -> Self {\n self * other\n }\n pub fn div(self, other: Self) -> Self {\n self / other\n }\n pub fn udiv_mod(self, divisor: Self) -> (Self, Self);\n pub fn udiv(self, divisor: Self) -> Self;\n pub fn umod(self, divisor: Self) -> Self;\n\n pub fn conditional_select(lhs: Self, rhs: Self, predicate: bool) -> Self;\n}\n\nimpl BigNumTrait for BigNum\nwhere\n Params: BigNumParamsGetter,\n{\n\n fn new() -> Self {\n Self { limbs: [0; N] }\n }\n\n fn one() -> Self {\n let mut result = BigNum::new();\n result.limbs[0] = 1;\n result\n }\n\n fn derive_from_seed(seed: [u8; SeedBytes]) -> Self {\n let params = Params::get_params();\n Self { limbs: derive_from_seed::<_, MOD_BITS, _>(params, seed) }\n }\n\n unconstrained fn __derive_from_seed(seed: [u8; SeedBytes]) -> Self {\n let params = Params::get_params();\n Self { limbs: __derive_from_seed::<_, MOD_BITS, _>(params, seed) }\n }\n\n fn from_slice(limbs: [Field]) -> Self {\n Self { limbs: limbs.as_array() }\n }\n\n fn from_be_bytes(x: [u8; NBytes]) -> Self {\n Self { limbs: from_be_bytes::<_, MOD_BITS, _>(x) }\n }\n\n fn to_le_bytes(self) -> [u8; NBytes] {\n to_le_bytes::<_, MOD_BITS, _>(self.limbs)\n }\n\n fn modulus() -> Self {\n Self { limbs: Params::get_params().modulus }\n }\n\n fn modulus_bits(_: Self) -> u32 {\n MOD_BITS\n }\n\n fn num_limbs(_: Self) -> u32 {\n N\n }\n\n fn get_limbs_slice(self) -> [Field] {\n self.limbs\n }\n\n fn get_limb(self, idx: u32) -> Field {\n self.limbs[idx]\n }\n\n fn set_limb(&mut self, idx: u32, value: Field) {\n self.limbs[idx] = value;\n }\n\n unconstrained fn __eq(self, other: Self) -> bool {\n __eq(self.limbs, other.limbs)\n }\n\n unconstrained fn __is_zero(self) -> bool {\n __is_zero(self.limbs)\n }\n\n unconstrained fn __neg(self) -> Self {\n let params = Params::get_params();\n Self::from_slice(__neg(params, self.limbs))\n }\n\n unconstrained fn __add(self, other: Self) -> Self {\n let params = Params::get_params();\n Self::from_slice(__add(params, self.limbs, other.limbs))\n }\n\n unconstrained fn __sub(self, other: Self) -> Self {\n let params = Params::get_params();\n Self::from_slice(__sub(params, self.limbs, other.limbs))\n }\n\n unconstrained fn __mul(self, other: Self) -> Self {\n let params = Params::get_params();\n Self::from_slice(__mul::<_, MOD_BITS>(params, self.limbs, other.limbs))\n }\n\n unconstrained fn __div(self, divisor: Self) -> Self {\n let params = Params::get_params();\n Self::from_slice(__div::<_, MOD_BITS>(params, self.limbs, divisor.limbs))\n }\n\n unconstrained fn __udiv_mod(self, divisor: Self) -> (Self, Self) {\n let (q, r) = __udiv_mod(self.limbs, divisor.limbs);\n (Self { limbs: q }, Self { limbs: r })\n }\n\n unconstrained fn __invmod(self) -> Self {\n let params = Params::get_params();\n assert(params.has_multiplicative_inverse);\n Self { limbs: __invmod::<_, MOD_BITS>(params, self.limbs) }\n }\n\n unconstrained fn __pow(self, exponent: Self) -> Self {\n let params = Params::get_params();\n Self { limbs: __pow::<_, MOD_BITS>(params, self.limbs, exponent.limbs) }\n }\n\n unconstrained fn __batch_invert(x: [Self; M]) -> [Self; M] {\n let params = Params::get_params();\n assert(params.has_multiplicative_inverse);\n __batch_invert::<_, MOD_BITS, _>(params, x.map(|bn: Self| bn.limbs)).map(|limbs| {\n Self { limbs }\n })\n }\n\n unconstrained fn __batch_invert_slice(x: [Self]) -> [Self] {\n let params = Params::get_params();\n assert(params.has_multiplicative_inverse);\n __batch_invert_slice::<_, MOD_BITS>(params, x.map(|bn: Self| bn.limbs)).map(|limbs| {\n Self { limbs }\n })\n }\n\n unconstrained fn __tonelli_shanks_sqrt(self) -> std::option::Option {\n let params = Params::get_params();\n let maybe_limbs = unsafe { __tonelli_shanks_sqrt(params, self.limbs) };\n maybe_limbs.map(|limbs| Self { limbs })\n }\n\n unconstrained fn __compute_quadratic_expression(\n lhs_terms: [[Self; LHS_N]; NUM_PRODUCTS],\n lhs_flags: [[bool; LHS_N]; NUM_PRODUCTS],\n rhs_terms: [[Self; RHS_N]; NUM_PRODUCTS],\n rhs_flags: [[bool; RHS_N]; NUM_PRODUCTS],\n linear_terms: [Self; ADD_N],\n linear_flags: [bool; ADD_N],\n ) -> (Self, Self) {\n let params = Params::get_params();\n let (q_limbs, r_limbs) = __compute_quadratic_expression::<_, MOD_BITS, _, _, _, _>(\n params,\n map(lhs_terms, |bns| map(bns, |bn: Self| bn.limbs)),\n lhs_flags,\n map(rhs_terms, |bns| map(bns, |bn: Self| bn.limbs)),\n rhs_flags,\n map(linear_terms, |bn: Self| bn.limbs),\n linear_flags,\n );\n (Self { limbs: q_limbs }, Self { limbs: r_limbs })\n }\n\n fn evaluate_quadratic_expression(\n lhs_terms: [[Self; LHS_N]; NUM_PRODUCTS],\n lhs_flags: [[bool; LHS_N]; NUM_PRODUCTS],\n rhs_terms: [[Self; RHS_N]; NUM_PRODUCTS],\n rhs_flags: [[bool; RHS_N]; NUM_PRODUCTS],\n linear_terms: [Self; ADD_N],\n linear_flags: [bool; ADD_N],\n ) {\n let params = Params::get_params();\n evaluate_quadratic_expression::<_, MOD_BITS, _, _, _, _>(\n params,\n map(lhs_terms, |bns| map(bns, |bn: Self| bn.limbs)),\n lhs_flags,\n map(rhs_terms, |bns| map(bns, |bn: Self| bn.limbs)),\n rhs_flags,\n map(linear_terms, |bn: Self| bn.limbs),\n linear_flags,\n )\n }\n\n fn validate_in_field(self: Self) {\n let params = Params::get_params();\n validate_in_field::<_, MOD_BITS>(params, self.limbs);\n }\n\n fn validate_in_range(self) {\n validate_in_range::<_, MOD_BITS>(self.limbs);\n }\n\n fn assert_is_not_equal(self, other: Self) {\n let params = Params::get_params();\n assert_is_not_equal(params, self.limbs, other.limbs);\n }\n\n fn neg(self) -> Self {\n let params = Params::get_params();\n Self { limbs: neg::<_, MOD_BITS>(params, self.limbs) }\n }\n\n fn udiv_mod(self, divisor: Self) -> (Self, Self) {\n let params = Params::get_params();\n let (q, r) = udiv_mod::<_, MOD_BITS>(params, self.limbs, divisor.limbs);\n (Self { limbs: q }, Self { limbs: r })\n }\n\n fn udiv(self, divisor: Self) -> Self {\n let params = Params::get_params();\n Self { limbs: udiv::<_, MOD_BITS>(params, self.limbs, divisor.limbs) }\n }\n\n fn umod(self, divisor: Self) -> Self {\n let params = Params::get_params();\n Self { limbs: umod::<_, MOD_BITS>(params, self.limbs, divisor.limbs) }\n }\n\n fn conditional_select(lhs: Self, rhs: Self, predicate: bool) -> Self {\n Self { limbs: conditional_select(lhs.limbs, rhs.limbs, predicate) }\n }\n}\n\n// impl BigNumTrait for BigNum where Params: BigNumParamsGetter {}\n\nimpl std::ops::Add for BigNum\nwhere\n Params: BigNumParamsGetter,\n{\n // Note: this method is expensive! Try to craft quadratic relations and directly evaluate them\n // via evaluate_quadratic_expression\n fn add(self, other: Self) -> Self {\n let params = Params::get_params();\n Self { limbs: add::<_, MOD_BITS>(params, self.limbs, other.limbs) }\n }\n}\n\nimpl std::ops::Sub for BigNum\nwhere\n Params: BigNumParamsGetter,\n{\n // Note: this method is expensive! Try to craft quadratic relations and directly evaluate them\n // via evaluate_quadratic_expression\n fn sub(self, other: Self) -> Self {\n let params = Params::get_params();\n Self { limbs: sub::<_, MOD_BITS>(params, self.limbs, other.limbs) }\n }\n}\n\nimpl std::ops::Mul for BigNum\nwhere\n Params: BigNumParamsGetter,\n{\n // Note: this method is expensive! Try to craft quadratic relations and directly evaluate them\n // via evaluate_quadratic_expression\n // e.g. performing a sum of multiple multiplications and additions via `evaluate_quadratic_expression`\n // will create much fewer constraints than calling `mul` and `add` directly\n fn mul(self, other: Self) -> Self {\n let params = Params::get_params();\n Self { limbs: mul::<_, MOD_BITS>(params, self.limbs, other.limbs) }\n }\n}\n\nimpl std::ops::Div for BigNum\nwhere\n Params: BigNumParamsGetter,\n{\n // Note: this method is expensive! Witness computation is extremely expensive as it requires modular exponentiation\n fn div(self, other: Self) -> Self {\n let params = Params::get_params();\n Self { limbs: div::<_, MOD_BITS>(params, self.limbs, other.limbs) }\n }\n}\n\nimpl std::cmp::Eq for BigNum\nwhere\n Params: BigNumParamsGetter,\n{\n fn eq(self, other: Self) -> bool {\n let params = Params::get_params();\n eq::<_, MOD_BITS>(params, self.limbs, other.limbs)\n }\n}\n\n","path":"/Users/khashayarbarooti/aztec/noir-bignum-oracles/src/bignum.nr"}},"names":["test_add_BN"],"brillig_names":["__add_with_flags","__sub_with_flags"]} \ No newline at end of file diff --git a/src/bignum.nr b/src/bignum.nr index 52002bd7..5d230d08 100644 --- a/src/bignum.nr +++ b/src/bignum.nr @@ -5,7 +5,7 @@ use crate::params::BigNumParamsGetter; use crate::fns::{ constrained_ops::{ add, assert_is_not_equal, conditional_select, derive_from_seed, div, eq, mul, neg, sub, - udiv, udiv_mod, umod, validate_in_field, validate_in_range, + udiv, udiv_mod, umod, validate_in_field, validate_in_range, from_field, }, expressions::{__compute_quadratic_expression, evaluate_quadratic_expression}, serialization::{from_be_bytes, to_le_bytes}, unconstrained_ops::{ @@ -24,6 +24,7 @@ pub trait BigNumTrait { // fn default() -> Self { std::default::Default::default () } pub fn new() -> Self; pub fn one() -> Self; + pub fn from_field(field: Field) -> Self; pub fn derive_from_seed(seed: [u8; SeedBytes]) -> Self; pub unconstrained fn __derive_from_seed(seed: [u8; SeedBytes]) -> Self; pub fn from_slice(limbs: [Field]) -> Self; @@ -99,6 +100,15 @@ pub trait BigNumTrait { pub fn conditional_select(lhs: Self, rhs: Self, predicate: bool) -> Self; } +// impl std::convert::From for BigNum +// where +// Params: BigNumParamsGetter, +// { +// fn from(input: Field) -> Self { +// let params = Params::get_params(); +// Self { limbs: from_field::(params, input) } +// } +// } impl BigNumTrait for BigNum where Params: BigNumParamsGetter, @@ -114,6 +124,11 @@ where result } + fn from_field(field: Field) -> Self { + let params = Params::get_params(); + Self { limbs: from_field::(params, field) } + } + fn derive_from_seed(seed: [u8; SeedBytes]) -> Self { let params = Params::get_params(); Self { limbs: derive_from_seed::<_, MOD_BITS, _>(params, seed) } diff --git a/src/fns/constrained_ops.nr b/src/fns/constrained_ops.nr index 758e23dc..64c9ef9c 100644 --- a/src/fns/constrained_ops.nr +++ b/src/fns/constrained_ops.nr @@ -4,7 +4,7 @@ use crate::fns::{ expressions::evaluate_quadratic_expression, unconstrained_helpers::{ __add_with_flags, __neg_with_flags, __sub_with_flags, __validate_gt_remainder, - __validate_in_field_compute_borrow_flags, + __validate_in_field_compute_borrow_flags, __from_field, }, unconstrained_ops::{__div, __mul, __udiv_mod}, }; @@ -35,6 +35,25 @@ use crate::fns::{ * We use a hash function that can be modelled as a random oracle * This function *should* produce an output that is a uniformly randomly distributed value modulo BigNum::modulus() **/ +pub (crate) fn from_field( + params: P, + field: Field, +) -> [Field; N] { + let result = __from_field::(field); + // validate the limbs are in range and the value in total is less than 2^254 + validate_in_range::(result); + let shift = 0x1000000000000000000000000000000; + // validate the limbs sum up to the field value + let field_val = if N < 2 { + result[0] + } else { + result[0] + result[1] * shift + result[2] * shift * shift + }; + assert(field_val == field); + result +} + + pub(crate) fn derive_from_seed( params: P, seed: [u8; SeedBytes], diff --git a/src/fns/unconstrained_helpers.nr b/src/fns/unconstrained_helpers.nr index 0afcfb40..ba0549f7 100644 --- a/src/fns/unconstrained_helpers.nr +++ b/src/fns/unconstrained_helpers.nr @@ -18,6 +18,15 @@ global TWO_POW_60: u64 = 0x1000000000000000; * __tonelli_shanks_sqrt */ + +pub(crate) unconstrained fn __from_field(field: Field) -> [Field; N] { + // cast the field to a u60 representation + let res_u60: U60Repr = U60Repr::from_field(field); + let result: [Field; N] = U60Repr::into(res_u60); + result +} + + pub(crate) unconstrained fn __validate_in_field_compute_borrow_flags( params: P, val: [Field; N], @@ -35,8 +44,8 @@ pub(crate) unconstrained fn __validate_gt_remainder( lhs: [Field; N], rhs: [Field; N], ) -> ([Field; N], [bool; N], [bool; N]) { - let a_u60: U60Repr = U60Repr::from(lhs); - let mut b_u60: U60Repr = U60Repr::from(rhs); + let a_u60: U60Repr = From::from(lhs); + let mut b_u60: U60Repr = From::from(rhs); let underflow = b_u60.gte(a_u60); b_u60 += U60Repr::one(); @@ -76,7 +85,7 @@ pub(crate) unconstrained fn __neg_with_flags( params: P, val: [Field; N], ) -> ([Field; N], [bool; N]) { - let x_u60: U60Repr = U60Repr::from(val); + let x_u60: U60Repr = From::from(val); let mut result_u60: U60Repr = U60Repr { limbs: [0; 2 * N] }; let mut borrow_in: u64 = 0; @@ -101,8 +110,8 @@ pub(crate) unconstrained fn __add_with_flags( lhs: [Field; N], rhs: [Field; N], ) -> ([Field; N], [bool; N], [bool; N], bool) { - let a_u60: U60Repr = U60Repr::from(lhs); - let b_u60: U60Repr = U60Repr::from(rhs); + let a_u60: U60Repr = From::from(lhs); + let b_u60: U60Repr = From::from(rhs); let add_u60 = a_u60 + b_u60; let overflow = add_u60.gte(params.modulus_u60); diff --git a/src/tests/bignum_test.nr b/src/tests/bignum_test.nr index ffc8f524..199c3a56 100644 --- a/src/tests/bignum_test.nr +++ b/src/tests/bignum_test.nr @@ -790,3 +790,12 @@ fn test_expressions() { assert(wx_constrained.limbs == wx.limbs); } + + +#[test] +fn test_from_field() { + let field: Field = 1; + let result = Fq::from_field(field); + assert(result == Fq::one()); +} + diff --git a/src/utils/split_bits.nr b/src/utils/split_bits.nr index 7a48dbd7..1b9e270d 100644 --- a/src/utils/split_bits.nr +++ b/src/utils/split_bits.nr @@ -2,6 +2,25 @@ global TWO_POW_56: u64 = 0x100000000000000; global TWO_POW_60: u64 = 0x1000000000000000; global TWO_POW_64: Field = 0x10000000000000000; + +//fields to u60rep conversion +pub unconstrained fn field_to_u60rep(mut x: Field) -> (u64, u64, u64) { + // get the first 60 bits by casting to u64 and then taking the lower 60 bits + // we use the fact that this casting drops everything above 64 bits + let x_low_u64 = (x as u64); + let low = x_low_u64 % TWO_POW_60; + // this becomes the same as a integer division because we're removing the remainder + x = (x-(low as Field)) / (TWO_POW_60 as Field); + let x_mid_u64 = (x as u64); + let mid = x_mid_u64 % TWO_POW_60; + x = x - (mid as Field); + let x_high_u64 = (x as u64); + let high = x_high_u64 % TWO_POW_60; + (low, mid, high) + +} + + // Decomposes a single field into two 120 bit fields pub unconstrained fn split_120_bits(mut x: Field) -> (Field, Field) { // Here we're taking advantage of truncating 64 bit limbs from the input field diff --git a/src/utils/u60_representation.nr b/src/utils/u60_representation.nr index ae0dc172..9b5bde62 100644 --- a/src/utils/u60_representation.nr +++ b/src/utils/u60_representation.nr @@ -1,5 +1,6 @@ use crate::utils::msb::get_msb64; use crate::utils::split_bits; +use crate::utils::split_bits::field_to_u60rep; /** * @brief U60Repr represents a BigNum element as a sequence of 60-bit unsigned integers. @@ -57,6 +58,31 @@ impl std::convert::From<[Field; N]> for U60Rep } } +// impl std::convert::From for U60Repr { +// fn from(input: Field) -> Self { +// let (low, mid, high) = unsafe { field_to_u60rep(input) } ; +// let mut result: Self = U60Repr { limbs: [0; N * NumSegments] }; +// let N_u60: u32 = N * NumSegments; +// assert(N_u60 >=1, "N must be at least 1"); +// if N_u60 == 1 { +// assert((mid ==0) & (high == 0), "input field is too large to fit in a single limb"); +// result.limbs[0] = low; +// } +// else if N_u60 == 2{ +// assert(high == 0, "input field is too large to fit in two limbs"); +// result.limbs[0] = low; +// result.limbs[1] = mid; +// }else{ +// result.limbs[0] = low; +// result.limbs[1] = mid; +// result.limbs[2] = high; +// } +// result +// } +// } + + + impl std::convert::Into<[Field; N]> for U60Repr { fn into(x: U60Repr) -> [Field; N] { let mut result: [Field; N] = [0; N]; @@ -76,6 +102,7 @@ impl std::cmp::Eq for U60Repr impl U60Repr { + pub(crate) unconstrained fn new( x: [Field; N * NumFieldSegments], ) -> Self { @@ -94,6 +121,28 @@ impl U60Repr { result } + + pub(crate) fn from_field(input: Field) -> Self { + let (low, mid, high) = unsafe { field_to_u60rep(input) } ; + let mut result: Self = U60Repr { limbs: [0; N * NumSegments] }; + let N_u60: u32 = N * NumSegments; + assert(N_u60 >=1, "N must be at least 1"); + if N_u60 == 1 { + assert((mid ==0) & (high == 0), "input field is too large to fit in a single limb"); + result.limbs[0] = low; + } + else if N_u60 == 2{ + assert(high == 0, "input field is too large to fit in two limbs"); + result.limbs[0] = low; + result.limbs[1] = mid; + }else{ + result.limbs[0] = low; + result.limbs[1] = mid; + result.limbs[2] = high; + } + result + } + pub(crate) unconstrained fn into_field_array( x: U60Repr, ) -> [Field; N * NumSegments / 2] { From 0939d85856dbcccf26e080f962de23af39bf0558 Mon Sep 17 00:00:00 2001 From: Khashayar Barooti Date: Tue, 24 Dec 2024 15:13:11 +0000 Subject: [PATCH 2/7] formatter --- src/bignum.nr | 7 ++++--- src/fns/constrained_ops.nr | 14 +++++++------- src/fns/unconstrained_helpers.nr | 4 +--- src/runtime_bignum.nr | 3 ++- src/tests/bignum_test.nr | 2 -- src/utils/split_bits.nr | 11 ++++------- src/utils/u60_representation.nr | 15 +++++---------- 7 files changed, 23 insertions(+), 33 deletions(-) diff --git a/src/bignum.nr b/src/bignum.nr index 5d230d08..ab6d12ce 100644 --- a/src/bignum.nr +++ b/src/bignum.nr @@ -4,9 +4,10 @@ use crate::params::BigNumParamsGetter; use crate::fns::{ constrained_ops::{ - add, assert_is_not_equal, conditional_select, derive_from_seed, div, eq, mul, neg, sub, - udiv, udiv_mod, umod, validate_in_field, validate_in_range, from_field, - }, expressions::{__compute_quadratic_expression, evaluate_quadratic_expression}, + add, assert_is_not_equal, conditional_select, derive_from_seed, div, eq, from_field, mul, + neg, sub, udiv, udiv_mod, umod, validate_in_field, validate_in_range, + }, + expressions::{__compute_quadratic_expression, evaluate_quadratic_expression}, serialization::{from_be_bytes, to_le_bytes}, unconstrained_ops::{ __add, __batch_invert, __batch_invert_slice, __derive_from_seed, __div, __eq, __invmod, diff --git a/src/fns/constrained_ops.nr b/src/fns/constrained_ops.nr index 64c9ef9c..085b5ccc 100644 --- a/src/fns/constrained_ops.nr +++ b/src/fns/constrained_ops.nr @@ -3,9 +3,10 @@ use crate::params::BigNumParams as P; use crate::fns::{ expressions::evaluate_quadratic_expression, unconstrained_helpers::{ - __add_with_flags, __neg_with_flags, __sub_with_flags, __validate_gt_remainder, - __validate_in_field_compute_borrow_flags, __from_field, - }, unconstrained_ops::{__div, __mul, __udiv_mod}, + __add_with_flags, __from_field, __neg_with_flags, __sub_with_flags, __validate_gt_remainder, + __validate_in_field_compute_borrow_flags, + }, + unconstrained_ops::{__div, __mul, __udiv_mod}, }; /** @@ -35,7 +36,7 @@ use crate::fns::{ * We use a hash function that can be modelled as a random oracle * This function *should* produce an output that is a uniformly randomly distributed value modulo BigNum::modulus() **/ -pub (crate) fn from_field( +pub(crate) fn from_field( params: P, field: Field, ) -> [Field; N] { @@ -43,17 +44,16 @@ pub (crate) fn from_field( // validate the limbs are in range and the value in total is less than 2^254 validate_in_range::(result); let shift = 0x1000000000000000000000000000000; - // validate the limbs sum up to the field value + // validate the limbs sum up to the field value let field_val = if N < 2 { result[0] } else { result[0] + result[1] * shift + result[2] * shift * shift }; assert(field_val == field); - result + result } - pub(crate) fn derive_from_seed( params: P, seed: [u8; SeedBytes], diff --git a/src/fns/unconstrained_helpers.nr b/src/fns/unconstrained_helpers.nr index ba0549f7..116e3932 100644 --- a/src/fns/unconstrained_helpers.nr +++ b/src/fns/unconstrained_helpers.nr @@ -18,15 +18,13 @@ global TWO_POW_60: u64 = 0x1000000000000000; * __tonelli_shanks_sqrt */ - pub(crate) unconstrained fn __from_field(field: Field) -> [Field; N] { - // cast the field to a u60 representation + // cast the field to a u60 representation let res_u60: U60Repr = U60Repr::from_field(field); let result: [Field; N] = U60Repr::into(res_u60); result } - pub(crate) unconstrained fn __validate_in_field_compute_borrow_flags( params: P, val: [Field; N], diff --git a/src/runtime_bignum.nr b/src/runtime_bignum.nr index 0325ca54..ec141ebd 100644 --- a/src/runtime_bignum.nr +++ b/src/runtime_bignum.nr @@ -5,7 +5,8 @@ use crate::fns::{ constrained_ops::{ add, assert_is_not_equal, conditional_select, derive_from_seed, div, eq, mul, neg, sub, udiv, udiv_mod, umod, validate_in_field, validate_in_range, - }, expressions::{__compute_quadratic_expression, evaluate_quadratic_expression}, + }, + expressions::{__compute_quadratic_expression, evaluate_quadratic_expression}, serialization::{from_be_bytes, to_le_bytes}, unconstrained_ops::{ __add, __batch_invert, __batch_invert_slice, __derive_from_seed, __div, __eq, __invmod, diff --git a/src/tests/bignum_test.nr b/src/tests/bignum_test.nr index 199c3a56..f3ca9935 100644 --- a/src/tests/bignum_test.nr +++ b/src/tests/bignum_test.nr @@ -790,8 +790,6 @@ fn test_expressions() { assert(wx_constrained.limbs == wx.limbs); } - - #[test] fn test_from_field() { let field: Field = 1; diff --git a/src/utils/split_bits.nr b/src/utils/split_bits.nr index 1b9e270d..bce30fff 100644 --- a/src/utils/split_bits.nr +++ b/src/utils/split_bits.nr @@ -2,25 +2,22 @@ global TWO_POW_56: u64 = 0x100000000000000; global TWO_POW_60: u64 = 0x1000000000000000; global TWO_POW_64: Field = 0x10000000000000000; - -//fields to u60rep conversion +//fields to u60rep conversion pub unconstrained fn field_to_u60rep(mut x: Field) -> (u64, u64, u64) { // get the first 60 bits by casting to u64 and then taking the lower 60 bits - // we use the fact that this casting drops everything above 64 bits + // we use the fact that this casting drops everything above 64 bits let x_low_u64 = (x as u64); let low = x_low_u64 % TWO_POW_60; - // this becomes the same as a integer division because we're removing the remainder - x = (x-(low as Field)) / (TWO_POW_60 as Field); + // this becomes the same as a integer division because we're removing the remainder + x = (x - (low as Field)) / (TWO_POW_60 as Field); let x_mid_u64 = (x as u64); let mid = x_mid_u64 % TWO_POW_60; x = x - (mid as Field); let x_high_u64 = (x as u64); let high = x_high_u64 % TWO_POW_60; (low, mid, high) - } - // Decomposes a single field into two 120 bit fields pub unconstrained fn split_120_bits(mut x: Field) -> (Field, Field) { // Here we're taking advantage of truncating 64 bit limbs from the input field diff --git a/src/utils/u60_representation.nr b/src/utils/u60_representation.nr index 9b5bde62..5dcfca07 100644 --- a/src/utils/u60_representation.nr +++ b/src/utils/u60_representation.nr @@ -81,8 +81,6 @@ impl std::convert::From<[Field; N]> for U60Rep // } // } - - impl std::convert::Into<[Field; N]> for U60Repr { fn into(x: U60Repr) -> [Field; N] { let mut result: [Field; N] = [0; N]; @@ -102,7 +100,6 @@ impl std::cmp::Eq for U60Repr impl U60Repr { - pub(crate) unconstrained fn new( x: [Field; N * NumFieldSegments], ) -> Self { @@ -121,21 +118,19 @@ impl U60Repr { result } - pub(crate) fn from_field(input: Field) -> Self { - let (low, mid, high) = unsafe { field_to_u60rep(input) } ; + let (low, mid, high) = unsafe { field_to_u60rep(input) }; let mut result: Self = U60Repr { limbs: [0; N * NumSegments] }; let N_u60: u32 = N * NumSegments; - assert(N_u60 >=1, "N must be at least 1"); + assert(N_u60 >= 1, "N must be at least 1"); if N_u60 == 1 { - assert((mid ==0) & (high == 0), "input field is too large to fit in a single limb"); + assert((mid == 0) & (high == 0), "input field is too large to fit in a single limb"); result.limbs[0] = low; - } - else if N_u60 == 2{ + } else if N_u60 == 2 { assert(high == 0, "input field is too large to fit in two limbs"); result.limbs[0] = low; result.limbs[1] = mid; - }else{ + } else { result.limbs[0] = low; result.limbs[1] = mid; result.limbs[2] = high; From b259a3dc4d5f2a7454f36db1ac0747bfaea631f7 Mon Sep 17 00:00:00 2001 From: Khashayar Barooti Date: Tue, 24 Dec 2024 15:20:45 +0000 Subject: [PATCH 3/7] changed the nargo version in the workflow --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1db5d1ed..fba3f1b3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ on: env: CARGO_TERM_COLOR: always - MINIMUM_NOIR_VERSION: v0.36.0 + MINIMUM_NOIR_VERSION: v1.0.0-beta.0 jobs: noir-version-list: From 27c613900a5da295092a497edd7f7c958b3d5b66 Mon Sep 17 00:00:00 2001 From: Khashayar Barooti Date: Mon, 6 Jan 2025 10:47:13 +0000 Subject: [PATCH 4/7] added tests for 2 and 3 digits, fixed bugs --- src/fns/constrained_ops.nr | 2 ++ src/tests/bignum_test.nr | 22 +++++++++++++++++++- src/utils/split_bits.nr | 28 ++++++++++++++++--------- src/utils/u60_representation.nr | 37 +++++++++++++++++++++++++-------- 4 files changed, 69 insertions(+), 20 deletions(-) diff --git a/src/fns/constrained_ops.nr b/src/fns/constrained_ops.nr index 085b5ccc..2cdb68b5 100644 --- a/src/fns/constrained_ops.nr +++ b/src/fns/constrained_ops.nr @@ -47,6 +47,8 @@ pub(crate) fn from_field( // validate the limbs sum up to the field value let field_val = if N < 2 { result[0] + } else if N == 2 { + result[0] + result[1] * shift } else { result[0] + result[1] * shift + result[2] * shift * shift }; diff --git a/src/tests/bignum_test.nr b/src/tests/bignum_test.nr index f3ca9935..5bccbef2 100644 --- a/src/tests/bignum_test.nr +++ b/src/tests/bignum_test.nr @@ -791,9 +791,29 @@ fn test_expressions() { } #[test] -fn test_from_field() { +fn test_from_field_1_digit() { let field: Field = 1; let result = Fq::from_field(field); assert(result == Fq::one()); } +#[test] +fn test_from_field_2_digits() { + let field: Field = 762576765071760201410184025311678064293966151975347778787092903729041075; + let result = Fq::from_field(field); + let expected: Fq = BigNum { + limbs: [0xe88ed97f8f707abd3fa65763c80eb3, 0x6e7d8b5586595aa1fb2ee04d5cb4f5, 0x0], + }; + assert(result == expected); +} + +#[test] +fn test_from_field_3_digits() { + let field: Field = -1; + let result = Fq::from_field(field); + let expected: Fq = BigNum { + limbs: [0x33e84879b9709143e1f593f0000000, 0x4e72e131a029b85045b68181585d28, 0x3064], + }; + assert(result == expected); +} + diff --git a/src/utils/split_bits.nr b/src/utils/split_bits.nr index bce30fff..823cb7d3 100644 --- a/src/utils/split_bits.nr +++ b/src/utils/split_bits.nr @@ -3,19 +3,27 @@ global TWO_POW_60: u64 = 0x1000000000000000; global TWO_POW_64: Field = 0x10000000000000000; //fields to u60rep conversion -pub unconstrained fn field_to_u60rep(mut x: Field) -> (u64, u64, u64) { +// field elements are 254 bits +// so there will be 5 limbs +pub unconstrained fn field_to_u60rep(mut x: Field) -> (u64, u64, u64, u64, u64) { // get the first 60 bits by casting to u64 and then taking the lower 60 bits // we use the fact that this casting drops everything above 64 bits - let x_low_u64 = (x as u64); - let low = x_low_u64 % TWO_POW_60; + let x_first_u64 = (x as u64); + let first: u64 = x_first_u64 % TWO_POW_60; // this becomes the same as a integer division because we're removing the remainder - x = (x - (low as Field)) / (TWO_POW_60 as Field); - let x_mid_u64 = (x as u64); - let mid = x_mid_u64 % TWO_POW_60; - x = x - (mid as Field); - let x_high_u64 = (x as u64); - let high = x_high_u64 % TWO_POW_60; - (low, mid, high) + x = (x - (first as Field)) / (TWO_POW_60 as Field); + let x_second_u64 = (x as u64); + let second = x_second_u64 % TWO_POW_60; + x = (x - (second as Field)) / (TWO_POW_60 as Field); + let x_third_u64 = (x as u64); + let third = x_third_u64 % TWO_POW_60; + x = (x - (third as Field)) / (TWO_POW_60 as Field); + let x_fourth_u64 = (x as u64); + let fourth = x_fourth_u64 % TWO_POW_60; + x = (x - (fourth as Field)) / (TWO_POW_60 as Field); + let x_fifth_u64 = (x as u64); + let fifth = x_fifth_u64 % TWO_POW_60; + (first, second, third, fourth, fifth) } // Decomposes a single field into two 120 bit fields diff --git a/src/utils/u60_representation.nr b/src/utils/u60_representation.nr index 5dcfca07..59f6c63b 100644 --- a/src/utils/u60_representation.nr +++ b/src/utils/u60_representation.nr @@ -119,21 +119,40 @@ impl U60Repr { } pub(crate) fn from_field(input: Field) -> Self { - let (low, mid, high) = unsafe { field_to_u60rep(input) }; + let (first, second, third, fourth, fifth) = unsafe { field_to_u60rep(input) }; let mut result: Self = U60Repr { limbs: [0; N * NumSegments] }; let N_u60: u32 = N * NumSegments; assert(N_u60 >= 1, "N must be at least 1"); if N_u60 == 1 { - assert((mid == 0) & (high == 0), "input field is too large to fit in a single limb"); - result.limbs[0] = low; + assert( + (second == 0) & (third == 0) & (fourth == 0) & (fifth == 0), + "input field is too large to fit in a single limb", + ); + result.limbs[0] = first; } else if N_u60 == 2 { - assert(high == 0, "input field is too large to fit in two limbs"); - result.limbs[0] = low; - result.limbs[1] = mid; + assert( + (third == 0) & (fourth == 0) & (fifth == 0), + "input field is too large to fit in two limbs", + ); + result.limbs[0] = first; + result.limbs[1] = second; + } else if N_u60 == 3 { + assert((fourth == 0) & (fifth == 0), "input field is too large to fit in three limbs"); + result.limbs[0] = first; + result.limbs[1] = second; + result.limbs[2] = third; + } else if N_u60 == 4 { + assert((fifth == 0), "input field is too large to fit in four limbs"); + result.limbs[0] = first; + result.limbs[1] = second; + result.limbs[2] = third; + result.limbs[3] = fourth; } else { - result.limbs[0] = low; - result.limbs[1] = mid; - result.limbs[2] = high; + result.limbs[0] = first; + result.limbs[1] = second; + result.limbs[2] = third; + result.limbs[3] = fourth; + result.limbs[4] = fifth; } result } From 4fd4b0c7d4e5889e99f72af598f5a67f589cb57a Mon Sep 17 00:00:00 2001 From: Khashayar Barooti Date: Mon, 6 Jan 2025 11:20:48 +0000 Subject: [PATCH 5/7] formatter --- src/bignum.nr | 1 - src/tests/bignum_test.nr | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/bignum.nr b/src/bignum.nr index 94346eaf..96dae3b9 100644 --- a/src/bignum.nr +++ b/src/bignum.nr @@ -89,7 +89,6 @@ pub trait BigNumTrait: Neg + Add + Sub + Mul + Div + Eq { pub fn conditional_select(lhs: Self, rhs: Self, predicate: bool) -> Self; } - // impl std::convert::From for BigNum // where // Params: BigNumParamsGetter, diff --git a/src/tests/bignum_test.nr b/src/tests/bignum_test.nr index e544cd4b..2f0082d8 100644 --- a/src/tests/bignum_test.nr +++ b/src/tests/bignum_test.nr @@ -801,9 +801,8 @@ fn test_from_field_1_digit() { fn test_from_field_2_digits() { let field: Field = 762576765071760201410184025311678064293966151975347778787092903729041075; let result = Fq::from_field(field); - let expected: Fq = BigNum { - limbs: [0xe88ed97f8f707abd3fa65763c80eb3, 0x6e7d8b5586595aa1fb2ee04d5cb4f5, 0x0], - }; + let expected: Fq = + BigNum { limbs: [0xe88ed97f8f707abd3fa65763c80eb3, 0x6e7d8b5586595aa1fb2ee04d5cb4f5, 0x0] }; assert(result == expected); } From fad1d8d5987b9c038ffd0aef55f73b1614192682 Mon Sep 17 00:00:00 2001 From: Khashayar Barooti Date: Mon, 6 Jan 2025 15:35:57 +0000 Subject: [PATCH 6/7] added the correct range check for the 3 digit scenario --- src/fns/constrained_ops.nr | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/fns/constrained_ops.nr b/src/fns/constrained_ops.nr index 2cdb68b5..751b9841 100644 --- a/src/fns/constrained_ops.nr +++ b/src/fns/constrained_ops.nr @@ -1,5 +1,4 @@ use crate::params::BigNumParams as P; - use crate::fns::{ expressions::evaluate_quadratic_expression, unconstrained_helpers::{ @@ -40,16 +39,30 @@ pub(crate) fn from_field( params: P, field: Field, ) -> [Field; N] { - let result = __from_field::(field); + // safty: we check that the resulting limbs represent the intended field element + // we check the bit length, the limbs being max 120 bits, and the value in total is less than the field modulus + let result = unsafe { __from_field::(field) }; // validate the limbs are in range and the value in total is less than 2^254 - validate_in_range::(result); let shift = 0x1000000000000000000000000000000; + // validate that the last limb is less than the modulus + if N > 2 { + // validate that the result is less than the modulus + let mut grumpkin_modulus = [0; N]; + grumpkin_modulus[0] = 0x33e84879b9709143e1f593f0000001; + grumpkin_modulus[1] = 0x4e72e131a029b85045b68181585d28; + grumpkin_modulus[2] = 0x3064; + validate_gt::(grumpkin_modulus, result); + // validate that the limbs are in range + validate_in_range::(result); + } // validate the limbs sum up to the field value let field_val = if N < 2 { result[0] } else if N == 2 { + validate_in_range::(result); result[0] + result[1] * shift } else { + validate_in_range::(result); result[0] + result[1] * shift + result[2] * shift * shift }; assert(field_val == field); From 29c753db888635e6a9362e5b5ed6c9622e89dffd Mon Sep 17 00:00:00 2001 From: Khashayar Barooti Date: Mon, 6 Jan 2025 15:39:05 +0000 Subject: [PATCH 7/7] formatter --- src/fns/constrained_ops.nr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fns/constrained_ops.nr b/src/fns/constrained_ops.nr index 751b9841..ee2aa8fb 100644 --- a/src/fns/constrained_ops.nr +++ b/src/fns/constrained_ops.nr @@ -1,4 +1,3 @@ -use crate::params::BigNumParams as P; use crate::fns::{ expressions::evaluate_quadratic_expression, unconstrained_helpers::{ @@ -7,6 +6,7 @@ use crate::fns::{ }, unconstrained_ops::{__div, __mul, __udiv_mod}, }; +use crate::params::BigNumParams as P; /** * In this file: @@ -46,13 +46,13 @@ pub(crate) fn from_field( let shift = 0x1000000000000000000000000000000; // validate that the last limb is less than the modulus if N > 2 { - // validate that the result is less than the modulus + // validate that the result is less than the modulus let mut grumpkin_modulus = [0; N]; grumpkin_modulus[0] = 0x33e84879b9709143e1f593f0000001; grumpkin_modulus[1] = 0x4e72e131a029b85045b68181585d28; grumpkin_modulus[2] = 0x3064; validate_gt::(grumpkin_modulus, result); - // validate that the limbs are in range + // validate that the limbs are in range validate_in_range::(result); } // validate the limbs sum up to the field value