Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
avm2_recursion_constraint.test.cpp
Go to the documentation of this file.
18
19#include <gtest/gtest.h>
20#include <memory>
21#include <vector>
22
23using namespace acir_format;
24using namespace bb;
25using namespace bb::avm2;
26
28 public:
31
33 using FF = Builder::FF;
34
36 public:
37 enum class Target : uint8_t { None, PublicInputs, Proof };
39 {
41 return targets;
42 };
43 static std::vector<std::string> get_labels()
44 {
45 std::vector<std::string> labels = { "None", "PublicInputs", "Proof" };
46 return labels;
47 };
48 };
49
51 {
52 auto [trace, public_inputs] = avm2::testing::get_minimal_trace_with_pi();
53
54 AvmProver prover;
55 auto proof = prover.prove(std::move(trace));
56 proof.resize(AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED, FF::zero()); // Pad proof
57
58 const bool verified = prover.verify(proof, public_inputs);
59 EXPECT_TRUE(verified) << "native proof verification failed";
60
61 auto public_inputs_flat = PublicInputs::columns_to_flat(public_inputs.to_columns());
62 public_inputs_flat.resize(AVM_PUBLIC_INPUTS_COLUMNS_COMBINED_LENGTH, FF::zero()); // Pad public inputs
63
64 return { proof, public_inputs_flat };
65 }
66
68
69 static void generate_constraints(AcirConstraint& avm_recursion_constraint, WitnessVector& witness_values)
70 {
71 const auto [proof, public_inputs_flat] = create_avm_data();
72 avm_recursion_constraint = RecursionConstraint{
73 .key = {}, // Unused, the key is hard-coded in the circuit
74 .proof = add_to_witness_and_track_indices(witness_values, proof),
75 .public_inputs = add_to_witness_and_track_indices(witness_values, public_inputs_flat),
76 .key_hash = IS_CONSTANT, // Unused, the key hash is hard-coded in the circuit
77 .proof_type = AVM,
79 };
80 }
81
83 AcirConstraint constraint, WitnessVector witness_values, const InvalidWitness::Target& invalid_witness_target)
84 {
85 switch (invalid_witness_target) {
87 break;
89 // Tamper with the public inputs
90 witness_values[constraint.public_inputs[0]] += FF::one();
91 break;
92 }
94 // Tamper with the proof by changing one of the univariate coefficients
95 witness_values[constraint.proof[FrCodec::calc_num_fields<AvmFlavor::Commitment>() *
97 break;
98 }
99 }
100
101 return { constraint, witness_values };
102 }
103};
104
105class AvmRecursionConstraintTest : public ::testing::Test, public TestClass<AvmRecursionConstraintTestingFunctions> {
106 protected:
108};
109
110TEST_F(AvmRecursionConstraintTest, GenerateVKFromConstraints)
111{
113 GTEST_SKIP() << "Skipping slow test";
114 }
115 // AVM constraints are always proven with UltraRollupFlavor (they are part of the base rollup circuit)
116 [[maybe_unused]] size_t num_gates = test_vk_independence<UltraRollupFlavor>();
117
118 // TODO(fcarreiro): Re-enable when the VK is fixed.
119 // EXPECT_EQ(num_gates, FINALIZED_GOBLIN_AVM_GATE_COUNT);
120}
121
123{
125 GTEST_SKIP() << "Skipping slow test";
126 }
127 std::vector<std::string> _ = test_tampering();
128}
129
130// TODO(fcarreiro): Re-enable when the VK is fixed.
131TEST_F(AvmRecursionConstraintTest, DISABLED_GateCountAndVKCheck)
132{
134 GTEST_SKIP() << "Skipping slow test";
135 }
137
138 AcirConstraint constraint;
139 WitnessVector witness;
140 Base::generate_constraints(constraint, witness);
141
143
144 AcirProgram program = { acir_format, {} };
145 ProgramMetadata metadata = Base::generate_metadata();
146 metadata.collect_gates_per_opcode = true;
147 auto builder = create_circuit<Builder>(program, metadata);
148
149 EXPECT_EQ(program.constraints.gates_per_opcode.size(), 1);
151
152 auto prover_instance = std::make_shared<ProverInstance>(builder);
153 auto vk = std::make_shared<typename UltraRollupFlavor::VerificationKey>(prover_instance->get_precomputed());
154
155 static constexpr FF EXPECTED_OUTER_VK_HASH =
156 FF("0x195059523571dbadeae1b213250567e17b4994568b736b73a1aae2b0c65fd2cd");
157 EXPECT_EQ(vk->hash(), EXPECTED_OUTER_VK_HASH)
158 << "The VK hash of the outer circuit in the Goblinized AVM recursive verifier has changed. If this is "
159 "expected, update the expected value in the test.";
160}
161
162class AvmRecursionInnerCircuitTests : public ::testing::Test {
163 public:
168
169 static constexpr FF EXPECTED_INNER_VK_HASH =
170 FF("0x2be1149e37f087a8420d61586aed0d7db942f04937f2d3fa01bdf7f4cc62fb3c");
171 static constexpr size_t EXPECT_GATE_COUNT = 1203700;
172
174
177 const HonkProof& proof,
178 const std::vector<FF>& public_inputs_flat)
179 {
180 std::vector<field_t<Builder>> stdlib_public_inputs_flat;
181 stdlib_public_inputs_flat.reserve(AVM_PUBLIC_INPUTS_COLUMNS_COMBINED_LENGTH);
182 for (const auto public_input : public_inputs_flat) {
183 stdlib_public_inputs_flat.emplace_back(field_t<Builder>::from_witness(&outer_builder, public_input));
184 }
185 stdlib::Proof<Builder> stdlib_proof;
186 stdlib_proof.reserve(AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED);
187 for (const auto proof_element : proof) {
188 stdlib_proof.emplace_back(field_t<Builder>::from_witness(&outer_builder, proof_element));
189 }
190
192 PublicInputs::flat_to_columns<field_t<Builder>>(stdlib_public_inputs_flat);
193 auto [mega_proof, goblin_proof, mega_vk] =
195 public_inputs);
196
197 return { stdlib_proof, public_inputs, { mega_proof, goblin_proof, mega_vk } };
198 }
199};
200
201// TODO(fcarreiro): Re-enable when the VK is fixed.
202TEST_F(AvmRecursionInnerCircuitTests, DISABLED_GateCountAndVKCheck)
203{
204 using MegaAvmProverInstance = ProverInstance_<MegaAvmFlavor>;
205 using MegaAvmVerificationKey = MegaAvmFlavor::VerificationKey;
206
208 GTEST_SKIP() << "Skipping slow test";
209 }
210 const auto [proof, public_inputs_flat] = AvmRecursionConstraintTestingFunctions::create_avm_data();
211
212 Builder outer_builder;
213
214 std::vector<field_t<Builder>> stdlib_public_inputs_flat;
215 stdlib_public_inputs_flat.reserve(AVM_PUBLIC_INPUTS_COLUMNS_COMBINED_LENGTH);
216 for (const auto public_input : public_inputs_flat) {
217 stdlib_public_inputs_flat.emplace_back(field_t<Builder>::from_witness(&outer_builder, public_input));
218 }
219 stdlib::Proof<Builder> stdlib_proof;
220 stdlib_proof.reserve(AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED);
221 for (const auto proof_element : proof) {
222 stdlib_proof.emplace_back(field_t<Builder>::from_witness(&outer_builder, proof_element));
223 }
224
226 PublicInputs::flat_to_columns<field_t<Builder>>(stdlib_public_inputs_flat);
227
228 MegaCircuitBuilder inner_builder;
230 inner_builder, stdlib_proof, public_inputs);
231
232 auto mega_proving_key = std::make_shared<MegaAvmProverInstance>(inner_builder);
233 auto mega_vk = std::make_shared<MegaAvmVerificationKey>(mega_proving_key->get_precomputed());
234
235 EXPECT_EQ(mega_vk->hash(), EXPECTED_INNER_VK_HASH)
236 << "The VK hash of the inner circuit in the Goblinized AVM recursive verifier has changed. If this is "
237 "expected, update the expected value in the test.";
238 EXPECT_EQ(inner_builder.num_gates(), EXPECT_GATE_COUNT);
239}
240
247{
249 GTEST_SKIP() << "Skipping slow test";
250 }
251 const auto [proof, public_inputs_flat] = AvmRecursionConstraintTestingFunctions::create_avm_data();
252
253 {
254 Builder outer_builder;
255 auto [stdlib_proof, public_inputs, inner_prover_output] =
256 create_and_prove_inner_circuit(outer_builder, proof, public_inputs_flat);
257
258 auto mega_proof_tampered = inner_prover_output.mega_proof;
259 mega_proof_tampered[0] += FF::one(); // Tamper with the first public input
260
261 TwoLayerAvmRecursiveVerifier goblin_avm_verifier(outer_builder);
262 TwoLayerAvmRecursiveVerifierOutput output = goblin_avm_verifier.construct_outer_recursive_verification_circuit(
263 stdlib_proof,
264 public_inputs,
265 { mega_proof_tampered, inner_prover_output.goblin_proof, inner_prover_output.mega_vk });
266
267 EXPECT_TRUE(outer_builder.failed());
268 }
269
270 {
271 Builder outer_builder;
272 auto [stdlib_proof, public_inputs, inner_prover_output] =
273 create_and_prove_inner_circuit(outer_builder, proof, public_inputs_flat);
274
275 // Tamper with one of the ECCVM op evaluations. The `op` evaluation is located 3 evaluations before the end of
276 // pre-IPA proof (followed by x_lo_y_hi, x_hi_z_1, y_lo_z_2 evaluations). See also
277 // GoblinAvmRecursiveVerifierTests::tamper_with_eccvm_op_eval for reference
278 static constexpr size_t evals_after_op = 3; // x_lo_y_hi, x_hi_z_1, y_lo_z_2
279 const size_t op_eval_idx = inner_prover_output.goblin_proof.eccvm_proof.size() - evals_after_op;
280
281 auto goblin_proof_tampered = inner_prover_output.goblin_proof;
282 goblin_proof_tampered.eccvm_proof[op_eval_idx] += FF(1);
283
284 TwoLayerAvmRecursiveVerifier goblin_avm_verifier(outer_builder);
285 TwoLayerAvmRecursiveVerifierOutput output = goblin_avm_verifier.construct_outer_recursive_verification_circuit(
286 stdlib_proof,
287 public_inputs,
288 { inner_prover_output.mega_proof, goblin_proof_tampered, inner_prover_output.mega_vk });
289
290 EXPECT_TRUE(outer_builder.failed());
291 }
292
293 {
294 Builder outer_builder;
295 auto [stdlib_proof, public_inputs, inner_prover_output] =
296 create_and_prove_inner_circuit(outer_builder, proof, public_inputs_flat);
297
298 auto mega_vk_tampered = inner_prover_output.mega_vk;
299 mega_vk_tampered->q_m = mega_vk_tampered->q_m + MegaAvmFlavor::Commitment::one(); // Tamper with q_m commitment
300
301 TwoLayerAvmRecursiveVerifier goblin_avm_verifier(outer_builder);
302 TwoLayerAvmRecursiveVerifierOutput output = goblin_avm_verifier.construct_outer_recursive_verification_circuit(
303 stdlib_proof,
304 public_inputs,
305 { inner_prover_output.mega_proof, inner_prover_output.goblin_proof, mega_vk_tampered });
306
307 EXPECT_TRUE(outer_builder.failed());
308 }
309}
#define AVM_PUBLIC_INPUTS_COLUMNS_COMBINED_LENGTH
#define AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED
static std::pair< AvmProver::Proof, std::vector< FF > > create_avm_data()
static std::pair< AcirConstraint, WitnessVector > invalidate_witness(AcirConstraint constraint, WitnessVector witness_values, const InvalidWitness::Target &invalid_witness_target)
static void generate_constraints(AcirConstraint &avm_recursion_constraint, WitnessVector &witness_values)
static std::tuple< stdlib::Proof< Builder >, std::vector< std::vector< field_t< Builder > > >, InnerProverOutput > create_and_prove_inner_circuit(Builder &outer_builder, const HonkProof &proof, const std::vector< FF > &public_inputs_flat)
TwoLayerAvmRecursiveVerifier::InnerProverOutput InnerProverOutput
Base Native verification key class.
Definition flavor.hpp:172
A ProverInstance is normally constructed from a finalized circuit and it contains all the information...
typename ExecutionTrace::FF FF
static constexpr size_t NUM_WITNESS_ENTITIES
Definition flavor.hpp:55
bool verify(const Proof &proof, const PublicInputs &pi)
Proof prove(tracegen::TraceContainer &&trace)
Recursive verifier of AVM2 proofs that utilizes the Goblin mechanism for efficient EC operations.
static void construct_inner_recursive_verification_circuit(MegaCircuitBuilder &inner_builder, const stdlib::Proof< UltraCircuitBuilder > &stdlib_proof, const std::vector< std::vector< UltraFF > > &public_inputs)
Construct the inner recursive verification circuit for the AVM2 recursive verifier.
static InnerProverOutput construct_and_prove_inner_recursive_verification_circuit(const stdlib::Proof< UltraCircuitBuilder > &stdlib_proof, const std::vector< std::vector< UltraFF > > &public_inputs)
Construct and prove the inner Mega-arithmetized AVM recursive verifier circuit.
stdlib::recursion::honk::UltraRecursiveVerifierOutput< UltraCircuitBuilder > TwoLayerAvmRecursiveVerifierOutput
TwoLayerAvmRecursiveVerifierOutput construct_outer_recursive_verification_circuit(const stdlib::Proof< UltraCircuitBuilder > &stdlib_proof, const std::vector< std::vector< UltraFF > > &public_inputs, const InnerProverOutput &inner_output) const
Construct the outer circuit which recursively verifies a Mega proof and a Goblin proof.
A simple wrapper around a vector of stdlib field elements representing a proof.
Definition proof.hpp:19
AluTraceBuilder builder
Definition alu.test.cpp:124
TestTraceContainer trace
AcirFormat constraint_to_acir_format(const ConstraintType &constraint)
Convert an AcirConstraint (single or vector) to AcirFormat by going through the full ACIR serde flow.
constexpr size_t GOBLIN_AVM_GATE_COUNT
std::vector< bb::fr > WitnessVector
std::vector< uint32_t > add_to_witness_and_track_indices(std::vector< bb::fr > &witness, const T &input)
Append values to a witness vector and track their indices.
Definition utils.hpp:90
bool skip_slow_tests()
Check if slow tests should be skipped.
Definition fixtures.cpp:199
std::pair< tracegen::TraceContainer, PublicInputs > get_minimal_trace_with_pi()
Definition fixtures.cpp:183
AvmFlavorSettings::FF FF
Definition field.hpp:10
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
TEST_F(BoomerangGoblinRecursiveVerifierTests, graph_description_basic)
Construct and check a goblin recursive verification circuit.
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
std::vector< fr > HonkProof
Definition proof.hpp:15
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Barretenberg's representation of ACIR constraints.
std::vector< size_t > gates_per_opcode
Struct containing both the constraints to be added to the circuit and the witness vector.
Metadata required to create a circuit.
RecursionConstraint struct contains information required to recursively verify a proof.
static WitnessOrConstant from_constant(FF value)
static std::vector< FF > columns_to_flat(std::vector< std::vector< FF > > const &columns)
Definition avm_io.cpp:289
Output type for recursive ultra verification.