diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 00000000..1799fc43 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,49 @@ +name: Benchmarks + +on: + push: + branches: + - main + pull_request: + +jobs: + test: + name: Benchmark library + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Install Nargo + uses: noir-lang/noirup@v0.1.3 + with: + toolchain: 1.0.0-beta.0 + + - name: Install bb + run: | + npm install -g bbup + bbup -nv 1.0.0-beta.0 + sudo apt install libc++-dev + + - name: Build Noir benchmark programs + run: nargo export + + - name: Generate gates report + run: ./scripts/build-gates-report.sh + env: + BACKEND: /home/runner/.bb/bb + + - name: Compare gates reports + id: gates_diff + uses: noir-lang/noir-gates-diff@1931aaaa848a1a009363d6115293f7b7fc72bb87 + with: + report: gates_report.json + summaryQuantile: 0.9 # only display the 10% most significant circuit size diffs in the summary (defaults to 20%) + + - name: Add gates diff to sticky comment + if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' + uses: marocchino/sticky-pull-request-comment@v2 + with: + # delete the comment in case changes no longer impact circuit sizes + delete: ${{ !steps.gates_diff.outputs.markdown }} + message: ${{ steps.gates_diff.outputs.markdown }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ef999484..98590389 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,10 +16,11 @@ jobs: runs-on: ubuntu-latest outputs: noir_versions: ${{ steps.get_versions.outputs.versions }} + steps: - name: Checkout sources id: get_versions - run: | + run: | # gh returns the Noir releases in reverse chronological order so we keep all releases published after the minimum supported version. VERSIONS=$(gh release list -R noir-lang/noir --exclude-pre-releases --json tagName -q 'map(.tagName) | index(env.MINIMUM_NOIR_VERSION) as $index | if $index then .[0:$index+1] else [env.MINIMUM_NOIR_VERSION] end') echo "versions=$VERSIONS" @@ -58,13 +59,11 @@ jobs: - name: Install Nargo uses: noir-lang/noirup@v0.1.3 with: - toolchain: ${{ env.MINIMUM_NOIR_VERSION }} - + toolchain: ${{env.MINIMUM_NOIR_VERSION}} - name: Run formatter run: nargo fmt --check - -# This is a job which depends on all test jobs and reports the overall status. + # This is a job which depends on all test jobs and reports the overall status. # This allows us to add/remove test jobs without having to update the required workflows. tests-end: name: Noir End @@ -85,4 +84,4 @@ jobs: fi env: # We treat any cancelled, skipped or failing jobs as a failure for the workflow as a whole. - FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }} + FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index f99b23da..422af23d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ target .vscode/launch.json +export/* +gates_report.json diff --git a/export/test_add_BN.json b/export/test_add_BN.json deleted file mode 100644 index 58b84a13..00000000 --- a/export/test_add_BN.json +++ /dev/null @@ -1 +0,0 @@ -{"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/info.sh b/info.sh deleted file mode 100755 index 1bf6a1fe..00000000 --- a/info.sh +++ /dev/null @@ -1 +0,0 @@ -nargo compile --force && bb gates -b ./target/bignum.json \ No newline at end of file diff --git a/scripts/build-gates-report.sh b/scripts/build-gates-report.sh new file mode 100755 index 00000000..f1043b28 --- /dev/null +++ b/scripts/build-gates-report.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -e + +BACKEND=${BACKEND:-bb} + +cd $(dirname "$0")/../ + +artifacts_path="./export" +artifacts=$(ls $artifacts_path) + +echo "{\"programs\": [" > gates_report.json + +# Bound for checking where to place last parentheses +NUM_ARTIFACTS=$(ls -1q "$artifacts_path" | wc -l) + +ITER="1" +for artifact in $artifacts; do + ARTIFACT_NAME=$(basename "$artifact") + + GATES_INFO=$($BACKEND gates -b "$artifacts_path/$artifact") + MAIN_FUNCTION_INFO=$(echo $GATES_INFO | jq -r '.functions[0] | .name = "main"') + echo "{\"package_name\": \"$ARTIFACT_NAME\", \"functions\": [$MAIN_FUNCTION_INFO]" >> gates_report.json + + if (($ITER == $NUM_ARTIFACTS)); then + echo "}" >> gates_report.json + else + echo "}, " >> gates_report.json + fi + + ITER=$(( $ITER + 1 )) +done + +echo "]}" >> gates_report.json + diff --git a/src/benchmarks/bignum_benchmarks.nr b/src/benchmarks/bignum_benchmarks.nr new file mode 100644 index 00000000..587847d6 --- /dev/null +++ b/src/benchmarks/bignum_benchmarks.nr @@ -0,0 +1,63 @@ +use crate::bignum::BigNum; + +use crate::fields::bn254Fq::BN254_Fq_Params; +use crate::fields::U256::U256Params; + +// the types we will be benchmarking + +type Fq = BigNum<3, 254, BN254_Fq_Params>; +type BN256 = BigNum<3, 257, U256Params>; + +// type Fq +// type BN256 +// type BN381 +// type BN2048 +#[export] +fn bench_add_Fq(a: Fq, b: Fq) -> Fq { + a + b +} + +#[export] +fn bench_sub_Fq(a: Fq, b: Fq) -> Fq { + a - b +} + +#[export] +fn bench_mul_Fq(a: Fq, b: Fq) -> Fq { + a * b +} + +#[export] +fn bench_div_Fq(a: Fq, b: Fq) -> Fq { + a / b +} + +#[export] +fn bench_add_BN256(a: BN256, b: BN256) -> BN256 { + a + b +} + +#[export] +fn bench_sub_BN256(a: BN256, b: BN256) -> BN256 { + a - b +} + +#[export] +fn bench_mul_BN256(a: BN256, b: BN256) -> BN256 { + a * b +} + +#[export] +fn bench_div_BN256(a: BN256, b: BN256) -> BN256 { + a / b +} + +#[export] +fn bench_from_field_Fq(a: Field) -> Fq { + Fq::from(a) +} + +#[export] +fn bench_from_field_BN256(a: Field) -> BN256 { + BN256::from(a) +} diff --git a/src/benchmarks/mod.nr b/src/benchmarks/mod.nr new file mode 100644 index 00000000..9a73d0af --- /dev/null +++ b/src/benchmarks/mod.nr @@ -0,0 +1 @@ +mod bignum_benchmarks; diff --git a/src/fns/constrained_ops.nr b/src/fns/constrained_ops.nr index 5d49c3b9..fcdf2147 100644 --- a/src/fns/constrained_ops.nr +++ b/src/fns/constrained_ops.nr @@ -28,13 +28,6 @@ use crate::params::BigNumParams as P; * umod */ -/** -* @brief given an input seed, generate a pseudorandom BigNum value -* @details we hash the input seed into `modulus_bits * 2` bits of entropy, -* which is then reduced into a BigNum value -* 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, @@ -70,6 +63,13 @@ pub(crate) fn from_field( result } +/** +* @brief given an input seed, generate a pseudorandom BigNum value +* @details we hash the input seed into `modulus_bits * 2` bits of entropy, +* which is then reduced into a BigNum value +* 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 derive_from_seed( params: P, seed: [u8; SeedBytes], diff --git a/src/lib.nr b/src/lib.nr index fa8840ff..d618e58e 100644 --- a/src/lib.nr +++ b/src/lib.nr @@ -21,3 +21,6 @@ pub use runtime_bignum::RuntimeBigNum; // Tests mod tests; + +// Benchmarks +mod benchmarks; diff --git a/src/tests/bignum_test.nr b/src/tests/bignum_test.nr index 2a4ba115..bc00bda5 100644 --- a/src/tests/bignum_test.nr +++ b/src/tests/bignum_test.nr @@ -815,4 +815,3 @@ fn test_from_field_3_digits() { }; assert(result == expected); } -