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 "../memory/rom_table.hpp"
15#include "../memory/twin_rom_table.hpp"
25template <
class Builder_,
class Fq,
class Fr,
class NativeGroup>
class element {
47 element(
const typename NativeGroup::affine_element& input);
63 const typename NativeGroup::affine_element& native_val = NativeGroup::affine_element::one();
67 for (
auto& limb : val.
x.binary_basis_limbs) {
68 limb_vals[idx++] = limb.element.get_value();
70 for (
auto& limb : val.
y.binary_basis_limbs) {
71 limb_vals[idx++] = limb.element.get_value();
84 const uint32_t start_idx =
x.set_public();
116 if (input.is_point_at_infinity()) {
117 Fq x = Fq::from_witness(ctx, NativeGroup::affine_one.
x);
118 Fq y = Fq::from_witness(ctx, NativeGroup::affine_one.
y);
122 Fq x = Fq::from_witness(ctx, input.x);
123 Fq y = Fq::from_witness(ctx, input.y);
146 if constexpr (!NativeGroup::has_a) {
148 Fq::mult_madd({ _x.
sqr(), _y }, { _x, -_y }, { _b },
true);
153 Fq::mult_madd({ _x.
sqr(), _x, _y }, { _x, _a, -_y }, { _b },
true);
156 if ((!has_circuit_failed) && (
get_context()->failed())) {
157 vinfo(
"Original bigfield error generated by biggroup::validate_on_curve: ",
get_context()->err());
167 this->
x.convert_constant_to_fixed_witness(builder);
168 this->
y.convert_constant_to_fixed_witness(builder);
179 this->
x.fix_witness();
180 this->
y.fix_witness();
200 Fr zero = Fr::from_witness_index(ctx, ctx->zero_idx());
201 zero.unset_free_witness_tag();
220 result.
write(
y.to_byte_array());
221 result.
write(
x.to_byte_array());
233 result.
y = -result.
y;
238 *
this = *
this + other;
243 *
this = *
this - other;
253 result.
y = result.
y.conditional_negate(predicate);
268 auto result = predicate.
get_value() ? other : *
this;
276 BB_ASSERT_NEQ(ctx,
nullptr,
"biggroup::conditional_select must have a context");
279 result.
x = result.
x.conditional_select(other.
x, predicate);
280 result.
y = result.
y.conditional_select(other.
y, predicate);
297 const std::string msg =
"biggroup::incomplete_assert_equal")
const
300 x.assert_equal(other.
x, msg +
" (x coordinate)");
301 y.assert_equal(other.
y, msg +
" (y coordinate)");
307 result.
x.reduce_mod_target_modulus();
308 result.
y.reduce_mod_target_modulus();
316 result.
x.self_reduce();
317 result.
y.self_reduce();
361 uint512_t x_val =
x.get_value() % Fq::modulus_u512;
362 uint512_t y_val =
y.get_value() % Fq::modulus_u512;
363 auto result =
typename NativeGroup::affine_element(x_val.
lo, y_val.
lo);
365 result.self_set_infinity();
371 const std::vector<Fr>& _scalars);
380 template <
size_t max_num_bits = 0>
383 const std::vector<Fr>& scalars,
384 const size_t max_num_bits = 0,
385 const bool with_edgecases =
false);
399 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, bb::g1>::value>>
402 const std::vector<Fr>& big_scalars,
404 const std::vector<Fr>& small_scalars,
405 const size_t max_num_small_bits);
407 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, bb::g1>::value>>
410 const std::vector<Fr>& big_scalars,
412 const std::vector<Fr>& small_scalars,
413 const Fr& generator_scalar,
414 const size_t max_num_small_bits);
416 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, secp256k1::g1>::value>>
421 template <
size_t max_num_bits = 0,
size_t WNAF_SIZE = 4>
424 template <
size_t wnaf_size,
size_t staggered_lo_offset = 0,
size_t staggered_hi_offset = 0>
429 if (
x.context !=
nullptr) {
432 if (
y.context !=
nullptr) {
440 if (
x.context !=
nullptr) {
443 if (
y.context !=
nullptr) {
446 if (other.
x.context !=
nullptr) {
447 return other.
x.context;
449 if (other.
y.context !=
nullptr) {
450 return other.
y.context;
459 if (add_to_used_witnesses) {
467 x.set_origin_tag(tag);
468 y.set_origin_tag(tag);
482 x.unset_free_witness_tag();
483 y.unset_free_witness_tag();
492 x.set_free_witness_tag();
493 y.set_free_witness_tag();
520 template <
size_t num_bits,
size_t wnaf_size,
size_t lo_stagger,
size_t hi_stagger>
525 const bool range_constrain_wnaf =
true,
537 template <
size_t wnaf_size>
539 const uint64_t stagger,
556 template <
size_t wnaf_size>
558 const uint64_t* wnaf_values,
561 const bool range_constrain_wnaf =
true);
574 template <
size_t wnaf_size>
580 const size_t stagger,
581 const size_t rounds);
583 template <
size_t num_elements>
587 template <
size_t num_elements>
692 for (
size_t i = 0; i < 8; ++i) {
693 endo_table.
element_table[i + 8].x = base_table[7 - i].x * beta;
736 remaining_points -= 4;
742 remaining_points -= 3;
748 remaining_points -= 2;
759 "point allocation mismatch");
765 points[
offset + (6 * i) + 1],
766 points[
offset + (6 * i) + 2],
767 points[
offset + (6 * i) + 3],
768 points[
offset + (6 * i) + 4],
769 points[
offset + (6 * i) + 5],
776 points[
offset + (5 * i) + 1],
777 points[
offset + (5 * i) + 2],
778 points[
offset + (5 * i) + 3],
779 points[
offset + (5 * i) + 4],
796 singletons.push_back(points[points.size() - 1]);
822 element accumulator = add_accumulator[0];
823 for (
size_t i = 1; i < add_accumulator.size(); ++i) {
824 accumulator = accumulator + add_accumulator[i];
850 if (add_accumulator.size() >= 2) {
852 for (
size_t i = 2; i < add_accumulator.size(); ++i) {
853 output = element::chain_add(add_accumulator[i], output);
864 round_accumulator.push_back(
six_tables[j].
get({ naf_entries[6 * j],
865 naf_entries[(6 * j) + 1],
866 naf_entries[(6 * j) + 2],
867 naf_entries[(6 * j) + 3],
868 naf_entries[(6 * j) + 4],
869 naf_entries[(6 * j) + 5] }));
871 size_t offset = num_sixes * 6;
874 naf_entries[
offset + (j * 5) + 1],
875 naf_entries[
offset + (j * 5) + 2],
876 naf_entries[
offset + (j * 5) + 3],
877 naf_entries[
offset + (j * 5) + 4] }));
884 naf_entries[
offset + 3] }));
888 round_accumulator.push_back(
898 element::chain_add_accumulator accumulator;
899 if (round_accumulator.size() == 1) {
900 return element::chain_add_accumulator(round_accumulator[0]);
903 if (round_accumulator.size() == 2) {
904 return element::chain_add_start(round_accumulator[0], round_accumulator[1]);
908 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
909 for (
size_t j = 2; j < round_accumulator.size(); ++j) {
910 accumulator = element::chain_add(round_accumulator[j], accumulator);
913 return (accumulator);
920 round_accumulator.push_back(
six_tables[j].
get({ naf_entries[(6 * j)],
921 naf_entries[(6 * j) + 1],
922 naf_entries[(6 * j) + 2],
923 naf_entries[(6 * j) + 3],
924 naf_entries[(6 * j) + 4],
925 naf_entries[(6 * j) + 5] }));
927 size_t offset = num_sixes * 6;
931 naf_entries[
offset + (5 * j) + 1],
932 naf_entries[
offset + (5 * j) + 2],
933 naf_entries[
offset + (5 * j) + 3],
934 naf_entries[
offset + (5 * j) + 4] }));
944 round_accumulator.push_back(
954 element result = round_accumulator[0];
955 element::chain_add_accumulator accumulator;
956 if (round_accumulator.size() == 1) {
960 if (round_accumulator.size() == 2) {
961 return result + round_accumulator[1];
965 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
966 for (
size_t j = 2; j < round_accumulator.size(); ++j) {
967 accumulator = element::chain_add(round_accumulator[j], accumulator);
970 return element::chain_add_end(accumulator);
995 template <
typename C,
typename Fq,
typename Fr,
typename G,
size_t wnaf_size>
1002 fragment_u64, stagger, is_negative, wnaf_skew);
1006template <
typename C,
typename Fq,
typename Fr,
typename G>
1009 return os <<
"{ " << v.
x <<
" , " << v.
y <<
" }";
1014template <
typename T>
1017template <
typename Builder,
class Fq,
class Fr,
class NativeGroup>
1027template <
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()
uint32_t get_normalized_witness_index() const
static bool_t conditional_assign(const bool_t< Builder > &predicate, const bool_t &lhs, const bool_t &rhs)
Implements the ternary operator - if predicate == true then return lhs, else return rhs.
void unset_free_witness_tag()
Builder * get_context() 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
Represents a dynamic array of bytes in-circuit.
byte_array & write(byte_array const &other)
Appends the contents of another byte_array (other) to the end of this one.
static auto get_staggered_wnaf_fragment_value(uint64_t fragment_u64, uint64_t stagger, bool is_negative, bool wnaf_skew)
element checked_unconditional_subtract(const element &other) const
element & operator=(const element &other)
byte_array< Builder > to_byte_array() const
Serialize the element to a byte array in form: (yhi || ylo || xhi || xlo).
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)
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.
element montgomery_ladder(const element &other) const
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.
static element chain_add_end(const chain_add_accumulator &accumulator)
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 std::vector< field_t< Builder > > compute_wnaf(const Fr &scalar)
static NativeGroup::affine_element compute_table_offset_generator()
Compute an offset generator for use in biggroup tables.
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)
static element bn254_endo_batch_mul_with_generator(const std::vector< element > &big_points, const std::vector< Fr > &big_scalars, const std::vector< element > &small_points, const std::vector< Fr > &small_scalars, const Fr &generator_scalar, const size_t max_num_small_bits)
static element read_group_element_rom_tables(const std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > &tables, const field_t< Builder > &index, const std::array< uint256_t, Fq::NUM_LIMBS *2 > &limb_max)
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)
element quadruple_and_add(const std::vector< element > &to_add) const
Compute 4.P + to_add[0] + ... + to_add[to_add.size() - 1].
OriginTag get_origin_tag() const
static element bn254_endo_batch_mul(const std::vector< element > &big_points, const std::vector< Fr > &big_scalars, const std::vector< element > &small_points, const std::vector< Fr > &small_scalars, const size_t max_num_small_bits)
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.
static std::array< fr, PUBLIC_INPUTS_SIZE > construct_dummy()
Construct a dummy element (the group generator) and return its limbs as fr constants.
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 secp256k1_wnaf_pair compute_secp256k1_endo_wnaf(const Fr &scalar, const bool range_constrain_wnaf=true)
static element wnaf_batch_mul(const std::vector< element > &points, const std::vector< Fr > &scalars)
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)
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 Fr reconstruct_bigfield_from_wnaf(Builder *builder, const std::vector< field_t< Builder > > &wnaf, const bool_ct &positive_skew, const bool_ct &negative_skew, const field_t< Builder > &stagger_fragment, const size_t stagger, const size_t rounds)
Reconstruct a scalar from its wNAF representation in circuit.
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)
Replace all pairs (∞, scalar) by the pair (one, 0) where one is a fixed generator of the curve.
void set_free_witness_tag()
Set the free witness flag for the element's tags.
static std::vector< field_t< Builder > > 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.
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 (*this) + other AND (*this) - other as a size-2 array.
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)
static std::pair< std::vector< element >, std::vector< Fr > > mask_points(const std::vector< element > &_points, const std::vector< Fr > &_scalars)
Given two lists of points that need to be multiplied by scalars, create a new list of length +1 with ...
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)
Generic batch multiplication that works for all elliptic curve types.
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.
#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...
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
element get_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.
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
element operator[](const field_t< Builder > &index) const
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
element operator[](const field_t< Builder > &index) const
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
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