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() ? need_borrow : field_t<Builder>::from_witness(lo.get_context(), need_borrow);
29
30 // directly call `create_new_range_constraint` to avoid creating an arithmetic gate
31 if (!lo.is_constant()) {
32 // We need to manually propagate the origin tag
33 borrow.set_origin_tag(lo.get_origin_tag());
34 lo.get_context()->create_new_range_constraint(borrow.get_witness_index(), 1, "borrow");
35 }
36
37 // Hi range check = r_hi - hi - borrow
38 // Lo range check = r_lo - lo + borrow * 2^lo_bits
39 field_t<Builder> hi_diff = (-hi + r_hi) - borrow;
40 field_t<Builder> lo_diff = (-lo + r_lo) + (borrow * (uint256_t(1) << lo_bits));
41
42 hi_diff.create_range_constraint(hi_bits);
43 lo_diff.create_range_constraint(lo_bits);
44}
45
46template <typename Builder>
48 const size_t lo_bits,
49 const bool skip_range_constraints)
50{
51 using native = typename field_t<Builder>::native;
52 static constexpr size_t max_bits = native::modulus.get_msb() + 1;
53 ASSERT(lo_bits < max_bits);
54
55 const uint256_t value(field.get_value());
56 const uint256_t lo_val = value.slice(0, lo_bits);
57 const uint256_t hi_val = value.slice(lo_bits, max_bits);
58
59 Builder* ctx = field.get_context();
60
61 // If `field` is constant, return constants based on the native hi/lo values
62 if (field.is_constant()) {
63 return { field_t<Builder>(lo_val), field_t<Builder>(hi_val) };
64 }
65
66 // Create hi/lo witnesses
67 auto lo = field_t<Builder>::from_witness(ctx, lo_val);
68 auto hi = field_t<Builder>::from_witness(ctx, hi_val);
69
70 // Component 1: Reconstruction constraint lo + hi * 2^lo_bits - field == 0
71 const uint256_t shift = uint256_t(1) << lo_bits;
72 auto zero = field_t<Builder>::from_witness_index(ctx, ctx->zero_idx());
74
75 // Set the origin tag for the limbs
76 lo.set_origin_tag(field.get_origin_tag());
77 hi.set_origin_tag(field.get_origin_tag());
78
79 // Component 2: Field validation against bn254 scalar field modulus
80 validate_split_in_field(lo, hi, lo_bits, native::modulus);
81
82 // Component 3: Range constraints (unless skipped)
83 if (!skip_range_constraints) {
84 lo.create_range_constraint(lo_bits);
85 // For bn254 scalar field, hi_bits = 254 - lo_bits
86 const size_t hi_bits = 254 - lo_bits;
87 hi.create_range_constraint(hi_bits);
88 }
89
90 return { lo, hi };
91}
92
93// Explicit instantiations for split_unique
95 const field_t<bb::UltraCircuitBuilder>& field, const size_t lo_bits, const bool skip_range_constraints);
97 const field_t<bb::MegaCircuitBuilder>& field, const size_t lo_bits, const bool skip_range_constraints);
98
99// Explicit instantiations for validate_split_in_field
102 const size_t lo_bits,
103 const uint256_t& field_modulus);
106 const size_t lo_bits,
107 const uint256_t& field_modulus);
108
109} // namespace bb::stdlib
#define ASSERT(expression,...)
Definition assert.hpp:77
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:61
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:910
Builder * get_context() const
Definition field.hpp:397
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
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:1078
bool is_constant() const
Definition field.hpp:407
void set_origin_tag(const OriginTag &new_tag) const
Definition field.hpp:332
uint32_t get_witness_index() const
Get the witness index of the current field element.
Definition field.hpp:469
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.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
General class for prime fields see Prime field documentation["field documentation"] for general imple...