Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
zk_boundary.test.cpp
Go to the documentation of this file.
1
14#include <gtest/gtest.h>
15
23
24using namespace bb;
25
26class ZKBoundaryTests : public ::testing::Test {
27 public:
29 using FF = Flavor::FF;
37
39
49
54 {
55 auto verification_key = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
56 auto vk_and_hash = std::make_shared<Flavor::VKAndHash>(verification_key);
57 Prover prover(prover_instance, verification_key);
58 Verifier verifier(vk_and_hash);
59 auto proof = prover.construct_proof();
60 return verifier.verify_proof(proof).result;
61 }
62};
63
71TEST_F(ZKBoundaryTests, EccOpWireAlignmentAtDisabledBoundary)
72{
73 auto instance = build_instance();
74 auto& polys = instance->polynomials;
75
76 // ecc_op_wire has data starting at TRACE_OFFSET (w_shift form); lagrange_ecc_op fires there.
77 constexpr size_t ecc_op_start = ProverInstance::TRACE_OFFSET;
78
79 // Sanity: lagrange_ecc_op should be 1 starting at ecc_op_start
80 ASSERT_EQ(polys.lagrange_ecc_op[ecc_op_start], FF{ 1 })
81 << "lagrange_ecc_op should be active at row " << ecc_op_start;
82
83 // Verify alignment: ecc_op_wire[r] == w[r + NUM_ZERO_ROWS] = w_shift[r] for each active ecc_op row.
84 auto ecc_op_wires = polys.get_ecc_op_wires();
85 auto wires = polys.get_wires();
86 for (size_t r = ecc_op_start; polys.lagrange_ecc_op[r] == FF{ 1 }; r++) {
87 for (auto [ecc_op_wire, wire] : zip_view(ecc_op_wires, wires)) {
88 ASSERT_EQ(ecc_op_wire[r], wire[r + NUM_ZERO_ROWS]) << "ecc_op_wire / w_shift mismatch at row " << r;
89 }
90 }
91
92 // Now corrupt: ecc_op_wire[ecc_op_start] and verify EccOpQueueRelation detects it
93 FF original = polys.ecc_op_wire_1[ecc_op_start];
94 polys.ecc_op_wire_1.at(ecc_op_start) = original + FF{ 1 };
95
97 polys, instance->relation_parameters, "EccOpQueue", /*start_row=*/ecc_op_start);
98 EXPECT_FALSE(failures.empty()) << "Corrupting ecc_op_wire at the boundary should cause EccOpQueueRelation failure";
99
100 // Restore
101 polys.ecc_op_wire_1.at(ecc_op_start) = original;
102}
103
109TEST_F(ZKBoundaryTests, WitnessMaskingStructure)
110{
111 auto instance = build_instance();
112 auto& polys = instance->polynomials;
113
114 // Row 0 must be zero for all shiftable witness wires (shift convention)
115 for (auto wire : polys.get_wires()) {
116 EXPECT_EQ(wire[0], FF{ 0 }) << "Shiftable wire must have zero at row 0";
117 }
118
119 // Rows 1-3 should have non-zero masking values (overwhelmingly likely for random elements)
120 for (auto wire : polys.get_wires()) {
121 bool has_masking = false;
122 for (size_t r = 1; r <= NUM_MASKED_ROWS; r++) {
123 has_masking |= (wire[r] != FF{ 0 });
124 }
125 EXPECT_TRUE(has_masking) << "Witness wire should have non-zero masking in rows 1-3";
126 }
127}
128
136TEST_F(ZKBoundaryTests, DisabledRegionIsolation)
137{
138 auto instance = build_instance();
139 auto& polys = instance->polynomials;
140
141 // Run oink to compute derived witnesses (logderiv inverses, z_perm, etc.)
142 auto verification_key = std::make_shared<VerificationKey>(instance->get_precomputed());
143 auto transcript = std::make_shared<Flavor::Transcript>();
144 OinkProver<Flavor> oink(instance, verification_key, transcript);
145 oink.prove();
146
147 // Check all relations pass on clean state (start from row TRACE_OFFSET
148 // since the RelationChecker doesn't account for row-disabling)
149 auto clean_failures = RelationChecker<MegaFlavor>::check_all(polys, instance->relation_parameters);
150 // Relations may have non-zero values at rows 0-3 (before row-disabling multiplier),
151 // but should be clean in the active region. Check the specific row of each failure.
152 for (auto& [name, subrel_failures] : clean_failures) {
153 for (auto& [subrel_idx, row] : subrel_failures) {
154 EXPECT_LT(row, static_cast<uint32_t>(ProverInstance::TRACE_OFFSET))
155 << "Relation " << name << " (subrelation " << subrel_idx << ") fails at active row " << row;
156 }
157 }
158}
159
163TEST_F(ZKBoundaryTests, LagrangeFirstPosition)
164{
165 auto instance = build_instance();
166 auto& polys = instance->polynomials;
167
168 // lagrange_first should be 1 at row TRACE_OFFSET and 0 elsewhere
169 EXPECT_EQ(polys.lagrange_first[ProverInstance::TRACE_OFFSET], FF{ 1 });
170 for (size_t r = 0; r < ProverInstance::TRACE_OFFSET; r++) {
171 EXPECT_EQ(polys.lagrange_first[r], FF{ 0 }) << "lagrange_first should be 0 at disabled row " << r;
172 }
173}
174
180TEST_F(ZKBoundaryTests, EccOpWiresZeroInDisabledRegion)
181{
182 auto instance = build_instance();
183 auto& polys = instance->polynomials;
184
185 // ecc_op data lives at rows [TRACE_OFFSET, ...) under the w_shift relation form; everything before
186 // (rows [0, TRACE_OFFSET)) should be zero.
187 for (auto ecc_op_wire : polys.get_ecc_op_wires()) {
188 for (size_t r = 0; r < ProverInstance::TRACE_OFFSET; r++) {
189 EXPECT_EQ(ecc_op_wire[r], FF{ 0 }) << "ecc_op_wire should be zero at row " << r << " (disabled region)";
190 }
191 }
192}
193
198TEST_F(ZKBoundaryTests, ProveAndVerifyMegaZK)
199{
200 auto instance = build_instance();
201 EXPECT_TRUE(prove_and_verify(instance));
202}
203
208TEST_F(ZKBoundaryTests, CorruptionInActiveRegionFailsProof)
209{
212
213 // Build two instances from the same circuit
214 auto builder_copy = builder;
215 auto good_instance = std::make_shared<ProverInstance>(builder);
216 auto bad_instance = std::make_shared<ProverInstance>(builder_copy);
217
218 // Find first arithmetic gate in the active region
219 const size_t dyadic_size = bad_instance->dyadic_size();
220 for (size_t i = ProverInstance::TRACE_OFFSET; i < dyadic_size; i++) {
221 if (bad_instance->polynomials.q_arith[i] != FF{ 0 }) {
222 bad_instance->polynomials.w_l.at(i) += FF{ 1 };
223 break;
224 }
225 }
226
227 EXPECT_TRUE(prove_and_verify(good_instance));
228 EXPECT_FALSE(prove_and_verify(bad_instance));
229}
std::shared_ptr< Napi::ThreadSafeFunction > instance
static bool prove_and_verify(std::shared_ptr< ProverInstance > prover_instance)
Construct proof and verify.
static void SetUpTestSuite()
static std::shared_ptr< ProverInstance > build_instance()
Build a prover instance from a simple Mega circuit with ECC ops.
static void construct_simple_circuit(MegaBuilder &builder)
Generate a simple test circuit with some ECC op gates and conventional arithmetic gates.
Curve::ScalarField FF
MegaCircuitBuilder CircuitBuilder
bb::Polynomial< FF > Polynomial
NativeVerificationKey_< PrecomputedEntities< Commitment >, Codec, HashFunction, CommitmentKey > VerificationKey
The verification key stores commitments to the precomputed (non-witness) polynomials used by the veri...
Child class of MegaFlavor that runs with ZK Sumcheck.
Base Native verification key class.
Definition flavor.hpp:135
Executes the "Oink" phase of the Honk proving protocol: the initial rounds that commit to witness dat...
void prove(bool emit_alpha=true)
Commit to witnesses, compute relation parameters, and prepare for Sumcheck.
Contains all the information required by a Honk prover to create a proof, constructed from a finalize...
static constexpr size_t TRACE_OFFSET
A debugging utility for checking whether a set of polynomials satisfies the relations for a given Fla...
static AllSubrelationFailures check_all(const auto &polynomials, const auto &params)
Check that the provided polynomials satisfy all relations for a given Flavor.
A wrapper for Relations to expose methods used by the Sumcheck prover or verifier to add the contribu...
Output verify_proof(const Proof &proof)
Perform ultra verification.
The VerifierInstance encapsulates all the necessary information for a Honk Verifier to verify a proof...
AluTraceBuilder builder
Definition alu.test.cpp:124
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:155
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13