From 1bfc15e08873a1f0f3743e259f418b70426b3f25 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:15:39 +0000 Subject: [PATCH] chore: pull out some sync changes (#10245) This pulls out some of the lower-risk changes from the sync. --- avm-transpiler/Cargo.lock | 53 +- .../dsl/acir_format/acir_integration.test.cpp | 2 +- .../crates/mock-types/src/lib.nr | 8 +- .../crates/types/Nargo.toml | 1 + .../crates/types/src/address/aztec_address.nr | 6 +- .../noir-repo/.github/ACVM_NOT_PUBLISHABLE.md | 2 +- noir/noir-repo/.release-please-manifest.json | 2 +- noir/noir-repo/CHANGELOG.md | 33 ++ noir/noir-repo/Cargo.lock | 56 +-- noir/noir-repo/Cargo.toml | 18 +- noir/noir-repo/acvm-repo/acir/Cargo.toml | 6 +- .../acir/src/circuit/black_box_functions.rs | 4 +- .../noir-repo/acvm-repo/acir_field/Cargo.toml | 2 +- .../acvm-repo/acir_field/src/field_element.rs | 77 +-- noir/noir-repo/acvm-repo/acvm/Cargo.toml | 13 +- noir/noir-repo/acvm-repo/acvm_js/Cargo.toml | 2 +- noir/noir-repo/acvm-repo/acvm_js/package.json | 2 +- .../acvm-repo/blackbox_solver/Cargo.toml | 2 +- .../acvm-repo/blackbox_solver/src/bigint.rs | 48 ++ .../acvm-repo/blackbox_solver/src/lib.rs | 2 +- .../bn254_blackbox_solver/Cargo.toml | 2 +- noir/noir-repo/acvm-repo/brillig/Cargo.toml | 2 +- .../noir-repo/acvm-repo/brillig_vm/Cargo.toml | 2 +- .../acvm-repo/brillig_vm/src/black_box.rs | 57 +-- .../noir-repo/acvm-repo/brillig_vm/src/lib.rs | 4 +- .../compiler/noirc_frontend/Cargo.toml | 4 +- .../compiler/noirc_frontend/src/ast/mod.rs | 66 +++ .../noirc_frontend/src/ast/statement.rs | 7 + .../src/elaborator/expressions.rs | 12 +- .../noirc_frontend/src/elaborator/mod.rs | 7 + .../src/elaborator/statements.rs | 13 + .../noirc_frontend/src/elaborator/types.rs | 3 +- .../noirc_frontend/src/hir/comptime/errors.rs | 10 + .../src/hir/comptime/interpreter.rs | 455 +++++++----------- .../src/hir/comptime/interpreter/builtin.rs | 40 +- .../interpreter/builtin/builtin_helpers.rs | 160 +++++- .../src/hir/comptime/interpreter/foreign.rs | 421 ++++++++++++++-- .../noirc_frontend/src/hir/comptime/tests.rs | 26 +- .../src/hir/def_collector/dc_mod.rs | 17 +- .../src/hir/def_collector/errors.rs | 12 +- .../noirc_frontend/src/hir/def_map/mod.rs | 6 +- .../src/hir/resolution/errors.rs | 9 + .../noirc_frontend/src/lexer/token.rs | 18 +- .../noirc_frontend/src/node_interner.rs | 2 +- .../compiler/noirc_frontend/src/tests.rs | 191 ++++++-- .../noirc_frontend/src/tests/unused_items.rs | 44 +- .../noirc_frontend/src/tests/visibility.rs | 105 ++++ .../noirc_frontend/src/usage_tracker.rs | 3 + noir/noir-repo/compiler/wasm/package.json | 2 +- noir/noir-repo/cspell.json | 2 + noir/noir-repo/noir_stdlib/src/bigint.nr | 12 +- .../noir_stdlib/src/collections/map.nr | 4 +- .../noir_stdlib/src/ec/consts/mod.nr | 1 - .../noir-repo/noir_stdlib/src/ec/consts/te.nr | 33 -- noir/noir-repo/noir_stdlib/src/ec/mod.nr | 199 -------- .../noir-repo/noir_stdlib/src/ec/montcurve.nr | 387 --------------- noir/noir-repo/noir_stdlib/src/ec/swcurve.nr | 394 --------------- noir/noir-repo/noir_stdlib/src/ec/tecurve.nr | 419 ---------------- noir/noir-repo/noir_stdlib/src/eddsa.nr | 76 --- noir/noir-repo/noir_stdlib/src/hash/sha256.nr | 18 +- noir/noir-repo/noir_stdlib/src/lib.nr | 2 - noir/noir-repo/noir_stdlib/src/mem.nr | 14 + .../bench_eddsa_poseidon/Nargo.toml | 1 + .../bench_eddsa_poseidon/src/main.nr | 57 ++- .../bench_poseidon2_hash_100/src/main.nr | 2 +- .../bench_poseidon2_hash_30/src/main.nr | 2 +- .../bench_poseidon_hash_100/src/main.nr | 2 +- .../bench_poseidon_hash_30/src/main.nr | 2 +- .../bench_poseidon_hash_100/src/main.nr | 2 +- .../bench_poseidon_hash_30/src/main.nr | 2 +- .../benchmarks/bench_sha256_100/src/main.nr | 2 +- .../benchmarks/bench_sha256_30/src/main.nr | 2 +- .../benchmarks/bench_sha256_long/src/main.nr | 2 +- .../assert_constant/src/main.nr | 10 +- .../comptime_globals_regression/src/main.nr | 2 +- .../comptime_module/src/main.nr | 2 +- .../ec_baby_jubjub/Nargo.toml | 7 - .../ec_baby_jubjub/src/main.nr | 210 -------- .../numeric_generics_explicit/src/main.nr | 2 +- .../src/main.nr | 4 +- .../raw_string/src/main.nr | 2 +- .../regression_2099/Nargo.toml | 2 + .../regression_2099/src/main.nr | 4 +- .../static_assert/src/main.nr | 10 +- .../src/main.nr | 4 +- .../databus_mapping_regression/src/main.nr | 4 +- .../bench_2_to_17/src/main.nr | 2 +- .../execution_success/brillig_cow/src/main.nr | 2 +- .../brillig_cow_assign/src/main.nr | 2 +- .../brillig_cow_regression/src/main.nr | 2 +- .../Nargo.toml | 3 +- .../Prover.toml | 0 .../src/main.nr | 0 .../execution_success/eddsa/Prover.toml | 3 - .../execution_success/eddsa/src/main.nr | 56 --- .../fmtstr_with_global/src/main.nr | 2 +- .../fold_2_to_17/src/main.nr | 2 +- .../fold_call_witness_condition/src/main.nr | 2 +- .../fold_numeric_generic_poseidon/src/main.nr | 4 +- .../global_consts/src/foo.nr | 2 +- .../global_consts/src/main.nr | 4 +- .../execution_success/hashmap/src/main.nr | 12 +- .../Nargo.toml | 2 +- .../negated_jmpif_condition/Prover.toml | 1 + .../negated_jmpif_condition/src/main.nr | 9 + .../src/main.nr | 2 +- .../ram_blowup_regression/src/main.nr | 2 +- .../regression_2660/src/main.nr | 2 +- .../regression_5252/src/main.nr | 4 +- .../sha256_var_size_regression/src/main.nr | 2 +- .../execution_success/strings/src/main.nr | 2 +- .../struct_inputs/src/foo/bar.nr | 2 +- .../execution_success/uhashmap/src/main.nr | 12 +- .../comptime_blackbox/Nargo.toml | 7 + .../comptime_blackbox/src/main.nr | 155 ++++++ .../test_libraries/diamond_deps_2/src/lib.nr | 2 +- .../tooling/debugger/ignored-tests.txt | 3 +- .../noir-repo/tooling/debugger/tests/debug.rs | 2 +- .../tooling/lsp/src/requests/completion.rs | 84 ++-- .../lsp/src/requests/completion/tests.rs | 33 ++ noir/noir-repo/tooling/nargo_cli/build.rs | 17 +- .../tooling/nargo_cli/src/cli/init_cmd.rs | 2 - .../nargo_fmt/src/formatter/expression.rs | 2 +- .../nargo_fmt/src/formatter/function.rs | 30 ++ .../tooling/nargo_toml/src/errors.rs | 2 + .../tooling/nargo_toml/src/semver.rs | 49 +- .../tooling/noir_codegen/package.json | 2 +- noir/noir-repo/tooling/noir_js/package.json | 2 +- .../tooling/noir_js_types/package.json | 2 +- noir/noir-repo/tooling/noirc_abi/Cargo.toml | 4 +- .../tooling/noirc_abi_wasm/package.json | 2 +- .../src/contract/artifact_hash.test.ts | 6 +- yarn-project/yarn.lock | 16 +- 133 files changed, 1957 insertions(+), 2542 deletions(-) delete mode 100644 noir/noir-repo/noir_stdlib/src/ec/consts/mod.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/ec/consts/te.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/ec/mod.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/ec/montcurve.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/ec/swcurve.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/ec/tecurve.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/eddsa.nr delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/ec_baby_jubjub/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr rename noir/noir-repo/test_programs/execution_success/{eddsa => brillig_uninitialized_arrays}/Nargo.toml (51%) rename noir/noir-repo/test_programs/execution_success/{brillig_unitialised_arrays => brillig_uninitialized_arrays}/Prover.toml (100%) rename noir/noir-repo/test_programs/execution_success/{brillig_unitialised_arrays => brillig_uninitialized_arrays}/src/main.nr (100%) delete mode 100644 noir/noir-repo/test_programs/execution_success/eddsa/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/eddsa/src/main.nr rename noir/noir-repo/test_programs/execution_success/{brillig_unitialised_arrays => negated_jmpif_condition}/Nargo.toml (60%) create mode 100644 noir/noir-repo/test_programs/execution_success/negated_jmpif_condition/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/negated_jmpif_condition/src/main.nr create mode 100644 noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/Nargo.toml create mode 100644 noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr diff --git a/avm-transpiler/Cargo.lock b/avm-transpiler/Cargo.lock index 680ce0e1280..bdac1771a70 100644 --- a/avm-transpiler/Cargo.lock +++ b/avm-transpiler/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "acir" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "acir_field", "base64 0.21.7", @@ -13,12 +13,14 @@ dependencies = [ "flate2", "serde", "serde-big-array", + "strum", + "strum_macros", "thiserror", ] [[package]] name = "acir_field" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "ark-bn254", "ark-ff", @@ -30,7 +32,7 @@ dependencies = [ [[package]] name = "acvm" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -43,7 +45,7 @@ dependencies = [ [[package]] name = "acvm_blackbox_solver" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "acir", "blake2", @@ -368,7 +370,7 @@ dependencies = [ [[package]] name = "brillig" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "acir_field", "serde", @@ -376,7 +378,7 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -667,7 +669,7 @@ dependencies = [ [[package]] name = "fm" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "codespan-reporting", "iter-extended", @@ -733,6 +735,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hex" version = "0.4.3" @@ -813,7 +821,7 @@ checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" [[package]] name = "iter-extended" -version = "0.39.0" +version = "1.0.0-beta.0" [[package]] name = "itertools" @@ -918,7 +926,7 @@ dependencies = [ [[package]] name = "noirc_errors" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "base64 0.21.7", @@ -935,7 +943,7 @@ dependencies = [ [[package]] name = "noirc_printable_type" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "iter-extended", @@ -1127,6 +1135,12 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + [[package]] name = "ryu" version = "1.0.18" @@ -1260,6 +1274,25 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp index 9dba2ea833d..45bf0703edb 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp @@ -242,7 +242,7 @@ INSTANTIATE_TEST_SUITE_P(AcirTests, "brillig_to_bytes_integration", "brillig_to_le_bytes", "brillig_top_level", - "brillig_unitialised_arrays", + "brillig_uninitialized_arrays", "brillig_wrapping", "cast_bool", "closures_mut_ref", diff --git a/noir-projects/mock-protocol-circuits/crates/mock-types/src/lib.nr b/noir-projects/mock-protocol-circuits/crates/mock-types/src/lib.nr index 55155921155..933e812174a 100644 --- a/noir-projects/mock-protocol-circuits/crates/mock-types/src/lib.nr +++ b/noir-projects/mock-protocol-circuits/crates/mock-types/src/lib.nr @@ -1,7 +1,7 @@ -global MAX_COMMITMENTS_PER_CALL = 2; -global MAX_COMMITMENTS_PER_TX = 4; -global MAX_COMMITMENT_READ_REQUESTS_PER_CALL = 2; -global MAX_COMMITMENT_READ_REQUESTS_PER_TX = 4; +global MAX_COMMITMENTS_PER_CALL: u32 = 2; +global MAX_COMMITMENTS_PER_TX: u32 = 4; +global MAX_COMMITMENT_READ_REQUESTS_PER_CALL: u32 = 2; +global MAX_COMMITMENT_READ_REQUESTS_PER_TX: u32 = 4; struct TxRequest { number_of_calls: u32, diff --git a/noir-projects/noir-protocol-circuits/crates/types/Nargo.toml b/noir-projects/noir-protocol-circuits/crates/types/Nargo.toml index 6c8b6657f62..eb20a902120 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/Nargo.toml +++ b/noir-projects/noir-protocol-circuits/crates/types/Nargo.toml @@ -5,3 +5,4 @@ authors = [""] compiler_version = ">=0.18.0" [dependencies] +ec = { tag = "v0.1.2", git = "https://github.com/noir-lang/ec" } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr index 0399b8017b7..f66f0418fe7 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr @@ -19,10 +19,8 @@ use crate::{ use dep::std::embedded_curve_ops::EmbeddedCurvePoint as Point; use crate::public_keys::AddressPoint; -use std::{ - ec::{pow, sqrt}, - embedded_curve_ops::{EmbeddedCurveScalar, fixed_base_scalar_mul as derive_public_key}, -}; +use ec::{pow, sqrt}; +use std::embedded_curve_ops::{EmbeddedCurveScalar, fixed_base_scalar_mul as derive_public_key}; // Aztec address pub struct AztecAddress { diff --git a/noir/noir-repo/.github/ACVM_NOT_PUBLISHABLE.md b/noir/noir-repo/.github/ACVM_NOT_PUBLISHABLE.md index 33230f8e8d8..06c9505ebae 100644 --- a/noir/noir-repo/.github/ACVM_NOT_PUBLISHABLE.md +++ b/noir/noir-repo/.github/ACVM_NOT_PUBLISHABLE.md @@ -5,7 +5,7 @@ assignees: TomAFrench, Savio-Sou The ACVM crates are currently unpublishable, making a release will NOT push our crates to crates.io. -This is likely due to a crate we depend on bumping its MSRV above our own. Our lockfile is not taken into account when publishing to crates.io (as people downloading our crate don't use it) so we need to be able to use the most up to date versions of our dependencies (including transient dependencies) specified. +This is likely due to a crate we depend on bumping its MSRV above our own. Our lockfile is not taken into account when publishing to crates.io (as people downloading our crate don't use it) so we need to be able to use the most up-to-date versions of our dependencies (including transient dependencies) specified. Check the [MSRV check]({{env.WORKFLOW_URL}}) workflow for details. diff --git a/noir/noir-repo/.release-please-manifest.json b/noir/noir-repo/.release-please-manifest.json index 418b49e9957..b5c8da729e3 100644 --- a/noir/noir-repo/.release-please-manifest.json +++ b/noir/noir-repo/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.39.0" + ".": "1.0.0-beta.0" } diff --git a/noir/noir-repo/CHANGELOG.md b/noir/noir-repo/CHANGELOG.md index 9b9e3ba76b5..19d36b885ed 100644 --- a/noir/noir-repo/CHANGELOG.md +++ b/noir/noir-repo/CHANGELOG.md @@ -1,5 +1,38 @@ # Changelog +## [1.0.0-beta.0](https://github.com/noir-lang/noir/compare/v0.39.0...v1.0.0-beta.0) (2024-11-22) + + +### ⚠ BREAKING CHANGES + +* Require types of globals to be specified ([#6592](https://github.com/noir-lang/noir/issues/6592)) +* remove eddsa from stdlib ([#6591](https://github.com/noir-lang/noir/issues/6591)) + +### Features + +* Add `array_refcount` and `slice_refcount` builtins for debugging ([#6584](https://github.com/noir-lang/noir/issues/6584)) ([45eb756](https://github.com/noir-lang/noir/commit/45eb7568d56b2d254453b85f236d554232aa5df9)) +* Avoid incrementing reference counts in some cases ([#6568](https://github.com/noir-lang/noir/issues/6568)) ([01c4a9f](https://github.com/noir-lang/noir/commit/01c4a9fb62ffe2190c73f0d5b12933d2eb8f6b5d)) +* **ssa:** Loop invariant code motion ([#6563](https://github.com/noir-lang/noir/issues/6563)) ([7216f08](https://github.com/noir-lang/noir/commit/7216f0829dcece948d3243471e6d57380522e997)) +* Trait aliases ([#6431](https://github.com/noir-lang/noir/issues/6431)) ([68c32b4](https://github.com/noir-lang/noir/commit/68c32b4ffd9b069fe4b119327dbf4018c17ab9d4)) +* Try to inline brillig calls with all constant arguments ([#6548](https://github.com/noir-lang/noir/issues/6548)) ([e4c66b9](https://github.com/noir-lang/noir/commit/e4c66b91d42b20d17837fe5e7c32c9a83b6ab354)) + + +### Bug Fixes + +* Consider prereleases to be compatible with pre-1.0.0 releases ([#6580](https://github.com/noir-lang/noir/issues/6580)) ([013e200](https://github.com/noir-lang/noir/commit/013e2000f1d7e7346b5cac0427732d545f501444)) +* Correct type when simplifying `derive_pedersen_generators` ([#6579](https://github.com/noir-lang/noir/issues/6579)) ([efa5cc4](https://github.com/noir-lang/noir/commit/efa5cc4bf173b0ce49f47b1954165a2bdb276792)) +* Don't report visibility errors when elaborating comptime value ([#6498](https://github.com/noir-lang/noir/issues/6498)) ([3c361c9](https://github.com/noir-lang/noir/commit/3c361c9f78a5d9de1b1bcb5a839d3bc481f89898)) +* Parse a bit more SSA stuff ([#6599](https://github.com/noir-lang/noir/issues/6599)) ([0a6207d](https://github.com/noir-lang/noir/commit/0a6207dde6c744e2853905014e70d33b29b3e53b)) +* Preserve newlines between comments when formatting statements ([#6601](https://github.com/noir-lang/noir/issues/6601)) ([d94eb08](https://github.com/noir-lang/noir/commit/d94eb085adf2cdd8f0e80d9cfd712c19c8810974)) +* Remove `compiler_version` from new `Nargo.toml` ([#6590](https://github.com/noir-lang/noir/issues/6590)) ([df8f2ee](https://github.com/noir-lang/noir/commit/df8f2eee5c27d3cd4b6128056afdd9bd4a0322fe)) + + +### Miscellaneous Chores + +* Remove eddsa from stdlib ([#6591](https://github.com/noir-lang/noir/issues/6591)) ([8e046af](https://github.com/noir-lang/noir/commit/8e046afbbe3fba06c1e177f74aacefdd1bf871b6)) +* Require types of globals to be specified ([#6592](https://github.com/noir-lang/noir/issues/6592)) ([8ff4efd](https://github.com/noir-lang/noir/commit/8ff4efda5589d39d31ced31c6575f43133fceebc)) +* Switch to 1.0.0-beta versioning ([#6503](https://github.com/noir-lang/noir/issues/6503)) ([44e7dc1](https://github.com/noir-lang/noir/commit/44e7dc1037b047db866af675cd8caa0fc8aee324)) + ## [0.39.0](https://github.com/noir-lang/noir/compare/v0.38.0...v0.39.0) (2024-11-19) diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index 6b24c0f8c67..94a84b89d05 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "acir" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "acir_field", "base64 0.21.7", @@ -26,7 +26,7 @@ dependencies = [ [[package]] name = "acir_field" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -40,7 +40,7 @@ dependencies = [ [[package]] name = "acvm" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "acvm_blackbox_solver" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "acir", "blake2", @@ -96,7 +96,7 @@ dependencies = [ [[package]] name = "acvm_js" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "bn254_blackbox_solver", @@ -591,7 +591,7 @@ dependencies = [ [[package]] name = "bn254_blackbox_solver" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -609,7 +609,7 @@ dependencies = [ [[package]] name = "brillig" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "acir_field", "serde", @@ -617,7 +617,7 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "0.55.0" +version = "1.0.0-beta.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -1571,7 +1571,7 @@ dependencies = [ [[package]] name = "fm" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "codespan-reporting", "iter-extended", @@ -2324,7 +2324,7 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "iter-extended" -version = "0.39.0" +version = "1.0.0-beta.0" [[package]] name = "itertools" @@ -2743,7 +2743,7 @@ dependencies = [ [[package]] name = "nargo" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "fm", @@ -2770,7 +2770,7 @@ dependencies = [ [[package]] name = "nargo_cli" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "ark-bn254", @@ -2832,7 +2832,7 @@ dependencies = [ [[package]] name = "nargo_fmt" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "noirc_frontend", "serde", @@ -2843,7 +2843,7 @@ dependencies = [ [[package]] name = "nargo_toml" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "dirs", "fm", @@ -2917,7 +2917,7 @@ dependencies = [ [[package]] name = "noir_debugger" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "assert_cmd", @@ -2941,7 +2941,7 @@ dependencies = [ [[package]] name = "noir_fuzzer" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "noirc_abi", @@ -2964,7 +2964,7 @@ dependencies = [ [[package]] name = "noir_lsp" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "async-lsp", @@ -2991,7 +2991,7 @@ dependencies = [ [[package]] name = "noir_profiler" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acir", "bn254_blackbox_solver", @@ -3017,7 +3017,7 @@ dependencies = [ [[package]] name = "noir_wasm" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "build-data", @@ -3041,7 +3041,7 @@ dependencies = [ [[package]] name = "noirc_abi" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "iter-extended", @@ -3060,7 +3060,7 @@ dependencies = [ [[package]] name = "noirc_abi_wasm" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "build-data", @@ -3077,11 +3077,11 @@ dependencies = [ [[package]] name = "noirc_arena" -version = "0.39.0" +version = "1.0.0-beta.0" [[package]] name = "noirc_artifacts" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "codespan-reporting", @@ -3096,7 +3096,7 @@ dependencies = [ [[package]] name = "noirc_driver" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "build-data", @@ -3115,7 +3115,7 @@ dependencies = [ [[package]] name = "noirc_errors" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "base64 0.21.7", @@ -3132,7 +3132,7 @@ dependencies = [ [[package]] name = "noirc_evaluator" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "bn254_blackbox_solver", @@ -3157,7 +3157,7 @@ dependencies = [ [[package]] name = "noirc_frontend" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "base64 0.21.7", @@ -3189,7 +3189,7 @@ dependencies = [ [[package]] name = "noirc_printable_type" -version = "0.39.0" +version = "1.0.0-beta.0" dependencies = [ "acvm", "iter-extended", diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index 1bfcb99e14e..94ebe54fde1 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -40,7 +40,7 @@ resolver = "2" [workspace.package] # x-release-please-start-version -version = "0.39.0" +version = "1.0.0-beta.0" # x-release-please-end authors = ["The Noir Team "] edition = "2021" @@ -57,13 +57,13 @@ unused_qualifications = "warn" [workspace.dependencies] # ACVM workspace dependencies -acir_field = { version = "0.55.0", path = "acvm-repo/acir_field", default-features = false } -acir = { version = "0.55.0", path = "acvm-repo/acir", default-features = false } -acvm = { version = "0.55.0", path = "acvm-repo/acvm" } -brillig = { version = "0.55.0", path = "acvm-repo/brillig", default-features = false } -brillig_vm = { version = "0.55.0", path = "acvm-repo/brillig_vm", default-features = false } -acvm_blackbox_solver = { version = "0.55.0", path = "acvm-repo/blackbox_solver", default-features = false } -bn254_blackbox_solver = { version = "0.55.0", path = "acvm-repo/bn254_blackbox_solver", default-features = false } +acir_field = { version = "1.0.0-beta.0", path = "acvm-repo/acir_field", default-features = false } +acir = { version = "1.0.0-beta.0", path = "acvm-repo/acir", default-features = false } +acvm = { version = "1.0.0-beta.0", path = "acvm-repo/acvm" } +brillig = { version = "1.0.0-beta.0", path = "acvm-repo/brillig", default-features = false } +brillig_vm = { version = "1.0.0-beta.0", path = "acvm-repo/brillig_vm", default-features = false } +acvm_blackbox_solver = { version = "1.0.0-beta.0", path = "acvm-repo/blackbox_solver", default-features = false } +bn254_blackbox_solver = { version = "1.0.0-beta.0", path = "acvm-repo/bn254_blackbox_solver", default-features = false } # Noir compiler workspace dependencies fm = { path = "compiler/fm" } @@ -157,6 +157,8 @@ proptest-derive = "0.4.0" rayon = "1.8.0" sha2 = { version = "0.10.6", features = ["compress"] } sha3 = "0.10.6" +strum = "0.24" +strum_macros = "0.24" im = { version = "15.1", features = ["serde"] } tracing = "0.1.40" diff --git a/noir/noir-repo/acvm-repo/acir/Cargo.toml b/noir/noir-repo/acvm-repo/acir/Cargo.toml index c0f8040580b..8139a58eefc 100644 --- a/noir/noir-repo/acvm-repo/acir/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir/Cargo.toml @@ -2,7 +2,7 @@ name = "acir" description = "ACIR is the IR that the VM processes, it is analogous to LLVM IR" # x-release-please-start-version -version = "0.55.0" +version = "1.0.0-beta.0" # x-release-please-end authors.workspace = true edition.workspace = true @@ -24,11 +24,11 @@ flate2.workspace = true bincode.workspace = true base64.workspace = true serde-big-array = "0.5.1" +strum = { workspace = true } +strum_macros = { workspace = true } [dev-dependencies] serde_json = "1.0" -strum = "0.24" -strum_macros = "0.24" serde-reflection = "0.3.6" serde-generate = "0.25.1" fxhash.workspace = true diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/black_box_functions.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/black_box_functions.rs index 2e5a94f1c50..25842c14dbc 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/black_box_functions.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/black_box_functions.rs @@ -4,12 +4,10 @@ //! implemented in more basic constraints. use serde::{Deserialize, Serialize}; -#[cfg(test)] use strum_macros::EnumIter; #[allow(clippy::upper_case_acronyms)] -#[derive(Clone, Debug, Hash, Copy, PartialEq, Eq, Serialize, Deserialize)] -#[cfg_attr(test, derive(EnumIter))] +#[derive(Clone, Debug, Hash, Copy, PartialEq, Eq, Serialize, Deserialize, EnumIter)] pub enum BlackBoxFunc { /// Ciphers (encrypts) the provided plaintext using AES128 in CBC mode, /// padding the input using PKCS#7. diff --git a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml index 98250e05a2d..039aefe355e 100644 --- a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml @@ -2,7 +2,7 @@ name = "acir_field" description = "The field implementation being used by ACIR." # x-release-please-start-version -version = "0.55.0" +version = "1.0.0-beta.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs index 47ceb903111..0249b410aa7 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs @@ -9,7 +9,7 @@ use crate::AcirField; // XXX: Switch out for a trait and proper implementations // This implementation is inefficient, can definitely remove hex usage and Iterator instances for trivial functionality -#[derive(Default, Clone, Copy, Eq, PartialOrd, Ord)] +#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct FieldElement(F); impl std::fmt::Display for FieldElement { @@ -43,18 +43,6 @@ impl std::fmt::Debug for FieldElement { } } -impl std::hash::Hash for FieldElement { - fn hash(&self, state: &mut H) { - state.write(&self.to_be_bytes()); - } -} - -impl PartialEq for FieldElement { - fn eq(&self, other: &Self) -> bool { - self.to_be_bytes() == other.to_be_bytes() - } -} - impl From for FieldElement { fn from(mut a: i128) -> FieldElement { let mut negative = false; @@ -158,23 +146,6 @@ impl FieldElement { let fr = F::from_str(input).ok()?; Some(FieldElement(fr)) } - - fn bits(&self) -> Vec { - fn byte_to_bit(byte: u8) -> Vec { - let mut bits = Vec::with_capacity(8); - for index in (0..=7).rev() { - bits.push((byte & (1 << index)) >> index == 1); - } - bits - } - - let bytes = self.to_be_bytes(); - let mut bits = Vec::with_capacity(bytes.len() * 8); - for byte in bytes { - bits.extend(byte_to_bit(byte)); - } - bits - } } impl AcirField for FieldElement { @@ -224,12 +195,26 @@ impl AcirField for FieldElement { /// This is the number of bits required to represent this specific field element fn num_bits(&self) -> u32 { - let bits = self.bits(); - // Iterate the number of bits and pop off all leading zeroes - let iter = bits.iter().skip_while(|x| !(**x)); + let bytes = self.to_be_bytes(); + + // Iterate through the byte decomposition and pop off all leading zeroes + let mut iter = bytes.iter().skip_while(|x| (**x) == 0); + + // The first non-zero byte in the decomposition may have some leading zero-bits. + let Some(head_byte) = iter.next() else { + // If we don't have a non-zero byte then the field element is zero, + // which we consider to require a single bit to represent. + return 1; + }; + let num_bits_for_head_byte = head_byte.ilog2(); + + // Each remaining byte in the byte decomposition requires 8 bits. + // // Note: count will panic if it goes over usize::MAX. // This may not be suitable for devices whose usize < u16 - iter.count() as u32 + let tail_length = iter.count() as u32; + + 8 * tail_length + num_bits_for_head_byte + 1 } fn to_u128(self) -> u128 { @@ -374,6 +359,30 @@ mod tests { use super::{AcirField, FieldElement}; use proptest::prelude::*; + #[test] + fn requires_one_bit_to_hold_zero() { + let field = FieldElement::::zero(); + assert_eq!(field.num_bits(), 1); + } + + proptest! { + #[test] + fn num_bits_agrees_with_ilog2(num in 1u128..) { + let field = FieldElement::::from(num); + prop_assert_eq!(field.num_bits(), num.ilog2() + 1); + } + } + + #[test] + fn test_fits_in_u128() { + let field = FieldElement::::from(u128::MAX); + assert_eq!(field.num_bits(), 128); + assert!(field.fits_in_u128()); + let big_field = field + FieldElement::one(); + assert_eq!(big_field.num_bits(), 129); + assert!(!big_field.fits_in_u128()); + } + #[test] fn serialize_fixed_test_vectors() { // Serialized field elements from of 0, -1, -2, -3 diff --git a/noir/noir-repo/acvm-repo/acvm/Cargo.toml b/noir/noir-repo/acvm-repo/acvm/Cargo.toml index 5d1bf5e8fee..e513ae4e727 100644 --- a/noir/noir-repo/acvm-repo/acvm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm" description = "The virtual machine that processes ACIR given a backend/proof system." # x-release-please-start-version -version = "0.55.0" +version = "1.0.0-beta.0" # x-release-please-end authors.workspace = true edition.workspace = true @@ -25,11 +25,7 @@ acvm_blackbox_solver.workspace = true indexmap = "1.7.0" [features] -bn254 = [ - "acir/bn254", - "brillig_vm/bn254", - "acvm_blackbox_solver/bn254", -] +bn254 = ["acir/bn254", "brillig_vm/bn254", "acvm_blackbox_solver/bn254"] bls12_381 = [ "acir/bls12_381", "brillig_vm/bls12_381", @@ -37,10 +33,11 @@ bls12_381 = [ ] [dev-dependencies] -ark-bls12-381 = { version = "^0.4.0", default-features = false, features = ["curve"] } +ark-bls12-381 = { version = "^0.4.0", default-features = false, features = [ + "curve", +] } ark-bn254.workspace = true bn254_blackbox_solver.workspace = true proptest.workspace = true zkhash = { version = "^0.2.0", default-features = false } num-bigint.workspace = true - diff --git a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml index 92934846f7b..bd536817428 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_js" description = "Typescript wrapper around the ACVM allowing execution of ACIR code" # x-release-please-start-version -version = "0.55.0" +version = "1.0.0-beta.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/acvm_js/package.json b/noir/noir-repo/acvm-repo/acvm_js/package.json index bfe408c3d97..904263b5e27 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/package.json +++ b/noir/noir-repo/acvm-repo/acvm_js/package.json @@ -1,6 +1,6 @@ { "name": "@noir-lang/acvm_js", - "version": "0.55.0", + "version": "1.0.0-beta.0", "publishConfig": { "access": "public" }, diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml index 3c2efa10f4b..fe3a938c503 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_blackbox_solver" description = "A solver for the blackbox functions found in ACIR and Brillig" # x-release-please-start-version -version = "0.55.0" +version = "1.0.0-beta.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs index b8bc9dc0d70..540862843ab 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs @@ -97,3 +97,51 @@ impl BigIntSolver { Ok(()) } } + +/// Wrapper over the generic bigint solver to automatically assign bigint IDs. +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct BigIntSolverWithId { + solver: BigIntSolver, + last_id: u32, +} + +impl BigIntSolverWithId { + pub fn create_bigint_id(&mut self) -> u32 { + let output = self.last_id; + self.last_id += 1; + output + } + + pub fn bigint_from_bytes( + &mut self, + inputs: &[u8], + modulus: &[u8], + ) -> Result { + let id = self.create_bigint_id(); + self.solver.bigint_from_bytes(inputs, modulus, id)?; + Ok(id) + } + + pub fn bigint_to_bytes(&self, input: u32) -> Result, BlackBoxResolutionError> { + self.solver.bigint_to_bytes(input) + } + + pub fn bigint_op( + &mut self, + lhs: u32, + rhs: u32, + func: BlackBoxFunc, + ) -> Result { + let modulus_lhs = self.solver.get_modulus(lhs, func)?; + let modulus_rhs = self.solver.get_modulus(rhs, func)?; + if modulus_lhs != modulus_rhs { + return Err(BlackBoxResolutionError::Failed( + func, + "moduli should be identical in BigInt operation".to_string(), + )); + } + let id = self.create_bigint_id(); + self.solver.bigint_op(lhs, rhs, id, func)?; + Ok(id) + } +} diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/lib.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/lib.rs index d8f926fcb4b..0fa56c2f531 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/lib.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/lib.rs @@ -18,7 +18,7 @@ mod hash; mod logic; pub use aes128::aes128_encrypt; -pub use bigint::BigIntSolver; +pub use bigint::{BigIntSolver, BigIntSolverWithId}; pub use curve_specific_solver::{BlackBoxFunctionSolver, StubbedBlackBoxSolver}; pub use ecdsa::{ecdsa_secp256k1_verify, ecdsa_secp256r1_verify}; pub use hash::{blake2s, blake3, keccakf1600, sha256_compression}; diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml index ebbee196d7b..8829692b9b4 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "bn254_blackbox_solver" description = "Solvers for black box functions which are specific for the bn254 curve" # x-release-please-start-version -version = "0.55.0" +version = "1.0.0-beta.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/brillig/Cargo.toml b/noir/noir-repo/acvm-repo/brillig/Cargo.toml index c574de37ced..9cc724f2b11 100644 --- a/noir/noir-repo/acvm-repo/brillig/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig" description = "Brillig is the bytecode ACIR uses for non-determinism." # x-release-please-start-version -version = "0.55.0" +version = "1.0.0-beta.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml index ff35496afed..8225244f9a7 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig_vm" description = "The virtual machine that processes Brillig bytecode, used to introduce non-determinism to the ACVM" # x-release-please-start-version -version = "0.55.0" +version = "1.0.0-beta.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs index 0d90a4c8502..19e2dd7553d 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs @@ -1,9 +1,8 @@ use acir::brillig::{BlackBoxOp, HeapArray, HeapVector, IntegerBitSize}; use acir::{AcirField, BlackBoxFunc}; -use acvm_blackbox_solver::BigIntSolver; use acvm_blackbox_solver::{ aes128_encrypt, blake2s, blake3, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccakf1600, - sha256_compression, BlackBoxFunctionSolver, BlackBoxResolutionError, + sha256_compression, BigIntSolverWithId, BlackBoxFunctionSolver, BlackBoxResolutionError, }; use num_bigint::BigUint; use num_traits::Zero; @@ -39,11 +38,13 @@ fn to_value_vec(input: &[u8]) -> Vec> { input.iter().map(|&x| x.into()).collect() } +pub(crate) type BrilligBigIntSolver = BigIntSolverWithId; + pub(crate) fn evaluate_black_box>( op: &BlackBoxOp, solver: &Solver, memory: &mut Memory, - bigint_solver: &mut BrilligBigintSolver, + bigint_solver: &mut BrilligBigIntSolver, ) -> Result<(), BlackBoxResolutionError> { match op { BlackBoxOp::AES128Encrypt { inputs, iv, key, outputs } => { @@ -56,7 +57,7 @@ pub(crate) fn evaluate_black_box })?; let key: [u8; 16] = to_u8_vec(read_heap_array(memory, key)).try_into().map_err(|_| { - BlackBoxResolutionError::Failed(bb_func, "Invalid ley length".to_string()) + BlackBoxResolutionError::Failed(bb_func, "Invalid key length".to_string()) })?; let ciphertext = aes128_encrypt(&inputs, iv, key)?; @@ -353,54 +354,6 @@ pub(crate) fn evaluate_black_box } } -/// Wrapper over the generic bigint solver to automatically assign bigint ids in brillig -#[derive(Default, Debug, Clone, PartialEq, Eq)] -pub(crate) struct BrilligBigintSolver { - bigint_solver: BigIntSolver, - last_id: u32, -} - -impl BrilligBigintSolver { - pub(crate) fn create_bigint_id(&mut self) -> u32 { - let output = self.last_id; - self.last_id += 1; - output - } - - pub(crate) fn bigint_from_bytes( - &mut self, - inputs: &[u8], - modulus: &[u8], - ) -> Result { - let id = self.create_bigint_id(); - self.bigint_solver.bigint_from_bytes(inputs, modulus, id)?; - Ok(id) - } - - pub(crate) fn bigint_to_bytes(&self, input: u32) -> Result, BlackBoxResolutionError> { - self.bigint_solver.bigint_to_bytes(input) - } - - pub(crate) fn bigint_op( - &mut self, - lhs: u32, - rhs: u32, - func: BlackBoxFunc, - ) -> Result { - let modulus_lhs = self.bigint_solver.get_modulus(lhs, func)?; - let modulus_rhs = self.bigint_solver.get_modulus(rhs, func)?; - if modulus_lhs != modulus_rhs { - return Err(BlackBoxResolutionError::Failed( - func, - "moduli should be identical in BigInt operation".to_string(), - )); - } - let id = self.create_bigint_id(); - self.bigint_solver.bigint_op(lhs, rhs, id, func)?; - Ok(id) - } -} - fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc { match op { BlackBoxOp::AES128Encrypt { .. } => BlackBoxFunc::AES128Encrypt, diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs index 45025fbb208..5b3688339b5 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs @@ -17,7 +17,7 @@ use acir::brillig::{ use acir::AcirField; use acvm_blackbox_solver::BlackBoxFunctionSolver; use arithmetic::{evaluate_binary_field_op, evaluate_binary_int_op, BrilligArithmeticError}; -use black_box::{evaluate_black_box, BrilligBigintSolver}; +use black_box::{evaluate_black_box, BrilligBigIntSolver}; // Re-export `brillig`. pub use acir::brillig; @@ -95,7 +95,7 @@ pub struct VM<'a, F, B: BlackBoxFunctionSolver> { /// The solver for blackbox functions black_box_solver: &'a B, // The solver for big integers - bigint_solver: BrilligBigintSolver, + bigint_solver: BrilligBigIntSolver, // Flag that determines whether we want to profile VM. profiling_active: bool, // Samples for profiling the VM execution. diff --git a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml index 581d7f1b61d..5d1520af54f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml @@ -30,8 +30,8 @@ cfg-if.workspace = true tracing.workspace = true petgraph = "0.6" rangemap = "1.4.0" -strum = "0.24" -strum_macros = "0.24" +strum.workspace = true +strum_macros.workspace = true [dev-dependencies] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs index 3c6664dd569..35e57cd4528 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs @@ -200,6 +200,14 @@ impl GenericTypeArgs { pub fn is_empty(&self) -> bool { self.ordered_args.is_empty() && self.named_args.is_empty() } + + fn contains_unspecified(&self) -> bool { + let ordered_args_contains_unspecified = + self.ordered_args.iter().any(|ordered_arg| ordered_arg.contains_unspecified()); + let named_args_contains_unspecified = + self.named_args.iter().any(|(_name, named_arg)| named_arg.contains_unspecified()); + ordered_args_contains_unspecified || named_args_contains_unspecified + } } impl From> for GenericTypeArgs { @@ -375,6 +383,10 @@ impl UnresolvedType { let typ = UnresolvedTypeData::Named(path, generic_type_args, true); UnresolvedType { typ, span } } + + pub(crate) fn contains_unspecified(&self) -> bool { + self.typ.contains_unspecified() + } } impl UnresolvedTypeData { @@ -395,6 +407,47 @@ impl UnresolvedTypeData { pub fn with_span(&self, span: Span) -> UnresolvedType { UnresolvedType { typ: self.clone(), span } } + + fn contains_unspecified(&self) -> bool { + match self { + UnresolvedTypeData::Array(typ, length) => { + typ.contains_unspecified() || length.contains_unspecified() + } + UnresolvedTypeData::Slice(typ) => typ.contains_unspecified(), + UnresolvedTypeData::Expression(expr) => expr.contains_unspecified(), + UnresolvedTypeData::String(length) => length.contains_unspecified(), + UnresolvedTypeData::FormatString(typ, length) => { + typ.contains_unspecified() || length.contains_unspecified() + } + UnresolvedTypeData::Parenthesized(typ) => typ.contains_unspecified(), + UnresolvedTypeData::Named(path, args, _is_synthesized) => { + // '_' is unspecified + let path_is_wildcard = path.is_wildcard(); + let an_arg_is_unresolved = args.contains_unspecified(); + path_is_wildcard || an_arg_is_unresolved + } + UnresolvedTypeData::TraitAsType(_path, args) => args.contains_unspecified(), + UnresolvedTypeData::MutableReference(typ) => typ.contains_unspecified(), + UnresolvedTypeData::Tuple(args) => args.iter().any(|arg| arg.contains_unspecified()), + UnresolvedTypeData::Function(args, ret, env, _unconstrained) => { + let args_contains_unspecified = args.iter().any(|arg| arg.contains_unspecified()); + args_contains_unspecified + || ret.contains_unspecified() + || env.contains_unspecified() + } + UnresolvedTypeData::Unspecified => true, + + UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Integer(_, _) + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Unit + | UnresolvedTypeData::Quoted(_) + | UnresolvedTypeData::AsTraitPath(_) + | UnresolvedTypeData::Resolved(_) + | UnresolvedTypeData::Interned(_) + | UnresolvedTypeData::Error => false, + } + } } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)] @@ -494,6 +547,19 @@ impl UnresolvedTypeExpression { | BinaryOpKind::Modulo ) } + + fn contains_unspecified(&self) -> bool { + match self { + // '_' is unspecified + UnresolvedTypeExpression::Variable(path) => path.is_wildcard(), + UnresolvedTypeExpression::BinaryOperation(lhs, _op, rhs, _span) => { + lhs.contains_unspecified() || rhs.contains_unspecified() + } + UnresolvedTypeExpression::Constant(_, _) | UnresolvedTypeExpression::AsTraitPath(_) => { + false + } + } + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs index 7244be371af..c77fe7513a1 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs @@ -27,6 +27,9 @@ use crate::token::{SecondaryAttribute, Token}; /// for an identifier that already failed to parse. pub const ERROR_IDENT: &str = "$error"; +/// This is used to represent an UnresolvedTypeData::Unspecified in a Path +pub const WILDCARD_TYPE: &str = "_"; + #[derive(Debug, PartialEq, Eq, Clone)] pub struct Statement { pub kind: StatementKind, @@ -483,6 +486,10 @@ impl Path { self.segments.first().cloned().map(|segment| segment.ident) } + pub(crate) fn is_wildcard(&self) -> bool { + self.to_ident().map(|ident| ident.0.contents) == Some(WILDCARD_TYPE.to_string()) + } + pub fn is_empty(&self) -> bool { self.segments.is_empty() && self.kind == PathKind::Plain } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs index ff482dca4fb..f801c1817ef 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -907,7 +907,17 @@ impl<'context> Elaborator<'context> { let location = Location::new(span, self.file); match value.into_expression(self.interner, location) { - Ok(new_expr) => self.elaborate_expression(new_expr), + Ok(new_expr) => { + // At this point the Expression was already elaborated and we got a Value. + // We'll elaborate this value turned into Expression to inline it and get + // an ExprId and Type, but we don't want any visibility errors to happen + // here (they could if we have `Foo { inner: 5 }` and `inner` is not + // accessible from where this expression is being elaborated). + self.silence_field_visibility_errors += 1; + let value = self.elaborate_expression(new_expr); + self.silence_field_visibility_errors -= 1; + value + } Err(error) => make_error(self, error), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs index 084bcbe3f8d..20d27fbc9ac 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -164,6 +164,12 @@ pub struct Elaborator<'context> { unresolved_globals: BTreeMap, pub(crate) interpreter_call_stack: im::Vector, + + /// If greater than 0, field visibility errors won't be reported. + /// This is used when elaborating a comptime expression that is a struct constructor + /// like `Foo { inner: 5 }`: in that case we already elaborated the code that led to + /// that comptime value and any visibility errors were already reported. + silence_field_visibility_errors: usize, } #[derive(Default)] @@ -213,6 +219,7 @@ impl<'context> Elaborator<'context> { current_trait: None, interpreter_call_stack, in_comptime_context: false, + silence_field_visibility_errors: 0, } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs index 757def16a93..6ed8fee753c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs @@ -76,8 +76,17 @@ impl<'context> Elaborator<'context> { ) -> (HirStatement, Type) { let expr_span = let_stmt.expression.span; let (expression, expr_type) = self.elaborate_expression(let_stmt.expression); + let type_contains_unspecified = let_stmt.r#type.contains_unspecified(); let annotated_type = self.resolve_inferred_type(let_stmt.r#type); + // Require the top-level of a global's type to be fully-specified + if type_contains_unspecified && global_id.is_some() { + let span = expr_span; + let expected_type = annotated_type.clone(); + let error = ResolverError::UnspecifiedGlobalType { span, expected_type }; + self.push_err(error); + } + let definition = match global_id { None => DefinitionKind::Local(Some(expression)), Some(id) => DefinitionKind::Global(id), @@ -509,6 +518,10 @@ impl<'context> Elaborator<'context> { visibility: ItemVisibility, span: Span, ) { + if self.silence_field_visibility_errors > 0 { + return; + } + if !struct_member_is_visible(struct_type.id, visibility, self.module_id(), self.def_maps) { self.push_err(ResolverError::PathResolutionError(PathResolutionError::Private( Ident::new(field_name.to_string(), span), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs index ae2bb942f48..7e06964b563 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs @@ -9,7 +9,7 @@ use crate::{ ast::{ AsTraitPath, BinaryOpKind, GenericTypeArgs, Ident, IntegerBitSize, Path, PathKind, Signedness, UnaryOp, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, - UnresolvedTypeData, UnresolvedTypeExpression, + UnresolvedTypeData, UnresolvedTypeExpression, WILDCARD_TYPE, }, hir::{ comptime::{Interpreter, Value}, @@ -40,7 +40,6 @@ use crate::{ use super::{lints, path_resolution::PathResolutionItem, Elaborator}; pub const SELF_TYPE_NAME: &str = "Self"; -pub const WILDCARD_TYPE: &str = "_"; pub(super) struct TraitPathResolution { pub(super) method: TraitMethod, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs index 198ba91156e..446c4dae2d3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -200,6 +200,11 @@ pub enum InterpreterError { item: String, location: Location, }, + InvalidInComptimeContext { + item: String, + location: Location, + explanation: String, + }, TypeAnnotationsNeededForMethodCall { location: Location, }, @@ -291,6 +296,7 @@ impl InterpreterError { | InterpreterError::UnsupportedTopLevelItemUnquote { location, .. } | InterpreterError::ComptimeDependencyCycle { location, .. } | InterpreterError::Unimplemented { location, .. } + | InterpreterError::InvalidInComptimeContext { location, .. } | InterpreterError::NoImpl { location, .. } | InterpreterError::ImplMethodTypeMismatch { location, .. } | InterpreterError::DebugEvaluateComptime { location, .. } @@ -540,6 +546,10 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let msg = format!("{item} is currently unimplemented"); CustomDiagnostic::simple_error(msg, String::new(), location.span) } + InterpreterError::InvalidInComptimeContext { item, location, explanation } => { + let msg = format!("{item} is invalid in comptime context"); + CustomDiagnostic::simple_error(msg, explanation.clone(), location.span) + } InterpreterError::BreakNotInLoop { location } => { let msg = "There is no loop to break out of!".into(); CustomDiagnostic::simple_error(msg, String::new(), location.span) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 994318a371a..49fd86b73bb 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -1,6 +1,7 @@ use std::collections::VecDeque; use std::{collections::hash_map::Entry, rc::Rc}; +use acvm::blackbox_solver::BigIntSolverWithId; use acvm::{acir::AcirField, FieldElement}; use fm::FileId; use im::Vector; @@ -62,6 +63,9 @@ pub struct Interpreter<'local, 'interner> { /// multiple times. Without this map, when one of these inner functions exits we would /// unbind the generic completely instead of resetting it to its previous binding. bound_generics: Vec>, + + /// Stateful bigint calculator. + bigint_solver: BigIntSolverWithId, } #[allow(unused)] @@ -71,9 +75,14 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { crate_id: CrateId, current_function: Option, ) -> Self { - let bound_generics = Vec::new(); - let in_loop = false; - Self { elaborator, crate_id, current_function, bound_generics, in_loop } + Self { + elaborator, + crate_id, + current_function, + bound_generics: Vec::new(), + in_loop: false, + bigint_solver: BigIntSolverWithId::default(), + } } pub(crate) fn call_function( @@ -227,11 +236,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { .expect("all builtin functions must contain a function attribute which contains the opcode which it links to"); if let Some(builtin) = func_attrs.builtin() { - let builtin = builtin.clone(); - self.call_builtin(&builtin, arguments, return_type, location) + self.call_builtin(builtin.clone().as_str(), arguments, return_type, location) } else if let Some(foreign) = func_attrs.foreign() { - let foreign = foreign.clone(); - foreign::call_foreign(self.elaborator.interner, &foreign, arguments, location) + self.call_foreign(foreign.clone().as_str(), arguments, return_type, location) } else if let Some(oracle) = func_attrs.oracle() { if oracle == "print" { self.print_oracle(arguments) @@ -906,6 +913,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } } + #[allow(clippy::bool_comparison)] fn evaluate_infix(&mut self, infix: HirInfixExpression, id: ExprId) -> IResult { let lhs_value = self.evaluate(infix.lhs)?; let rhs_value = self.evaluate(infix.rhs)?; @@ -924,310 +932,183 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { InterpreterError::InvalidValuesForBinary { lhs, rhs, location, operator } }; - use InterpreterError::InvalidValuesForBinary; - match infix.operator.kind { - BinaryOpKind::Add => match (lhs_value.clone(), rhs_value.clone()) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs + rhs)), - (Value::I8(lhs), Value::I8(rhs)) => { - Ok(Value::I8(lhs.checked_add(rhs).ok_or(error("+"))?)) - } - (Value::I16(lhs), Value::I16(rhs)) => { - Ok(Value::I16(lhs.checked_add(rhs).ok_or(error("+"))?)) - } - (Value::I32(lhs), Value::I32(rhs)) => { - Ok(Value::I32(lhs.checked_add(rhs).ok_or(error("+"))?)) + /// Generate matches that can promote the type of one side to the other if they are compatible. + macro_rules! match_values { + (($lhs_value:ident as $lhs:ident $op:literal $rhs_value:ident as $rhs:ident) { + $( + ($lhs_var:ident, $rhs_var:ident) to $res_var:ident => $expr:expr + ),* + $(,)? + } + ) => { + match ($lhs_value, $rhs_value) { + $( + (Value::$lhs_var($lhs), Value::$rhs_var($rhs)) => { + Ok(Value::$res_var(($expr).ok_or(error($op))?)) + }, + )* + (lhs, rhs) => { + Err(error($op)) + }, + } + }; + } + + /// Generate matches for arithmetic operations on `Field` and integers. + macro_rules! match_arithmetic { + (($lhs_value:ident as $lhs:ident $op:literal $rhs_value:ident as $rhs:ident) { field: $field_expr:expr, int: $int_expr:expr, }) => { + match_values! { + ($lhs_value as $lhs $op $rhs_value as $rhs) { + (Field, Field) to Field => Some($field_expr), + (I8, I8) to I8 => $int_expr, + (I16, I16) to I16 => $int_expr, + (I32, I32) to I32 => $int_expr, + (I64, I64) to I64 => $int_expr, + (U8, U8) to U8 => $int_expr, + (U16, U16) to U16 => $int_expr, + (U32, U32) to U32 => $int_expr, + (U64, U64) to U64 => $int_expr, + } } - (Value::I64(lhs), Value::I64(rhs)) => { - Ok(Value::I64(lhs.checked_add(rhs).ok_or(error("+"))?)) + }; + } + + /// Generate matches for comparison operations on all types, returning `Bool`. + macro_rules! match_cmp { + (($lhs_value:ident as $lhs:ident $op:literal $rhs_value:ident as $rhs:ident) => $expr:expr) => { + match_values! { + ($lhs_value as $lhs $op $rhs_value as $rhs) { + (Field, Field) to Bool => Some($expr), + (Bool, Bool) to Bool => Some($expr), + (I8, I8) to Bool => Some($expr), + (I16, I16) to Bool => Some($expr), + (I32, I32) to Bool => Some($expr), + (I64, I64) to Bool => Some($expr), + (U8, U8) to Bool => Some($expr), + (U16, U16) to Bool => Some($expr), + (U32, U32) to Bool => Some($expr), + (U64, U64) to Bool => Some($expr), + } } - (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(lhs.checked_add(rhs).ok_or(error("+"))?)) + }; + } + + /// Generate matches for bitwise operations on `Bool` and integers. + macro_rules! match_bitwise { + (($lhs_value:ident as $lhs:ident $op:literal $rhs_value:ident as $rhs:ident) => $expr:expr) => { + match_values! { + ($lhs_value as $lhs $op $rhs_value as $rhs) { + (Bool, Bool) to Bool => Some($expr), + (I8, I8) to I8 => Some($expr), + (I16, I16) to I16 => Some($expr), + (I32, I32) to I32 => Some($expr), + (I64, I64) to I64 => Some($expr), + (U8, U8) to U8 => Some($expr), + (U16, U16) to U16 => Some($expr), + (U32, U32) to U32 => Some($expr), + (U64, U64) to U64 => Some($expr), + } } - (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(lhs.checked_add(rhs).ok_or(error("+"))?)) + }; + } + + /// Generate matches for operations on just integer values. + macro_rules! match_integer { + (($lhs_value:ident as $lhs:ident $op:literal $rhs_value:ident as $rhs:ident) => $expr:expr) => { + match_values! { + ($lhs_value as $lhs $op $rhs_value as $rhs) { + (I8, I8) to I8 => $expr, + (I16, I16) to I16 => $expr, + (I32, I32) to I32 => $expr, + (I64, I64) to I64 => $expr, + (U8, U8) to U8 => $expr, + (U16, U16) to U16 => $expr, + (U32, U32) to U32 => $expr, + (U64, U64) to U64 => $expr, + } } - (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(lhs.checked_add(rhs).ok_or(error("+"))?)) + }; + } + + /// Generate matches for bit shifting, which in Noir only accepts `u8` for RHS. + macro_rules! match_bitshift { + (($lhs_value:ident as $lhs:ident $op:literal $rhs_value:ident as $rhs:ident) => $expr:expr) => { + match_values! { + ($lhs_value as $lhs $op $rhs_value as $rhs) { + (I8, U8) to I8 => $expr, + (I16, U8) to I16 => $expr, + (I32, U8) to I32 => $expr, + (I64, U8) to I64 => $expr, + (U8, U8) to U8 => $expr, + (U16, U8) to U16 => $expr, + (U32, U8) to U32 => $expr, + (U64, U8) to U64 => $expr, + } } - (Value::U64(lhs), Value::U64(rhs)) => { - Ok(Value::U64(lhs.checked_add(rhs).ok_or(error("+"))?)) + }; + } + + use InterpreterError::InvalidValuesForBinary; + match infix.operator.kind { + BinaryOpKind::Add => match_arithmetic! { + (lhs_value as lhs "+" rhs_value as rhs) { + field: lhs + rhs, + int: lhs.checked_add(rhs), } - (lhs, rhs) => Err(error("+")), }, - BinaryOpKind::Subtract => match (lhs_value.clone(), rhs_value.clone()) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs - rhs)), - (Value::I8(lhs), Value::I8(rhs)) => { - Ok(Value::I8(lhs.checked_sub(rhs).ok_or(error("-"))?)) - } - (Value::I16(lhs), Value::I16(rhs)) => { - Ok(Value::I16(lhs.checked_sub(rhs).ok_or(error("-"))?)) - } - (Value::I32(lhs), Value::I32(rhs)) => { - Ok(Value::I32(lhs.checked_sub(rhs).ok_or(error("-"))?)) - } - (Value::I64(lhs), Value::I64(rhs)) => { - Ok(Value::I64(lhs.checked_sub(rhs).ok_or(error("-"))?)) - } - (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(lhs.checked_sub(rhs).ok_or(error("-"))?)) - } - (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(lhs.checked_sub(rhs).ok_or(error("-"))?)) + BinaryOpKind::Subtract => match_arithmetic! { + (lhs_value as lhs "-" rhs_value as rhs) { + field: lhs - rhs, + int: lhs.checked_sub(rhs), } - (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(lhs.checked_sub(rhs).ok_or(error("-"))?)) - } - (Value::U64(lhs), Value::U64(rhs)) => { - Ok(Value::U64(lhs.checked_sub(rhs).ok_or(error("-"))?)) - } - (lhs, rhs) => Err(error("-")), }, - BinaryOpKind::Multiply => match (lhs_value.clone(), rhs_value.clone()) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs * rhs)), - (Value::I8(lhs), Value::I8(rhs)) => { - Ok(Value::I8(lhs.checked_mul(rhs).ok_or(error("*"))?)) - } - (Value::I16(lhs), Value::I16(rhs)) => { - Ok(Value::I16(lhs.checked_mul(rhs).ok_or(error("*"))?)) - } - (Value::I32(lhs), Value::I32(rhs)) => { - Ok(Value::I32(lhs.checked_mul(rhs).ok_or(error("*"))?)) - } - (Value::I64(lhs), Value::I64(rhs)) => { - Ok(Value::I64(lhs.checked_mul(rhs).ok_or(error("*"))?)) - } - (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(lhs.checked_mul(rhs).ok_or(error("*"))?)) - } - (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(lhs.checked_mul(rhs).ok_or(error("*"))?)) - } - (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(lhs.checked_mul(rhs).ok_or(error("*"))?)) + BinaryOpKind::Multiply => match_arithmetic! { + (lhs_value as lhs "*" rhs_value as rhs) { + field: lhs * rhs, + int: lhs.checked_mul(rhs), } - (Value::U64(lhs), Value::U64(rhs)) => { - Ok(Value::U64(lhs.checked_mul(rhs).ok_or(error("*"))?)) - } - (lhs, rhs) => Err(error("*")), }, - BinaryOpKind::Divide => match (lhs_value.clone(), rhs_value.clone()) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs / rhs)), - (Value::I8(lhs), Value::I8(rhs)) => { - Ok(Value::I8(lhs.checked_div(rhs).ok_or(error("/"))?)) - } - (Value::I16(lhs), Value::I16(rhs)) => { - Ok(Value::I16(lhs.checked_div(rhs).ok_or(error("/"))?)) - } - (Value::I32(lhs), Value::I32(rhs)) => { - Ok(Value::I32(lhs.checked_div(rhs).ok_or(error("/"))?)) - } - (Value::I64(lhs), Value::I64(rhs)) => { - Ok(Value::I64(lhs.checked_div(rhs).ok_or(error("/"))?)) - } - (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(lhs.checked_div(rhs).ok_or(error("/"))?)) - } - (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(lhs.checked_div(rhs).ok_or(error("/"))?)) - } - (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(lhs.checked_div(rhs).ok_or(error("/"))?)) + BinaryOpKind::Divide => match_arithmetic! { + (lhs_value as lhs "/" rhs_value as rhs) { + field: lhs / rhs, + int: lhs.checked_div(rhs), } - (Value::U64(lhs), Value::U64(rhs)) => { - Ok(Value::U64(lhs.checked_div(rhs).ok_or(error("/"))?)) - } - (lhs, rhs) => Err(error("/")), }, - BinaryOpKind::Equal => match (lhs_value.clone(), rhs_value.clone()) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs == rhs)), - (lhs, rhs) => Err(error("==")), + BinaryOpKind::Equal => match_cmp! { + (lhs_value as lhs "==" rhs_value as rhs) => lhs == rhs }, - BinaryOpKind::NotEqual => match (lhs_value.clone(), rhs_value.clone()) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs != rhs)), - (lhs, rhs) => Err(error("!=")), + BinaryOpKind::NotEqual => match_cmp! { + (lhs_value as lhs "!=" rhs_value as rhs) => lhs != rhs }, - BinaryOpKind::Less => match (lhs_value.clone(), rhs_value.clone()) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs < rhs)), - (lhs, rhs) => Err(error("<")), + BinaryOpKind::Less => match_cmp! { + (lhs_value as lhs "<" rhs_value as rhs) => lhs < rhs }, - BinaryOpKind::LessEqual => match (lhs_value.clone(), rhs_value.clone()) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (lhs, rhs) => Err(error("<=")), + BinaryOpKind::LessEqual => match_cmp! { + (lhs_value as lhs "<=" rhs_value as rhs) => lhs <= rhs }, - BinaryOpKind::Greater => match (lhs_value.clone(), rhs_value.clone()) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs > rhs)), - (lhs, rhs) => Err(error(">")), + BinaryOpKind::Greater => match_cmp! { + (lhs_value as lhs ">" rhs_value as rhs) => lhs > rhs }, - BinaryOpKind::GreaterEqual => match (lhs_value.clone(), rhs_value.clone()) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (lhs, rhs) => Err(error(">=")), + BinaryOpKind::GreaterEqual => match_cmp! { + (lhs_value as lhs ">=" rhs_value as rhs) => lhs >= rhs }, - BinaryOpKind::And => match (lhs_value.clone(), rhs_value.clone()) { - (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs & rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs & rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs & rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs & rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs & rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs & rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs & rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs & rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs & rhs)), - (lhs, rhs) => Err(error("&")), + BinaryOpKind::And => match_bitwise! { + (lhs_value as lhs "&" rhs_value as rhs) => lhs & rhs }, - BinaryOpKind::Or => match (lhs_value.clone(), rhs_value.clone()) { - (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs | rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs | rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs | rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs | rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs | rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs | rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs | rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs | rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs | rhs)), - (lhs, rhs) => Err(error("|")), + BinaryOpKind::Or => match_bitwise! { + (lhs_value as lhs "|" rhs_value as rhs) => lhs | rhs }, - BinaryOpKind::Xor => match (lhs_value.clone(), rhs_value.clone()) { - (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs ^ rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs ^ rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs ^ rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs ^ rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs ^ rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs ^ rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs ^ rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs ^ rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs ^ rhs)), - (lhs, rhs) => Err(error("^")), + BinaryOpKind::Xor => match_bitwise! { + (lhs_value as lhs "^" rhs_value as rhs) => lhs ^ rhs }, - BinaryOpKind::ShiftRight => match (lhs_value.clone(), rhs_value.clone()) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8( - lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, - )), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16( - lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, - )), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32( - lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, - )), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64( - lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, - )), - (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(lhs.checked_shr(rhs.into()).ok_or(error(">>"))?)) - } - (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(lhs.checked_shr(rhs.into()).ok_or(error(">>"))?)) - } - (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(lhs.checked_shr(rhs).ok_or(error(">>"))?)) - } - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64( - lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, - )), - (lhs, rhs) => Err(error(">>")), + BinaryOpKind::ShiftRight => match_bitshift! { + (lhs_value as lhs ">>" rhs_value as rhs) => lhs.checked_shr(rhs.into()) }, - BinaryOpKind::ShiftLeft => match (lhs_value.clone(), rhs_value.clone()) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8( - lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, - )), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16( - lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, - )), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32( - lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, - )), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64( - lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, - )), - (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(lhs.checked_shl(rhs.into()).ok_or(error("<<"))?)) - } - (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(lhs.checked_shl(rhs.into()).ok_or(error("<<"))?)) - } - (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(lhs.checked_shl(rhs).ok_or(error("<<"))?)) - } - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64( - lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, - )), - (lhs, rhs) => Err(error("<<")), + BinaryOpKind::ShiftLeft => match_bitshift! { + (lhs_value as lhs "<<" rhs_value as rhs) => lhs.checked_shl(rhs.into()) }, - BinaryOpKind::Modulo => match (lhs_value.clone(), rhs_value.clone()) { - (Value::I8(lhs), Value::I8(rhs)) => { - Ok(Value::I8(lhs.checked_rem(rhs).ok_or(error("%"))?)) - } - (Value::I16(lhs), Value::I16(rhs)) => { - Ok(Value::I16(lhs.checked_rem(rhs).ok_or(error("%"))?)) - } - (Value::I32(lhs), Value::I32(rhs)) => { - Ok(Value::I32(lhs.checked_rem(rhs).ok_or(error("%"))?)) - } - (Value::I64(lhs), Value::I64(rhs)) => { - Ok(Value::I64(lhs.checked_rem(rhs).ok_or(error("%"))?)) - } - (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(lhs.checked_rem(rhs).ok_or(error("%"))?)) - } - (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(lhs.checked_rem(rhs).ok_or(error("%"))?)) - } - (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(lhs.checked_rem(rhs).ok_or(error("%"))?)) - } - (Value::U64(lhs), Value::U64(rhs)) => { - Ok(Value::U64(lhs.checked_rem(rhs).ok_or(error("%"))?)) - } - (lhs, rhs) => Err(error("%")), + BinaryOpKind::Modulo => match_integer! { + (lhs_value as lhs "%" rhs_value as rhs) => lhs.checked_rem(rhs) }, } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 80c1ee217c2..3d8ccf78926 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -1,14 +1,14 @@ use std::rc::Rc; -use acvm::{AcirField, FieldElement}; +use acvm::{acir::BlackBoxFunc, AcirField, FieldElement}; use builtin_helpers::{ - block_expression_to_value, check_argument_count, check_function_not_yet_resolved, - check_one_argument, check_three_arguments, check_two_arguments, get_bool, get_expr, get_field, - get_format_string, get_function_def, get_module, get_quoted, get_slice, get_struct, - get_trait_constraint, get_trait_def, get_trait_impl, get_tuple, get_type, get_typed_expr, - get_u32, get_unresolved_type, has_named_attribute, hir_pattern_to_tokens, - mutate_func_meta_type, parse, quote_ident, replace_func_meta_parameters, - replace_func_meta_return_type, + block_expression_to_value, byte_array_type, check_argument_count, + check_function_not_yet_resolved, check_one_argument, check_three_arguments, + check_two_arguments, get_bool, get_expr, get_field, get_format_string, get_function_def, + get_module, get_quoted, get_slice, get_struct, get_trait_constraint, get_trait_def, + get_trait_impl, get_tuple, get_type, get_typed_expr, get_u32, get_unresolved_type, + has_named_attribute, hir_pattern_to_tokens, mutate_func_meta_type, parse, quote_ident, + replace_func_meta_parameters, replace_func_meta_return_type, }; use im::Vector; use iter_extended::{try_vecmap, vecmap}; @@ -42,7 +42,7 @@ use crate::{ }; use self::builtin_helpers::{eq_item, get_array, get_ctstring, get_str, get_u8, hash_item, lex}; -use super::{foreign, Interpreter}; +use super::Interpreter; pub(crate) mod builtin_helpers; @@ -57,9 +57,12 @@ impl<'local, 'context> Interpreter<'local, 'context> { let interner = &mut self.elaborator.interner; let call_stack = &self.elaborator.interpreter_call_stack; match name { - "apply_range_constraint" => foreign::apply_range_constraint(arguments, location), + "apply_range_constraint" => { + self.call_foreign("range", arguments, return_type, location) + } "array_as_str_unchecked" => array_as_str_unchecked(interner, arguments, location), "array_len" => array_len(interner, arguments, location), + "array_refcount" => Ok(Value::U32(0)), "assert_constant" => Ok(Value::Bool(true)), "as_slice" => as_slice(interner, arguments, location), "ctstring_eq" => ctstring_eq(arguments, location), @@ -167,6 +170,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "slice_pop_front" => slice_pop_front(interner, arguments, location, call_stack), "slice_push_back" => slice_push_back(interner, arguments, location), "slice_push_front" => slice_push_front(interner, arguments, location), + "slice_refcount" => Ok(Value::U32(0)), "slice_remove" => slice_remove(interner, arguments, location, call_stack), "str_as_bytes" => str_as_bytes(interner, arguments, location), "str_as_ctstring" => str_as_ctstring(interner, arguments, location), @@ -232,8 +236,11 @@ impl<'local, 'context> Interpreter<'local, 'context> { "unresolved_type_is_field" => unresolved_type_is_field(interner, arguments, location), "unresolved_type_is_unit" => unresolved_type_is_unit(interner, arguments, location), "zeroed" => zeroed(return_type, location.span), + blackbox if BlackBoxFunc::is_valid_black_box_func_name(blackbox) => { + self.call_foreign(blackbox, arguments, return_type, location) + } _ => { - let item = format!("Comptime evaluation for builtin function {name}"); + let item = format!("Comptime evaluation for builtin function '{name}'"); Err(InterpreterError::Unimplemented { item, location }) } } @@ -322,10 +329,7 @@ fn str_as_bytes( let string = get_str(interner, string)?; let bytes: im::Vector = string.bytes().map(Value::U8).collect(); - let byte_array_type = Type::Array( - Box::new(Type::Constant(bytes.len().into(), Kind::u32())), - Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight)), - ); + let byte_array_type = byte_array_type(bytes.len()); Ok(Value::Array(bytes, byte_array_type)) } @@ -818,10 +822,8 @@ fn to_le_radix( Some(digit) => Value::U8(*digit), None => Value::U8(0), }); - Ok(Value::Array( - decomposed_integer.into(), - Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), - )) + let result_type = byte_array_type(decomposed_integer.len()); + Ok(Value::Array(decomposed_integer.into(), result_type)) } fn compute_to_radix_le(field: FieldElement, radix: u32) -> Vec { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index 3f9d92cfe88..cf90aab32e0 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -2,6 +2,7 @@ use std::hash::Hash; use std::{hash::Hasher, rc::Rc}; use acvm::FieldElement; +use iter_extended::try_vecmap; use noirc_errors::Location; use crate::hir::comptime::display::tokens_to_string; @@ -30,6 +31,8 @@ use crate::{ token::{SecondaryAttribute, Token, Tokens}, QuotedType, Type, }; +use crate::{Kind, Shared, StructType}; +use rustc_hash::FxHashMap as HashMap; pub(crate) fn check_argument_count( expected: usize, @@ -45,38 +48,40 @@ pub(crate) fn check_argument_count( } pub(crate) fn check_one_argument( - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult<(Value, Location)> { - check_argument_count(1, &arguments, location)?; + let [arg1] = check_arguments(arguments, location)?; - Ok(arguments.pop().unwrap()) + Ok(arg1) } pub(crate) fn check_two_arguments( - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult<((Value, Location), (Value, Location))> { - check_argument_count(2, &arguments, location)?; - - let argument2 = arguments.pop().unwrap(); - let argument1 = arguments.pop().unwrap(); + let [arg1, arg2] = check_arguments(arguments, location)?; - Ok((argument1, argument2)) + Ok((arg1, arg2)) } #[allow(clippy::type_complexity)] pub(crate) fn check_three_arguments( - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult<((Value, Location), (Value, Location), (Value, Location))> { - check_argument_count(3, &arguments, location)?; + let [arg1, arg2, arg3] = check_arguments(arguments, location)?; - let argument3 = arguments.pop().unwrap(); - let argument2 = arguments.pop().unwrap(); - let argument1 = arguments.pop().unwrap(); + Ok((arg1, arg2, arg3)) +} - Ok((argument1, argument2, argument3)) +#[allow(clippy::type_complexity)] +pub(crate) fn check_arguments( + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult<[(Value, Location); N]> { + check_argument_count(N, &arguments, location)?; + Ok(arguments.try_into().expect("checked arg count")) } pub(crate) fn get_array( @@ -93,6 +98,47 @@ pub(crate) fn get_array( } } +/// Get the fields if the value is a `Value::Struct`, otherwise report that a struct type +/// with `name` is expected. Returns the `Type` but doesn't verify that it's called `name`. +pub(crate) fn get_struct_fields( + name: &str, + (value, location): (Value, Location), +) -> IResult<(HashMap, Value>, Type)> { + match value { + Value::Struct(fields, typ) => Ok((fields, typ)), + _ => { + let expected = StructType::new( + StructId::dummy_id(), + Ident::new(name.to_string(), location.span), + location, + Vec::new(), + Vec::new(), + ); + let expected = Type::Struct(Shared::new(expected), Vec::new()); + type_mismatch(value, expected, location) + } + } +} + +/// Get a specific field of a struct and apply a decoder function on it. +pub(crate) fn get_struct_field( + field_name: &str, + struct_fields: &HashMap, Value>, + struct_type: &Type, + location: Location, + f: impl Fn((Value, Location)) -> IResult, +) -> IResult { + let key = Rc::new(field_name.to_string()); + let Some(value) = struct_fields.get(&key) else { + return Err(InterpreterError::ExpectedStructToHaveField { + typ: struct_type.clone(), + field_name: Rc::into_inner(key).unwrap(), + location, + }); + }; + f((value.clone(), location)) +} + pub(crate) fn get_bool((value, location): (Value, Location)) -> IResult { match value { Value::Bool(value) => Ok(value), @@ -114,6 +160,49 @@ pub(crate) fn get_slice( } } +/// Interpret the input as a slice, then map each element. +/// Returns the values in the slice and the original type. +pub(crate) fn get_slice_map( + interner: &NodeInterner, + (value, location): (Value, Location), + f: impl Fn((Value, Location)) -> IResult, +) -> IResult<(Vec, Type)> { + let (values, typ) = get_slice(interner, (value, location))?; + let values = try_vecmap(values, |value| f((value, location)))?; + Ok((values, typ)) +} + +/// Interpret the input as an array, then map each element. +/// Returns the values in the array and the original array type. +pub(crate) fn get_array_map( + interner: &NodeInterner, + (value, location): (Value, Location), + f: impl Fn((Value, Location)) -> IResult, +) -> IResult<(Vec, Type)> { + let (values, typ) = get_array(interner, (value, location))?; + let values = try_vecmap(values, |value| f((value, location)))?; + Ok((values, typ)) +} + +/// Get an array and convert it to a fixed size. +/// Returns the values in the array and the original array type. +pub(crate) fn get_fixed_array_map( + interner: &NodeInterner, + (value, location): (Value, Location), + f: impl Fn((Value, Location)) -> IResult, +) -> IResult<([T; N], Type)> { + let (values, typ) = get_array_map(interner, (value, location), f)?; + + values.try_into().map(|v| (v, typ.clone())).map_err(|_| { + // Assuming that `values.len()` corresponds to `typ`. + let Type::Array(_, ref elem) = typ else { + unreachable!("get_array_map checked it was an array") + }; + let expected = Type::Array(Box::new(Type::Constant(N.into(), Kind::u32())), elem.clone()); + InterpreterError::TypeMismatch { expected, actual: typ, location } + }) +} + pub(crate) fn get_str( interner: &NodeInterner, (value, location): (Value, Location), @@ -520,3 +609,44 @@ pub(super) fn eq_item( let other_arg = get_item(other_arg)?; Ok(Value::Bool(self_arg == other_arg)) } + +/// Type to be used in `Value::Array(, )`. +pub(crate) fn byte_array_type(len: usize) -> Type { + Type::Array( + Box::new(Type::Constant(len.into(), Kind::u32())), + Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight)), + ) +} + +/// Type to be used in `Value::Slice(, )`. +pub(crate) fn byte_slice_type() -> Type { + Type::Slice(Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight))) +} + +/// Create a `Value::Array` from bytes. +pub(crate) fn to_byte_array(values: &[u8]) -> Value { + Value::Array(values.iter().copied().map(Value::U8).collect(), byte_array_type(values.len())) +} + +/// Create a `Value::Slice` from bytes. +pub(crate) fn to_byte_slice(values: &[u8]) -> Value { + Value::Slice(values.iter().copied().map(Value::U8).collect(), byte_slice_type()) +} + +/// Create a `Value::Array` from fields. +pub(crate) fn to_field_array(values: &[FieldElement]) -> Value { + let typ = Type::Array( + Box::new(Type::Constant(values.len().into(), Kind::u32())), + Box::new(Type::FieldElement), + ); + Value::Array(values.iter().copied().map(Value::Field).collect(), typ) +} + +/// Create a `Value::Struct` from fields and the expected return type. +pub(crate) fn to_struct( + fields: impl IntoIterator, + typ: Type, +) -> Value { + let fields = fields.into_iter().map(|(k, v)| (Rc::new(k.to_string()), v)).collect(); + Value::Struct(fields, typ) +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs index 3de72969cab..d2611f72535 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs @@ -1,40 +1,126 @@ use acvm::{ - acir::BlackBoxFunc, blackbox_solver::BlackBoxFunctionSolver, AcirField, BlackBoxResolutionError, + acir::BlackBoxFunc, + blackbox_solver::{BigIntSolverWithId, BlackBoxFunctionSolver}, + AcirField, BlackBoxResolutionError, FieldElement, }; -use bn254_blackbox_solver::Bn254BlackBoxSolver; +use bn254_blackbox_solver::Bn254BlackBoxSolver; // Currently locked to only bn254! use im::Vector; -use iter_extended::try_vecmap; use noirc_errors::Location; use crate::{ - hir::comptime::{errors::IResult, InterpreterError, Value}, + hir::comptime::{ + errors::IResult, interpreter::builtin::builtin_helpers::to_byte_array, InterpreterError, + Value, + }, node_interner::NodeInterner, + Type, }; -use super::builtin::builtin_helpers::{ - check_one_argument, check_two_arguments, get_array, get_field, get_u32, get_u64, +use super::{ + builtin::builtin_helpers::{ + check_arguments, check_one_argument, check_three_arguments, check_two_arguments, + get_array_map, get_bool, get_field, get_fixed_array_map, get_slice_map, get_struct_field, + get_struct_fields, get_u32, get_u64, get_u8, to_byte_slice, to_field_array, to_struct, + }, + Interpreter, }; -pub(super) fn call_foreign( +impl<'local, 'context> Interpreter<'local, 'context> { + pub(super) fn call_foreign( + &mut self, + name: &str, + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, + ) -> IResult { + call_foreign( + self.elaborator.interner, + &mut self.bigint_solver, + name, + arguments, + return_type, + location, + ) + } +} + +// Similar to `evaluate_black_box` in `brillig_vm`. +fn call_foreign( interner: &mut NodeInterner, + bigint_solver: &mut BigIntSolverWithId, name: &str, - arguments: Vec<(Value, Location)>, + args: Vec<(Value, Location)>, + return_type: Type, location: Location, ) -> IResult { + use BlackBoxFunc::*; + match name { - "poseidon2_permutation" => poseidon2_permutation(interner, arguments, location), - "keccakf1600" => keccakf1600(interner, arguments, location), + "aes128_encrypt" => aes128_encrypt(interner, args, location), + "bigint_from_le_bytes" => { + bigint_from_le_bytes(interner, bigint_solver, args, return_type, location) + } + "bigint_to_le_bytes" => bigint_to_le_bytes(bigint_solver, args, location), + "bigint_add" => bigint_op(bigint_solver, BigIntAdd, args, return_type, location), + "bigint_sub" => bigint_op(bigint_solver, BigIntSub, args, return_type, location), + "bigint_mul" => bigint_op(bigint_solver, BigIntMul, args, return_type, location), + "bigint_div" => bigint_op(bigint_solver, BigIntDiv, args, return_type, location), + "blake2s" => blake_hash(interner, args, location, acvm::blackbox_solver::blake2s), + "blake3" => blake_hash(interner, args, location, acvm::blackbox_solver::blake3), + "ecdsa_secp256k1" => ecdsa_secp256_verify( + interner, + args, + location, + acvm::blackbox_solver::ecdsa_secp256k1_verify, + ), + "ecdsa_secp256r1" => ecdsa_secp256_verify( + interner, + args, + location, + acvm::blackbox_solver::ecdsa_secp256r1_verify, + ), + "embedded_curve_add" => embedded_curve_add(args, location), + "multi_scalar_mul" => multi_scalar_mul(interner, args, location), + "poseidon2_permutation" => poseidon2_permutation(interner, args, location), + "keccakf1600" => keccakf1600(interner, args, location), + "range" => apply_range_constraint(args, location), + "sha256_compression" => sha256_compression(interner, args, location), _ => { - let item = format!("Comptime evaluation for builtin function {name}"); - Err(InterpreterError::Unimplemented { item, location }) + let explanation = match name { + "schnorr_verify" => "Schnorr verification will be removed.".into(), + "and" | "xor" => "It should be turned into a binary operation.".into(), + "recursive_aggregation" => "A proof cannot be verified at comptime.".into(), + _ => { + let item = format!("Comptime evaluation for foreign function '{name}'"); + return Err(InterpreterError::Unimplemented { item, location }); + } + }; + + let item = format!("Attempting to evaluate foreign function '{name}'"); + Err(InterpreterError::InvalidInComptimeContext { item, location, explanation }) } } } -pub(super) fn apply_range_constraint( +/// `pub fn aes128_encrypt(input: [u8; N], iv: [u8; 16], key: [u8; 16]) -> [u8]` +fn aes128_encrypt( + interner: &mut NodeInterner, arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { + let (inputs, iv, key) = check_three_arguments(arguments, location)?; + + let (inputs, _) = get_array_map(interner, inputs, get_u8)?; + let (iv, _) = get_fixed_array_map(interner, iv, get_u8)?; + let (key, _) = get_fixed_array_map(interner, key, get_u8)?; + + let output = acvm::blackbox_solver::aes128_encrypt(&inputs, iv, key) + .map_err(|e| InterpreterError::BlackBoxError(e, location))?; + + Ok(to_byte_slice(&output)) +} + +fn apply_range_constraint(arguments: Vec<(Value, Location)>, location: Location) -> IResult { let (value, num_bits) = check_two_arguments(arguments, location)?; let input = get_field(value)?; @@ -53,21 +139,192 @@ pub(super) fn apply_range_constraint( } } -// poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] +/// `fn from_le_bytes(bytes: [u8], modulus: [u8]) -> BigInt` +/// +/// Returns the ID of the new bigint allocated by the solver. +fn bigint_from_le_bytes( + interner: &mut NodeInterner, + solver: &mut BigIntSolverWithId, + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + let (bytes, modulus) = check_two_arguments(arguments, location)?; + + let (bytes, _) = get_slice_map(interner, bytes, get_u8)?; + let (modulus, _) = get_slice_map(interner, modulus, get_u8)?; + + let id = solver + .bigint_from_bytes(&bytes, &modulus) + .map_err(|e| InterpreterError::BlackBoxError(e, location))?; + + Ok(to_bigint(id, return_type)) +} + +/// `fn to_le_bytes(self) -> [u8; 32]` +/// +/// Take the ID of a bigint and returned its content. +fn bigint_to_le_bytes( + solver: &mut BigIntSolverWithId, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let int = check_one_argument(arguments, location)?; + let id = get_bigint_id(int)?; + + let mut bytes = + solver.bigint_to_bytes(id).map_err(|e| InterpreterError::BlackBoxError(e, location))?; + + assert!(bytes.len() <= 32); + bytes.resize(32, 0); + + Ok(to_byte_array(&bytes)) +} + +/// `fn bigint_add(self, other: BigInt) -> BigInt` +/// +/// Takes two previous allocated IDs, gets the values from the solver, +/// stores the result of the operation, returns the new ID. +fn bigint_op( + solver: &mut BigIntSolverWithId, + func: BlackBoxFunc, + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + let (lhs, rhs) = check_two_arguments(arguments, location)?; + + let lhs = get_bigint_id(lhs)?; + let rhs = get_bigint_id(rhs)?; + + let id = solver + .bigint_op(lhs, rhs, func) + .map_err(|e| InterpreterError::BlackBoxError(e, location))?; + + Ok(to_bigint(id, return_type)) +} + +/// Run one of the Blake hash functions. +/// ```text +/// pub fn blake2s(input: [u8; N]) -> [u8; 32] +/// pub fn blake3(input: [u8; N]) -> [u8; 32] +/// ``` +fn blake_hash( + interner: &mut NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, + f: impl Fn(&[u8]) -> Result<[u8; 32], BlackBoxResolutionError>, +) -> IResult { + let inputs = check_one_argument(arguments, location)?; + + let (inputs, _) = get_array_map(interner, inputs, get_u8)?; + let output = f(&inputs).map_err(|e| InterpreterError::BlackBoxError(e, location))?; + + Ok(to_byte_array(&output)) +} + +/// Run one of the Secp256 signature verifications. +/// ```text +/// pub fn verify_signature( +/// public_key_x: [u8; 32], +/// public_key_y: [u8; 32], +/// signature: [u8; 64], +/// message_hash: [u8; N], +/// ) -> bool + +/// pub fn verify_signature_slice( +/// public_key_x: [u8; 32], +/// public_key_y: [u8; 32], +/// signature: [u8; 64], +/// message_hash: [u8], +/// ) -> bool +/// ``` +fn ecdsa_secp256_verify( + interner: &mut NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, + f: impl Fn(&[u8], &[u8; 32], &[u8; 32], &[u8; 64]) -> Result, +) -> IResult { + let [pub_key_x, pub_key_y, sig, msg_hash] = check_arguments(arguments, location)?; + + let (pub_key_x, _) = get_fixed_array_map(interner, pub_key_x, get_u8)?; + let (pub_key_y, _) = get_fixed_array_map(interner, pub_key_y, get_u8)?; + let (sig, _) = get_fixed_array_map(interner, sig, get_u8)?; + + // Hash can be an array or slice. + let (msg_hash, _) = if matches!(msg_hash.0.get_type().as_ref(), Type::Array(_, _)) { + get_array_map(interner, msg_hash.clone(), get_u8)? + } else { + get_slice_map(interner, msg_hash, get_u8)? + }; + + let is_valid = f(&msg_hash, &pub_key_x, &pub_key_y, &sig) + .map_err(|e| InterpreterError::BlackBoxError(e, location))?; + + Ok(Value::Bool(is_valid)) +} + +/// ```text +/// fn embedded_curve_add( +/// point1: EmbeddedCurvePoint, +/// point2: EmbeddedCurvePoint, +/// ) -> [Field; 3] +/// ``` +fn embedded_curve_add(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + let (point1, point2) = check_two_arguments(arguments, location)?; + + let (p1x, p1y, p1inf) = get_embedded_curve_point(point1)?; + let (p2x, p2y, p2inf) = get_embedded_curve_point(point2)?; + + let (x, y, inf) = Bn254BlackBoxSolver + .ec_add(&p1x, &p1y, &p1inf.into(), &p2x, &p2y, &p2inf.into()) + .map_err(|e| InterpreterError::BlackBoxError(e, location))?; + + Ok(to_field_array(&[x, y, inf])) +} + +/// ```text +/// pub fn multi_scalar_mul( +/// points: [EmbeddedCurvePoint; N], +/// scalars: [EmbeddedCurveScalar; N], +/// ) -> [Field; 3] +/// ``` +fn multi_scalar_mul( + interner: &mut NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let (points, scalars) = check_two_arguments(arguments, location)?; + + let (points, _) = get_array_map(interner, points, get_embedded_curve_point)?; + let (scalars, _) = get_array_map(interner, scalars, get_embedded_curve_scalar)?; + + let points: Vec<_> = points.into_iter().flat_map(|(x, y, inf)| [x, y, inf.into()]).collect(); + let mut scalars_lo = Vec::new(); + let mut scalars_hi = Vec::new(); + for (lo, hi) in scalars { + scalars_lo.push(lo); + scalars_hi.push(hi); + } + + let (x, y, inf) = Bn254BlackBoxSolver + .multi_scalar_mul(&points, &scalars_lo, &scalars_hi) + .map_err(|e| InterpreterError::BlackBoxError(e, location))?; + + Ok(to_field_array(&[x, y, inf])) +} + +/// `poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N]` fn poseidon2_permutation( interner: &mut NodeInterner, arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { let (input, state_length) = check_two_arguments(arguments, location)?; - let input_location = input.1; - let (input, typ) = get_array(interner, input)?; + let (input, typ) = get_array_map(interner, input, get_field)?; let state_length = get_u32(state_length)?; - let input = try_vecmap(input, |integer| get_field((integer, input_location)))?; - - // Currently locked to only bn254! let fields = Bn254BlackBoxSolver .poseidon2_permutation(&input, state_length) .map_err(|error| InterpreterError::BlackBoxError(error, location))?; @@ -76,25 +333,135 @@ fn poseidon2_permutation( Ok(Value::Array(array, typ)) } +/// `fn keccakf1600(input: [u64; 25]) -> [u64; 25] {}` fn keccakf1600( interner: &mut NodeInterner, arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { let input = check_one_argument(arguments, location)?; - let input_location = input.1; - let (input, typ) = get_array(interner, input)?; + let (state, typ) = get_fixed_array_map(interner, input, get_u64)?; - let input = try_vecmap(input, |integer| get_u64((integer, input_location)))?; - - let mut state = [0u64; 25]; - for (it, input_value) in state.iter_mut().zip(input.iter()) { - *it = *input_value; - } let result_lanes = acvm::blackbox_solver::keccakf1600(state) .map_err(|error| InterpreterError::BlackBoxError(error, location))?; let array: Vector = result_lanes.into_iter().map(Value::U64).collect(); Ok(Value::Array(array, typ)) } + +/// `pub fn sha256_compression(input: [u32; 16], state: [u32; 8]) -> [u32; 8]` +fn sha256_compression( + interner: &mut NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let (input, state) = check_two_arguments(arguments, location)?; + + let (input, _) = get_fixed_array_map(interner, input, get_u32)?; + let (mut state, typ) = get_fixed_array_map(interner, state, get_u32)?; + + acvm::blackbox_solver::sha256_compression(&mut state, &input); + + let state = state.into_iter().map(Value::U32).collect(); + Ok(Value::Array(state, typ)) +} + +/// Decode a `BigInt` struct. +/// +/// Returns the ID of the value in the solver. +fn get_bigint_id((value, location): (Value, Location)) -> IResult { + let (fields, typ) = get_struct_fields("BigInt", (value, location))?; + let p = get_struct_field("pointer", &fields, &typ, location, get_u32)?; + let m = get_struct_field("modulus", &fields, &typ, location, get_u32)?; + assert_eq!(p, m, "`pointer` and `modulus` are expected to be the same"); + Ok(p) +} + +/// Decode an `EmbeddedCurvePoint` struct. +/// +/// Returns `(x, y, is_infinite)`. +fn get_embedded_curve_point( + (value, location): (Value, Location), +) -> IResult<(FieldElement, FieldElement, bool)> { + let (fields, typ) = get_struct_fields("EmbeddedCurvePoint", (value, location))?; + let x = get_struct_field("x", &fields, &typ, location, get_field)?; + let y = get_struct_field("y", &fields, &typ, location, get_field)?; + let is_infinite = get_struct_field("is_infinite", &fields, &typ, location, get_bool)?; + Ok((x, y, is_infinite)) +} + +/// Decode an `EmbeddedCurveScalar` struct. +/// +/// Returns `(lo, hi)`. +fn get_embedded_curve_scalar( + (value, location): (Value, Location), +) -> IResult<(FieldElement, FieldElement)> { + let (fields, typ) = get_struct_fields("EmbeddedCurveScalar", (value, location))?; + let lo = get_struct_field("lo", &fields, &typ, location, get_field)?; + let hi = get_struct_field("hi", &fields, &typ, location, get_field)?; + Ok((lo, hi)) +} + +fn to_bigint(id: u32, typ: Type) -> Value { + to_struct([("pointer", Value::U32(id)), ("modulus", Value::U32(id))], typ) +} + +#[cfg(test)] +mod tests { + use acvm::acir::BlackBoxFunc; + use noirc_errors::Location; + use strum::IntoEnumIterator; + + use crate::hir::comptime::tests::with_interpreter; + use crate::hir::comptime::InterpreterError::{ + ArgumentCountMismatch, InvalidInComptimeContext, Unimplemented, + }; + use crate::Type; + + use super::call_foreign; + + /// Check that all `BlackBoxFunc` are covered by `call_foreign`. + #[test] + fn test_blackbox_implemented() { + let dummy = " + comptime fn main() -> pub u8 { + 0 + } + "; + + let not_implemented = with_interpreter(dummy, |interpreter, _, _| { + let no_location = Location::dummy(); + let mut not_implemented = Vec::new(); + + for blackbox in BlackBoxFunc::iter() { + let name = blackbox.name(); + match call_foreign( + interpreter.elaborator.interner, + &mut interpreter.bigint_solver, + name, + Vec::new(), + Type::Unit, + no_location, + ) { + Ok(_) => { + // Exists and works with no args (unlikely) + } + Err(ArgumentCountMismatch { .. }) => { + // Exists but doesn't work with no args (expected) + } + Err(InvalidInComptimeContext { .. }) => {} + Err(Unimplemented { .. }) => not_implemented.push(name), + Err(other) => panic!("unexpected error: {other:?}"), + }; + } + + not_implemented + }); + + assert!( + not_implemented.is_empty(), + "unimplemented blackbox functions: {not_implemented:?}" + ); + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs index e033ec6ddb9..2d3bf928917 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -9,14 +9,20 @@ use noirc_errors::Location; use super::errors::InterpreterError; use super::value::Value; +use super::Interpreter; use crate::elaborator::Elaborator; -use crate::hir::def_collector::dc_crate::DefCollector; +use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::def_collector::dc_mod::collect_defs; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleData}; use crate::hir::{Context, ParsedFiles}; +use crate::node_interner::FuncId; use crate::parse_program; -fn interpret_helper(src: &str) -> Result { +/// Create an interpreter for a code snippet and pass it to a test function. +pub(crate) fn with_interpreter( + src: &str, + f: impl FnOnce(&mut Interpreter, FuncId, &[(CompilationError, FileId)]) -> T, +) -> T { let file = FileId::default(); // Can't use Index::test_new here for some reason, even with #[cfg(test)]. @@ -51,14 +57,24 @@ fn interpret_helper(src: &str) -> Result { context.def_maps.insert(krate, collector.def_map); let main = context.get_main_function(&krate).expect("Expected 'main' function"); + let mut elaborator = Elaborator::elaborate_and_return_self(&mut context, krate, collector.items, None); - assert_eq!(elaborator.errors.len(), 0); + + let errors = elaborator.errors.clone(); let mut interpreter = elaborator.setup_interpreter(); - let no_location = Location::dummy(); - interpreter.call_function(main, Vec::new(), HashMap::new(), no_location) + f(&mut interpreter, main, &errors) +} + +/// Evaluate a code snippet by calling the `main` function. +fn interpret_helper(src: &str) -> Result { + with_interpreter(src, |interpreter, main, errors| { + assert_eq!(errors.len(), 0); + let no_location = Location::dummy(); + interpreter.call_function(main, Vec::new(), HashMap::new(), no_location) + }) } fn interpret(src: &str) -> Value { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index bae57daae15..e7953aab5a4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -216,6 +216,13 @@ impl<'a> ModCollector<'a> { errors.push((error.into(), self.file_id)); } + if noir_function.def.attributes.has_export() { + let error = DefCollectorErrorKind::ExportOnAssociatedFunction { + span: noir_function.name_ident().span(), + }; + errors.push((error.into(), self.file_id)); + } + let location = Location::new(noir_function.def.span, self.file_id); context.def_interner.push_function(*func_id, &noir_function.def, module, location); } @@ -944,6 +951,7 @@ pub fn collect_function( } else { function.name() == MAIN_FUNCTION }; + let has_export = function.def.attributes.has_export(); let name = function.name_ident().clone(); let func_id = interner.push_empty_fn(); @@ -954,7 +962,7 @@ pub fn collect_function( interner.register_function(func_id, &function.def); } - if !is_test && !is_entry_point_function { + if !is_test && !is_entry_point_function && !has_export { let item = UnusedItem::Function(func_id); usage_tracker.add_unused_item(module, name.clone(), item, visibility); } @@ -1087,6 +1095,12 @@ pub fn collect_impl( errors.push((error.into(), file_id)); continue; } + if method.def.attributes.has_export() { + let error = DefCollectorErrorKind::ExportOnAssociatedFunction { + span: method.name_ident().span(), + }; + errors.push((error.into(), file_id)); + } let func_id = interner.push_empty_fn(); method.def.where_clause.extend(r#impl.where_clause.clone()); @@ -1257,6 +1271,7 @@ pub(crate) fn collect_global( // Add the statement to the scope so its path can be looked up later let result = def_map.modules[module_id.0].declare_global(name.clone(), visibility, global_id); + // Globals marked as ABI don't have to be used. if !is_abi { let parent_module_id = ModuleId { krate: crate_id, local_id: module_id }; usage_tracker.add_unused_item( diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/errors.rs index c08b4ff2062..cafbc670e32 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/errors.rs @@ -84,6 +84,8 @@ pub enum DefCollectorErrorKind { UnsupportedNumericGenericType(#[from] UnsupportedNumericGenericType), #[error("The `#[test]` attribute may only be used on a non-associated function")] TestOnAssociatedFunction { span: Span }, + #[error("The `#[export]` attribute may only be used on a non-associated function")] + ExportOnAssociatedFunction { span: Span }, } impl DefCollectorErrorKind { @@ -182,8 +184,8 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { DefCollectorErrorKind::PathResolutionError(error) => error.into(), DefCollectorErrorKind::CannotReexportItemWithLessVisibility{item_name, desired_visibility} => { Diagnostic::simple_warning( - format!("cannot re-export {item_name} because it has less visibility than this use statement"), - format!("consider marking {item_name} as {desired_visibility}"), + format!("cannot re-export {item_name} because it has less visibility than this use statement"), + format!("consider marking {item_name} as {desired_visibility}"), item_name.span()) } DefCollectorErrorKind::NonStructTypeInImpl { span } => Diagnostic::simple_error( @@ -298,7 +300,11 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { String::new(), *span, ), - + DefCollectorErrorKind::ExportOnAssociatedFunction { span } => Diagnostic::simple_error( + "The `#[export]` attribute is disallowed on `impl` methods".into(), + String::new(), + *span, + ), } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs index de94f73b44b..3bb16a92fdb 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -193,11 +193,7 @@ impl CrateDefMap { module.value_definitions().filter_map(|id| { if let Some(func_id) = id.as_function() { let attributes = interner.function_attributes(&func_id); - if attributes.secondary.contains(&SecondaryAttribute::Export) { - Some(func_id) - } else { - None - } + attributes.has_export().then_some(func_id) } else { None } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs index b82eafa5b9d..80bd5247ee6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -101,6 +101,8 @@ pub enum ResolverError { JumpOutsideLoop { is_break: bool, span: Span }, #[error("Only `comptime` globals can be mutable")] MutableGlobal { span: Span }, + #[error("Globals must have a specified type")] + UnspecifiedGlobalType { span: Span, expected_type: Type }, #[error("Self-referential structs are not supported")] SelfReferentialStruct { span: Span }, #[error("#[no_predicates] attribute is only allowed on constrained functions")] @@ -431,6 +433,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) }, + ResolverError::UnspecifiedGlobalType { span, expected_type } => { + Diagnostic::simple_error( + "Globals must have a specified type".to_string(), + format!("Inferred type is `{expected_type}`"), + *span, + ) + }, ResolverError::SelfReferentialStruct { span } => { Diagnostic::simple_error( "Self-referential structs are not supported".into(), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs index dbb28cf78c0..836161c7c9f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs @@ -676,9 +676,7 @@ impl Attributes { /// This is useful for finding out if we should compile a contract method /// as an entry point or not. pub fn has_contract_library_method(&self) -> bool { - self.secondary - .iter() - .any(|attribute| attribute == &SecondaryAttribute::ContractLibraryMethod) + self.has_secondary_attr(&SecondaryAttribute::ContractLibraryMethod) } pub fn is_test_function(&self) -> bool { @@ -718,11 +716,21 @@ impl Attributes { } pub fn has_varargs(&self) -> bool { - self.secondary.iter().any(|attr| matches!(attr, SecondaryAttribute::Varargs)) + self.has_secondary_attr(&SecondaryAttribute::Varargs) } pub fn has_use_callers_scope(&self) -> bool { - self.secondary.iter().any(|attr| matches!(attr, SecondaryAttribute::UseCallersScope)) + self.has_secondary_attr(&SecondaryAttribute::UseCallersScope) + } + + /// True if the function is marked with an `#[export]` attribute. + pub fn has_export(&self) -> bool { + self.has_secondary_attr(&SecondaryAttribute::Export) + } + + /// Check if secondary attributes contain a specific instance. + pub fn has_secondary_attr(&self, attr: &SecondaryAttribute) -> bool { + self.secondary.contains(attr) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs index 736d37fe83f..6d70ea2fd6d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -2351,7 +2351,7 @@ impl Methods { } /// Select the 1 matching method with an object type matching `typ` - fn find_matching_method( + pub fn find_matching_method( &self, typ: &Type, has_self_param: bool, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index 20a5bac49f6..605236c8dda 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -1300,11 +1300,17 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { #[test] fn deny_cyclic_globals() { let src = r#" - global A = B; - global B = A; + global A: u32 = B; + global B: u32 = A; fn main() {} "#; - assert_eq!(get_program_errors(src).len(), 1); + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) + )); } #[test] @@ -3210,10 +3216,10 @@ fn as_trait_path_syntax_no_impl() { } #[test] -fn infer_globals_to_u32_from_type_use() { +fn dont_infer_globals_to_u32_from_type_use() { let src = r#" global ARRAY_LEN = 3; - global STR_LEN = 2; + global STR_LEN: _ = 2; global FMT_STR_LEN = 2; fn main() { @@ -3223,6 +3229,59 @@ fn infer_globals_to_u32_from_type_use() { } "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 3); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) + )); + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) + )); + assert!(matches!( + errors[2].0, + CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) + )); +} + +#[test] +fn dont_infer_partial_global_types() { + let src = r#" + pub global ARRAY: [Field; _] = [0; 3]; + pub global NESTED_ARRAY: [[Field; _]; 3] = [[]; 3]; + pub global STR: str<_> = "hi"; + pub global NESTED_STR: [str<_>] = &["hi"]; + pub global FMT_STR: fmtstr<_, _> = f"hi {ARRAY}"; + pub global TUPLE_WITH_MULTIPLE: ([str<_>], [[Field; _]; 3]) = (&["hi"], [[]; 3]); + + fn main() { } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 6); + for (error, _file_id) in errors { + assert!(matches!( + error, + CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) + )); + } +} + +#[test] +fn u32_globals_as_sizes_in_types() { + let src = r#" + global ARRAY_LEN: u32 = 3; + global STR_LEN: u32 = 2; + global FMT_STR_LEN: u32 = 2; + + fn main() { + let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + let _b: str = "hi"; + let _c: fmtstr = f"hi"; + } + "#; + let errors = get_program_errors(src); assert_eq!(errors.len(), 0); } @@ -3686,57 +3745,103 @@ fn allows_struct_with_generic_infix_type_as_main_input_3() { x: [u64; N * 2], } - global N = 9; + global N: u32 = 9; fn main(_x: Foo) {} "#; assert_no_errors(src); } -#[test] -fn disallows_test_attribute_on_impl_method() { - let src = r#" - pub struct Foo {} - impl Foo { - #[test] - fn foo() {} - } +fn test_disallows_attribute_on_impl_method( + attr: &str, + check_error: impl FnOnce(&CompilationError), +) { + let src = format!( + " + pub struct Foo {{ }} - fn main() {} - "#; - let errors = get_program_errors(src); + impl Foo {{ + #[{attr}] + fn foo() {{ }} + }} + + fn main() {{ }} + " + ); + let errors = get_program_errors(&src); assert_eq!(errors.len(), 1); + check_error(&errors[0].0); +} - assert!(matches!( - errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::TestOnAssociatedFunction { - span: _ - }) - )); +fn test_disallows_attribute_on_trait_impl_method( + attr: &str, + check_error: impl FnOnce(&CompilationError), +) { + let src = format!( + " + pub trait Trait {{ + fn foo() {{ }} + }} + + pub struct Foo {{ }} + + impl Trait for Foo {{ + #[{attr}] + fn foo() {{ }} + }} + + fn main() {{ }} + " + ); + let errors = get_program_errors(&src); + assert_eq!(errors.len(), 1); + check_error(&errors[0].0); } #[test] -fn disallows_test_attribute_on_trait_impl_method() { - let src = r#" - pub trait Trait { - fn foo() {} - } +fn disallows_test_attribute_on_impl_method() { + test_disallows_attribute_on_impl_method("test", |error| { + assert!(matches!( + error, + CompilationError::DefinitionError( + DefCollectorErrorKind::TestOnAssociatedFunction { .. } + ) + )); + }); +} - pub struct Foo {} - impl Trait for Foo { - #[test] - fn foo() {} - } +#[test] +fn disallows_test_attribute_on_trait_impl_method() { + test_disallows_attribute_on_trait_impl_method("test", |error| { + assert!(matches!( + error, + CompilationError::DefinitionError( + DefCollectorErrorKind::TestOnAssociatedFunction { .. } + ) + )); + }); +} - fn main() {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); +#[test] +fn disallows_export_attribute_on_impl_method() { + test_disallows_attribute_on_impl_method("export", |error| { + assert!(matches!( + error, + CompilationError::DefinitionError( + DefCollectorErrorKind::ExportOnAssociatedFunction { .. } + ) + )); + }); +} - assert!(matches!( - errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::TestOnAssociatedFunction { - span: _ - }) - )); +#[test] +fn disallows_export_attribute_on_trait_impl_method() { + test_disallows_attribute_on_trait_impl_method("export", |error| { + assert!(matches!( + error, + CompilationError::DefinitionError( + DefCollectorErrorKind::ExportOnAssociatedFunction { .. } + ) + )); + }); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs index 5f9fc887b27..c38e604f2c3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs @@ -191,8 +191,8 @@ fn errors_on_unused_type_alias() { #[test] fn warns_on_unused_global() { let src = r#" - global foo = 1; - global bar = 1; + global foo: u32 = 1; + global bar: Field = 1; fn main() { let _ = bar; @@ -216,7 +216,7 @@ fn does_not_warn_on_unused_global_if_it_has_an_abi_attribute() { let src = r#" contract foo { #[abi(notes)] - global bar = 1; + global bar: u64 = 1; } fn main() {} @@ -224,9 +224,31 @@ fn does_not_warn_on_unused_global_if_it_has_an_abi_attribute() { assert_no_errors(src); } +#[test] +fn does_not_warn_on_unused_struct_if_it_has_an_abi_attribute() { + let src = r#" + #[abi(dummy)] + struct Foo { bar: u8 } + + fn main() {} + "#; + assert_no_errors(src); +} + +#[test] +fn does_not_warn_on_unused_function_if_it_has_an_export_attribute() { + let src = r#" + #[export] + fn foo() {} + + fn main() {} + "#; + assert_no_errors(src); +} + #[test] fn no_warning_on_inner_struct_when_parent_is_used() { - let src = r#" + let src = r#" struct Bar { inner: [Field; 3], } @@ -247,7 +269,7 @@ fn no_warning_on_inner_struct_when_parent_is_used() { #[test] fn no_warning_on_struct_if_it_has_an_abi_attribute() { - let src = r#" + let src = r#" #[abi(functions)] struct Foo { a: Field, @@ -260,7 +282,7 @@ fn no_warning_on_struct_if_it_has_an_abi_attribute() { #[test] fn no_warning_on_indirect_struct_if_it_has_an_abi_attribute() { - let src = r#" + let src = r#" struct Bar { field: Field, } @@ -277,7 +299,7 @@ fn no_warning_on_indirect_struct_if_it_has_an_abi_attribute() { #[test] fn no_warning_on_self_in_trait_impl() { - let src = r#" + let src = r#" struct Bar {} trait Foo { @@ -298,18 +320,18 @@ fn no_warning_on_self_in_trait_impl() { #[test] fn resolves_trait_where_clause_in_the_correct_module() { // This is a regression test for https://github.com/noir-lang/noir/issues/6479 - let src = r#" + let src = r#" mod foo { pub trait Foo {} } - + use foo::Foo; - + pub trait Bar where T: Foo, {} - + fn main() {} "#; assert_no_errors(src); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs index 7cfec32062d..824a1de4c37 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs @@ -493,3 +493,108 @@ fn does_not_error_if_referring_to_top_level_private_module_via_crate() { "#; assert_no_errors(src); } + +#[test] +fn visibility_bug_inside_comptime() { + let src = r#" + mod foo { + pub struct Foo { + inner: Field, + } + + impl Foo { + pub fn new(inner: Field) -> Self { + Self { inner } + } + } + } + + use foo::Foo; + + fn main() { + let _ = Foo::new(5); + let _ = comptime { Foo::new(5) }; + } + "#; + assert_no_errors(src); +} + +#[test] +fn errors_if_accessing_private_struct_member_inside_comptime_context() { + let src = r#" + mod foo { + pub struct Foo { + inner: Field, + } + + impl Foo { + pub fn new(inner: Field) -> Self { + Self { inner } + } + } + } + + use foo::Foo; + + fn main() { + comptime { + let foo = Foo::new(5); + let _ = foo.inner; + }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::PathResolutionError( + PathResolutionError::Private(ident), + )) = &errors[0].0 + else { + panic!("Expected a private error"); + }; + + assert_eq!(ident.to_string(), "inner"); +} + +#[test] +fn errors_if_accessing_private_struct_member_inside_function_generated_at_comptime() { + let src = r#" + mod foo { + pub struct Foo { + foo_inner: Field, + } + } + + use foo::Foo; + + #[generate_inner_accessor] + struct Bar { + bar_inner: Foo, + } + + comptime fn generate_inner_accessor(_s: StructDefinition) -> Quoted { + quote { + fn bar_get_foo_inner(x: Bar) -> Field { + x.bar_inner.foo_inner + } + } + } + + fn main(x: Bar) { + let _ = bar_get_foo_inner(x); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::PathResolutionError( + PathResolutionError::Private(ident), + )) = &errors[0].0 + else { + panic!("Expected a private error"); + }; + + assert_eq!(ident.to_string(), "foo_inner"); +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/usage_tracker.rs b/noir/noir-repo/compiler/noirc_frontend/src/usage_tracker.rs index fa87ca6961b..6987358ddb7 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/usage_tracker.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/usage_tracker.rs @@ -35,6 +35,8 @@ pub struct UsageTracker { } impl UsageTracker { + /// Register an item as unused, waiting to be marked as used later. + /// Things that should not emit warnings should not be added at all. pub(crate) fn add_unused_item( &mut self, module_id: ModuleId, @@ -73,6 +75,7 @@ impl UsageTracker { }; } + /// Get all the unused items per module. pub fn unused_items(&self) -> &HashMap> { &self.unused_items } diff --git a/noir/noir-repo/compiler/wasm/package.json b/noir/noir-repo/compiler/wasm/package.json index 8528d4b9633..946ba8dc699 100644 --- a/noir/noir-repo/compiler/wasm/package.json +++ b/noir/noir-repo/compiler/wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.39.0", + "version": "1.0.0-beta.0", "license": "(MIT OR Apache-2.0)", "main": "dist/main.js", "types": "./dist/types/src/index.d.cts", diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index a386ed80ee9..36bba737cd7 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -171,6 +171,7 @@ "PLONKish", "pprof", "precomputes", + "preheader", "preimage", "preprocess", "prettytable", @@ -182,6 +183,7 @@ "quantile", "quasiquote", "rangemap", + "refcount", "repr", "reqwest", "rfind", diff --git a/noir/noir-repo/noir_stdlib/src/bigint.nr b/noir/noir-repo/noir_stdlib/src/bigint.nr index be072257be3..c94a7a75f25 100644 --- a/noir/noir-repo/noir_stdlib/src/bigint.nr +++ b/noir/noir-repo/noir_stdlib/src/bigint.nr @@ -1,27 +1,27 @@ use crate::cmp::Eq; use crate::ops::{Add, Div, Mul, Sub}; -global bn254_fq = &[ +global bn254_fq: [u8] = &[ 0x47, 0xFD, 0x7C, 0xD8, 0x16, 0x8C, 0x20, 0x3C, 0x8d, 0xca, 0x71, 0x68, 0x91, 0x6a, 0x81, 0x97, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30, ]; -global bn254_fr = &[ +global bn254_fr: [u8] = &[ 1, 0, 0, 240, 147, 245, 225, 67, 145, 112, 185, 121, 72, 232, 51, 40, 93, 88, 129, 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48, ]; -global secpk1_fr = &[ +global secpk1_fr: [u8] = &[ 0x41, 0x41, 0x36, 0xD0, 0x8C, 0x5E, 0xD2, 0xBF, 0x3B, 0xA0, 0x48, 0xAF, 0xE6, 0xDC, 0xAE, 0xBA, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ]; -global secpk1_fq = &[ +global secpk1_fq: [u8] = &[ 0x2F, 0xFC, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ]; -global secpr1_fq = &[ +global secpr1_fq: [u8] = &[ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, ]; -global secpr1_fr = &[ +global secpr1_fr: [u8] = &[ 81, 37, 99, 252, 194, 202, 185, 243, 132, 158, 23, 167, 173, 250, 230, 188, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, ]; diff --git a/noir/noir-repo/noir_stdlib/src/collections/map.nr b/noir/noir-repo/noir_stdlib/src/collections/map.nr index b46bfa837fb..bcce08faab4 100644 --- a/noir/noir-repo/noir_stdlib/src/collections/map.nr +++ b/noir/noir-repo/noir_stdlib/src/collections/map.nr @@ -7,8 +7,8 @@ use crate::option::Option; // We use load factor alpha_max = 0.75. // Upon exceeding it, assert will fail in order to inform the user // about performance degradation, so that he can adjust the capacity. -global MAX_LOAD_FACTOR_NUMERATOR = 3; -global MAX_LOAD_FACTOR_DEN0MINATOR = 4; +global MAX_LOAD_FACTOR_NUMERATOR: u32 = 3; +global MAX_LOAD_FACTOR_DEN0MINATOR: u32 = 4; /// `HashMap` is used to efficiently store and look up key-value pairs. /// diff --git a/noir/noir-repo/noir_stdlib/src/ec/consts/mod.nr b/noir/noir-repo/noir_stdlib/src/ec/consts/mod.nr deleted file mode 100644 index 73c594c6a26..00000000000 --- a/noir/noir-repo/noir_stdlib/src/ec/consts/mod.nr +++ /dev/null @@ -1 +0,0 @@ -pub mod te; diff --git a/noir/noir-repo/noir_stdlib/src/ec/consts/te.nr b/noir/noir-repo/noir_stdlib/src/ec/consts/te.nr deleted file mode 100644 index 150eb849947..00000000000 --- a/noir/noir-repo/noir_stdlib/src/ec/consts/te.nr +++ /dev/null @@ -1,33 +0,0 @@ -use crate::ec::tecurve::affine::Curve as TECurve; -use crate::ec::tecurve::affine::Point as TEPoint; - -pub struct BabyJubjub { - pub curve: TECurve, - pub base8: TEPoint, - pub suborder: Field, -} - -#[field(bn254)] -// Uncommenting this results in deprecated warnings in the stdlib -// #[deprecated] -pub fn baby_jubjub() -> BabyJubjub { - BabyJubjub { - // Baby Jubjub (ERC-2494) parameters in affine representation - curve: TECurve::new( - 168700, - 168696, - // G - TEPoint::new( - 995203441582195749578291179787384436505546430278305826713579947235728471134, - 5472060717959818805561601436314318772137091100104008585924551046643952123905, - ), - ), - // [8]G precalculated - base8: TEPoint::new( - 5299619240641551281634865583518297030282874472190772894086521144482721001553, - 16950150798460657717958625567821834550301663161624707787222815936182638968203, - ), - // The size of the group formed from multiplying the base field by 8. - suborder: 2736030358979909402780800718157159386076813972158567259200215660948447373041, - } -} diff --git a/noir/noir-repo/noir_stdlib/src/ec/mod.nr b/noir/noir-repo/noir_stdlib/src/ec/mod.nr deleted file mode 100644 index b62bc99d9c8..00000000000 --- a/noir/noir-repo/noir_stdlib/src/ec/mod.nr +++ /dev/null @@ -1,199 +0,0 @@ -// Elliptic curve implementation -// Overview -// ======== -// The following three elliptic curve representations are admissible: -pub mod tecurve; // Twisted Edwards curves -pub mod swcurve; // Elliptic curves in Short Weierstrass form -pub mod montcurve; // Montgomery curves -pub mod consts; // Commonly used curve presets -// -// Note that Twisted Edwards and Montgomery curves are (birationally) equivalent, so that -// they may be freely converted between one another, whereas Short Weierstrass curves are -// more general. Diagramatically: -// -// tecurve == montcurve `subset` swcurve -// -// Each module is further divided into two submodules, 'affine' and 'curvegroup', depending -// on the preferred coordinate representation. Affine coordinates are none other than the usual -// two-dimensional Cartesian coordinates used in the definitions of these curves, whereas -// 'CurveGroup' coordinates (terminology borrowed from Arkworks, whose conventions we try -// to follow) are special coordinate systems with respect to which the group operations may be -// implemented more efficiently, usually by means of an appropriate choice of projective coordinates. -// -// In each of these submodules, there is a Point struct and a Curve struct, the former -// representing a point in the coordinate system and the latter a curve configuration. -// -// Points -// ====== -// Points may be instantiated using the associated function `new`, which takes coordinates -// as its arguments. For instance, -// -// `let p = swcurve::Point::new(1,1);` -// -// The additive identity may be constructed by a call to the associated function `zero` of no -// arguments: -// -// `let zero = swcurve::Point::zero();` -// -// Points may be tested for equality by calling the method `eq`: -// -// `let pred = p.eq(zero);` -// -// There is also the method `is_zero` to explicitly check whether a point is the additive identity: -// -// `constrain pred == p.is_zero();` -// -// Points may be negated by calling the `negate` method and converted to CurveGroup (or affine) -// coordinates by calling the `into_group` (resp. `into_affine`) method on them. Finally, -// Points may be freely mapped between their respective Twisted Edwards and Montgomery -// representations by calling the `into_montcurve` or `into_tecurve` methods. For mappings -// between Twisted Edwards/Montgomery curves and Short Weierstrass curves, see the Curve section -// below, as the underlying mappings are those of curves rather than ambient spaces. -// As a rule, Points in affine (or CurveGroup) coordinates are mapped to Points in affine -// (resp. CurveGroup) coordinates. -// -// Curves -// ====== -// A curve configuration (Curve) is completely determined by the Field coefficients of its defining -// equation (a and b in the case of swcurve, a and d in the case of tecurve, and j and k in -// the case of montcurve) together with a generator (`gen`) in the corresponding coordinate system. -// For example, the Baby Jubjub curve configuration as defined in ERC-2494 may be instantiated as a Twisted -// Edwards curve in affine coordinates as follows: -// -// `let bjj_affine = tecurve::Curve::new(168700, 168696, tecurve::Point::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905));` -// -// The `contains` method may be used to check whether a Point lies on a given curve: -// -// `constrain bjj_affine.contains(tecurve::Point::zero());` -// -// The elliptic curve group's addition operation is exposed as the `add` method, e.g. -// -// `let p = bjj_affine.add(bjj_affine.gen, bjj_affine.gen);` -// -// subtraction as the `subtract` method, e.g. -// -// `constrain tecurve::Point::zero().eq(bjj_affine.subtract(bjj_affine.gen, bjj_affine.gen));` -// -// scalar multiplication as the `mul` method, where the scalar is assumed to be a Field* element, e.g. -// -// `constrain tecurve::Point::zero().eq(bjj_affine.mul(2, tecurve::Point::zero());` -// -// There is a scalar multiplication method (`bit_mul`) provided where the scalar input is expected to be -// an array of bits (little-endian convention), as well as a multi-scalar multiplication method** (`msm`) -// which takes an array of Field elements and an array of elliptic curve points as arguments, both assumed -// to be of the same length. -// -// Curve configurations may be converted between different coordinate representations by calling the `into_group` -// and `into_affine` methods on them, e.g. -// -// `let bjj_curvegroup = bjj_affine.into_group();` -// -// Curve configurations may also be converted between different curve representations by calling the `into_swcurve`, -// `into_montcurve` and `into_tecurve` methods subject to the relation between the curve representations mentioned -// above. Note that it is possible to map Points from a Twisted Edwards/Montgomery curve to the corresponding -// Short Weierstrass representation and back, and the methods to do so are exposed as `map_into_swcurve` and -// `map_from_swcurve`, which each take one argument, the point to be mapped. -// -// Curve maps -// ========== -// There are a few different ways of mapping Field elements to elliptic curves. Here we provide the simplified -// Shallue-van de Woestijne-Ulas and Elligator 2 methods, the former being applicable to all curve types -// provided above subject to the constraint that the coefficients of the corresponding Short Weierstrass curve satisfies -// a*b != 0 and the latter being applicable to Montgomery and Twisted Edwards curves subject to the constraint that -// the coefficients of the corresponding Montgomery curve satisfy j*k != 0 and (j^2 - 4)/k^2 is non-square. -// -// The simplified Shallue-van de Woestijne-Ulas method is exposed as the method `swu_map` on the Curve configuration and -// depends on two parameters, a Field element z != -1 for which g(x) - z is irreducible over Field and g(b/(z*a)) is -// square, where g(x) = x^3 + a*x + b is the right-hand side of the defining equation of the corresponding Short -// Weierstrass curve, and a Field element u to be mapped onto the curve. For example, in the case of bjj_affine above, -// it may be determined using the scripts provided at that z = 5. -// -// The Elligator 2 method is exposed as the method `elligator2_map` on the Curve configurations of Montgomery and -// Twisted Edwards curves. Like the simplified SWU method above, it depends on a certain non-square element of Field, -// but this element need not satisfy any further conditions, so it is included as the (Field-dependent) constant -//`ZETA` below. Thus, the `elligator2_map` method depends only on one parameter, the Field element to be mapped onto -// the curve. -// -// For details on all of the above in the context of hashing to elliptic curves, see . -// -// -// *TODO: Replace Field with Bigint. -// **TODO: Support arrays of structs to make this work. -// Field-dependent constant ZETA = a non-square element of Field -// Required for Elligator 2 map -// TODO: Replace with built-in constant. -global ZETA = 5; -// Field-dependent constants for Tonelli-Shanks algorithm (see sqrt function below) -// TODO: Possibly make this built-in. -global C1 = 28; -global C3 = 40770029410420498293352137776570907027550720424234931066070132305055; -global C5 = 19103219067921713944291392827692070036145651957329286315305642004821462161904; -// Higher-order version of scalar multiplication -// TODO: Make this work so that the submodules' bit_mul may be defined in terms of it. -//fn bit_mul(add: fn(T,T) -> T, e: T, bits: [u1; N], p: T) -> T { -// let mut out = e; -// let n = bits.len(); -// -// for i in 0..n { -// out = add( -// add(out, out), -// if(bits[n - i - 1] == 0) {e} else {p}); -// } -// -// out -//} -// TODO: Make this built-in. -pub fn safe_inverse(x: Field) -> Field { - if x == 0 { - 0 - } else { - 1 / x - } -} -// Boolean indicating whether Field element is a square, i.e. whether there exists a y in Field s.t. x = y*y. -pub fn is_square(x: Field) -> bool { - let v = pow(x, 0 - 1 / 2); - - v * (v - 1) == 0 -} -// Power function of two Field arguments of arbitrary size. -// Adapted from std::field::pow_32. -pub fn pow(x: Field, y: Field) -> Field { - let mut r = 1 as Field; - let b: [u1; 254] = y.to_le_bits(); - - for i in 0..254 { - r *= r; - r *= (b[254 - 1 - i] as Field) * x + (1 - b[254 - 1 - i] as Field); - } - - r -} -// Tonelli-Shanks algorithm for computing the square root of a Field element. -// Requires C1 = max{c: 2^c divides (p-1)}, where p is the order of Field -// as well as C3 = (C2 - 1)/2, where C2 = (p-1)/(2^c1), -// and C5 = ZETA^C2, where ZETA is a non-square element of Field. -// These are pre-computed above as globals. -pub fn sqrt(x: Field) -> Field { - let mut z = pow(x, C3); - let mut t = z * z * x; - z *= x; - let mut b = t; - let mut c = C5; - - for i in 0..(C1 - 1) { - for _j in 1..(C1 - i - 1) { - b *= b; - } - - z *= if b == 1 { 1 } else { c }; - - c *= c; - - t *= if b == 1 { 1 } else { c }; - - b = t; - } - - z -} diff --git a/noir/noir-repo/noir_stdlib/src/ec/montcurve.nr b/noir/noir-repo/noir_stdlib/src/ec/montcurve.nr deleted file mode 100644 index 239585ba13f..00000000000 --- a/noir/noir-repo/noir_stdlib/src/ec/montcurve.nr +++ /dev/null @@ -1,387 +0,0 @@ -pub mod affine { - // Affine representation of Montgomery curves - // Points are represented by two-dimensional Cartesian coordinates. - // All group operations are induced by those of the corresponding Twisted Edwards curve. - // See e.g. for details on the correspondences. - use crate::cmp::Eq; - use crate::ec::is_square; - use crate::ec::montcurve::curvegroup; - use crate::ec::safe_inverse; - use crate::ec::sqrt; - use crate::ec::swcurve::affine::Curve as SWCurve; - use crate::ec::swcurve::affine::Point as SWPoint; - use crate::ec::tecurve::affine::Curve as TECurve; - use crate::ec::tecurve::affine::Point as TEPoint; - use crate::ec::ZETA; - - // Curve specification - pub struct Curve { // Montgomery Curve configuration (ky^2 = x^3 + j*x^2 + x) - pub j: Field, - pub k: Field, - // Generator as point in Cartesian coordinates - pub gen: Point, - } - // Point in Cartesian coordinates - pub struct Point { - pub x: Field, - pub y: Field, - pub infty: bool, // Indicator for point at infinity - } - - impl Point { - // Point constructor - pub fn new(x: Field, y: Field) -> Self { - Self { x, y, infty: false } - } - - // Check if zero - pub fn is_zero(self) -> bool { - self.infty - } - - // Conversion to CurveGroup coordinates - pub fn into_group(self) -> curvegroup::Point { - if self.is_zero() { - curvegroup::Point::zero() - } else { - let (x, y) = (self.x, self.y); - curvegroup::Point::new(x, y, 1) - } - } - - // Additive identity - pub fn zero() -> Self { - Self { x: 0, y: 0, infty: true } - } - - // Negation - pub fn negate(self) -> Self { - let Self { x, y, infty } = self; - - Self { x, y: 0 - y, infty } - } - - // Map into equivalent Twisted Edwards curve - pub fn into_tecurve(self) -> TEPoint { - let Self { x, y, infty } = self; - - if infty | (y * (x + 1) == 0) { - TEPoint::zero() - } else { - TEPoint::new(x / y, (x - 1) / (x + 1)) - } - } - } - - impl Eq for Point { - fn eq(self, p: Self) -> bool { - (self.infty & p.infty) | (!self.infty & !p.infty & (self.x == p.x) & (self.y == p.y)) - } - } - - impl Curve { - // Curve constructor - pub fn new(j: Field, k: Field, gen: Point) -> Self { - // Check curve coefficients - assert(k != 0); - assert(j * j != 4); - - let curve = Self { j, k, gen }; - - // gen should be on the curve - assert(curve.contains(curve.gen)); - - curve - } - - // Conversion to CurveGroup coordinates - pub fn into_group(self) -> curvegroup::Curve { - curvegroup::Curve::new(self.j, self.k, self.gen.into_group()) - } - - // Membership check - pub fn contains(self, p: Point) -> bool { - let Self { j, k, gen: _gen } = self; - let Point { x, y, infty } = p; - - infty | (k * y * y == x * (x * x + j * x + 1)) - } - - // Point addition - pub fn add(self, p1: Point, p2: Point) -> Point { - self.into_tecurve().add(p1.into_tecurve(), p2.into_tecurve()).into_montcurve() - } - - // Scalar multiplication with scalar represented by a bit array (little-endian convention). - // If k is the natural number represented by `bits`, then this computes p + ... + p k times. - pub fn bit_mul(self, bits: [u1; N], p: Point) -> Point { - self.into_tecurve().bit_mul(bits, p.into_tecurve()).into_montcurve() - } - - // Scalar multiplication (p + ... + p n times) - pub fn mul(self, n: Field, p: Point) -> Point { - self.into_tecurve().mul(n, p.into_tecurve()).into_montcurve() - } - - // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) - pub fn msm(self, n: [Field; N], p: [Point; N]) -> Point { - let mut out = Point::zero(); - - for i in 0..N { - out = self.add(out, self.mul(n[i], p[i])); - } - - out - } - - // Point subtraction - pub fn subtract(self, p1: Point, p2: Point) -> Point { - self.add(p1, p2.negate()) - } - - // Conversion to equivalent Twisted Edwards curve - pub fn into_tecurve(self) -> TECurve { - let Self { j, k, gen } = self; - TECurve::new((j + 2) / k, (j - 2) / k, gen.into_tecurve()) - } - - // Conversion to equivalent Short Weierstrass curve - pub fn into_swcurve(self) -> SWCurve { - let j = self.j; - let k = self.k; - let a0 = (3 - j * j) / (3 * k * k); - let b0 = (2 * j * j * j - 9 * j) / (27 * k * k * k); - - SWCurve::new(a0, b0, self.map_into_swcurve(self.gen)) - } - - // Point mapping into equivalent Short Weierstrass curve - pub fn map_into_swcurve(self, p: Point) -> SWPoint { - if p.is_zero() { - SWPoint::zero() - } else { - SWPoint::new((3 * p.x + self.j) / (3 * self.k), p.y / self.k) - } - } - - // Point mapping from equivalent Short Weierstrass curve - pub fn map_from_swcurve(self, p: SWPoint) -> Point { - let SWPoint { x, y, infty } = p; - let j = self.j; - let k = self.k; - - Point { x: (3 * k * x - j) / 3, y: y * k, infty } - } - - // Elligator 2 map-to-curve method; see . - pub fn elligator2_map(self, u: Field) -> Point { - let j = self.j; - let k = self.k; - let z = ZETA; // Non-square Field element required for map - // Check whether curve is admissible - assert(j != 0); - let l = (j * j - 4) / (k * k); - assert(l != 0); - assert(is_square(l) == false); - - let x1 = safe_inverse(1 + z * u * u) * (0 - (j / k)); - - let gx1 = x1 * x1 * x1 + (j / k) * x1 * x1 + x1 / (k * k); - let x2 = 0 - x1 - (j / k); - let gx2 = x2 * x2 * x2 + (j / k) * x2 * x2 + x2 / (k * k); - - let x = if is_square(gx1) { x1 } else { x2 }; - - let y = if is_square(gx1) { - let y0 = sqrt(gx1); - if y0.sgn0() == 1 { - y0 - } else { - 0 - y0 - } - } else { - let y0 = sqrt(gx2); - if y0.sgn0() == 0 { - y0 - } else { - 0 - y0 - } - }; - - Point::new(x * k, y * k) - } - - // SWU map-to-curve method (via rational map) - pub fn swu_map(self, z: Field, u: Field) -> Point { - self.map_from_swcurve(self.into_swcurve().swu_map(z, u)) - } - } -} -pub mod curvegroup { - // Affine representation of Montgomery curves - // Points are represented by three-dimensional projective (homogeneous) coordinates. - // All group operations are induced by those of the corresponding Twisted Edwards curve. - // See e.g. for details on the correspondences. - use crate::cmp::Eq; - use crate::ec::montcurve::affine; - use crate::ec::swcurve::curvegroup::Curve as SWCurve; - use crate::ec::swcurve::curvegroup::Point as SWPoint; - use crate::ec::tecurve::curvegroup::Curve as TECurve; - use crate::ec::tecurve::curvegroup::Point as TEPoint; - - pub struct Curve { // Montgomery Curve configuration (ky^2 z = x*(x^2 + j*x*z + z*z)) - pub j: Field, - pub k: Field, - // Generator as point in projective coordinates - pub gen: Point, - } - // Point in projective coordinates - pub struct Point { - pub x: Field, - pub y: Field, - pub z: Field, - } - - impl Point { - // Point constructor - pub fn new(x: Field, y: Field, z: Field) -> Self { - Self { x, y, z } - } - - // Check if zero - pub fn is_zero(self) -> bool { - self.z == 0 - } - - // Conversion to affine coordinates - pub fn into_affine(self) -> affine::Point { - if self.is_zero() { - affine::Point::zero() - } else { - let (x, y, z) = (self.x, self.y, self.z); - affine::Point::new(x / z, y / z) - } - } - - // Additive identity - pub fn zero() -> Self { - Self { x: 0, y: 1, z: 0 } - } - - // Negation - pub fn negate(self) -> Self { - let Self { x, y, z } = self; - - Point::new(x, 0 - y, z) - } - - // Map into equivalent Twisted Edwards curve - pub fn into_tecurve(self) -> TEPoint { - self.into_affine().into_tecurve().into_group() - } - } - - impl Eq for Point { - fn eq(self, p: Self) -> bool { - (self.z == p.z) - | (((self.x * self.z) == (p.x * p.z)) & ((self.y * self.z) == (p.y * p.z))) - } - } - - impl Curve { - // Curve constructor - pub fn new(j: Field, k: Field, gen: Point) -> Self { - // Check curve coefficients - assert(k != 0); - assert(j * j != 4); - - let curve = Self { j, k, gen }; - - // gen should be on the curve - assert(curve.contains(curve.gen)); - - curve - } - - // Conversion to affine coordinates - pub fn into_affine(self) -> affine::Curve { - affine::Curve::new(self.j, self.k, self.gen.into_affine()) - } - - // Membership check - pub fn contains(self, p: Point) -> bool { - let Self { j, k, gen: _gen } = self; - let Point { x, y, z } = p; - - k * y * y * z == x * (x * x + j * x * z + z * z) - } - - // Point addition - pub fn add(self, p1: Point, p2: Point) -> Point { - self.into_affine().add(p1.into_affine(), p2.into_affine()).into_group() - } - - // Scalar multiplication with scalar represented by a bit array (little-endian convention). - // If k is the natural number represented by `bits`, then this computes p + ... + p k times. - pub fn bit_mul(self, bits: [u1; N], p: Point) -> Point { - self.into_tecurve().bit_mul(bits, p.into_tecurve()).into_montcurve() - } - - // Scalar multiplication (p + ... + p n times) - pub fn mul(self, n: Field, p: Point) -> Point { - self.into_tecurve().mul(n, p.into_tecurve()).into_montcurve() - } - - // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) - pub fn msm(self, n: [Field; N], p: [Point; N]) -> Point { - let mut out = Point::zero(); - - for i in 0..N { - out = self.add(out, self.mul(n[i], p[i])); - } - - out - } - - // Point subtraction - pub fn subtract(self, p1: Point, p2: Point) -> Point { - self.add(p1, p2.negate()) - } - - // Conversion to equivalent Twisted Edwards curve - pub fn into_tecurve(self) -> TECurve { - let Self { j, k, gen } = self; - TECurve::new((j + 2) / k, (j - 2) / k, gen.into_tecurve()) - } - - // Conversion to equivalent Short Weierstrass curve - pub fn into_swcurve(self) -> SWCurve { - let j = self.j; - let k = self.k; - let a0 = (3 - j * j) / (3 * k * k); - let b0 = (2 * j * j * j - 9 * j) / (27 * k * k * k); - - SWCurve::new(a0, b0, self.map_into_swcurve(self.gen)) - } - - // Point mapping into equivalent Short Weierstrass curve - pub fn map_into_swcurve(self, p: Point) -> SWPoint { - self.into_affine().map_into_swcurve(p.into_affine()).into_group() - } - - // Point mapping from equivalent Short Weierstrass curve - pub fn map_from_swcurve(self, p: SWPoint) -> Point { - self.into_affine().map_from_swcurve(p.into_affine()).into_group() - } - - // Elligator 2 map-to-curve method - pub fn elligator2_map(self, u: Field) -> Point { - self.into_affine().elligator2_map(u).into_group() - } - - // SWU map-to-curve method (via rational map) - pub fn swu_map(self, z: Field, u: Field) -> Point { - self.into_affine().swu_map(z, u).into_group() - } - } -} diff --git a/noir/noir-repo/noir_stdlib/src/ec/swcurve.nr b/noir/noir-repo/noir_stdlib/src/ec/swcurve.nr deleted file mode 100644 index d9c1cf8c8c7..00000000000 --- a/noir/noir-repo/noir_stdlib/src/ec/swcurve.nr +++ /dev/null @@ -1,394 +0,0 @@ -pub mod affine { - // Affine representation of Short Weierstrass curves - // Points are represented by two-dimensional Cartesian coordinates. - // Group operations are implemented in terms of those in CurveGroup (in this case, extended Twisted Edwards) coordinates - // for reasons of efficiency, cf. . - use crate::cmp::Eq; - use crate::ec::is_square; - use crate::ec::safe_inverse; - use crate::ec::sqrt; - use crate::ec::swcurve::curvegroup; - - // Curve specification - pub struct Curve { // Short Weierstrass curve - // Coefficients in defining equation y^2 = x^3 + ax + b - pub a: Field, - pub b: Field, - // Generator as point in Cartesian coordinates - pub gen: Point, - } - // Point in Cartesian coordinates - pub struct Point { - pub x: Field, - pub y: Field, - pub infty: bool, // Indicator for point at infinity - } - - impl Point { - // Point constructor - pub fn new(x: Field, y: Field) -> Self { - Self { x, y, infty: false } - } - - // Check if zero - pub fn is_zero(self) -> bool { - self.eq(Point::zero()) - } - - // Conversion to CurveGroup coordinates - pub fn into_group(self) -> curvegroup::Point { - let Self { x, y, infty } = self; - - if infty { - curvegroup::Point::zero() - } else { - curvegroup::Point::new(x, y, 1) - } - } - - // Additive identity - pub fn zero() -> Self { - Self { x: 0, y: 0, infty: true } - } - - // Negation - pub fn negate(self) -> Self { - let Self { x, y, infty } = self; - Self { x, y: 0 - y, infty } - } - } - - impl Eq for Point { - fn eq(self, p: Self) -> bool { - let Self { x: x1, y: y1, infty: inf1 } = self; - let Self { x: x2, y: y2, infty: inf2 } = p; - - (inf1 & inf2) | (!inf1 & !inf2 & (x1 == x2) & (y1 == y2)) - } - } - - impl Curve { - // Curve constructor - pub fn new(a: Field, b: Field, gen: Point) -> Curve { - // Check curve coefficients - assert(4 * a * a * a + 27 * b * b != 0); - - let curve = Curve { a, b, gen }; - - // gen should be on the curve - assert(curve.contains(curve.gen)); - - curve - } - - // Conversion to CurveGroup coordinates - pub fn into_group(self) -> curvegroup::Curve { - let Curve { a, b, gen } = self; - - curvegroup::Curve { a, b, gen: gen.into_group() } - } - - // Membership check - pub fn contains(self, p: Point) -> bool { - let Point { x, y, infty } = p; - infty | (y * y == x * x * x + self.a * x + self.b) - } - - // Point addition, implemented in terms of mixed addition for reasons of efficiency - pub fn add(self, p1: Point, p2: Point) -> Point { - self.mixed_add(p1, p2.into_group()).into_affine() - } - - // Mixed point addition, i.e. first argument in affine, second in CurveGroup coordinates. - pub fn mixed_add(self, p1: Point, p2: curvegroup::Point) -> curvegroup::Point { - if p1.is_zero() { - p2 - } else if p2.is_zero() { - p1.into_group() - } else { - let Point { x: x1, y: y1, infty: _inf } = p1; - let curvegroup::Point { x: x2, y: y2, z: z2 } = p2; - let you1 = x1 * z2 * z2; - let you2 = x2; - let s1 = y1 * z2 * z2 * z2; - let s2 = y2; - - if you1 == you2 { - if s1 != s2 { - curvegroup::Point::zero() - } else { - self.into_group().double(p2) - } - } else { - let h = you2 - you1; - let r = s2 - s1; - let x3 = r * r - h * h * h - 2 * you1 * h * h; - let y3 = r * (you1 * h * h - x3) - s1 * h * h * h; - let z3 = h * z2; - - curvegroup::Point::new(x3, y3, z3) - } - } - } - - // Scalar multiplication with scalar represented by a bit array (little-endian convention). - // If k is the natural number represented by `bits`, then this computes p + ... + p k times. - pub fn bit_mul(self, bits: [u1; N], p: Point) -> Point { - self.into_group().bit_mul(bits, p.into_group()).into_affine() - } - - // Scalar multiplication (p + ... + p n times) - pub fn mul(self, n: Field, p: Point) -> Point { - self.into_group().mul(n, p.into_group()).into_affine() - } - - // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) - pub fn msm(self, n: [Field; N], p: [Point; N]) -> Point { - let mut out = Point::zero(); - - for i in 0..N { - out = self.add(out, self.mul(n[i], p[i])); - } - - out - } - - // Point subtraction - pub fn subtract(self, p1: Point, p2: Point) -> Point { - self.add(p1, p2.negate()) - } - - // Simplified Shallue-van de Woestijne-Ulas map-to-curve method; see . - // First determine non-square z != -1 in Field s.t. g(x) - z irreducible over Field and g(b/(z*a)) is square, - // where g(x) = x^3 + a*x + b. swu_map(c,z,.) then maps a Field element to a point on curve c. - pub fn swu_map(self, z: Field, u: Field) -> Point { - // Check whether curve is admissible - assert(self.a * self.b != 0); - - let Curve { a, b, gen: _gen } = self; - - let tv1 = safe_inverse(z * z * u * u * u * u + u * u * z); - let x1 = if tv1 == 0 { - b / (z * a) - } else { - (0 - b / a) * (1 + tv1) - }; - let gx1 = x1 * x1 * x1 + a * x1 + b; - let x2 = z * u * u * x1; - let gx2 = x2 * x2 * x2 + a * x2 + b; - let (x, y) = if is_square(gx1) { - (x1, sqrt(gx1)) - } else { - (x2, sqrt(gx2)) - }; - Point::new(x, if u.sgn0() != y.sgn0() { 0 - y } else { y }) - } - } -} - -pub mod curvegroup { - // CurveGroup representation of Weierstrass curves - // Points are represented by three-dimensional Jacobian coordinates. - // See for details. - use crate::cmp::Eq; - use crate::ec::swcurve::affine; - - // Curve specification - pub struct Curve { // Short Weierstrass curve - // Coefficients in defining equation y^2 = x^3 + axz^4 + bz^6 - pub a: Field, - pub b: Field, - // Generator as point in Cartesian coordinates - pub gen: Point, - } - // Point in three-dimensional Jacobian coordinates - pub struct Point { - pub x: Field, - pub y: Field, - pub z: Field, // z = 0 corresponds to point at infinity. - } - - impl Point { - // Point constructor - pub fn new(x: Field, y: Field, z: Field) -> Self { - Self { x, y, z } - } - - // Check if zero - pub fn is_zero(self) -> bool { - self.eq(Point::zero()) - } - - // Conversion to affine coordinates - pub fn into_affine(self) -> affine::Point { - let Self { x, y, z } = self; - - if z == 0 { - affine::Point::zero() - } else { - affine::Point::new(x / (z * z), y / (z * z * z)) - } - } - - // Additive identity - pub fn zero() -> Self { - Self { x: 0, y: 0, z: 0 } - } - - // Negation - pub fn negate(self) -> Self { - let Self { x, y, z } = self; - Self { x, y: 0 - y, z } - } - } - - impl Eq for Point { - fn eq(self, p: Self) -> bool { - let Self { x: x1, y: y1, z: z1 } = self; - let Self { x: x2, y: y2, z: z2 } = p; - - ((z1 == 0) & (z2 == 0)) - | ( - (z1 != 0) - & (z2 != 0) - & (x1 * z2 * z2 == x2 * z1 * z1) - & (y1 * z2 * z2 * z2 == y2 * z1 * z1 * z1) - ) - } - } - - impl Curve { - // Curve constructor - pub fn new(a: Field, b: Field, gen: Point) -> Curve { - // Check curve coefficients - assert(4 * a * a * a + 27 * b * b != 0); - - let curve = Curve { a, b, gen }; - - // gen should be on the curve - assert(curve.contains(curve.gen)); - - curve - } - - // Conversion to affine coordinates - pub fn into_affine(self) -> affine::Curve { - let Curve { a, b, gen } = self; - - affine::Curve { a, b, gen: gen.into_affine() } - } - - // Membership check - pub fn contains(self, p: Point) -> bool { - let Point { x, y, z } = p; - if z == 0 { - true - } else { - y * y == x * x * x + self.a * x * z * z * z * z + self.b * z * z * z * z * z * z - } - } - - // Addition - pub fn add(self, p1: Point, p2: Point) -> Point { - if p1.is_zero() { - p2 - } else if p2.is_zero() { - p1 - } else { - let Point { x: x1, y: y1, z: z1 } = p1; - let Point { x: x2, y: y2, z: z2 } = p2; - let you1 = x1 * z2 * z2; - let you2 = x2 * z1 * z1; - let s1 = y1 * z2 * z2 * z2; - let s2 = y2 * z1 * z1 * z1; - - if you1 == you2 { - if s1 != s2 { - Point::zero() - } else { - self.double(p1) - } - } else { - let h = you2 - you1; - let r = s2 - s1; - let x3 = r * r - h * h * h - 2 * you1 * h * h; - let y3 = r * (you1 * h * h - x3) - s1 * h * h * h; - let z3 = h * z1 * z2; - - Point::new(x3, y3, z3) - } - } - } - - // Point doubling - pub fn double(self, p: Point) -> Point { - let Point { x, y, z } = p; - - if p.is_zero() { - p - } else if y == 0 { - Point::zero() - } else { - let s = 4 * x * y * y; - let m = 3 * x * x + self.a * z * z * z * z; - let x0 = m * m - 2 * s; - let y0 = m * (s - x0) - 8 * y * y * y * y; - let z0 = 2 * y * z; - - Point::new(x0, y0, z0) - } - } - - // Scalar multiplication with scalar represented by a bit array (little-endian convention). - // If k is the natural number represented by `bits`, then this computes p + ... + p k times. - pub fn bit_mul(self, bits: [u1; N], p: Point) -> Point { - let mut out = Point::zero(); - - for i in 0..N { - out = self.add( - self.add(out, out), - if (bits[N - i - 1] == 0) { - Point::zero() - } else { - p - }, - ); - } - - out - } - - // Scalar multiplication (p + ... + p n times) - pub fn mul(self, n: Field, p: Point) -> Point { - // TODO: temporary workaround until issue 1354 is solved - let mut n_as_bits: [u1; 254] = [0; 254]; - let tmp: [u1; 254] = n.to_le_bits(); - for i in 0..254 { - n_as_bits[i] = tmp[i]; - } - - self.bit_mul(n_as_bits, p) - } - - // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) - pub fn msm(self, n: [Field; N], p: [Point; N]) -> Point { - let mut out = Point::zero(); - - for i in 0..N { - out = self.add(out, self.mul(n[i], p[i])); - } - - out - } - - // Point subtraction - pub fn subtract(self, p1: Point, p2: Point) -> Point { - self.add(p1, p2.negate()) - } - - // Simplified SWU map-to-curve method - pub fn swu_map(self, z: Field, u: Field) -> Point { - self.into_affine().swu_map(z, u).into_group() - } - } -} diff --git a/noir/noir-repo/noir_stdlib/src/ec/tecurve.nr b/noir/noir-repo/noir_stdlib/src/ec/tecurve.nr deleted file mode 100644 index 45a6b322ed1..00000000000 --- a/noir/noir-repo/noir_stdlib/src/ec/tecurve.nr +++ /dev/null @@ -1,419 +0,0 @@ -pub mod affine { - // Affine coordinate representation of Twisted Edwards curves - // Points are represented by two-dimensional Cartesian coordinates. - // Group operations are implemented in terms of those in CurveGroup (in this case, extended Twisted Edwards) coordinates - // for reasons of efficiency. - // See for details. - use crate::cmp::Eq; - use crate::ec::montcurve::affine::Curve as MCurve; - use crate::ec::montcurve::affine::Point as MPoint; - use crate::ec::swcurve::affine::Curve as SWCurve; - use crate::ec::swcurve::affine::Point as SWPoint; - use crate::ec::tecurve::curvegroup; - - // Curve specification - pub struct Curve { // Twisted Edwards curve - // Coefficients in defining equation ax^2 + y^2 = 1 + dx^2y^2 - pub a: Field, - pub d: Field, - // Generator as point in Cartesian coordinates - pub gen: Point, - } - // Point in Cartesian coordinates - pub struct Point { - pub x: Field, - pub y: Field, - } - - impl Point { - // Point constructor - // #[deprecated("It's recommmended to use the external noir-edwards library (https://github.com/noir-lang/noir-edwards)")] - pub fn new(x: Field, y: Field) -> Self { - Self { x, y } - } - - // Check if zero - pub fn is_zero(self) -> bool { - self.eq(Point::zero()) - } - - // Conversion to CurveGroup coordinates - pub fn into_group(self) -> curvegroup::Point { - let Self { x, y } = self; - - curvegroup::Point::new(x, y, x * y, 1) - } - - // Additive identity - pub fn zero() -> Self { - Point::new(0, 1) - } - - // Negation - pub fn negate(self) -> Self { - let Self { x, y } = self; - Point::new(0 - x, y) - } - - // Map into prime-order subgroup of equivalent Montgomery curve - pub fn into_montcurve(self) -> MPoint { - if self.is_zero() { - MPoint::zero() - } else { - let Self { x, y } = self; - let x0 = (1 + y) / (1 - y); - let y0 = (1 + y) / (x * (1 - y)); - - MPoint::new(x0, y0) - } - } - } - - impl Eq for Point { - fn eq(self, p: Self) -> bool { - let Self { x: x1, y: y1 } = self; - let Self { x: x2, y: y2 } = p; - - (x1 == x2) & (y1 == y2) - } - } - - impl Curve { - // Curve constructor - pub fn new(a: Field, d: Field, gen: Point) -> Curve { - // Check curve coefficients - assert(a * d * (a - d) != 0); - - let curve = Curve { a, d, gen }; - - // gen should be on the curve - assert(curve.contains(curve.gen)); - - curve - } - - // Conversion to CurveGroup coordinates - pub fn into_group(self) -> curvegroup::Curve { - let Curve { a, d, gen } = self; - - curvegroup::Curve { a, d, gen: gen.into_group() } - } - - // Membership check - pub fn contains(self, p: Point) -> bool { - let Point { x, y } = p; - self.a * x * x + y * y == 1 + self.d * x * x * y * y - } - - // Point addition, implemented in terms of mixed addition for reasons of efficiency - pub fn add(self, p1: Point, p2: Point) -> Point { - self.mixed_add(p1, p2.into_group()).into_affine() - } - - // Mixed point addition, i.e. first argument in affine, second in CurveGroup coordinates. - pub fn mixed_add(self, p1: Point, p2: curvegroup::Point) -> curvegroup::Point { - let Point { x: x1, y: y1 } = p1; - let curvegroup::Point { x: x2, y: y2, t: t2, z: z2 } = p2; - - let a = x1 * x2; - let b = y1 * y2; - let c = self.d * x1 * y1 * t2; - let e = (x1 + y1) * (x2 + y2) - a - b; - let f = z2 - c; - let g = z2 + c; - let h = b - self.a * a; - - let x = e * f; - let y = g * h; - let t = e * h; - let z = f * g; - - curvegroup::Point::new(x, y, t, z) - } - - // Scalar multiplication with scalar represented by a bit array (little-endian convention). - // If k is the natural number represented by `bits`, then this computes p + ... + p k times. - pub fn bit_mul(self, bits: [u1; N], p: Point) -> Point { - self.into_group().bit_mul(bits, p.into_group()).into_affine() - } - - // Scalar multiplication (p + ... + p n times) - pub fn mul(self, n: Field, p: Point) -> Point { - self.into_group().mul(n, p.into_group()).into_affine() - } - - // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) - pub fn msm(self, n: [Field; N], p: [Point; N]) -> Point { - let mut out = Point::zero(); - - for i in 0..N { - out = self.add(out, self.mul(n[i], p[i])); - } - - out - } - - // Point subtraction - pub fn subtract(self, p1: Point, p2: Point) -> Point { - self.add(p1, p2.negate()) - } - - // Conversion to equivalent Montgomery curve - pub fn into_montcurve(self) -> MCurve { - let j = 2 * (self.a + self.d) / (self.a - self.d); - let k = 4 / (self.a - self.d); - let gen_montcurve = self.gen.into_montcurve(); - - MCurve::new(j, k, gen_montcurve) - } - - // Conversion to equivalent Short Weierstrass curve - pub fn into_swcurve(self) -> SWCurve { - self.into_montcurve().into_swcurve() - } - - // Point mapping into equivalent Short Weierstrass curve - pub fn map_into_swcurve(self, p: Point) -> SWPoint { - self.into_montcurve().map_into_swcurve(p.into_montcurve()) - } - - // Point mapping from equivalent Short Weierstrass curve - pub fn map_from_swcurve(self, p: SWPoint) -> Point { - self.into_montcurve().map_from_swcurve(p).into_tecurve() - } - - // Elligator 2 map-to-curve method (via rational map) - pub fn elligator2_map(self, u: Field) -> Point { - self.into_montcurve().elligator2_map(u).into_tecurve() - } - - // Simplified SWU map-to-curve method (via rational map) - pub fn swu_map(self, z: Field, u: Field) -> Point { - self.into_montcurve().swu_map(z, u).into_tecurve() - } - } -} -pub mod curvegroup { - // CurveGroup coordinate representation of Twisted Edwards curves - // Points are represented by four-dimensional projective coordinates, viz. extended Twisted Edwards coordinates. - // See section 3 of for details. - use crate::cmp::Eq; - use crate::ec::montcurve::curvegroup::Curve as MCurve; - use crate::ec::montcurve::curvegroup::Point as MPoint; - use crate::ec::swcurve::curvegroup::Curve as SWCurve; - use crate::ec::swcurve::curvegroup::Point as SWPoint; - use crate::ec::tecurve::affine; - - // Curve specification - pub struct Curve { // Twisted Edwards curve - // Coefficients in defining equation a(x^2 + y^2)z^2 = z^4 + dx^2y^2 - pub a: Field, - pub d: Field, - // Generator as point in projective coordinates - pub gen: Point, - } - // Point in extended twisted Edwards coordinates - pub struct Point { - pub x: Field, - pub y: Field, - pub t: Field, - pub z: Field, - } - - impl Point { - // Point constructor - pub fn new(x: Field, y: Field, t: Field, z: Field) -> Self { - Self { x, y, t, z } - } - - // Check if zero - pub fn is_zero(self) -> bool { - let Self { x, y, t, z } = self; - (x == 0) & (y == z) & (y != 0) & (t == 0) - } - - // Conversion to affine coordinates - pub fn into_affine(self) -> affine::Point { - let Self { x, y, t: _t, z } = self; - - affine::Point::new(x / z, y / z) - } - - // Additive identity - pub fn zero() -> Self { - Point::new(0, 1, 0, 1) - } - - // Negation - pub fn negate(self) -> Self { - let Self { x, y, t, z } = self; - - Point::new(0 - x, y, 0 - t, z) - } - - // Map into prime-order subgroup of equivalent Montgomery curve - pub fn into_montcurve(self) -> MPoint { - self.into_affine().into_montcurve().into_group() - } - } - - impl Eq for Point { - fn eq(self, p: Self) -> bool { - let Self { x: x1, y: y1, t: _t1, z: z1 } = self; - let Self { x: x2, y: y2, t: _t2, z: z2 } = p; - - (x1 * z2 == x2 * z1) & (y1 * z2 == y2 * z1) - } - } - - impl Curve { - // Curve constructor - pub fn new(a: Field, d: Field, gen: Point) -> Curve { - // Check curve coefficients - assert(a * d * (a - d) != 0); - - let curve = Curve { a, d, gen }; - - // gen should be on the curve - assert(curve.contains(curve.gen)); - - curve - } - - // Conversion to affine coordinates - pub fn into_affine(self) -> affine::Curve { - let Curve { a, d, gen } = self; - - affine::Curve { a, d, gen: gen.into_affine() } - } - - // Membership check - pub fn contains(self, p: Point) -> bool { - let Point { x, y, t, z } = p; - - (z != 0) - & (z * t == x * y) - & (z * z * (self.a * x * x + y * y) == z * z * z * z + self.d * x * x * y * y) - } - - // Point addition - pub fn add(self, p1: Point, p2: Point) -> Point { - let Point { x: x1, y: y1, t: t1, z: z1 } = p1; - let Point { x: x2, y: y2, t: t2, z: z2 } = p2; - - let a = x1 * x2; - let b = y1 * y2; - let c = self.d * t1 * t2; - let d = z1 * z2; - let e = (x1 + y1) * (x2 + y2) - a - b; - let f = d - c; - let g = d + c; - let h = b - self.a * a; - - let x = e * f; - let y = g * h; - let t = e * h; - let z = f * g; - - Point::new(x, y, t, z) - } - - // Point doubling, cf. section 3.3 - pub fn double(self, p: Point) -> Point { - let Point { x, y, t: _t, z } = p; - - let a = x * x; - let b = y * y; - let c = 2 * z * z; - let d = self.a * a; - let e = (x + y) * (x + y) - a - b; - let g = d + b; - let f = g - c; - let h = d - b; - - let x0 = e * f; - let y0 = g * h; - let t0 = e * h; - let z0 = f * g; - - Point::new(x0, y0, t0, z0) - } - - // Scalar multiplication with scalar represented by a bit array (little-endian convention). - // If k is the natural number represented by `bits`, then this computes p + ... + p k times. - pub fn bit_mul(self, bits: [u1; N], p: Point) -> Point { - let mut out = Point::zero(); - - for i in 0..N { - out = self.add( - self.add(out, out), - if (bits[N - i - 1] == 0) { - Point::zero() - } else { - p - }, - ); - } - - out - } - - // Scalar multiplication (p + ... + p n times) - pub fn mul(self, n: Field, p: Point) -> Point { - // TODO: temporary workaround until issue 1354 is solved - let mut n_as_bits: [u1; 254] = [0; 254]; - let tmp: [u1; 254] = n.to_le_bits(); - for i in 0..254 { - n_as_bits[i] = tmp[i]; - } - - self.bit_mul(n_as_bits, p) - } - - // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) - pub fn msm(self, n: [Field; N], p: [Point; N]) -> Point { - let mut out = Point::zero(); - - for i in 0..N { - out = self.add(out, self.mul(n[i], p[i])); - } - - out - } - - // Point subtraction - pub fn subtract(self, p1: Point, p2: Point) -> Point { - self.add(p1, p2.negate()) - } - - // Conversion to equivalent Montgomery curve - pub fn into_montcurve(self) -> MCurve { - self.into_affine().into_montcurve().into_group() - } - - // Conversion to equivalent Short Weierstrass curve - pub fn into_swcurve(self) -> SWCurve { - self.into_montcurve().into_swcurve() - } - - // Point mapping into equivalent short Weierstrass curve - pub fn map_into_swcurve(self, p: Point) -> SWPoint { - self.into_montcurve().map_into_swcurve(p.into_montcurve()) - } - - // Point mapping from equivalent short Weierstrass curve - pub fn map_from_swcurve(self, p: SWPoint) -> Point { - self.into_montcurve().map_from_swcurve(p).into_tecurve() - } - - // Elligator 2 map-to-curve method (via rational maps) - pub fn elligator2_map(self, u: Field) -> Point { - self.into_montcurve().elligator2_map(u).into_tecurve() - } - - // Simplified SWU map-to-curve method (via rational map) - pub fn swu_map(self, z: Field, u: Field) -> Point { - self.into_montcurve().swu_map(z, u).into_tecurve() - } - } -} diff --git a/noir/noir-repo/noir_stdlib/src/eddsa.nr b/noir/noir-repo/noir_stdlib/src/eddsa.nr deleted file mode 100644 index c049b7abbb5..00000000000 --- a/noir/noir-repo/noir_stdlib/src/eddsa.nr +++ /dev/null @@ -1,76 +0,0 @@ -use crate::default::Default; -use crate::ec::consts::te::baby_jubjub; -use crate::ec::tecurve::affine::Point as TEPoint; -use crate::hash::Hasher; -use crate::hash::poseidon::PoseidonHasher; - -// Returns true if signature is valid -pub fn eddsa_poseidon_verify( - pub_key_x: Field, - pub_key_y: Field, - signature_s: Field, - signature_r8_x: Field, - signature_r8_y: Field, - message: Field, -) -> bool { - eddsa_verify::( - pub_key_x, - pub_key_y, - signature_s, - signature_r8_x, - signature_r8_y, - message, - ) -} - -pub fn eddsa_verify( - pub_key_x: Field, - pub_key_y: Field, - signature_s: Field, - signature_r8_x: Field, - signature_r8_y: Field, - message: Field, -) -> bool -where - H: Hasher + Default, -{ - // Verifies by testing: - // S * B8 = R8 + H(R8, A, m) * A8 - let bjj = baby_jubjub(); - - let pub_key = TEPoint::new(pub_key_x, pub_key_y); - assert(bjj.curve.contains(pub_key)); - - let signature_r8 = TEPoint::new(signature_r8_x, signature_r8_y); - assert(bjj.curve.contains(signature_r8)); - // Ensure S < Subgroup Order - assert(signature_s.lt(bjj.suborder)); - // Calculate the h = H(R, A, msg) - let mut hasher = H::default(); - hasher.write(signature_r8_x); - hasher.write(signature_r8_y); - hasher.write(pub_key_x); - hasher.write(pub_key_y); - hasher.write(message); - let hash: Field = hasher.finish(); - // Calculate second part of the right side: right2 = h*8*A - // Multiply by 8 by doubling 3 times. This also ensures that the result is in the subgroup. - let pub_key_mul_2 = bjj.curve.add(pub_key, pub_key); - let pub_key_mul_4 = bjj.curve.add(pub_key_mul_2, pub_key_mul_2); - let pub_key_mul_8 = bjj.curve.add(pub_key_mul_4, pub_key_mul_4); - // We check that A8 is not zero. - assert(!pub_key_mul_8.is_zero()); - // Compute the right side: R8 + h * A8 - let right = bjj.curve.add(signature_r8, bjj.curve.mul(hash, pub_key_mul_8)); - // Calculate left side of equation left = S * B8 - let left = bjj.curve.mul(signature_s, bjj.base8); - - left.eq(right) -} - -// Returns the public key of the given secret key as (pub_key_x, pub_key_y) -pub fn eddsa_to_pub(secret: Field) -> (Field, Field) { - let bjj = baby_jubjub(); - let pub_key = bjj.curve.mul(secret, bjj.curve.gen); - (pub_key.x, pub_key.y) -} diff --git a/noir/noir-repo/noir_stdlib/src/hash/sha256.nr b/noir/noir-repo/noir_stdlib/src/hash/sha256.nr index d55044907ac..b9a2b02c9d9 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/sha256.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/sha256.nr @@ -4,27 +4,27 @@ use crate::runtime::is_unconstrained; // 32 bytes. // A message block is up to 64 bytes taken from the input. -global BLOCK_SIZE = 64; +global BLOCK_SIZE: u32 = 64; // The first index in the block where the 8 byte message size will be written. -global MSG_SIZE_PTR = 56; +global MSG_SIZE_PTR: u32 = 56; // Size of the message block when packed as 4-byte integer array. -global INT_BLOCK_SIZE = 16; +global INT_BLOCK_SIZE: u32 = 16; // A `u32` integer consists of 4 bytes. -global INT_SIZE = 4; +global INT_SIZE: u32 = 4; // Index of the integer in the `INT_BLOCK` where the length is written. -global INT_SIZE_PTR = MSG_SIZE_PTR / INT_SIZE; +global INT_SIZE_PTR: u32 = MSG_SIZE_PTR / INT_SIZE; // Magic numbers for bit shifting. // Works with actual bit shifting as well as the compiler turns them into * and / // but circuit execution appears to be 10% faster this way. -global TWO_POW_8 = 256; -global TWO_POW_16 = TWO_POW_8 * 256; -global TWO_POW_24 = TWO_POW_16 * 256; -global TWO_POW_32 = TWO_POW_24 as u64 * 256; +global TWO_POW_8: u32 = 256; +global TWO_POW_16: u32 = TWO_POW_8 * 256; +global TWO_POW_24: u32 = TWO_POW_16 * 256; +global TWO_POW_32: u64 = TWO_POW_24 as u64 * 256; // Index of a byte in a 64 byte block; ie. 0..=63 type BLOCK_BYTE_PTR = u32; diff --git a/noir/noir-repo/noir_stdlib/src/lib.nr b/noir/noir-repo/noir_stdlib/src/lib.nr index 91a1980fe70..8e9dc13c13d 100644 --- a/noir/noir-repo/noir_stdlib/src/lib.nr +++ b/noir/noir-repo/noir_stdlib/src/lib.nr @@ -6,12 +6,10 @@ pub mod merkle; pub mod schnorr; pub mod ecdsa_secp256k1; pub mod ecdsa_secp256r1; -pub mod eddsa; pub mod embedded_curve_ops; pub mod sha256; pub mod sha512; pub mod field; -pub mod ec; pub mod collections; pub mod compat; pub mod convert; diff --git a/noir/noir-repo/noir_stdlib/src/mem.nr b/noir/noir-repo/noir_stdlib/src/mem.nr index 0d47a21b50d..23125867eab 100644 --- a/noir/noir-repo/noir_stdlib/src/mem.nr +++ b/noir/noir-repo/noir_stdlib/src/mem.nr @@ -15,3 +15,17 @@ pub fn zeroed() -> T {} /// that it is equal to the previous. #[builtin(checked_transmute)] pub fn checked_transmute(value: T) -> U {} + +/// Returns the internal reference count of an array value in unconstrained code. +/// +/// Arrays only have reference count in unconstrained code - using this anywhere +/// else will return zero. +#[builtin(array_refcount)] +pub fn array_refcount(array: [T; N]) -> u32 {} + +/// Returns the internal reference count of a slice value in unconstrained code. +/// +/// Slices only have reference count in unconstrained code - using this anywhere +/// else will return zero. +#[builtin(slice_refcount)] +pub fn slice_refcount(slice: [T]) -> u32 {} diff --git a/noir/noir-repo/test_programs/benchmarks/bench_eddsa_poseidon/Nargo.toml b/noir/noir-repo/test_programs/benchmarks/bench_eddsa_poseidon/Nargo.toml index bc2a779f7b2..6c754f1d107 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_eddsa_poseidon/Nargo.toml +++ b/noir/noir-repo/test_programs/benchmarks/bench_eddsa_poseidon/Nargo.toml @@ -5,3 +5,4 @@ type = "bin" authors = [""] [dependencies] +ec = { tag = "v0.1.2", git = "https://github.com/noir-lang/ec" } diff --git a/noir/noir-repo/test_programs/benchmarks/bench_eddsa_poseidon/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_eddsa_poseidon/src/main.nr index cb853e48c30..c4a1d4b51f5 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_eddsa_poseidon/src/main.nr +++ b/noir/noir-repo/test_programs/benchmarks/bench_eddsa_poseidon/src/main.nr @@ -1,4 +1,10 @@ -use std::eddsa::eddsa_poseidon_verify; +use std::default::Default; +use std::hash::Hasher; +use std::hash::poseidon::PoseidonHasher; + +use ec::consts::te::baby_jubjub; +use ec::tecurve::affine::Point as TEPoint; + fn main( msg: pub Field, @@ -6,7 +12,52 @@ fn main( pub_key_y: Field, r8_x: Field, r8_y: Field, - s: Field + s: Field, ) -> pub bool { - eddsa_poseidon_verify(pub_key_x, pub_key_y, s, r8_x, r8_y, msg) + eddsa_verify::(pub_key_x, pub_key_y, s, r8_x, r8_y, msg) +} + +pub fn eddsa_verify( + pub_key_x: Field, + pub_key_y: Field, + signature_s: Field, + signature_r8_x: Field, + signature_r8_y: Field, + message: Field, +) -> bool +where + H: Hasher + Default, +{ + // Verifies by testing: + // S * B8 = R8 + H(R8, A, m) * A8 + let bjj = baby_jubjub(); + + let pub_key = TEPoint::new(pub_key_x, pub_key_y); + assert(bjj.curve.contains(pub_key)); + + let signature_r8 = TEPoint::new(signature_r8_x, signature_r8_y); + assert(bjj.curve.contains(signature_r8)); + // Ensure S < Subgroup Order + assert(signature_s.lt(bjj.suborder)); + // Calculate the h = H(R, A, msg) + let mut hasher = H::default(); + hasher.write(signature_r8_x); + hasher.write(signature_r8_y); + hasher.write(pub_key_x); + hasher.write(pub_key_y); + hasher.write(message); + let hash: Field = hasher.finish(); + // Calculate second part of the right side: right2 = h*8*A + // Multiply by 8 by doubling 3 times. This also ensures that the result is in the subgroup. + let pub_key_mul_2 = bjj.curve.add(pub_key, pub_key); + let pub_key_mul_4 = bjj.curve.add(pub_key_mul_2, pub_key_mul_2); + let pub_key_mul_8 = bjj.curve.add(pub_key_mul_4, pub_key_mul_4); + // We check that A8 is not zero. + assert(!pub_key_mul_8.is_zero()); + // Compute the right side: R8 + h * A8 + let right = bjj.curve.add(signature_r8, bjj.curve.mul(hash, pub_key_mul_8)); + // Calculate left side of equation left = S * B8 + let left = bjj.curve.mul(signature_s, bjj.base8); + + left.eq(right) } diff --git a/noir/noir-repo/test_programs/benchmarks/bench_poseidon2_hash_100/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_poseidon2_hash_100/src/main.nr index 39c714e524f..66a785f446a 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_poseidon2_hash_100/src/main.nr +++ b/noir/noir-repo/test_programs/benchmarks/bench_poseidon2_hash_100/src/main.nr @@ -1,6 +1,6 @@ use std::hash::poseidon2; -global SIZE = 100; +global SIZE: u32 = 100; fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { let mut results: [Field; SIZE] = [0; SIZE]; diff --git a/noir/noir-repo/test_programs/benchmarks/bench_poseidon2_hash_30/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_poseidon2_hash_30/src/main.nr index d1251a4c853..2e72ebc3519 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_poseidon2_hash_30/src/main.nr +++ b/noir/noir-repo/test_programs/benchmarks/bench_poseidon2_hash_30/src/main.nr @@ -1,6 +1,6 @@ use std::hash::poseidon2; -global SIZE = 30; +global SIZE: u32 = 30; fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { let mut results: [Field; SIZE] = [0; SIZE]; diff --git a/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_100/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_100/src/main.nr index 1c9bbfe61bf..75d853941e5 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_100/src/main.nr +++ b/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_100/src/main.nr @@ -1,6 +1,6 @@ use std::hash; -global SIZE = 100; +global SIZE: u32 = 100; fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { let mut results: [Field; SIZE] = [0; SIZE]; diff --git a/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_30/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_30/src/main.nr index 3edb47e9f72..d4f357e11f9 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_30/src/main.nr +++ b/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_30/src/main.nr @@ -1,6 +1,6 @@ use std::hash; -global SIZE = 30; +global SIZE: u32 = 30; fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { let mut results: [Field; SIZE] = [0; SIZE]; diff --git a/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr index 1c9bbfe61bf..75d853941e5 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr +++ b/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr @@ -1,6 +1,6 @@ use std::hash; -global SIZE = 100; +global SIZE: u32 = 100; fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { let mut results: [Field; SIZE] = [0; SIZE]; diff --git a/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr index 3edb47e9f72..d4f357e11f9 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr +++ b/noir/noir-repo/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr @@ -1,6 +1,6 @@ use std::hash; -global SIZE = 30; +global SIZE: u32 = 30; fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { let mut results: [Field; SIZE] = [0; SIZE]; diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr index 6df856a83fc..6e4bfc27c8f 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr +++ b/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr @@ -1,4 +1,4 @@ -global SIZE = 100; +global SIZE: u32 = 100; fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { let mut results: [[u8; 32]; SIZE] = [[0; 32]; SIZE]; diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr index 220c1cfbbed..0a4288114e3 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr +++ b/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr @@ -1,4 +1,4 @@ -global SIZE = 30; +global SIZE: u32 = 30; fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { let mut results: [[u8; 32]; SIZE] = [[0; 32]; SIZE]; diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr index 17129275371..c47bdc2a561 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr +++ b/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr @@ -1,6 +1,6 @@ // Input size long enough that we have to compress a few times // and then pad the last block out. -global INPUT_SIZE = 2 * 64 + 60; +global INPUT_SIZE: u32 = 2 * 64 + 60; fn main(input: [u8; INPUT_SIZE]) -> pub [u8; 32] { std::hash::sha256(input) diff --git a/noir/noir-repo/test_programs/compile_success_empty/assert_constant/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/assert_constant/src/main.nr index 978f668f611..42d66f88137 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/assert_constant/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/assert_constant/src/main.nr @@ -1,13 +1,13 @@ use std::static_assert; -global GLOBAL_ONE = 1; -global GLOBAL_TWO = 2; -global GLOBAL_THREE = GLOBAL_ONE + GLOBAL_TWO; +global GLOBAL_ONE: Field = 1; +global GLOBAL_TWO: Field = 2; +global GLOBAL_THREE: Field = GLOBAL_ONE + GLOBAL_TWO; // contents known at compile time // length known at compile time -global GLOBAL_ARRAY_PAIR = [GLOBAL_ONE, GLOBAL_TWO]; -global GLOBAL_SLICE_PAIR = &[GLOBAL_ONE, GLOBAL_TWO]; +global GLOBAL_ARRAY_PAIR: [Field; 2] = [GLOBAL_ONE, GLOBAL_TWO]; +global GLOBAL_SLICE_PAIR: [Field] = &[GLOBAL_ONE, GLOBAL_TWO]; struct Foo { field: Field, diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_globals_regression/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_globals_regression/src/main.nr index 86b85fbc00a..45afef6d831 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_globals_regression/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_globals_regression/src/main.nr @@ -1,4 +1,4 @@ -comptime mut global COUNTER = 0; +comptime mut global COUNTER: Field = 0; fn main() { comptime { increment() }; diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_module/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_module/src/main.nr index 8114fa34555..20fd8053fbe 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_module/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_module/src/main.nr @@ -24,7 +24,7 @@ mod yet_another_module { #[outer_attribute_separate_module] mod separate_module; -comptime mut global counter = 0; +comptime mut global counter: u32 = 0; comptime fn increment_counter() { counter += 1; diff --git a/noir/noir-repo/test_programs/compile_success_empty/ec_baby_jubjub/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/ec_baby_jubjub/Nargo.toml deleted file mode 100644 index fdb0df17112..00000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/ec_baby_jubjub/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "ec_baby_jubjub" -description = "Baby Jubjub sanity checks" -type = "bin" -authors = [""] - -[dependencies] diff --git a/noir/noir-repo/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr deleted file mode 100644 index caaa51d84f0..00000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr +++ /dev/null @@ -1,210 +0,0 @@ -// Tests may be checked against https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/tree/main/poc -use std::ec::tecurve::affine::Curve as AffineCurve; -use std::ec::tecurve::affine::Point as Gaffine; -use std::ec::tecurve::curvegroup::Point as G; - -use std::ec::swcurve::affine::Point as SWGaffine; -use std::ec::swcurve::curvegroup::Point as SWG; - -use std::compat; -use std::ec::montcurve::affine::Point as MGaffine; -use std::ec::montcurve::curvegroup::Point as MG; - -fn main() { - // This test only makes sense if Field is the right prime field. - if compat::is_bn254() { - // Define Baby Jubjub (ERC-2494) parameters in affine representation - let bjj_affine = AffineCurve::new( - 168700, - 168696, - Gaffine::new( - 995203441582195749578291179787384436505546430278305826713579947235728471134, - 5472060717959818805561601436314318772137091100104008585924551046643952123905, - ), - ); - // Test addition - let p1_affine = Gaffine::new( - 17777552123799933955779906779655732241715742912184938656739573121738514868268, - 2626589144620713026669568689430873010625803728049924121243784502389097019475, - ); - let p2_affine = Gaffine::new( - 16540640123574156134436876038791482806971768689494387082833631921987005038935, - 20819045374670962167435360035096875258406992893633759881276124905556507972311, - ); - - let p3_affine = bjj_affine.add(p1_affine, p2_affine); - assert(p3_affine.eq(Gaffine::new( - 7916061937171219682591368294088513039687205273691143098332585753343424131937, - 14035240266687799601661095864649209771790948434046947201833777492504781204499, - ))); - // Test scalar multiplication - let p4_affine = bjj_affine.mul(2, p1_affine); - assert(p4_affine.eq(Gaffine::new( - 6890855772600357754907169075114257697580319025794532037257385534741338397365, - 4338620300185947561074059802482547481416142213883829469920100239455078257889, - ))); - assert(p4_affine.eq(bjj_affine.bit_mul([0, 1], p1_affine))); - // Test subtraction - let p5_affine = bjj_affine.subtract(p3_affine, p3_affine); - assert(p5_affine.eq(Gaffine::zero())); - // Check that these points are on the curve - assert( - bjj_affine.contains(bjj_affine.gen) - & bjj_affine.contains(p1_affine) - & bjj_affine.contains(p2_affine) - & bjj_affine.contains(p3_affine) - & bjj_affine.contains(p4_affine) - & bjj_affine.contains(p5_affine), - ); - // Test CurveGroup equivalents - let bjj = bjj_affine.into_group(); // Baby Jubjub - let p1 = p1_affine.into_group(); - let p2 = p2_affine.into_group(); - let p3 = p3_affine.into_group(); - let p4 = p4_affine.into_group(); - let p5 = p5_affine.into_group(); - // Test addition - assert(p3.eq(bjj.add(p1, p2))); - // Test scalar multiplication - assert(p4.eq(bjj.mul(2, p1))); - assert(p4.eq(bjj.bit_mul([0, 1], p1))); - // Test subtraction - assert(G::zero().eq(bjj.subtract(p3, p3))); - assert(p5.eq(G::zero())); - // Check that these points are on the curve - assert( - bjj.contains(bjj.gen) - & bjj.contains(p1) - & bjj.contains(p2) - & bjj.contains(p3) - & bjj.contains(p4) - & bjj.contains(p5), - ); - // Test SWCurve equivalents of the above - // First the affine representation - let bjj_swcurve_affine = bjj_affine.into_swcurve(); - - let p1_swcurve_affine = bjj_affine.map_into_swcurve(p1_affine); - let p2_swcurve_affine = bjj_affine.map_into_swcurve(p2_affine); - let p3_swcurve_affine = bjj_affine.map_into_swcurve(p3_affine); - let p4_swcurve_affine = bjj_affine.map_into_swcurve(p4_affine); - let p5_swcurve_affine = bjj_affine.map_into_swcurve(p5_affine); - // Addition - assert(p3_swcurve_affine.eq(bjj_swcurve_affine.add(p1_swcurve_affine, p2_swcurve_affine))); - // Doubling - assert(p4_swcurve_affine.eq(bjj_swcurve_affine.mul(2, p1_swcurve_affine))); - assert(p4_swcurve_affine.eq(bjj_swcurve_affine.bit_mul([0, 1], p1_swcurve_affine))); - // Subtraction - assert(SWGaffine::zero().eq(bjj_swcurve_affine.subtract( - p3_swcurve_affine, - p3_swcurve_affine, - ))); - assert(p5_swcurve_affine.eq(SWGaffine::zero())); - // Check that these points are on the curve - assert( - bjj_swcurve_affine.contains(bjj_swcurve_affine.gen) - & bjj_swcurve_affine.contains(p1_swcurve_affine) - & bjj_swcurve_affine.contains(p2_swcurve_affine) - & bjj_swcurve_affine.contains(p3_swcurve_affine) - & bjj_swcurve_affine.contains(p4_swcurve_affine) - & bjj_swcurve_affine.contains(p5_swcurve_affine), - ); - // Then the CurveGroup representation - let bjj_swcurve = bjj.into_swcurve(); - - let p1_swcurve = bjj.map_into_swcurve(p1); - let p2_swcurve = bjj.map_into_swcurve(p2); - let p3_swcurve = bjj.map_into_swcurve(p3); - let p4_swcurve = bjj.map_into_swcurve(p4); - let p5_swcurve = bjj.map_into_swcurve(p5); - // Addition - assert(p3_swcurve.eq(bjj_swcurve.add(p1_swcurve, p2_swcurve))); - // Doubling - assert(p4_swcurve.eq(bjj_swcurve.mul(2, p1_swcurve))); - assert(p4_swcurve.eq(bjj_swcurve.bit_mul([0, 1], p1_swcurve))); - // Subtraction - assert(SWG::zero().eq(bjj_swcurve.subtract(p3_swcurve, p3_swcurve))); - assert(p5_swcurve.eq(SWG::zero())); - // Check that these points are on the curve - assert( - bjj_swcurve.contains(bjj_swcurve.gen) - & bjj_swcurve.contains(p1_swcurve) - & bjj_swcurve.contains(p2_swcurve) - & bjj_swcurve.contains(p3_swcurve) - & bjj_swcurve.contains(p4_swcurve) - & bjj_swcurve.contains(p5_swcurve), - ); - // Test MontCurve conversions - // First the affine representation - let bjj_montcurve_affine = bjj_affine.into_montcurve(); - - let p1_montcurve_affine = p1_affine.into_montcurve(); - let p2_montcurve_affine = p2_affine.into_montcurve(); - let p3_montcurve_affine = p3_affine.into_montcurve(); - let p4_montcurve_affine = p4_affine.into_montcurve(); - let p5_montcurve_affine = p5_affine.into_montcurve(); - // Addition - assert(p3_montcurve_affine.eq(bjj_montcurve_affine.add( - p1_montcurve_affine, - p2_montcurve_affine, - ))); - // Doubling - assert(p4_montcurve_affine.eq(bjj_montcurve_affine.mul(2, p1_montcurve_affine))); - assert(p4_montcurve_affine.eq(bjj_montcurve_affine.bit_mul([0, 1], p1_montcurve_affine))); - // Subtraction - assert(MGaffine::zero().eq(bjj_montcurve_affine.subtract( - p3_montcurve_affine, - p3_montcurve_affine, - ))); - assert(p5_montcurve_affine.eq(MGaffine::zero())); - // Check that these points are on the curve - assert( - bjj_montcurve_affine.contains(bjj_montcurve_affine.gen) - & bjj_montcurve_affine.contains(p1_montcurve_affine) - & bjj_montcurve_affine.contains(p2_montcurve_affine) - & bjj_montcurve_affine.contains(p3_montcurve_affine) - & bjj_montcurve_affine.contains(p4_montcurve_affine) - & bjj_montcurve_affine.contains(p5_montcurve_affine), - ); - // Then the CurveGroup representation - let bjj_montcurve = bjj.into_montcurve(); - - let p1_montcurve = p1_montcurve_affine.into_group(); - let p2_montcurve = p2_montcurve_affine.into_group(); - let p3_montcurve = p3_montcurve_affine.into_group(); - let p4_montcurve = p4_montcurve_affine.into_group(); - let p5_montcurve = p5_montcurve_affine.into_group(); - // Addition - assert(p3_montcurve.eq(bjj_montcurve.add(p1_montcurve, p2_montcurve))); - // Doubling - assert(p4_montcurve.eq(bjj_montcurve.mul(2, p1_montcurve))); - assert(p4_montcurve.eq(bjj_montcurve.bit_mul([0, 1], p1_montcurve))); - // Subtraction - assert(MG::zero().eq(bjj_montcurve.subtract(p3_montcurve, p3_montcurve))); - assert(p5_montcurve.eq(MG::zero())); - // Check that these points are on the curve - assert( - bjj_montcurve.contains(bjj_montcurve.gen) - & bjj_montcurve.contains(p1_montcurve) - & bjj_montcurve.contains(p2_montcurve) - & bjj_montcurve.contains(p3_montcurve) - & bjj_montcurve.contains(p4_montcurve) - & bjj_montcurve.contains(p5_montcurve), - ); - // Elligator 2 map-to-curve - let ell2_pt_map = bjj_affine.elligator2_map(27); - - assert(ell2_pt_map.eq(MGaffine::new( - 7972459279704486422145701269802978968072470631857513331988813812334797879121, - 8142420778878030219043334189293412482212146646099536952861607542822144507872, - ) - .into_tecurve())); - // SWU map-to-curve - let swu_pt_map = bjj_affine.swu_map(5, 27); - - assert(swu_pt_map.eq(bjj_affine.map_from_swcurve(SWGaffine::new( - 2162719247815120009132293839392097468339661471129795280520343931405114293888, - 5341392251743377373758788728206293080122949448990104760111875914082289313973, - )))); - } -} diff --git a/noir/noir-repo/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr index c2eeeb37395..978a7fdf66b 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr @@ -1,5 +1,5 @@ // Regression that a global of the same name does not trigger a duplicate definition error -global N = 1000; +global N: u32 = 1000; fn main() { let a = id([1, 2]); diff --git a/noir/noir-repo/test_programs/compile_success_empty/parenthesized_expression_in_array_length/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/parenthesized_expression_in_array_length/src/main.nr index b596d331e7f..d4479ec933b 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/parenthesized_expression_in_array_length/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/parenthesized_expression_in_array_length/src/main.nr @@ -1,5 +1,5 @@ -global N = 100; -global BLOCK_SIZE = 10; +global N: u32 = 100; +global BLOCK_SIZE: u32 = 10; fn main() { let _: [Field; 110] = [0; ((N + BLOCK_SIZE) * BLOCK_SIZE) / BLOCK_SIZE]; diff --git a/noir/noir-repo/test_programs/compile_success_empty/raw_string/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/raw_string/src/main.nr index ad8dfe82ae5..6bed1cfecc9 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/raw_string/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/raw_string/src/main.nr @@ -1,4 +1,4 @@ -global D = r#####"Hello "world""#####; +global D: str<13> = r#####"Hello "world""#####; fn main() { let a = "Hello \"world\""; diff --git a/noir/noir-repo/test_programs/compile_success_empty/regression_2099/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/regression_2099/Nargo.toml index 6b9f9a24038..69fd4caabed 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/regression_2099/Nargo.toml +++ b/noir/noir-repo/test_programs/compile_success_empty/regression_2099/Nargo.toml @@ -2,4 +2,6 @@ name = "regression_2099" type = "bin" authors = [""] + [dependencies] +ec = { tag = "v0.1.2", git = "https://github.com/noir-lang/ec" } diff --git a/noir/noir-repo/test_programs/compile_success_empty/regression_2099/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/regression_2099/src/main.nr index 3fe3cdaf39a..3a8b9092792 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/regression_2099/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/regression_2099/src/main.nr @@ -1,5 +1,5 @@ -use std::ec::tecurve::affine::Curve as AffineCurve; -use std::ec::tecurve::affine::Point as Gaffine; +use ec::tecurve::affine::Curve as AffineCurve; +use ec::tecurve::affine::Point as Gaffine; fn main() { // Define Baby Jubjub (ERC-2494) parameters in affine representation diff --git a/noir/noir-repo/test_programs/compile_success_empty/static_assert/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/static_assert/src/main.nr index 873efe734e1..fda310ba7eb 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/static_assert/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/static_assert/src/main.nr @@ -1,13 +1,13 @@ use std::static_assert; -global GLOBAL_ONE = 1; -global GLOBAL_TWO = 2; -global GLOBAL_THREE = GLOBAL_ONE + GLOBAL_TWO; +global GLOBAL_ONE: Field = 1; +global GLOBAL_TWO: Field = 2; +global GLOBAL_THREE: Field = GLOBAL_ONE + GLOBAL_TWO; // contents known at compile time // length known at compile time -global GLOBAL_ARRAY_PAIR = [GLOBAL_ONE, GLOBAL_TWO]; -global GLOBAL_SLICE_PAIR = &[GLOBAL_ONE, GLOBAL_TWO]; +global GLOBAL_ARRAY_PAIR: [Field; 2] = [GLOBAL_ONE, GLOBAL_TWO]; +global GLOBAL_SLICE_PAIR: [Field] = &[GLOBAL_ONE, GLOBAL_TWO]; pub struct Foo { field: Field, diff --git a/noir/noir-repo/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr index 11d50fc2ab5..591c03de905 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr @@ -8,7 +8,7 @@ fn main() { comptime fn foo(_: StructDefinition) -> Quoted { quote { - global ONE = 1; - global TWO = 2; + global ONE: Field = 1; + global TWO: u32 = 2; } } diff --git a/noir/noir-repo/test_programs/compile_success_no_bug/databus_mapping_regression/src/main.nr b/noir/noir-repo/test_programs/compile_success_no_bug/databus_mapping_regression/src/main.nr index ff74c82f2ee..9b6ad264a9e 100644 --- a/noir/noir-repo/test_programs/compile_success_no_bug/databus_mapping_regression/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_no_bug/databus_mapping_regression/src/main.nr @@ -23,8 +23,8 @@ pub fn array_to_bounded_vec(array: [T; N]) -> BoundedVec wh BoundedVec { storage: array, len } } -global TX_SIZE = 5; -global APP_CALL_SIZE = 2; +global TX_SIZE: u32 = 5; +global APP_CALL_SIZE: u32 = 2; fn main( a: call_data(0) [Field; TX_SIZE], diff --git a/noir/noir-repo/test_programs/execution_success/bench_2_to_17/src/main.nr b/noir/noir-repo/test_programs/execution_success/bench_2_to_17/src/main.nr index ae80dfcf0b4..204fbc38a16 100644 --- a/noir/noir-repo/test_programs/execution_success/bench_2_to_17/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/bench_2_to_17/src/main.nr @@ -1,6 +1,6 @@ use std::hash::poseidon2; -global len = 2450 * 2; +global len: u32 = 2450 * 2; fn main(x: Field) { let ped_input = [x; len]; let mut val = poseidon2::Poseidon2::hash(ped_input, len); diff --git a/noir/noir-repo/test_programs/execution_success/brillig_cow/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_cow/src/main.nr index 1d4c7f3172e..2dd0d4b3411 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_cow/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_cow/src/main.nr @@ -1,5 +1,5 @@ // Tests the copy on write optimization for arrays. We look for cases where we are modifying an array in place when we shouldn't. -global ARRAY_SIZE = 5; +global ARRAY_SIZE: u32 = 5; struct ExecutionResult { original: [Field; ARRAY_SIZE], diff --git a/noir/noir-repo/test_programs/execution_success/brillig_cow_assign/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_cow_assign/src/main.nr index 73b91e24bea..cfa228b3a96 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_cow_assign/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_cow_assign/src/main.nr @@ -1,4 +1,4 @@ -global N = 10; +global N: u32 = 10; unconstrained fn main() { let mut arr = [0; N]; diff --git a/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr index ad2a291f87d..69273bc3dca 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr @@ -7,7 +7,7 @@ global MAX_NEW_CONTRACTS_PER_TX: u32 = 1; global NUM_ENCRYPTED_LOGS_HASHES_PER_TX: u32 = 1; global NUM_UNENCRYPTED_LOGS_HASHES_PER_TX: u32 = 1; global NUM_FIELDS_PER_SHA256: u32 = 2; -global TX_EFFECT_HASH_INPUT_SIZE = 169; +global TX_EFFECT_HASH_INPUT_SIZE: u32 = 169; global TX_EFFECT_HASH_LOG_FIELDS: u32 = 4; global TX_EFFECT_HASH_FULL_FIELDS: u32 = 165; diff --git a/noir/noir-repo/test_programs/execution_success/eddsa/Nargo.toml b/noir/noir-repo/test_programs/execution_success/brillig_uninitialized_arrays/Nargo.toml similarity index 51% rename from noir/noir-repo/test_programs/execution_success/eddsa/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/brillig_uninitialized_arrays/Nargo.toml index 0f545c2febc..68bcf9929cc 100644 --- a/noir/noir-repo/test_programs/execution_success/eddsa/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/brillig_uninitialized_arrays/Nargo.toml @@ -1,6 +1,5 @@ [package] -name = "eddsa" -description = "Eddsa verification" +name = "brillig_uninitialized_arrays" type = "bin" authors = [""] diff --git a/noir/noir-repo/test_programs/execution_success/brillig_unitialised_arrays/Prover.toml b/noir/noir-repo/test_programs/execution_success/brillig_uninitialized_arrays/Prover.toml similarity index 100% rename from noir/noir-repo/test_programs/execution_success/brillig_unitialised_arrays/Prover.toml rename to noir/noir-repo/test_programs/execution_success/brillig_uninitialized_arrays/Prover.toml diff --git a/noir/noir-repo/test_programs/execution_success/brillig_unitialised_arrays/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_uninitialized_arrays/src/main.nr similarity index 100% rename from noir/noir-repo/test_programs/execution_success/brillig_unitialised_arrays/src/main.nr rename to noir/noir-repo/test_programs/execution_success/brillig_uninitialized_arrays/src/main.nr diff --git a/noir/noir-repo/test_programs/execution_success/eddsa/Prover.toml b/noir/noir-repo/test_programs/execution_success/eddsa/Prover.toml deleted file mode 100644 index 53555202ca6..00000000000 --- a/noir/noir-repo/test_programs/execution_success/eddsa/Prover.toml +++ /dev/null @@ -1,3 +0,0 @@ -_priv_key_a = 123 -_priv_key_b = 456 -msg = 789 diff --git a/noir/noir-repo/test_programs/execution_success/eddsa/src/main.nr b/noir/noir-repo/test_programs/execution_success/eddsa/src/main.nr deleted file mode 100644 index d4c3664f0c9..00000000000 --- a/noir/noir-repo/test_programs/execution_success/eddsa/src/main.nr +++ /dev/null @@ -1,56 +0,0 @@ -use std::compat; -use std::ec::consts::te::baby_jubjub; -use std::ec::tecurve::affine::Point as TEPoint; -use std::eddsa::{eddsa_poseidon_verify, eddsa_to_pub, eddsa_verify}; -use std::hash::poseidon2::Poseidon2Hasher; - -fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) { - // Skip this test for non-bn254 backends - if compat::is_bn254() { - let bjj = baby_jubjub(); - - let pub_key_a = bjj.curve.mul(_priv_key_a, bjj.curve.gen); - let pub_key_b = bjj.curve.mul(_priv_key_b, bjj.curve.gen); - let (pub_key_a_x, pub_key_a_y) = eddsa_to_pub(_priv_key_a); - let (pub_key_b_x, pub_key_b_y) = eddsa_to_pub(_priv_key_b); - assert(TEPoint::new(pub_key_a_x, pub_key_a_y) == pub_key_a); - assert(TEPoint::new(pub_key_b_x, pub_key_b_y) == pub_key_b); - // Manually computed as fields can't use modulo. Importantantly the commitment is within - // the subgroup order. Note that choice of hash is flexible for this step. - // let r_a = hash::pedersen_commitment([_priv_key_a, msg])[0] % bjj.suborder; // modulus computed manually - let r_a = 1414770703199880747815475415092878800081323795074043628810774576767372531818; - // let r_b = hash::pedersen_commitment([_priv_key_b, msg])[0] % bjj.suborder; // modulus computed manually - let r_b = 571799555715456644614141527517766533395606396271089506978608487688924659618; - - let r8_a = bjj.curve.mul(r_a, bjj.base8); - let r8_b = bjj.curve.mul(r_b, bjj.base8); - // let h_a: [Field; 6] = hash::poseidon::bn254::hash_5([ - // r8_a.x, - // r8_a.y, - // pub_key_a.x, - // pub_key_a.y, - // msg, - // ]); - // let h_b: [Field; 6] = hash::poseidon::bn254::hash_5([ - // r8_b.x, - // r8_b.y, - // pub_key_b.x, - // pub_key_b.y, - // msg, - // ]); - // let s_a = (r_a + _priv_key_a * h_a) % bjj.suborder; // modulus computed manually - let s_a = 30333430637424319196043722294837632681219980330991241982145549329256671548; - // let s_b = (r_b + _priv_key_b * h_b) % bjj.suborder; // modulus computed manually - let s_b = 1646085314320208098241070054368798527940102577261034947654839408482102287019; - // User A verifies their signature over the message - assert(eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg)); - // User B's signature over the message can't be used with user A's pub key - assert(!eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_b, r8_b.x, r8_b.y, msg)); - // User A's signature over the message can't be used with another message - assert(!eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg + 1)); - // Using a different hash should fail - assert( - !eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg), - ); - } -} diff --git a/noir/noir-repo/test_programs/execution_success/fmtstr_with_global/src/main.nr b/noir/noir-repo/test_programs/execution_success/fmtstr_with_global/src/main.nr index 8b9c9635015..4ca118f856f 100644 --- a/noir/noir-repo/test_programs/execution_success/fmtstr_with_global/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/fmtstr_with_global/src/main.nr @@ -1,4 +1,4 @@ -global FOO = 1; +global FOO: Field = 1; fn main() { println(f"foo = {FOO}"); diff --git a/noir/noir-repo/test_programs/execution_success/fold_2_to_17/src/main.nr b/noir/noir-repo/test_programs/execution_success/fold_2_to_17/src/main.nr index a3a747e4aee..d54dff4617a 100644 --- a/noir/noir-repo/test_programs/execution_success/fold_2_to_17/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/fold_2_to_17/src/main.nr @@ -1,6 +1,6 @@ use std::hash::poseidon2; -global len = 2450 * 2 - 240; // for just under 2^17 gates +global len: u32 = 2450 * 2 - 240; // for just under 2^17 gates fn main(x: Field) { let ped_input = [x; len]; let mut val = poseidon2::Poseidon2::hash(ped_input, len); diff --git a/noir/noir-repo/test_programs/execution_success/fold_call_witness_condition/src/main.nr b/noir/noir-repo/test_programs/execution_success/fold_call_witness_condition/src/main.nr index 5dc75e4a99f..5b9a5db62c5 100644 --- a/noir/noir-repo/test_programs/execution_success/fold_call_witness_condition/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/fold_call_witness_condition/src/main.nr @@ -1,4 +1,4 @@ -global NUM_RESULTS = 2; +global NUM_RESULTS: u32 = 2; fn main(x: Field, y: pub Field, enable: bool) -> pub [Field; NUM_RESULTS] { let mut result = [0; NUM_RESULTS]; for i in 0..NUM_RESULTS { diff --git a/noir/noir-repo/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr b/noir/noir-repo/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr index c5993cf6523..15b9dd26195 100644 --- a/noir/noir-repo/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr @@ -1,7 +1,7 @@ -use std::hash::{pedersen_hash_with_separator, poseidon2::Poseidon2}; +use std::hash::poseidon2::Poseidon2; global NUM_HASHES: u32 = 2; -global HASH_LENGTH = 10; +global HASH_LENGTH: u32 = 10; #[fold] pub fn poseidon_hash(inputs: [Field; N]) -> Field { diff --git a/noir/noir-repo/test_programs/execution_success/global_consts/src/foo.nr b/noir/noir-repo/test_programs/execution_success/global_consts/src/foo.nr index 50e331493dc..2c39b534259 100644 --- a/noir/noir-repo/test_programs/execution_success/global_consts/src/foo.nr +++ b/noir/noir-repo/test_programs/execution_success/global_consts/src/foo.nr @@ -2,7 +2,7 @@ mod bar; global N: u32 = 5; global MAGIC_NUMBER: u32 = 3; -global TYPE_INFERRED = 42; +global TYPE_INFERRED: u32 = 42; pub fn from_foo(x: [Field; bar::N]) { for i in 0..bar::N { diff --git a/noir/noir-repo/test_programs/execution_success/global_consts/src/main.nr b/noir/noir-repo/test_programs/execution_success/global_consts/src/main.nr index 30c5f7167f3..2eaab810d6a 100644 --- a/noir/noir-repo/test_programs/execution_success/global_consts/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/global_consts/src/main.nr @@ -18,7 +18,7 @@ struct Test { v: Field, } global VALS: [Test; 1] = [Test { v: 100 }]; -global NESTED = [VALS, VALS]; +global NESTED: [[Test; 1]; 2] = [VALS, VALS]; unconstrained fn calculate_global_value() -> Field { 42 @@ -121,4 +121,4 @@ impl Bar { } // Regression for #1440 -global foo = Foo { a: Bar::get_a() }; +global foo: Foo = Foo { a: Bar::get_a() }; diff --git a/noir/noir-repo/test_programs/execution_success/hashmap/src/main.nr b/noir/noir-repo/test_programs/execution_success/hashmap/src/main.nr index 964b900dce5..cfd4e4a9136 100644 --- a/noir/noir-repo/test_programs/execution_success/hashmap/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/hashmap/src/main.nr @@ -16,15 +16,15 @@ struct Entry { } global HASHMAP_CAP: u32 = 8; -global HASHMAP_LEN = 6; +global HASHMAP_LEN: u32 = 6; -global FIELD_CMP = |a: Field, b: Field| a.lt(b); +global FIELD_CMP: fn(Field, Field) -> bool = |a: Field, b: Field| a.lt(b); -global K_CMP = FIELD_CMP; -global V_CMP = FIELD_CMP; -global KV_CMP = |a: (K, V), b: (K, V)| a.0.lt(b.0); +global K_CMP: fn(Field, Field) -> bool = FIELD_CMP; +global V_CMP: fn(Field, Field) -> bool = FIELD_CMP; +global KV_CMP: fn((K, V), (K, V)) -> bool = |a: (K, V), b: (K, V)| a.0.lt(b.0); -global ALLOCATE_HASHMAP = +global ALLOCATE_HASHMAP: fn() -> HashMap> = || -> HashMap> HashMap::default(); fn main(input: [Entry; HASHMAP_LEN]) { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_unitialised_arrays/Nargo.toml b/noir/noir-repo/test_programs/execution_success/negated_jmpif_condition/Nargo.toml similarity index 60% rename from noir/noir-repo/test_programs/execution_success/brillig_unitialised_arrays/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/negated_jmpif_condition/Nargo.toml index f23ecc787d0..c83e2c1c1fd 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_unitialised_arrays/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/negated_jmpif_condition/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "brillig_unitialised_arrays" +name = "negated_jmpif_condition" type = "bin" authors = [""] diff --git a/noir/noir-repo/test_programs/execution_success/negated_jmpif_condition/Prover.toml b/noir/noir-repo/test_programs/execution_success/negated_jmpif_condition/Prover.toml new file mode 100644 index 00000000000..151faa5a9b1 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/negated_jmpif_condition/Prover.toml @@ -0,0 +1 @@ +x = "2" \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/negated_jmpif_condition/src/main.nr b/noir/noir-repo/test_programs/execution_success/negated_jmpif_condition/src/main.nr new file mode 100644 index 00000000000..06de2b41820 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/negated_jmpif_condition/src/main.nr @@ -0,0 +1,9 @@ +fn main(mut x: Field) { + let mut q = 0; + + if x != 10 { + q = 2; + } + + assert(q == 2); +} diff --git a/noir/noir-repo/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr b/noir/noir-repo/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr index aa1106132ff..82a868f3ffb 100644 --- a/noir/noir-repo/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr @@ -1,7 +1,7 @@ use std::hash::poseidon2::Poseidon2; global NUM_HASHES: u32 = 2; -global HASH_LENGTH = 10; +global HASH_LENGTH: u32 = 10; #[no_predicates] pub fn poseidon_hash(inputs: [Field; N]) -> Field { diff --git a/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr index 59843c368ec..6deb54dd21d 100644 --- a/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr @@ -1,4 +1,4 @@ -global TX_EFFECTS_HASH_INPUT_FIELDS = 256; +global TX_EFFECTS_HASH_INPUT_FIELDS: u32 = 256; // Convert a 32 byte array to a field element by truncating the final byte pub fn field_from_bytes_32_trunc(bytes32: [u8; 32]) -> Field { diff --git a/noir/noir-repo/test_programs/execution_success/regression_2660/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_2660/src/main.nr index f32bc924e3a..92aa15abb43 100644 --- a/noir/noir-repo/test_programs/execution_success/regression_2660/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/regression_2660/src/main.nr @@ -1,4 +1,4 @@ -global foo = -1; +global foo: i32 = -1; fn main(x: i32) { let y = x + foo; diff --git a/noir/noir-repo/test_programs/execution_success/regression_5252/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_5252/src/main.nr index 6ab4157e7a5..5f56b7f7f35 100644 --- a/noir/noir-repo/test_programs/execution_success/regression_5252/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/regression_5252/src/main.nr @@ -1,7 +1,7 @@ use std::hash::{poseidon, poseidon2::Poseidon2}; -global NUM_HASHES = 3; -global HASH_LENGTH = 20; +global NUM_HASHES: u32 = 3; +global HASH_LENGTH: u32 = 20; pub fn poseidon_hash(inputs: [Field; N]) -> Field { Poseidon2::hash(inputs, inputs.len()) diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr index de1c2b23c5f..4278cdda8a3 100644 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr @@ -1,4 +1,4 @@ -global NUM_HASHES = 2; +global NUM_HASHES: u32 = 2; fn main(foo: [u8; 95], toggle: bool, enable: [bool; NUM_HASHES]) { let mut result = [[0; 32]; NUM_HASHES]; diff --git a/noir/noir-repo/test_programs/execution_success/strings/src/main.nr b/noir/noir-repo/test_programs/execution_success/strings/src/main.nr index d28a9f483ac..c4fa0539745 100644 --- a/noir/noir-repo/test_programs/execution_success/strings/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/strings/src/main.nr @@ -1,5 +1,5 @@ // Test global string literals -global HELLO_WORLD = "hello world"; +global HELLO_WORLD: str<11> = "hello world"; fn main(message: pub str<11>, y: Field, hex_as_string: str<4>, hex_as_field: Field) { let mut bad_message = "hello world"; diff --git a/noir/noir-repo/test_programs/execution_success/struct_inputs/src/foo/bar.nr b/noir/noir-repo/test_programs/execution_success/struct_inputs/src/foo/bar.nr index 6d879326677..7a79528f8ab 100644 --- a/noir/noir-repo/test_programs/execution_success/struct_inputs/src/foo/bar.nr +++ b/noir/noir-repo/test_programs/execution_success/struct_inputs/src/foo/bar.nr @@ -1,4 +1,4 @@ -global N = 2; +global N: Field = 2; struct barStruct { val: Field, diff --git a/noir/noir-repo/test_programs/execution_success/uhashmap/src/main.nr b/noir/noir-repo/test_programs/execution_success/uhashmap/src/main.nr index e917a83c5fd..b56a4fe1747 100644 --- a/noir/noir-repo/test_programs/execution_success/uhashmap/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/uhashmap/src/main.nr @@ -11,15 +11,15 @@ struct Entry { value: Field, } -global HASHMAP_LEN = 6; +global HASHMAP_LEN: u32 = 6; -global FIELD_CMP = |a: Field, b: Field| a.lt(b); +global FIELD_CMP: fn(Field, Field) -> bool = |a: Field, b: Field| a.lt(b); -global K_CMP = FIELD_CMP; -global V_CMP = FIELD_CMP; -global KV_CMP = |a: (K, V), b: (K, V)| a.0.lt(b.0); +global K_CMP: fn(Field, Field) -> bool = FIELD_CMP; +global V_CMP: fn(Field, Field) -> bool = FIELD_CMP; +global KV_CMP: fn((K, V), (K, V)) -> bool = |a: (K, V), b: (K, V)| a.0.lt(b.0); -global ALLOCATE_HASHMAP = +global ALLOCATE_HASHMAP: fn() -> UHashMap> = || -> UHashMap> UHashMap::default(); unconstrained fn main(input: [Entry; HASHMAP_LEN]) { diff --git a/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/Nargo.toml b/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/Nargo.toml new file mode 100644 index 00000000000..5eac6f3c91a --- /dev/null +++ b/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "comptime_blackbox" +type = "bin" +authors = [""] +compiler_version = ">=0.27.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr new file mode 100644 index 00000000000..c3784e73b09 --- /dev/null +++ b/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr @@ -0,0 +1,155 @@ +//! Tests to show that the comptime interpreter implement blackbox functions. +use std::bigint; +use std::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul}; + +/// Test that all bigint operations work in comptime. +#[test] +fn test_bigint() { + let result: [u8] = comptime { + let a = bigint::Secpk1Fq::from_le_bytes(&[0, 1, 2, 3, 4]); + let b = bigint::Secpk1Fq::from_le_bytes(&[5, 6, 7, 8, 9]); + let c = (a + b) * b / a - a; + c.to_le_bytes() + }; + // Do the same calculation outside comptime. + let a = bigint::Secpk1Fq::from_le_bytes(&[0, 1, 2, 3, 4]); + let b = bigint::Secpk1Fq::from_le_bytes(&[5, 6, 7, 8, 9]); + let c = bigint::Secpk1Fq::from_le_bytes(result); + assert_eq(c, (a + b) * b / a - a); +} + +/// Test that to_le_radix returns an array. +#[test] +fn test_to_le_radix() { + comptime { + let field = 2; + let bytes: [u8; 8] = field.to_le_radix(256); + let _num = bigint::BigInt::from_le_bytes(bytes, bigint::bn254_fq); + }; +} + +#[test] +fn test_bitshift() { + let c = comptime { + let a: i32 = 10; + let b: u32 = 4; + a << b as u8 + }; + assert_eq(c, 160); +} + +#[test] +fn test_aes128_encrypt() { + let ciphertext = comptime { + let plaintext: [u8; 5] = [1, 2, 3, 4, 5]; + let iv: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + let key: [u8; 16] = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]; + std::aes128::aes128_encrypt(plaintext, iv, key) + }; + let clear_len = 5; + let cipher_len = clear_len + 16 - clear_len % 16; + assert_eq(ciphertext.len(), cipher_len); +} + +#[test] +fn test_blake2s() { + let hash = comptime { + let input = [104, 101, 108, 108, 111]; + std::hash::blake2s(input) + }; + assert_eq(hash[0], 0x19); + assert_eq(hash[31], 0x25); +} + +#[test] +fn test_blake3() { + let hash = comptime { + let input = [104, 101, 108, 108, 111]; + std::hash::blake3(input) + }; + assert_eq(hash[0], 0xea); + assert_eq(hash[31], 0x0f); +} + +/// Test that ecdsa_secp256k1 is implemented. +#[test] +fn test_ecdsa_secp256k1() { + let (valid_array, valid_slice) = comptime { + let pub_key_x: [u8; 32] = hex_to_bytes("a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7").as_array(); + let pub_key_y: [u8; 32] = hex_to_bytes("893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7").as_array(); + let signature: [u8; 64] = hex_to_bytes("e5081c80ab427dc370346f4a0e31aa2bad8d9798c38061db9ae55a4e8df454fd28119894344e71b78770cc931d61f480ecbb0b89d6eb69690161e49a715fcd55").as_array(); + let hashed_message: [u8; 32] = hex_to_bytes("3a73f4123a5cd2121f21cd7e8d358835476949d035d9c2da6806b4633ac8c1e2").as_array(); + + let valid_array = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + let valid_slice = std::ecdsa_secp256k1::verify_signature_slice(pub_key_x, pub_key_y, signature, hashed_message.as_slice()); + + (valid_array, valid_slice) + }; + assert(valid_array); + assert(valid_slice); +} + +/// Test that ecdsa_secp256r1 is implemented. +#[test] +fn test_ecdsa_secp256r1() { + let (valid_array, valid_slice) = comptime { + let pub_key_x: [u8; 32] = hex_to_bytes("550f471003f3df97c3df506ac797f6721fb1a1fb7b8f6f83d224498a65c88e24").as_array(); + let pub_key_y: [u8; 32] = hex_to_bytes("136093d7012e509a73715cbd0b00a3cc0ff4b5c01b3ffa196ab1fb327036b8e6").as_array(); + let signature: [u8; 64] = hex_to_bytes("2c70a8d084b62bfc5ce03641caf9f72ad4da8c81bfe6ec9487bb5e1bef62a13218ad9ee29eaf351fdc50f1520c425e9b908a07278b43b0ec7b872778c14e0784").as_array(); + let hashed_message: [u8; 32] = hex_to_bytes("54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad").as_array(); + + let valid_array = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + let valid_slice = std::ecdsa_secp256r1::verify_signature_slice(pub_key_x, pub_key_y, signature, hashed_message.as_slice()); + (valid_array, valid_slice) + }; + assert(valid_array); + assert(valid_slice); +} + +/// Test that sha256_compression is implemented. +#[test] +fn test_sha256() { + let hash = comptime { + let input: [u8; 1] = [0xbd]; + std::hash::sha256(input) + }; + assert_eq(hash[0], 0x68); + assert_eq(hash[31], 0x2b); +} + +/// Test that `embedded_curve_add` and `multi_scalar_mul` are implemented. +#[test] +fn test_embedded_curve_ops() { + let (sum, mul) = comptime { + let g1 = EmbeddedCurvePoint { x: 1, y: 17631683881184975370165255887551781615748388533673675138860, is_infinite: false }; + let s1 = EmbeddedCurveScalar { lo: 1, hi: 0 }; + let sum = g1 + g1; + let mul = multi_scalar_mul([g1, g1], [s1, s1]); + (sum, mul) + }; + assert_eq(sum, mul); +} + +/// Parse a lowercase hexadecimal string (without 0x prefix) as byte slice. +comptime fn hex_to_bytes(s: str) -> [u8] { + assert(N % 2 == 0); + let mut out = &[]; + let bz = s.as_bytes(); + let mut h: u32 = 0; + for i in 0 .. bz.len() { + let ascii = bz[i]; + let d = if ascii < 58 { + ascii - 48 + } else { + assert(ascii >= 97); // enforce >= 'a' + assert(ascii <= 102); // enforce <= 'f' + ascii - 87 + }; + h = h * 16 + d as u32; + if i % 2 == 1 { + out = out.push_back(h as u8); + h = 0; + } + } + out +} diff --git a/noir/noir-repo/test_programs/test_libraries/diamond_deps_2/src/lib.nr b/noir/noir-repo/test_programs/test_libraries/diamond_deps_2/src/lib.nr index 46dce3d5600..23de4d4c0f3 100644 --- a/noir/noir-repo/test_programs/test_libraries/diamond_deps_2/src/lib.nr +++ b/noir/noir-repo/test_programs/test_libraries/diamond_deps_2/src/lib.nr @@ -1,4 +1,4 @@ -global RESOLVE_THIS = 3; +global RESOLVE_THIS: Field = 3; pub fn call_dep2(x: Field, y: Field) -> Field { x + y diff --git a/noir/noir-repo/tooling/debugger/ignored-tests.txt b/noir/noir-repo/tooling/debugger/ignored-tests.txt index 0037b8e5d5f..e0548fe1e1a 100644 --- a/noir/noir-repo/tooling/debugger/ignored-tests.txt +++ b/noir/noir-repo/tooling/debugger/ignored-tests.txt @@ -2,7 +2,8 @@ brillig_references debug_logs is_unconstrained macros +reference_counts references regression_4709 reference_only_used_as_alias -brillig_rc_regression_6123 \ No newline at end of file +brillig_rc_regression_6123 diff --git a/noir/noir-repo/tooling/debugger/tests/debug.rs b/noir/noir-repo/tooling/debugger/tests/debug.rs index 2dca6b95f0e..eb43cf9cc6d 100644 --- a/noir/noir-repo/tooling/debugger/tests/debug.rs +++ b/noir/noir-repo/tooling/debugger/tests/debug.rs @@ -12,7 +12,7 @@ mod tests { let nargo_bin = cargo_bin("nargo").into_os_string().into_string().expect("Cannot parse nargo path"); - let timeout_seconds = 25; + let timeout_seconds = 30; let mut dbg_session = spawn_bash(Some(timeout_seconds * 1000)).expect("Could not start bash session"); diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index d71f0414adc..3762ba9cf8d 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -645,55 +645,53 @@ impl<'a> NodeFinder<'a> { let struct_id = get_type_struct_id(typ); let is_primitive = typ.is_primitive(); + let has_self_param = matches!(function_kind, FunctionKind::SelfType(..)); for (name, methods) in methods_by_name { - for (func_id, method_type) in methods.iter() { - if function_kind == FunctionKind::Any { - if let Some(method_type) = method_type { - if method_type.unify(typ).is_err() { - continue; - } - } - } - - if let Some(struct_id) = struct_id { - let modifiers = self.interner.function_modifiers(&func_id); - let visibility = modifiers.visibility; - if !struct_member_is_visible( - struct_id, - visibility, - self.module_id, - self.def_maps, - ) { - continue; - } - } + let Some(func_id) = + methods.find_matching_method(typ, has_self_param, self.interner).or_else(|| { + // Also try to find a method assuming typ is `&mut typ`: + // we want to suggest methods that take `&mut self` even though a variable might not + // be mutable, so a user can know they need to mark it as mutable. + let typ = Type::MutableReference(Box::new(typ.clone())); + methods.find_matching_method(&typ, has_self_param, self.interner) + }) + else { + continue; + }; - if is_primitive - && !method_call_is_visible( - typ, - func_id, - self.module_id, - self.interner, - self.def_maps, - ) - { + if let Some(struct_id) = struct_id { + let modifiers = self.interner.function_modifiers(&func_id); + let visibility = modifiers.visibility; + if !struct_member_is_visible(struct_id, visibility, self.module_id, self.def_maps) { continue; } + } - if name_matches(name, prefix) { - let completion_items = self.function_completion_items( - name, - func_id, - function_completion_kind, - function_kind, - None, // attribute first type - self_prefix, - ); - if !completion_items.is_empty() { - self.completion_items.extend(completion_items); - self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id)); - } + if is_primitive + && !method_call_is_visible( + typ, + func_id, + self.module_id, + self.interner, + self.def_maps, + ) + { + continue; + } + + if name_matches(name, prefix) { + let completion_items = self.function_completion_items( + name, + func_id, + function_completion_kind, + function_kind, + None, // attribute first type + self_prefix, + ); + if !completion_items.is_empty() { + self.completion_items.extend(completion_items); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id)); } } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs index 8cfb2a4b5ee..9306e38a48a 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -2780,4 +2780,37 @@ fn main() { ) .await; } + + #[test] + async fn test_suggests_methods_based_on_type_generics() { + let src = r#" + struct Foo { + t: T, + } + + impl Foo { + fn bar_baz(_self: Self) -> Field { + 5 + } + } + + impl Foo { + fn bar(_self: Self) -> Field { + 5 + } + + fn baz(_self: Self) -> Field { + 6 + } + } + + fn main() -> pub Field { + let foo: Foo = Foo { t: 5 }; + foo.b>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + assert!(items[0].label == "bar_baz()"); + } } diff --git a/noir/noir-repo/tooling/nargo_cli/build.rs b/noir/noir-repo/tooling/nargo_cli/build.rs index ad1f82f4e45..740e5ed2052 100644 --- a/noir/noir-repo/tooling/nargo_cli/build.rs +++ b/noir/noir-repo/tooling/nargo_cli/build.rs @@ -60,9 +60,13 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ ]; /// Tests which aren't expected to work with the default inliner cases. -const INLINER_MIN_OVERRIDES: [(&str, i64); 1] = [ +const INLINER_MIN_OVERRIDES: [(&str, i64); 2] = [ // 0 works if PoseidonHasher::write is tagged as `inline_always`, otherwise 22. ("eddsa", 0), + // (#6583): The RcTracker in the DIE SSA pass is removing inc_rcs that are still needed. + // This triggers differently depending on the optimization level (although all are wrong), + // so we arbitrarily only run with the inlined versions. + ("reference_counts", 0), ]; /// Some tests are expected to have warnings @@ -82,7 +86,14 @@ fn read_test_cases( let test_case_dirs = fs::read_dir(test_data_dir).unwrap().flatten().filter(|c| c.path().is_dir()); - test_case_dirs.into_iter().map(|dir| { + test_case_dirs.into_iter().filter_map(|dir| { + // When switching git branches we might end up with non-empty directories that have a `target` + // directory inside them but no `Nargo.toml`. + // These "tests" would always fail, but it's okay to ignore them so we do that here. + if !dir.path().join("Nargo.toml").exists() { + return None; + } + let test_name = dir.file_name().into_string().expect("Directory can't be converted to string"); if test_name.contains('-') { @@ -90,7 +101,7 @@ fn read_test_cases( "Invalid test directory: {test_name}. Cannot include `-`, please convert to `_`" ); } - (test_name, dir.path()) + Some((test_name, dir.path())) }) } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs index c69775d3323..ffeb5d9ba74 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs @@ -5,7 +5,6 @@ use super::NargoConfig; use clap::Args; use nargo::constants::{PKG_FILE, SRC_DIR}; use nargo::package::{CrateName, PackageType}; -use noirc_driver::NOIRC_VERSION; use std::path::PathBuf; /// Create a Noir project in the current directory. @@ -66,7 +65,6 @@ pub(crate) fn initialize_project( name = "{package_name}" type = "{package_type}" authors = [""] -compiler_version = ">={NOIRC_VERSION}" [dependencies]"# ); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs index 0ac4c98bb95..0730d06ad72 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs @@ -1165,7 +1165,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { // Finally format the comment, if any group.text(self.chunk(|formatter| { - formatter.skip_comments_and_whitespace(); + formatter.skip_comments_and_whitespace_writing_multiple_lines_if_found(); })); group.decrease_indentation(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs index fd6977df613..8207db5e486 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs @@ -571,6 +571,36 @@ fn baz() { let z = 3 ; let y = 2; } +"; + let expected = src; + assert_format(src, expected); + } + + #[test] + fn keeps_newlines_between_comments_no_statements() { + let src = "fn foo() { + // foo + + // bar + + // baz +} +"; + let expected = src; + assert_format(src, expected); + } + + #[test] + fn keeps_newlines_between_comments_one_statement() { + let src = "fn foo() { + let x = 1; + + // foo + + // bar + + // baz +} "; let expected = src; assert_format(src, expected); diff --git a/noir/noir-repo/tooling/nargo_toml/src/errors.rs b/noir/noir-repo/tooling/nargo_toml/src/errors.rs index 1ee8e90c8e5..7e1003d04f7 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/errors.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/errors.rs @@ -80,6 +80,8 @@ pub enum ManifestError { #[allow(clippy::enum_variant_names)] #[derive(Error, Debug, PartialEq, Eq, Clone)] pub enum SemverError { + #[error("Invalid value for `compiler_version` in package {package_name}. Requirements may only refer to full releases")] + InvalidCompilerVersionRequirement { package_name: CrateName, required_compiler_version: String }, #[error("Incompatible compiler version in package {package_name}. Required compiler version is {required_compiler_version} but the compiler version is {compiler_version_found}.\n Update the compiler_version field in Nargo.toml to >={required_compiler_version} or compile this project with version {required_compiler_version}")] IncompatibleVersion { package_name: CrateName, diff --git a/noir/noir-repo/tooling/nargo_toml/src/semver.rs b/noir/noir-repo/tooling/nargo_toml/src/semver.rs index 253ac82aa34..ececa1b30dd 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/semver.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/semver.rs @@ -3,11 +3,14 @@ use nargo::{ package::{Dependency, Package}, workspace::Workspace, }; -use semver::{Error, Version, VersionReq}; +use noirc_driver::CrateName; +use semver::{Error, Prerelease, Version, VersionReq}; // Parse a semver compatible version string pub(crate) fn parse_semver_compatible_version(version: &str) -> Result { - Version::parse(version) + let mut version = Version::parse(version)?; + version.pre = Prerelease::EMPTY; + Ok(version) } // Check that all of the packages in the workspace are compatible with the current compiler version @@ -25,10 +28,7 @@ pub(crate) fn semver_check_workspace( } // Check that a package and all of its dependencies are compatible with the current compiler version -pub(crate) fn semver_check_package( - package: &Package, - compiler_version: &Version, -) -> Result<(), SemverError> { +fn semver_check_package(package: &Package, compiler_version: &Version) -> Result<(), SemverError> { // Check that this package's compiler version requirements are satisfied if let Some(version) = &package.compiler_required_version { let version_req = match VersionReq::parse(version) { @@ -40,6 +40,9 @@ pub(crate) fn semver_check_package( }) } }; + + validate_compiler_version_requirement(&package.name, &version_req)?; + if !version_req.matches(compiler_version) { return Err(SemverError::IncompatibleVersion { package_name: package.name.clone(), @@ -61,6 +64,20 @@ pub(crate) fn semver_check_package( Ok(()) } +fn validate_compiler_version_requirement( + package_name: &CrateName, + required_compiler_version: &VersionReq, +) -> Result<(), SemverError> { + if required_compiler_version.comparators.iter().any(|comparator| !comparator.pre.is_empty()) { + return Err(SemverError::InvalidCompilerVersionRequirement { + package_name: package_name.clone(), + required_compiler_version: required_compiler_version.to_string(), + }); + } + + Ok(()) +} + // Strip the build meta data from the version string since it is ignored by semver. fn strip_build_meta_data(version: &Version) -> String { let version_string = version.to_string(); @@ -191,6 +208,26 @@ mod tests { }; } + #[test] + fn test_semver_prerelease() { + let compiler_version = parse_semver_compatible_version("1.0.0-beta.0").unwrap(); + + let package = Package { + compiler_required_version: Some(">=0.1.0".to_string()), + root_dir: PathBuf::new(), + package_type: PackageType::Library, + entry_path: PathBuf::new(), + name: CrateName::from_str("test").unwrap(), + dependencies: BTreeMap::new(), + version: Some("1.0".to_string()), + expression_width: None, + }; + + if let Err(err) = semver_check_package(&package, &compiler_version) { + panic!("{err}"); + }; + } + #[test] fn test_semver_build_data() { let compiler_version = Version::parse("0.1.0+this-is-ignored-by-semver").unwrap(); diff --git a/noir/noir-repo/tooling/noir_codegen/package.json b/noir/noir-repo/tooling/noir_codegen/package.json index 3530a0ed6f4..c96ecd22230 100644 --- a/noir/noir-repo/tooling/noir_codegen/package.json +++ b/noir/noir-repo/tooling/noir_codegen/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.39.0", + "version": "1.0.0-beta.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/noir/noir-repo/tooling/noir_js/package.json b/noir/noir-repo/tooling/noir_js/package.json index 8c1c52af8f0..0f0e111c30b 100644 --- a/noir/noir-repo/tooling/noir_js/package.json +++ b/noir/noir-repo/tooling/noir_js/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.39.0", + "version": "1.0.0-beta.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/noir/noir-repo/tooling/noir_js_types/package.json b/noir/noir-repo/tooling/noir_js_types/package.json index 2196bc08249..17e9efc7678 100644 --- a/noir/noir-repo/tooling/noir_js_types/package.json +++ b/noir/noir-repo/tooling/noir_js_types/package.json @@ -4,7 +4,7 @@ "The Noir Team " ], "packageManager": "yarn@3.5.1", - "version": "0.39.0", + "version": "1.0.0-beta.0", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { diff --git a/noir/noir-repo/tooling/noirc_abi/Cargo.toml b/noir/noir-repo/tooling/noirc_abi/Cargo.toml index a7baf334bff..22114408e18 100644 --- a/noir/noir-repo/tooling/noirc_abi/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_abi/Cargo.toml @@ -23,8 +23,8 @@ num-bigint = "0.4" num-traits = "0.2" [dev-dependencies] -strum = "0.24" -strum_macros = "0.24" +strum.workspace = true +strum_macros.workspace = true proptest.workspace = true proptest-derive.workspace = true diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/package.json b/noir/noir-repo/tooling/noirc_abi_wasm/package.json index 5f92ada116e..9194714454d 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/package.json +++ b/noir/noir-repo/tooling/noirc_abi_wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.39.0", + "version": "1.0.0-beta.0", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { diff --git a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts index 070ca03ba9b..06f2bb682bf 100644 --- a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts +++ b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts @@ -7,7 +7,7 @@ import { readFileSync } from 'fs'; import { getPathToFixture, getTestContractArtifact } from '../tests/fixtures.js'; import { computeArtifactHash } from './artifact_hash.js'; -const TEST_CONTRACT_ARTIFACT_HASH = `"0x08f4e5d2a0be9dc5c31894f88aaa93d353918078cd7fdfbbc7818ec3f2268b77"`; +const TEST_CONTRACT_ARTIFACT_HASH = `"0x1d429080e986cf55e59203b4229063bf9b4d875e832fe56c5257303075110190"`; describe('ArtifactHash', () => { it('calculates the artifact hash', () => { @@ -30,8 +30,10 @@ describe('ArtifactHash', () => { it('calculates the test contract artifact hash multiple times to ensure deterministic hashing', () => { const testArtifact = getTestContractArtifact(); + const calculatedArtifactHash = computeArtifactHash(testArtifact).toString(); + expect(calculatedArtifactHash).toMatchInlineSnapshot(TEST_CONTRACT_ARTIFACT_HASH); for (let i = 0; i < 1000; i++) { - expect(computeArtifactHash(testArtifact).toString()).toMatchInlineSnapshot(TEST_CONTRACT_ARTIFACT_HASH); + expect(computeArtifactHash(testArtifact).toString()).toBe(calculatedArtifactHash); } }); diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 2d0af3c2c7c..d80df020866 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -3507,7 +3507,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/noir_codegen@portal:../noir/packages/noir_codegen::locator=%40aztec%2Faztec3-packages%40workspace%3A." dependencies: - "@noir-lang/types": 0.39.0 + "@noir-lang/types": 1.0.0-beta.0 glob: ^10.3.10 ts-command-line-args: ^2.5.1 bin: @@ -3516,13 +3516,13 @@ __metadata: linkType: soft "@noir-lang/noir_js@file:../noir/packages/noir_js::locator=%40aztec%2Faztec3-packages%40workspace%3A.": - version: 0.39.0 - resolution: "@noir-lang/noir_js@file:../noir/packages/noir_js#../noir/packages/noir_js::hash=2fe976&locator=%40aztec%2Faztec3-packages%40workspace%3A." + version: 1.0.0-beta.0 + resolution: "@noir-lang/noir_js@file:../noir/packages/noir_js#../noir/packages/noir_js::hash=90b0a4&locator=%40aztec%2Faztec3-packages%40workspace%3A." dependencies: - "@noir-lang/acvm_js": 0.55.0 - "@noir-lang/noirc_abi": 0.39.0 - "@noir-lang/types": 0.39.0 - checksum: bbed7618af5ffb055e7020686035fcf2d25ba0510f48654338192d2bb18cf7ca74403ea6f6ea713ff1d204436f9c2066eb5adf645f585b353d3c6f9d6ee38403 + "@noir-lang/acvm_js": 1.0.0-beta.0 + "@noir-lang/noirc_abi": 1.0.0-beta.0 + "@noir-lang/types": 1.0.0-beta.0 + checksum: 938fa6d909859abbc62eb45508b4a1729c3b3b0ced99dba2de37571697b9fa9f1c90c932e3768c01ceb50315e69d5c249e8a717e470f6fec33cf105a39b73cd4 languageName: node linkType: hard @@ -3530,7 +3530,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/noirc_abi@portal:../noir/packages/noirc_abi::locator=%40aztec%2Faztec3-packages%40workspace%3A." dependencies: - "@noir-lang/types": 0.39.0 + "@noir-lang/types": 1.0.0-beta.0 languageName: node linkType: soft