Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
straus_lookup_table.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
8#include "./cycle_group.hpp"
10
11namespace bb::stdlib {
12
26template <typename Builder>
28 const Element& base_point, const Element& offset_generator, size_t table_bits)
29{
30 const size_t table_size = 1UL << table_bits;
31 std::vector<Element> hints;
32 hints.emplace_back(offset_generator);
33 for (size_t i = 1; i < table_size; ++i) {
34 hints.emplace_back(hints[i - 1] + base_point);
35 }
36 return hints;
37}
38
50template <typename Builder>
52 const cycle_group<Builder>& base_point,
53 const cycle_group<Builder>& offset_generator,
54 size_t table_bits,
56 : _context(context)
57 , tag(OriginTag(base_point.get_origin_tag(), offset_generator.get_origin_tag()))
58{
59 const size_t table_size = 1UL << table_bits;
61 point_table.resize(table_size);
62
63 // We want to support the case where input points are points at infinity.
64 // If base point is at infinity, we want every point in the table to just be `generator_point`.
65 // We achieve this via the following:
66 // 1: We create a "work_point" that is base_point if not at infinity, else it is set (arbitrarily) to "one"
67 // 2: When computing the point table, we use "work_point" in additions instead of the "base_point" (to prevent
68 // x-coordinate collisions in honest case) 3: When assigning to the point table, we conditionally assign either
69 // the output of the point addition (if not at infinity) or the generator point (if at infinity)
70 // 3: If point at infinity, conditionally (re)assign each entry in the table to be equal to the offset
71 // generator so that the final table is genuninely correct in all cases. (Otherwise, the table is unchanged
72 // from step 2)
73 cycle_group<Builder> fallback_point(Group::affine_one);
74 field_t modded_x = field_t::conditional_assign(base_point.is_point_at_infinity(), fallback_point.x, base_point.x);
75 field_t modded_y = field_t::conditional_assign(base_point.is_point_at_infinity(), fallback_point.y, base_point.y);
76 cycle_group<Builder> modded_base_point(modded_x, modded_y, false);
77 // We assume that the native hints (if present) do not account for the point at infinity edge case in the same way
78 // as above (i.e. replacing with "one") so we avoid using any provided hints in this case. (N.B. No efficiency is
79 // lost here since native addition with the point at infinity is nearly free).
80 const bool hints_available = hints.has_value() && !base_point.is_point_at_infinity().get_value();
81 auto get_hint = [&](size_t i) -> std::optional<AffineElement> {
82 if (!hints_available) {
83 return std::nullopt;
84 }
85 BB_ASSERT_LT(i, hints.value().size(), "Invalid hint index");
86 return std::optional<AffineElement>(hints.value()[i]);
87 };
88
89 if (base_point.is_constant() && !base_point.is_point_at_infinity().get_value()) {
90 // Case 1: if the input point is constant, it is cheaper to fix the point as a witness and then derive the
91 // table, than it is to derive the table and fix its witnesses to be constant! (due to group additions = 1 gate,
92 // and fixing x/y coords to be constant = 2 gates)
93 modded_base_point = cycle_group<Builder>::from_constant_witness(_context, modded_base_point.get_value());
94 point_table[0] = cycle_group<Builder>::from_constant_witness(_context, offset_generator.get_value());
95 for (size_t i = 1; i < table_size; ++i) {
96 point_table[i] = point_table[i - 1].unconditional_add(modded_base_point, get_hint(i - 1));
97 }
98 } else {
99 // Case 2: Point is non-constant so the table is derived via unconditional additions. We check the x_coordinates
100 // of all summand pairs are distinct via a batched product check to avoid individual modular inversions.
101 field_t coordinate_check_product = 1;
102 point_table[0] = offset_generator;
103 for (size_t i = 1; i < table_size; ++i) {
104 const field_t x_diff = point_table[i - 1].x - modded_base_point.x;
105 coordinate_check_product *= x_diff;
106 point_table[i] = point_table[i - 1].unconditional_add(modded_base_point, get_hint(i - 1));
107 }
108 coordinate_check_product.assert_is_not_zero("straus_lookup_table x-coordinate collision");
109
110 // If the input base point was the point at infinity, the correct point table simply contains the offset
111 // generator at every entry. However, since we replaced the point at infinity with "one" when computing the
112 // table (see explanation above), we must conditionally correct the table entries here.
113 for (size_t i = 1; i < table_size; ++i) {
115 base_point.is_point_at_infinity(), offset_generator, point_table[i]);
116 }
117 }
118
119 // Construct a ROM array containing the point table
120 rom_id = context->create_ROM_array(table_size);
121 for (size_t i = 0; i < table_size; ++i) {
122 // Convert any constant points to witnesses constrained to equal the constant value for use in ROM array
123 if (point_table[i].is_constant()) {
124 const auto element = point_table[i].get_value();
126 }
127 std::array<uint32_t, 2> coordinate_indices = { point_table[i].x.get_witness_index(),
128 point_table[i].y.get_witness_index() };
129 context->set_ROM_element_pair(rom_id, i, coordinate_indices);
130 }
131}
132
142template <typename Builder> cycle_group<Builder> straus_lookup_table<Builder>::read(const field_t& _index)
143{
144 field_t index(_index);
145 // A ROM array index must be a witness; we convert constants to a witness constrained to equal the constant value
146 if (index.is_constant()) {
147 index = field_t::from_witness(_context, _index.get_value());
148 index.assert_equal(_index.get_value());
149 }
150 auto [x_idx, y_idx] = _context->read_ROM_array_pair(rom_id, index.get_witness_index());
151 field_t x = field_t::from_witness_index(_context, x_idx);
152 field_t y = field_t::from_witness_index(_context, y_idx);
153 // Merge tag of table with tag of index
154 x.set_origin_tag(OriginTag(tag, _index.get_origin_tag()));
155 y.set_origin_tag(OriginTag(tag, _index.get_origin_tag()));
156
157 // The result is known to not be the point at infinity due to the use of offset generators in the table
158 return cycle_group<Builder>(x, y, /*is_infinity=*/false);
159}
160
163
164} // namespace bb::stdlib
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:148
bool get_value() const
Definition bool.hpp:111
cycle_group represents a group Element of the proving system's embedded curve, i.e....
static cycle_group from_constant_witness(Builder *_context, const AffineElement &_in)
Converts a native AffineElement into a witness, but constrains the witness values to be known constan...
static cycle_group conditional_assign(const bool_t &predicate, const cycle_group &lhs, const cycle_group &rhs)
bool_t is_point_at_infinity() const
AffineElement get_value() const
void assert_equal(const field_t &rhs, std::string const &msg="field_t::assert_equal") const
Copy constraint: constrain that *this field is equal to rhs element.
Definition field.cpp:931
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
Definition field.cpp:61
static field_t conditional_assign(const bool_t< Builder > &predicate, const field_t &lhs, const field_t &rhs)
If predicate == true then return lhs, else return rhs.
Definition field.cpp:886
OriginTag get_origin_tag() const
Definition field.hpp:333
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:829
static field_t from_witness(Builder *ctx, const bb::fr &input)
Definition field.hpp:432
bool is_constant() const
Definition field.hpp:407
void set_origin_tag(const OriginTag &new_tag) const
Definition field.hpp:332
void assert_is_not_zero(std::string const &msg="field_t::assert_is_not_zero") const
Constrain *this to be non-zero by establishing that it has an inverse.
Definition field.cpp:709
uint32_t get_witness_index() const
Get the witness index of the current field element.
Definition field.hpp:469
straus_lookup_table computes a lookup table of size 1 << table_bits
static std::vector< Element > compute_native_table(const Element &base_point, const Element &offset_generator, size_t table_bits)
Compute the output points generated when computing the Straus lookup table.
cycle_group< Builder > read(const field_t &index)
Given an _index witness, return straus_lookup_table[index]
StrictMock< MockContext > context
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
Definition tuple.hpp:13