9#include "../bigfield/bigfield.hpp"
10#include "../bigfield/goblin_field.hpp"
11#include "../byte_array/byte_array.hpp"
12#include "../circuit_builders/circuit_builders_fwd.hpp"
13#include "../field/field.hpp"
14#include "../field/field_utils.hpp"
15#include "../memory/rom_table.hpp"
16#include "../memory/twin_rom_table.hpp"
26template <
class Builder_,
class Fq,
class Fr,
class NativeGroup>
class element {
39 element(
const typename NativeGroup::affine_element& input);
43 element(
const Fq&
x,
const Fq&
y,
const bool assert_on_curve =
true);
58 const uint32_t start_idx =
_x.set_public();
92 if (input.is_point_at_infinity()) {
93 Fq x = Fq::from_witness(ctx, NativeGroup::affine_one.
x);
94 Fq y = Fq::from_witness(ctx, NativeGroup::affine_one.
y);
98 Fq x = Fq::from_witness(ctx, input.x);
99 Fq y = Fq::from_witness(ctx, input.y);
122 if constexpr (!NativeGroup::has_a) {
124 Fq::mult_madd({ adjusted_x.
sqr(), adjusted_y }, { adjusted_x, -adjusted_y }, { adjusted_b },
true);
129 Fq::mult_madd({ adjusted_x.
sqr(), adjusted_x, adjusted_y },
130 { adjusted_x, adjusted_a, -adjusted_y },
135 if ((!has_circuit_failed) && (
get_context()->failed())) {
136 vinfo(
"Original bigfield error generated by biggroup::validate_on_curve: ",
get_context()->err());
143 const bool x_is_const =
_x.is_constant();
144 const bool y_is_const =
_y.is_constant();
145 BB_ASSERT_EQ(x_is_const, y_is_const,
"biggroup: x and y coordinate constant status mismatch");
154 this->
_x.convert_constant_to_fixed_witness(builder);
155 this->
_y.convert_constant_to_fixed_witness(builder);
166 this->
_x.fix_witness();
167 this->
_y.fix_witness();
182 return element(x_fq, y_fq,
false);
187 Fr zero = Fr::from_witness_index(ctx, ctx->zero_idx());
188 zero.unset_free_witness_tag();
191 element result(x_fq, y_fq,
false);
207 result.
_y = -result.
_y;
212 *
this = *
this + other;
217 *
this = *
this - other;
226 result.
_y = result.
_y.conditional_negate(predicate);
241 auto result = predicate.
get_value() ? other : *
this;
249 BB_ASSERT_NEQ(ctx,
nullptr,
"biggroup::conditional_select must have a context");
252 result.
_x = result.
_x.conditional_select(other.
_x, predicate);
253 result.
_y = result.
_y.conditional_select(other.
_y, predicate);
270 const std::string msg =
"biggroup::incomplete_assert_equal")
const
273 _x.assert_equal(other.
_x, msg +
" (x coordinate)");
274 _y.assert_equal(other.
_y, msg +
" (y coordinate)");
280 result.
_x.reduce_mod_target_modulus();
281 result.
_y.reduce_mod_target_modulus();
289 result.
_x.self_reduce();
290 result.
_y.self_reduce();
296 _x.assert_is_in_field(msg +
" (x coordinate)");
297 _y.assert_is_in_field(msg +
" (y coordinate)");
317 , is_full_element(true)
338 uint512_t x_val =
_x.get_value() % Fq::modulus_u512;
339 uint512_t y_val =
_y.get_value() % Fq::modulus_u512;
340 auto result =
typename NativeGroup::affine_element(x_val.
lo, y_val.
lo);
342 result.self_set_infinity();
348 const std::vector<Fr>& scalars,
349 const size_t max_num_bits = 0,
350 const bool with_edgecases =
false,
351 const Fr& masking_scalar =
Fr(1));
353 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, secp256k1::g1>::value>>
381 template <
size_t wnaf_size,
size_t staggered_lo_offset = 0,
size_t staggered_hi_offset = 0>
399 if (add_to_used_witnesses) {
407 _x.set_origin_tag(
tag);
408 _y.set_origin_tag(
tag);
414 _x.clear_round_provenance();
415 _y.clear_round_provenance();
429 _x.unset_free_witness_tag();
430 _y.unset_free_witness_tag();
439 _x.set_free_witness_tag();
440 _y.set_free_witness_tag();
468 const std::vector<Fr>& _scalars,
469 const Fr& masking_scalar);
496 template <
size_t num_bits,
size_t wnaf_size,
size_t lo_stagger,
size_t hi_stagger>
501 const bool range_constrain_wnaf =
true,
513 template <
size_t wnaf_size>
515 const uint64_t stagger,
532 template <
size_t wnaf_size>
534 const uint64_t* wnaf_values,
537 const bool range_constrain_wnaf =
true);
550 template <
size_t wnaf_size>
556 const size_t stagger,
557 const size_t rounds);
559 template <
size_t num_elements>
563 template <
size_t num_elements>
668 for (
size_t i = 0; i < 8; ++i) {
669 endo_table.
element_table[i + 8].x = base_table[7 - i].x * beta;
712 remaining_points -= 4;
718 remaining_points -= 3;
724 remaining_points -= 2;
735 "point allocation mismatch");
741 points[
offset + (6 * i) + 1],
742 points[
offset + (6 * i) + 2],
743 points[
offset + (6 * i) + 3],
744 points[
offset + (6 * i) + 4],
745 points[
offset + (6 * i) + 5],
752 points[
offset + (5 * i) + 1],
753 points[
offset + (5 * i) + 2],
754 points[
offset + (5 * i) + 3],
755 points[
offset + (5 * i) + 4],
772 singletons.push_back(points[points.size() - 1]);
797 if (add_accumulator.size() >= 2) {
799 for (
size_t i = 2; i < add_accumulator.size(); ++i) {
800 output = element::chain_add(add_accumulator[i], output);
811 round_accumulator.push_back(
six_tables[j].
get({ naf_entries[6 * j],
812 naf_entries[(6 * j) + 1],
813 naf_entries[(6 * j) + 2],
814 naf_entries[(6 * j) + 3],
815 naf_entries[(6 * j) + 4],
816 naf_entries[(6 * j) + 5] }));
818 size_t offset = num_sixes * 6;
821 naf_entries[
offset + (j * 5) + 1],
822 naf_entries[
offset + (j * 5) + 2],
823 naf_entries[
offset + (j * 5) + 3],
824 naf_entries[
offset + (j * 5) + 4] }));
831 naf_entries[
offset + 3] }));
835 round_accumulator.push_back(
845 if (round_accumulator.size() == 1) {
846 return element::chain_add_accumulator(round_accumulator[0]);
849 if (round_accumulator.size() == 2) {
850 return element::chain_add_start(round_accumulator[0], round_accumulator[1]);
854 element::chain_add_accumulator accumulator =
855 element::chain_add_start(round_accumulator[0], round_accumulator[1]);
856 for (
size_t j = 2; j < round_accumulator.size(); ++j) {
857 accumulator = element::chain_add(round_accumulator[j], accumulator);
860 return (accumulator);
867 round_accumulator.push_back(
six_tables[j].
get({ naf_entries[(6 * j)],
868 naf_entries[(6 * j) + 1],
869 naf_entries[(6 * j) + 2],
870 naf_entries[(6 * j) + 3],
871 naf_entries[(6 * j) + 4],
872 naf_entries[(6 * j) + 5] }));
874 size_t offset = num_sixes * 6;
878 naf_entries[
offset + (5 * j) + 1],
879 naf_entries[
offset + (5 * j) + 2],
880 naf_entries[
offset + (5 * j) + 3],
881 naf_entries[
offset + (5 * j) + 4] }));
891 round_accumulator.push_back(
901 element result = round_accumulator[0];
902 element::chain_add_accumulator accumulator;
903 if (round_accumulator.size() == 1) {
907 if (round_accumulator.size() == 2) {
908 return result + round_accumulator[1];
912 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
913 for (
size_t j = 2; j < round_accumulator.size(); ++j) {
914 accumulator = element::chain_add(round_accumulator[j], accumulator);
917 return element::chain_add_end(accumulator);
939 const std::vector<Fr>& scalars,
940 const size_t max_num_bits);
946 template <
typename C,
typename Fq,
typename Fr,
typename G,
size_t wnaf_size>
953 fragment_u64, stagger, is_negative, wnaf_skew);
956 template <
typename C,
typename Fq,
typename Fr,
typename G>
963 template <
typename C,
typename Fq,
typename Fr,
typename G>
971template <
typename C,
typename Fq,
typename Fr,
typename G>
974 return os <<
"{ " << v.
x() <<
" , " << v.
y() <<
" }";
982template <
typename Builder,
class Fq,
class Fr,
class NativeGroup>
992template <
typename C,
typename Fq,
typename Fr,
typename G>
#define BB_ASSERT_NEQ(actual, expected,...)
#define BB_ASSERT_EQ(actual, expected,...)
constexpr uint256_t slice(uint64_t start, uint64_t end) const
Implements boolean logic in-circuit.
void set_origin_tag(const OriginTag &new_tag) const
bool_t normalize() const
A bool_t element is normalized if witness_inverted == false. For a given *this, output its normalized...
void set_free_witness_tag()
static bool_t conditional_assign(const bool_t< Builder > &predicate, const bool_t &lhs, const bool_t &rhs)
Conditionally assign lhs or rhs based on predicate, always returns normalized result.
void unset_free_witness_tag()
Builder * get_context() const
void clear_round_provenance() const
void assert_equal(const bool_t &rhs, std::string const &msg="bool_t::assert_equal") const
Implements copy constraint for bool_t elements.
OriginTag get_origin_tag() const
static auto checked_unconditional_add_sub(const element_goblin::goblin_element< C, Fq, Fr, G > &elem1, const element_goblin::goblin_element< C, Fq, Fr, G > &elem2)
static auto get_staggered_wnaf_fragment_value(uint64_t fragment_u64, uint64_t stagger, bool is_negative, bool wnaf_skew)
static auto checked_unconditional_add_sub(const element< C, Fq, Fr, G > &elem1, const element< C, Fq, Fr, G > &elem2)
element checked_unconditional_subtract(const element &other) const
element & operator=(const element &other)
void assert_coordinates_in_field(const std::string &msg="biggroup::assert_coordinates_in_field") const
element operator-=(const element &other)
NativeGroup::affine_element get_value() const
static std::pair< Fr, secp256k1_wnaf > compute_secp256k1_single_wnaf(Builder *builder, const secp256k1::fr &scalar, size_t stagger, bool is_negative, const bool range_constrain_wnaf=true, bool is_lo=false)
Compute the wNAF representation (in circuit) of a scalar for secp256k1.
Builder * get_context(const element &other) const
element multiple_montgomery_ladder(const std::vector< chain_add_accumulator > &to_add) const
Perform repeated iterations of the montgomery ladder algorithm.
void validate_on_curve(std::string const &msg="biggroup::validate_on_curve") const
Check that the point is on the curve.
static element one(Builder *ctx)
Creates a constant group generator.
static chain_add_accumulator chain_add_start(const element &p1, const element &p2)
Begin a chain of additions.
static element from_witness(Builder *ctx, const typename NativeGroup::affine_element &input)
Create a biggroup witness from a native group element, allocating new witnesses as necessary.
void fix_witness()
Fix a witness. The value of the witness is constrained with a selector.
static std::pair< four_bit_table_plookup, four_bit_table_plookup > create_endo_pair_four_bit_table_plookup(const element &input)
Create a endo pair four bit table for the given group element.
void clear_round_provenance() const
static element chain_add_end(const chain_add_accumulator &accumulator)
End an addition chain and compute the final y-coordinate.
static element batch_mul(const std::vector< element > &points, const std::vector< Fr > &scalars, const size_t max_num_bits=0, const bool with_edgecases=false, const Fr &masking_scalar=Fr(1))
Generic batch multiplication that works for all elliptic curve types.
element operator-() const
void unset_free_witness_tag()
Unset the free witness flag for the element's tags.
static element point_at_infinity(Builder *ctx)
static NativeGroup::affine_element compute_table_offset_generator()
Compute an offset generator for use in biggroup tables.
static std::vector< field_ct > convert_wnaf_values_to_witnesses(Builder *builder, const uint64_t *wnaf_values, bool is_negative, size_t rounds, const bool range_constrain_wnaf=true)
Convert wNAF values to witness values.
void set_origin_tag(OriginTag tag) const
void incomplete_assert_equal(const element &other, const std::string msg="biggroup::incomplete_assert_equal") const
Asserts that two group elements are equal (i.e., x, y coordinates and infinity flag are all equal).
void set_point_at_infinity(const bool_ct &is_infinity, const bool &add_to_used_witnesses=false)
stdlib::witness_t< Builder > witness_ct
static std::pair< quad_lookup_table, quad_lookup_table > create_endo_pair_quad_lookup_table(const std::array< element, 4 > &inputs)
element normalize() const
element(const typename NativeGroup::affine_element &input)
static element process_strauss_msm_rounds(const std::vector< element > &points, const std::vector< Fr > &scalars, const size_t max_num_bits)
OriginTag get_origin_tag() const
static Fr reconstruct_bigfield_from_wnaf(Builder *builder, const std::vector< field_ct > &wnaf, const bool_ct &positive_skew, const bool_ct &negative_skew, const field_ct &stagger_fragment, const size_t stagger, const size_t rounds)
Reconstruct a scalar from its wNAF representation in circuit.
static std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > create_group_element_rom_tables(const std::array< element, num_elements > &rom_data, std::array< uint256_t, Fq::NUM_LIMBS *2 > &limb_max)
void convert_constant_to_fixed_witness(Builder *builder)
Creates fixed witnesses from a constant element.
element scalar_mul(const Fr &scalar, const size_t max_num_bits=0) const
Implements scalar multiplication that supports short scalars. For multiple scalar multiplication use ...
element operator+=(const element &other)
static element read_group_element_rom_tables(const std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > &tables, const field_ct &index, const std::array< uint256_t, Fq::NUM_LIMBS *2 > &limb_max)
static secp256k1_wnaf_pair compute_secp256k1_endo_wnaf(const Fr &scalar, const bool range_constrain_wnaf=true)
Compute endomorphism for a secp256k1 scalar, and then compute the wNAF representation of both halves.
element checked_unconditional_add(const element &other) const
static std::vector< bool_ct > compute_naf(const Fr &scalar, const size_t max_num_bits=0)
Compute Non-Adjacent Form (NAF) representation of a scalar.
static std::pair< uint64_t, bool > get_staggered_wnaf_fragment_value(const uint64_t fragment_u64, const uint64_t stagger, bool is_negative, bool wnaf_skew)
Compute the stagger-related part of wNAF and the final skew.
uint32_t set_public() const
Set the witness indices for the x and y coordinates to public.
static std::pair< std::vector< element >, std::vector< Fr > > mask_points(const std::vector< element > &_points, const std::vector< Fr > &_scalars, const Fr &masking_scalar)
Mask points for batch multiplication to handle edge cases.
Builder * get_context() const
element operator*(const Fr &scalar) const
element conditional_negate(const bool_ct &predicate) const
static std::pair< std::vector< element >, std::vector< Fr > > handle_points_at_infinity(const std::vector< element > &_points, const std::vector< Fr > &_scalars)
Handle points at infinity in batch operations, replaces (∞, scalar) pairs by (G, 0)
void set_free_witness_tag()
Set the free witness flag for the element's tags.
element conditional_select(const element &other, const bool_ct &predicate) const
Selects this if predicate is false, other if predicate is true.
element get_standard_form() const
Enforce x and y coordinates of a point to be (0, 0) in the case of point at infinity.
static element reconstruct_from_public(const std::span< const Fr, PUBLIC_INPUTS_SIZE > &limbs)
Reconstruct a biggroup element from limbs of its coordinates (generally stored in the public inputs)
std::array< element, 2 > checked_unconditional_add_sub(const element &other) const
Compute both add and subtract (a + b, a - b) results simultaneously.
static constexpr size_t PUBLIC_INPUTS_SIZE
element operator+(const element &other) const
static chain_add_accumulator chain_add(const element &p1, const chain_add_accumulator &accumulator)
Evaluate a chain addition using incomplete addition formulae.
static std::pair< element, element > compute_offset_generators(const size_t num_rounds)
bool_ct is_point_at_infinity() const
static element secp256k1_ecdsa_mul(const element &pubkey, const Fr &u1, const Fr &u2)
Custom element class for when using goblin.
std::array< goblin_element, 2 > checked_unconditional_add_sub(const goblin_element &other) const
#define G(r, i, a, b, c, d)
uint8_t const size_t length
std::ostream & operator<<(std::ostream &os, element< C, Fq, Fr, G > const &v)
std::conditional_t< IsGoblinBigGroup< C, Fq, Fr, G >, element_goblin::goblin_element< C, goblin_field< C >, Fr, G >, element_default::element< C, Fq, Fr, G > > element
element wraps either element_default::element or element_goblin::goblin_element depending on parametr...
void mark_witness_as_used(const field_t< Builder > &field)
Mark a field_t witness as used (for UltraBuilder only).
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
static constexpr field cube_root_of_unity()
static constexpr size_t PUBLIC_INPUTS_SIZE
BB_INLINE constexpr field sqr() const noexcept
static field reconstruct_from_public(const std::span< const field< V >, PUBLIC_INPUTS_SIZE > &limbs)
static constexpr field zero()
element get(std::vector< bool_ct > &naf_entries) const
chain_add_accumulator get_chain_initial_entry() const
std::vector< quad_lookup_table > quad_tables
std::vector< lookup_table_plookup< 6 > > six_tables
std::vector< lookup_table_plookup< 5 > > five_tables
std::vector< element > singletons
std::vector< triple_lookup_table > triple_tables
element::chain_add_accumulator get_chain_add_accumulator(std::vector< bool_ct > &naf_entries) const
batch_lookup_table_plookup(const std::vector< element > &points)
std::vector< twin_lookup_table > twin_tables
chain_add_accumulator()=default
chain_add_accumulator(chain_add_accumulator &&other) noexcept=default
chain_add_accumulator(const chain_add_accumulator &other)=default
chain_add_accumulator(const element &input)
~chain_add_accumulator()=default
chain_add_accumulator & operator=(const chain_add_accumulator &other)=default
chain_add_accumulator & operator=(chain_add_accumulator &&other) noexcept=default
Eight-bit fixed base table for scalar multiplication.
element operator[](const field_ct &index) const
eight_bit_fixed_base_table & operator=(eight_bit_fixed_base_table &&other) noexcept=default
~eight_bit_fixed_base_table()=default
eight_bit_fixed_base_table & operator=(const eight_bit_fixed_base_table &other)=default
eight_bit_fixed_base_table(const CurveType input_curve_type, bool use_endo)
eight_bit_fixed_base_table(eight_bit_fixed_base_table &&other) noexcept=default
eight_bit_fixed_base_table(const eight_bit_fixed_base_table &other)=default
Four-bit variable-base table for scalar multiplication.
std::array< uint256_t, Fq::NUM_LIMBS *2 > limb_max
four_bit_table_plookup & operator=(four_bit_table_plookup &&other) noexcept=default
four_bit_table_plookup(const four_bit_table_plookup &other)=default
std::array< element, 16 > element_table
four_bit_table_plookup()=default
~four_bit_table_plookup()=default
four_bit_table_plookup(four_bit_table_plookup &&other) noexcept=default
std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > coordinates
element operator[](const size_t idx) const
four_bit_table_plookup & operator=(const four_bit_table_plookup &other)=default
element operator[](const field_ct &index) const
Generic lookup table that uses ROM tables internally to access group elements.
std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > coordinates
lookup_table_plookup(const lookup_table_plookup &other)=default
std::array< element, table_size > element_table
lookup_table_plookup(lookup_table_plookup &&other) noexcept=default
~lookup_table_plookup()=default
lookup_table_plookup & operator=(lookup_table_plookup &&other) noexcept=default
lookup_table_plookup & operator=(const lookup_table_plookup &other)=default
element operator[](const size_t idx) const
lookup_table_plookup()=default
static constexpr size_t table_size
element get(const std::array< bool_ct, length > &bits) const
std::array< uint256_t, Fq::NUM_LIMBS *2 > limb_max
std::vector< field_t< Builder > > wnaf
field_t< Builder > least_significant_wnaf_fragment
curve::BN254::BaseField Fq