Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
field_utils.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 "./field_utils.hpp"
8#include "./field.hpp"
10
11namespace bb::stdlib {
12
13template <typename Builder>
15 const field_t<Builder>& hi,
16 const size_t lo_bits,
17 const uint256_t& field_modulus)
18{
19 const size_t hi_bits = static_cast<size_t>(field_modulus.get_msb()) + 1 - lo_bits;
20
21 // Split the field modulus at the same position
22 const uint256_t r_lo = field_modulus.slice(0, lo_bits);
23 const uint256_t r_hi = field_modulus.slice(lo_bits, field_modulus.get_msb() + 1);
24
25 // Check if we need to borrow
26 bool need_borrow = uint256_t(lo.get_value()) > r_lo;
27 field_t<Builder> borrow =
28 lo.is_constant()
29 ? need_borrow
31
32 // directly call `create_new_range_constraint` to avoid creating an arithmetic gate
33 if (!lo.is_constant()) {
34 // We need to manually propagate the origin tag
35 borrow.set_origin_tag(lo.get_origin_tag());
36 lo.get_context()->create_new_range_constraint(borrow.get_witness_index(), 1, "borrow");
37 }
38
39 // Hi range check = r_hi - hi - borrow
40 // Lo range check = r_lo - lo + borrow * 2^lo_bits
41 field_t<Builder> hi_diff = (-hi + r_hi) - borrow;
42 field_t<Builder> lo_diff = (-lo + r_lo) + (borrow * (uint256_t(1) << lo_bits));
43
44 hi_diff.create_range_constraint(hi_bits);
45 lo_diff.create_range_constraint(lo_bits);
46}
47
48template <typename Builder>
50 const size_t lo_bits,
51 const bool skip_range_constraints)
52{
53 using native = typename field_t<Builder>::native;
54 static constexpr size_t max_bits = native::modulus.get_msb() + 1;
55 BB_ASSERT(lo_bits < max_bits);
56
57 const uint256_t value(field.get_value());
58 const uint256_t lo_val = value.slice(0, lo_bits);
59 const uint256_t hi_val = value.slice(lo_bits, max_bits);
60
61 Builder* ctx = field.get_context();
62
63 // If `field` is constant, return constants based on the native hi/lo values
64 if (field.is_constant()) {
65 return { field_t<Builder>(lo_val), field_t<Builder>(hi_val) };
66 }
67
68 // Create hi/lo witnesses
69 auto lo = field_t<Builder>::from_witness(ctx, typename field_t<Builder>::native(lo_val));
70 auto hi = field_t<Builder>::from_witness(ctx, typename field_t<Builder>::native(hi_val));
71
72 // Component 1: Reconstruction constraint lo + hi * 2^lo_bits - field == 0
73 const uint256_t shift = uint256_t(1) << lo_bits;
74 auto zero = field_t<Builder>::from_witness_index(ctx, ctx->zero_idx());
76
77 // Set the origin tag for the limbs
78 lo.set_origin_tag(field.get_origin_tag());
79 hi.set_origin_tag(field.get_origin_tag());
80
81 // Component 2: Field validation against bn254 scalar field modulus
82 // Note: We use _unsafe variant because Component 3 applies range constraints (unless explicitly skipped). When
83 // range constraints are skipped, caller must ensure they are applied elsewhere.
84 validate_split_in_field_unsafe(lo, hi, lo_bits, native::modulus);
85
86 // Component 3: Range constraints (unless skipped)
87 if (!skip_range_constraints) {
88 lo.create_range_constraint(lo_bits);
89 // For bn254 scalar field, hi_bits = 254 - lo_bits
90 const size_t hi_bits = 254 - lo_bits;
91 hi.create_range_constraint(hi_bits);
92 }
93
94 return { lo, hi };
95}
96
97template <typename Builder> void mark_witness_as_used(const field_t<Builder>& field)
98{
99 if (!field.is_constant()) {
100 // Use raw witness_index to avoid normalization overhead
101 field.get_context()->update_used_witnesses(field.witness_index);
102 }
103}
104
105// Explicit instantiations for split_unique
107 const field_t<bb::UltraCircuitBuilder>& field, const size_t lo_bits, const bool skip_range_constraints);
109 const field_t<bb::MegaCircuitBuilder>& field, const size_t lo_bits, const bool skip_range_constraints);
110
111// Explicit instantiations for validate_split_in_field_unsafe
114 const size_t lo_bits,
115 const uint256_t& field_modulus);
118 const size_t lo_bits,
119 const uint256_t& field_modulus);
120
121// Explicit instantiations for mark_witness_as_used
124
125} // namespace bb::stdlib
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
constexpr uint256_t slice(uint64_t start, uint64_t end) const
constexpr uint64_t get_msb() const
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
Definition field.cpp:62
void create_range_constraint(size_t num_bits, std::string const &msg="field_t::range_constraint") const
Let x = *this.normalize(), constrain x.v < 2^{num_bits}.
Definition field.cpp:909
Builder * get_context() const
Definition field.hpp:419
OriginTag get_origin_tag() const
Definition field.hpp:346
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:828
static field_t from_witness(Builder *ctx, const bb::fr &input)
Definition field.hpp:454
static void evaluate_linear_identity(const field_t &a, const field_t &b, const field_t &c, const field_t &d, const std::string &msg="field_t::evaluate_linear_identity")
Constrain a + b + c + d to be equal to 0.
Definition field.cpp:1088
bool is_constant() const
Definition field.hpp:429
void set_origin_tag(const OriginTag &new_tag) const
Definition field.hpp:345
uint32_t get_witness_index() const
Get the witness index of the current field element.
Definition field.hpp:506
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_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)
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
Definition tuple.hpp:13