barretenberg
Loading...
Searching...
No Matches
Public Types | Public Member Functions | Static Public Member Functions | Public Attributes | Static Public Attributes | List of all members
proof_system::plonk::stdlib::field_t< Builder > Class Template Reference

Public Types

using View = field_t
 

Public Member Functions

 field_t (Builder *parent_context=nullptr)
 
 field_t (Builder *parent_context, const barretenberg::fr &value)
 
 field_t (const int value)
 
 field_t (const unsigned long long value)
 
 field_t (const unsigned int value)
 
 field_t (const unsigned long value)
 
 field_t (const barretenberg::fr &value)
 
 field_t (const uint256_t &value)
 
 field_t (const witness_t< Builder > &value)
 
 field_t (const field_t &other)
 
 field_t (field_t &&other) noexcept
 
 field_t (const bool_t< Builder > &other)
 
 operator bool_t< Builder > () const
 
field_toperator= (const field_t &other)
 
field_toperator= (field_t &&other) noexcept
 
field_t operator+ (const field_t &other) const
 
field_t operator- (const field_t &other) const
 
field_t operator* (const field_t &other) const
 
field_t operator/ (const field_t &other) const
 
field_t divide_no_zero_check (const field_t &other) const
 
field_t sqr () const
 
field_t pow (const field_t &exponent) const
 raise a field_t to a power of an exponent (field_t). Note that the exponent must not exceed 32 bits and is implicitly range constrained.
 
field_t operator+= (const field_t &other)
 
field_t operator-= (const field_t &other)
 
field_t operator*= (const field_t &other)
 
field_t operator/= (const field_t &other)
 
field_toperator++ ()
 
field_t operator++ (const int)
 
field_t invert () const
 
field_t operator- () const
 
field_t conditional_negate (const bool_t< Builder > &predicate) const
 
void assert_equal (const field_t &rhs, std::string const &msg="field_t::assert_equal") const
 Constrain that this field is equal to the given field.
 
void assert_not_equal (const field_t &rhs, std::string const &msg="field_t::assert_not_equal") const
 
void assert_is_in_set (const std::vector< field_t > &set, std::string const &msg="field_t::assert_not_in_set") const
 
field_t madd (const field_t &to_mul, const field_t &to_add) const
 
field_t add_two (const field_t &add_a, const field_t &add_b) const
 
bool_t< Builderoperator== (const field_t &other) const
 
bool_t< Builderoperator!= (const field_t &other) const
 
field_t normalize () const
 
barretenberg::fr get_value () const
 
Builderget_context () const
 
std::array< field_t, 3 > slice (uint8_t msb, uint8_t lsb) const
 
bool_t< Builderis_zero () const
 
void create_range_constraint (size_t num_bits, std::string const &msg="field_t::range_constraint") const
 
void assert_is_not_zero (std::string const &msg="field_t::assert_is_not_zero") const
 
void assert_is_zero (std::string const &msg="field_t::assert_is_zero") const
 
bool is_constant () const
 
void set_public () const
 
void convert_constant_to_fixed_witness (Builder *ctx)
 
void fix_witness ()
 
uint32_t get_witness_index () const
 
std::vector< bool_t< Builder > > decompose_into_bits (size_t num_bits=256, std::function< witness_t< Builder >(Builder *ctx, uint64_t, uint256_t)> get_bit=[](Builder *ctx, uint64_t j, const uint256_t &val) { return witness_t< Builder >(ctx, val.get_bit(j));}) const
 Build a circuit allowing a user to prove that they have deomposed this into bits.
 
template<size_t num_bits>
bool_t< Builderranged_less_than (const field_t< Builder > &other) const
 Return (a < b) as bool circuit type. This method assumes that both a and b are < 2^{input_bits} - 1 i.e. it is not checked here, we assume this has been done previously.
 

Static Public Member Functions

static field_t from_witness_index (Builder *parent_context, uint32_t witness_index)
 
static field_t copy_as_new_witness (Builder &context, field_t const &other)
 
static field_t coset_generator (const size_t generator_idx)
 
static field_t external_coset_generator ()
 
static field_t conditional_assign (const bool_t< Builder > &predicate, const field_t &lhs, const field_t &rhs)
 
static std::array< field_t, 4 > preprocess_two_bit_table (const field_t &T0, const field_t &T1, const field_t &T2, const field_t &T3)
 
static field_t select_from_two_bit_table (const std::array< field_t, 4 > &table, const bool_t< Builder > &t1, const bool_t< Builder > &t0)
 
static std::array< field_t, 8 > preprocess_three_bit_table (const field_t &T0, const field_t &T1, const field_t &T2, const field_t &T3, const field_t &T4, const field_t &T5, const field_t &T6, const field_t &T7)
 
static field_t select_from_three_bit_table (const std::array< field_t, 8 > &table, const bool_t< Builder > &t2, const bool_t< Builder > &t1, const bool_t< Builder > &t0)
 
static void evaluate_linear_identity (const field_t &a, const field_t &b, const field_t &c, const field_t &d)
 
static void evaluate_polynomial_identity (const field_t &a, const field_t &b, const field_t &c, const field_t &d)
 
static field_t accumulate (const std::vector< field_t > &to_add)
 
static field_t from_witness (Builder *ctx, const barretenberg::fr &input)
 

Public Attributes

Buildercontext = nullptr
 
barretenberg::fr additive_constant
 
barretenberg::fr multiplicative_constant
 
uint32_t witness_index = IS_CONSTANT
 

Static Public Attributes

static constexpr bool is_composite = false
 
static constexpr uint256_t modulus = barretenberg::fr::modulus
 

Member Function Documentation

◆ accumulate()

template<typename Builder >
field_t< Builder > proof_system::plonk::stdlib::field_t< Builder >::accumulate ( const std::vector< field_t< Builder > > &  input)
static

Compute sum of inputs

If we are using UltraCircuitBuilder, we can accumulate 3 values into a sum per gate. We track a decumulating sum of values in the 4th wire of every row. i.e. the 4th wire of the first row is the total output value

At every gate, we subtract off three elements from input. Every gate apart from the final gate, is an 'extended' addition gate, that includes the 4th wire of the next gate

e.g. to accumulate 9 limbs, structure is:

| l_1 | l_2 | l_3 | s_3 | | l_4 | l_5 | l_6 | s_2 | | l_7 | l_8 | l_9 | s_1 |

We validate:

s_3 - l_1 - l_2 - l_3 - s_2 = 0 s_2 - l_4 - l_5 - l_6 - s_1 = 0 s_1 - l_7 - l_8 - l_9 = 0

If num elements is not a multiple of 3, the final gate will be padded with zero_idx wires

◆ assert_equal()

template<typename Builder >
void proof_system::plonk::stdlib::field_t< Builder >::assert_equal ( const field_t< Builder > &  rhs,
std::string const &  msg = "field_t< Builder >::assert_equal" 
) const

Constrain that this field is equal to the given field.

Warning
: After calling this method, both field values will be equal, regardless of whether the constraint succeeds or fails. This can lead to confusion when debugging. If you want to log the inputs, do so before calling this method.

◆ convert_constant_to_fixed_witness()

template<typename Builder >
void proof_system::plonk::stdlib::field_t< Builder >::convert_constant_to_fixed_witness ( Builder ctx)
inline

Create a witness form a constant. This way the value of the witness is fixed and public (public, because the value becomes hard-coded as an element of the q_c selector vector).

◆ decompose_into_bits()

template<typename Builder >
std::vector< bool_t< Builder > > proof_system::plonk::stdlib::field_t< Builder >::decompose_into_bits ( size_t  num_bits = 256,
std::function< witness_t< Builder >(Builder *ctx, uint64_t, uint256_t)>  get_bit = [](Builder* ctx, uint64_t j, const uint256_t& val) { return witness_t<Builder>(ctx, val.get_bit(j)); } 
) const

Build a circuit allowing a user to prove that they have deomposed this into bits.

A bit vector result is extracted and used to to construct a sum sum using the normal binary expansion. Along the way, we extract a value shifted_high_limb which is equal to sum_hi in the natural decomposition sum = sum_lo + 2**128*sum_hi. We impose a copy constraint between sum and this but that only imposes equality in Fr; it could be that result has overflowed the modulus r. To impose a unique value of result, we constrain sum to satisfy r - 1 >= sum >= 0. In order to do this inside of Fr, we must reduce break the check down in the smaller checks so that we can check non-negativity of integers using range constraints in Fr.

At circuit compilation time we build the decomposition r - 1 = p_lo + 2**128*p_hi. Then desired schoolbook subtraction is p_hi - b | p_lo + b*2**128 (++foo++ is foo crossed out) ++p_hi++ | ++p_lo++ (b = 0, 1)

  • |

sum_hi | sum_lo

y_lo := p_hi - b - sum_hi | y_hi := p_lo + b*2**128 - sum_lo

Here b is the boolean "a carry is necessary". Each of the resulting values can be checked for underflow by imposing a small range constraint, since the negative of a small value in Fr is a large value in Fr.

◆ fix_witness()

template<typename Builder >
void proof_system::plonk::stdlib::field_t< Builder >::fix_witness ( )
inline

Fix a witness. The value of the witness is constrained with a selector

◆ is_zero()

is_zero will return a bool_t, and add constraints that enforce its correctness N.B. If you want to ENFORCE that a field_t object is zero, use assert_is_zero

◆ madd()

template<typename Builder >
field_t< Builder > proof_system::plonk::stdlib::field_t< Builder >::madd ( const field_t< Builder > &  to_mul,
const field_t< Builder > &  to_add 
) const

multiply *this by to_mul and add to_add One madd call costs 1 constraint for Ultra plonk

Returns
this * to_mul + to_add

◆ normalize()

template<typename Builder >
field_t< Builder > proof_system::plonk::stdlib::field_t< Builder >::normalize

normalize returns a field_t element with equivalent value to this, but where multiplicative_constant = 1 and additive_constant = 0. I.e. the returned value is defined entirely by the builder variable that witness_index points to (no scaling factors).

If the witness_index of this is ever needed, normalize should be called first.

Will cost 1 constraint if the field element is not already normalized, as a new witness value would need to be created. Constants do not need to be normalized, as there is no underlying 'witness'; a constant's value is wholly tracked by this.additive_constant, so we definitely don't want to set that to 0!

◆ operator*()

template<typename Builder >
field_t< Builder > proof_system::plonk::stdlib::field_t< Builder >::operator* ( const field_t< Builder > &  other) const

Let: a := this; b := other; a.v := ctx->variables[this.witness_index]; b.v := ctx->variables[other.witness_index]; .mul = .multiplicative_constant .add = .additive_constant

Value of this = a.v * a.mul + a.add; Value of other = b.add Value of result = a * b = a.v * [a.mul * b.add] + [a.add * b.add] ^ ^result.mul ^result.add ^result.v

Value of this = a.add; Value of other = b.v * b.mul + b.add Value of result = a * b = b.v * [a.add * b.mul] + [a.add * b.add] ^ ^result.mul ^result.add ^result.v

Value of this = a.v * a.mul + a.add; Value of other = b.v * b.mul + b.add; Value of result = a * b = [a.v * b.v] * [a.mul * b.mul] + a.v * [a.mul * b.add] + b.v * [a.add * b.mul] + [a.ac * b.add] = [a.v * b.v] * [ q_m ] + a.v * [ q_l ] + b.v * [ q_r ] + [ q_c ] ^ ^Notice the add/mul_constants form selectors when a gate is created. | Only the witnesses (pointed-to by the witness_indexes) form the wires in/out of | the gate. ^This entire value is pushed to ctx->variables as a new witness. The implied additive & multiplicative constants of the new witness are 0 & 1 resp. Left wire value: a.v Right wire value: b.v Output wire value: result.v (with q_o = -1)

◆ pow()

template<typename Builder >
field_t< Builder > proof_system::plonk::stdlib::field_t< Builder >::pow ( const field_t< Builder > &  exponent) const

raise a field_t to a power of an exponent (field_t). Note that the exponent must not exceed 32 bits and is implicitly range constrained.

Returns
this ** (exponent)

◆ ranged_less_than()

template<typename Builder >
template<size_t num_bits>
bool_t< Builder > proof_system::plonk::stdlib::field_t< Builder >::ranged_less_than ( const field_t< Builder > &  other) const
inline

Return (a < b) as bool circuit type. This method assumes that both a and b are < 2^{input_bits} - 1 i.e. it is not checked here, we assume this has been done previously.

Template Parameters
Builder
input_bits
Parameters
a
b
Returns
bool_t<Builder>

◆ slice()

template<typename Builder >
std::array< field_t< Builder >, 3 > proof_system::plonk::stdlib::field_t< Builder >::slice ( uint8_t  msb,
uint8_t  lsb 
) const

Slices a field_t at given indices (msb, lsb) both included in the slice, returns three parts: [low, slice, high].

Member Data Documentation

◆ additive_constant

template<typename Builder >
barretenberg::fr proof_system::plonk::stdlib::field_t< Builder >::additive_constant
mutable

additive_constant and multiplicative_constant are constant scaling factors applied to a field_t object.

The 'value' represented by a field_t is calculated as:

  • For field_ts with witness_index = IS_CONSTANT: this.additive_constant
  • For non-constant field_ts: this.context->variables[this.witness_index] * this.multiplicative_constant + this.additive_constant

We track these scaling factors, because we can apply the same scaling factors to Plonk wires when creating gates. I.e. if we want to multiply a wire by a constant, or add a constant, we do not need to add extra gates to do this. Instead, we track the scaling factors, and apply them to the relevant wires when adding constraints.

This also makes constant field_t objects effectively free. (Where 'constant' is a circuit constant, not a C++ constant!). E.g. the following 3 lines of code add 0 constraints into a circuit:

field_t foo = 1; field_t bar = 5; field_t bar *= foo;

Similarly if we add in:

field_t zip = witness_t(context, 10); zip *= bar + foo;

The above adds 0 constraints, the only effect is that zip's scaling factors have been modified. However if we now add:

field_t zap = witness_t(context, 50); zip *= zap;

This will add a constraint, as both zip and zap map to circuit witnesses.

◆ witness_index

template<typename Builder >
uint32_t proof_system::plonk::stdlib::field_t< Builder >::witness_index = IS_CONSTANT
mutable

Every builder object contains a vector variables (a.k.a. 'witnesses'); circuit variables that can be assigned to wires when creating constraints. witness_index describes a location in this container. I.e. it 'points' to a circuit variable.

A witness is not the same thing as a 'wire' in a circuit. Multiple wires can be assigned to the same witness via Plonk's copy constraints. Alternatively, a witness might not be assigned to any wires! This case would be similar to an unused variable in a regular program

E.g. if we write field_t foo = witness_t(context, 100), this will add the value 100 into context's list of circuit variables. However if we do not use foo in any operations, then this value will never be assigned to a wire in a circuit.

For a more in depth example, consider the following code:

field_t foo = witness_t(context, 10); field_t bar = witness_t(context, 50); field_t baz = foo * (bar + 7);

This will add 3 new circuit witnesses (10, 50, 570) to variables. One constraint will also be created, that validates baz has been correctly constructed. The builder will assign foo, bar, baz to wires w_1, w_2, w_3 in a new gate which checks that:

 w_1 * w_2 + w_1 * 7 - w_3 = 0

If any of foo, bar, baz are used in future arithmetic, copy constraints will be automatically applied, this ensure that all gate wires that map to foo, for example, will contain the same value.

If witness_index == IS_CONSTANT, the object represents a constant value. i.e. a value that's hardcoded in the circuit, that a prover cannot change by modifying their witness transcript.

A Plonk gate is a mix of witness values and selector values. e.g. the regular PLONK arithmetic gate checks that:

 w_1 * w_2 * q_m + w_1 * q_1 + w_2 * w_2 + w_3 * q_3 + q_c = 0

The w value are wires, the q values are selector constants. If a field object contains a witness_index, it will be assigned to w values when constraints are applied. If it's a circuit constant, it will be assigned to q values.

TLDR: witness_index is a pseudo pointer to a circuit witness


The documentation for this class was generated from the following files: