From f6b1ba60daf37a5a6466ca1e5ee7be70354af485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bene=C5=A1?= Date: Tue, 7 May 2024 16:20:30 +0200 Subject: [PATCH] feat: `multi_scalar_mul` blackbox func (#6097) Fixes https://github.com/noir-lang/noir/issues/4928 Fixes https://github.com/noir-lang/noir/issues/4932 **Note**: Noticed that we have [lookup table](https://github.com/AztecProtocol/aztec-packages/blob/46749ac9f32d4720efb380fb3a0e10a8ab1c345d/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/fixed_base/fixed_base.hpp#L15) for fixed base in BB. Not sure if it's still needed after nuking the fixed based scalar mul. --- .../dsl/acir_format/acir_format.cpp | 10 +- .../dsl/acir_format/acir_format.hpp | 9 +- .../dsl/acir_format/acir_format.test.cpp | 18 +- .../acir_format/acir_to_constraint_buf.hpp | 17 +- .../acir_format/bigint_constraint.test.cpp | 15 +- .../dsl/acir_format/block_constraint.test.cpp | 3 +- .../dsl/acir_format/ec_operations.test.cpp | 3 +- .../dsl/acir_format/ecdsa_secp256k1.test.cpp | 9 +- .../dsl/acir_format/ecdsa_secp256r1.test.cpp | 12 +- .../dsl/acir_format/fixed_base_scalar_mul.cpp | 33 --- .../dsl/acir_format/fixed_base_scalar_mul.hpp | 21 -- .../dsl/acir_format/multi_scalar_mul.cpp | 48 ++++ .../dsl/acir_format/multi_scalar_mul.hpp | 21 ++ .../acir_format/poseidon2_constraint.test.cpp | 3 +- .../acir_format/recursion_constraint.test.cpp | 6 +- .../dsl/acir_format/serde/acir.hpp | 258 ++++-------------- .../acir_format/sha256_constraint.test.cpp | 3 +- .../acir_format/variable_base_scalar_mul.cpp | 38 --- .../acir_format/variable_base_scalar_mul.hpp | 23 -- barretenberg/ts/src/info.ts | 2 +- .../aztec/src/keys/point_to_symmetric_key.nr | 4 +- .../crates/types/src/grumpkin_private_key.nr | 4 +- noir/noir-repo/acvm-repo/acir/README.md | 15 +- .../noir-repo/acvm-repo/acir/codegen/acir.cpp | 212 +++----------- .../acir/src/circuit/black_box_functions.rs | 12 +- .../opcodes/black_box_function_call.rs | 36 +-- .../acir/tests/test_program_serialization.rs | 57 ++-- .../src/pwg/blackbox/embedded_curve_ops.rs | 53 ++++ .../src/pwg/blackbox/fixed_base_scalar_mul.rs | 70 ----- .../acvm-repo/acvm/src/pwg/blackbox/mod.rs | 23 +- .../test/browser/execute_circuit.test.ts | 14 +- .../acvm_js/test/node/execute_circuit.test.ts | 14 +- .../test/shared/fixed_base_scalar_mul.ts | 17 -- ...base_scalar_mul.ts => multi_scalar_mul.ts} | 8 +- .../src/curve_specific_solver.rs | 30 +- ...se_scalar_mul.rs => embedded_curve_ops.rs} | 196 +++++++------ .../bn254_blackbox_solver/src/lib.rs | 24 +- .../acvm-repo/brillig/src/black_box.rs | 18 +- .../acvm-repo/brillig_vm/src/black_box.rs | 27 +- .../brillig/brillig_gen/brillig_black_box.rs | 39 +-- .../noirc_evaluator/src/brillig/brillig_ir.rs | 16 +- .../src/brillig/brillig_ir/debug_show.rs | 27 +- .../ssa/acir_gen/acir_ir/generated_acir.rs | 26 +- .../src/ssa/ir/instruction/call.rs | 3 +- .../noir/standard_library/black_box_fns.md | 2 +- .../embedded_curve_ops.mdx | 77 ++++++ .../cryptographic_primitives/scalar.mdx | 44 --- .../{scalar_mul.nr => embedded_curve_ops.nr} | 45 ++- .../noir_stdlib/src/grumpkin_scalar.nr | 1 + .../noir_stdlib/src/grumpkin_scalar_mul.nr | 6 - noir/noir-repo/noir_stdlib/src/lib.nr | 3 +- .../intrinsic_die/src/main.nr | 4 +- .../Nargo.toml | 2 +- .../brillig_embedded_curve/Prover.toml | 3 + .../brillig_embedded_curve/src/main.nr | 28 ++ .../brillig_scalar_mul/Prover.toml | 7 - .../brillig_scalar_mul/src/main.nr | 32 --- .../Nargo.toml | 2 +- .../embedded_curve_ops/Prover.toml | 3 + .../embedded_curve_ops/src/main.nr | 24 ++ .../fixed_base_scalar_mul/Prover.toml | 7 - .../fixed_base_scalar_mul/src/main.nr | 31 --- .../simple_shield/src/main.nr | 2 +- .../variable_base_scalar_mul/Nargo.toml | 6 - .../variable_base_scalar_mul/Prover.toml | 4 - .../variable_base_scalar_mul/src/main.nr | 33 --- .../mock_backend/src/info_cmd.rs | 2 +- noir/noir-repo/tooling/lsp/src/solver.rs | 18 +- 68 files changed, 627 insertions(+), 1256 deletions(-) delete mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.hpp create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.cpp create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.hpp delete mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/variable_base_scalar_mul.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/variable_base_scalar_mul.hpp create mode 100644 noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs delete mode 100644 noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/fixed_base_scalar_mul.rs delete mode 100644 noir/noir-repo/acvm-repo/acvm_js/test/shared/fixed_base_scalar_mul.ts rename noir/noir-repo/acvm-repo/acvm_js/test/shared/{variable_base_scalar_mul.ts => multi_scalar_mul.ts} (69%) rename noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/{fixed_base_scalar_mul.rs => embedded_curve_ops.rs} (53%) create mode 100644 noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx delete mode 100644 noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/scalar.mdx rename noir/noir-repo/noir_stdlib/src/{scalar_mul.nr => embedded_curve_ops.nr} (54%) delete mode 100644 noir/noir-repo/noir_stdlib/src/grumpkin_scalar_mul.nr rename noir/noir-repo/test_programs/execution_success/{fixed_base_scalar_mul => brillig_embedded_curve}/Nargo.toml (62%) create mode 100644 noir/noir-repo/test_programs/execution_success/brillig_embedded_curve/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/brillig_embedded_curve/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/brillig_scalar_mul/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/brillig_scalar_mul/src/main.nr rename noir/noir-repo/test_programs/execution_success/{brillig_scalar_mul => embedded_curve_ops}/Nargo.toml (65%) create mode 100644 noir/noir-repo/test_programs/execution_success/embedded_curve_ops/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/embedded_curve_ops/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/fixed_base_scalar_mul/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/fixed_base_scalar_mul/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/variable_base_scalar_mul/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/variable_base_scalar_mul/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/variable_base_scalar_mul/src/main.nr diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 32972a28781..6629707e6bf 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -84,14 +84,10 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo for (const auto& constraint : constraint_system.poseidon2_constraints) { create_poseidon2_permutations(builder, constraint); } - // Add fixed base scalar mul constraints - for (const auto& constraint : constraint_system.fixed_base_scalar_mul_constraints) { - create_fixed_base_constraint(builder, constraint); - } - // Add variable base scalar mul constraints - for (const auto& constraint : constraint_system.variable_base_scalar_mul_constraints) { - create_variable_base_constraint(builder, constraint); + // Add multi scalar mul constraints + for (const auto& constraint : constraint_system.multi_scalar_mul_constraints) { + create_multi_scalar_mul_constraint(builder, constraint); } // Add ec add constraints diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index a7f5b475737..bde98babdaa 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -8,16 +8,15 @@ #include "ec_operations.hpp" #include "ecdsa_secp256k1.hpp" #include "ecdsa_secp256r1.hpp" -#include "fixed_base_scalar_mul.hpp" #include "keccak_constraint.hpp" #include "logic_constraint.hpp" +#include "multi_scalar_mul.hpp" #include "pedersen.hpp" #include "poseidon2_constraint.hpp" #include "range_constraint.hpp" #include "recursion_constraint.hpp" #include "schnorr_verify.hpp" #include "sha256_constraint.hpp" -#include "variable_base_scalar_mul.hpp" #include namespace acir_format { @@ -48,8 +47,7 @@ struct AcirFormat { std::vector pedersen_constraints; std::vector pedersen_hash_constraints; std::vector poseidon2_constraints; - std::vector fixed_base_scalar_mul_constraints; - std::vector variable_base_scalar_mul_constraints; + std::vector multi_scalar_mul_constraints; std::vector ec_add_constraints; std::vector recursion_constraints; std::vector bigint_from_le_bytes_constraints; @@ -83,8 +81,7 @@ struct AcirFormat { pedersen_constraints, pedersen_hash_constraints, poseidon2_constraints, - fixed_base_scalar_mul_constraints, - variable_base_scalar_mul_constraints, + multi_scalar_mul_constraints, ec_add_constraints, recursion_constraints, poly_triple_constraints, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index 7de0f847b1f..fa24b515465 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -47,8 +47,7 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -164,8 +163,7 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -233,8 +231,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -329,8 +326,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -444,8 +440,7 @@ TEST_F(AcirFormatTests, TestVarKeccak) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -492,8 +487,7 @@ TEST_F(AcirFormatTests, TestKeccakPermutation) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp index f31fc73c806..faaa3d4bb24 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp @@ -310,19 +310,10 @@ void handle_blackbox_func_call(Program::Opcode::BlackBoxFuncCall const& arg, Aci .result = arg.output.value, .signature = map(arg.signature, [](auto& e) { return e.witness.value; }), }); - } else if constexpr (std::is_same_v) { - af.fixed_base_scalar_mul_constraints.push_back(FixedBaseScalarMul{ - .low = arg.low.witness.value, - .high = arg.high.witness.value, - .pub_key_x = arg.outputs[0].value, - .pub_key_y = arg.outputs[1].value, - }); - } else if constexpr (std::is_same_v) { - af.variable_base_scalar_mul_constraints.push_back(VariableBaseScalarMul{ - .point_x = arg.point_x.witness.value, - .point_y = arg.point_y.witness.value, - .scalar_low = arg.scalar_low.witness.value, - .scalar_high = arg.scalar_high.witness.value, + } else if constexpr (std::is_same_v) { + af.multi_scalar_mul_constraints.push_back(MultiScalarMul{ + .points = map(arg.points, [](auto& e) { return e.witness.value; }), + .scalars = map(arg.scalars, [](auto& e) { return e.witness.value; }), .out_point_x = arg.outputs[0].value, .out_point_y = arg.outputs[1].value, }); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp index 3b32c7f2695..47e3e64b435 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp @@ -184,8 +184,7 @@ TEST_F(BigIntTests, TestBigIntConstraintMultiple) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -253,8 +252,7 @@ TEST_F(BigIntTests, TestBigIntConstraintSimple) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = { from_le_bytes_constraint_bigint1 }, @@ -307,8 +305,7 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -365,8 +362,7 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse2) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -444,8 +440,7 @@ TEST_F(BigIntTests, TestBigIntDIV) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = { from_le_bytes_constraint_bigint1, from_le_bytes_constraint_bigint2 }, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp index 75b9150d335..39424f4c3a1 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp @@ -126,8 +126,7 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp index bdda21409a1..0fb59c5b03a 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp @@ -66,8 +66,7 @@ TEST_F(EcOperations, TestECOperations) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = { ec_add_constraint }, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index c494cc13e79..2dd20037387 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -106,8 +106,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintSucceed) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -156,8 +155,7 @@ TEST_F(ECDSASecp256k1, TestECDSACompilesForVerifier) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -201,8 +199,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp index 6728445d237..19b87a26ddd 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp @@ -140,8 +140,7 @@ TEST(ECDSASecp256r1, test_hardcoded) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -192,8 +191,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintSucceed) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -242,8 +240,7 @@ TEST(ECDSASecp256r1, TestECDSACompilesForVerifier) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -287,8 +284,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintFail) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.cpp deleted file mode 100644 index 517a2baf6ee..00000000000 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "fixed_base_scalar_mul.hpp" -#include "barretenberg/dsl/types.hpp" -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" -#include "barretenberg/plonk_honk_shared/arithmetization/gate_data.hpp" - -namespace acir_format { - -template void create_fixed_base_constraint(Builder& builder, const FixedBaseScalarMul& input) -{ - using cycle_group_ct = bb::stdlib::cycle_group; - using cycle_scalar_ct = typename bb::stdlib::cycle_group::cycle_scalar; - using field_ct = bb::stdlib::field_t; - - // We reconstruct the scalar from the low and high limbs - field_ct low_as_field = field_ct::from_witness_index(&builder, input.low); - field_ct high_as_field = field_ct::from_witness_index(&builder, input.high); - cycle_scalar_ct scalar(low_as_field, high_as_field); - - // We multiply the scalar with G1 to get the result - auto result = cycle_group_ct(grumpkin::g1::affine_one) * scalar; - - // Finally we add the constraints - builder.assert_equal(result.x.get_witness_index(), input.pub_key_x); - builder.assert_equal(result.y.get_witness_index(), input.pub_key_y); -} - -template void create_fixed_base_constraint(UltraCircuitBuilder& builder, - const FixedBaseScalarMul& input); -template void create_fixed_base_constraint(GoblinUltraCircuitBuilder& builder, - const FixedBaseScalarMul& input); - -} // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.hpp deleted file mode 100644 index ef7d634870b..00000000000 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include "barretenberg/dsl/types.hpp" -#include "barretenberg/serialize/msgpack.hpp" -#include - -namespace acir_format { - -struct FixedBaseScalarMul { - uint32_t low; - uint32_t high; - uint32_t pub_key_x; - uint32_t pub_key_y; - - // for serialization, update with any new fields - MSGPACK_FIELDS(low, high, pub_key_x, pub_key_y); - friend bool operator==(FixedBaseScalarMul const& lhs, FixedBaseScalarMul const& rhs) = default; -}; - -template void create_fixed_base_constraint(Builder& builder, const FixedBaseScalarMul& input); - -} // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.cpp new file mode 100644 index 00000000000..83354d97c76 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.cpp @@ -0,0 +1,48 @@ +#include "multi_scalar_mul.hpp" +#include "barretenberg/dsl/types.hpp" +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" +#include "barretenberg/plonk_honk_shared/arithmetization/gate_data.hpp" +#include "barretenberg/stdlib/primitives/biggroup/biggroup.hpp" + +namespace acir_format { + +template void create_multi_scalar_mul_constraint(Builder& builder, const MultiScalarMul& input) +{ + using cycle_group_ct = bb::stdlib::cycle_group; + using cycle_scalar_ct = typename bb::stdlib::cycle_group::cycle_scalar; + using field_ct = bb::stdlib::field_t; + + std::vector points; + std::vector scalars; + + for (size_t i = 0; i < input.points.size(); i += 2) { + // Instantiate the input point/variable base as `cycle_group_ct` + auto point_x = field_ct::from_witness_index(&builder, input.points[i]); + auto point_y = field_ct::from_witness_index(&builder, input.points[i + 1]); + cycle_group_ct input_point(point_x, point_y, false); + + // Reconstruct the scalar from the low and high limbs + field_ct scalar_low_as_field = field_ct::from_witness_index(&builder, input.scalars[i]); + field_ct scalar_high_as_field = field_ct::from_witness_index(&builder, input.scalars[i + 1]); + cycle_scalar_ct scalar(scalar_low_as_field, scalar_high_as_field); + + // Add the point and scalar to the vectors + points.push_back(input_point); + scalars.push_back(scalar); + } + + // Call batch_mul to multiply the points and scalars and sum the results + auto output_point = cycle_group_ct::batch_mul(scalars, points); + + // Add the constraints + builder.assert_equal(output_point.x.get_witness_index(), input.out_point_x); + builder.assert_equal(output_point.y.get_witness_index(), input.out_point_y); +} + +template void create_multi_scalar_mul_constraint(UltraCircuitBuilder& builder, + const MultiScalarMul& input); +template void create_multi_scalar_mul_constraint(GoblinUltraCircuitBuilder& builder, + const MultiScalarMul& input); + +} // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.hpp new file mode 100644 index 00000000000..12b070076f9 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.hpp @@ -0,0 +1,21 @@ +#pragma once +#include "barretenberg/dsl/types.hpp" +#include "barretenberg/serialize/msgpack.hpp" +#include + +namespace acir_format { + +struct MultiScalarMul { + std::vector points; + std::vector scalars; + uint32_t out_point_x; + uint32_t out_point_y; + + // for serialization, update with any new fields + MSGPACK_FIELDS(points, scalars, out_point_x, out_point_y); + friend bool operator==(MultiScalarMul const& lhs, MultiScalarMul const& rhs) = default; +}; + +template void create_multi_scalar_mul_constraint(Builder& builder, const MultiScalarMul& input); + +} // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp index f672505b4d7..ee230848b55 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp @@ -46,8 +46,7 @@ TEST_F(Poseidon2Tests, TestPoseidon2Permutation) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = { poseidon2_constraint }, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 97e53d30c62..031095f95be 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -98,8 +98,7 @@ Builder create_inner_circuit() .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, @@ -256,8 +255,7 @@ Builder create_outer_circuit(std::vector& inner_circuits) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = recursion_constraints, .bigint_from_le_bytes_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp index bdfb6605ad2..561e7021683 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp @@ -135,26 +135,14 @@ struct BlackBoxFuncCall { static EcdsaSecp256r1 bincodeDeserialize(std::vector); }; - struct FixedBaseScalarMul { - Program::FunctionInput low; - Program::FunctionInput high; + struct MultiScalarMul { + std::vector points; + std::vector scalars; std::array outputs; - friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); + friend bool operator==(const MultiScalarMul&, const MultiScalarMul&); std::vector bincodeSerialize() const; - static FixedBaseScalarMul bincodeDeserialize(std::vector); - }; - - struct VariableBaseScalarMul { - Program::FunctionInput point_x; - Program::FunctionInput point_y; - Program::FunctionInput scalar_low; - Program::FunctionInput scalar_high; - std::array outputs; - - friend bool operator==(const VariableBaseScalarMul&, const VariableBaseScalarMul&); - std::vector bincodeSerialize() const; - static VariableBaseScalarMul bincodeDeserialize(std::vector); + static MultiScalarMul bincodeDeserialize(std::vector); }; struct EmbeddedCurveAdd { @@ -289,8 +277,7 @@ struct BlackBoxFuncCall { PedersenHash, EcdsaSecp256k1, EcdsaSecp256r1, - FixedBaseScalarMul, - VariableBaseScalarMul, + MultiScalarMul, EmbeddedCurveAdd, Keccak256, Keccakf1600, @@ -756,26 +743,14 @@ struct BlackBoxOp { static PedersenHash bincodeDeserialize(std::vector); }; - struct FixedBaseScalarMul { - Program::MemoryAddress low; - Program::MemoryAddress high; - Program::HeapArray result; - - friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); - std::vector bincodeSerialize() const; - static FixedBaseScalarMul bincodeDeserialize(std::vector); - }; - - struct VariableBaseScalarMul { - Program::MemoryAddress point_x; - Program::MemoryAddress point_y; - Program::MemoryAddress scalar_low; - Program::MemoryAddress scalar_high; - Program::HeapArray result; + struct MultiScalarMul { + Program::HeapVector points; + Program::HeapVector scalars; + Program::HeapArray outputs; - friend bool operator==(const VariableBaseScalarMul&, const VariableBaseScalarMul&); + friend bool operator==(const MultiScalarMul&, const MultiScalarMul&); std::vector bincodeSerialize() const; - static VariableBaseScalarMul bincodeDeserialize(std::vector); + static MultiScalarMul bincodeDeserialize(std::vector); }; struct EmbeddedCurveAdd { @@ -879,8 +854,7 @@ struct BlackBoxOp { SchnorrVerify, PedersenCommitment, PedersenHash, - FixedBaseScalarMul, - VariableBaseScalarMul, + MultiScalarMul, EmbeddedCurveAdd, BigIntAdd, BigIntSub, @@ -3036,77 +3010,12 @@ Program::BlackBoxFuncCall::EcdsaSecp256r1 serde::Deserializable BlackBoxFuncCall::FixedBaseScalarMul::bincodeSerialize() const -{ - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); -} - -inline BlackBoxFuncCall::FixedBaseScalarMul BlackBoxFuncCall::FixedBaseScalarMul::bincodeDeserialize( - std::vector input) -{ - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw_or_abort("Some input bytes were not read"); - } - return value; -} - -} // end of namespace Program - -template <> -template -void serde::Serializable::serialize( - const Program::BlackBoxFuncCall::FixedBaseScalarMul& obj, Serializer& serializer) -{ - serde::Serializable::serialize(obj.low, serializer); - serde::Serializable::serialize(obj.high, serializer); - serde::Serializable::serialize(obj.outputs, serializer); -} - -template <> -template -Program::BlackBoxFuncCall::FixedBaseScalarMul serde::Deserializable< - Program::BlackBoxFuncCall::FixedBaseScalarMul>::deserialize(Deserializer& deserializer) -{ - Program::BlackBoxFuncCall::FixedBaseScalarMul obj; - obj.low = serde::Deserializable::deserialize(deserializer); - obj.high = serde::Deserializable::deserialize(deserializer); - obj.outputs = serde::Deserializable::deserialize(deserializer); - return obj; -} - -namespace Program { - -inline bool operator==(const BlackBoxFuncCall::VariableBaseScalarMul& lhs, - const BlackBoxFuncCall::VariableBaseScalarMul& rhs) +inline bool operator==(const BlackBoxFuncCall::MultiScalarMul& lhs, const BlackBoxFuncCall::MultiScalarMul& rhs) { - if (!(lhs.point_x == rhs.point_x)) { - return false; - } - if (!(lhs.point_y == rhs.point_y)) { - return false; - } - if (!(lhs.scalar_low == rhs.scalar_low)) { + if (!(lhs.points == rhs.points)) { return false; } - if (!(lhs.scalar_high == rhs.scalar_high)) { + if (!(lhs.scalars == rhs.scalars)) { return false; } if (!(lhs.outputs == rhs.outputs)) { @@ -3115,18 +3024,17 @@ inline bool operator==(const BlackBoxFuncCall::VariableBaseScalarMul& lhs, return true; } -inline std::vector BlackBoxFuncCall::VariableBaseScalarMul::bincodeSerialize() const +inline std::vector BlackBoxFuncCall::MultiScalarMul::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxFuncCall::VariableBaseScalarMul BlackBoxFuncCall::VariableBaseScalarMul::bincodeDeserialize( - std::vector input) +inline BlackBoxFuncCall::MultiScalarMul BlackBoxFuncCall::MultiScalarMul::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -3137,26 +3045,22 @@ inline BlackBoxFuncCall::VariableBaseScalarMul BlackBoxFuncCall::VariableBaseSca template <> template -void serde::Serializable::serialize( - const Program::BlackBoxFuncCall::VariableBaseScalarMul& obj, Serializer& serializer) +void serde::Serializable::serialize( + const Program::BlackBoxFuncCall::MultiScalarMul& obj, Serializer& serializer) { - serde::Serializable::serialize(obj.point_x, serializer); - serde::Serializable::serialize(obj.point_y, serializer); - serde::Serializable::serialize(obj.scalar_low, serializer); - serde::Serializable::serialize(obj.scalar_high, serializer); + serde::Serializable::serialize(obj.points, serializer); + serde::Serializable::serialize(obj.scalars, serializer); serde::Serializable::serialize(obj.outputs, serializer); } template <> template -Program::BlackBoxFuncCall::VariableBaseScalarMul serde::Deserializable< - Program::BlackBoxFuncCall::VariableBaseScalarMul>::deserialize(Deserializer& deserializer) +Program::BlackBoxFuncCall::MultiScalarMul serde::Deserializable::deserialize( + Deserializer& deserializer) { - Program::BlackBoxFuncCall::VariableBaseScalarMul obj; - obj.point_x = serde::Deserializable::deserialize(deserializer); - obj.point_y = serde::Deserializable::deserialize(deserializer); - obj.scalar_low = serde::Deserializable::deserialize(deserializer); - obj.scalar_high = serde::Deserializable::deserialize(deserializer); + Program::BlackBoxFuncCall::MultiScalarMul obj; + obj.points = serde::Deserializable::deserialize(deserializer); + obj.scalars = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } @@ -4482,31 +4386,31 @@ Program::BlackBoxOp::PedersenHash serde::Deserializable BlackBoxOp::FixedBaseScalarMul::bincodeSerialize() const +inline std::vector BlackBoxOp::MultiScalarMul::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp::FixedBaseScalarMul BlackBoxOp::FixedBaseScalarMul::bincodeDeserialize(std::vector input) +inline BlackBoxOp::MultiScalarMul BlackBoxOp::MultiScalarMul::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -4517,91 +4421,23 @@ inline BlackBoxOp::FixedBaseScalarMul BlackBoxOp::FixedBaseScalarMul::bincodeDes template <> template -void serde::Serializable::serialize( - const Program::BlackBoxOp::FixedBaseScalarMul& obj, Serializer& serializer) +void serde::Serializable::serialize(const Program::BlackBoxOp::MultiScalarMul& obj, + Serializer& serializer) { - serde::Serializable::serialize(obj.low, serializer); - serde::Serializable::serialize(obj.high, serializer); - serde::Serializable::serialize(obj.result, serializer); + serde::Serializable::serialize(obj.points, serializer); + serde::Serializable::serialize(obj.scalars, serializer); + serde::Serializable::serialize(obj.outputs, serializer); } template <> template -Program::BlackBoxOp::FixedBaseScalarMul serde::Deserializable::deserialize( +Program::BlackBoxOp::MultiScalarMul serde::Deserializable::deserialize( Deserializer& deserializer) { - Program::BlackBoxOp::FixedBaseScalarMul obj; - obj.low = serde::Deserializable::deserialize(deserializer); - obj.high = serde::Deserializable::deserialize(deserializer); - obj.result = serde::Deserializable::deserialize(deserializer); - return obj; -} - -namespace Program { - -inline bool operator==(const BlackBoxOp::VariableBaseScalarMul& lhs, const BlackBoxOp::VariableBaseScalarMul& rhs) -{ - if (!(lhs.point_x == rhs.point_x)) { - return false; - } - if (!(lhs.point_y == rhs.point_y)) { - return false; - } - if (!(lhs.scalar_low == rhs.scalar_low)) { - return false; - } - if (!(lhs.scalar_high == rhs.scalar_high)) { - return false; - } - if (!(lhs.result == rhs.result)) { - return false; - } - return true; -} - -inline std::vector BlackBoxOp::VariableBaseScalarMul::bincodeSerialize() const -{ - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); -} - -inline BlackBoxOp::VariableBaseScalarMul BlackBoxOp::VariableBaseScalarMul::bincodeDeserialize( - std::vector input) -{ - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw_or_abort("Some input bytes were not read"); - } - return value; -} - -} // end of namespace Program - -template <> -template -void serde::Serializable::serialize( - const Program::BlackBoxOp::VariableBaseScalarMul& obj, Serializer& serializer) -{ - serde::Serializable::serialize(obj.point_x, serializer); - serde::Serializable::serialize(obj.point_y, serializer); - serde::Serializable::serialize(obj.scalar_low, serializer); - serde::Serializable::serialize(obj.scalar_high, serializer); - serde::Serializable::serialize(obj.result, serializer); -} - -template <> -template -Program::BlackBoxOp::VariableBaseScalarMul serde::Deserializable< - Program::BlackBoxOp::VariableBaseScalarMul>::deserialize(Deserializer& deserializer) -{ - Program::BlackBoxOp::VariableBaseScalarMul obj; - obj.point_x = serde::Deserializable::deserialize(deserializer); - obj.point_y = serde::Deserializable::deserialize(deserializer); - obj.scalar_low = serde::Deserializable::deserialize(deserializer); - obj.scalar_high = serde::Deserializable::deserialize(deserializer); - obj.result = serde::Deserializable::deserialize(deserializer); + Program::BlackBoxOp::MultiScalarMul obj; + obj.points = serde::Deserializable::deserialize(deserializer); + obj.scalars = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp index aa520806b2b..54457630b67 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp @@ -48,8 +48,7 @@ TEST_F(Sha256Tests, TestSha256Compression) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .poseidon2_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .variable_base_scalar_mul_constraints = {}, + .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/variable_base_scalar_mul.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/variable_base_scalar_mul.cpp deleted file mode 100644 index 6446b68158c..00000000000 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/variable_base_scalar_mul.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "variable_base_scalar_mul.hpp" -#include "barretenberg/dsl/types.hpp" -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" -#include "barretenberg/plonk_honk_shared/arithmetization/gate_data.hpp" - -namespace acir_format { - -template void create_variable_base_constraint(Builder& builder, const VariableBaseScalarMul& input) -{ - using cycle_group_ct = bb::stdlib::cycle_group; - using cycle_scalar_ct = typename bb::stdlib::cycle_group::cycle_scalar; - using field_ct = bb::stdlib::field_t; - - // We instantiate the input point/variable base as `cycle_group_ct` - auto point_x = field_ct::from_witness_index(&builder, input.point_x); - auto point_y = field_ct::from_witness_index(&builder, input.point_y); - cycle_group_ct input_point(point_x, point_y, false); - - // We reconstruct the scalar from the low and high limbs - field_ct scalar_low_as_field = field_ct::from_witness_index(&builder, input.scalar_low); - field_ct scalar_high_as_field = field_ct::from_witness_index(&builder, input.scalar_high); - cycle_scalar_ct scalar(scalar_low_as_field, scalar_high_as_field); - - // We multiply the scalar with input point/variable base to get the result - auto result = input_point * scalar; - - // Finally we add the constraints - builder.assert_equal(result.x.get_witness_index(), input.out_point_x); - builder.assert_equal(result.y.get_witness_index(), input.out_point_y); -} - -template void create_variable_base_constraint(UltraCircuitBuilder& builder, - const VariableBaseScalarMul& input); -template void create_variable_base_constraint(GoblinUltraCircuitBuilder& builder, - const VariableBaseScalarMul& input); - -} // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/variable_base_scalar_mul.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/variable_base_scalar_mul.hpp deleted file mode 100644 index d903df2cb32..00000000000 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/variable_base_scalar_mul.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "barretenberg/dsl/types.hpp" -#include "barretenberg/serialize/msgpack.hpp" -#include - -namespace acir_format { - -struct VariableBaseScalarMul { - uint32_t point_x; - uint32_t point_y; - uint32_t scalar_low; - uint32_t scalar_high; - uint32_t out_point_x; - uint32_t out_point_y; - - // for serialization, update with any new fields - MSGPACK_FIELDS(point_x, point_y, scalar_low, scalar_high, out_point_x, out_point_y); - friend bool operator==(VariableBaseScalarMul const& lhs, VariableBaseScalarMul const& rhs) = default; -}; - -template void create_variable_base_constraint(Builder& builder, const VariableBaseScalarMul& input); - -} // namespace acir_format diff --git a/barretenberg/ts/src/info.ts b/barretenberg/ts/src/info.ts index 6032427bdd5..04e33525400 100644 --- a/barretenberg/ts/src/info.ts +++ b/barretenberg/ts/src/info.ts @@ -17,7 +17,7 @@ export const acvmInfoJson = { 'pedersen_hash', 'ecdsa_secp256k1', 'ecdsa_secp256r1', - 'fixed_base_scalar_mul', + 'multi_scalar_mul', 'recursive_aggregation', ], }; diff --git a/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr b/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr index 488df346e73..905a049f9d5 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr @@ -1,10 +1,10 @@ use dep::protocol_types::{constants::GENERATOR_INDEX__SYMMETRIC_KEY, grumpkin_private_key::GrumpkinPrivateKey, grumpkin_point::GrumpkinPoint, utils::arr_copy_slice}; -use dep::std::{hash::sha256, grumpkin_scalar::GrumpkinScalar, scalar_mul::variable_base_embedded_curve}; +use dep::std::{hash::sha256, grumpkin_scalar::GrumpkinScalar, embedded_curve_ops::multi_scalar_mul}; // TODO(#5726): This function is called deriveAESSecret in TS. I don't like point_to_symmetric_key name much since // point is not the only input of the function. Unify naming with TS once we have a better name. pub fn point_to_symmetric_key(secret: GrumpkinPrivateKey, point: GrumpkinPoint) -> [u8; 32] { - let shared_secret_fields = variable_base_embedded_curve(point.x, point.y, secret.low, secret.high); + let shared_secret_fields = multi_scalar_mul([point.x, point.y], [secret.low, secret.high]); // TODO(https://github.com/AztecProtocol/aztec-packages/issues/6061): make the func return Point struct directly let shared_secret = GrumpkinPoint::new(shared_secret_fields[0], shared_secret_fields[1]); let mut shared_secret_bytes_with_separator = [0 as u8; 65]; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/grumpkin_private_key.nr b/noir-projects/noir-protocol-circuits/crates/types/src/grumpkin_private_key.nr index 23b6dddd143..20b43f7aebc 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/grumpkin_private_key.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/grumpkin_private_key.nr @@ -1,4 +1,4 @@ -use dep::std::{cmp::Eq, grumpkin_scalar::GrumpkinScalar, grumpkin_scalar_mul::grumpkin_fixed_base}; +use dep::std::{cmp::Eq, grumpkin_scalar::GrumpkinScalar, embedded_curve_ops::fixed_base_scalar_mul}; use crate::{ grumpkin_point::GrumpkinPoint, traits::Empty @@ -41,7 +41,7 @@ impl GrumpkinPrivateKey { } pub fn derive_public_key(self) -> GrumpkinPoint { - let public_key = grumpkin_fixed_base(GrumpkinScalar { high: self.high, low: self.low }); + let public_key = fixed_base_scalar_mul(self.low, self.high); GrumpkinPoint { x: public_key[0], y: public_key[1] } } } diff --git a/noir/noir-repo/acvm-repo/acir/README.md b/noir/noir-repo/acvm-repo/acir/README.md index e72f7ea178d..838c0b9dc36 100644 --- a/noir/noir-repo/acvm-repo/acir/README.md +++ b/noir/noir-repo/acvm-repo/acir/README.md @@ -139,18 +139,11 @@ Inputs and outputs are similar to SchnorrVerify, except that because we use a di **EcdsaSecp256r1**: Same as EcdsaSecp256k1, but done over another curve. -**FixedBaseScalarMul**: scalar multiplication with a fixed generator of the embedded curve -- input: low, high are 2 (field , 254), representing the low and high part of the input. For Barretenberg, they must both be less than 128 bits. -- output: x and y coordinates of $low*G+high*2^{128}*G$, where G is a fixed generator - -Because the Grumpkin scalar field is bigger than the ACIR field, we provide 2 ACIR fields representing the low and high parts of the Grumpkin scalar $a$: -$a=low+high*2^{128},$ with $low, high < 2^{128}$ - -**VariableBaseScalarMul**: scalar multiplication with a variable base/input point (P) of the embedded curve +**MultiScalarMul**: scalar multiplication with a variable base/input point (P) of the embedded curve - input: - point_x, point_y representing x and y coordinates of input point P - scalar_low, scalar_high are 2 (field , 254), representing the low and high part of the input scalar. For Barretenberg, they must both be less than 128 bits. -- output: x and y coordinates of $low*P+high*2^{128}*P$, where P is the input point P + points (FieldElement, N) a vector of x and y coordinates of input points [x1, y1, x2, y2,...]. + scalars (FieldElement, N) a vector of low and high limbs of input scalars [s1_low, s1_high, s2_low, s2_high, ...]. (FieldElement, N) For Barretenberg, they must both be less than 128 bits. +- output: (FieldElement, N) a vector of x and y coordinates of output points [op1_x, op1_y, op2_x, op2_y, ...]. Points computed as $s_low*P+s_high*2^{128}*P$ Because the Grumpkin scalar field is bigger than the ACIR field, we provide 2 ACIR fields representing the low and high parts of the Grumpkin scalar $a$: $a=low+high*2^{128},$ with $low, high < 2^{128}$ diff --git a/noir/noir-repo/acvm-repo/acir/codegen/acir.cpp b/noir/noir-repo/acvm-repo/acir/codegen/acir.cpp index 1e5207c01cb..10015ce18bb 100644 --- a/noir/noir-repo/acvm-repo/acir/codegen/acir.cpp +++ b/noir/noir-repo/acvm-repo/acir/codegen/acir.cpp @@ -135,26 +135,14 @@ namespace Program { static EcdsaSecp256r1 bincodeDeserialize(std::vector); }; - struct FixedBaseScalarMul { - Program::FunctionInput low; - Program::FunctionInput high; + struct MultiScalarMul { + std::vector points; + std::vector scalars; std::array outputs; - friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); + friend bool operator==(const MultiScalarMul&, const MultiScalarMul&); std::vector bincodeSerialize() const; - static FixedBaseScalarMul bincodeDeserialize(std::vector); - }; - - struct VariableBaseScalarMul { - Program::FunctionInput point_x; - Program::FunctionInput point_y; - Program::FunctionInput scalar_low; - Program::FunctionInput scalar_high; - std::array outputs; - - friend bool operator==(const VariableBaseScalarMul&, const VariableBaseScalarMul&); - std::vector bincodeSerialize() const; - static VariableBaseScalarMul bincodeDeserialize(std::vector); + static MultiScalarMul bincodeDeserialize(std::vector); }; struct EmbeddedCurveAdd { @@ -278,7 +266,7 @@ namespace Program { static Sha256Compression bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); std::vector bincodeSerialize() const; @@ -731,26 +719,14 @@ namespace Program { static PedersenHash bincodeDeserialize(std::vector); }; - struct FixedBaseScalarMul { - Program::MemoryAddress low; - Program::MemoryAddress high; - Program::HeapArray result; - - friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); - std::vector bincodeSerialize() const; - static FixedBaseScalarMul bincodeDeserialize(std::vector); - }; - - struct VariableBaseScalarMul { - Program::MemoryAddress point_x; - Program::MemoryAddress point_y; - Program::MemoryAddress scalar_low; - Program::MemoryAddress scalar_high; - Program::HeapArray result; + struct MultiScalarMul { + Program::HeapVector points; + Program::HeapVector scalars; + Program::HeapArray outputs; - friend bool operator==(const VariableBaseScalarMul&, const VariableBaseScalarMul&); + friend bool operator==(const MultiScalarMul&, const MultiScalarMul&); std::vector bincodeSerialize() const; - static VariableBaseScalarMul bincodeDeserialize(std::vector); + static MultiScalarMul bincodeDeserialize(std::vector); }; struct EmbeddedCurveAdd { @@ -844,7 +820,7 @@ namespace Program { static Sha256Compression bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BlackBoxOp&, const BlackBoxOp&); std::vector bincodeSerialize() const; @@ -2672,22 +2648,22 @@ Program::BlackBoxFuncCall::EcdsaSecp256r1 serde::Deserializable BlackBoxFuncCall::FixedBaseScalarMul::bincodeSerialize() const { + inline std::vector BlackBoxFuncCall::MultiScalarMul::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxFuncCall::FixedBaseScalarMul BlackBoxFuncCall::FixedBaseScalarMul::bincodeDeserialize(std::vector input) { + inline BlackBoxFuncCall::MultiScalarMul BlackBoxFuncCall::MultiScalarMul::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2698,68 +2674,18 @@ namespace Program { template <> template -void serde::Serializable::serialize(const Program::BlackBoxFuncCall::FixedBaseScalarMul &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.low, serializer); - serde::Serializable::serialize(obj.high, serializer); +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::MultiScalarMul &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.points, serializer); + serde::Serializable::serialize(obj.scalars, serializer); serde::Serializable::serialize(obj.outputs, serializer); } template <> template -Program::BlackBoxFuncCall::FixedBaseScalarMul serde::Deserializable::deserialize(Deserializer &deserializer) { - Program::BlackBoxFuncCall::FixedBaseScalarMul obj; - obj.low = serde::Deserializable::deserialize(deserializer); - obj.high = serde::Deserializable::deserialize(deserializer); - obj.outputs = serde::Deserializable::deserialize(deserializer); - return obj; -} - -namespace Program { - - inline bool operator==(const BlackBoxFuncCall::VariableBaseScalarMul &lhs, const BlackBoxFuncCall::VariableBaseScalarMul &rhs) { - if (!(lhs.point_x == rhs.point_x)) { return false; } - if (!(lhs.point_y == rhs.point_y)) { return false; } - if (!(lhs.scalar_low == rhs.scalar_low)) { return false; } - if (!(lhs.scalar_high == rhs.scalar_high)) { return false; } - if (!(lhs.outputs == rhs.outputs)) { return false; } - return true; - } - - inline std::vector BlackBoxFuncCall::VariableBaseScalarMul::bincodeSerialize() const { - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); - } - - inline BlackBoxFuncCall::VariableBaseScalarMul BlackBoxFuncCall::VariableBaseScalarMul::bincodeDeserialize(std::vector input) { - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw serde::deserialization_error("Some input bytes were not read"); - } - return value; - } - -} // end of namespace Program - -template <> -template -void serde::Serializable::serialize(const Program::BlackBoxFuncCall::VariableBaseScalarMul &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.point_x, serializer); - serde::Serializable::serialize(obj.point_y, serializer); - serde::Serializable::serialize(obj.scalar_low, serializer); - serde::Serializable::serialize(obj.scalar_high, serializer); - serde::Serializable::serialize(obj.outputs, serializer); -} - -template <> -template -Program::BlackBoxFuncCall::VariableBaseScalarMul serde::Deserializable::deserialize(Deserializer &deserializer) { - Program::BlackBoxFuncCall::VariableBaseScalarMul obj; - obj.point_x = serde::Deserializable::deserialize(deserializer); - obj.point_y = serde::Deserializable::deserialize(deserializer); - obj.scalar_low = serde::Deserializable::deserialize(deserializer); - obj.scalar_high = serde::Deserializable::deserialize(deserializer); +Program::BlackBoxFuncCall::MultiScalarMul serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::MultiScalarMul obj; + obj.points = serde::Deserializable::deserialize(deserializer); + obj.scalars = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } @@ -3782,68 +3708,22 @@ Program::BlackBoxOp::PedersenHash serde::Deserializable BlackBoxOp::FixedBaseScalarMul::bincodeSerialize() const { - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); - } - - inline BlackBoxOp::FixedBaseScalarMul BlackBoxOp::FixedBaseScalarMul::bincodeDeserialize(std::vector input) { - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw serde::deserialization_error("Some input bytes were not read"); - } - return value; - } - -} // end of namespace Program - -template <> -template -void serde::Serializable::serialize(const Program::BlackBoxOp::FixedBaseScalarMul &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.low, serializer); - serde::Serializable::serialize(obj.high, serializer); - serde::Serializable::serialize(obj.result, serializer); -} - -template <> -template -Program::BlackBoxOp::FixedBaseScalarMul serde::Deserializable::deserialize(Deserializer &deserializer) { - Program::BlackBoxOp::FixedBaseScalarMul obj; - obj.low = serde::Deserializable::deserialize(deserializer); - obj.high = serde::Deserializable::deserialize(deserializer); - obj.result = serde::Deserializable::deserialize(deserializer); - return obj; -} - -namespace Program { - - inline bool operator==(const BlackBoxOp::VariableBaseScalarMul &lhs, const BlackBoxOp::VariableBaseScalarMul &rhs) { - if (!(lhs.point_x == rhs.point_x)) { return false; } - if (!(lhs.point_y == rhs.point_y)) { return false; } - if (!(lhs.scalar_low == rhs.scalar_low)) { return false; } - if (!(lhs.scalar_high == rhs.scalar_high)) { return false; } - if (!(lhs.result == rhs.result)) { return false; } + inline bool operator==(const BlackBoxOp::MultiScalarMul &lhs, const BlackBoxOp::MultiScalarMul &rhs) { + if (!(lhs.points == rhs.points)) { return false; } + if (!(lhs.scalars == rhs.scalars)) { return false; } + if (!(lhs.outputs == rhs.outputs)) { return false; } return true; } - inline std::vector BlackBoxOp::VariableBaseScalarMul::bincodeSerialize() const { + inline std::vector BlackBoxOp::MultiScalarMul::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::VariableBaseScalarMul BlackBoxOp::VariableBaseScalarMul::bincodeDeserialize(std::vector input) { + inline BlackBoxOp::MultiScalarMul BlackBoxOp::MultiScalarMul::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -3854,23 +3734,19 @@ namespace Program { template <> template -void serde::Serializable::serialize(const Program::BlackBoxOp::VariableBaseScalarMul &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.point_x, serializer); - serde::Serializable::serialize(obj.point_y, serializer); - serde::Serializable::serialize(obj.scalar_low, serializer); - serde::Serializable::serialize(obj.scalar_high, serializer); - serde::Serializable::serialize(obj.result, serializer); +void serde::Serializable::serialize(const Program::BlackBoxOp::MultiScalarMul &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.points, serializer); + serde::Serializable::serialize(obj.scalars, serializer); + serde::Serializable::serialize(obj.outputs, serializer); } template <> template -Program::BlackBoxOp::VariableBaseScalarMul serde::Deserializable::deserialize(Deserializer &deserializer) { - Program::BlackBoxOp::VariableBaseScalarMul obj; - obj.point_x = serde::Deserializable::deserialize(deserializer); - obj.point_y = serde::Deserializable::deserialize(deserializer); - obj.scalar_low = serde::Deserializable::deserialize(deserializer); - obj.scalar_high = serde::Deserializable::deserialize(deserializer); - obj.result = serde::Deserializable::deserialize(deserializer); +Program::BlackBoxOp::MultiScalarMul serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::MultiScalarMul obj; + obj.points = serde::Deserializable::deserialize(deserializer); + obj.scalars = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } 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 9a43702a408..53c68debce1 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 @@ -36,10 +36,8 @@ pub enum BlackBoxFunc { EcdsaSecp256k1, /// Verifies a ECDSA signature over the secp256r1 curve. EcdsaSecp256r1, - /// Performs scalar multiplication over the embedded curve on which [`FieldElement`][acir_field::FieldElement] is defined and a fixed base/generator point G1. - FixedBaseScalarMul, - /// Performs scalar multiplication over the embedded curve on which [`FieldElement`][acir_field::FieldElement] is defined and a variable base/input point P. - VariableBaseScalarMul, + /// Performs multi scalar multiplication over the embedded curve. + MultiScalarMul, /// Calculates the Keccak256 hash of the inputs. Keccak256, /// Keccak Permutation function of 1600 width @@ -83,8 +81,7 @@ impl BlackBoxFunc { BlackBoxFunc::PedersenCommitment => "pedersen_commitment", BlackBoxFunc::PedersenHash => "pedersen_hash", BlackBoxFunc::EcdsaSecp256k1 => "ecdsa_secp256k1", - BlackBoxFunc::FixedBaseScalarMul => "fixed_base_scalar_mul", - BlackBoxFunc::VariableBaseScalarMul => "variable_base_scalar_mul", + BlackBoxFunc::MultiScalarMul => "multi_scalar_mul", BlackBoxFunc::EmbeddedCurveAdd => "embedded_curve_add", BlackBoxFunc::AND => "and", BlackBoxFunc::XOR => "xor", @@ -114,8 +111,7 @@ impl BlackBoxFunc { "pedersen_hash" => Some(BlackBoxFunc::PedersenHash), "ecdsa_secp256k1" => Some(BlackBoxFunc::EcdsaSecp256k1), "ecdsa_secp256r1" => Some(BlackBoxFunc::EcdsaSecp256r1), - "fixed_base_scalar_mul" => Some(BlackBoxFunc::FixedBaseScalarMul), - "variable_base_scalar_mul" => Some(BlackBoxFunc::VariableBaseScalarMul), + "multi_scalar_mul" => Some(BlackBoxFunc::MultiScalarMul), "embedded_curve_add" => Some(BlackBoxFunc::EmbeddedCurveAdd), "and" => Some(BlackBoxFunc::AND), "xor" => Some(BlackBoxFunc::XOR), diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index 5715019937c..51b2ca9d51f 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -80,16 +80,9 @@ pub enum BlackBoxFuncCall { hashed_message: Box<[FunctionInput; 32]>, output: Witness, }, - FixedBaseScalarMul { - low: FunctionInput, - high: FunctionInput, - outputs: (Witness, Witness), - }, - VariableBaseScalarMul { - point_x: FunctionInput, - point_y: FunctionInput, - scalar_low: FunctionInput, - scalar_high: FunctionInput, + MultiScalarMul { + points: Vec, + scalars: Vec, outputs: (Witness, Witness), }, EmbeddedCurveAdd { @@ -195,8 +188,7 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::PedersenHash { .. } => BlackBoxFunc::PedersenHash, BlackBoxFuncCall::EcdsaSecp256k1 { .. } => BlackBoxFunc::EcdsaSecp256k1, BlackBoxFuncCall::EcdsaSecp256r1 { .. } => BlackBoxFunc::EcdsaSecp256r1, - BlackBoxFuncCall::FixedBaseScalarMul { .. } => BlackBoxFunc::FixedBaseScalarMul, - BlackBoxFuncCall::VariableBaseScalarMul { .. } => BlackBoxFunc::VariableBaseScalarMul, + BlackBoxFuncCall::MultiScalarMul { .. } => BlackBoxFunc::MultiScalarMul, BlackBoxFuncCall::EmbeddedCurveAdd { .. } => BlackBoxFunc::EmbeddedCurveAdd, BlackBoxFuncCall::Keccak256 { .. } => BlackBoxFunc::Keccak256, BlackBoxFuncCall::Keccakf1600 { .. } => BlackBoxFunc::Keccakf1600, @@ -239,15 +231,11 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::BigIntMul { .. } | BlackBoxFuncCall::BigIntDiv { .. } | BlackBoxFuncCall::BigIntToLeBytes { .. } => Vec::new(), - BlackBoxFuncCall::FixedBaseScalarMul { low, high, .. } => vec![*low, *high], - BlackBoxFuncCall::VariableBaseScalarMul { - point_x, - point_y, - scalar_low, - scalar_high, - .. - } => { - vec![*point_x, *point_y, *scalar_low, *scalar_high] + BlackBoxFuncCall::MultiScalarMul { points, scalars, .. } => { + let mut inputs: Vec = Vec::with_capacity(points.len() * 2); + inputs.extend(points.iter().copied()); + inputs.extend(scalars.iter().copied()); + inputs } BlackBoxFuncCall::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, .. @@ -260,7 +248,8 @@ impl BlackBoxFuncCall { message, .. } => { - let mut inputs = Vec::with_capacity(2 + signature.len() + message.len()); + let mut inputs: Vec = + Vec::with_capacity(2 + signature.len() + message.len()); inputs.push(*public_key_x); inputs.push(*public_key_y); inputs.extend(signature.iter().copied()); @@ -345,8 +334,7 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::EcdsaSecp256k1 { output, .. } | BlackBoxFuncCall::PedersenHash { output, .. } | BlackBoxFuncCall::EcdsaSecp256r1 { output, .. } => vec![*output], - BlackBoxFuncCall::FixedBaseScalarMul { outputs, .. } - | BlackBoxFuncCall::VariableBaseScalarMul { outputs, .. } + BlackBoxFuncCall::MultiScalarMul { outputs, .. } | BlackBoxFuncCall::PedersenCommitment { outputs, .. } | BlackBoxFuncCall::EmbeddedCurveAdd { outputs, .. } => vec![outputs.0, outputs.1], BlackBoxFuncCall::RANGE { .. } diff --git a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs index 2ad082410a1..63cba788c02 100644 --- a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs +++ b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs @@ -58,47 +58,22 @@ fn addition_circuit() { } #[test] -fn fixed_base_scalar_mul_circuit() { - let fixed_base_scalar_mul = Opcode::BlackBoxFuncCall(BlackBoxFuncCall::FixedBaseScalarMul { - low: FunctionInput { witness: Witness(1), num_bits: 128 }, - high: FunctionInput { witness: Witness(2), num_bits: 128 }, - outputs: (Witness(3), Witness(4)), +fn multi_scalar_mul_circuit() { + let multi_scalar_mul = Opcode::BlackBoxFuncCall(BlackBoxFuncCall::MultiScalarMul { + points: vec![ + FunctionInput { witness: Witness(1), num_bits: 128 }, + FunctionInput { witness: Witness(2), num_bits: 128 }, + ], + scalars: vec![ + FunctionInput { witness: Witness(3), num_bits: 128 }, + FunctionInput { witness: Witness(4), num_bits: 128 }, + ], + outputs: (Witness(5), Witness(6)), }); - let circuit = Circuit { - current_witness_index: 5, - opcodes: vec![fixed_base_scalar_mul], - private_parameters: BTreeSet::from([Witness(1), Witness(2)]), - return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(3), Witness(4)])), - ..Circuit::default() - }; - let program = Program { functions: vec![circuit], unconstrained_functions: vec![] }; - - let bytes = Program::serialize_program(&program); - - let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 85, 138, 81, 10, 0, 48, 8, 66, 87, 219, 190, 118, 233, - 29, 61, 35, 3, 19, 228, 137, 60, 91, 149, 139, 26, 119, 242, 145, 31, 117, 114, 163, 135, - 142, 139, 219, 91, 127, 117, 71, 2, 117, 84, 50, 98, 113, 0, 0, 0, - ]; - - assert_eq!(bytes, expected_serialization) -} - -#[test] -fn variable_base_scalar_mul_circuit() { - let variable_base_scalar_mul = - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::VariableBaseScalarMul { - point_x: FunctionInput { witness: Witness(1), num_bits: 128 }, - point_y: FunctionInput { witness: Witness(2), num_bits: 128 }, - scalar_low: FunctionInput { witness: Witness(3), num_bits: 128 }, - scalar_high: FunctionInput { witness: Witness(4), num_bits: 128 }, - outputs: (Witness(5), Witness(6)), - }); - let circuit = Circuit { current_witness_index: 7, - opcodes: vec![variable_base_scalar_mul], + opcodes: vec![multi_scalar_mul], private_parameters: BTreeSet::from([Witness(1), Witness(2), Witness(3), Witness(4)]), return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(5), Witness(6)])), ..Circuit::default() @@ -108,10 +83,10 @@ fn variable_base_scalar_mul_circuit() { let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 139, 65, 10, 0, 32, 8, 4, 213, 172, 46, 61, 186, - 167, 103, 52, 65, 185, 176, 140, 44, 142, 202, 73, 143, 42, 247, 230, 128, 51, 106, 176, - 64, 135, 53, 218, 112, 252, 113, 141, 223, 187, 9, 155, 36, 231, 203, 2, 176, 218, 19, 62, - 137, 0, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 85, 76, 65, 14, 0, 32, 8, 82, 179, 58, 244, 105, 159, + 30, 45, 218, 136, 141, 33, 40, 186, 93, 76, 208, 57, 31, 93, 96, 136, 47, 250, 146, 188, + 209, 39, 181, 131, 131, 187, 148, 110, 240, 246, 101, 38, 63, 180, 243, 97, 3, 86, 121, 62, + 10, 153, 0, 0, 0, ]; assert_eq!(bytes, expected_serialization) diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs new file mode 100644 index 00000000000..ee35385fa81 --- /dev/null +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs @@ -0,0 +1,53 @@ +use acir::{ + circuit::opcodes::FunctionInput, + native_types::{Witness, WitnessMap}, +}; +use acvm_blackbox_solver::BlackBoxFunctionSolver; + +use crate::pwg::{insert_value, witness_to_value, OpcodeResolutionError}; + +pub(super) fn multi_scalar_mul( + backend: &impl BlackBoxFunctionSolver, + initial_witness: &mut WitnessMap, + points: &[FunctionInput], + scalars: &[FunctionInput], + outputs: (Witness, Witness), +) -> Result<(), OpcodeResolutionError> { + let points: Result, _> = + points.iter().map(|input| witness_to_value(initial_witness, input.witness)).collect(); + let points: Vec<_> = points?.into_iter().cloned().collect(); + + let scalars: Result, _> = + scalars.iter().map(|input| witness_to_value(initial_witness, input.witness)).collect(); + let scalars: Vec<_> = scalars?.into_iter().cloned().collect(); + + // Call the backend's multi-scalar multiplication function + let (res_x, res_y) = backend.multi_scalar_mul(&points, &scalars)?; + + // Insert the resulting point into the witness map + insert_value(&outputs.0, res_x, initial_witness)?; + insert_value(&outputs.1, res_y, initial_witness)?; + + Ok(()) +} + +pub(super) fn embedded_curve_add( + backend: &impl BlackBoxFunctionSolver, + initial_witness: &mut WitnessMap, + input1_x: FunctionInput, + input1_y: FunctionInput, + input2_x: FunctionInput, + input2_y: FunctionInput, + outputs: (Witness, Witness), +) -> Result<(), OpcodeResolutionError> { + let input1_x = witness_to_value(initial_witness, input1_x.witness)?; + let input1_y = witness_to_value(initial_witness, input1_y.witness)?; + let input2_x = witness_to_value(initial_witness, input2_x.witness)?; + let input2_y = witness_to_value(initial_witness, input2_y.witness)?; + let (res_x, res_y) = backend.ec_add(input1_x, input1_y, input2_x, input2_y)?; + + insert_value(&outputs.0, res_x, initial_witness)?; + insert_value(&outputs.1, res_y, initial_witness)?; + + Ok(()) +} diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/fixed_base_scalar_mul.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/fixed_base_scalar_mul.rs deleted file mode 100644 index 79e33ae8de5..00000000000 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/fixed_base_scalar_mul.rs +++ /dev/null @@ -1,70 +0,0 @@ -// TODO(https://github.com/noir-lang/noir/issues/4932): rename this file to something more generic -use acir::{ - circuit::opcodes::FunctionInput, - native_types::{Witness, WitnessMap}, -}; -use acvm_blackbox_solver::BlackBoxFunctionSolver; - -use crate::pwg::{insert_value, witness_to_value, OpcodeResolutionError}; - -pub(super) fn fixed_base_scalar_mul( - backend: &impl BlackBoxFunctionSolver, - initial_witness: &mut WitnessMap, - low: FunctionInput, - high: FunctionInput, - outputs: (Witness, Witness), -) -> Result<(), OpcodeResolutionError> { - let low = witness_to_value(initial_witness, low.witness)?; - let high = witness_to_value(initial_witness, high.witness)?; - - let (pub_x, pub_y) = backend.fixed_base_scalar_mul(low, high)?; - - insert_value(&outputs.0, pub_x, initial_witness)?; - insert_value(&outputs.1, pub_y, initial_witness)?; - - Ok(()) -} - -pub(super) fn variable_base_scalar_mul( - backend: &impl BlackBoxFunctionSolver, - initial_witness: &mut WitnessMap, - point_x: FunctionInput, - point_y: FunctionInput, - scalar_low: FunctionInput, - scalar_high: FunctionInput, - outputs: (Witness, Witness), -) -> Result<(), OpcodeResolutionError> { - let point_x = witness_to_value(initial_witness, point_x.witness)?; - let point_y = witness_to_value(initial_witness, point_y.witness)?; - let scalar_low = witness_to_value(initial_witness, scalar_low.witness)?; - let scalar_high = witness_to_value(initial_witness, scalar_high.witness)?; - - let (out_point_x, out_point_y) = - backend.variable_base_scalar_mul(point_x, point_y, scalar_low, scalar_high)?; - - insert_value(&outputs.0, out_point_x, initial_witness)?; - insert_value(&outputs.1, out_point_y, initial_witness)?; - - Ok(()) -} - -pub(super) fn embedded_curve_add( - backend: &impl BlackBoxFunctionSolver, - initial_witness: &mut WitnessMap, - input1_x: FunctionInput, - input1_y: FunctionInput, - input2_x: FunctionInput, - input2_y: FunctionInput, - outputs: (Witness, Witness), -) -> Result<(), OpcodeResolutionError> { - let input1_x = witness_to_value(initial_witness, input1_x.witness)?; - let input1_y = witness_to_value(initial_witness, input1_y.witness)?; - let input2_x = witness_to_value(initial_witness, input2_x.witness)?; - let input2_y = witness_to_value(initial_witness, input2_y.witness)?; - let (res_x, res_y) = backend.ec_add(input1_x, input1_y, input2_x, input2_y)?; - - insert_value(&outputs.0, res_x, initial_witness)?; - insert_value(&outputs.1, res_y, initial_witness)?; - - Ok(()) -} diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 2487d511b50..8ed7d2a2711 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -13,14 +13,14 @@ use super::{insert_value, OpcodeNotSolvable, OpcodeResolutionError}; use crate::{pwg::witness_to_value, BlackBoxFunctionSolver}; pub(crate) mod bigint; -mod fixed_base_scalar_mul; +mod embedded_curve_ops; mod hash; mod logic; mod pedersen; mod range; mod signature; -use fixed_base_scalar_mul::{embedded_curve_add, fixed_base_scalar_mul, variable_base_scalar_mul}; +use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul}; // Hash functions should eventually be exposed for external consumers. use hash::{solve_generic_256_hash_opcode, solve_sha_256_permutation_opcode}; use logic::{and, xor}; @@ -155,24 +155,9 @@ pub(crate) fn solve( message.as_ref(), *output, ), - BlackBoxFuncCall::FixedBaseScalarMul { low, high, outputs } => { - fixed_base_scalar_mul(backend, initial_witness, *low, *high, *outputs) + BlackBoxFuncCall::MultiScalarMul { points, scalars, outputs } => { + multi_scalar_mul(backend, initial_witness, points, scalars, *outputs) } - BlackBoxFuncCall::VariableBaseScalarMul { - point_x, - point_y, - scalar_low, - scalar_high, - outputs, - } => variable_base_scalar_mul( - backend, - initial_witness, - *point_x, - *point_y, - *scalar_low, - *scalar_high, - *outputs, - ), BlackBoxFuncCall::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, outputs } => { embedded_curve_add( backend, diff --git a/noir/noir-repo/acvm-repo/acvm_js/test/browser/execute_circuit.test.ts b/noir/noir-repo/acvm-repo/acvm_js/test/browser/execute_circuit.test.ts index f6287c2ae8a..625cc91cfe9 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/test/browser/execute_circuit.test.ts +++ b/noir/noir-repo/acvm-repo/acvm_js/test/browser/execute_circuit.test.ts @@ -93,18 +93,8 @@ it('successfully executes a Pedersen opcode', async function () { expect(solvedWitness).to.be.deep.eq(expectedWitnessMap); }); -it('successfully executes a FixedBaseScalarMul opcode', async () => { - const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/fixed_base_scalar_mul'); - - const solvedWitness: WitnessMap = await executeCircuit(bytecode, initialWitnessMap, () => { - throw Error('unexpected oracle'); - }); - - expect(solvedWitness).to.be.deep.eq(expectedWitnessMap); -}); - -it('successfully executes a VariableBaseScalarMul opcode', async () => { - const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/variable_base_scalar_mul'); +it('successfully executes a MultiScalarMul opcode', async () => { + const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/multi_scalar_mul'); const solvedWitness: WitnessMap = await executeCircuit(bytecode, initialWitnessMap, () => { throw Error('unexpected oracle'); diff --git a/noir/noir-repo/acvm-repo/acvm_js/test/node/execute_circuit.test.ts b/noir/noir-repo/acvm-repo/acvm_js/test/node/execute_circuit.test.ts index f9fd5c10b3e..3f9bde2898e 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/test/node/execute_circuit.test.ts +++ b/noir/noir-repo/acvm-repo/acvm_js/test/node/execute_circuit.test.ts @@ -90,18 +90,8 @@ it('successfully executes a Pedersen opcode', async function () { expect(solvedWitness).to.be.deep.eq(expectedWitnessMap); }); -it('successfully executes a FixedBaseScalarMul opcode', async () => { - const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/fixed_base_scalar_mul'); - - const solvedWitness: WitnessMap = await executeCircuit(bytecode, initialWitnessMap, () => { - throw Error('unexpected oracle'); - }); - - expect(solvedWitness).to.be.deep.eq(expectedWitnessMap); -}); - -it('successfully executes a VariableBaseScalarMul opcode', async () => { - const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/variable_base_scalar_mul'); +it('successfully executes a MultiScalarMul opcode', async () => { + const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/multi_scalar_mul'); const solvedWitness: WitnessMap = await executeCircuit(bytecode, initialWitnessMap, () => { throw Error('unexpected oracle'); diff --git a/noir/noir-repo/acvm-repo/acvm_js/test/shared/fixed_base_scalar_mul.ts b/noir/noir-repo/acvm-repo/acvm_js/test/shared/fixed_base_scalar_mul.ts deleted file mode 100644 index 97b5041121a..00000000000 --- a/noir/noir-repo/acvm-repo/acvm_js/test/shared/fixed_base_scalar_mul.ts +++ /dev/null @@ -1,17 +0,0 @@ -// See `fixed_base_scalar_mul_circuit` integration test in `acir/tests/test_program_serialization.rs`. -export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 85, 138, 81, 10, 0, 48, 8, 66, 87, 219, 190, 118, 233, 29, 61, 35, 3, 19, 228, 137, - 60, 91, 149, 139, 26, 119, 242, 145, 31, 117, 114, 163, 135, 142, 139, 219, 91, 127, 117, 71, 2, 117, 84, 50, 98, 113, - 0, 0, 0, -]); -export const initialWitnessMap = new Map([ - [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], - [2, '0x0000000000000000000000000000000000000000000000000000000000000000'], -]); - -export const expectedWitnessMap = new Map([ - [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], - [2, '0x0000000000000000000000000000000000000000000000000000000000000000'], - [3, '0x0000000000000000000000000000000000000000000000000000000000000001'], - [4, '0x0000000000000002cf135e7506a45d632d270d45f1181294833fc48d823f272c'], -]); diff --git a/noir/noir-repo/acvm-repo/acvm_js/test/shared/variable_base_scalar_mul.ts b/noir/noir-repo/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts similarity index 69% rename from noir/noir-repo/acvm-repo/acvm_js/test/shared/variable_base_scalar_mul.ts rename to noir/noir-repo/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts index 400f7bf4e61..c76fe264e12 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/test/shared/variable_base_scalar_mul.ts +++ b/noir/noir-repo/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts @@ -1,8 +1,8 @@ -// See `variable_base_scalar_mul_circuit` integration test in `acir/tests/test_program_serialization.rs`. +// See `multi_scalar_mul_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 139, 65, 10, 0, 32, 8, 4, 213, 172, 46, 61, 186, 167, 103, 52, 65, 185, 176, - 140, 44, 142, 202, 73, 143, 42, 247, 230, 128, 51, 106, 176, 64, 135, 53, 218, 112, 252, 113, 141, 223, 187, 9, 155, - 36, 231, 203, 2, 176, 218, 19, 62, 137, 0, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 85, 76, 65, 14, 0, 32, 8, 82, 179, 58, 244, 105, 159, 30, 45, 218, 136, 141, 33, + 40, 186, 93, 76, 208, 57, 31, 93, 96, 136, 47, 250, 146, 188, 209, 39, 181, 131, 131, 187, 148, 110, 240, 246, 101, + 38, 63, 180, 243, 97, 3, 86, 121, 62, 10, 153, 0, 0, 0, ]); export const initialWitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs index a809e21e2ca..3403b0fe232 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs @@ -24,17 +24,10 @@ pub trait BlackBoxFunctionSolver { inputs: &[FieldElement], domain_separator: u32, ) -> Result; - fn fixed_base_scalar_mul( + fn multi_scalar_mul( &self, - low: &FieldElement, - high: &FieldElement, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError>; - fn variable_base_scalar_mul( - &self, - point_x: &FieldElement, - point_y: &FieldElement, - scalar_low: &FieldElement, - scalar_high: &FieldElement, + points: &[FieldElement], + scalars: &[FieldElement], ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError>; fn ec_add( &self, @@ -85,21 +78,12 @@ impl BlackBoxFunctionSolver for StubbedBlackBoxSolver { ) -> Result { Err(Self::fail(BlackBoxFunc::PedersenHash)) } - fn fixed_base_scalar_mul( - &self, - _low: &FieldElement, - _high: &FieldElement, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - Err(Self::fail(BlackBoxFunc::FixedBaseScalarMul)) - } - fn variable_base_scalar_mul( + fn multi_scalar_mul( &self, - _point_x: &FieldElement, - _point_y: &FieldElement, - _scalar_low: &FieldElement, - _scalar_high: &FieldElement, + _points: &[FieldElement], + _scalars: &[FieldElement], ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - Err(Self::fail(BlackBoxFunc::VariableBaseScalarMul)) + Err(Self::fail(BlackBoxFunc::MultiScalarMul)) } fn ec_add( &self, diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/fixed_base_scalar_mul.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/embedded_curve_ops.rs similarity index 53% rename from noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/fixed_base_scalar_mul.rs rename to noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/embedded_curve_ops.rs index 2d7ffe1cf1c..3f6d2ac86c1 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/fixed_base_scalar_mul.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/embedded_curve_ops.rs @@ -7,61 +7,59 @@ use acir::{BlackBoxFunc, FieldElement}; use crate::BlackBoxResolutionError; -/// Performs fixed-base scalar multiplication using the curve's generator point. -pub fn fixed_base_scalar_mul( - low: &FieldElement, - high: &FieldElement, +/// Performs multi scalar multiplication of points with scalars. +pub fn multi_scalar_mul( + points: &[FieldElement], + scalars: &[FieldElement], ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - let generator = grumpkin::SWAffine::generator(); - let generator_x = FieldElement::from_repr(*generator.x().unwrap()); - let generator_y = FieldElement::from_repr(*generator.y().unwrap()); + if points.len() != scalars.len() { + return Err(BlackBoxResolutionError::Failed( + BlackBoxFunc::MultiScalarMul, + "Points and scalars must have the same length".to_string(), + )); + } - variable_base_scalar_mul(&generator_x, &generator_y, low, high).map_err(|err| match err { - BlackBoxResolutionError::Failed(_, message) => { - BlackBoxResolutionError::Failed(BlackBoxFunc::FixedBaseScalarMul, message) + let mut output_point = grumpkin::SWAffine::zero(); + + for i in (0..points.len()).step_by(2) { + let point = create_point(points[i], points[i + 1]) + .map_err(|e| BlackBoxResolutionError::Failed(BlackBoxFunc::MultiScalarMul, e))?; + + let scalar_low: u128 = scalars[i].try_into_u128().ok_or_else(|| { + BlackBoxResolutionError::Failed( + BlackBoxFunc::MultiScalarMul, + format!("Limb {} is not less than 2^128", scalars[i].to_hex()), + ) + })?; + + let scalar_high: u128 = scalars[i + 1].try_into_u128().ok_or_else(|| { + BlackBoxResolutionError::Failed( + BlackBoxFunc::MultiScalarMul, + format!("Limb {} is not less than 2^128", scalars[i + 1].to_hex()), + ) + })?; + + let mut bytes = scalar_high.to_be_bytes().to_vec(); + bytes.extend_from_slice(&scalar_low.to_be_bytes()); + + // Check if this is smaller than the grumpkin modulus + let grumpkin_integer = BigUint::from_bytes_be(&bytes); + + if grumpkin_integer >= grumpkin::FrConfig::MODULUS.into() { + return Err(BlackBoxResolutionError::Failed( + BlackBoxFunc::MultiScalarMul, + format!("{} is not a valid grumpkin scalar", grumpkin_integer.to_str_radix(16)), + )); } - }) -} -pub fn variable_base_scalar_mul( - point_x: &FieldElement, - point_y: &FieldElement, - scalar_low: &FieldElement, - scalar_high: &FieldElement, -) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - let point1 = create_point(*point_x, *point_y) - .map_err(|e| BlackBoxResolutionError::Failed(BlackBoxFunc::VariableBaseScalarMul, e))?; - - let scalar_low: u128 = scalar_low.try_into_u128().ok_or_else(|| { - BlackBoxResolutionError::Failed( - BlackBoxFunc::VariableBaseScalarMul, - format!("Limb {} is not less than 2^128", scalar_low.to_hex()), - ) - })?; - - let scalar_high: u128 = scalar_high.try_into_u128().ok_or_else(|| { - BlackBoxResolutionError::Failed( - BlackBoxFunc::VariableBaseScalarMul, - format!("Limb {} is not less than 2^128", scalar_high.to_hex()), - ) - })?; - - let mut bytes = scalar_high.to_be_bytes().to_vec(); - bytes.extend_from_slice(&scalar_low.to_be_bytes()); - - // Check if this is smaller than the grumpkin modulus - let grumpkin_integer = BigUint::from_bytes_be(&bytes); - - if grumpkin_integer >= grumpkin::FrConfig::MODULUS.into() { - return Err(BlackBoxResolutionError::Failed( - BlackBoxFunc::VariableBaseScalarMul, - format!("{} is not a valid grumpkin scalar", grumpkin_integer.to_str_radix(16)), - )); + let iteration_output_point = + grumpkin::SWAffine::from(point.mul_bigint(grumpkin_integer.to_u64_digits())); + + output_point = grumpkin::SWAffine::from(output_point + iteration_output_point); } - let result = grumpkin::SWAffine::from(point1.mul_bigint(grumpkin_integer.to_u64_digits())); - if let Some((res_x, res_y)) = result.xy() { - Ok((FieldElement::from_repr(*res_x), FieldElement::from_repr(*res_y))) + if let Some((out_x, out_y)) = output_point.xy() { + Ok((FieldElement::from_repr(*out_x), FieldElement::from_repr(*out_y))) } else { Ok((FieldElement::zero(), FieldElement::zero())) } @@ -100,30 +98,36 @@ fn create_point(x: FieldElement, y: FieldElement) -> Result [FieldElement; 2] { + let generator = grumpkin::SWAffine::generator(); + let generator_x = FieldElement::from_repr(*generator.x().unwrap()); + let generator_y = FieldElement::from_repr(*generator.y().unwrap()); + [generator_x, generator_y] + } + #[test] fn smoke_test() -> Result<(), BlackBoxResolutionError> { - let input = FieldElement::one(); + // We check that multiplying 1 by generator results in the generator + let generator = get_generator(); - let res = fixed_base_scalar_mul(&input, &FieldElement::zero())?; - let x = "0000000000000000000000000000000000000000000000000000000000000001"; - let y = "0000000000000002cf135e7506a45d632d270d45f1181294833fc48d823f272c"; + let res = multi_scalar_mul(&generator, &[FieldElement::one(), FieldElement::zero()])?; - assert_eq!(x, res.0.to_hex()); - assert_eq!(y, res.1.to_hex()); + assert_eq!(generator[0], res.0); + assert_eq!(generator[1], res.1); Ok(()) } #[test] fn low_high_smoke_test() -> Result<(), BlackBoxResolutionError> { - let low = FieldElement::one(); - let high = FieldElement::from(2u128); + let points = get_generator(); + let scalars = [FieldElement::one(), FieldElement::from(2u128)]; - let res = fixed_base_scalar_mul(&low, &high)?; + let res = multi_scalar_mul(&points, &scalars)?; let x = "0702ab9c7038eeecc179b4f209991bcb68c7cb05bf4c532d804ccac36199c9a9"; let y = "23f10e9e43a3ae8d75d24154e796aae12ae7af546716e8f81a2564f1b5814130"; @@ -133,19 +137,21 @@ mod grumpkin_fixed_base_scalar_mul { } #[test] - fn rejects_invalid_limbs() { + fn rejects_invalid_scalar_limbs() { + let points = get_generator(); + let max_limb = FieldElement::from(u128::MAX); let invalid_limb = max_limb + FieldElement::one(); let expected_error = Err(BlackBoxResolutionError::Failed( - BlackBoxFunc::FixedBaseScalarMul, + BlackBoxFunc::MultiScalarMul, "Limb 0000000000000000000000000000000100000000000000000000000000000000 is not less than 2^128".into(), )); - let res = fixed_base_scalar_mul(&invalid_limb, &FieldElement::zero()); + let res = multi_scalar_mul(&points, &[FieldElement::one(), invalid_limb]); assert_eq!(res, expected_error); - let res = fixed_base_scalar_mul(&FieldElement::zero(), &invalid_limb); + let res = multi_scalar_mul(&points, &[invalid_limb, FieldElement::one()]); assert_eq!(res, expected_error); } @@ -153,60 +159,57 @@ mod grumpkin_fixed_base_scalar_mul { fn rejects_grumpkin_modulus() { let x = grumpkin::FrConfig::MODULUS.to_bytes_be(); - let high = FieldElement::from_be_bytes_reduce(&x[0..16]); let low = FieldElement::from_be_bytes_reduce(&x[16..32]); + let high = FieldElement::from_be_bytes_reduce(&x[0..16]); - let res = fixed_base_scalar_mul(&low, &high); + let res = multi_scalar_mul(&get_generator(), &[low, high]); assert_eq!( res, Err(BlackBoxResolutionError::Failed( - BlackBoxFunc::FixedBaseScalarMul, + BlackBoxFunc::MultiScalarMul, "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 is not a valid grumpkin scalar".into(), )) ); } #[test] - fn variable_base_matches_fixed_base_for_generator_on_input( - ) -> Result<(), BlackBoxResolutionError> { - let low = FieldElement::one(); - let high = FieldElement::from(2u128); - - let generator = grumpkin::SWAffine::generator(); - let generator_x = FieldElement::from_repr(*generator.x().unwrap()); - let generator_y = FieldElement::from_repr(*generator.y().unwrap()); - - let fixed_res = fixed_base_scalar_mul(&low, &high)?; - let variable_res = variable_base_scalar_mul(&generator_x, &generator_y, &low, &high)?; - - assert_eq!(fixed_res, variable_res); - Ok(()) - } - - #[test] - fn variable_base_scalar_mul_rejects_invalid_point() { + fn rejects_invalid_point() { let invalid_point_x = FieldElement::one(); let invalid_point_y = FieldElement::one(); let valid_scalar_low = FieldElement::zero(); let valid_scalar_high = FieldElement::zero(); - let res = variable_base_scalar_mul( - &invalid_point_x, - &invalid_point_y, - &valid_scalar_low, - &valid_scalar_high, + let res = multi_scalar_mul( + &[invalid_point_x, invalid_point_y], + &[valid_scalar_low, valid_scalar_high], ); assert_eq!( res, Err(BlackBoxResolutionError::Failed( - BlackBoxFunc::VariableBaseScalarMul, + BlackBoxFunc::MultiScalarMul, "Point (0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000001) is not on curve".into(), )) ); } + #[test] + fn throws_on_args_length_mismatch() { + let points = get_generator(); + let scalars = [FieldElement::from(2u128)]; + + let res = multi_scalar_mul(&points, &scalars); + + assert_eq!( + res, + Err(BlackBoxResolutionError::Failed( + BlackBoxFunc::MultiScalarMul, + "Points and scalars must have the same length".into(), + )) + ); + } + #[test] fn rejects_addition_of_points_not_in_curve() { let x = FieldElement::from(1u128); @@ -222,4 +225,17 @@ mod grumpkin_fixed_base_scalar_mul { )) ); } + + #[test] + fn output_of_msm_matches_add() -> Result<(), BlackBoxResolutionError> { + let points = get_generator(); + let scalars = [FieldElement::from(2u128), FieldElement::zero()]; + + let msm_res = multi_scalar_mul(&points, &scalars)?; + let add_res = embedded_curve_add(points[0], points[1], points[0], points[1])?; + + assert_eq!(msm_res.0, add_res.0); + assert_eq!(msm_res.1, add_res.1); + Ok(()) + } } diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs index 9395260fe36..4cb51b59755 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -5,13 +5,11 @@ use acir::{BlackBoxFunc, FieldElement}; use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError}; -mod fixed_base_scalar_mul; +mod embedded_curve_ops; mod poseidon2; mod wasm; -pub use fixed_base_scalar_mul::{ - embedded_curve_add, fixed_base_scalar_mul, variable_base_scalar_mul, -}; +pub use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul}; pub use poseidon2::poseidon2_permutation; use wasm::Barretenberg; @@ -91,22 +89,12 @@ impl BlackBoxFunctionSolver for Bn254BlackBoxSolver { }) } - fn fixed_base_scalar_mul( + fn multi_scalar_mul( &self, - low: &FieldElement, - high: &FieldElement, + points: &[FieldElement], + scalars: &[FieldElement], ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - fixed_base_scalar_mul(low, high) - } - - fn variable_base_scalar_mul( - &self, - point_x: &FieldElement, - point_y: &FieldElement, - low: &FieldElement, - high: &FieldElement, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - variable_base_scalar_mul(point_x, point_y, low, high) + multi_scalar_mul(points, scalars) } fn ec_add( diff --git a/noir/noir-repo/acvm-repo/brillig/src/black_box.rs b/noir/noir-repo/acvm-repo/brillig/src/black_box.rs index f31a434c772..2a61bb2b96d 100644 --- a/noir/noir-repo/acvm-repo/brillig/src/black_box.rs +++ b/noir/noir-repo/acvm-repo/brillig/src/black_box.rs @@ -66,19 +66,11 @@ pub enum BlackBoxOp { domain_separator: MemoryAddress, output: MemoryAddress, }, - /// Performs scalar multiplication over the embedded curve. - FixedBaseScalarMul { - low: MemoryAddress, - high: MemoryAddress, - result: HeapArray, - }, - /// Performs scalar multiplication over the embedded curve with variable base point. - VariableBaseScalarMul { - point_x: MemoryAddress, - point_y: MemoryAddress, - scalar_low: MemoryAddress, - scalar_high: MemoryAddress, - result: HeapArray, + /// Performs multi scalar multiplication over the embedded curve. + MultiScalarMul { + points: HeapVector, + scalars: HeapVector, + outputs: HeapArray, }, /// Performs addition over the embedded curve. EmbeddedCurveAdd { 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 9557cdae7b9..1cd08563677 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 @@ -136,26 +136,16 @@ pub(crate) fn evaluate_black_box( memory.write(*result, verified.into()); Ok(()) } - BlackBoxOp::FixedBaseScalarMul { low, high, result } => { - let low = memory.read(*low).try_into().unwrap(); - let high = memory.read(*high).try_into().unwrap(); - let (x, y) = solver.fixed_base_scalar_mul(&low, &high)?; + BlackBoxOp::MultiScalarMul { points, scalars, outputs: result } => { + let points: Vec = + read_heap_vector(memory, points).iter().map(|x| x.try_into().unwrap()).collect(); + let scalars: Vec = + read_heap_vector(memory, scalars).iter().map(|x| x.try_into().unwrap()).collect(); + + let (x, y) = solver.multi_scalar_mul(&points, &scalars)?; memory.write_slice(memory.read_ref(result.pointer), &[x.into(), y.into()]); Ok(()) } - BlackBoxOp::VariableBaseScalarMul { point_x, point_y, scalar_low, scalar_high, result } => { - let point_x = memory.read(*point_x).try_into().unwrap(); - let point_y = memory.read(*point_y).try_into().unwrap(); - let scalar_low = memory.read(*scalar_low).try_into().unwrap(); - let scalar_high = memory.read(*scalar_high).try_into().unwrap(); - let (out_point_x, out_point_y) = - solver.variable_base_scalar_mul(&point_x, &point_y, &scalar_low, &scalar_high)?; - memory.write_slice( - memory.read_ref(result.pointer), - &[out_point_x.into(), out_point_y.into()], - ); - Ok(()) - } BlackBoxOp::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, result } => { let input1_x = memory.read(*input1_x).try_into().unwrap(); let input1_y = memory.read(*input1_y).try_into().unwrap(); @@ -301,8 +291,7 @@ fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc { BlackBoxOp::SchnorrVerify { .. } => BlackBoxFunc::SchnorrVerify, BlackBoxOp::PedersenCommitment { .. } => BlackBoxFunc::PedersenCommitment, BlackBoxOp::PedersenHash { .. } => BlackBoxFunc::PedersenHash, - BlackBoxOp::FixedBaseScalarMul { .. } => BlackBoxFunc::FixedBaseScalarMul, - BlackBoxOp::VariableBaseScalarMul { .. } => BlackBoxFunc::VariableBaseScalarMul, + BlackBoxOp::MultiScalarMul { .. } => BlackBoxFunc::MultiScalarMul, BlackBoxOp::EmbeddedCurveAdd { .. } => BlackBoxFunc::EmbeddedCurveAdd, BlackBoxOp::BigIntAdd { .. } => BlackBoxFunc::BigIntAdd, BlackBoxOp::BigIntSub { .. } => BlackBoxFunc::BigIntSub, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index 210e56b2ecb..9262047fb60 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -188,39 +188,20 @@ pub(crate) fn convert_black_box_call( unreachable!("ICE: Schnorr verify expects two registers for the public key, an array for signature, an array for the message hash and one result register") } } - BlackBoxFunc::FixedBaseScalarMul => { - if let ( - [BrilligVariable::SingleAddr(low), BrilligVariable::SingleAddr(high)], - [BrilligVariable::BrilligArray(result_array)], - ) = (function_arguments, function_results) - { - brillig_context.black_box_op_instruction(BlackBoxOp::FixedBaseScalarMul { - low: low.address, - high: high.address, - result: result_array.to_heap_array(), - }); - } else { - unreachable!( - "ICE: FixedBaseScalarMul expects two register arguments and one array result" - ) - } - } - BlackBoxFunc::VariableBaseScalarMul => { - if let ( - [BrilligVariable::SingleAddr(point_x), BrilligVariable::SingleAddr(point_y), BrilligVariable::SingleAddr(scalar_low), BrilligVariable::SingleAddr(scalar_high)], - [BrilligVariable::BrilligArray(result_array)], - ) = (function_arguments, function_results) + BlackBoxFunc::MultiScalarMul => { + if let ([points, scalars], [BrilligVariable::BrilligArray(outputs)]) = + (function_arguments, function_results) { - brillig_context.black_box_op_instruction(BlackBoxOp::VariableBaseScalarMul { - point_x: point_x.address, - point_y: point_y.address, - scalar_low: scalar_low.address, - scalar_high: scalar_high.address, - result: result_array.to_heap_array(), + let points = convert_array_or_vector(brillig_context, points, bb_func); + let scalars = convert_array_or_vector(brillig_context, scalars, bb_func); + brillig_context.black_box_op_instruction(BlackBoxOp::MultiScalarMul { + points: points.to_heap_vector(), + scalars: scalars.to_heap_vector(), + outputs: outputs.to_heap_array(), }); } else { unreachable!( - "ICE: VariableBaseScalarMul expects four register arguments and one array result" + "ICE: MultiScalarMul expects two register arguments and one array result" ) } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index b4ed59de59d..fadcdb22c15 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -167,24 +167,14 @@ pub(crate) mod tests { ) -> Result { Ok(6_u128.into()) } - fn fixed_base_scalar_mul( + fn multi_scalar_mul( &self, - _low: &FieldElement, - _high: &FieldElement, + _points: &[FieldElement], + _scalars: &[FieldElement], ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { Ok((4_u128.into(), 5_u128.into())) } - fn variable_base_scalar_mul( - &self, - _point_x: &FieldElement, - _point_y: &FieldElement, - _scalar_low: &FieldElement, - _scalar_high: &FieldElement, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - Ok((7_u128.into(), 8_u128.into())) - } - fn ec_add( &self, _input1_x: &FieldElement, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index 8b00939b3a7..4843026293b 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -315,30 +315,13 @@ impl DebugShow { result ); } - BlackBoxOp::FixedBaseScalarMul { low, high, result } => { + BlackBoxOp::MultiScalarMul { points, scalars, outputs } => { debug_println!( self.enable_debug_trace, - " FIXED_BASE_SCALAR_MUL {} {} -> {}", - low, - high, - result - ); - } - BlackBoxOp::VariableBaseScalarMul { - point_x, - point_y, - scalar_low, - scalar_high, - result, - } => { - debug_println!( - self.enable_debug_trace, - " VARIABLE_BASE_SCALAR_MUL ({} {}) ({} {}) -> {}", - point_x, - point_y, - scalar_low, - scalar_high, - result + " MULTI_SCALAR_MUL {} {} -> {}", + points, + scalars, + outputs ); } BlackBoxOp::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, result } => { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index 2f4f4f9f6cc..c0b427046ad 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -278,16 +278,9 @@ impl GeneratedAcir { output: outputs[0], } } - BlackBoxFunc::FixedBaseScalarMul => BlackBoxFuncCall::FixedBaseScalarMul { - low: inputs[0][0], - high: inputs[1][0], - outputs: (outputs[0], outputs[1]), - }, - BlackBoxFunc::VariableBaseScalarMul => BlackBoxFuncCall::VariableBaseScalarMul { - point_x: inputs[0][0], - point_y: inputs[1][0], - scalar_low: inputs[2][0], - scalar_high: inputs[3][0], + BlackBoxFunc::MultiScalarMul => BlackBoxFuncCall::MultiScalarMul { + points: inputs[0].clone(), + scalars: inputs[1].clone(), outputs: (outputs[0], outputs[1]), }, BlackBoxFunc::EmbeddedCurveAdd => BlackBoxFuncCall::EmbeddedCurveAdd { @@ -672,13 +665,8 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option { | BlackBoxFunc::EcdsaSecp256k1 | BlackBoxFunc::EcdsaSecp256r1 => None, - // Inputs for fixed based scalar multiplication - // is the low and high limbs of the scalar - BlackBoxFunc::FixedBaseScalarMul => Some(2), - - // Inputs for variable based scalar multiplication are the x and y coordinates of the base point and low - // and high limbs of the scalar - BlackBoxFunc::VariableBaseScalarMul => Some(4), + // Inputs for multi scalar multiplication is an arbitrary number of [point, scalar] pairs. + BlackBoxFunc::MultiScalarMul => None, // Recursive aggregation has a variable number of inputs BlackBoxFunc::RecursiveAggregation => None, @@ -734,9 +722,7 @@ fn black_box_expected_output_size(name: BlackBoxFunc) -> Option { // Output of operations over the embedded curve // will be 2 field elements representing the point. - BlackBoxFunc::FixedBaseScalarMul - | BlackBoxFunc::VariableBaseScalarMul - | BlackBoxFunc::EmbeddedCurveAdd => Some(2), + BlackBoxFunc::MultiScalarMul | BlackBoxFunc::EmbeddedCurveAdd => Some(2), // Big integer operations return a big integer BlackBoxFunc::BigIntAdd diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 3d7cb478f64..98a85f068df 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -454,8 +454,7 @@ fn simplify_black_box_func( simplify_signature(dfg, arguments, acvm::blackbox_solver::ecdsa_secp256r1_verify) } - BlackBoxFunc::FixedBaseScalarMul - | BlackBoxFunc::VariableBaseScalarMul + BlackBoxFunc::MultiScalarMul | BlackBoxFunc::SchnorrVerify | BlackBoxFunc::PedersenCommitment | BlackBoxFunc::PedersenHash diff --git a/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md b/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md index be8c65679c3..ceb37774785 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md @@ -19,7 +19,7 @@ Here is a list of the current black box functions: - [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) - [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) - [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) +- [Embedded curve operations (MSM, addition, ...)](./cryptographic_primitives/embedded_curve_ops.mdx) - AND - XOR - RANGE diff --git a/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx new file mode 100644 index 00000000000..f1122fc37d5 --- /dev/null +++ b/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx @@ -0,0 +1,77 @@ +--- +title: Scalar multiplication +description: See how you can perform scalar multiplication in Noir +keywords: [cryptographic primitives, Noir project, scalar multiplication] +sidebar_position: 1 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. +For the BN254 scalar field, this is BabyJubJub or Grumpkin. + +:::note +Suffixes `_low` and `_high` denote low and high limbs of a scalar. +::: + +## embedded_curve_ops::multi_scalar_mul + +Performs multi scalar multiplication over the embedded curve. +The function accepts arbitrary amount of point-scalar pairs on the input, it multiplies the individual pairs over +the curve and returns a sum of the resulting points. + +Points represented as x and y coordinates [x1, y1, x2, y2, ...], scalars as low and high limbs [low1, high1, low2, high2, ...]. + +#include_code multi_scalar_mul noir_stdlib/src/embedded_curve_ops.nr rust + +example + +```rust +fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) { + let point = std::embedded_curve_ops::multi_scalar_mul([point_x, point_y], [scalar_low, scalar_high]); + println(point); +} +``` + +## embedded_curve_ops::fixed_base_scalar_mul + +Performs fixed base scalar multiplication over the embedded curve (multiplies input scalar with a generator point). +The function accepts a single scalar on the input represented as 2 fields. + +#include_code fixed_base_scalar_mul noir_stdlib/src/embedded_curve_ops.nr rust + +example + +```rust +fn main(scalar_low: Field, scalar_high: Field) { + let point = std::embedded_curve_ops::fixed_base_scalar_mul(scalar_low, scalar_high); + println(point); +} +``` + +## embedded_curve_ops::embedded_curve_add + +Adds two points on the embedded curve. +This function takes two `EmbeddedCurvePoint` structures as parameters, representing points on the curve, and returns a new `EmbeddedCurvePoint` structure that represents their sum. + +### Parameters: +- `point1` (`EmbeddedCurvePoint`): The first point to add. +- `point2` (`EmbeddedCurvePoint`): The second point to add. + +### Returns: +- `EmbeddedCurvePoint`: The resulting point after the addition of `point1` and `point2`. + +#include_code embedded_curve_add noir_stdlib/src/embedded_curve_ops.nr rust + +example + +```rust +fn main() { + let point1 = EmbeddedCurvePoint { x: 1, y: 2 }; + let point2 = EmbeddedCurvePoint { x: 3, y: 4 }; + let result = std::embedded_curve_ops::embedded_curve_add(point1, point2); + println!("Resulting Point: ({}, {})", result.x, result.y); +} +``` + + diff --git a/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/scalar.mdx b/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/scalar.mdx deleted file mode 100644 index b835236a03e..00000000000 --- a/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/scalar.mdx +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Scalar multiplication -description: See how you can perform scalar multiplications over a fixed and variable bases in Noir -keywords: [cryptographic primitives, Noir project, scalar multiplication] -sidebar_position: 1 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; - -## scalar_mul::fixed_base_embedded_curve - -Performs scalar multiplication of a fixed base/generator over the embedded curve whose coordinates are defined -by the configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. Suffixes `_low` and -`_high` denote low and high limbs of the input scalar. - -#include_code fixed_base_embedded_curve noir_stdlib/src/scalar_mul.nr rust - -example - -```rust -fn main(scalar_low: Field, scalar_high: Field) { - let point = std::scalar_mul::fixed_base_embedded_curve(scalar_low, scalar_high); - println(point); -} -``` - -## scalar_mul::variable_base_embedded_curve - -Performs scalar multiplication of a variable base/input point over the embedded curve whose coordinates are defined -by the configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. Suffixes `_low` and -`_high` denote low and high limbs of the input scalar. - -#include_code variable_base_embedded_curve noir_stdlib/src/scalar_mul.nr rust - -example - -```rust -fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) { - let resulting_point = std::scalar_mul::fixed_base_embedded_curve(point_x, point_y, scalar_low, scalar_high); - println(resulting_point); -} -``` - - diff --git a/noir/noir-repo/noir_stdlib/src/scalar_mul.nr b/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr similarity index 54% rename from noir/noir-repo/noir_stdlib/src/scalar_mul.nr rename to noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr index 457b7b7791c..9ac037f5ae6 100644 --- a/noir/noir-repo/noir_stdlib/src/scalar_mul.nr +++ b/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr @@ -18,42 +18,39 @@ impl Add for EmbeddedCurvePoint { } } -// Computes a fixed base scalar multiplication over the embedded curve. +// Computes a multi scalar multiplication over the embedded curve. // For bn254, We have Grumpkin and Baby JubJub. // For bls12-381, we have JubJub and Bandersnatch. // // The embedded curve being used is decided by the // underlying proof system. -#[foreign(fixed_base_scalar_mul)] -// docs:start:fixed_base_embedded_curve -pub fn fixed_base_embedded_curve( - low: Field, // low limb of the scalar - high: Field // high limb of the scalar +#[foreign(multi_scalar_mul)] +// docs:start:multi_scalar_mul +pub fn multi_scalar_mul( + points: [Field; N], // points represented as x and y coordinates [x1, y1, x2, y2, ...] + scalars: [Field; N] // scalars represented as low and high limbs [low1, high1, low2, high2, ...] ) -> [Field; 2] -// docs:end:fixed_base_embedded_curve +// docs:end:multi_scalar_mul {} -// Computes a variable base scalar multiplication over the embedded curve. -// For bn254, We have Grumpkin and Baby JubJub. -// For bls12-381, we have JubJub and Bandersnatch. -// -// The embedded curve being used is decided by the -// underlying proof system. -// TODO(https://github.com/noir-lang/noir/issues/4931): use a point struct instead of two fields -#[foreign(variable_base_scalar_mul)] -// docs:start:variable_base_embedded_curve -pub fn variable_base_embedded_curve( - point_x: Field, // x coordinate of a point to multiply the scalar with - point_y: Field, // y coordinate of a point to multiply the scalar with - scalar_low: Field, // low limb of the scalar - scalar_high: Field // high limb of the scalar +// docs:start:fixed_base_scalar_mul +pub fn fixed_base_scalar_mul( + scalar_low: Field, + scalar_high: Field ) -> [Field; 2] -// docs:end:variable_base_embedded_curve -{} +// docs:end:fixed_base_scalar_mul +{ + let g1_x = 1; + let g1_y = 17631683881184975370165255887551781615748388533673675138860; + multi_scalar_mul([g1_x, g1_y], [scalar_low, scalar_high]) +} // This is a hack as returning an `EmbeddedCurvePoint` from a foreign function in brillig returns a [BrilligVariable::SingleAddr; 2] rather than BrilligVariable::BrilligArray // as is defined in the brillig bytecode format. This is a workaround which allows us to fix this without modifying the serialization format. -fn embedded_curve_add(point1: EmbeddedCurvePoint, point2: EmbeddedCurvePoint) -> EmbeddedCurvePoint { +// docs:start:embedded_curve_add +fn embedded_curve_add(point1: EmbeddedCurvePoint, point2: EmbeddedCurvePoint) -> EmbeddedCurvePoint +// docs:end:embedded_curve_add +{ let point_array = embedded_curve_add_array_return(point1, point2); let x = point_array[0]; let y = point_array[1]; diff --git a/noir/noir-repo/noir_stdlib/src/grumpkin_scalar.nr b/noir/noir-repo/noir_stdlib/src/grumpkin_scalar.nr index d05158488f4..dd4b029d0a7 100644 --- a/noir/noir-repo/noir_stdlib/src/grumpkin_scalar.nr +++ b/noir/noir-repo/noir_stdlib/src/grumpkin_scalar.nr @@ -1,3 +1,4 @@ +// TODO(https://github.com/noir-lang/noir/issues/4968): move to aztec noir-protocol-circuits struct GrumpkinScalar { low: Field, high: Field, diff --git a/noir/noir-repo/noir_stdlib/src/grumpkin_scalar_mul.nr b/noir/noir-repo/noir_stdlib/src/grumpkin_scalar_mul.nr deleted file mode 100644 index c1195073ef6..00000000000 --- a/noir/noir-repo/noir_stdlib/src/grumpkin_scalar_mul.nr +++ /dev/null @@ -1,6 +0,0 @@ -use crate::grumpkin_scalar::GrumpkinScalar; -use crate::scalar_mul::{fixed_base_embedded_curve, variable_base_embedded_curve}; - -pub fn grumpkin_fixed_base(scalar: GrumpkinScalar) -> [Field; 2] { - fixed_base_embedded_curve(scalar.low, scalar.high) -} \ No newline at end of file diff --git a/noir/noir-repo/noir_stdlib/src/lib.nr b/noir/noir-repo/noir_stdlib/src/lib.nr index ebde4b88858..73fc7a28417 100644 --- a/noir/noir-repo/noir_stdlib/src/lib.nr +++ b/noir/noir-repo/noir_stdlib/src/lib.nr @@ -7,8 +7,7 @@ mod ecdsa_secp256k1; mod ecdsa_secp256r1; mod eddsa; mod grumpkin_scalar; -mod grumpkin_scalar_mul; -mod scalar_mul; +mod embedded_curve_ops; mod sha256; mod sha512; mod field; diff --git a/noir/noir-repo/test_programs/compile_success_empty/intrinsic_die/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/intrinsic_die/src/main.nr index 8cac707dfea..9ce17f72c0d 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/intrinsic_die/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/intrinsic_die/src/main.nr @@ -2,5 +2,7 @@ use dep::std; // This test checks that we perform dead-instruction-elimination on intrinsic functions. fn main(x: Field) { let hash = std::hash::pedersen_commitment([x]); - let _p1 = std::scalar_mul::fixed_base_embedded_curve(x, 0); + let g1_x = 0x0000000000000000000000000000000000000000000000000000000000000001; + let g1_y = 0x0000000000000002cf135e7506a45d632d270d45f1181294833fc48d823f272c; + let _p1 = std::embedded_curve_ops::multi_scalar_mul([g1_x, g1_y], [x, 0]); } diff --git a/noir/noir-repo/test_programs/execution_success/fixed_base_scalar_mul/Nargo.toml b/noir/noir-repo/test_programs/execution_success/brillig_embedded_curve/Nargo.toml similarity index 62% rename from noir/noir-repo/test_programs/execution_success/fixed_base_scalar_mul/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/brillig_embedded_curve/Nargo.toml index a8e45c9b5ad..b92e11d6383 100644 --- a/noir/noir-repo/test_programs/execution_success/fixed_base_scalar_mul/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/brillig_embedded_curve/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "fixed_base_scalar_mul" +name = "brillig_embedded_curve" type = "bin" authors = [""] diff --git a/noir/noir-repo/test_programs/execution_success/brillig_embedded_curve/Prover.toml b/noir/noir-repo/test_programs/execution_success/brillig_embedded_curve/Prover.toml new file mode 100644 index 00000000000..7113b9cd038 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/brillig_embedded_curve/Prover.toml @@ -0,0 +1,3 @@ +priv_key = "1" +pub_x = "0x0000000000000000000000000000000000000000000000000000000000000001" +pub_y = "0x0000000000000002cf135e7506a45d632d270d45f1181294833fc48d823f272c" \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/brillig_embedded_curve/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_embedded_curve/src/main.nr new file mode 100644 index 00000000000..1a183bb13d9 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/brillig_embedded_curve/src/main.nr @@ -0,0 +1,28 @@ +use dep::std; + +unconstrained fn main( + priv_key: Field, + pub_x: pub Field, + pub_y: pub Field, +) { + let g1_y = 17631683881184975370165255887551781615748388533673675138860; + let g1 = std::embedded_curve_ops::EmbeddedCurvePoint { x: 1, y: g1_y }; + + // Test that multi_scalar_mul correctly derives the public key + let res = std::embedded_curve_ops::multi_scalar_mul([g1.x, g1.y], [priv_key, 0]); + assert(res[0] == pub_x); + assert(res[1] == pub_y); + + // Test that double function calling embedded_curve_add works as expected + let pub_point = std::embedded_curve_ops::EmbeddedCurvePoint { x: pub_x, y: pub_y }; + let res = pub_point.double(); + let double = g1.add(g1); + + assert(double.x == res.x); + + // Test calling multi_scalar_mul with multiple points and scalars + let res = std::embedded_curve_ops::multi_scalar_mul([g1.x, g1.y, g1.x, g1.y], [priv_key, 0, priv_key, 0]); + + // The results should be double the g1 point because the scalars are 1 and we pass in g1 twice + assert(double.x == res[0]); +} diff --git a/noir/noir-repo/test_programs/execution_success/brillig_scalar_mul/Prover.toml b/noir/noir-repo/test_programs/execution_success/brillig_scalar_mul/Prover.toml deleted file mode 100644 index 69b91cb5f31..00000000000 --- a/noir/noir-repo/test_programs/execution_success/brillig_scalar_mul/Prover.toml +++ /dev/null @@ -1,7 +0,0 @@ -a = "1" -a_pub_x = "0x0000000000000000000000000000000000000000000000000000000000000001" -a_pub_y = "0x0000000000000002cf135e7506a45d632d270d45f1181294833fc48d823f272c" - -b = "2" -b_pub_x = "0x06ce1b0827aafa85ddeb49cdaa36306d19a74caa311e13d46d8bc688cdbffffe" -b_pub_y = "0x1c122f81a3a14964909ede0ba2a6855fc93faf6fa1a788bf467be7e7a43f80ac" \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/brillig_scalar_mul/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_scalar_mul/src/main.nr deleted file mode 100644 index c7c3a85a4ff..00000000000 --- a/noir/noir-repo/test_programs/execution_success/brillig_scalar_mul/src/main.nr +++ /dev/null @@ -1,32 +0,0 @@ -use dep::std; - -unconstrained fn main( - a: Field, - a_pub_x: pub Field, - a_pub_y: pub Field, - b: Field, - b_pub_x: pub Field, - b_pub_y: pub Field -) { - let mut priv_key = a; - let mut pub_x: Field = a_pub_x; - let mut pub_y: Field = a_pub_y; - if a != 1 { - // Change `a` in Prover.toml to test input `b` - priv_key = b; - pub_x = b_pub_x; - pub_y = b_pub_y; - } - let res = std::scalar_mul::fixed_base_embedded_curve(priv_key, 0); - assert(res[0] == pub_x); - assert(res[1] == pub_y); - - let pub_point= std::scalar_mul::EmbeddedCurvePoint { x: pub_x, y: pub_y }; - let g1_y = 17631683881184975370165255887551781615748388533673675138860; - let g1= std::scalar_mul::EmbeddedCurvePoint { x: 1, y: g1_y }; - - let res = pub_point.double(); - let double = g1.add(g1); - - assert(double.x == res.x); -} diff --git a/noir/noir-repo/test_programs/execution_success/brillig_scalar_mul/Nargo.toml b/noir/noir-repo/test_programs/execution_success/embedded_curve_ops/Nargo.toml similarity index 65% rename from noir/noir-repo/test_programs/execution_success/brillig_scalar_mul/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/embedded_curve_ops/Nargo.toml index eefd041b899..1c389149aaf 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_scalar_mul/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/embedded_curve_ops/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "brillig_scalar_mul" +name = "embedded_curve_ops" type = "bin" authors = [""] diff --git a/noir/noir-repo/test_programs/execution_success/embedded_curve_ops/Prover.toml b/noir/noir-repo/test_programs/execution_success/embedded_curve_ops/Prover.toml new file mode 100644 index 00000000000..7113b9cd038 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/embedded_curve_ops/Prover.toml @@ -0,0 +1,3 @@ +priv_key = "1" +pub_x = "0x0000000000000000000000000000000000000000000000000000000000000001" +pub_y = "0x0000000000000002cf135e7506a45d632d270d45f1181294833fc48d823f272c" \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/embedded_curve_ops/src/main.nr b/noir/noir-repo/test_programs/execution_success/embedded_curve_ops/src/main.nr new file mode 100644 index 00000000000..3cb27d8c181 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/embedded_curve_ops/src/main.nr @@ -0,0 +1,24 @@ +use dep::std; + +fn main(priv_key: Field, pub_x: pub Field, pub_y: pub Field) { + let g1_y = 17631683881184975370165255887551781615748388533673675138860; + let g1 = std::embedded_curve_ops::EmbeddedCurvePoint { x: 1, y: g1_y }; + + // Test that multi_scalar_mul correctly derives the public key + let res = std::embedded_curve_ops::multi_scalar_mul([g1.x, g1.y], [priv_key, 0]); + assert(res[0] == pub_x); + assert(res[1] == pub_y); + + // Test that double function calling embedded_curve_add works as expected + let pub_point = std::embedded_curve_ops::EmbeddedCurvePoint { x: pub_x, y: pub_y }; + let res = pub_point.double(); + let double = g1.add(g1); + + assert(double.x == res.x); + + // Test calling multi_scalar_mul with multiple points and scalars + let res = std::embedded_curve_ops::multi_scalar_mul([g1.x, g1.y, g1.x, g1.y], [priv_key, 0, priv_key, 0]); + + // The results should be double the g1 point because the scalars are 1 and we pass in g1 twice + assert(double.x == res[0]); +} diff --git a/noir/noir-repo/test_programs/execution_success/fixed_base_scalar_mul/Prover.toml b/noir/noir-repo/test_programs/execution_success/fixed_base_scalar_mul/Prover.toml deleted file mode 100644 index 69b91cb5f31..00000000000 --- a/noir/noir-repo/test_programs/execution_success/fixed_base_scalar_mul/Prover.toml +++ /dev/null @@ -1,7 +0,0 @@ -a = "1" -a_pub_x = "0x0000000000000000000000000000000000000000000000000000000000000001" -a_pub_y = "0x0000000000000002cf135e7506a45d632d270d45f1181294833fc48d823f272c" - -b = "2" -b_pub_x = "0x06ce1b0827aafa85ddeb49cdaa36306d19a74caa311e13d46d8bc688cdbffffe" -b_pub_y = "0x1c122f81a3a14964909ede0ba2a6855fc93faf6fa1a788bf467be7e7a43f80ac" \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/fixed_base_scalar_mul/src/main.nr b/noir/noir-repo/test_programs/execution_success/fixed_base_scalar_mul/src/main.nr deleted file mode 100644 index e20f47907db..00000000000 --- a/noir/noir-repo/test_programs/execution_success/fixed_base_scalar_mul/src/main.nr +++ /dev/null @@ -1,31 +0,0 @@ -use dep::std; - -fn main( - a: Field, - a_pub_x: pub Field, - a_pub_y: pub Field, - b: Field, - b_pub_x: pub Field, - b_pub_y: pub Field -) { - let mut priv_key = a; - let mut pub_x: Field = a_pub_x; - let mut pub_y: Field = a_pub_y; - if a != 1 { - // Change `a` in Prover.toml to test input `b` - priv_key = b; - pub_x = b_pub_x; - pub_y = b_pub_y; - } - let res = std::scalar_mul::fixed_base_embedded_curve(priv_key, 0); - assert(res[0] == pub_x); - assert(res[1] == pub_y); - let pub_point= std::scalar_mul::EmbeddedCurvePoint { x: pub_x, y: pub_y }; - let g1_y = 17631683881184975370165255887551781615748388533673675138860; - let g1= std::scalar_mul::EmbeddedCurvePoint { x: 1, y: g1_y }; - - let res = pub_point.double(); - let double = g1.add(g1); - - assert(double.x == res.x); -} diff --git a/noir/noir-repo/test_programs/execution_success/simple_shield/src/main.nr b/noir/noir-repo/test_programs/execution_success/simple_shield/src/main.nr index c46d3b4594c..548ba17d462 100644 --- a/noir/noir-repo/test_programs/execution_success/simple_shield/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/simple_shield/src/main.nr @@ -13,7 +13,7 @@ fn main( to_pubkey_y: Field ) -> pub [Field; 2] { // Compute public key from private key to show ownership - let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key, 0); + let pubkey = std::embedded_curve_ops::fixed_base_scalar_mul(priv_key, 0); let pubkey_x = pubkey[0]; let pubkey_y = pubkey[1]; // Compute input note commitment diff --git a/noir/noir-repo/test_programs/execution_success/variable_base_scalar_mul/Nargo.toml b/noir/noir-repo/test_programs/execution_success/variable_base_scalar_mul/Nargo.toml deleted file mode 100644 index 66712ab503c..00000000000 --- a/noir/noir-repo/test_programs/execution_success/variable_base_scalar_mul/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "variable_base_scalar_mul" -type = "bin" -authors = [""] - -[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/variable_base_scalar_mul/Prover.toml b/noir/noir-repo/test_programs/execution_success/variable_base_scalar_mul/Prover.toml deleted file mode 100644 index 51d6fc9b96c..00000000000 --- a/noir/noir-repo/test_programs/execution_success/variable_base_scalar_mul/Prover.toml +++ /dev/null @@ -1,4 +0,0 @@ -point_x = "0x0000000000000000000000000000000000000000000000000000000000000001" -point_y = "0x0000000000000002cf135e7506a45d632d270d45f1181294833fc48d823f272c" -scalar_low = "0x0000000000000000000000000000000000000000000000000000000000000003" -scalar_high = "0x0000000000000000000000000000000000000000000000000000000000000000" diff --git a/noir/noir-repo/test_programs/execution_success/variable_base_scalar_mul/src/main.nr b/noir/noir-repo/test_programs/execution_success/variable_base_scalar_mul/src/main.nr deleted file mode 100644 index 4914ad01777..00000000000 --- a/noir/noir-repo/test_programs/execution_success/variable_base_scalar_mul/src/main.nr +++ /dev/null @@ -1,33 +0,0 @@ -use dep::std; - -fn main(point_x: pub Field, point_y: pub Field, scalar_low: pub Field, scalar_high: pub Field) { - // We multiply the point by 3 and check it matches result out of embedded_curve_add func - let res = std::scalar_mul::variable_base_embedded_curve(point_x, point_y, scalar_low, scalar_high); - - let point = std::scalar_mul::EmbeddedCurvePoint { x: point_x, y: point_y }; - - let double = point.double(); - let triple = point + double; - - assert(triple.x == res[0]); - assert(triple.y == res[1]); - - // We test that brillig gives us the same result - let brillig_res = get_brillig_result(point_x, point_y, scalar_low, scalar_high); - assert(res[0] == brillig_res[0]); - assert(res[1] == brillig_res[1]); - - // Multiplying the point by 1 should return the same point - let res = std::scalar_mul::variable_base_embedded_curve(point_x, point_y, 1, 0); - assert(point_x == res[0]); - assert(point_y == res[1]); -} - -unconstrained fn get_brillig_result( - point_x: Field, - point_y: Field, - scalar_low: Field, - scalar_high: Field -) -> [Field; 2] { - std::scalar_mul::variable_base_embedded_curve(point_x, point_y, scalar_low, scalar_high) -} diff --git a/noir/noir-repo/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs b/noir/noir-repo/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs index 75a6d323e7b..cdaebb95fc9 100644 --- a/noir/noir-repo/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs +++ b/noir/noir-repo/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs @@ -21,7 +21,7 @@ const INFO_RESPONSE: &str = r#"{ "pedersen_hash", "ecdsa_secp256k1", "ecdsa_secp256r1", - "fixed_base_scalar_mul", + "multi_scalar_mul", "recursive_aggregation" ] }"#; diff --git a/noir/noir-repo/tooling/lsp/src/solver.rs b/noir/noir-repo/tooling/lsp/src/solver.rs index b47c30af5f6..249406effaf 100644 --- a/noir/noir-repo/tooling/lsp/src/solver.rs +++ b/noir/noir-repo/tooling/lsp/src/solver.rs @@ -24,22 +24,12 @@ impl BlackBoxFunctionSolver for WrapperSolver { self.0.pedersen_commitment(inputs, domain_separator) } - fn fixed_base_scalar_mul( + fn multi_scalar_mul( &self, - low: &acvm::FieldElement, - high: &acvm::FieldElement, + points: &[acvm::FieldElement], + scalars: &[acvm::FieldElement], ) -> Result<(acvm::FieldElement, acvm::FieldElement), acvm::BlackBoxResolutionError> { - self.0.fixed_base_scalar_mul(low, high) - } - - fn variable_base_scalar_mul( - &self, - point_x: &acvm::FieldElement, - point_y: &acvm::FieldElement, - scalar_low: &acvm::FieldElement, - scalar_high: &acvm::FieldElement, - ) -> Result<(acvm::FieldElement, acvm::FieldElement), acvm::BlackBoxResolutionError> { - self.0.variable_base_scalar_mul(point_x, point_y, scalar_low, scalar_high) + self.0.multi_scalar_mul(points, scalars) } fn pedersen_hash(