From ccb7bff8e16ea9c8bc4bd48754db59857137507e Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Thu, 25 Jan 2024 14:09:05 +0100 Subject: [PATCH] feat: Implement Embedded EC add and double opcodes (#3982) This PR resolves the following points from the issue https://github.com/noir-lang/noir/issues/3983: - Add ACVM solvers for ECADD and ECDOUBLE - Generate circuits for ECADD and ECDOUBLE in BB/dsl - Add unit test in BB/DSL for these new opcodes - Add integration test in Noir demonstrating the use of the opcodes --- .../dsl/acir_format/acir_format.cpp | 4 +- .../acir_format/acir_to_constraint_buf.hpp | 16 +++ .../dsl/acir_format/ec_operations.cpp | 75 ++++++++-- .../dsl/acir_format/ec_operations.hpp | 6 +- .../dsl/acir_format/ec_operations.test.cpp | 133 ++++++++++++++++++ .../acir/src/circuit/black_box_functions.rs | 8 +- .../src/pwg/blackbox/fixed_base_scalar_mul.rs | 31 ++++ noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs | 17 ++- .../src/fixed_base_scalar_mul.rs | 20 +++ .../bn254_blackbox_solver/src/lib.rs | 18 +-- noir/noir_stdlib/src/scalar_mul.nr | 12 ++ .../execution_success/scalar_mul/src/main.nr | 14 ++ 12 files changed, 321 insertions(+), 33 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp 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 0fb5cebbe55..59c7275d7eb 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -81,12 +81,12 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo // Add ec add constraints for (const auto& constraint : constraint_system.ec_add_constraints) { - create_ec_add_constraint(builder, constraint); + create_ec_add_constraint(builder, constraint, has_valid_witness_assignments); } // Add ec double for (const auto& constraint : constraint_system.ec_double_constraints) { - create_ec_double_constraint(builder, constraint); + create_ec_double_constraint(builder, constraint, has_valid_witness_assignments); } // Add block 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 5d6cf0063b8..e68dfb68d9c 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 @@ -205,6 +205,22 @@ void handle_blackbox_func_call(Circuit::Opcode::BlackBoxFuncCall const& arg, Aci .pub_key_x = arg.outputs[0].value, .pub_key_y = arg.outputs[1].value, }); + } else if constexpr (std::is_same_v) { + af.ec_add_constraints.push_back(EcAdd{ + .input1_x = arg.input1_x.witness.value, + .input1_y = arg.input1_y.witness.value, + .input2_x = arg.input2_x.witness.value, + .input2_y = arg.input2_y.witness.value, + .result_x = arg.outputs[0].value, + .result_y = arg.outputs[1].value, + }); + } else if constexpr (std::is_same_v) { + af.ec_double_constraints.push_back(EcDouble{ + .input_x = arg.input_x.witness.value, + .input_y = arg.input_y.witness.value, + .result_x = arg.outputs[0].value, + .result_y = arg.outputs[1].value, + }); } else if constexpr (std::is_same_v) { af.keccak_constraints.push_back(KeccakConstraint{ .inputs = map(arg.inputs, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp index fc657ccebb1..0919ec81caa 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp @@ -2,30 +2,81 @@ #include "barretenberg/dsl/types.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" +#include "barretenberg/ecc/groups/affine_element.hpp" #include "barretenberg/proof_system/arithmetization/gate_data.hpp" namespace acir_format { -template void create_ec_add_constraint(Builder& builder, const EcAdd& input) +template +void create_ec_add_constraint(Builder& builder, const EcAdd& input, bool has_valid_witness_assignments) { - // TODO - builder.assert_equal(input.input1_x, input.input1_x); - ASSERT(false); + // Input to cycle_group points + using cycle_group_ct = bb::stdlib::cycle_group; + using field_ct = bb::stdlib::field_t; + + auto x1 = field_ct::from_witness_index(&builder, input.input1_x); + auto y1 = field_ct::from_witness_index(&builder, input.input1_y); + auto x2 = field_ct::from_witness_index(&builder, input.input2_x); + auto y2 = field_ct::from_witness_index(&builder, input.input2_y); + if (!has_valid_witness_assignments) { + auto g1 = grumpkin::g1::affine_one; + // We need to have correct values representing points on the curve + builder.variables[input.input1_x] = g1.x; + builder.variables[input.input1_y] = g1.y; + builder.variables[input.input2_x] = g1.x; + builder.variables[input.input2_y] = g1.y; + } + + cycle_group_ct input1_point(x1, y1, false); + cycle_group_ct input2_point(x2, y2, false); + + // Addition + cycle_group_ct result = input1_point + input2_point; + + auto x_normalized = result.x.normalize(); + auto y_normalized = result.y.normalize(); + builder.assert_equal(x_normalized.witness_index, input.result_x); + builder.assert_equal(y_normalized.witness_index, input.result_y); } -template void create_ec_add_constraint(UltraCircuitBuilder& builder, const EcAdd& input); +template void create_ec_add_constraint(UltraCircuitBuilder& builder, + const EcAdd& input, + bool has_valid_witness_assignments); template void create_ec_add_constraint(GoblinUltraCircuitBuilder& builder, - const EcAdd& input); + const EcAdd& input, + bool has_valid_witness_assignments); -template void create_ec_double_constraint(Builder& builder, const EcDouble& input) +template +void create_ec_double_constraint(Builder& builder, const EcDouble& input, bool has_valid_witness_assignments) { - // TODO - builder.assert_equal(input.input_x, input.input_x); - ASSERT(false); + using cycle_group_ct = bb::stdlib::cycle_group; + using field_ct = bb::stdlib::field_t; + // Input to cycle_group point + auto x = field_ct::from_witness_index(&builder, input.input_x); + auto y = field_ct::from_witness_index(&builder, input.input_y); + + if (!has_valid_witness_assignments) { + auto g1 = grumpkin::g1::affine_one; + // We need to have correct values representing point on the curve + builder.variables[input.input_x] = g1.x; + builder.variables[input.input_y] = g1.y; + } + cycle_group_ct input_point(x, y, false); + + // Doubling + cycle_group_ct result = input_point.dbl(); + + auto x_normalized = result.x.normalize(); + auto y_normalized = result.y.normalize(); + builder.assert_equal(x_normalized.witness_index, input.result_x); + builder.assert_equal(y_normalized.witness_index, input.result_y); } -template void create_ec_double_constraint(UltraCircuitBuilder& builder, const EcDouble& input); +template void create_ec_double_constraint(UltraCircuitBuilder& builder, + const EcDouble& input, + bool has_valid_witness_assignments); template void create_ec_double_constraint(GoblinUltraCircuitBuilder& builder, - const EcDouble& input); + const EcDouble& input, + bool has_valid_witness_assignments); } // 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 index f6d8e6168eb..be378192cb9 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.hpp @@ -18,7 +18,8 @@ struct EcAdd { friend bool operator==(EcAdd const& lhs, EcAdd const& rhs) = default; }; -template void create_ec_add_constraint(Builder& builder, const EcAdd& input); +template +void create_ec_add_constraint(Builder& builder, const EcAdd& input, bool has_valid_witness_assignments); struct EcDouble { uint32_t input_x; @@ -31,5 +32,6 @@ struct EcDouble { friend bool operator==(EcDouble const& lhs, EcDouble const& rhs) = default; }; -template void create_ec_double_constraint(Builder& builder, const EcDouble& input); +template +void create_ec_double_constraint(Builder& builder, const EcDouble& input, bool has_valid_witness_assignments); } // namespace acir_format 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 new file mode 100644 index 00000000000..ea5fb82c5d2 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp @@ -0,0 +1,133 @@ +#include "ec_operations.hpp" +#include "acir_format.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" + +#include +#include + +namespace acir_format::tests { +using curve_ct = bb::stdlib::secp256k1; + +class EcOperations : public ::testing::Test { + protected: + static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } +}; + +size_t generate_ec_add_constraint(EcAdd& ec_add_constraint, WitnessVector& witness_values) +{ + using cycle_group_ct = bb::stdlib::cycle_group; + witness_values.push_back(0); + auto g1 = grumpkin::g1::affine_one; + cycle_group_ct input_point(g1); + // Doubling + cycle_group_ct result = input_point.dbl(); + // add: x,y,x2,y2 + witness_values.push_back(g1.x); + witness_values.push_back(g1.y); + witness_values.push_back(g1.x); + witness_values.push_back(g1.y); + witness_values.push_back(result.x.get_value()); + witness_values.push_back(result.y.get_value()); + ec_add_constraint = EcAdd{ + .input1_x = 1, + .input1_y = 2, + .input2_x = 3, + .input2_y = 4, + .result_x = 5, + .result_y = 6, + }; + return witness_values.size(); +} + +size_t generate_ec_double_constraint(EcDouble& ec_double_constraint, WitnessVector& witness_values) +{ + + using cycle_group_ct = bb::stdlib::cycle_group; + auto g1 = grumpkin::g1::affine_one; + cycle_group_ct input_point(g1); + // Doubling + cycle_group_ct result = input_point.dbl(); + // add: x,y,x2,y2 + uint32_t result_x_witness_index = static_cast(witness_values.size()); + + witness_values.push_back(result.x.get_value()); + uint32_t result_y_witness_index = static_cast(witness_values.size()); + witness_values.push_back(result.y.get_value()); + ec_double_constraint = EcDouble{ + .input_x = 1, + .input_y = 2, + .result_x = result_x_witness_index, + .result_y = result_y_witness_index, + }; + return witness_values.size(); +} + +TEST_F(EcOperations, TestECOperations) +{ + EcAdd ec_add_constraint; + EcDouble ec_double_constraint; + + WitnessVector witness_values; + generate_ec_add_constraint(ec_add_constraint, witness_values); + size_t num_variables = generate_ec_double_constraint(ec_double_constraint, witness_values); + + poly_triple constrain_5_is_7{ + .a = 5, + .b = 7, + .c = 0, + .q_m = 0, + .q_l = 1, + .q_r = -1, + .q_o = 0, + .q_c = 0, + }; + poly_triple constrain_6_is_8{ + .a = 6, + .b = 8, + .c = 0, + .q_m = 0, + .q_l = 1, + .q_r = -1, + .q_o = 0, + .q_c = 0, + }; + + AcirFormat constraint_system{ + .varnum = static_cast(num_variables + 1), + .public_inputs = {}, + .logic_constraints = {}, + .range_constraints = {}, + .sha256_constraints = {}, + .schnorr_constraints = {}, + .ecdsa_k1_constraints = {}, + .ecdsa_r1_constraints = {}, + .blake2s_constraints = {}, + .blake3_constraints = {}, + .keccak_constraints = {}, + .keccak_var_constraints = {}, + .keccak_permutations = {}, + .pedersen_constraints = {}, + .pedersen_hash_constraints = {}, + .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = { ec_add_constraint }, + .ec_double_constraints = { ec_double_constraint }, + .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, + .constraints = { constrain_5_is_7, constrain_6_is_8 }, + .block_constraints = {}, + }; + + auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness_values); + + auto composer = Composer(); + auto prover = composer.create_prover(builder); + + auto proof = prover.construct_proof(); + EXPECT_TRUE(builder.check_circuit()); + auto verifier = composer.create_verifier(builder); + EXPECT_EQ(verifier.verify_proof(proof), true); +} + +} // namespace acir_format::tests 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 41b8923a8c9..83c1a27bb88 100644 --- a/noir/acvm-repo/acir/src/circuit/black_box_functions.rs +++ b/noir/acvm-repo/acir/src/circuit/black_box_functions.rs @@ -80,8 +80,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::EmbeddedCurveAdd => "embedded_curve_add", + BlackBoxFunc::EmbeddedCurveDouble => "embedded_curve_double", BlackBoxFunc::AND => "and", BlackBoxFunc::XOR => "xor", BlackBoxFunc::RANGE => "range", @@ -109,8 +109,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), + "embedded_curve_add" => Some(BlackBoxFunc::EmbeddedCurveAdd), + "embedded_curve_double" => Some(BlackBoxFunc::EmbeddedCurveDouble), "and" => Some(BlackBoxFunc::AND), "xor" => Some(BlackBoxFunc::XOR), "range" => Some(BlackBoxFunc::RANGE), diff --git a/noir/acvm-repo/acvm/src/pwg/blackbox/fixed_base_scalar_mul.rs b/noir/acvm-repo/acvm/src/pwg/blackbox/fixed_base_scalar_mul.rs index 582ed56584b..2bf16419232 100644 --- a/noir/acvm-repo/acvm/src/pwg/blackbox/fixed_base_scalar_mul.rs +++ b/noir/acvm-repo/acvm/src/pwg/blackbox/fixed_base_scalar_mul.rs @@ -23,3 +23,34 @@ pub(super) fn fixed_base_scalar_mul( Ok(()) } + +pub(super) fn embedded_curve_double( + backend: &impl BlackBoxFunctionSolver, + initial_witness: &mut WitnessMap, + input_x: FunctionInput, + input_y: FunctionInput, + outputs: (Witness, Witness), +) -> Result<(), OpcodeResolutionError> { + embedded_curve_add(backend, initial_witness, input_x, input_y, input_x, input_y, outputs) +} + +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/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs index a56b24b86f3..99eff68dd23 100644 --- a/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -18,6 +18,7 @@ mod range; mod signature; use fixed_base_scalar_mul::fixed_base_scalar_mul; +use fixed_base_scalar_mul::{embedded_curve_add, embedded_curve_double}; // Hash functions should eventually be exposed for external consumers. use hash::solve_generic_256_hash_opcode; use logic::{and, xor}; @@ -177,11 +178,19 @@ pub(crate) fn solve( BlackBoxFuncCall::FixedBaseScalarMul { low, high, outputs } => { fixed_base_scalar_mul(backend, initial_witness, *low, *high, *outputs) } - BlackBoxFuncCall::EmbeddedCurveAdd { .. } => { - todo!(); + BlackBoxFuncCall::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, outputs } => { + embedded_curve_add( + backend, + initial_witness, + *input1_x, + *input1_y, + *input2_x, + *input2_y, + *outputs, + ) } - BlackBoxFuncCall::EmbeddedCurveDouble { .. } => { - todo!(); + BlackBoxFuncCall::EmbeddedCurveDouble { input_x, input_y, outputs } => { + embedded_curve_double(backend, initial_witness, *input_x, *input_y, *outputs) } // 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/bn254_blackbox_solver/src/fixed_base_scalar_mul.rs b/noir/acvm-repo/bn254_blackbox_solver/src/fixed_base_scalar_mul.rs index 7f004de0fe9..5e68c7d4030 100644 --- a/noir/acvm-repo/bn254_blackbox_solver/src/fixed_base_scalar_mul.rs +++ b/noir/acvm-repo/bn254_blackbox_solver/src/fixed_base_scalar_mul.rs @@ -47,6 +47,26 @@ pub fn fixed_base_scalar_mul( } } +pub fn embedded_curve_add( + input1_x: FieldElement, + input1_y: FieldElement, + input2_x: FieldElement, + input2_y: FieldElement, +) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { + let mut point1 = grumpkin::SWAffine::new(input1_x.into_repr(), input1_y.into_repr()); + let point2 = grumpkin::SWAffine::new(input2_x.into_repr(), input2_y.into_repr()); + let res = point1 + point2; + point1 = res.into(); + if let Some((res_x, res_y)) = point1.xy() { + Ok((FieldElement::from_repr(*res_x), FieldElement::from_repr(*res_y))) + } else { + Err(BlackBoxResolutionError::Failed( + BlackBoxFunc::EmbeddedCurveAdd, + "Point is not on curve".to_string(), + )) + } +} + #[cfg(test)] mod grumpkin_fixed_base_scalar_mul { use ark_ff::BigInteger; diff --git a/noir/acvm-repo/bn254_blackbox_solver/src/lib.rs b/noir/acvm-repo/bn254_blackbox_solver/src/lib.rs index 92c45e93dea..f4b866b5882 100644 --- a/noir/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/noir/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -8,7 +8,7 @@ use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError}; mod fixed_base_scalar_mul; mod wasm; -pub use fixed_base_scalar_mul::fixed_base_scalar_mul; +pub use fixed_base_scalar_mul::{embedded_curve_add, fixed_base_scalar_mul}; use wasm::Barretenberg; use self::wasm::{Pedersen, SchnorrSig}; @@ -90,19 +90,19 @@ impl BlackBoxFunctionSolver for Bn254BlackBoxSolver { fn ec_add( &self, - _input1_x: &FieldElement, - _input1_y: &FieldElement, - _input2_x: &FieldElement, - _input2_y: &FieldElement, + input1_x: &FieldElement, + input1_y: &FieldElement, + input2_x: &FieldElement, + input2_y: &FieldElement, ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - todo!(); + embedded_curve_add(*input1_x, *input1_y, *input2_x, *input2_y) } fn ec_double( &self, - _input_x: &FieldElement, - _input_y: &FieldElement, + input_x: &FieldElement, + input_y: &FieldElement, ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - todo!(); + embedded_curve_add(*input_x, *input_y, *input_x, *input_y) } } diff --git a/noir/noir_stdlib/src/scalar_mul.nr b/noir/noir_stdlib/src/scalar_mul.nr index 37cd935cdb9..e1d0ff4d453 100644 --- a/noir/noir_stdlib/src/scalar_mul.nr +++ b/noir/noir_stdlib/src/scalar_mul.nr @@ -1,3 +1,8 @@ +struct EmbeddedCurvePoint { + x: Field, + y: Field, +} + // Computes a fixed base scalar multiplication over the embedded curve. // For bn254, We have Grumpkin and Baby JubJub. // For bls12-381, we have JubJub and Bandersnatch. @@ -6,3 +11,10 @@ // underlying proof system. #[foreign(fixed_base_scalar_mul)] pub fn fixed_base_embedded_curve(_low: Field, _high: Field) -> [Field; 2] {} + + +#[foreign(embedded_curve_add)] +pub fn embedded_curve_add(_point1: EmbeddedCurvePoint, _point2: EmbeddedCurvePoint) -> EmbeddedCurvePoint {} + +#[foreign(embedded_curve_double)] +pub fn embedded_curve_double(_point: EmbeddedCurvePoint) -> EmbeddedCurvePoint {} diff --git a/noir/test_programs/execution_success/scalar_mul/src/main.nr b/noir/test_programs/execution_success/scalar_mul/src/main.nr index 2ddf22cf71e..a88754da5d3 100644 --- a/noir/test_programs/execution_success/scalar_mul/src/main.nr +++ b/noir/test_programs/execution_success/scalar_mul/src/main.nr @@ -20,4 +20,18 @@ fn main( 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 = std::scalar_mul::embedded_curve_double(pub_point); + let double = std::scalar_mul::embedded_curve_add(g1,g1 ); + + assert(double.x == res.x); }