Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ultra_honk.test.cpp
Go to the documentation of this file.
1#include "ultra_honk.test.hpp"
3
4#include <gtest/gtest.h>
5
6using namespace bb;
7
9
10#ifdef STARKNET_GARAGA_FLAVORS
11using FlavorTypes = testing::Types<UltraFlavor,
16 UltraStarknetFlavor,
17 UltraStarknetZKFlavor>;
18#else
20 testing::Types<UltraFlavor, UltraZKFlavor, UltraKeccakFlavor, UltraKeccakZKFlavor, UltraRollupFlavor>;
21#endif
32TYPED_TEST(UltraHonkTests, ProofLengthCheck)
33{
34 using Flavor = TypeParam;
39 using Proof = typename Flavor::Transcript::Proof;
40
41 auto builder = Builder{};
42 IO::add_default(builder);
43 // Construct a UH proof and ensure its size matches expectation; if not, the constant may need to be updated
45 auto verification_key = std::make_shared<typename Flavor::VerificationKey>(prover_instance->get_precomputed());
46 UltraProver_<Flavor> prover(prover_instance, verification_key);
47 Proof ultra_proof = prover.construct_proof();
48 const size_t virtual_log_n = Flavor::USE_PADDING ? CONST_PROOF_SIZE_LOG_N : prover_instance->log_dyadic_size();
49 size_t expected_proof_length = Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS(virtual_log_n) + IO::PUBLIC_INPUTS_SIZE;
50 EXPECT_EQ(ultra_proof.size(), expected_proof_length);
51}
52
60TYPED_TEST(UltraHonkTests, ANonZeroPolynomialIsAGoodPolynomial)
61{
62 auto circuit_builder = UltraCircuitBuilder();
63 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
64
65 auto prover_instance = std::make_shared<typename TestFixture::ProverInstance>(circuit_builder);
66 auto verification_key = std::make_shared<typename TypeParam::VerificationKey>(prover_instance->get_precomputed());
67 typename TestFixture::Prover prover(prover_instance, verification_key);
68 auto proof = prover.construct_proof();
69 auto& polynomials = prover_instance->polynomials;
70
71 auto ensure_non_zero = [](auto& polynomial) {
72 bool has_non_zero_coefficient = false;
73 for (auto& coeff : polynomial.coeffs()) {
74 has_non_zero_coefficient |= !coeff.is_zero();
75 }
76 ASSERT_TRUE(has_non_zero_coefficient);
77 };
78
79 for (auto& poly : polynomials.get_selectors()) {
80 ensure_non_zero(poly);
81 }
82
83 for (auto& poly : polynomials.get_tables()) {
84 ensure_non_zero(poly);
85 }
86
87 for (auto& poly : polynomials.get_wires()) {
88 ensure_non_zero(poly);
89 }
90}
91
97{
99 size_t num_gates = 10;
100
101 // Add some arbitrary arithmetic gates that utilize public inputs
103 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder);
104
105 TestFixture::prove_and_verify(builder, /*expected_result=*/true);
106}
107
108TYPED_TEST(UltraHonkTests, TestNoLookupProof)
109{
110 auto circuit_builder = UltraCircuitBuilder();
111
112 for (size_t i = 0; i < 16; ++i) {
113 for (size_t j = 0; j < 16; ++j) {
114 uint64_t left = static_cast<uint64_t>(j);
115 uint64_t right = static_cast<uint64_t>(i);
116 uint32_t left_idx = circuit_builder.add_variable(fr(left));
117 uint32_t right_idx = circuit_builder.add_variable(fr(right));
118 uint32_t result_idx = circuit_builder.add_variable(fr(left ^ right));
119
120 uint32_t add_idx =
121 circuit_builder.add_variable(fr(left) + fr(right) + circuit_builder.get_variable(result_idx));
122 circuit_builder.create_big_add_gate(
123 { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) });
124 }
125 }
126 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
127
128 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
129}
130
131TYPED_TEST(UltraHonkTests, TestEllipticGate)
132{
134 typedef grumpkin::g1::element element;
135 auto circuit_builder = UltraCircuitBuilder();
136
139
140 affine_element p3(element(p1) + element(p2));
141
142 uint32_t x1 = circuit_builder.add_variable(p1.x);
143 uint32_t y1 = circuit_builder.add_variable(p1.y);
144 uint32_t x2 = circuit_builder.add_variable(p2.x);
145 uint32_t y2 = circuit_builder.add_variable(p2.y);
146 uint32_t x3 = circuit_builder.add_variable(p3.x);
147 uint32_t y3 = circuit_builder.add_variable(p3.y);
148
149 circuit_builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 });
150
151 p3 = affine_element(element(p1) + element(p2));
152 x3 = circuit_builder.add_variable(p3.x);
153 y3 = circuit_builder.add_variable(p3.y);
154 circuit_builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 });
155
156 p3 = affine_element(element(p1) - element(p2));
157 x3 = circuit_builder.add_variable(p3.x);
158 y3 = circuit_builder.add_variable(p3.y);
159 circuit_builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, -1 });
160
161 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
162
163 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
164}
165
166TYPED_TEST(UltraHonkTests, NonNativeFieldMultiplication)
167{
168 using fq = fq;
169 auto circuit_builder = UltraCircuitBuilder();
170
173 uint256_t modulus = fq::modulus;
174
177 uint1024_t p_big = uint512_t(uint256_t(modulus));
178
179 uint1024_t q_big = (a_big * b_big) / p_big;
180 uint1024_t r_big = (a_big * b_big) % p_big;
181
182 uint256_t q(q_big.lo.lo);
183 uint256_t r(r_big.lo.lo);
184
185 const auto split_into_limbs = [&](const uint512_t& input) {
186 constexpr size_t NUM_BITS = 68;
187 std::array<fr, 4> limbs;
188 limbs[0] = input.slice(0, NUM_BITS).lo;
189 limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo;
190 limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo;
191 limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo;
192 return limbs;
193 };
194
195 const auto get_limb_witness_indices = [&](const std::array<fr, 4>& limbs) {
196 std::array<uint32_t, 4> limb_indices;
197 limb_indices[0] = circuit_builder.add_variable(limbs[0]);
198 limb_indices[1] = circuit_builder.add_variable(limbs[1]);
199 limb_indices[2] = circuit_builder.add_variable(limbs[2]);
200 limb_indices[3] = circuit_builder.add_variable(limbs[3]);
201 return limb_indices;
202 };
203 const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4);
204 auto modulus_limbs = split_into_limbs(BINARY_BASIS_MODULUS - uint512_t(modulus));
205
206 const auto a_indices = get_limb_witness_indices(split_into_limbs(uint256_t(a)));
207 const auto b_indices = get_limb_witness_indices(split_into_limbs(uint256_t(b)));
208 const auto q_indices = get_limb_witness_indices(split_into_limbs(uint256_t(q)));
209 const auto r_indices = get_limb_witness_indices(split_into_limbs(uint256_t(r)));
210
212 a_indices, b_indices, q_indices, r_indices, modulus_limbs,
213 };
214 const auto [lo_1_idx, hi_1_idx] = circuit_builder.evaluate_non_native_field_multiplication(inputs);
215
216 // Range constrain the lo and hi carry outputs
217 const bool is_low_70_bits = uint256_t(circuit_builder.get_variable(lo_1_idx)).get_msb() < 70;
218 const bool is_high_70_bits = uint256_t(circuit_builder.get_variable(hi_1_idx)).get_msb() < 70;
219 if (is_low_70_bits && is_high_70_bits) {
220 // Uses more efficient NNF range check if both limbs are < 2^70
221 circuit_builder.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70);
222 } else {
223 // Fallback to default range checks
224 circuit_builder.create_limbed_range_constraint(lo_1_idx, 72);
225 circuit_builder.create_limbed_range_constraint(hi_1_idx, 72);
226 }
227
228 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
229
230 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
231}
232
233TYPED_TEST(UltraHonkTests, RangeChecksOnDuplicates)
234{
235 auto circuit_builder = UltraCircuitBuilder();
236
237 uint32_t a = circuit_builder.add_variable(fr(100));
238 uint32_t b = circuit_builder.add_variable(fr(100));
239 uint32_t c = circuit_builder.add_variable(fr(100));
240 uint32_t d = circuit_builder.add_variable(fr(100));
241
242 circuit_builder.assert_equal(a, b);
243 circuit_builder.assert_equal(a, c);
244 circuit_builder.assert_equal(a, d);
245
246 circuit_builder.create_small_range_constraint(a, 1000);
247 circuit_builder.create_small_range_constraint(b, 1001);
248 circuit_builder.create_small_range_constraint(c, 999);
249 circuit_builder.create_small_range_constraint(d, 1000);
250
251 circuit_builder.create_big_add_gate(
252 {
253 a,
254 b,
255 c,
256 d,
257 0,
258 0,
259 0,
260 0,
261 0,
262 },
263 false);
264
265 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
266
267 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
268}
269
270// Ensure copy constraints added on variables smaller than 2^14, which have been previously
271// range constrained, do not break the set equivalence checks because of indices mismatch.
272// 2^14 is DEFAULT_PLOOKUP_RANGE_BITNUM i.e. the maximum size before a variable gets sliced
273// before range constraints are applied to it.
274TYPED_TEST(UltraHonkTests, RangeConstraintSmallVariable)
275{
276 auto circuit_builder = UltraCircuitBuilder();
277
278 uint16_t mask = (1 << 8) - 1;
279 int a = engine.get_random_uint16() & mask;
280 uint32_t a_idx = circuit_builder.add_variable(fr(a));
281 uint32_t b_idx = circuit_builder.add_variable(fr(a));
282 ASSERT_NE(a_idx, b_idx);
283 uint32_t c_idx = circuit_builder.add_variable(fr(a));
284 ASSERT_NE(c_idx, b_idx);
285 circuit_builder.create_dyadic_range_constraint(b_idx, 8, "bad range");
286 circuit_builder.assert_equal(a_idx, b_idx);
287 circuit_builder.create_dyadic_range_constraint(c_idx, 8, "bad range");
288 circuit_builder.assert_equal(a_idx, c_idx);
289
290 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
291
292 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
293}
294
301TYPED_TEST(UltraHonkTests, NativeVKHashMismatchDetected)
302{
303 using Flavor = TypeParam;
305 using Builder = typename Flavor::CircuitBuilder;
306 using Prover = UltraProver_<Flavor>;
309 using VKAndHash = typename Flavor::VKAndHash;
310 using Verifier = UltraVerifier_<Flavor, IO>;
311
312 // Create a simple circuit
315 this->set_default_pairing_points_and_ipa_claim_and_proof(builder);
316
317 // Create prover instance and VK
318 auto prover_instance = std::make_shared<ProverInstance>(builder);
319 auto vk = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
320
321 // Create prover and prove
322 Prover prover(prover_instance, vk);
323 auto proof = prover.construct_proof();
324 auto vk_and_hash = std::make_shared<VKAndHash>(vk);
325
326 // Corrupt the stored hash
327 vk_and_hash->hash = fr::random_element();
328
329 // Verification should fail with BB_ASSERT_EQ detecting the mismatch
330 Verifier verifier(vk_and_hash);
331 EXPECT_THROW_WITH_MESSAGE(verifier.verify_proof(proof), "VK Hash Mismatch");
332}
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:192
Manages the data that is propagated on the public inputs of an application/function circuit.
The verification key is responsible for storing the commitments to the precomputed (non-witnessk) pol...
ECCVMCircuitBuilder CircuitBuilder
static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS
static constexpr bool USE_PADDING
static void add_arithmetic_gates_with_public_inputs(Builder &builder, const size_t num_gates=4)
Add a specified number of arithmetic gates (with public inputs) to the provided circuit.
static void add_arithmetic_gates(Builder &builder, const size_t num_gates=4)
Add a specified number of arithmetic gates to the provided circuit.
Base Native verification key class.
Definition flavor.hpp:141
A ProverInstance is normally constructed from a finalized circuit and it contains all the information...
The data that is propagated on the public inputs of a rollup circuit.
UltraRollupFlavor extends UltraFlavor with IPA proof support.
Child class of UltraFlavor that runs with ZK Sumcheck.
static affine_element random_element(numeric::RNG *engine=nullptr) noexcept
Samples a random point on the curve.
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition element.hpp:33
virtual uint16_t get_random_uint16()=0
constexpr uint64_t get_msb() const
Manages the data that is propagated on the public inputs of an application/function circuit.
The data that is propagated on the public inputs of a rollup circuit.
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
grumpkin::g1::affine_element affine_element
Definition c_bind.hpp:9
numeric::RNG & engine
AvmProvingInputs inputs
testing::Types< MegaFlavor, UltraFlavor, UltraZKFlavor, UltraRollupFlavor > FlavorTypes
uintx< uint256_t > uint512_t
Definition uintx.hpp:307
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FqParams > fq
Definition fq.hpp:169
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
field< Bn254FrParams > fr
Definition fr.hpp:174
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr uint256_t modulus
static field random_element(numeric::RNG *engine=nullptr) noexcept
An object storing two EC points that represent the inputs to a pairing check.
void ensure_non_zero(auto &polynomial)