52#include <gtest/gtest.h>
65struct ValidTranslatorState {
77ValidTranslatorState build_valid_translator_state()
79 const size_t full_circuit_size = Flavor::MINI_CIRCUIT_SIZE * Flavor::CONCATENATION_GROUP_SIZE;
87 for (
const auto&
group : pp.get_groups_to_be_concatenated()) {
88 for (
auto& poly :
group) {
89 if (poly.is_empty()) {
92 for (
size_t i = poly.start_index(); i < poly.end_index() - NUM_DISABLED_ROWS_IN_SUMCHECK; i++) {
95 for (
size_t i = poly.end_index() - NUM_DISABLED_ROWS_IN_SUMCHECK; i < poly.end_index(); i++) {
107 key.compute_lagrange_polynomials();
108 key.compute_extra_range_constraint_numerator();
109 key.compute_concatenated_polynomials();
110 key.compute_translator_range_constraint_ordered_polynomials();
114 compute_grand_product<Flavor, TranslatorPermutationRelation<FF>>(pp, params);
127ValidTranslatorState build_valid_accumulator_transfer_state()
135 op_queue->construct_zk_columns();
138 for (
size_t i = 0; i < 50; i++) {
139 op_queue->add_accumulate(GroupElement::random_element(&
engine));
142 op_queue->eq_and_reset();
144 for (
size_t i = 0; i < 50; i++) {
145 op_queue->add_accumulate(GroupElement::random_element(&
engine));
148 op_queue->eq_and_reset();
149 for (
size_t i = 0; i < Flavor::CircuitBuilder::NUM_RANDOM_OPS_END; i++) {
150 op_queue->random_op_ultra_only();
152 op_queue->merge_fixed_append(op_queue->get_append_offset_for_prover());
154 const auto batching_challenge_v = BF::random_element(&
engine);
155 const auto evaluation_input_x = BF::random_element(&
engine);
161 auto& pp =
key.proving_key->polynomials;
164 pp.accumulators_binary_limbs_1[Flavor::RESULT_ROW],
165 pp.accumulators_binary_limbs_2[Flavor::RESULT_ROW],
166 pp.accumulators_binary_limbs_3[Flavor::RESULT_ROW] };
170 static constexpr size_t NUM_LIMB_BITS = Flavor::CircuitBuilder::NUM_LIMB_BITS;
171 auto uint_input_x =
uint256_t(evaluation_input_x);
173 uint_input_x.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2),
174 uint_input_x.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3),
175 uint_input_x.slice(NUM_LIMB_BITS * 3, NUM_LIMB_BITS * 4),
177 auto v_power = BF::one();
178 for (
size_t i = 0; i < 4; i++) {
179 v_power *= batching_challenge_v;
182 uint_v_power.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2),
183 uint_v_power.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3),
184 uint_v_power.slice(NUM_LIMB_BITS * 3, NUM_LIMB_BITS * 4),
204 auto [
key, params] = build_valid_translator_state();
205 auto& pp =
key.proving_key->polynomials;
210 EXPECT_TRUE(baseline.empty()) <<
"Baseline permutation should pass";
214 const size_t corrupt_pos = Flavor::MINI_CIRCUIT_SIZE + 1;
218 compute_grand_product<Flavor, TranslatorPermutationRelation<FF>>(pp, params);
222 EXPECT_FALSE(failures.empty()) <<
"Permutation should fail after concatenated corruption";
231 auto [
key, params] = build_valid_translator_state();
232 auto& pp =
key.proving_key->polynomials;
234 const size_t full_circuit_size = Flavor::MINI_CIRCUIT_SIZE * Flavor::CONCATENATION_GROUP_SIZE;
238 pp, params,
"TranslatorDeltaRangeConstraintRelation");
239 EXPECT_TRUE(baseline.empty()) <<
"Baseline delta range should pass";
242 const size_t real_last_pos = full_circuit_size - Flavor::MAX_RANDOM_VALUES_PER_ORDERED - 1;
243 pp.ordered_range_constraints_0.at(real_last_pos) =
FF(42);
246 pp, params,
"TranslatorDeltaRangeConstraintRelation");
247 EXPECT_FALSE(failures.empty()) <<
"Delta range should fail when real_last position != 2^14 - 1";
255 auto [
key, params] = build_valid_translator_state();
256 auto& pp =
key.proving_key->polynomials;
261 EXPECT_TRUE(baseline.empty()) <<
"Baseline permutation should pass";
264 const size_t corrupt_pos = (Flavor::MINI_CIRCUIT_SIZE * 2) + 500;
271 EXPECT_FALSE(failures.empty()) <<
"Permutation should fail after z_perm corruption";
286 auto [
key, params] = build_valid_translator_state();
287 auto& pp =
key.proving_key->polynomials;
292 EXPECT_TRUE(baseline.empty()) <<
"Baseline permutation should pass";
295 ASSERT_TRUE(pp.z_perm.is_shiftable());
296 size_t structural_first_row = pp.z_perm.start_index() - 1;
299 const auto& lagrange_first = pp.lagrange_first;
300 size_t scanned_first_row = 0;
302 for (
size_t i = lagrange_first.start_index(); i < lagrange_first.end_index(); ++i) {
303 if (lagrange_first[i] !=
FF(0)) {
304 scanned_first_row = i;
309 ASSERT_TRUE(found) <<
"lagrange_first has no non-zero entry";
310 ASSERT_EQ(structural_first_row, scanned_first_row)
311 <<
"lagrange_first position doesn't match z_perm shiftable structure";
313 const size_t first_row = scanned_first_row;
316 pp.z_perm = pp.z_perm.full();
317 pp.z_perm_shift = pp.z_perm_shift.full();
319 ASSERT_EQ(pp.z_perm[first_row],
FF(0));
322 pp.z_perm.at(first_row) =
FF(1);
325 pp, params,
"TranslatorPermutationRelation - After setting z_perm != 0 at lagrange_first");
326 EXPECT_FALSE(failures.empty()) <<
"Permutation should fail after z_perm init corruption";
328 EXPECT_TRUE(failures.contains(2)) <<
"Sub-relation 2 (z_perm init) should catch the corruption";
329 EXPECT_EQ(failures.at(2),
static_cast<uint32_t
>(first_row)) <<
"Failure should be at lagrange_first row";
342 auto [
key, params] = build_valid_translator_state();
343 auto& pp =
key.proving_key->polynomials;
347 pp, params,
"TranslatorShiftableFirstCoeffZeroRelation");
348 EXPECT_TRUE(baseline.empty()) <<
"Baseline shiftable-first-coeff-zero should pass";
351 ASSERT_TRUE(pp.ordered_range_constraints_0.is_shiftable());
352 const size_t first_row = pp.ordered_range_constraints_0.start_index() - 1;
353 ASSERT_NE(pp.lagrange_first[first_row],
FF(0)) <<
"lagrange_first should be active at the ordered poly's zero row";
357 pp.ordered_range_constraints_0 = pp.ordered_range_constraints_0.full();
358 pp.ordered_range_constraints_0_shift = pp.ordered_range_constraints_0_shift.full();
359 ASSERT_EQ(pp.ordered_range_constraints_0[first_row],
FF(0));
362 pp.ordered_range_constraints_0.at(first_row) = -
FF(3);
365 pp, params,
"TranslatorShiftableFirstCoeffZeroRelation - After setting ordered_range_constraints_0[0] != 0");
366 EXPECT_FALSE(failures.empty()) <<
"Relation should fail after nonzero first sorted value";
368 EXPECT_TRUE(failures.contains(0)) <<
"Sub-relation 0 (ordered_0 first-coeff anchor) should catch the corruption";
369 EXPECT_EQ(failures.at(0),
static_cast<uint32_t
>(first_row)) <<
"Failure should be at lagrange_first row";
382 auto [
key, params] = build_valid_translator_state();
383 auto& pp =
key.proving_key->polynomials;
385 const size_t full_circuit_size = Flavor::MINI_CIRCUIT_SIZE * Flavor::CONCATENATION_GROUP_SIZE;
389 pp, params,
"TranslatorDeltaRangeConstraintRelation");
390 EXPECT_TRUE(baseline.empty()) <<
"Baseline delta range should pass";
394 const size_t boundary_pos = full_circuit_size - Flavor::MAX_RANDOM_VALUES_PER_ORDERED - 2;
395 pp.ordered_range_constraints_0.at(boundary_pos) =
FF(0);
398 pp, params,
"TranslatorDeltaRangeConstraintRelation");
399 EXPECT_FALSE(failures.empty()) <<
"Delta range should fail at the masking boundary";
408 auto [
key, params] = build_valid_translator_state();
409 auto& pp =
key.proving_key->polynomials;
413 pp, params,
"TranslatorDeltaRangeConstraintRelation");
414 EXPECT_TRUE(baseline.empty()) <<
"Baseline delta range should pass";
419 const size_t pos = 5000;
420 pp.ordered_range_constraints_0.at(pos) = pp.ordered_range_constraints_0[pos + 1] +
FF(1);
423 pp, params,
"TranslatorDeltaRangeConstraintRelation");
424 EXPECT_FALSE(failures.empty()) <<
"Delta range should fail on descending (negative delta) pair";
433 auto [
key, params] = build_valid_translator_state();
434 auto& pp =
key.proving_key->polynomials;
438 pp, params,
"TranslatorDeltaRangeConstraintRelation");
439 EXPECT_TRUE(baseline.empty()) <<
"Baseline delta range should pass";
442 const size_t pos = 3000;
443 FF next_val = pp.ordered_range_constraints_0[pos + 1];
444 pp.ordered_range_constraints_0.at(pos) = next_val -
FF(4);
447 pp, params,
"TranslatorDeltaRangeConstraintRelation");
448 EXPECT_FALSE(failures.empty()) <<
"Delta range should fail when delta is exactly 4";
457 auto [
key, params] = build_valid_translator_state();
458 auto& pp =
key.proving_key->polynomials;
462 pp, params,
"TranslatorDeltaRangeConstraintRelation");
463 EXPECT_TRUE(baseline.empty()) <<
"Baseline delta range should pass";
467 pp.ordered_range_constraints_0.at(1) =
FF(100);
470 pp, params,
"TranslatorDeltaRangeConstraintRelation");
471 EXPECT_FALSE(failures.empty()) <<
"Delta range should fail when first sorted value > 3";
480 auto [
key, params] = build_valid_translator_state();
481 auto& pp =
key.proving_key->polynomials;
485 pp, params,
"TranslatorDeltaRangeConstraintRelation");
486 EXPECT_TRUE(baseline.empty()) <<
"Baseline delta range should pass";
489 const size_t pos = 2000;
490 pp.ordered_range_constraints_4.at(pos) = pp.ordered_range_constraints_4[pos - 1] +
FF(100);
493 pp, params,
"TranslatorDeltaRangeConstraintRelation");
494 EXPECT_FALSE(failures.empty()) <<
"Delta range should fail on 5th ordered poly corruption";
503 auto [
key, params] = build_valid_translator_state();
504 auto& pp =
key.proving_key->polynomials;
509 EXPECT_TRUE(baseline.empty()) <<
"Baseline permutation should pass";
512 const size_t corrupt_pos = 500;
517 EXPECT_FALSE(failures.empty()) <<
"Permutation should fail after ordered poly corruption";
530 const size_t full_circuit_size = Flavor::MINI_CIRCUIT_SIZE * Flavor::CONCATENATION_GROUP_SIZE;
538 for (
const auto&
group : pp.get_groups_to_be_concatenated()) {
539 for (
auto& poly :
group) {
540 if (poly.is_empty()) {
543 for (
size_t i = poly.start_index(); i < poly.end_index() - NUM_DISABLED_ROWS_IN_SUMCHECK; i++) {
546 for (
size_t i = poly.end_index() - NUM_DISABLED_ROWS_IN_SUMCHECK; i < poly.end_index(); i++) {
554 const FF sentinel(42);
555 auto groups = pp.get_groups_to_be_concatenated();
556 auto& target_wire = groups[0][0];
557 const size_t wire_masking_start = target_wire.end_index() - NUM_DISABLED_ROWS_IN_SUMCHECK;
558 target_wire.at(wire_masking_start) = sentinel;
566 key.compute_lagrange_polynomials();
567 key.compute_extra_range_constraint_numerator();
568 key.compute_concatenated_polynomials();
572 const size_t concat_masking_pos = wire_masking_start;
573 EXPECT_EQ(pp.concatenated_range_constraints_0[concat_masking_pos], sentinel)
574 <<
"Sentinel should appear at the correct concatenated position";
576 key.compute_translator_range_constraint_ordered_polynomials();
587 for (
const auto& ord_poly : pp.get_ordered_range_constraints()) {
588 for (
size_t pos = full_circuit_size - Flavor::MAX_RANDOM_VALUES_PER_ORDERED; pos < full_circuit_size; pos++) {
589 if (ord_poly[pos] == sentinel) {
598 EXPECT_TRUE(found) <<
"Sentinel value 42 should appear in the ordered poly masking tail";
602 compute_grand_product<Flavor, TranslatorPermutationRelation<FF>>(pp, params);
606 EXPECT_TRUE(perm_failures.empty()) <<
"Permutation should pass with in-range masking value";
609 pp, params,
"TranslatorDeltaRangeConstraintRelation");
610 EXPECT_TRUE(delta_failures.empty()) <<
"Delta range should pass with in-range masking value";
619 auto [
key, params] = build_valid_translator_state();
620 auto& pp =
key.proving_key->polynomials;
625 EXPECT_TRUE(baseline.empty()) <<
"Baseline permutation should pass";
628 const size_t corrupt_pos = 5;
633 EXPECT_FALSE(failures.empty()) <<
"Permutation should fail after extra numerator corruption";
644 auto [
key, params] = build_valid_accumulator_transfer_state();
645 auto& pp =
key.proving_key->polynomials;
649 pp, params,
"TranslatorAccumulatorTransferRelation");
650 EXPECT_TRUE(baseline.empty()) <<
"Baseline accumulator transfer should pass";
657 pp, params,
"TranslatorAccumulatorTransferRelation");
658 EXPECT_FALSE(failures.empty()) <<
"Accumulator transfer should fail after odd row corruption";
667 auto [
key, params] = build_valid_accumulator_transfer_state();
668 auto& pp =
key.proving_key->polynomials;
671 pp, params,
"TranslatorAccumulatorTransferRelation");
672 EXPECT_TRUE(baseline.empty()) <<
"Baseline accumulator transfer should pass";
675 const size_t last_in_minicircuit = Flavor::MINI_CIRCUIT_SIZE - Flavor::NUM_MASKED_ROWS_END - 1;
676 pp.accumulators_binary_limbs_0.at(last_in_minicircuit) =
FF(1);
679 pp, params,
"TranslatorAccumulatorTransferRelation");
680 EXPECT_FALSE(failures.empty()) <<
"Accumulator transfer should fail when zero-init position is non-zero";
689 auto [
key, params] = build_valid_accumulator_transfer_state();
690 auto& pp =
key.proving_key->polynomials;
693 pp, params,
"TranslatorAccumulatorTransferRelation");
694 EXPECT_TRUE(baseline.empty()) <<
"Baseline accumulator transfer should pass";
697 params.accumulated_result[0] +=
FF(1);
700 pp, params,
"TranslatorAccumulatorTransferRelation");
701 EXPECT_FALSE(failures.empty()) <<
"Accumulator transfer should fail on result mismatch";
716 auto [
key, params] = build_valid_accumulator_transfer_state();
717 auto& pp =
key.proving_key->polynomials;
720 for (
size_t i = Flavor::RANDOMNESS_START; i < Flavor::RESULT_ROW; i++) {
728 const size_t end_mask_start = Flavor::MINI_CIRCUIT_SIZE - Flavor::NUM_MASKED_ROWS_END;
729 for (
size_t i = end_mask_start; i < Flavor::MINI_CIRCUIT_SIZE; i++) {
738 pp, params,
"TranslatorAccumulatorTransferRelation");
739 EXPECT_TRUE(failures.empty()) <<
"Accumulator transfer should pass even with arbitrary masking region values";
748 auto [
key, params] = build_valid_accumulator_transfer_state();
749 auto& pp =
key.proving_key->polynomials;
752 pp, params,
"TranslatorAccumulatorTransferRelation");
753 EXPECT_TRUE(baseline.empty()) <<
"Baseline accumulator transfer should pass";
757 const size_t first_transfer_row = Flavor::RESULT_ROW + 1;
761 pp, params,
"TranslatorAccumulatorTransferRelation");
762 EXPECT_FALSE(failures.empty()) <<
"Accumulator transfer should fail at first transfer row";
772 auto [
key, params] = build_valid_accumulator_transfer_state();
773 auto& pp =
key.proving_key->polynomials;
776 pp, params,
"TranslatorAccumulatorTransferRelation");
777 EXPECT_TRUE(baseline.empty()) <<
"Baseline accumulator transfer should pass";
780 const size_t last_transfer_row = Flavor::MINI_CIRCUIT_SIZE - Flavor::NUM_MASKED_ROWS_END - 3;
784 pp, params,
"TranslatorAccumulatorTransferRelation");
785 EXPECT_FALSE(failures.empty()) <<
"Accumulator transfer should fail at last transfer row";
795 auto [
key, params] = build_valid_translator_state();
796 auto& pp =
key.proving_key->polynomials;
801 EXPECT_TRUE(baseline.empty()) <<
"Baseline permutation should pass";
805 const size_t block_boundary_pos = 5 * Flavor::MINI_CIRCUIT_SIZE;
806 EXPECT_EQ(pp.concatenated_range_constraints_0[block_boundary_pos],
FF(0))
807 <<
"Block boundary should initially be zero";
809 pp.concatenated_range_constraints_0.at(block_boundary_pos) =
FF(999);
812 compute_grand_product<Flavor, TranslatorPermutationRelation<FF>>(pp, params);
816 EXPECT_FALSE(failures.empty()) <<
"Permutation should fail after block boundary corruption";
835 static constexpr size_t NUM_LIMB_BITS = Flavor::CircuitBuilder::NUM_LIMB_BITS;
837 auto [
key, params] = build_valid_accumulator_transfer_state();
838 auto& pp =
key.proving_key->polynomials;
842 pp, params,
"TranslatorNonNativeFieldRelation");
843 EXPECT_TRUE(baseline.empty()) <<
"Baseline non-native field should pass";
845 constexpr size_t ROW = Flavor::RESULT_ROW;
849 auto read_limbs = [](
const auto& l0,
const auto& l1,
const auto& l2,
const auto& l3,
size_t row) {
851 (
uint256_t(l2[row]) << (2 * NUM_LIMB_BITS)) | (
uint256_t(l3[row]) << (3 * NUM_LIMB_BITS));
854 uint256_t old_acc = read_limbs(pp.accumulators_binary_limbs_0,
855 pp.accumulators_binary_limbs_1,
856 pp.accumulators_binary_limbs_2,
857 pp.accumulators_binary_limbs_3,
862 (
uint256_t(pp.quotient_low_binary_limbs[ROW + 1]) << NUM_LIMB_BITS) |
863 (
uint256_t(pp.quotient_high_binary_limbs[ROW]) << (2 * NUM_LIMB_BITS)) |
864 (
uint256_t(pp.quotient_high_binary_limbs[ROW + 1]) << (3 * NUM_LIMB_BITS));
871 auto split = [](
const uint256_t& val) -> std::array<FF, 4> {
872 return {
FF(val.slice(0, NUM_LIMB_BITS)),
873 FF(val.slice(NUM_LIMB_BITS, 2 * NUM_LIMB_BITS)),
874 FF(val.slice(2 * NUM_LIMB_BITS, 3 * NUM_LIMB_BITS)),
875 FF(val.slice(3 * NUM_LIMB_BITS, 4 * NUM_LIMB_BITS)) };
878 auto new_acc_limbs = split(new_acc);
879 auto new_quot_limbs = split(new_quot);
882 pp.accumulators_binary_limbs_0.at(ROW) = new_acc_limbs[0];
883 pp.accumulators_binary_limbs_1.at(ROW) = new_acc_limbs[1];
884 pp.accumulators_binary_limbs_2.at(ROW) = new_acc_limbs[2];
885 pp.accumulators_binary_limbs_3.at(ROW) = new_acc_limbs[3];
888 pp.quotient_low_binary_limbs.at(ROW) = new_quot_limbs[0];
889 pp.quotient_low_binary_limbs.at(ROW + 1) = new_quot_limbs[1];
890 pp.quotient_high_binary_limbs.at(ROW) = new_quot_limbs[2];
891 pp.quotient_high_binary_limbs.at(ROW + 1) = new_quot_limbs[3];
897 pp, params,
"TranslatorNonNativeFieldRelation");
900 EXPECT_TRUE(failures.contains(1)) <<
"Subrelation 1 (higher carry check) should reject the alias";
901 EXPECT_EQ(failures.at(1),
static_cast<uint32_t
>(ROW)) <<
"Failure should be at RESULT_ROW";
904 EXPECT_FALSE(failures.contains(2)) <<
"Subrelation 2 (native check) should pass under the alias mutation";
921 auto [
key, params] = build_valid_accumulator_transfer_state();
922 auto& pp =
key.proving_key->polynomials;
925 pp, params,
"TranslatorOpcodeConstraintRelation");
926 EXPECT_TRUE(baseline.empty()) <<
"Baseline opcode constraint should pass";
929 const size_t odd_row = Flavor::RESULT_ROW + 1;
930 ASSERT_EQ(pp.op[odd_row],
FF(0));
933 pp.op.at(odd_row) =
FF(3);
936 pp, params,
"TranslatorOpcodeConstraintRelation");
937 EXPECT_FALSE(failures.empty()) <<
"Opcode constraint should fail with a genuine opcode on an odd row";
938 EXPECT_TRUE(failures.contains(0)) <<
"Subrelation 0 (opcode validity) should catch the odd-row opcode";
939 EXPECT_EQ(failures.at(0),
static_cast<uint32_t
>(odd_row)) <<
"Failure should be at the odd row";
static void SetUpTestSuite()
A container for the prover polynomials.
typename Curve::ScalarField FF
ECCVMCircuitBuilder CircuitBuilder
typename Curve::BaseField BF
bb::Polynomial< FF > Polynomial
typename G1::element GroupElement
A debugging utility for checking whether a set of polynomials satisfies the relations for a given Fla...
A wrapper for Relations to expose methods used by the Sumcheck prover or verifier to add the contribu...
group class. Represents an elliptic curve group element. Group is parametrised by Fq and Fr
virtual uint16_t get_random_uint16()=0
typename ECCVMFlavor::ProverPolynomials ProverPolynomials
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
TEST_F(IPATest, ChallengesAreZero)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Container for parameters used by the grand product (permutation, lookup) Honk relations.
std::array< std::array< T, NUM_BINARY_LIMBS_IN_GOBLIN_TRANSLATOR+NUM_NATIVE_LIMBS_IN_GOBLIN_TRANSLATOR >, NUM_CHALLENGE_POWERS_IN_GOBLIN_TRANSLATOR > batching_challenge_v
std::array< T, NUM_BINARY_LIMBS_IN_GOBLIN_TRANSLATOR > accumulated_result
std::array< T, NUM_BINARY_LIMBS_IN_GOBLIN_TRANSLATOR+NUM_NATIVE_LIMBS_IN_GOBLIN_TRANSLATOR > evaluation_input_x
static field random_element(numeric::RNG *engine=nullptr) noexcept