From 537630ffe1b3747a03aa821687e33db04b1fe3ad Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Tue, 9 Jan 2024 18:58:04 +0100 Subject: [PATCH] feat: add ACIR opcodes for ECADD and ECDOUBLE (#3878) Please provide a paragraph or two giving a summary of the change, including relevant motivation and context. Related to https://github.com/noir-lang/noir/issues/3958 This PR does not implements the opcodes, but modifies BB interface so that it accept the new opcodes. It also does not implement the solver for the opcodes. # Checklist: Remove the checklist to signal you've completed it. Enable auto-merge if the PR is ready to merge. - [ ] If the pull request requires a cryptography review (e.g. cryptographic algorithm implementations) I have added the 'crypto' tag. - [X] I have reviewed my diff in github, line by line and removed unexpected formatting changes, testing logs, or commented-out code. - [X] Every change is related to the PR description. - [X] I have [linked](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) this pull request to relevant issues (if any exist). --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- .../dsl/acir_format/acir_format.cpp | 10 + .../dsl/acir_format/acir_format.hpp | 3 + .../dsl/acir_format/acir_format.test.cpp | 12 + .../dsl/acir_format/block_constraint.test.cpp | 2 + .../dsl/acir_format/ec_operations.cpp | 31 ++ .../dsl/acir_format/ec_operations.hpp | 35 ++ .../dsl/acir_format/ecdsa_secp256k1.test.cpp | 6 + .../dsl/acir_format/ecdsa_secp256r1.test.cpp | 8 + .../acir_format/recursion_constraint.test.cpp | 4 + .../dsl/acir_format/serde/acir.hpp | 301 +++++++++++++++++- noir/acvm-repo/acir/codegen/acir.cpp | 236 +++++++++++++- .../acir/src/circuit/black_box_functions.rs | 8 + .../opcodes/black_box_function_call.rs | 20 +- .../acvm/src/compiler/transformers/mod.rs | 8 + noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs | 6 + noir/acvm-repo/blackbox_solver/src/lib.rs | 12 + .../bn254_blackbox_solver/src/lib.rs | 18 ++ noir/acvm-repo/brillig/src/black_box.rs | 4 + noir/acvm-repo/brillig_vm/src/black_box.rs | 16 + noir/acvm-repo/brillig_vm/src/lib.rs | 16 + .../brillig/brillig_gen/brillig_black_box.rs | 37 +++ .../src/brillig/brillig_ir/debug_show.rs | 20 ++ .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 22 ++ .../ssa/acir_gen/acir_ir/generated_acir.rs | 22 +- .../src/ssa/ir/instruction/call.rs | 4 +- noir/tooling/lsp/src/solver.rs | 18 ++ 26 files changed, 872 insertions(+), 7 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.hpp 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 912c23e77df..03a3c1713b3 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -149,6 +149,16 @@ void build_constraints(Builder& builder, acir_format const& constraint_system, b create_fixed_base_constraint(builder, constraint); } + // Add ec add constraints + for (const auto& constraint : constraint_system.ec_add_constraints) { + create_ec_add_constraint(builder, constraint); + } + + // Add ec double + for (const auto& constraint : constraint_system.ec_double_constraints) { + create_ec_double_constraint(builder, constraint); + } + // Add block constraints for (const auto& constraint : constraint_system.block_constraints) { create_block_constraints(builder, constraint, has_valid_witness_assignments); 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 a2f198e2f7f..b458b578c55 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -5,6 +5,7 @@ #include "blake2s_constraint.hpp" #include "blake3_constraint.hpp" #include "block_constraint.hpp" +#include "ec_operations.hpp" #include "ecdsa_secp256k1.hpp" #include "ecdsa_secp256r1.hpp" #include "fixed_base_scalar_mul.hpp" @@ -38,6 +39,8 @@ struct acir_format { std::vector pedersen_constraints; std::vector pedersen_hash_constraints; std::vector fixed_base_scalar_mul_constraints; + std::vector ec_add_constraints; + std::vector ec_double_constraints; std::vector recursion_constraints; // A standard plonk arithmetic constraint, as defined in the poly_triple struct, consists of selector values 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 4b6fdcd52a5..5e61e2d4b1f 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 @@ -44,6 +44,8 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = { constraint }, .block_constraints = {}, @@ -152,6 +154,8 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = { expr_a, expr_b, expr_c, expr_d }, .block_constraints = {} }; @@ -218,6 +222,8 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = { poly_triple{ .a = schnorr_constraint.result, @@ -307,6 +313,8 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = { poly_triple{ .a = schnorr_constraint.result, @@ -415,6 +423,8 @@ TEST_F(AcirFormatTests, TestVarKeccak) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = { dummy }, .block_constraints = {}, @@ -454,6 +464,8 @@ TEST_F(AcirFormatTests, TestKeccakPermutation) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = {}, .block_constraints = {} }; 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 0473907838a..9aba7ec59e9 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 @@ -125,6 +125,8 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = {}, .block_constraints = { block }, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp new file mode 100644 index 00000000000..fc657ccebb1 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp @@ -0,0 +1,31 @@ +#include "ec_operations.hpp" +#include "barretenberg/dsl/types.hpp" +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" +#include "barretenberg/proof_system/arithmetization/gate_data.hpp" + +namespace acir_format { + +template void create_ec_add_constraint(Builder& builder, const EcAdd& input) +{ + // TODO + builder.assert_equal(input.input1_x, input.input1_x); + ASSERT(false); +} + +template void create_ec_add_constraint(UltraCircuitBuilder& builder, const EcAdd& input); +template void create_ec_add_constraint(GoblinUltraCircuitBuilder& builder, + const EcAdd& input); + +template void create_ec_double_constraint(Builder& builder, const EcDouble& input) +{ + // TODO + builder.assert_equal(input.input_x, input.input_x); + ASSERT(false); +} + +template void create_ec_double_constraint(UltraCircuitBuilder& builder, const EcDouble& input); +template void create_ec_double_constraint(GoblinUltraCircuitBuilder& builder, + const EcDouble& input); + +} // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.hpp new file mode 100644 index 00000000000..f6d8e6168eb --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.hpp @@ -0,0 +1,35 @@ +#pragma once +#include "barretenberg/dsl/types.hpp" +#include "barretenberg/serialize/msgpack.hpp" +#include + +namespace acir_format { + +struct EcAdd { + uint32_t input1_x; + uint32_t input1_y; + uint32_t input2_x; + uint32_t input2_y; + uint32_t result_x; + uint32_t result_y; + + // for serialization, update with any new fields + MSGPACK_FIELDS(input1_x, input1_y, input2_x, input2_y, result_x, result_y); + friend bool operator==(EcAdd const& lhs, EcAdd const& rhs) = default; +}; + +template void create_ec_add_constraint(Builder& builder, const EcAdd& input); + +struct EcDouble { + uint32_t input_x; + uint32_t input_y; + uint32_t result_x; + uint32_t result_y; + + // for serialization, update with any new fields + MSGPACK_FIELDS(input_x, input_y, result_x, result_y); + friend bool operator==(EcDouble const& lhs, EcDouble const& rhs) = default; +}; + +template void create_ec_double_constraint(Builder& builder, const EcDouble& input); +} // namespace acir_format 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 4dfdff06fb2..cc9a5a2e607 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 @@ -104,6 +104,8 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintSucceed) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = {}, .block_constraints = {}, @@ -146,6 +148,8 @@ TEST_F(ECDSASecp256k1, TestECDSACompilesForVerifier) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = {}, .block_constraints = {}, @@ -183,6 +187,8 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = {}, .block_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 deb95e656b5..f840d2b594b 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 @@ -138,6 +138,8 @@ TEST(ECDSASecp256r1, test_hardcoded) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = {}, .block_constraints = {}, @@ -181,6 +183,8 @@ TEST(ECDSASecp256r1, TestECDSAConstraintSucceed) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = {}, .block_constraints = {}, @@ -222,6 +226,8 @@ TEST(ECDSASecp256r1, TestECDSACompilesForVerifier) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = {}, .block_constraints = {}, @@ -258,6 +264,8 @@ TEST(ECDSASecp256r1, TestECDSAConstraintFail) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = {}, .block_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 5defd40ed19..4e2db15d485 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 @@ -97,6 +97,8 @@ Builder create_inner_circuit() .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = {}, .constraints = { expr_a, expr_b, expr_c, expr_d }, .block_constraints = {} }; @@ -253,6 +255,8 @@ Builder create_outer_circuit(std::vector& inner_circuits) .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .ec_double_constraints = {}, .recursion_constraints = recursion_constraints, .constraints = {}, .block_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 82e57cad6c6..b3c2106a4fe 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp @@ -145,6 +145,28 @@ struct BlackBoxFuncCall { static FixedBaseScalarMul bincodeDeserialize(std::vector); }; + struct EmbeddedCurveAdd { + Circuit::FunctionInput input1_x; + Circuit::FunctionInput input1_y; + Circuit::FunctionInput input2_x; + Circuit::FunctionInput input2_y; + std::array outputs; + + friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); + std::vector bincodeSerialize() const; + static EmbeddedCurveAdd bincodeDeserialize(std::vector); + }; + + struct EmbeddedCurveDouble { + Circuit::FunctionInput input_x; + Circuit::FunctionInput input_y; + std::array outputs; + + friend bool operator==(const EmbeddedCurveDouble&, const EmbeddedCurveDouble&); + std::vector bincodeSerialize() const; + static EmbeddedCurveDouble bincodeDeserialize(std::vector); + }; + struct Keccak256 { std::vector inputs; std::vector outputs; @@ -196,6 +218,8 @@ struct BlackBoxFuncCall { EcdsaSecp256k1, EcdsaSecp256r1, FixedBaseScalarMul, + EmbeddedCurveAdd, + EmbeddedCurveDouble, Keccak256, Keccak256VariableLength, Keccakf1600, @@ -497,6 +521,28 @@ struct BlackBoxOp { static FixedBaseScalarMul bincodeDeserialize(std::vector); }; + struct EmbeddedCurveAdd { + Circuit::RegisterIndex input1_x; + Circuit::RegisterIndex input1_y; + Circuit::RegisterIndex input2_x; + Circuit::RegisterIndex input2_y; + Circuit::HeapArray result; + + friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); + std::vector bincodeSerialize() const; + static EmbeddedCurveAdd bincodeDeserialize(std::vector); + }; + + struct EmbeddedCurveDouble { + Circuit::RegisterIndex input1_x; + Circuit::RegisterIndex input1_y; + Circuit::HeapArray result; + + friend bool operator==(const EmbeddedCurveDouble&, const EmbeddedCurveDouble&); + std::vector bincodeSerialize() const; + static EmbeddedCurveDouble bincodeDeserialize(std::vector); + }; + std::variant + FixedBaseScalarMul, + EmbeddedCurveAdd, + EmbeddedCurveDouble> value; friend bool operator==(const BlackBoxOp&, const BlackBoxOp&); @@ -2479,6 +2527,133 @@ Circuit::BlackBoxFuncCall::FixedBaseScalarMul serde::Deserializable< namespace Circuit { +inline bool operator==(const BlackBoxFuncCall::EmbeddedCurveAdd& lhs, const BlackBoxFuncCall::EmbeddedCurveAdd& rhs) +{ + if (!(lhs.input1_x == rhs.input1_x)) { + return false; + } + if (!(lhs.input1_y == rhs.input1_y)) { + return false; + } + if (!(lhs.input2_x == rhs.input2_x)) { + return false; + } + if (!(lhs.input2_y == rhs.input2_y)) { + return false; + } + if (!(lhs.outputs == rhs.outputs)) { + return false; + } + return true; +} + +inline std::vector BlackBoxFuncCall::EmbeddedCurveAdd::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxFuncCall::EmbeddedCurveAdd BlackBoxFuncCall::EmbeddedCurveAdd::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 Circuit + +template <> +template +void serde::Serializable::serialize( + const Circuit::BlackBoxFuncCall::EmbeddedCurveAdd& obj, Serializer& serializer) +{ + serde::Serializable::serialize(obj.input1_x, serializer); + serde::Serializable::serialize(obj.input1_y, serializer); + serde::Serializable::serialize(obj.input2_x, serializer); + serde::Serializable::serialize(obj.input2_y, serializer); + serde::Serializable::serialize(obj.outputs, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::EmbeddedCurveAdd serde::Deserializable< + Circuit::BlackBoxFuncCall::EmbeddedCurveAdd>::deserialize(Deserializer& deserializer) +{ + Circuit::BlackBoxFuncCall::EmbeddedCurveAdd obj; + obj.input1_x = serde::Deserializable::deserialize(deserializer); + obj.input1_y = serde::Deserializable::deserialize(deserializer); + obj.input2_x = serde::Deserializable::deserialize(deserializer); + obj.input2_y = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxFuncCall::EmbeddedCurveDouble& lhs, + const BlackBoxFuncCall::EmbeddedCurveDouble& rhs) +{ + if (!(lhs.input_x == rhs.input_x)) { + return false; + } + if (!(lhs.input_y == rhs.input_y)) { + return false; + } + if (!(lhs.outputs == rhs.outputs)) { + return false; + } + return true; +} + +inline std::vector BlackBoxFuncCall::EmbeddedCurveDouble::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxFuncCall::EmbeddedCurveDouble BlackBoxFuncCall::EmbeddedCurveDouble::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 Circuit + +template <> +template +void serde::Serializable::serialize( + const Circuit::BlackBoxFuncCall::EmbeddedCurveDouble& obj, Serializer& serializer) +{ + serde::Serializable::serialize(obj.input_x, serializer); + serde::Serializable::serialize(obj.input_y, serializer); + serde::Serializable::serialize(obj.outputs, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::EmbeddedCurveDouble serde::Deserializable< + Circuit::BlackBoxFuncCall::EmbeddedCurveDouble>::deserialize(Deserializer& deserializer) +{ + Circuit::BlackBoxFuncCall::EmbeddedCurveDouble obj; + obj.input_x = serde::Deserializable::deserialize(deserializer); + obj.input_y = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + inline bool operator==(const BlackBoxFuncCall::Keccak256& lhs, const BlackBoxFuncCall::Keccak256& rhs) { if (!(lhs.inputs == rhs.inputs)) { @@ -3282,6 +3457,130 @@ Circuit::BlackBoxOp::FixedBaseScalarMul serde::Deserializable BlackBoxOp::EmbeddedCurveAdd::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::EmbeddedCurveAdd BlackBoxOp::EmbeddedCurveAdd::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 Circuit + +template <> +template +void serde::Serializable::serialize( + const Circuit::BlackBoxOp::EmbeddedCurveAdd& obj, Serializer& serializer) +{ + serde::Serializable::serialize(obj.input1_x, serializer); + serde::Serializable::serialize(obj.input1_y, serializer); + serde::Serializable::serialize(obj.input2_x, serializer); + serde::Serializable::serialize(obj.input2_y, serializer); + serde::Serializable::serialize(obj.result, serializer); +} + +template <> +template +Circuit::BlackBoxOp::EmbeddedCurveAdd serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BlackBoxOp::EmbeddedCurveAdd obj; + obj.input1_x = serde::Deserializable::deserialize(deserializer); + obj.input1_y = serde::Deserializable::deserialize(deserializer); + obj.input2_x = serde::Deserializable::deserialize(deserializer); + obj.input2_y = serde::Deserializable::deserialize(deserializer); + obj.result = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp::EmbeddedCurveDouble& lhs, const BlackBoxOp::EmbeddedCurveDouble& rhs) +{ + if (!(lhs.input1_x == rhs.input1_x)) { + return false; + } + if (!(lhs.input1_y == rhs.input1_y)) { + return false; + } + if (!(lhs.result == rhs.result)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::EmbeddedCurveDouble::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::EmbeddedCurveDouble BlackBoxOp::EmbeddedCurveDouble::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 Circuit + +template <> +template +void serde::Serializable::serialize( + const Circuit::BlackBoxOp::EmbeddedCurveDouble& obj, Serializer& serializer) +{ + serde::Serializable::serialize(obj.input1_x, serializer); + serde::Serializable::serialize(obj.input1_y, serializer); + serde::Serializable::serialize(obj.result, serializer); +} + +template <> +template +Circuit::BlackBoxOp::EmbeddedCurveDouble serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BlackBoxOp::EmbeddedCurveDouble obj; + obj.input1_x = serde::Deserializable::deserialize(deserializer); + obj.input1_y = serde::Deserializable::deserialize(deserializer); + obj.result = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + inline bool operator==(const BlockId& lhs, const BlockId& rhs) { if (!(lhs.value == rhs.value)) { diff --git a/noir/acvm-repo/acir/codegen/acir.cpp b/noir/acvm-repo/acir/codegen/acir.cpp index 2b217c7e93d..d0b920e057c 100644 --- a/noir/acvm-repo/acir/codegen/acir.cpp +++ b/noir/acvm-repo/acir/codegen/acir.cpp @@ -145,6 +145,28 @@ namespace Circuit { static FixedBaseScalarMul bincodeDeserialize(std::vector); }; + struct EmbeddedCurveAdd { + Circuit::FunctionInput input1_x; + Circuit::FunctionInput input1_y; + Circuit::FunctionInput input2_x; + Circuit::FunctionInput input2_y; + std::array outputs; + + friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); + std::vector bincodeSerialize() const; + static EmbeddedCurveAdd bincodeDeserialize(std::vector); + }; + + struct EmbeddedCurveDouble { + Circuit::FunctionInput input_x; + Circuit::FunctionInput input_y; + std::array outputs; + + friend bool operator==(const EmbeddedCurveDouble&, const EmbeddedCurveDouble&); + std::vector bincodeSerialize() const; + static EmbeddedCurveDouble bincodeDeserialize(std::vector); + }; + struct Keccak256 { std::vector inputs; std::vector outputs; @@ -184,7 +206,7 @@ namespace Circuit { static RecursiveAggregation bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); std::vector bincodeSerialize() const; @@ -481,7 +503,29 @@ namespace Circuit { static FixedBaseScalarMul bincodeDeserialize(std::vector); }; - std::variant value; + struct EmbeddedCurveAdd { + Circuit::RegisterIndex input1_x; + Circuit::RegisterIndex input1_y; + Circuit::RegisterIndex input2_x; + Circuit::RegisterIndex input2_y; + Circuit::HeapArray result; + + friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); + std::vector bincodeSerialize() const; + static EmbeddedCurveAdd bincodeDeserialize(std::vector); + }; + + struct EmbeddedCurveDouble { + Circuit::RegisterIndex input1_x; + Circuit::RegisterIndex input1_y; + Circuit::HeapArray result; + + friend bool operator==(const EmbeddedCurveDouble&, const EmbeddedCurveDouble&); + std::vector bincodeSerialize() const; + static EmbeddedCurveDouble bincodeDeserialize(std::vector); + }; + + std::variant value; friend bool operator==(const BlackBoxOp&, const BlackBoxOp&); std::vector bincodeSerialize() const; @@ -2160,6 +2204,100 @@ Circuit::BlackBoxFuncCall::FixedBaseScalarMul serde::Deserializable BlackBoxFuncCall::EmbeddedCurveAdd::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxFuncCall::EmbeddedCurveAdd BlackBoxFuncCall::EmbeddedCurveAdd::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 Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::EmbeddedCurveAdd &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.input1_x, serializer); + serde::Serializable::serialize(obj.input1_y, serializer); + serde::Serializable::serialize(obj.input2_x, serializer); + serde::Serializable::serialize(obj.input2_y, serializer); + serde::Serializable::serialize(obj.outputs, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::EmbeddedCurveAdd serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::EmbeddedCurveAdd obj; + obj.input1_x = serde::Deserializable::deserialize(deserializer); + obj.input1_y = serde::Deserializable::deserialize(deserializer); + obj.input2_x = serde::Deserializable::deserialize(deserializer); + obj.input2_y = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxFuncCall::EmbeddedCurveDouble &lhs, const BlackBoxFuncCall::EmbeddedCurveDouble &rhs) { + if (!(lhs.input_x == rhs.input_x)) { return false; } + if (!(lhs.input_y == rhs.input_y)) { return false; } + if (!(lhs.outputs == rhs.outputs)) { return false; } + return true; + } + + inline std::vector BlackBoxFuncCall::EmbeddedCurveDouble::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxFuncCall::EmbeddedCurveDouble BlackBoxFuncCall::EmbeddedCurveDouble::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 Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::EmbeddedCurveDouble &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.input_x, serializer); + serde::Serializable::serialize(obj.input_y, serializer); + serde::Serializable::serialize(obj.outputs, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::EmbeddedCurveDouble serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::EmbeddedCurveDouble obj; + obj.input_x = serde::Deserializable::deserialize(deserializer); + obj.input_y = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + return obj; +} + namespace Circuit { inline bool operator==(const BlackBoxFuncCall::Keccak256 &lhs, const BlackBoxFuncCall::Keccak256 &rhs) { @@ -2780,6 +2918,100 @@ Circuit::BlackBoxOp::FixedBaseScalarMul serde::Deserializable BlackBoxOp::EmbeddedCurveAdd::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::EmbeddedCurveAdd BlackBoxOp::EmbeddedCurveAdd::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 Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::EmbeddedCurveAdd &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.input1_x, serializer); + serde::Serializable::serialize(obj.input1_y, serializer); + serde::Serializable::serialize(obj.input2_x, serializer); + serde::Serializable::serialize(obj.input2_y, serializer); + serde::Serializable::serialize(obj.result, serializer); +} + +template <> +template +Circuit::BlackBoxOp::EmbeddedCurveAdd serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::EmbeddedCurveAdd obj; + obj.input1_x = serde::Deserializable::deserialize(deserializer); + obj.input1_y = serde::Deserializable::deserialize(deserializer); + obj.input2_x = serde::Deserializable::deserialize(deserializer); + obj.input2_y = serde::Deserializable::deserialize(deserializer); + obj.result = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::EmbeddedCurveDouble &lhs, const BlackBoxOp::EmbeddedCurveDouble &rhs) { + if (!(lhs.input1_x == rhs.input1_x)) { return false; } + if (!(lhs.input1_y == rhs.input1_y)) { return false; } + if (!(lhs.result == rhs.result)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::EmbeddedCurveDouble::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::EmbeddedCurveDouble BlackBoxOp::EmbeddedCurveDouble::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 Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::EmbeddedCurveDouble &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.input1_x, serializer); + serde::Serializable::serialize(obj.input1_y, serializer); + serde::Serializable::serialize(obj.result, serializer); +} + +template <> +template +Circuit::BlackBoxOp::EmbeddedCurveDouble serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::EmbeddedCurveDouble obj; + obj.input1_x = serde::Deserializable::deserialize(deserializer); + obj.input1_y = serde::Deserializable::deserialize(deserializer); + obj.result = serde::Deserializable::deserialize(deserializer); + return obj; +} + namespace Circuit { inline bool operator==(const BlockId &lhs, const BlockId &rhs) { diff --git a/noir/acvm-repo/acir/src/circuit/black_box_functions.rs b/noir/acvm-repo/acir/src/circuit/black_box_functions.rs index 6e45a9a2c21..78bc4121de7 100644 --- a/noir/acvm-repo/acir/src/circuit/black_box_functions.rs +++ b/noir/acvm-repo/acir/src/circuit/black_box_functions.rs @@ -45,6 +45,10 @@ pub enum BlackBoxFunc { /// Compute a recursive aggregation object when verifying a proof inside another circuit. /// This outputted aggregation object will then be either checked in a top-level verifier or aggregated upon again. RecursiveAggregation, + /// Addition over the embedded curve on which [`FieldElement`][acir_field::FieldElement] is defined. + EmbeddedCurveAdd, + /// Point doubling over the embedded curve on which [`FieldElement`][acir_field::FieldElement] is defined. + EmbeddedCurveDouble } impl std::fmt::Display for BlackBoxFunc { @@ -64,6 +68,8 @@ impl BlackBoxFunc { BlackBoxFunc::PedersenHash => "pedersen_hash", BlackBoxFunc::EcdsaSecp256k1 => "ecdsa_secp256k1", BlackBoxFunc::FixedBaseScalarMul => "fixed_base_scalar_mul", + BlackBoxFunc::EmbeddedCurveAdd => "ec_add", + BlackBoxFunc::EmbeddedCurveDouble => "ec_double", BlackBoxFunc::AND => "and", BlackBoxFunc::XOR => "xor", BlackBoxFunc::RANGE => "range", @@ -84,6 +90,8 @@ impl BlackBoxFunc { "ecdsa_secp256k1" => Some(BlackBoxFunc::EcdsaSecp256k1), "ecdsa_secp256r1" => Some(BlackBoxFunc::EcdsaSecp256r1), "fixed_base_scalar_mul" => Some(BlackBoxFunc::FixedBaseScalarMul), + "ec_add" => Some(BlackBoxFunc::EmbeddedCurveAdd), + "ec_double" => Some(BlackBoxFunc::EmbeddedCurveDouble), "and" => Some(BlackBoxFunc::AND), "xor" => Some(BlackBoxFunc::XOR), "range" => Some(BlackBoxFunc::RANGE), diff --git a/noir/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/noir/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index 2ef9790221c..2ac4d558b68 100644 --- a/noir/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/noir/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -79,6 +79,18 @@ pub enum BlackBoxFuncCall { high: FunctionInput, outputs: (Witness, Witness), }, + EmbeddedCurveAdd { + input1_x: FunctionInput, + input1_y: FunctionInput, + input2_x: FunctionInput, + input2_y: FunctionInput, + outputs: (Witness, Witness), + }, + EmbeddedCurveDouble { + input_x: FunctionInput, + input_y: FunctionInput, + outputs: (Witness, Witness), + }, Keccak256 { inputs: Vec, outputs: Vec, @@ -125,6 +137,8 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::EcdsaSecp256k1 { .. } => BlackBoxFunc::EcdsaSecp256k1, BlackBoxFuncCall::EcdsaSecp256r1 { .. } => BlackBoxFunc::EcdsaSecp256r1, BlackBoxFuncCall::FixedBaseScalarMul { .. } => BlackBoxFunc::FixedBaseScalarMul, + BlackBoxFuncCall::EmbeddedCurveAdd { .. } => BlackBoxFunc::EmbeddedCurveAdd, + BlackBoxFuncCall::EmbeddedCurveDouble { .. } => BlackBoxFunc::EmbeddedCurveDouble, BlackBoxFuncCall::Keccak256 { .. } => BlackBoxFunc::Keccak256, BlackBoxFuncCall::Keccak256VariableLength { .. } => BlackBoxFunc::Keccak256, BlackBoxFuncCall::Keccakf1600 { .. } => BlackBoxFunc::Keccakf1600, @@ -149,6 +163,8 @@ impl BlackBoxFuncCall { vec![*lhs, *rhs] } BlackBoxFuncCall::FixedBaseScalarMul { low, high, .. } => vec![*low, *high], + BlackBoxFuncCall::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, .. } => vec![*input1_x, *input1_y, *input2_x, *input2_y], + BlackBoxFuncCall::EmbeddedCurveDouble { input_x, input_y, .. } => vec![*input_x, *input_y], BlackBoxFuncCall::RANGE { input } => vec![*input], BlackBoxFuncCall::SchnorrVerify { public_key_x, @@ -238,7 +254,9 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::PedersenHash { output, .. } | BlackBoxFuncCall::EcdsaSecp256r1 { output, .. } => vec![*output], BlackBoxFuncCall::FixedBaseScalarMul { outputs, .. } - | BlackBoxFuncCall::PedersenCommitment { outputs, .. } => vec![outputs.0, outputs.1], + | BlackBoxFuncCall::PedersenCommitment { outputs, .. } + | BlackBoxFuncCall::EmbeddedCurveAdd { outputs, .. } + | BlackBoxFuncCall::EmbeddedCurveDouble { outputs, .. } => vec![outputs.0, outputs.1], BlackBoxFuncCall::RANGE { .. } | BlackBoxFuncCall::RecursiveAggregation { .. } => vec![], BlackBoxFuncCall::Keccak256VariableLength { outputs, .. } => outputs.to_vec(), } diff --git a/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs b/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs index 14cb1ae9fa7..bb4dd9e66b2 100644 --- a/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -122,6 +122,14 @@ pub(super) fn transform_internal( outputs, .. } + | acir::circuit::opcodes::BlackBoxFuncCall::EmbeddedCurveAdd { + outputs, + .. + } + | acir::circuit::opcodes::BlackBoxFuncCall::EmbeddedCurveDouble { + outputs, + .. + } | acir::circuit::opcodes::BlackBoxFuncCall::PedersenCommitment { outputs, .. diff --git a/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs index f752924ac18..db58795c881 100644 --- a/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -177,6 +177,12 @@ pub(crate) fn solve( BlackBoxFuncCall::FixedBaseScalarMul { low, high, outputs } => { fixed_base_scalar_mul(backend, initial_witness, *low, *high, *outputs) } + BlackBoxFuncCall::EmbeddedCurveAdd { outputs, input1_x, input1_y, input2_x, input2_y } => { + todo!(); + } + BlackBoxFuncCall::EmbeddedCurveDouble { outputs, input_x, input_y } => { + todo!(); + } // Recursive aggregation will be entirely handled by the backend and is not solved by the ACVM BlackBoxFuncCall::RecursiveAggregation { .. } => Ok(()), } diff --git a/noir/acvm-repo/blackbox_solver/src/lib.rs b/noir/acvm-repo/blackbox_solver/src/lib.rs index ede648ef75e..6458f4e6f64 100644 --- a/noir/acvm-repo/blackbox_solver/src/lib.rs +++ b/noir/acvm-repo/blackbox_solver/src/lib.rs @@ -47,6 +47,18 @@ pub trait BlackBoxFunctionSolver { low: &FieldElement, high: &FieldElement, ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError>; + fn ec_add( + &self, + input1_x: &FieldElement, + input1_y: &FieldElement, + input2_x: &FieldElement, + input2_y: &FieldElement, + ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError>; + fn ec_double( + &self, + input_x: &FieldElement, + input_x: &FieldElement, + ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError>; } pub fn sha256(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> { diff --git a/noir/acvm-repo/bn254_blackbox_solver/src/lib.rs b/noir/acvm-repo/bn254_blackbox_solver/src/lib.rs index e315c4650be..80de89490dd 100644 --- a/noir/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/noir/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -87,4 +87,22 @@ impl BlackBoxFunctionSolver for Bn254BlackBoxSolver { ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { fixed_base_scalar_mul(low, high) } + + fn ec_add( + &self, + input1_x: &FieldElement, + input1_y: &FieldElement, + input2_x: &FieldElement, + input2_y: &FieldElement, + ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { + todo!(); + } + + fn ec_double( + &self, + input_x: &FieldElement, + input_y: &FieldElement, + ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { + todo!(); + } } diff --git a/noir/acvm-repo/brillig/src/black_box.rs b/noir/acvm-repo/brillig/src/black_box.rs index 41e54ab2705..4c755f8eae2 100644 --- a/noir/acvm-repo/brillig/src/black_box.rs +++ b/noir/acvm-repo/brillig/src/black_box.rs @@ -41,4 +41,8 @@ pub enum BlackBoxOp { PedersenHash { inputs: HeapVector, domain_separator: RegisterIndex, output: RegisterIndex }, /// Performs scalar multiplication over the embedded curve. FixedBaseScalarMul { low: RegisterIndex, high: RegisterIndex, result: HeapArray }, + /// Performs addtion over the embedded curve. + EmbeddedCurveAdd { input1_x: RegisterIndex, input1_y: RegisterIndex, input2_x: RegisterIndex, input2_y: RegisterIndex, result: HeapArray }, + /// Performs point doubling over the embedded curve. + EmbeddedCurveDouble { input1_x: RegisterIndex, input1_y: RegisterIndex, result: HeapArray }, } diff --git a/noir/acvm-repo/brillig_vm/src/black_box.rs b/noir/acvm-repo/brillig_vm/src/black_box.rs index bf05522f89a..1b67e52f9b0 100644 --- a/noir/acvm-repo/brillig_vm/src/black_box.rs +++ b/noir/acvm-repo/brillig_vm/src/black_box.rs @@ -140,6 +140,22 @@ pub(crate) fn evaluate_black_box( memory.write_slice(registers.get(result.pointer).to_usize(), &[x.into(), y.into()]); Ok(()) } + BlackBoxOp::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, result } => { + let input1_x = registers.get(*input1_x).to_field(); + let input1_y = registers.get(*input1_y).to_field(); + let input2_x = registers.get(*input2_x).to_field(); + let input2_y = registers.get(*input2_y).to_field(); + let (x, y) = solver.ec_add(&input1_x, &input1_y, &input2_x, &input2_y)?; + memory.write_slice(registers.get(result.pointer).to_usize(), &[x.into(), y.into()]); + Ok(()) + } + BlackBoxOp::EmbeddedCurveDouble { input1_x, input1_y, result } => { + let input1_x = registers.get(*input1_x).to_field(); + let input1_y = registers.get(*input1_y).to_field(); + let (x, y) = solver.ec_double(&input1_x, &input1_y)?; + memory.write_slice(registers.get(result.pointer).to_usize(), &[x.into(), y.into()]); + Ok(()) + } BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { let inputs: Vec = read_heap_vector(memory, registers, inputs).iter().map(|x| x.to_field()).collect(); diff --git a/noir/acvm-repo/brillig_vm/src/lib.rs b/noir/acvm-repo/brillig_vm/src/lib.rs index 482b9b36d77..df4c8135bce 100644 --- a/noir/acvm-repo/brillig_vm/src/lib.rs +++ b/noir/acvm-repo/brillig_vm/src/lib.rs @@ -451,6 +451,22 @@ impl BlackBoxFunctionSolver for DummyBlackBoxSolver { ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { Ok((4_u128.into(), 5_u128.into())) } + fn ec_add( + &self, + _input1_x: &FieldElement, + _input1_y: &FieldElement, + _input2_x: &FieldElement, + _input2_y: &FieldElement, + ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { + Ok((5_u128.into(), 6_u128.into())) + } + fn ec_double( + &self, + _input1_x: &FieldElement, + _input1_y: &FieldElement, + ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { + Ok((7_u128.into(), 8_u128.into())) + } } #[cfg(test)] diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index a6d3220fa85..8235cbf6340 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -169,6 +169,43 @@ pub(crate) fn convert_black_box_call( ) } } + BlackBoxFunc::EmbeddedCurveAdd => { + if let ( + [BrilligVariable::Simple(input1_x), BrilligVariable::Simple(input1_y), + BrilligVariable::Simple(input2_x), BrilligVariable::Simple(input2_y)], + [BrilligVariable::BrilligArray(result_array)], + ) = (function_arguments, function_results) + { + brillig_context.black_box_op_instruction(BlackBoxOp::EmbeddedCurveAdd { + input1_x: *input1_x, + input1_y: *input1_y, + input2_x: *input2_x, + input2_y: *input2_y, + result: result_array.to_heap_array(), + }); + } else { + unreachable!( + "ICE: EmbeddedCurveAdd expects four register arguments and one array result" + ) + } + } + BlackBoxFunc::EmbeddedCurveDouble => { + if let ( + [BrilligVariable::Simple(input1_x), BrilligVariable::Simple(input1_y)], + [BrilligVariable::BrilligArray(result_array)], + ) = (function_arguments, function_results) + { + brillig_context.black_box_op_instruction(BlackBoxOp::EmbeddedCurveDouble { + input1_x: *input1_x, + input1_y: *input1_y, + result: result_array.to_heap_array(), + }); + } else { + unreachable!( + "ICE: EmbeddedCurveAdd expects two register arguments and one array result" + ) + } + } BlackBoxFunc::AND => { unreachable!("ICE: `BlackBoxFunc::AND` calls should be transformed into a `BinaryOp`") } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index aae74849b8c..0d35d948694 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -396,6 +396,26 @@ impl DebugShow { result ); } + BlackBoxOp::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, result } => { + debug_println!( + self.enable_debug_trace, + " EMBEDDED_CURVE_ADD ({} {}) ({} {}) -> {}", + input1_x, + input1_y, + input2_x, + input2_y, + result + ); + } + BlackBoxOp::EmbeddedCurveDouble { input1_x, input1_y, result } => { + debug_println!( + self.enable_debug_trace, + " EMBEDDED_CURVE_DOUBLE ({} {}) -> {}", + input1_x, + input1_y, + result + ); + } BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { debug_println!( self.enable_debug_trace, diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index ddafc0bb570..f8b8579eb66 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1749,6 +1749,28 @@ fn execute_brillig( "FixedBaseScalarMul is not supported".to_string(), )) } + fn ec_add( + &self, + _input1_x: &FieldElement, + _input1_y: &FieldElement, + _input2_x: &FieldElement, + _input2_y: &FieldElement, + ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { + Err(BlackBoxResolutionError::Failed( + BlackBoxFunc::EmbeddedCurveAdd, + "EcAdd is not supported".to_string(), + )) + } + fn ec_double( + &self, + _input_x: &FieldElement, + _input_y: &FieldElement, + ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { + Err(BlackBoxResolutionError::Failed( + BlackBoxFunc::EmbeddedCurveDouble, + "EcDouble is not supported".to_string(), + )) + } } // Set input values diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index 20055aac8ad..04152127660 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -205,6 +205,18 @@ impl GeneratedAcir { high: inputs[1][0], outputs: (outputs[0], outputs[1]), }, + BlackBoxFunc::EmbeddedCurveAdd => BlackBoxFuncCall::EmbeddedCurveAdd { + input1_x: inputs[0][0], + input1_y: inputs[1][0], + input2_x: inputs[2][0], + input2_y: inputs[3][0], + outputs: (outputs[0], outputs[1]), + }, + BlackBoxFunc::EmbeddedCurveDouble => BlackBoxFuncCall::EmbeddedCurveDouble { + input_x: inputs[0][0], + input_y: inputs[1][0], + outputs: (outputs[0], outputs[1]), + }, BlackBoxFunc::Keccak256 => { let var_message_size = match inputs.to_vec().pop() { Some(var_message_size) => var_message_size[0], @@ -581,6 +593,10 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option { BlackBoxFunc::FixedBaseScalarMul => Some(2), // Recursive aggregation has a variable number of inputs BlackBoxFunc::RecursiveAggregation => None, + // Addition over the embedded curve: input are coordinates (x1,y1) and (x2,y2) of the Grumpkin points + BlackBoxFunc::EmbeddedCurveAdd => Some(4), + // Doubling over the embedded curve: input is (x,y) coordinate of the point. + BlackBoxFunc::EmbeddedCurveDouble => Some(2), } } @@ -608,9 +624,11 @@ fn black_box_expected_output_size(name: BlackBoxFunc) -> Option { BlackBoxFunc::SchnorrVerify | BlackBoxFunc::EcdsaSecp256k1 | BlackBoxFunc::EcdsaSecp256r1 => Some(1), - // Output of fixed based scalar mul over the embedded curve + // Output of operations over the embedded curve // will be 2 field elements representing the point. - BlackBoxFunc::FixedBaseScalarMul => Some(2), + BlackBoxFunc::FixedBaseScalarMul + | BlackBoxFunc::EmbeddedCurveAdd + | BlackBoxFunc::EmbeddedCurveDouble => Some(2), // Recursive aggregation has a variable number of outputs BlackBoxFunc::RecursiveAggregation => None, } diff --git a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index f77e84d99e0..5b3a1669bff 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -406,7 +406,9 @@ fn simplify_black_box_func( BlackBoxFunc::FixedBaseScalarMul | BlackBoxFunc::SchnorrVerify | BlackBoxFunc::PedersenCommitment - | BlackBoxFunc::PedersenHash => { + | BlackBoxFunc::PedersenHash + | BlackBoxFunc::EmbeddedCurveAdd + | BlackBoxFunc::EmbeddedCurveDouble => { // Currently unsolvable here as we rely on an implementation in the backend. SimplifyResult::None } diff --git a/noir/tooling/lsp/src/solver.rs b/noir/tooling/lsp/src/solver.rs index 090f71d63b4..5d95c88e49b 100644 --- a/noir/tooling/lsp/src/solver.rs +++ b/noir/tooling/lsp/src/solver.rs @@ -39,6 +39,24 @@ impl BlackBoxFunctionSolver for WrapperSolver { ) -> Result { self.0.pedersen_hash(inputs, domain_separator) } + + fn ec_add( + &self, + input1_x: &acvm::FieldElement, + input1_y: &acvm::FieldElement, + input2_x: &acvm::FieldElement, + input2_y: &acvm::FieldElement, + ) -> Result<(acvm::FieldElement, acvm::FieldElement), acvm::BlackBoxResolutionError> { + self.0.ec_add(input1_x, input1_y, input2_x, input2_y) + } + + fn ec_double( + &self, + input_x: &acvm::FieldElement, + input_y: &acvm::FieldElement, + ) -> Result<(acvm::FieldElement, acvm::FieldElement), acvm::BlackBoxResolutionError> { + self.0.ec_double(input_x, input_y) + } } // We also have a mocked implementation of the `BlackBoxFunctionSolver` trait for use in tests