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
25template <typename Builder>
26cycle_scalar<Builder>::cycle_scalar(const field_t& lo, const field_t& hi, [[maybe_unused]] SkipValidation flag)
27 : _lo(lo)
28 , _hi(hi)
29{}
30
45template <typename Builder>
47 : _lo(lo)
48 , _hi(hi)
49{
51}
52
59template <typename Builder> cycle_scalar<Builder>::cycle_scalar(const ScalarField& in)
60{
61 const uint256_t value(in);
62 const auto [lo_v, hi_v] = decompose_into_lo_hi_u256(value);
63 _lo = lo_v;
64 _hi = hi_v;
65}
66
77template <typename Builder>
79{
80 const uint256_t value_u256(value);
81 const auto [lo_v, hi_v] = decompose_into_lo_hi_u256(value_u256);
86
87 cycle_scalar result{ lo, hi };
88
89 return result;
90}
91
126template <typename Builder> cycle_scalar<Builder>::cycle_scalar(BigScalarField& scalar)
127{
128 constexpr uint64_t NUM_LIMB_BITS = BigScalarField::NUM_LIMB_BITS;
129
130 if (scalar.is_constant()) {
131 const uint256_t value((scalar.get_value() % uint512_t(ScalarField::modulus)).lo);
132 const auto [value_lo, value_hi] = decompose_into_lo_hi_u256(value);
133
134 _lo = value_lo;
135 _hi = value_hi;
136 _lo.set_origin_tag(scalar.get_origin_tag());
137 _hi.set_origin_tag(scalar.get_origin_tag());
138 return;
139 }
140
141 // Step 1: Ensure the bigfield scalar fits into LO_BITS + HI_BITS by reducing if necessary. Note: we can tolerate
142 // the scalar being > ScalarField::modulus, because performing a scalar mul implicitly performs a modular reduction.
143 if (scalar.get_maximum_value() >= (uint512_t(1) << (LO_BITS + HI_BITS))) {
144 scalar.self_reduce();
145 }
146
147 field_t limb0 = scalar.binary_basis_limbs[0].element;
148 field_t limb1 = scalar.binary_basis_limbs[1].element;
149 field_t limb2 = scalar.binary_basis_limbs[2].element;
150 field_t limb3 = scalar.binary_basis_limbs[3].element;
151
152 uint256_t limb1_max = scalar.binary_basis_limbs[1].maximum_value;
153
154 // Step 2: Ensure that limb0 only contains at most NUM_LIMB_BITS. If not, slice off the excess and add it into limb1
155 uint256_t limb0_max = scalar.binary_basis_limbs[0].maximum_value;
156 if (limb0_max > BigScalarField::DEFAULT_MAXIMUM_LIMB) {
157
158 // Split limb0 into lo (NUM_LIMB_BITS) and hi (remaining bits) slices. Note that no_wrap_split_at enforces range
159 // constraints of NUM_LIMB_BITS and (limb0_max_bits - NUM_LIMB_BITS) respectively on the slices.
160 const auto limb0_max_bits = static_cast<size_t>(limb0_max.get_msb() + 1);
161 auto [limb0_lo, limb0_hi] = limb0.no_wrap_split_at(NUM_LIMB_BITS, limb0_max_bits);
162
163 // Move the high bits from limb0 into limb1
164 limb0 = limb0_lo;
165 limb1 += limb0_hi;
166 uint256_t limb0_hi_max = limb0_max >> NUM_LIMB_BITS;
167 limb1_max += limb0_hi_max;
168 }
169
170 // Sanity check that limb1 is the limb that contributes both to *this.lo and *this.hi
171 BB_ASSERT_GT(NUM_LIMB_BITS * 2, LO_BITS);
172 BB_ASSERT_LT(NUM_LIMB_BITS, LO_BITS);
173
174 // Step 3: limb1 contributes to both *this._lo and *this._hi. Compute the values of the two limb1 slices
175 const size_t lo_bits_in_limb_1 = LO_BITS - NUM_LIMB_BITS;
176 const auto limb1_max_bits = static_cast<size_t>(limb1_max.get_msb() + 1);
177 auto [limb1_lo, limb1_hi] = limb1.no_wrap_split_at(lo_bits_in_limb_1, limb1_max_bits);
178
179 // Propagate the origin tag to the chunks of limb1
180 limb1_lo.set_origin_tag(limb1.get_origin_tag());
181 limb1_hi.set_origin_tag(limb1.get_origin_tag());
182
183 // Step 4: Construct *this._lo out of limb0 and limb1_lo
184 _lo = limb0 + (limb1_lo * BigScalarField::shift_1);
185
186 // Step 5: Construct *this._hi out of limb1_hi, limb2 and limb3
187 const uint256_t limb_2_shift = uint256_t(1) << ((2 * NUM_LIMB_BITS) - LO_BITS);
188 const uint256_t limb_3_shift = uint256_t(1) << ((3 * NUM_LIMB_BITS) - LO_BITS);
189 _hi = limb1_hi.add_two(limb2 * limb_2_shift, limb3 * limb_3_shift);
190
191 // Manually propagate the origin tag of the scalar to the lo/hi limbs
192 _lo.set_origin_tag(scalar.get_origin_tag());
193 _hi.set_origin_tag(scalar.get_origin_tag());
194
195 validate_scalar_is_in_field();
196};
197
198template <typename Builder> bool cycle_scalar<Builder>::is_constant() const
199{
200 return (_lo.is_constant() && _hi.is_constant());
201}
202
217template <typename Builder> void cycle_scalar<Builder>::validate_scalar_is_in_field() const
218{
219 // Using _unsafe variant: range constraints are deferred to batch_mul's decompose_into_default_range
220 validate_split_in_field_unsafe(_lo, _hi, LO_BITS, ScalarField::modulus);
221}
222
224{
225 uint256_t lo_v(_lo.get_value());
226 uint256_t hi_v(_hi.get_value());
227 return ScalarField(lo_v + (hi_v << LO_BITS));
228}
229
232
233} // namespace bb::stdlib
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:107
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:137
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:702
bool is_constant() const
Check if the bigfield is constant, i.e. its prime limb is constant.
Definition bigfield.hpp:599
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
void self_reduce() const
Reduce the bigfield element modulo the target modulus.
Represents a member of the Grumpkin curve scalar field (i.e. BN254 base field).
typename Curve::ScalarField ScalarField
ScalarField get_value() const
cycle_scalar(const field_t &lo, const field_t &hi, SkipValidation flag)
Private constructor that skips field validation (for internal use only)
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 Grumpkin scalar field modulus.
OriginTag get_origin_tag() const
Definition field.hpp:346
void set_free_witness_tag()
Set the free witness flag for the field element's tag.
Definition field.hpp:351
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:1291
StrictMock< MockContext > context
void validate_split_in_field_unsafe(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 (assuming range constraints on lo and hi)