Skip to content

Commit b31eec9

Browse files
chore: field_conversion clean-up/int audit (#16898)
### 🧾 Audit Context `stdlib::field_conversion` provides the serialization/deserialization methods for `stdlib::Transcript`. ### 🛠️ Changes Made - Simplified the logic by making it more uniform and getting rid of redundant arguments - Enabled `on_curve` checks for Ultra deserialization of group elements (BN254 and Grumpkin) - Removed redundant range constraints in `fr --> bigfield` conversion and optimized the constraint ensuring that `fr = lo + hi * shift`. Note that limbs were constrained to be (136, 118) bits, although such range constraints are enforced by `bigfield(low, hi)` constructor. - Optimized `point_at_infinity` check for Grumpkin points. - Expanded docs and made them Doxygen-friendly - Added tests and [an issue](AztecProtocol/barretenberg#1541) that will be resolved in a follow-up - The vks and circuit sizes have changed due to enabled checks and the small optimizations described above ### 📌 Notes for Reviewers Agreed with @suyash67 that existing point-at-infinity issue AztecProtocol/barretenberg#1527 should be handled by ensuring the consistent representation of points at infinity in all groups. Added a test that will fail once the issue is resolved. --------- Co-authored-by: ledwards2225 <[email protected]>
1 parent bb543ac commit b31eec9

21 files changed

+469
-289
lines changed

barretenberg/cpp/scripts/test_civc_standalone_vks_havent_changed.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ cd ..
1313
# - Generate a hash for versioning: sha256sum bb-civc-inputs.tar.gz
1414
# - Upload the compressed results: aws s3 cp bb-civc-inputs.tar.gz s3://aztec-ci-artifacts/protocol/bb-civc-inputs-[hash(0:8)].tar.gz
1515
# Note: In case of the "Test suite failed to run ... Unexpected token 'with' " error, need to run: docker pull aztecprotocol/build:3.0
16-
pinned_short_hash="04f38201"
16+
pinned_short_hash="e70f08be"
1717
pinned_civc_inputs_url="https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-civc-inputs-${pinned_short_hash}.tar.gz"
1818

1919
function compress_and_upload {

barretenberg/cpp/src/barretenberg/dsl/acir_format/civc_recursion_constraints.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ create_civc_recursion_constraints(Builder& builder,
111111
}
112112

113113
// Recursively verify CIVC proof
114-
auto mega_vk = std::make_shared<VerificationKey>(builder, key_fields);
114+
auto mega_vk = std::make_shared<VerificationKey>(key_fields);
115115
auto mega_vk_and_hash = std::make_shared<RecursiveVKAndHash>(mega_vk, vk_hash);
116116
ClientIVCRecursiveVerifier::StdlibProof stdlib_proof(proof_fields, input.public_inputs.size());
117117

barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ HonkRecursionConstraintOutput<typename Flavor::CircuitBuilder> create_honk_recur
141141
}
142142

143143
// Recursively verify the proof
144-
auto vkey = std::make_shared<RecursiveVerificationKey>(builder, key_fields);
144+
auto vkey = std::make_shared<RecursiveVerificationKey>(key_fields);
145145
auto vk_and_hash = std::make_shared<RecursiveVKAndHash>(vkey, vk_hash);
146146
RecursiveVerifier verifier(&builder, vk_and_hash);
147147
UltraRecursiveVerifierOutput<Builder> verifier_output = verifier.template verify_proof<IO>(proof_fields);

barretenberg/cpp/src/barretenberg/flavor/mega_recursive_flavor.hpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,18 +140,18 @@ template <typename BuilderType> class MegaRecursiveFlavor_ {
140140
* @param builder
141141
* @param elements
142142
*/
143-
VerificationKey(CircuitBuilder& builder, std::span<FF> elements)
143+
VerificationKey(std::span<FF> elements)
144144
{
145145
using namespace bb::stdlib::field_conversion;
146146

147147
size_t num_frs_read = 0;
148148

149-
this->log_circuit_size = deserialize_from_frs<FF>(builder, elements, num_frs_read);
150-
this->num_public_inputs = deserialize_from_frs<FF>(builder, elements, num_frs_read);
151-
this->pub_inputs_offset = deserialize_from_frs<FF>(builder, elements, num_frs_read);
149+
this->log_circuit_size = deserialize_from_frs<FF>(elements, num_frs_read);
150+
this->num_public_inputs = deserialize_from_frs<FF>(elements, num_frs_read);
151+
this->pub_inputs_offset = deserialize_from_frs<FF>(elements, num_frs_read);
152152

153153
for (Commitment& commitment : this->get_all()) {
154-
commitment = deserialize_from_frs<Commitment>(builder, elements, num_frs_read);
154+
commitment = deserialize_from_frs<Commitment>(elements, num_frs_read);
155155
}
156156

157157
if (num_frs_read != elements.size()) {
@@ -174,7 +174,7 @@ template <typename BuilderType> class MegaRecursiveFlavor_ {
174174
for (const auto& idx : witness_indices) {
175175
vk_fields.emplace_back(FF::from_witness_index(&builder, idx));
176176
}
177-
return VerificationKey(builder, vk_fields);
177+
return VerificationKey(vk_fields);
178178
}
179179

180180
/**

barretenberg/cpp/src/barretenberg/flavor/ultra_recursive_flavor.hpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,18 +140,18 @@ template <typename BuilderType> class UltraRecursiveFlavor_ {
140140
* @param builder
141141
* @param elements
142142
*/
143-
VerificationKey(CircuitBuilder& builder, std::span<FF> elements)
143+
VerificationKey(std::span<FF> elements)
144144
{
145145
using namespace bb::stdlib::field_conversion;
146146

147147
size_t num_frs_read = 0;
148148

149-
this->log_circuit_size = deserialize_from_frs<FF>(builder, elements, num_frs_read);
150-
this->num_public_inputs = deserialize_from_frs<FF>(builder, elements, num_frs_read);
151-
this->pub_inputs_offset = deserialize_from_frs<FF>(builder, elements, num_frs_read);
149+
this->log_circuit_size = deserialize_from_frs<FF>(elements, num_frs_read);
150+
this->num_public_inputs = deserialize_from_frs<FF>(elements, num_frs_read);
151+
this->pub_inputs_offset = deserialize_from_frs<FF>(elements, num_frs_read);
152152

153153
for (Commitment& commitment : this->get_all()) {
154-
commitment = deserialize_from_frs<Commitment>(builder, elements, num_frs_read);
154+
commitment = deserialize_from_frs<Commitment>(elements, num_frs_read);
155155
}
156156
}
157157

@@ -170,7 +170,7 @@ template <typename BuilderType> class UltraRecursiveFlavor_ {
170170
for (const auto& idx : witness_indices) {
171171
vk_fields.emplace_back(FF::from_witness_index(&builder, idx));
172172
}
173-
return VerificationKey(builder, vk_fields);
173+
return VerificationKey(vk_fields);
174174
}
175175
};
176176

barretenberg/cpp/src/barretenberg/flavor/ultra_rollup_recursive_flavor.hpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,18 +86,18 @@ template <typename BuilderType> class UltraRollupRecursiveFlavor_ : public Ultra
8686
* @param builder
8787
* @param elements
8888
*/
89-
VerificationKey(CircuitBuilder& builder, std::span<FF> elements)
89+
VerificationKey(std::span<FF> elements)
9090
{
9191
using namespace bb::stdlib::field_conversion;
9292

9393
size_t num_frs_read = 0;
9494

95-
this->log_circuit_size = deserialize_from_frs<FF>(builder, elements, num_frs_read);
96-
this->num_public_inputs = deserialize_from_frs<FF>(builder, elements, num_frs_read);
97-
this->pub_inputs_offset = deserialize_from_frs<FF>(builder, elements, num_frs_read);
95+
this->log_circuit_size = deserialize_from_frs<FF>(elements, num_frs_read);
96+
this->num_public_inputs = deserialize_from_frs<FF>(elements, num_frs_read);
97+
this->pub_inputs_offset = deserialize_from_frs<FF>(elements, num_frs_read);
9898

9999
for (Commitment& commitment : this->get_all()) {
100-
commitment = deserialize_from_frs<Commitment>(builder, elements, num_frs_read);
100+
commitment = deserialize_from_frs<Commitment>(elements, num_frs_read);
101101
}
102102
}
103103

@@ -116,7 +116,7 @@ template <typename BuilderType> class UltraRollupRecursiveFlavor_ : public Ultra
116116
for (const auto& idx : witness_indices) {
117117
vk_fields.emplace_back(FF::from_witness_index(&builder, idx));
118118
}
119-
return VerificationKey(builder, vk_fields);
119+
return VerificationKey(vk_fields);
120120
}
121121
};
122122

barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ class ECCVMRecursiveTests : public ::testing::Test {
139139
}
140140

141141
// Check that the size of the recursive verifier is consistent with historical expectation
142-
uint32_t NUM_GATES_EXPECTED = 213923;
142+
uint32_t NUM_GATES_EXPECTED = 213775;
143143
ASSERT_EQ(static_cast<uint32_t>(outer_circuit.get_num_finalized_gates()), NUM_GATES_EXPECTED)
144144
<< "Ultra-arithmetized ECCVM Recursive verifier gate count changed! Update this value if you are sure this "
145145
"is expected.";

barretenberg/cpp/src/barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.test.cpp

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,55 @@ class GoblinRecursiveVerifierTests : public testing::Test {
2525
using RecursiveCommitment = GoblinRecursiveVerifier::MergeVerifier::Commitment;
2626
using MergeCommitments = MergeVerifier::InputCommitments;
2727
using RecursiveMergeCommitments = GoblinRecursiveVerifier::MergeVerifier::InputCommitments;
28-
28+
using FF = TranslatorFlavor::FF;
29+
using BF = TranslatorFlavor::BF;
2930
static void SetUpTestSuite() { bb::srs::init_file_crs_factory(bb::srs::bb_crs_path()); }
3031

32+
// Compute the size of a Translator commitment (in bb::fr's)
33+
static constexpr size_t comm_frs = bb::field_conversion::calc_num_bn254_frs<Commitment>(); // 4
34+
static constexpr size_t eval_frs = bb::field_conversion::calc_num_bn254_frs<FF>(); // 1
35+
36+
// The `op` wire commitment is currently the second element of the proof, following the
37+
// `accumulated_result` which is a BN254 BaseField element.
38+
static constexpr size_t offset = bb::field_conversion::calc_num_bn254_frs<BF>();
39+
3140
struct ProverOutput {
3241
GoblinProof proof;
3342
Goblin::VerificationKey verifier_input;
3443
MergeCommitments merge_commitments;
3544
RecursiveMergeCommitments recursive_merge_commitments;
3645
};
46+
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1298):
47+
// Better recursion testing - create more flexible proof tampering tests.
48+
// Modify the `op` commitment which a part of the Merge protocol.
49+
static void tamper_with_op_commitment(HonkProof& translator_proof)
50+
{
51+
52+
// Extract `op` fields and convert them to a Commitment object
53+
auto element_frs = std::span{ translator_proof }.subspan(offset, comm_frs);
54+
auto op_commitment = NativeTranscriptParams::template deserialize<Commitment>(element_frs);
55+
// Modify the commitment
56+
op_commitment = op_commitment * FF(2);
57+
// Serialize the tampered commitment into the proof (overwriting the valid one).
58+
auto op_commitment_reserialized = bb::NativeTranscriptParams::serialize(op_commitment);
59+
std::copy(op_commitment_reserialized.begin(),
60+
op_commitment_reserialized.end(),
61+
translator_proof.begin() + static_cast<std::ptrdiff_t>(offset));
62+
};
63+
64+
// Translator proof ends with [..., Libra:quotient_eval, Shplonk:Q, KZG:W]. We invalidate the proof by multiplying
65+
// the eval by 2 (it leads to a Libra consistency check failure).
66+
static void tamper_with_libra_eval(HonkProof& translator_proof)
67+
{
68+
// Proof tail size
69+
static constexpr size_t tail_size = 2 * comm_frs + eval_frs; // 2*4 + 1 = 9
70+
71+
// Index of the target field (one fr) from the beginning
72+
const size_t idx = translator_proof.size() - tail_size;
3773

74+
// Tamper: multiply by 2 (or tweak however you like)
75+
translator_proof[idx] = translator_proof[idx] + translator_proof[idx];
76+
};
3877
/**
3978
* @brief Create a goblin proof and the VM verification keys needed by the goblin recursive verifier
4079
*
@@ -208,13 +247,7 @@ TEST_F(GoblinRecursiveVerifierTests, TranslatorFailure)
208247
// Tamper with the Translator proof preamble
209248
{
210249
GoblinProof tampered_proof = proof;
211-
for (auto& val : tampered_proof.translator_proof) {
212-
if (val > 0) { // tamper by finding the first non-zero value and incrementing it by 1
213-
val += 1;
214-
break;
215-
}
216-
}
217-
250+
tamper_with_op_commitment(tampered_proof.translator_proof);
218251
Builder builder;
219252

220253
RecursiveMergeCommitments recursive_merge_commitments;
@@ -232,18 +265,10 @@ TEST_F(GoblinRecursiveVerifierTests, TranslatorFailure)
232265
verifier.verify(tampered_proof, recursive_merge_commitments, MergeSettings::APPEND);
233266
EXPECT_FALSE(CircuitChecker::check(builder));
234267
}
235-
// Tamper with the Translator proof non-preamble values
268+
// Tamper with the Translator proof non - preamble values
236269
{
237270
auto tampered_proof = proof;
238-
int seek = 10;
239-
for (auto& val : tampered_proof.translator_proof) {
240-
if (val > 0) { // tamper by finding the tenth non-zero value and incrementing it by 1
241-
if (--seek == 0) {
242-
val += 1;
243-
break;
244-
}
245-
}
246-
}
271+
tamper_with_libra_eval(tampered_proof.translator_proof);
247272

248273
Builder builder;
249274

@@ -296,9 +321,6 @@ TEST_F(GoblinRecursiveVerifierTests, TranslatorMergeConsistencyFailure)
296321
{
297322

298323
{
299-
using Commitment = TranslatorFlavor::Commitment;
300-
using FF = TranslatorFlavor::FF;
301-
using BF = TranslatorFlavor::BF;
302324

303325
Builder builder;
304326

@@ -310,27 +332,6 @@ TEST_F(GoblinRecursiveVerifierTests, TranslatorMergeConsistencyFailure)
310332
// Check natively that the proof is correct.
311333
EXPECT_TRUE(Goblin::verify(proof, merge_commitments, verifier_transcript, MergeSettings::APPEND));
312334

313-
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1298):
314-
// Better recursion testing - create more flexible proof tampering tests.
315-
// Modify the `op` commitment which a part of the Merge protocol.
316-
auto tamper_with_op_commitment = [](HonkProof& translator_proof) {
317-
// Compute the size of a Translator commitment (in bb::fr's)
318-
static constexpr size_t num_frs_comm = bb::field_conversion::calc_num_bn254_frs<Commitment>();
319-
// The `op` wire commitment is currently the second element of the proof, following the
320-
// `accumulated_result` which is a BN254 BaseField element.
321-
static constexpr size_t offset = bb::field_conversion::calc_num_bn254_frs<BF>();
322-
// Extract `op` fields and convert them to a Commitment object
323-
auto element_frs = std::span{ translator_proof }.subspan(offset, num_frs_comm);
324-
auto op_commitment = NativeTranscriptParams::template deserialize<Commitment>(element_frs);
325-
// Modify the commitment
326-
op_commitment = op_commitment * FF(2);
327-
// Serialize the tampered commitment into the proof (overwriting the valid one).
328-
auto op_commitment_reserialized = bb::NativeTranscriptParams::serialize(op_commitment);
329-
std::copy(op_commitment_reserialized.begin(),
330-
op_commitment_reserialized.end(),
331-
translator_proof.begin() + static_cast<std::ptrdiff_t>(offset));
332-
};
333-
334335
tamper_with_op_commitment(proof.translator_proof);
335336
// Construct and check the Goblin Recursive Verifier circuit
336337

barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ template <typename RecursiveFlavor> class RecursiveVerifierTest : public testing
293293
}
294294
// Check the size of the recursive verifier
295295
if constexpr (std::same_as<RecursiveFlavor, MegaZKRecursiveFlavor_<UltraCircuitBuilder>>) {
296-
uint32_t NUM_GATES_EXPECTED = 796978;
296+
uint32_t NUM_GATES_EXPECTED = 801953;
297297
ASSERT_EQ(static_cast<uint32_t>(outer_circuit.get_num_finalized_gates()), NUM_GATES_EXPECTED)
298298
<< "MegaZKHonk Recursive verifier changed in Ultra gate count! Update this value if you "
299299
"are sure this is expected.";

barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ template <class Builder_, class Fq, class Fr, class NativeGroup> class element {
4545
element();
4646
element(const typename NativeGroup::affine_element& input);
4747
element(const Fq& x, const Fq& y);
48+
element(const Fq& x, const Fq& y, const bool_ct& is_infinity);
4849

4950
element(const element& other);
5051
element(element&& other) noexcept;

0 commit comments

Comments
 (0)