-
Notifications
You must be signed in to change notification settings - Fork 296
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: (bb) remove redundant constraints on field/group elements when …
…using goblin plonk (#8409) This PR adds distinct classes for goblin plonk group elements and coordinate scalars in the stdlib so that we can refine the implementation of these objects without proliferating edge cases in the rest of our codebase This Pr reduces the cost of `ProtogalaxyRecursiveTests/0.RecursiveFoldingTest` from 24,630 gates to 14,106 gates. `stdlib::element` is now a class alias that points to either the default element class definition or the goblin plonk class definition depending on whether goblin plonk is supported. This allows us to apply the following improvements/useful restrictions: 1. goblin plonk group elements no longer apply `on_curve` checks when created (performed in the eccvm) 2. goblin plonk coordinate field elements no longer have range constraints applied to them (performed in the translator circuit) 3. goblin plonk coordinate field elements no longer generate constraints when `assert_is_in_field` is applied (performed in the translator circuit) 4. goblin plonk coordinate field elements do not have arithmetic operations exposed (manipulation of goblin plonk group elements should happen exclusively through the eccvm) In addition, this PR improve the handling of checking whether bn254 points are at infinity when consuming points from a transcript via `field_conversion`
- Loading branch information
1 parent
aa67a14
commit 12a093d
Showing
18 changed files
with
617 additions
and
276 deletions.
There are no files selected for viewing
114 changes: 114 additions & 0 deletions
114
barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/goblin_field.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
#pragma once | ||
#include "../bigfield/bigfield.hpp" | ||
#include "../circuit_builders/circuit_builders_fwd.hpp" | ||
#include "../field/field.hpp" | ||
|
||
namespace bb::stdlib { | ||
|
||
/** | ||
* @brief goblin_field wraps x/y coordinates of bn254 group elements when using goblin | ||
* @details this class exists because we do not want to parametrise goblin bn254 coordinates with bigfield. | ||
* bigfield generates a large number of constraints to apply checks that are not needed for goblin coordinates | ||
* This is because, in the goblin context we can apply the following heuristics: | ||
* 1. goblin coordinate field elements are range-constrained in the Translator circuit (no need to range | ||
* constrain here) | ||
* 2. field elements that come out of the ECCVM are well-formed, we do not need to call `assert_is_in_field` | ||
* 3. there should be no need to apply arithmetic to goblin coordinate field elements in-circuit | ||
* Having a distinct class for `goblin_field` allows us to harvest these optimisations without a proliferation | ||
* of edge cases and bloated logic in other classes | ||
* @tparam Builder | ||
*/ | ||
template <class Builder> class goblin_field { | ||
public: | ||
static constexpr uint1024_t DEFAULT_MAXIMUM_REMAINDER = | ||
bigfield<Builder, bb::Bn254FqParams>::DEFAULT_MAXIMUM_REMAINDER; | ||
static constexpr size_t NUM_LIMBS = bigfield<Builder, bb::Bn254FqParams>::NUM_LIMBS; | ||
static constexpr size_t NUM_LIMB_BITS = bigfield<Builder, bb::Bn254FqParams>::NUM_LIMB_BITS; | ||
static constexpr size_t NUM_LAST_LIMB_BITS = bigfield<Builder, bb::Bn254FqParams>::NUM_LAST_LIMB_BITS; | ||
|
||
using field_ct = stdlib::field_t<Builder>; | ||
using bool_ct = stdlib::bool_t<Builder>; | ||
std::array<field_ct, 2> limbs; | ||
|
||
// constructors mirror bigfield constructors | ||
goblin_field() | ||
: limbs{ 0, 0 } | ||
{} | ||
goblin_field(Builder* parent_context, const uint256_t& value) | ||
{ | ||
(*this) = goblin_field(bb::fq(value)); | ||
limbs[0].context = parent_context; | ||
limbs[1].context = parent_context; | ||
} | ||
goblin_field(bb::fq input) | ||
{ | ||
uint256_t converted(input); | ||
uint256_t lo_v = converted.slice(0, NUM_LIMB_BITS * 2); | ||
uint256_t hi_v = converted.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3 + NUM_LAST_LIMB_BITS); | ||
limbs = { bb::fr(lo_v), bb::fr(hi_v) }; | ||
} | ||
goblin_field(field_ct lo, field_ct hi) | ||
: limbs{ lo, hi } | ||
{} | ||
|
||
// N.B. this method is because AggregationState expects group element coordinates to be split into 4 slices | ||
// (we could update to only use 2 for Mega but that feels complex) | ||
goblin_field(field_ct lolo, field_ct lohi, field_ct hilo, field_ct hihi, [[maybe_unused]] bool can_overflow = false) | ||
: limbs{ lolo + lohi * (uint256_t(1) << NUM_LIMB_BITS), hilo + hihi * (uint256_t(1) << NUM_LIMB_BITS) } | ||
{} | ||
|
||
void assert_equal(const goblin_field& other) const | ||
{ | ||
limbs[0].assert_equal(other.limbs[0]); | ||
limbs[1].assert_equal(other.limbs[1]); | ||
} | ||
static goblin_field zero() { return goblin_field{ 0, 0 }; } | ||
|
||
static goblin_field from_witness(Builder* ctx, bb::fq input) | ||
{ | ||
uint256_t converted(input); | ||
uint256_t lo_v = converted.slice(0, NUM_LIMB_BITS * 2); | ||
uint256_t hi_v = converted.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3 + NUM_LAST_LIMB_BITS); | ||
field_ct lo = field_ct::from_witness(ctx, lo_v); | ||
field_ct hi = field_ct::from_witness(ctx, hi_v); | ||
return goblin_field(lo, hi); | ||
} | ||
|
||
static goblin_field conditional_assign(const bool_ct& predicate, const goblin_field& lhs, goblin_field& rhs) | ||
{ | ||
goblin_field result; | ||
result.limbs = { | ||
field_ct::conditional_assign(predicate, lhs.limbs[0], rhs.limbs[0]), | ||
field_ct::conditional_assign(predicate, lhs.limbs[1], rhs.limbs[1]), | ||
}; | ||
return result; | ||
} | ||
|
||
// matches the interface for bigfield | ||
uint512_t get_value() const | ||
{ | ||
uint256_t lo = limbs[0].get_value(); | ||
uint256_t hi = limbs[1].get_value(); | ||
uint256_t result = lo + (hi << 136); | ||
return result; | ||
} | ||
|
||
// matches the interface for bigfield | ||
uint512_t get_maximum_value() const { return (*this).get_value(); } | ||
|
||
Builder* get_context() const | ||
{ | ||
if (limbs[0].get_context()) { | ||
return limbs[0].get_context(); | ||
} | ||
return limbs[1].get_context(); | ||
} | ||
|
||
// done in the translator circuit | ||
void assert_is_in_field(){}; | ||
}; | ||
template <typename C> inline std::ostream& operator<<(std::ostream& os, goblin_field<C> const& v) | ||
{ | ||
return os << "{ " << v.limbs[0] << " , " << v.limbs[1] << " }"; | ||
} | ||
} // namespace bb::stdlib |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.