From 4461ee41dd46f19be4ba3b785bbccbfecd67e9da 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 +++++++++++++++++- 10 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp create mode 100644 cpp/src/barretenberg/dsl/acir_format/ec_operations.hpp diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 912c23e77..03a3c1713 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/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/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index a2f198e2f..b458b578c 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/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/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index 4b6fdcd52..5e61e2d4b 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/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/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp index 047390783..9aba7ec59 100644 --- a/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp +++ b/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/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp b/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp new file mode 100644 index 000000000..fc657cceb --- /dev/null +++ b/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/cpp/src/barretenberg/dsl/acir_format/ec_operations.hpp b/cpp/src/barretenberg/dsl/acir_format/ec_operations.hpp new file mode 100644 index 000000000..f6d8e6168 --- /dev/null +++ b/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/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index 4dfdff06f..cc9a5a2e6 100644 --- a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/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/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp index deb95e656..f840d2b59 100644 --- a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp +++ b/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/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 5defd40ed..4e2db15d4 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/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/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp b/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp index 82e57cad6..b3c2106a4 100644 --- a/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp +++ b/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)) {