Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
cycle_scalar.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
7#include "./cycle_scalar.hpp"
8#include "./cycle_group.hpp"
12
13namespace bb::stdlib {
14
15template <typename Builder>
17 : lo(_lo)
18 , hi(_hi)
19{}
20
27template <typename Builder> cycle_scalar<Builder>::cycle_scalar(const ScalarField& in)
28{
29 const uint256_t value(in);
30 const auto [lo_v, hi_v] = decompose_into_lo_hi_u256(value);
31 lo = lo_v;
32 hi = hi_v;
33}
34
44template <typename Builder>
46{
47 const uint256_t value_u256(value);
48 const auto [lo_v, hi_v] = decompose_into_lo_hi_u256(value_u256);
53 return cycle_scalar(lo, hi);
54}
55
67template <typename Builder>
69{
70 const size_t num_bits = 256;
71 const uint256_t lo_v = bitstring.slice(0, LO_BITS);
72 const uint256_t hi_v = bitstring.slice(LO_BITS, num_bits);
73 auto lo = field_t::from_witness(context, lo_v);
74 auto hi = field_t::from_witness(context, hi_v);
75 return cycle_scalar{
76 lo, hi, num_bits, /*skip_primality_test=*/true, /*use_bn254_scalar_field_for_primality_test=*/false
77 };
78}
79
90{
91 // Use split_unique with skip_range_constraints=true since the range constraints are implicit
92 // in the lookup arguments used in scalar multiplication and thus do not need to be applied here.
93 auto [lo, hi] = split_unique(in, LO_BITS, /*skip_range_constraints=*/true);
94 // AUDITTODO: we skip the primality test in the constructor here since its done in split_unique. Eventually
95 // the skip_primality_test logic will be removed entirely and constraints will always be applied immediately on
96 // construction.
97 return cycle_scalar{
98 lo, hi, NUM_BITS, /*skip_primality_test=*/true, /*use_bn254_scalar_field_for_primality_test=*/true
99 };
100}
101
135template <typename Builder> cycle_scalar<Builder>::cycle_scalar(BigScalarField& scalar)
136{
137 constexpr uint64_t NUM_LIMB_BITS = BigScalarField::NUM_LIMB_BITS;
138
139 if (scalar.is_constant()) {
140 const uint256_t value((scalar.get_value() % uint512_t(ScalarField::modulus)).lo);
141 const auto [value_lo, value_hi] = decompose_into_lo_hi_u256(value);
142
143 lo = value_lo;
144 hi = value_hi;
145 lo.set_origin_tag(scalar.get_origin_tag());
146 hi.set_origin_tag(scalar.get_origin_tag());
147 return;
148 }
149
150 // Step 1: Ensure the bigfield scalar fits into LO_BITS + HI_BITS by reducing if necessary. Note: we can tolerate
151 // the scalar being > ScalarField::modulus, because performing a scalar mul implicitly performs a modular reduction.
152 if (scalar.get_maximum_value() >= (uint512_t(1) << (LO_BITS + HI_BITS))) {
153 scalar.self_reduce();
154 }
155
156 field_t limb0 = scalar.binary_basis_limbs[0].element;
157 field_t limb1 = scalar.binary_basis_limbs[1].element;
158 field_t limb2 = scalar.binary_basis_limbs[2].element;
159 field_t limb3 = scalar.binary_basis_limbs[3].element;
160
161 uint256_t limb1_max = scalar.binary_basis_limbs[1].maximum_value;
162
163 // Step 2: Ensure that limb0 only contains at most NUM_LIMB_BITS. If not, slice off the excess and add it into limb1
164 uint256_t limb0_max = scalar.binary_basis_limbs[0].maximum_value;
165 if (limb0_max > BigScalarField::DEFAULT_MAXIMUM_LIMB) {
166
167 // Split limb0 into lo (NUM_LIMB_BITS) and hi (remaining bits) slices. Note that no_wrap_split_at enforces range
168 // constraints of NUM_LIMB_BITS and (limb0_max_bits - NUM_LIMB_BITS) respectively on the slices.
169 const auto limb0_max_bits = static_cast<size_t>(limb0_max.get_msb() + 1);
170 auto [limb0_lo, limb0_hi] = limb0.no_wrap_split_at(NUM_LIMB_BITS, limb0_max_bits);
171
172 // Move the high bits from limb0 into limb1
173 limb0 = limb0_lo;
174 limb1 += limb0_hi;
175 uint256_t limb0_hi_max = limb0_max >> NUM_LIMB_BITS;
176 limb1_max += limb0_hi_max;
177 }
178
179 // Sanity check that limb1 is the limb that contributes both to *this.lo and *this.hi
180 BB_ASSERT_GT(NUM_LIMB_BITS * 2, LO_BITS);
181 BB_ASSERT_LT(NUM_LIMB_BITS, LO_BITS);
182
183 // Step 3: limb1 contributes to both *this.lo and *this.hi. Compute the values of the two limb1 slices
184 const size_t lo_bits_in_limb_1 = LO_BITS - NUM_LIMB_BITS;
185 const auto limb1_max_bits = static_cast<size_t>(limb1_max.get_msb() + 1);
186 auto [limb1_lo, limb1_hi] = limb1.no_wrap_split_at(lo_bits_in_limb_1, limb1_max_bits);
187
188 // Propagate the origin tag to the chunks of limb1
189 limb1_lo.set_origin_tag(limb1.get_origin_tag());
190 limb1_hi.set_origin_tag(limb1.get_origin_tag());
191
192 // Step 4: Construct *this.lo out of limb0 and limb1_lo
193 lo = limb0 + (limb1_lo * BigScalarField::shift_1);
194
195 // Step 5: Construct *this.hi out of limb1_hi, limb2 and limb3
196 const uint256_t limb_2_shift = uint256_t(1) << ((2 * NUM_LIMB_BITS) - LO_BITS);
197 const uint256_t limb_3_shift = uint256_t(1) << ((3 * NUM_LIMB_BITS) - LO_BITS);
198 hi = limb1_hi.add_two(limb2 * limb_2_shift, limb3 * limb_3_shift);
199
200 // Manually propagate the origin tag of the scalar to the lo/hi limbs
201 lo.set_origin_tag(scalar.get_origin_tag());
202 hi.set_origin_tag(scalar.get_origin_tag());
203};
204
205template <typename Builder> bool cycle_scalar<Builder>::is_constant() const
206{
207 return (lo.is_constant() && hi.is_constant());
208}
209
219template <typename Builder> void cycle_scalar<Builder>::validate_scalar_is_in_field() const
220{
221 if (!_skip_primality_test) {
222 const uint256_t& field_modulus =
223 _use_bn254_scalar_field_for_primality_test ? field_t::native::modulus : ScalarField::modulus;
224 validate_split_in_field(lo, hi, LO_BITS, field_modulus);
225 }
226}
227
229{
230 uint256_t lo_v(lo.get_value());
231 uint256_t hi_v(hi.get_value());
232 return ScalarField(lo_v + (hi_v << LO_BITS));
233}
234
237
238} // namespace bb::stdlib
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:118
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:148
constexpr uint256_t slice(uint64_t start, uint64_t end) const
constexpr uint64_t get_msb() const
uint512_t get_value() const
uint512_t get_maximum_value() const
bb::OriginTag get_origin_tag() const
Definition bigfield.hpp:711
bool is_constant() const
Check if the bigfield is constant, i.e. its prime limb is constant.
Definition bigfield.hpp:615
std::array< Limb, NUM_LIMBS > binary_basis_limbs
Represents a bigfield element in the binary basis. A bigfield element is represented as a combination...
Definition bigfield.hpp:80
Represents a member of the Grumpkin curve scalar field (i.e. BN254 base field).
typename Curve::ScalarField ScalarField
static cycle_scalar from_u256_witness(Builder *context, const uint256_t &bitstring)
Construct a cycle scalar from a uint256_t witness bitstring.
ScalarField get_value() const
cycle_scalar(const field_t &_lo, const field_t &_hi, const size_t bits, const bool skip_primality_test, const bool use_bn254_scalar_field_for_primality_test)
static cycle_scalar from_witness(Builder *context, const ScalarField &value)
Construct a cycle scalar from a witness value in the Grumpkin scalar field.
void validate_scalar_is_in_field() const
Validates that the scalar (lo + hi * 2^LO_BITS) is less than the appropriate field modulus.
static cycle_scalar create_from_bn254_scalar(const field_t &_in)
Construct a cycle scalar (grumpkin scalar field element) from a bn254 scalar field element.
OriginTag get_origin_tag() const
Definition field.hpp:333
static constexpr uint256_t modulus
Definition field.hpp:212
static field_t from_witness(Builder *ctx, const bb::fr &input)
Definition field.hpp:432
void set_free_witness_tag()
Set the free witness flag for the field element's tag.
Definition field.hpp:338
std::pair< field_t< Builder >, field_t< Builder > > no_wrap_split_at(const size_t lsb_index, const size_t num_bits=grumpkin::MAX_NO_WRAP_INTEGER_BIT_LENGTH) const
Splits the field element into (lo, hi), where:
Definition field.cpp:1281
StrictMock< MockContext > context
std::pair< field_t< Builder >, field_t< Builder > > split_unique(const field_t< Builder > &field, const size_t lo_bits, const bool skip_range_constraints)
Split a bn254 scalar field element into unique lo and hi limbs.
void validate_split_in_field(const field_t< Builder > &lo, const field_t< Builder > &hi, const size_t lo_bits, const uint256_t &field_modulus)
Validates that lo + hi * 2^lo_bits < field_modulus.