From bc2ad1620c2f01e8cbd0cb8b0dc2d8ac678e83f4 Mon Sep 17 00:00:00 2001 From: Khashayar Barooti Date: Tue, 24 Dec 2024 15:12:25 +0000 Subject: [PATCH 1/9] 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/9] 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/9] 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/9] 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/9] 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/9] 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/9] 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 From c132e7b922a53de89bb89961912956f512924c09 Mon Sep 17 00:00:00 2001 From: Khashayar Barooti Date: Tue, 7 Jan 2025 15:07:45 +0000 Subject: [PATCH 8/9] the import warning --- Nargo.toml | 2 +- src/bignum.nr | 2 +- src/fns/constrained_ops.nr | 3 +-- src/fns/unconstrained_helpers.nr | 2 +- src/fns/unconstrained_ops.nr | 2 +- src/lib.nr | 2 +- src/runtime_bignum.nr | 4 ++-- src/tests/runtime_bignum_test.nr | 7 ++++--- src/utils/u60_representation_test.nr | 12 ++++++------ 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Nargo.toml b/Nargo.toml index 1c3c348f..d1e894aa 100644 --- a/Nargo.toml +++ b/Nargo.toml @@ -2,6 +2,6 @@ name = "bignum" type = "lib" authors = [""] -compiler_version = ">=1.0.0" +compiler_version = ">=0.38.0" [dependencies] diff --git a/src/bignum.nr b/src/bignum.nr index 96dae3b9..74c89118 100644 --- a/src/bignum.nr +++ b/src/bignum.nr @@ -242,7 +242,7 @@ where unconstrained fn __tonelli_shanks_sqrt(self) -> std::option::Option { let params = Params::get_params(); - let maybe_limbs = unsafe { __tonelli_shanks_sqrt(params, self.limbs) }; + let maybe_limbs = __tonelli_shanks_sqrt(params, self.limbs) ; maybe_limbs.map(|limbs| Self { limbs }) } diff --git a/src/fns/constrained_ops.nr b/src/fns/constrained_ops.nr index ee2aa8fb..bc4a0fa4 100644 --- a/src/fns/constrained_ops.nr +++ b/src/fns/constrained_ops.nr @@ -39,8 +39,7 @@ pub(crate) fn from_field( params: P, field: Field, ) -> [Field; N] { - // 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 + // @safety : 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 let shift = 0x1000000000000000000000000000000; diff --git a/src/fns/unconstrained_helpers.nr b/src/fns/unconstrained_helpers.nr index 116e3932..63cb64ef 100644 --- a/src/fns/unconstrained_helpers.nr +++ b/src/fns/unconstrained_helpers.nr @@ -324,7 +324,7 @@ unconstrained fn __recursively_find_multiplicative_generator( params, _target, diff --git a/src/fns/unconstrained_ops.nr b/src/fns/unconstrained_ops.nr index e2322fa2..25ae2e2b 100644 --- a/src/fns/unconstrained_ops.nr +++ b/src/fns/unconstrained_ops.nr @@ -324,7 +324,7 @@ pub(crate) unconstrained fn __tonelli_shanks_sqrt // We can iteratively transform t into ever smaller subgroups, until t = 1. // At each iteration, we need to find a new value for b, which we can obtain // by repeatedly squaring z^{Q} - let one_u60: U60Repr = unsafe { U60Repr::one() }; + let one_u60: U60Repr = U60Repr::one() ; let primitive_root_log_size = __primitive_root_log_size::<_, MOD_BITS>(params); let mut Q = (params.modulus_u60 - one_u60).shr(primitive_root_log_size - 1); let Q_minus_one_over_two_u60 = (Q - one_u60).shr(2); diff --git a/src/lib.nr b/src/lib.nr index fa8840ff..f2f8ab21 100644 --- a/src/lib.nr +++ b/src/lib.nr @@ -18,6 +18,6 @@ pub(crate) mod utils; pub use bignum::BigNum; pub use bignum::BigNumTrait; // So that external code can operate on a generic BigNum, `where BigNum: BigNumTrait`. pub use runtime_bignum::RuntimeBigNum; - +pub use runtime_bignum::RuntimeBigNumTrait; // Tests mod tests; diff --git a/src/runtime_bignum.nr b/src/runtime_bignum.nr index 3cdc9c0e..609a71bc 100644 --- a/src/runtime_bignum.nr +++ b/src/runtime_bignum.nr @@ -138,7 +138,7 @@ impl RuntimeBigNumTrait for RuntimeB params: BigNumParams, seed: [u8; SeedBytes], ) -> Self { - let limbs = unsafe { derive_from_seed::<_, MOD_BITS, _>(params, seed) }; + let limbs = derive_from_seed::<_, MOD_BITS, _>(params, seed) ; Self { limbs, params } } @@ -281,7 +281,7 @@ impl RuntimeBigNumTrait for RuntimeB unconstrained fn __batch_invert_slice(x: [Self]) -> [Self] { let params = x[0].params; assert(params.has_multiplicative_inverse); - let all_limbs = unsafe { + let all_limbs = { let inv_slice = __batch_invert_slice::<_, MOD_BITS>(params, x.map(|bn| Self::get_limbs(bn))); inv_slice.as_array() diff --git a/src/tests/runtime_bignum_test.nr b/src/tests/runtime_bignum_test.nr index 3b7fe4f0..dce51ccc 100644 --- a/src/tests/runtime_bignum_test.nr +++ b/src/tests/runtime_bignum_test.nr @@ -210,7 +210,7 @@ comptime fn make_test(_m: Module, N: u32, MOD_BITS: u32, typ: Quoted) -> Quoted #[test] fn test_add() { let params = $typ ::get_params(); - + // safety: these are just random values for tests let a: $RuntimeBigNum<$N, $MOD_BITS> = unsafe{ $RuntimeBigNum::__derive_from_seed(params, [1, 2, 3, 4]) }; let b: $RuntimeBigNum<$N, $MOD_BITS> = unsafe{ $RuntimeBigNum::__derive_from_seed(params, [4, 5, 6, 7]) }; let one: $RuntimeBigNum<$N, $MOD_BITS> = $RuntimeBigNum::one(params); @@ -539,9 +539,9 @@ pub mod BLS12_377_Fr_ParamsTests {} fn test_div(params: BigNumParams) { let a: RuntimeBigNum = - unsafe { RuntimeBigNum::__derive_from_seed(params, [1, 2, 3, 4]) }; + RuntimeBigNum::__derive_from_seed(params, [1, 2, 3, 4]) ; let b: RuntimeBigNum = - unsafe { RuntimeBigNum::__derive_from_seed(params, [4, 5, 6, 7]) }; + RuntimeBigNum::__derive_from_seed(params, [4, 5, 6, 7]) ; let c = a / b; assert((b * c) == a); @@ -741,3 +741,4 @@ fn test_barrett_reduction_fix() { }; assert(remainder[8].lt(params.modulus[8])); } + diff --git a/src/utils/u60_representation_test.nr b/src/utils/u60_representation_test.nr index da1a38ec..642e9b89 100644 --- a/src/utils/u60_representation_test.nr +++ b/src/utils/u60_representation_test.nr @@ -4,8 +4,8 @@ use crate::utils::u60_representation::U60Repr; unconstrained fn test_conversion() { let p = 0xffffffffffffffffffffffffffffff; // 2^120 - 1 let expected: [Field; 3 * 2] = [p, p - 1, p - 2, p - 3, p - 4, p - 5]; - let u60repr: U60Repr<3, 4> = unsafe { U60Repr::new(expected) }; - let result = unsafe { u60repr.into_field_array() }; + let u60repr: U60Repr<3, 4> = U60Repr::new(expected) ; + let result = u60repr.into_field_array() ; assert(result == expected); } @@ -65,22 +65,22 @@ fn test_get_bit() { unconstrained fn test_gte() { let p = 0xffffffffffffffffffffffffffffff; // 2^120 - 1 let input: [Field; 6] = [p, p - 1, p - 2, p - 3, p - 4, p - 5]; - let lhs: U60Repr<3, 6> = unsafe { U60Repr::new(input) }; + let lhs: U60Repr<3, 6> = U60Repr::new(input) ; { let rhs = lhs; - assert(unsafe { lhs.gte(rhs) }); + assert( lhs.gte(rhs) ); } { // make rhs smaller by 1 let mut rhs: U60Repr<3, 6> = lhs; rhs.limbs[0] -= 1; - assert(unsafe { lhs.gte(rhs) }); + assert( lhs.gte(rhs) ); } { // make rhs greater by 1 let mut rhs = lhs; rhs.limbs[0] += 1; - assert(!unsafe { lhs.gte(rhs) }); + assert(! lhs.gte(rhs) ); } } From 1d4aa78b6f8259fcafe0f8bad92aeceef758a02e Mon Sep 17 00:00:00 2001 From: Khashayar Barooti Date: Tue, 7 Jan 2025 15:10:01 +0000 Subject: [PATCH 9/9] the import warning --- src/lib.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.nr b/src/lib.nr index f2f8ab21..a6b6ada6 100644 --- a/src/lib.nr +++ b/src/lib.nr @@ -18,6 +18,6 @@ pub(crate) mod utils; pub use bignum::BigNum; pub use bignum::BigNumTrait; // So that external code can operate on a generic BigNum, `where BigNum: BigNumTrait`. pub use runtime_bignum::RuntimeBigNum; -pub use runtime_bignum::RuntimeBigNumTrait; +pub use runtime_bignum::RuntimeBigNumTrait; // Tests mod tests;